• Entry widget with range check

    From snosniv@21:1/5 to All on Wed Mar 8 03:18:12 2023
    I currently have thus simple proc to check entry widgets are valid:

    ##----------------------------------------------------------------------------- ## Check the number in an entry widget is an integer ##------------------------------------------------------------------------------
    proc ValidInt {val} {
    return [expr {[string is integer $val]
    || [string match {[-+]} $val]} ]
    } ##-----------------------------------------------------------------------------

    However, I'd like to also validate the value is within a range,
    so can I do something like:

    proc ValidInt {val min max} {
    return [expr {[string is integer $val] && ($val >= $min) && ($val <= $max)
    || [string match {[-+]} $val]} ]
    }

    But that isn't quite working. :-(

    TIA, KevP.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From snosniv@21:1/5 to snosniv on Wed Mar 8 05:33:09 2023
    On Wednesday, March 8, 2023 at 11:18:14 AM UTC, snosniv wrote:
    I currently have thus simple proc to check entry widgets are valid:

    ##-----------------------------------------------------------------------------
    ## Check the number in an entry widget is an integer ##------------------------------------------------------------------------------
    proc ValidInt {val} {
    return [expr {[string is integer $val]
    || [string match {[-+]} $val]} ]
    } ##-----------------------------------------------------------------------------

    However, I'd like to also validate the value is within a range,
    so can I do something like:

    proc ValidInt {val min max} {
    return [expr {[string is integer $val] && ($val >= $min) && ($val <= $max) || [string match {[-+]} $val]} ]
    }

    But that isn't quite working. :-(

    TIA, KevP.

    Perhaps I should add that all values will be positive, (maybe 0 or blank too). I'm checking mins & secs amongst others, so secs will be either blank (I already check that & assign 0) or between 0 & 59,
    and mins will be between 0 & 99 (also checked for blank & assigned 0). (e.g. I could have 2 mins & blank secs, or blank mins & 15 secs).
    Other entry widgets will have different limits.

    KevP.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Robert Heller@21:1/5 to snosniv on Wed Mar 8 13:54:28 2023
    At Wed, 8 Mar 2023 05:33:09 -0800 (PST) snosniv <[email protected]> wrote:


    On Wednesday, March 8, 2023 at 11:18:14 AM UTC, snosniv wrote:
    I currently have thus simple proc to check entry widgets are valid:

    ##-----------------------------------------------------------------------------
    ## Check the number in an entry widget is an integer ##------------------------------------------------------------------------------
    proc ValidInt {val} {
    return [expr {[string is integer $val]
    || [string match {[-+]} $val]} ]
    } ##-----------------------------------------------------------------------------

    However, I'd like to also validate the value is within a range,
    so can I do something like:

    proc ValidInt {val min max} {
    return [expr {[string is integer $val] && ($val >= $min) && ($val <= $max) || [string match {[-+]} $val]} ]
    }

    But that isn't quite working. :-(

    TIA, KevP.

    Perhaps I should add that all values will be positive, (maybe 0 or blank too).
    I'm checking mins & secs amongst others, so secs will be either blank (I already check that & assign 0) or between 0 & 59,
    and mins will be between 0 & 99 (also checked for blank & assigned 0). (e.g. I could have 2 mins & blank secs, or blank mins & 15 secs).
    Other entry widgets will have different limits.

    KevP.

    If the "entry" is always going to be an integer (or blank==0), then maybe
    using a SpinBox (ttk::spinbox) might be a better choice. All of the data validation is implicit. (The spinbox also has incr/decr arrows on it, which
    is a UI convenience.)




    --
    Robert Heller -- Cell: 413-658-7953 GV: 978-633-5364
    Deepwoods Software -- Custom Software Services
    http://www.deepsoft.com/ -- Linux Administration Services
    [email protected] -- Webhosting Services

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From snosniv@21:1/5 to Robert Heller on Wed Mar 8 05:59:06 2023
    On Wednesday, 8 March 2023 at 13:54:40 UTC, Robert Heller wrote:
    At Wed, 8 Mar 2023 05:33:09 -0800 (PST) snosniv <[email protected]> wrote:

    On Wednesday, March 8, 2023 at 11:18:14 AM UTC, snosniv wrote:
    I currently have thus simple proc to check entry widgets are valid:

    ##-----------------------------------------------------------------------------
    ## Check the number in an entry widget is an integer ##------------------------------------------------------------------------------
    proc ValidInt {val} {
    return [expr {[string is integer $val]
    || [string match {[-+]} $val]} ]
    } ##-----------------------------------------------------------------------------

    However, I'd like to also validate the value is within a range,
    so can I do something like:

    proc ValidInt {val min max} {
    return [expr {[string is integer $val] && ($val >= $min) && ($val <= $max)
    || [string match {[-+]} $val]} ]
    }

    But that isn't quite working. :-(

    TIA, KevP.

    Perhaps I should add that all values will be positive, (maybe 0 or blank too).
    I'm checking mins & secs amongst others, so secs will be either blank (I already check that & assign 0) or between 0 & 59,
    and mins will be between 0 & 99 (also checked for blank & assigned 0). (e.g. I could have 2 mins & blank secs, or blank mins & 15 secs).
    Other entry widgets will have different limits.

    KevP.
    If the "entry" is always going to be an integer (or blank==0), then maybe using a SpinBox (ttk::spinbox) might be a better choice. All of the data validation is implicit. (The spinbox also has incr/decr arrows on it, which is a UI convenience.)




    --
    Robert Heller -- Cell: 413-658-7953 GV: 978-633-5364
    Deepwoods Software -- Custom Software Services
    http://www.deepsoft.com/ -- Linux Administration Services [email protected] -- Webhosting Services

    I had considered that, but think mu GUI may get unwieldly, but I'll have a go & see how it feels, thanks.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Rich@21:1/5 to snosniv on Wed Mar 8 14:47:29 2023
    snosniv <[email protected]> wrote:
    I currently have thus simple proc to check entry widgets are valid:

    ##-----------------------------------------------------------------------------
    ## Check the number in an entry widget is an integer ##------------------------------------------------------------------------------
    proc ValidInt {val} {
    return [expr {[string is integer $val]
    || [string match {[-+]} $val]} ]
    } ##-----------------------------------------------------------------------------

    However, I'd like to also validate the value is within a range,
    so can I do something like:

    proc ValidInt {val min max} {
    return [expr {[string is integer $val] && ($val >= $min) && ($val <= $max)
    || [string match {[-+]} $val]} ]
    }

    But that isn't quite working. :-(

    TIA, KevP.

    If you read the [expr] man page, you'll see that the operators are
    "grouped in decreasing order of precedence" therein. Reading further,
    you will see that logical and (&&) has higher precedence than logical
    or (||).

    Your long comparison is (B as a standin for "boolean"):

    B && B && B || B

    Due to the precedence rules, this is effectively:

    ( B && B && B ) || B

    Which means any time the boolean on the other side of the or is true,
    the entire statement is true, regardless of what the and'ed booleans
    produce.

    Next, instrumenting your proc is a very useful technique for debugging
    these sorts of issues. Do this, as a test:

    proc ValidInt {val min max} {
    puts "string is integer $val: [set b1 [string is integer $val]]"
    puts "($val >= $min): [set b2 [expr {($val >= $min)}]]"
    puts "($val <= $max): [set b3 [expr {($val <= $max)}]]"
    puts "string match {\[-+]} $val: [set b4 [string match {[-+]} $val]]"
    puts "combined clause: $b1 && $b2 && $b3 || $b4"
    return [expr {[string is integer $val] && ($val >= $min) && ($val <= $max) || [string match {[-+]} $val]} ]
    }

    And then you can run tests to check the result, *and* see the
    incremental buildup of the result inside the proc. And seeing that
    incremental buildup is often key to recognizing why "it does not work":

    % ValidInt 0 1 2
    string is integer 0: 1
    (0 >= 1): 0
    (0 <= 2): 1
    string match {[-+]} 0: 0
    combined clause: 1 && 0 && 1 || 0
    0

    Works

    % ValidInt 5 1 2
    string is integer 5: 1
    (5 >= 1): 1
    (5 <= 2): 0
    string match {[-+]} 5: 0
    combined clause: 1 && 1 && 0 || 0
    0

    Works

    % ValidInt - 1 2
    string is integer -: 0
    (- >= 1): 0
    (- <= 2): 1
    string match {[-+]} -: 1
    combined clause: 0 && 0 && 1 || 1
    1

    Presumably this fails (you did not give an example that failed, so I
    had to hunt one down that presumably is a failure, and most of us would
    not consider a lone "-" to be a valid number).

    Why did it fail? Because the last, logical or, string match returned
    true, and because of precedence, returned true for the entire
    statement.

    What you need to do is check first for "is integer", and only if "is
    integer" passes, then check for <= and >=, i.e.:

    return [expr {[string is integer $val] && ( ($val >= $min) && ($val <= $max) )}]

    The reason is that <= and >= are dual use. They prefer numeric
    comparisons, but fall back to string comparisons, and so - <= 2 becomes
    "-" <= "2" (strings) because - is not an integer. So you want to
    require that "is integer" be true before performing either range check.
    Nesting the range checks inside parenthesis and and'ing that to 'is
    integer' sets the statement up so that 'is integer' must be true before
    any of the range checks are tested.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From snosniv@21:1/5 to Rich on Thu Mar 9 02:06:40 2023
    On Wednesday, 8 March 2023 at 14:47:33 UTC, Rich wrote:
    snosniv <[email protected]> wrote:
    I currently have thus simple proc to check entry widgets are valid:

    ##-----------------------------------------------------------------------------
    ## Check the number in an entry widget is an integer ##------------------------------------------------------------------------------
    proc ValidInt {val} {
    return [expr {[string is integer $val]
    || [string match {[-+]} $val]} ]
    } ##-----------------------------------------------------------------------------

    However, I'd like to also validate the value is within a range,
    so can I do something like:

    proc ValidInt {val min max} {
    return [expr {[string is integer $val] && ($val >= $min) && ($val <= $max) || [string match {[-+]} $val]} ]
    }

    But that isn't quite working. :-(

    TIA, KevP.
    If you read the [expr] man page, you'll see that the operators are
    "grouped in decreasing order of precedence" therein. Reading further,
    you will see that logical and (&&) has higher precedence than logical
    or (||).

    Your long comparison is (B as a standin for "boolean"):

    B && B && B || B

    Due to the precedence rules, this is effectively:

    ( B && B && B ) || B

    Which means any time the boolean on the other side of the or is true,
    the entire statement is true, regardless of what the and'ed booleans
    produce.

    Next, instrumenting your proc is a very useful technique for debugging
    these sorts of issues. Do this, as a test:
    proc ValidInt {val min max} {
    puts "string is integer $val: [set b1 [string is integer $val]]"
    puts "($val >= $min): [set b2 [expr {($val >= $min)}]]"
    puts "($val <= $max): [set b3 [expr {($val <= $max)}]]"
    puts "string match {\[-+]} $val: [set b4 [string match {[-+]} $val]]"
    puts "combined clause: $b1 && $b2 && $b3 || $b4"
    return [expr {[string is integer $val] && ($val >= $min) && ($val <= $max) || [string match {[-+]} $val]} ]
    }
    And then you can run tests to check the result, *and* see the
    incremental buildup of the result inside the proc. And seeing that incremental buildup is often key to recognizing why "it does not work":

    % ValidInt 0 1 2
    string is integer 0: 1
    (0 >= 1): 0
    (0 <= 2): 1
    string match {[-+]} 0: 0
    combined clause: 1 && 0 && 1 || 0
    0

    Works

    % ValidInt 5 1 2
    string is integer 5: 1
    (5 >= 1): 1
    (5 <= 2): 0
    string match {[-+]} 5: 0
    combined clause: 1 && 1 && 0 || 0
    0

    Works

    % ValidInt - 1 2
    string is integer -: 0
    (- >= 1): 0
    (- <= 2): 1
    string match {[-+]} -: 1
    combined clause: 0 && 0 && 1 || 1
    1

    Presumably this fails (you did not give an example that failed, so I
    had to hunt one down that presumably is a failure, and most of us would
    not consider a lone "-" to be a valid number).

    Why did it fail? Because the last, logical or, string match returned
    true, and because of precedence, returned true for the entire
    statement.

    What you need to do is check first for "is integer", and only if "is
    integer" passes, then check for <= and >=, i.e.:

    return [expr {[string is integer $val] && ( ($val >= $min) && ($val <= $max) )}]

    The reason is that <= and >= are dual use. They prefer numeric
    comparisons, but fall back to string comparisons, and so - <= 2 becomes
    "-" <= "2" (strings) because - is not an integer. So you want to
    require that "is integer" be true before performing either range check. Nesting the range checks inside parenthesis and and'ing that to 'is
    integer' sets the statement up so that 'is integer' must be true before
    any of the range checks are tested.

    Still having issues with this. :-(

    I tried:
    ##-----------------------------------------------------------------------------
    proc ValidIntRng {val min max} {
    return [expr {[string is integer $val] && ( ($val >= $min) && ($val <= $max) )}]
    } ##-----------------------------------------------------------------------------

    But with this, when I type a 2 digit value, I cannot delete the first digit, only the second?
    This seems odd behaviour!

    As for spinbox idea, I thought that would be better perhaps, but that lets me enter float numbers,
    I want to restrict to integers (I did try -format %2d, but no good).

    Kev P.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Rich@21:1/5 to snosniv on Thu Mar 9 16:18:49 2023
    snosniv <[email protected]> wrote:
    Still having issues with this. :-(

    I tried: ##-----------------------------------------------------------------------------
    proc ValidIntRng {val min max} {
    return [expr {[string is integer $val] && ( ($val >= $min) && ($val <= $max) )}]
    } ##-----------------------------------------------------------------------------

    But with this, when I type a 2 digit value, I cannot delete the first
    digit, only the second? This seems odd behaviour!

    Please provide a minimal and *complete* code sample.

    I.e., just enough wrapper code to create an entry and to show how you
    are connnecting the above to the entry.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From snosniv@21:1/5 to Rich on Thu Mar 9 13:56:06 2023
    On Thursday, 9 March 2023 at 16:18:53 UTC, Rich wrote:
    snosniv <[email protected]> wrote:
    Still having issues with this. :-(

    I tried: ##-----------------------------------------------------------------------------
    proc ValidIntRng {val min max} {
    return [expr {[string is integer $val] && ( ($val >= $min) && ($val <= $max) )}]
    } ##-----------------------------------------------------------------------------

    But with this, when I type a 2 digit value, I cannot delete the first digit, only the second? This seems odd behaviour!
    Please provide a minimal and *complete* code sample.

    I.e., just enough wrapper code to create an entry and to show how you
    are connnecting the above to the entry.

    Here's a quick knock up demonstrating what I see: (doesn't matter if I use the commented out proc, same result).

    ## Sample code showing that entered 1st digit won't delete.
    entry .enMINS -textvar warm_mins -width 2 -validate all -vcmd {ValidIntRng %P 0 99}
    grid .enMINS -row 2 -column 1
    proc ValidIntRng {val min max} {
    return [expr {[string is integer $val] && (($val >= $min) && ($val <= $max))}]
    }

    #proc ValidIntRng {val min max} {
    # puts "string is integer $val: [set b1 [string is integer $val]]"
    # puts "($val >= $min): [set b2 [expr {($val >= $min)}]]"
    # puts "($val <= $max): [set b3 [expr {($val <= $max)}]]"
    # puts "string match {\[-+]} $val: [set b4 [string match {[-+]} $val]]"
    # puts "combined clause: $b1 && $b2 && $b3 || $b4"
    # return [expr {([string is integer $val] && (($val >= $min) && ($val <= $max))) || [string match {[-+]} $val]} ]
    #}

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From saitology9@21:1/5 to snosniv on Thu Mar 9 17:53:15 2023
    On 3/9/2023 4:56 PM, snosniv wrote:

    ## Sample code showing that entered 1st digit won't delete.
    entry .enMINS -textvar warm_mins -width 2 -validate all -vcmd {ValidIntRng %P 0 99}
    grid .enMINS -row 2 -column 1
    proc ValidIntRng {val min max} {
    return [expr {[string is integer $val] && (($val >= $min) && ($val <= $max))}]
    }

    Just looking at this, if you want to be able to delete all characters in
    the entry widget, you will need to adjust the validator accordingly to
    accept empty input:


    proc ValidIntRng {val min max} {
    expr {($val eq "") || ([string is integer $val] && ($val >= $min)
    && ($val <= $max))}
    }

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From snosniv@21:1/5 to All on Fri Mar 10 03:03:22 2023
    On Thursday, 9 March 2023 at 22:53:21 UTC, saitology9 wrote:
    On 3/9/2023 4:56 PM, snosniv wrote:

    ## Sample code showing that entered 1st digit won't delete.
    entry .enMINS -textvar warm_mins -width 2 -validate all -vcmd {ValidIntRng %P 0 99}
    grid .enMINS -row 2 -column 1
    proc ValidIntRng {val min max} {
    return [expr {[string is integer $val] && (($val >= $min) && ($val <= $max))}]
    }
    Just looking at this, if you want to be able to delete all characters in
    the entry widget, you will need to adjust the validator accordingly to
    accept empty input:
    proc ValidIntRng {val min max} {
    expr {($val eq "") || ([string is integer $val] && ($val >= $min)
    && ($val <= $max))}
    }

    Thanks, simple when you know how.
    I'm only an occasional "dabbler", learn stuff, then forget it again a year or so later. :-(

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)