Labelled Scale Widget with Toggled Keyboard Entry

WJG March 16th 2005 - If scale widgets are crammed together or have a restricted area on screen then offering the user the option to enter numbers directly through the keyboard can be an advantage. This may be especially true if the user has some specific number in mind and dragging sliders can be, well, a drag. For those crafting a GUI with lots of sliders crammed into a tight space then this code snippet might be of use.

The code is pretty-simple and needs no clarification. Regarding the bindings, perform a simple Double-Button-1 click on the slider title and a entry field will appear displaying the current slider value. Pressing Return in the entry field or a further Double-Button-1 click on the title to restore the original scale widget.

User modifications might include deleting the Entry widget <Return> binding to offer the bare choice between input methods and provide additional variable handling code to allow doubles to be managed through the slider (it defaults to integers}.


 package require Tk
 proc newscale {w title variable from to} {
    #define this proc first to prevent interpreter error
    proc _[set w].ent {val} {
        return [expr {[string is integer $val] || [string match {[-+]} $val]} ]
    }
    #create widget holder
    frame $w -borderwidth 2 -relief ridge -height 10
    label $w.lab -text $title -anchor w
    pack $w.lab -side left
    #alternative kb entry method
    entry $w.ent -textvariable $variable -validate all -vcmd "_$w.ent %P"
    #interactive slider with value
    frame $w.fr2 
    label $w.fr2.lab -textvariable $variable -anchor w -width 3
    pack $w.fr2.lab -side left
    scale $w.fr2.sc -orient horizontal -borderwidth 1 \
            -showvalue 0 -variable $variable \
            -width 8 -sliderlength 10 \
            -from $from -to $to
    pack $w.fr2.sc -side left 
    pack $w.fr2 -side left
    #bindings
    bind $w.lab <Double-Button-1> {
        if {[winfo ismapped [winfo parent %W].fr2.sc] } {
            [winfo parent %W].ent delete 0 end
            [winfo parent %W].ent insert 0 [[winfo parent %W].fr2.lab cget -text ]
            pack [winfo parent %W].ent
            focus [winfo parent %W].ent
            pack forget [winfo parent %W].fr2
        } else  {
            #set global variable value
            pack [winfo parent %W].fr2
            pack forget [winfo parent %W].ent -fill x
        }
    }
    bind $w.ent <Return> {
        pack forget %W
        pack [winfo parent %W].fr2  
    }
 }
 proc demo {} { 
    set w .fr1
    set ::v(1) -33
    set ::v(2) 0
    set ::v(3) 33
    newscale .sc1 {Range1: } ::v(1) -100 100
    newscale .sc2 {Range2: } ::v(2) -100 100
    newscale .sc3 {Range3: } ::v(3) -100 100
    pack .sc1 .sc2 .sc3 -side top
 }
 demo