czardas

Row Sum (Σ) Formula Validation

18 posts in this topic

#1 ·  Posted (edited)

Much work still needs to be done on my current project, but design concepts often benefit more from early criticism. Although there may be nothing particularly innovative about this project, user experience is everything. I would like you to look at the behaviour of the controls on this GUI. Unless you type something really awful, it allows you to finish typing the formula before testing the input. When typing into an edit control, pressing the enter key activates the okay button.

The program itself, mainly consists of a listview control, which you can't see - just imagine it looks like a stone age version of Excel with a nice paint job, but no bells or whistles. What Row Sum (Σ) is intended to do is loop through all the rows, calculate a sum and print the output in a newly created column. The user is prompted to type a formula into the following GUI. Please try to figure it out, and break it any way you can.

#include <GUIConstants.au3>
#include <GuiEdit.au3>
#include <Misc.au3>

Global $g_iAvailableCols = 24 ; arbitary dev variable [column count will eventually be read from a listview control]

; missing several thousand lines of code...

Formula()

; caution about including columns containing empty fields in the formula
; empty fields are treated as zero
; with addition and subtraction there is never any problem
; multiplication by an empty field (or by zero) returns zero
; division by an empty field (or by zero) returns an empty field [unless - see exception]
; use of the power operator is limited to rows in which all referenced fields contain numbers
; imaginary roots of negative numbers return an empty field [unless - see exception]
; exception: infinity (or an imaginary number) to the power of zero returns a meaningless number

Func FormulaSyntax()
    MsgBox(BitOR(64, 8192), "Row Sum (" & ChrW(0x03A3) & ") : Syntax To Use", _ ; $MB_ICONINFORMATION, $MB_TASKMODAL
    " mathematical operators :" & @TAB & "+ - * / ^" & @CRLF & _
    " column numbers :" & @TAB & @TAB & "c1, c2, c3  etc..." & @CRLF & _
    " decimal digits :" & @TAB & @TAB & "0 to 9 and ." & @CRLF & _
    " parenthesis :" & @TAB & @TAB & "( )" & @CRLF & _
    " minus sign :" & @TAB & @TAB & "-1, -c2, -(c3)  etc..." & @CRLF & _
    " example :" & @TAB & @TAB  & @TAB & "c1 +c2 -c3")
EndFunc ;==> FormulaSyntax

Func Formula() ; ($hParent, $idListView, $hListView) ; [missing parent window]
    Local $sTitle = "Row Sum (" & ChrW(0x03A3) & ")", _
    $iStyle = BitOR($WS_CAPTION, $WS_POPUP, $WS_SYSMENU), _
    $iExStyle = BitOR($WS_EX_MDICHILD, $WS_EX_TOOLWINDOW, $WS_EX_TOPMOST)

    Local $hChild = GUICreate($sTitle, 334 +25, 105 +1, Default + 100, Default + 100); , $iStyle, $iExStyle, $hParent)

    Local $hLabel = GUICtrlCreateLabel("Formula :", 8, 8, 55, 18)
    GUICtrlSetFont(-1, 10)

    Local $hSyntax = GUICtrlCreateButton("???", 72, 7, 40, 20)

    Local $hLabel = GUICtrlCreateLabel("Header :", 127, 8, 49, 18)
    GUICtrlSetFont(-1, 10)

    Local $hHeader = GUICtrlCreateInput("", 182, 7, 170, 20)
    GUICtrlSetFont(-1, 10)

    Local $hInput = GUICtrlCreateInput("", 7, 33, 320 +25, 40, BitOR($WS_TABSTOP, $ES_MULTILINE))
    GUICtrlSetFont(-1, 10)

    Local $hCheckBox = GUICtrlCreateCheckbox(" Round to", 7, 79, 75, 20)
    GUICtrlSetFont(-1, 10)

    Local $hPlaces = GUICtrlCreateInput("2", 86, 79 +1, 20, 20, BitOR($WS_TABSTOP, $ES_CENTER, $ES_NUMBER))
    GUICtrlCreateLabel("decimal places", 114 -2, 79 +2, 92, 18)
    GUICtrlSetFont(-1, 10)

    Local $hCancel = GUICtrlCreateButton("Cancel", 210, 79 +1, 66, 20)

    Local $hOkay = GUICtrlCreateButton("OK", 285, 79 +1, 66, 20)
    GUISetState(@SW_SHOW)

    Local $sInput = "", $aArray, $iError = 0, $iCols = $g_iAvailableCols ; $iCols = _GUICtrlListView_GetColumnCount($hListView), _ ; >>> CHANGE THIS LATER
    ;$iRows = _GUICtrlListView_GetItemCount($hListView), $aColOrder = _GUICtrlListView_GetColumnOrderArray($hListView)

    Local $vTemp, $msg2, $iPlaces = '2'
    While 1
        $msg2 = GUIGetMsg()
        If $msg2 = $hCancel Or $msg2 = $GUI_EVENT_CLOSE Then ExitLoop

        If $msg2 = $hSyntax Then
            FormulaSyntax()
            ContinueLoop
        EndIf

        $vTemp = GUICtrlRead($hPlaces)
        If $vTemp <> $iPlaces Then
            If $vTemp > 14 Then
                GUICtrlSetData($hPlaces, 14)
                $iPlaces = 14
                _GUICtrlEdit_SetSel($hPlaces, 0, -1)
            ElseIf Not $vTemp Then
                GUICtrlSetData($hPlaces ,'0')
                $iPlaces = '0'
                _GUICtrlEdit_SetSel($hPlaces, 0, -1)
            EndIf
        EndIf

        If BitAND(GUICtrlRead($hCheckBox), $GUI_CHECKED) == $GUI_CHECKED _
            And BitAND(GUICtrlGetState($hPlaces), $GUI_DISABLE) == $GUI_DISABLE Then
            GUICtrlSetState($hPlaces, $GUI_ENABLE)
        ElseIf BitAND(GUICtrlRead($hCheckBox), $GUI_CHECKED) <> $GUI_CHECKED _
            And BitAND(GUICtrlGetState($hPlaces), $GUI_ENABLE) == $GUI_ENABLE Then
            GUICtrlSetState($hPlaces, $GUI_DISABLE)
        EndIf

        $sFormula = GUICtrlRead($hInput)

        If $sFormula <> $sInput Then
            If StringRegExp($sFormula, '[^ ]') Then ; spaces between components are ignored

                If Not ValidSyntax($sFormula, $iCols) Then
                    If @extended < 5 Then
                        If $iError = 0 Then GUICtrlSetBkColor($hInput, 0xFFA090)
                        $iError = 1
                    ElseIf $iError Then
                        GUICtrlSetBkColor($hInput, 0xFFFFFF)
                        $iError = 0
                    EndIf

                ElseIf $iError Then
                    GUICtrlSetBkColor($hInput, 0xFFFFFF)
                    $iError = 0
                EndIf

            ElseIf $iError Then
                GUICtrlSetBkColor($hInput, 0xFFFFFF)
                $iError = 0
            EndIf
            $sInput = $sFormula
        EndIf

        ;  BitAND(WinGetState($hChild), $WIN_STATE_ACTIVE) = 8
        If $msg2 = $hOkay Or (_IsPressed("0D") And BitAND(WinGetState($hChild), 8) = 8 And (ControlGetFocus($hChild) = "Edit2" Or ControlGetFocus($hChild) = "Edit1")) Then
            $sFormula = GUICtrlRead($hInput) ;
            If StringRegExp($sFormula, '[^ ]') And ValidSyntax($sFormula, $iCols) Then
                ; missing formula execution code

                MsgBox(0, "VALID SYNTAX", "Well done, your formula can safely be executed, but" & @CRLF & "can't find parent window.")
                ExitLoop
            Else
                Switch @extended
                    Case 1
                        $vTemp = "The formula contains illegal characters."
                    Case 2
                        $vTemp = "No such column exists."
                    Case 3
                        $vTemp = "The formula contains a syntax error."
                    Case 4
                        $vTemp = "The formula is missing opening parenthesis."
                    Case 5
                        $vTemp = "The formula is unterminated"
                    case 6
                        $vTemp = "The formula is missing closing parenthesis."
                EndSwitch

                MsgBox(262160, "uh-uh!", StringRegExp($sFormula, '[^ ]') ? $vTemp : "Please enter a formula.")
                While _IsPressed("0D")
                    Sleep(50)
                WEnd
                ;$iError = 1
            EndIf
        EndIf
    WEnd
    GUIDelete($hChild)
EndFunc ; formula

Func ValidSyntax($sFormula, $iMax) ; [currently requires external check that string <> ""]
    ; quick test for accepted characters
    If StringRegExp($sFormula, '(?i)[^c \d\.\+\-\*/\^\(\)]') Then Return SetExtended(1, False) ; illegal characters
    If StringRegExp($sFormula, '(?i)\.\D|c[0\D]|\d\s+[\.\d]') Then Return SetExtended(3, False) ; syntax

    $sFormula = StringStripWS($sFormula, 8) ; strip all spaces

    ; split to separate formula components
    Local $aFormula = StringRegExp($sFormula, '(?i)\-?c\d+|\-?c|\-?\d*\.\d*|\-?\d+|\-\(|[\+\-\*/\^\(\)]|.+', 3), _ ; create array [ADDED] ==> |.+
    $iLast, $sTest, $sExpect = '(?i)\-?\(|\-?c\d+|\-?c|\-?\d*\.\d*|\-?\d+|\-', _ ; valid first component
    $iBracket = 0, $bSyntax = False, $bNoSuch = False, $bTermination = False ; error tracking variables

    For $i = 0 To UBound($aFormula) -1
        If Not StringRegExp($aFormula[$i], $sExpect) Then ; unexpected code sequence
            $bSyntax = True
            ExitLoop
        EndIf

        If StringRegExp($aFormula[$i], '(?i)\-?c\d+') Then ; column number
            $sTest = StringRegExpReplace($aFormula[$i], '(?i)[\-c]+', '')
            If $sTest > $iMax Or $sTest == 0 Then ; no such column exists
                $bNoSuch = True
                ExitLoop
            EndIf
            $sExpect = '[\+\-\*/\^\)]' ; operators / closing brackets

        ElseIf StringRegExp($aFormula[$i], '\-?\d*\.\d*') Then ; decimal
            If $aFormula[$i] == '.' Or $aFormula[$i] == '-.' Then
                If $i = UBound($aFormula) -1 Then
                    $bTermination = True
                    ExitLoop
                EndIf
                $bSyntax = True ; badly formated decimal
                ExitLoop
            EndIf
            $sExpect = '[\+\-\*/\^\)]' ; as above

        ElseIf StringRegExp($aFormula[$i], '\-?\d+') Then ; integer
            $sExpect = '[\+\-\*/\^\)]'

        ElseIf StringRegExp($aFormula[$i], '\A[\+\-\*/\^]\z') Then ; operator
            $iLast = UBound($aFormula) -1
            If $i = $iLast Or ($i = $iLast -1 And $aFormula[$iLast] == '-') Then
                $bTermination = True
                ExitLoop
            EndIf
            $sExpect = '(?i)\-?\(|\-?c\d+|\-?c|\-?\d*\.\d*|\-?\d+'

        ElseIf $aFormula[$i] == '(' Or $aFormula[$i] == '-(' Then ; opening bracket
            $iBracket += 1
            $sExpect = '(?i)\-?\(|\-?c\d+|\-?c|\-?\d*\.\d*|\-?\d+|\-'

        ElseIf $aFormula[$i] == ')' Then ; closing bracket
            $iBracket -= 1
            $sExpect = '[\+\-\*/\^\)]'

        ElseIf $aFormula[$i] = 'c' Or $aFormula[$i] = '-c' Then ; unterminated column number
            If $i = UBound($aFormula) -1 Then
                $bTermination = True
            Else
                $bSyntax = True
                ExitLoop
            EndIf

        Else ; unexpected exception [this should never happen]
            $bSyntax = True
            ExitLoop
        EndIf
        If $iBracket < 0 Then ExitLoop ; closing (unopened) parenthesis error

        ; [formula looks okay so far]
    Next

    ; better to be a bit wordy [for clarity]
    If $bNoSuch Then Return SetExtended(2, False) ; no such column exists
    If $bSyntax Then Return SetExtended(3, False) ; syntax error
    If $iBracket < 0 Then Return SetExtended(4, False) ; missing opening parenthesis
    If $bTermination Then Return SetExtended(5, False) ; formula may not terminate with operator or c
    If $iBracket > 0 Then Return SetExtended(6, False) ; missing closing parenthesis
    ; ConsoleWrite('valid' & @LF)
    Return True ; formula is correct
EndFunc ;==> ValidSyntax

 

Edited by czardas

Share this post


Link to post
Share on other sites



Just did a quick test.
You stated that "user experience is everything". I, as a user, would like to see the general error message "The formula you entered contains errors." replaced by helpful error messages like "no such column exists" etc.

1 person likes this

My UDFs and Tutorials:

Spoiler

UDFs:
Active Directory (NEW 2017-04-18 - Version 1.4.8.0) - Download - General Help & Support - Example Scripts - Wiki
OutlookEX (NEW 2017-02-27 - Version 1.3.1.0) - Download - General Help & Support - Example Scripts - Wiki
ExcelChart (2015-04-01 - Version 0.4.0.0) - Download - General Help & Support - Example Scripts
Excel - Example Scripts - Wiki
Word - Wiki
PowerPoint (2015-06-06 - Version 0.0.5.0) - Download - General Help & Support

Tutorials:
ADO - Wiki

 

Share this post


Link to post
Share on other sites

Thanks water :), how very true. I should have added that part: all the information is returned by ValidSyntax() with the @extended flag. I was just too lazy to add the final code (or distracted by other concerns). I was more wondering if I might have missed something while testing formula syntax.

Share this post


Link to post
Share on other sites

Just noticed that there is a limit of 24 columns. Would be fine to have this described in the help function :)


My UDFs and Tutorials:

Spoiler

UDFs:
Active Directory (NEW 2017-04-18 - Version 1.4.8.0) - Download - General Help & Support - Example Scripts - Wiki
OutlookEX (NEW 2017-02-27 - Version 1.3.1.0) - Download - General Help & Support - Example Scripts - Wiki
ExcelChart (2015-04-01 - Version 0.4.0.0) - Download - General Help & Support - Example Scripts
Excel - Example Scripts - Wiki
Word - Wiki
PowerPoint (2015-06-06 - Version 0.0.5.0) - Download - General Help & Support

Tutorials:
ADO - Wiki

 

Share this post


Link to post
Share on other sites

#5 ·  Posted (edited)

The limit is a temporary measure. You can change it to any number you like > 0. Modify this line:

Global $g_iAvailableCols = 24 ; arbitary dev variable [column count will eventually be read from a listview control]

 

Edited by czardas

Share this post


Link to post
Share on other sites

I mean: Modify the help page to

" column numbers :" & @TAB & @TAB & "c1, c2, c3  etc. maximum is c" & $g_iAvailableCols & @CRLF & _

 


My UDFs and Tutorials:

Spoiler

UDFs:
Active Directory (NEW 2017-04-18 - Version 1.4.8.0) - Download - General Help & Support - Example Scripts - Wiki
OutlookEX (NEW 2017-02-27 - Version 1.3.1.0) - Download - General Help & Support - Example Scripts - Wiki
ExcelChart (2015-04-01 - Version 0.4.0.0) - Download - General Help & Support - Example Scripts
Excel - Example Scripts - Wiki
Word - Wiki
PowerPoint (2015-06-06 - Version 0.0.5.0) - Download - General Help & Support

Tutorials:
ADO - Wiki

 

Share this post


Link to post
Share on other sites

If the # of decimal places exceeds 14 then it is set to the # of allowed columns (now 24).
Is this intended by you?


My UDFs and Tutorials:

Spoiler

UDFs:
Active Directory (NEW 2017-04-18 - Version 1.4.8.0) - Download - General Help & Support - Example Scripts - Wiki
OutlookEX (NEW 2017-02-27 - Version 1.3.1.0) - Download - General Help & Support - Example Scripts - Wiki
ExcelChart (2015-04-01 - Version 0.4.0.0) - Download - General Help & Support - Example Scripts
Excel - Example Scripts - Wiki
Word - Wiki
PowerPoint (2015-06-06 - Version 0.0.5.0) - Download - General Help & Support

Tutorials:
ADO - Wiki

 

Share this post


Link to post
Share on other sites

Maybe, I get what you are saying. There will be a help menu topic. I wanted to know if it is possible to figure out what's going on without the need to refer to help topics. There has to be a quick syntax reference though. I'm just about to update the first post to include a more detailed error report.

Share this post


Link to post
Share on other sites

#9 ·  Posted (edited)

Oops, I missed your last question about decimal places (post #7): an error crept in on my first edit which I have now fixed. I also notice that the final error message doesn't entirely like the enter key when an edit control has focus. It's an easy fix.

Edit: The enter key issue has also been fixed now.

Edited by czardas

Share this post


Link to post
Share on other sites

#10 ·  Posted (edited)

Another validation bugfix, although you probably won't notice any difference. In theory it should be easy to quickly learn how to type a formula with just the information available. How easy do you think this is? Does the colour change assist you or annoy you?

Edited by czardas

Share this post


Link to post
Share on other sites

I like the colour change because it immediately tells me that the last letter/number I entered was wrong.

1 person likes this

My UDFs and Tutorials:

Spoiler

UDFs:
Active Directory (NEW 2017-04-18 - Version 1.4.8.0) - Download - General Help & Support - Example Scripts - Wiki
OutlookEX (NEW 2017-02-27 - Version 1.3.1.0) - Download - General Help & Support - Example Scripts - Wiki
ExcelChart (2015-04-01 - Version 0.4.0.0) - Download - General Help & Support - Example Scripts
Excel - Example Scripts - Wiki
Word - Wiki
PowerPoint (2015-06-06 - Version 0.0.5.0) - Download - General Help & Support

Tutorials:
ADO - Wiki

 

Share this post


Link to post
Share on other sites

@water I couldn't ask for a better critic after all the work you have done creating the Excel UDF. I really appreciate you taking the time to try it. :thumbsup:

Share this post


Link to post
Share on other sites

Thanks :)


My UDFs and Tutorials:

Spoiler

UDFs:
Active Directory (NEW 2017-04-18 - Version 1.4.8.0) - Download - General Help & Support - Example Scripts - Wiki
OutlookEX (NEW 2017-02-27 - Version 1.3.1.0) - Download - General Help & Support - Example Scripts - Wiki
ExcelChart (2015-04-01 - Version 0.4.0.0) - Download - General Help & Support - Example Scripts
Excel - Example Scripts - Wiki
Word - Wiki
PowerPoint (2015-06-06 - Version 0.0.5.0) - Download - General Help & Support

Tutorials:
ADO - Wiki

 

Share this post


Link to post
Share on other sites

#14 ·  Posted (edited)

I've made some small improvements to the code in first post. I may change it to allow rounding up to 15 decimal places. Once I implement this code, it may become unrecognizable. While this may not be the most exciting topic of discussion, I'm also posting the code I've just written for rounding and formatting the executed formula, since it is part of the same module: it might also be of inspiration to someone. It is currently intended that the formula will return a double. The following code runs with all the safeties turned off (potentially returning up to 17 significant digits along with a load of zeros). The user will be advised (not forced) to remain within 15 significant figures when rounding. Trailing zero padding is added by default.

MsgBox(0, "", DecimalPlaces(Execute('0.30000000000000099'), 15))
MsgBox(0, "", DecimalPlaces(Execute('2/-3'), 15))
MsgBox(0, "", DecimalPlaces(Execute('-0.000000000000011 *3'), 15))
MsgBox(0, "", DecimalPlaces(Execute('10000000000000.01000000000000000'), 15))
MsgBox(0, "", DecimalPlaces(Execute('-123456789012345.6199123458989'), 3))
MsgBox(0, "", DecimalPlaces(1.8734176520787252e+004, 12)) ; lucky number!
MsgBox(0, "", DecimalPlaces(Execute('-000222222222222222922222222222222222222200.888888888888888*2'), 14))

; accepts doubles
Func DecimalPlaces($fNumber, $iPlaces = 2) ;, $bTrailingZeros = True) ; [add this option later along with program settings]
    ; error checks [magnitude maybe]
    $iPlaces = Int($iPlaces) ; max = 15
    $fNumber = Number($fNumber, 3) ; convert to double (just in case) [more complex routines could also be written: BigNum etc...]

    Local $bNeg = False
    If StringLeft($fNumber, 1) == '-' Then
        $bNeg = True
        $fNumber = Abs($fNumber)
    EndIf

    $fNumber = Round($fNumber, $iPlaces)

    Local $nDigits, $iExponent, $nOutput, $iLen, $iDot = StringInStr($fNumber, '.')
    If StringInStr($fNumber, 'e') Then
        $nDigits = StringReplace(StringRegExpReplace($fNumber, 'e[\+\-]\d+\z', ''), '.', '')
        $iLen = StringLen($nDigits)
        $iExponent = StringRight($fNumber, 4)
        If $iLen = 15 Then ; [uh-uh - rounding had no discernible effect on length]
            $iLen = $iExponent + $iPlaces +1
            $nDigits = __FloatToDigits($fNumber, (($iLen < 16) ? $iLen : 16)) ; return all 17 digits of the rounded double [it makes sense]
            $iExponent = @extended
            $iLen = StringLen($nDigits)
        Else
            $iExponent += (1 -$iLen)
        EndIf

    ElseIf $iDot And StringLen($fNumber) < 16 Then
        $nOutput = StringLeft($fNumber & '000000000000000', $iDot + $iPlaces)
        If $bNeg Then $nOutput = '-' & $nOutput
        Return $nOutput

    Else
        $iLen =  ($iDot ? $iDot : StringLen($fNumber)) + $iPlaces
        $nDigits = __FloatToDigits($fNumber, (($iLen < 16) ? $iLen : 16))
        $iExponent = @extended
        $iLen = StringLen($nDigits)
    EndIf

    Local $sZeros = '000000000000000'
    If $iExponent < 0 Then
        Local $iGap = $iLen + $iExponent
        If $iGap < 1 Then
            $nOutput = '0.' & StringLeft($sZeros, Abs($iGap)) & $nDigits
        Else
            $nOutput = StringLeft($nDigits, $iGap) & '.' & StringRight($nDigits, $iLen -$iGap)
        EndIf

        $iGap = $iLen -$iGap
        If $iGap < $iPlaces Then
            $nOutput &= StringLeft($sZeros, $iPlaces -$iGap)

        ElseIf $iGap > $iPlaces Then ; this one slipped through the net
            Local $bAddOne = (StringMid($nOutput, StringInStr($nOutput, '.') +$iPlaces +1, 1) >= 5) ? True : False ; round up
            $nOutput = StringTrimRight($nOutput, $iGap -$iPlaces)

            If $bAddOne Then ; when all else fails, use the old fashioned method
                Local $vNext, $nRound
                For $i = StringLen($nOutput) To 1 Step -1
                    $vNext = StringMid($nOutput, $i, 1)
                    If $vNext = '.' Then
                        $nRound &= '.'
                    Else
                        $vNext += 1
                        If $vNext < 10 Then
                            $nRound &= $vNext
                            $nOutput = StringLeft($nOutput, $i -1) & StringReverse($nRound)
                            $bAddOne = False
                            ExitLoop ; this should happen on the first run [does this loop ever actually repeat?]
                        EndIf
                        $nRound &= '0'
                    EndIf
                Next
                If $bAddOne Then $nOutput = '1' & StringReverse($nRound)
            EndIf
        EndIf

    Else
        While StringLen($sZeros) < $iExponent
            $sZeros &= '000000000000000'
        WEnd

        $nOutput = $nDigits & StringLeft($sZeros, $iExponent) & '.' & StringLeft('000000000000000', $iPlaces)
    EndIf

    If $iPlaces = 0 Then $nOutput = StringReplace($nOutput, '.', '')
    If $bNeg And StringRegExp($nOutput, '[1-9]') Then $nOutput = '-' & $nOutput

    Return $nOutput
EndFunc ;==> DecimalPlaces

; function taken from : Fraction.au3
; #INTERNAL_USE_ONLY# ==========================================================================================================
; Name...........: __FloatToDigits
; Description ...: Extracts a specified number of digits from a float.
; Syntax.........: __FloatToDigits($fFloat, $iDigits)
; Parameters.....; $fFloat - The float to extract the digits from.
;                  $iDigits - The number of digits to extract after the floating point (exponential representation).
; Return values .: Success - Returns a 32-bit or 64-bit signed integer.
;                  Sets @extended to the decimal exponent. ==> $fFloat = return value * 10 ^ exponent
;                  Failure sets @error to 1 if the input is not a float or undefined.
; Author ........: czardas
; ==============================================================================================================================
Func __FloatToDigits($fFloat, $iDigits = 14)
    If VarGetType($fFloat) <> 'Double' Or StringInStr($fFloat, '#') Then Return SetError(1)
    Local $iSign = ($fFloat < 0) ? -1 : 1
    ; machine epsilon = 5 × 10^-15, so the final two digits (16 and 17) could be highly innacurate
    $fFloat = StringFormat('%.' & $iDigits & 'e', $fFloat) ; rounds to the specified number of decimal places

    Local $aFloat = StringSplit($fFloat, "e", 2) ; zero-based array
    If $iSign < 0 Then $aFloat[0] = StringTrimLeft($aFloat[0], 1) ; remove the minus sign

    ; remove the decimal point and trailing zeros
    $aFloat[0] = StringLeft($aFloat[0], 1) & StringRegExpReplace(StringRight($aFloat[0], $iDigits), '(0+\z)', '')
    $aFloat[1] += 1 - StringLen($aFloat[0]) ; adjust the exponent to accommodate changes

    Return SetExtended($aFloat[1], Int($aFloat[0]) * $iSign) ; add back the minus sign
EndFunc ;==> __FloatToDigits

There may yet be some superfluous (and therefore dubious) lines of code in the above example. I'll try removing them later, and see if it breaks anything. Which is worse, broken code or fuzzy human logic? :ermm:

Edited by czardas
code tidying

Share this post


Link to post
Share on other sites

Oh my! The worst is broken code with fuzzy human logic. I'm sure there must be a trick I can use like adding '.0' and just throwing the value into some engine designed by someone who can read the mind of the user.

Share this post


Link to post
Share on other sites

#16 ·  Posted (edited)

The above code has now been implemented and seems to work like a charm. I was also thinking of adding a column ratio feature, but I'm not sure how useful it might be. Here someone asks how to do this: http://stackoverflow.com/questions/472989/how-do-i-display-a-ratio-in-excel-in-the-format-ab , and I want to know why. Can you think of any practical applications?

Edited by czardas

Share this post


Link to post
Share on other sites

No, not really ;)

1 person likes this

My UDFs and Tutorials:

Spoiler

UDFs:
Active Directory (NEW 2017-04-18 - Version 1.4.8.0) - Download - General Help & Support - Example Scripts - Wiki
OutlookEX (NEW 2017-02-27 - Version 1.3.1.0) - Download - General Help & Support - Example Scripts - Wiki
ExcelChart (2015-04-01 - Version 0.4.0.0) - Download - General Help & Support - Example Scripts
Excel - Example Scripts - Wiki
Word - Wiki
PowerPoint (2015-06-06 - Version 0.0.5.0) - Download - General Help & Support

Tutorials:
ADO - Wiki

 

Share this post


Link to post
Share on other sites

I suppose application may be limited, but I think it looks nice. It can be useful in music theory or calculating betting odds, or if you just want to annoy people on stackoverflow. :D

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now

  • Similar Content

    • XinYoung
      By XinYoung
      Hello all,
      I've been working on 4 standalone scripts. They each have their own GUI and are now independent .exe's. But instead of having 4 separate icons on my computer, is it possible to put them all together into one .exe?
      What I want is one interface with tabs. Each script's GUI would be on their own respective tab. Is this possible?
      The different scripts don't need to communicate with one another. I just want them all in one .exe.
      I can't find this anywhere... Any help would be greatly appreciated. Thank you!
    • kawliga751
      By kawliga751
      I have a workplace GUI that has a search field function that pulls up customer records to allow changes. I am attempting to 1)LOGIN 2)Enter Search field, enter a number and then {ENTER} 3)change specific field once customer record loads. I have very limited experience in GUI manipulation but I am able to login using the below:
      #include <IE.au3> Const $navOpenInNewTab = 0x0800 Dim $oIE = _IECreate('http://asag.xxxxxxx.com/ASAG/index.htm') Call ("RacksheetSignIn") Func RacksheetSignIn () Local $username = _IEGetObjByName ($oIE,"uname") Local $password = _IEGetObjByName ($oIE,"pword") $oIE.Navigate2('http://asag.xxxxxxx.com/apps/RackSheets/LogInSAG/login.asp',2048) _IEFormElementSetValue ($username, "xxxxxxx") _IEFormElementSetValue ($password, "xxxxxxx") Send('{Enter}') EndFunc However I can't seem to use the _IEGetObjByName or Mouseclick to focus on the search field to allow me to enter my search criteria. When I "inspect element" in the Search field I get the attached:
       
      Any help would be MUCH appreciated
       
       
       

    • Jibberish
      By Jibberish
      I am having difficulties getting updated results from GUICtrlCreateInput. I wrote a small script to demonstrate what I am trying to do.
      This script reads a text file into an array and displays the Name and Number from the text file.
      The user can check a name and change the number, and the results are displayed in _ArrayDisplay.
      Only the Checked names are displayed in their original position. This is necessary for future
      plumbing.
      The problem with this script is the original number is returned, not the changed number. I cannot
      figure out how to get the updated number to be displayed.
      ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ; ; This script reads a text file into an array and displays the Name and Number from the text file. ; The user can check a name and change the number, and the results are displayed in _ArrayDisplay. ; Only the Checked names are displayed in their original position. This is necessary for future ; plumbing. ; ; The problem with this script is the original number is returned, not the changed number. I cannot ; figure out how to get the updated number to be displayed. ; ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #include-once #include <MsgBoxConstants.au3> #include <array.au3> #include <Date.au3> #include <WinAPIFiles.au3> #include <AutoItConstants.au3> #include <Misc.au3> #include <File.au3> #include <GUIConstantsEx.au3> #include <StaticConstants.au3> Local $iMax = 6 ;Default number of Videos - 1 for the array counter Dim $aTextFile[$iMax][2] ; [start with 5 entries][Name, Number] Don't get confused! [Row][Column] Local $i = 0 Local $iLeft = 30 Local $iTop = 30 Local $sName Local $sNumber Local $aArray Local $iMaxCol = 5 Local $iRow = 0 Local $iCol = 0 Local $aNumberCount[$iMaxCol][2] $aArray1 = ReadFile() $aNamesNumbers = DisplayNames($aArray1) _ArrayDisplay($aNamesNumbers) ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ; ; ReadFile() places the contents of the NameNumber.txt file in an array ; ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Func ReadFile() Local $sFileData = @ScriptDir & "\NameNumber.txt" Local $iStrReturn = 0 Local $aArrayFile Local $aTextFile[$iMaxCol][2] Local $aName Local $sCheckBox1 Const $iL = 10 ; Count from Left for GUI so all buttons line up Local $sFill Local $iA = 0, $iN = 0 ; Put the Names into an Array _FileReadToArray($sFileData, $aArrayFile) For $iA = 0 To $aArrayFile[0] ; Step through the array looking for Names If StringInStr($aArrayFile[$iA], "Name:") Then $aName = StringSplit($aArrayFile[$iA],":") $sFill = $aName[2] If UBound($aTextFile) <= $iRow Then ; Resize the array when $iRow is equal to the element count in the array to prevent subscript error ReDim $aTextFile[UBound($aTextFile) + 1][$iMaxCol] EndIf $aTextFile[$iRow][$iCol] = $sFill $iRow += 1 EndIf Next $iCol = 1 $iRow = 0 $sFill = "" For $iA = 1 To $aArrayFile[0] ; Step through the array looking for Numbers If StringInStr($aArrayFile[$iA], "Number:") Then $aName = StringSplit($aArrayFile[$iA],":") $sFill = $aName[2] If UBound($aTextFile) <= $iRow Then ; Resize the array when $iRow is equal to the element count in the array to prevent subscript error ReDim $aTextFile[UBound($aTextFile) + 1][$iMaxCol] EndIf $aTextFile[$iRow][$iCol] = $sFill $iRow += 1 EndIf Next Return $aTextFile EndFunc ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ; ; Display the Name and Number ; ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Func DisplayNames($aArray1) Local $aName[$iMax] Local $aNumbers[$iMax] $iMMCount = UBound($aArray1) $iMMCount -=1 Local $iWidth = 300 Local $iLength = 300;$iMMCount * 30 GUISetFont(12) $hGUI = GUICreate("Edit / Update Number Test", $iWidth, $iLength, -1, -1) GUICtrlCreateLabel(" Name Number",1,5) ;$iTop += 30 ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ; ; Display Name with a Checkbox. Only Checked Names should be saved. ; ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ For $i = 0 to $iMMCount Step 1 $sNameText = $aArray1[$i][0] $iNameLength = StringLen($sNameText) $aName[$i] = GUICtrlCreateCheckbox($sNameText,$iLeft, $iTop) $iTop += 30 Next ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ; ; Display the number from the text file below each name. Allow the user to change the number and display the ; new number. ; ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ $iLeft = 60 GUIStartGroup() $iTop = 28 For $i = 0 to $iMMCount Step 1 $sNumberText = $aArray1[$i][1] $aNumberCount[$i][1] = $sNumberText $aNumberCount[$i][0] = GUICtrlCreateInput($sNumberText,$iLeft, $iTop, 50,18, $GUI_DOCKAUTO) GUICtrlSetPos($aNumberCount[$i][0],200) $iTop += 30 Next ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ; ; Read the checked names and (possibly updated) number ; ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ $iMMCount = UBound($aNumberCount) $iMMNewCount = $iMMCount - 1 Local $aGUICheckbox[$iMMCount] Local $aCheckedNameNumber[$iMMCount][2] $iLeft = 30 Local $idCloseGUI = GUICtrlCreateButton("Close",$iLeft, $iTop) GUISetState(@SW_SHOW) While 1 $nMsg = GUIGetMsg() Switch $nMsg Case $GUI_EVENT_CLOSE Exit Case $idCloseGUI For $i = 0 To $iMMNewCount Step 1 Switch GUICtrlRead($aName[$i]) Case $GUI_CHECKED $aCheckedNameNumber[$i][0] = $aArray1[$i][0] Case $GUI_UNCHECKED EndSwitch Switch GUICtrlRead($aNumberCount[$i][0]) Case $aCheckedNameNumber[$i][1] = GUICtrlRead($aNumberCount[$i][1]) EndSwitch Next ExitLoop EndSwitch WEnd For $i = 0 to $iMMNewCount step 1 GUICtrlRead($aNumberCount[$i][0]) If $aCheckedNameNumber[$i][0] <> "" Then $aCheckedNameNumber[$i][1] = $aNumberCount[$i][1] EndIf Next GUIDelete($hGUI) Return $aCheckedNameNumber EndFunc This is the text file I am reading. If you want to try this out put the NameNumber.txt file in your script directory. It's attached to the post.
      * This is a dummy file with a Name and Number * The only purpose of this file is to read the updated Number. Name:Taggart Number:916 Name:Mongo Number:90 Name:Hedley Lamarr Number:22 Name:Bart Number:9999 Name:The Waco Kid Number:2244 If I change the number, the original number is displayed at the end, not the updated/modified number. I need the modified number to be displayed.
      Thanks in advance for any assistance!
      Jibberish
      NameNumber.txt
    • FMS
      By FMS
      Hello,
      As I was searching this forum I came along something intresting what was new to me.
      CUI/GUI hybrid.
      Whish sounds very usefull to me to compile it all into 1 script.
      So i was fideling around whit it but couldn't get it to work.
      In the real script (not this simplified version) i got a lot of functions whish i can use whan talking to it by commandline and switches.
      I could not get this to work and hope somebody could give me some pointers on how to make it work.
      Also I was hoping iff somebody could help me whit the /? function?
      (I don't know how to output the help text back to the commandline or the function that can help me do it)
      please advice and thanks in advanced.
      #AutoIt3Wrapper_UseUpx=n #AutoIt3Wrapper_UseX64=n #AutoIt3Wrapper_Change2CUI=y #include <Process.au3> #include <Array.au3> If $CmdLine[0] = 0 Then If _ProcessGetName(ProcessGetParent(@AutoItPID)) = "cmd.exe" Then ;ConsoleWrite("Program was started in CMD" & @CRLF) If Mod($CmdLine[0], 2) <> 0 Then Exit MsgBox(16, $sTitle, "Invalid number of parameters! has to be a multiple of 2!") If $CmdLine[0] = 0 Then Exit MsgBox(16, $sTitle, "No parameters passed!") For $i = 1 To $CmdLine[0] Step 2 If $CmdLine[$i] = "/switch" Then MsgBox(64, "Passed Parameters", "/switch is " & $CmdLine[$i+1]) If $CmdLine[$i] = "/?" Then MsgBox(64, "Passed Parameters", "/? " ) Next _ArrayDisplay($CmdLine) Else #include <ButtonConstants.au3> #include <GUIConstantsEx.au3> #include <WindowsConstants.au3> $Form1 = GUICreate("Form1", 239, 65, 192, 124) $Button1 = GUICtrlCreateButton("Button1", 32, 24, 75, 25) $Button2 = GUICtrlCreateButton("Button2", 120, 24, 75, 25) GUISetState(@SW_SHOW) While 1 $nMsg = GUIGetMsg() Switch $nMsg Case $GUI_EVENT_CLOSE Exit Case $Button1 button1() Case $button2 button2() EndSwitch WEnd EndIf EndIf Exit Func button1() MsgBox(16, "title", "button1()!") EndFunc Func button2() MsgBox(16, "title", "button1()!") EndFunc Func ProcessGetParent($i_PID) ;get PID from parent process done by SmOke_N Local $TH32CS_SNAPPROCESS = 0x00000002 Local $a_tool_help = DllCall("Kernel32.dll", "long", "CreateToolhelp32Snapshot", "int", $TH32CS_SNAPPROCESS, "int", 0) If IsArray($a_tool_help) = 0 Or $a_tool_help[0] = -1 Then Return SetError(1, 0, $i_PID) Local $tagPROCESSENTRY32 = _ DllStructCreate( _ "dword dwsize;" & _ "dword cntUsage;" & _ "dword th32ProcessID;" & _ "uint th32DefaultHeapID;" & _ "dword th32ModuleID;" & _ "dword cntThreads;" & _ "dword th32ParentProcessID;" & _ "long pcPriClassBase;" & _ "dword dwFlags;" & _ "char szExeFile[260]" _ ) DllStructSetData($tagPROCESSENTRY32, 1, DllStructGetSize($tagPROCESSENTRY32)) Local $p_PROCESSENTRY32 = DllStructGetPtr($tagPROCESSENTRY32) Local $a_pfirst = DllCall("Kernel32.dll", "int", "Process32First", "long", $a_tool_help[0], "ptr", $p_PROCESSENTRY32) If IsArray($a_pfirst) = 0 Then Return SetError(2, 0, $i_PID) Local $a_pnext, $i_return = 0 If DllStructGetData($tagPROCESSENTRY32, "th32ProcessID") = $i_PID Then $i_return = DllStructGetData($tagPROCESSENTRY32, "th32ParentProcessID") DllCall("Kernel32.dll", "int", "CloseHandle", "long", $a_tool_help[0]) If $i_return Then Return $i_return Return $i_PID EndIf While @error = 0 $a_pnext = DllCall("Kernel32.dll", "int", "Process32Next", "long", $a_tool_help[0], "ptr", $p_PROCESSENTRY32) If DllStructGetData($tagPROCESSENTRY32, "th32ProcessID") = $i_PID Then $i_return = DllStructGetData($tagPROCESSENTRY32, "th32ParentProcessID") If $i_return Then ExitLoop $i_return = $i_PID ExitLoop EndIf WEnd DllCall("Kernel32.dll", "int", "CloseHandle", "long", $a_tool_help[0]) Return $i_return EndFunc ;==>ProcessGetParent  
    • Jibberish
      By Jibberish
      Hello,
      I have used this forum to get help many times. I thought it was time to (hopefully) help others.
      I created a script that reads a text file with a list of videos, displays the videos in a GUI with checkboxes next to the names, and displays the selected videos. This will become a part of a larger script I am creating to test a video player. The tough part for me was creating the GUI and Dynamic list of videos. I had a lot of trouble finding samples to help me, but finally found one  written by Melba23. The link is in the code, so he gets credit for helping! I also have not used arrays much and they are very picky about looping through the arrays without getting the dreaded error " Array variable has incorrect number of subscripts or subscript dimension range exceeded."  However diligence paid off!
      To run this code, take the video names commented below and create a videos.txt file in your script execution directory. You can put however many video names in this list. Thus the dynamic features of the code.
      Cheers!
      Jibberish
      #include <MsgBoxConstants.au3> #include <StringConstants.au3> #include <array.au3> #include <File.au3> #include <GUIConstantsEx.au3> Local $sMediaFile = @ScriptDir & "\videos.txt" ;~ Videos in videos.txt are: ;~ bbb_1080_60s.mp4 ;~ bbb_1080_60s_1.mp4 ;~ bbb_1080_60s_2.mp4 ;~ tos_4K_60s_HEVC.mp4 ;~ tos_4K_60s_HEVC_1.mp4 ;~ tos_4K_60s_HEVC_2.mp4 ;~ ;~ Additional videos can be added to this list. The functions are Dynamic. Dim $aMediaManifest Local $aArrayFile Local $aVideos Local $sVideoName Local $i ; MAIN ; Put the Video File Names into an Array _FileReadToArray($sMediaFile, $aArrayFile) Local $iVideoCount = UBound($aArrayFile) -1 ; Get the number of videos - 1 to prevent errors _ArrayDelete($aArrayFile, 0) ;Counter just gets in the way ; Move backwards through the array deleting the blank lines For $i = $iVideoCount - 1 To 0 Step -1 If $aArrayFile[$i] = "" Then _ArrayDelete($aArrayFile, $i) EndIf Next $aVideos = DisplayVideos($aArrayFile) $iVideoCount = UBound($aArrayFile) -1 _ArrayDisplay($aVideos) ; Display the checked videos ;~ End of MAIN ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ; ; GUI to display Videos in checkboxes ; ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Func DisplayVideos($aArrayFile) Local $iTop = -1 Local $iLeft = -1 Local $iWidth Local $iHeight = $iVideoCount * 30 Local $iL = 10 Local $iT = 10 Local $iRow = 0 Local $aVideo Local $iA = 0, $iB = 0 Local $sFill = "" $iMMCount = UBound($aArrayFile) $iMMNewCount = $iMMCount - 1 Local $aGUICheckbox[$iMMCount] Local $aCheckedVideos[$iMMCount] ; Put the Video File Names into an Array $hGUI = GUICreate("Video Checkbox", $iLeft, $iTop, $iWidth, $iHeight) GUICtrlCreateLabel("Videos", 180, $iT) $iT = $iT + 30 ; This is a great example of using arrays to create GUI check boxes or radio buttons For $i = 0 To $iMMNewCount Step 1 $sMP4Text = $aArrayFile[$i] $aGUICheckbox[$i] = GUICtrlCreateCheckbox($sMP4Text, 30, $iT) $iT += 30 Next $idClose1 = GUICtrlCreateButton("Start", $iL, $iT) GUISetState(@SW_SHOW) ; This section reads the checkboxes and puts the video names in an array in their original position ; in case this is important (as it is to me) ; This was the toughest part to code, and I found no samples online until I saw Melba23's sample here: ; https://www.autoitscript.com/forum/topic/119843-dynamic-gui-problem/#comment-832672 ; I got this working with only a little modification. THANK YOU MELBA23 While 1 $nMsg = GUIGetMsg() Switch $nMsg Case $GUI_EVENT_CLOSE Exit Case $idClose1 For $i = 0 To $iMMNewCount Step 1 Switch GUICtrlRead($aGUICheckbox[$i]) Case $GUI_CHECKED $aCheckedVideos[$i] = $aArrayFile[$i] Case $GUI_UNCHECKED EndSwitch Next ExitLoop EndSwitch WEnd GUIDelete($hGUI) Return $aCheckedVideos EndFunc ;==>DisplayVideos