Integer entry validation with range check

A few general aspects are discussed in entry validation.

See Also

string is
Provides isdecimal and isnumeric.

Description

Rolf Schroedter, using hints given by Jeffrey Hobbs and DKF:

The validation is performed at two levels:

1. Weak validation:

   - Allow empty string or sign only
   - Verify sign

2. Strong validation:

   - Strong integer and range check
   - Change the entry background color to yellow/red on error

The "focusout" validation is optional, it can be removed.

Sample code:

proc validInteger {win event X oldX min max} {
    # Make sure min<=max
    if {$min > $max} {
        set tmp $min; set min $max; set max $tmp
    }
    # Allow valid integers, empty strings, sign without number
    # Reject Octal numbers, but allow a single "0"
    # Which signes are allowed ?
    if {($min <= 0) && ($max >= 0)} {   ;# positive & negative sign
        set pattern {^[+-]?(()|0|([1-9][0-9]*))$}
    } elseif {$max < 0} {               ;# negative sign
        set pattern {^[-]?(()|0|([1-9][0-9]*))$}
    } else {                            ;# positive sign
        set pattern {^[+]?(()|0|([1-9][0-9]*))$}
    }
    # Weak integer checking: allow empty string, empty sign, reject octals
    set weakCheck [regexp $pattern $X]
    # if weak check fails, continue with old value
    if {! $weakCheck} {set X $oldX}
    # Strong integer checking with range
    set strongCheck [expr {[string is int -strict $X] && ($X >= $min) && ($X <= $max)}]

    switch $event {
        key {
            $win configure -bg [expr {$strongCheck ? {white} : {yellow}}]
            return $weakCheck
        }
        focusout {
            if {! $strongCheck} {$win configure -bg red}
            return $strongCheck
        }
        default {
            return 1
        }
    }
}

proc checkForm {args} {
    set err 0
    foreach win $args {
        switch -- [$win cget -bg] {
            red -
            yellow {
                incr err
            }
        }
    }
    if {$err} {
        tk_messageBox -type ok -icon error -message "Check $err invalid field(s)"
    } else {
        tk_messageBox -type ok -message {Changes applied successfully}
    }
}

set x1 12; set x2 345; set x3 -678

label .t1 -text {int (10..92)}
entry .e1  -textvariable x1 -validate all -vcmd {validInteger %W %V %P %s +10 +92}
label .t2 -text {int (-256..+1024)}
entry .e2 -textvariable x2 -validate all -vcmd {validInteger %W %V %P %s +1024 -256}
label .t3 -text {int (-768..0)}
entry .e3 -textvariable x3 -validate all -vcmd {validInteger %W %V %P %s -768 0}

button .apply -text {Apply changes} -command {checkForm .e1 .e2 .e3}

grid .t1 .e1 -sticky w
grid .t2 .e2 -sticky w
grid .t3 .e3 -sticky w
grid .apply - -sticky ew

RJM: The textvariable can still have invalid values, so this example may use the textvariable only as a temporary value, which would be copied in other variables after hitting the "Apply changes" button.

Sometimes however it is useful to have the -vcmd also update another data representation during the alteration of the value (e.g. while spinning a spinbox). This may be desired when a graphical representation of the value must be updated simultaneously. Moreover, a dual variable concept complicates entry or spinbox updates when associated variables are updated anywhere else.

The following example not only checks limits, but also restricts to limit during entry. This is suboptimal and quite dangerous (the entry manual warns in this respect). Actually, upon autolimitation, the -vcmd is called again after the textvariable's alteration is recognized in the Tk event loop.

 ..... -vcmd "after idle {%W config -validate %v}; validate_time $index %P"
proc validate_time {index value} {
    global sonopar

    if {![string is double $value]} {return 0}
    if {!$sonopar(update)} {
        after idle graphupdate . ;# ensure update AFTER validation accepted by spinbox widget
    }
    if {$value < 0.2} {set sonopar(time,$index) 0.2; return 0}
    if {$value > 25.5} {set sonopar(time,$index) 25.5; return 0}
    return 1
}

Of course, extensions in the sense of background colouring and others may be considered.