JOB - 2017-02-02 20:36:26
If someone is missing the -resolution option for the ttk::scale widget...
I also noticed that there is a small misbehavior in the ttk::scale binding declarations (tcl/tk8.6). See extra binding in the code below (Build method) which fixes this issue.
In the hope, that it will be useful, here is the code:
# ----------------------------------------------------------------------------- # xscale.tcl --- # ----------------------------------------------------------------------------- # (c) 2017, Johann Oberdorfer - Engineering Support | CAD | Software # johann.oberdorfer [at] gmail.com # www.johann-oberdorfer.eu # ----------------------------------------------------------------------------- # This source file is distributed under the BSD license. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the BSD License for more details. # ----------------------------------------------------------------------------- # Purpose: # A TclOO class template to extend ttk::scale functionality. # Same behavior as tk::scale widget, # implements -resolution option. # ----------------------------------------------------------------------------- # https://wiki.tcl-lang.org/40210 # derived from ttk::scale, implements -resolution found in older scale package provide xscale 0.1 namespace eval xscale { variable cnt 0 # this is a tk-like wrapper around my... class so that # object creation works like other tk widgets proc xscale {path args} { variable cnt incr cnt set obj [XScaleClass create tmp${cnt} $path {*}$args] # rename oldName newName rename $obj ::$path return $path } oo::class create XScaleClass { constructor { path args } { my variable widgetOptions my variable oldval my variable label_txt set label_txt "" array set widgetOptions { -resolution 1.0 -command "" -showvalue 0 -compound "right" } # incorporate arguments to local widget options array set widgetOptions $args # we use a frame for this specific widget class set f [ttk::frame $path -class xscale] # we must rename the widget command # since it clashes with the object being created set widget ${path}_ my Build $f rename $path $widget my configure {*}$args } # add a destructor to clean up the widget destructor { set w [namespace tail [self]] catch {bind $w <Destroy> {}} catch {destroy $w} } method cget { {opt "" } } { my variable scalewidget my variable widgetOptions if { [string length $opt] == 0 } { return [array get widgetOptions] } if { [info exists widgetOptions($opt) ] } { return $widgetOptions($opt) } return [$scalewidget cget $opt] } method configure { args } { my variable scalewidget my variable widgetOptions my variable label_txt if {[llength $args] == 0} { # return all tablelist options set opt_list [$scalewidget configure] # as well as all custom options foreach xopt [array get widgetOptions] { lappend opt_list $xopt } return $opt_list } elseif {[llength $args] == 1} { # return configuration value for this option set opt $args if { [info exists widgetOptions($opt) ] } { return $widgetOptions($opt) } return [$scalewidget cget $opt] } # error checking if {[expr {[llength $args]%2}] == 1} { return -code error "value for \"[lindex $args end]\" missing" } # process the new configuration options... array set opts $args foreach opt_name [array names opts] { set opt_value $opts($opt_name) # overwrite with new value if { [info exists widgetOptions($opt_name)] } { set widgetOptions($opt_name) $opt_value } # some options need action from the widgets side switch -- $opt_name { -resolution { set widgetOptions(-resolution) $opt_value } -variable { my SetVariable $opt_value my ShowValue } -command { # not allowed to overwrite our own command # procedure as it triggers the "hopping" behavior # use the variable to get the actual scale value set cmd $opt_value append cmd "; [namespace code {my ResolutionCmd}]" $scalewidget configure -command $cmd } -showvalue { # immediately show or hide the actual value... set widgetOptions(-showvalue) $opt_value my ShowValue } -value { # overwrite existing option! return -code error \ "option -value is not supported, use -variable instead!" } -compound { # static declaration for the moment } default { # ------------------------------------------------------- # if the configure option wasn't one of our special one's, # pass control over to the original ttk::scale widget # ------------------------------------------------------- # puts ">>> $opt_name : $opt_value" if {[catch {$scalewidget configure $opt_name $opt_value} result]} { return -code error $result } } } } } # -------------------------------------------------- # if the command wasn't one of our special one's, # pass control over to the original tablelist widget # -------------------------------------------------- method unknown {method args} { my variable scalewidget if {[catch {$scalewidget $method {*}$args} result]} { return -code error $result } return $result } method ShowValue { } { my variable scalewidget my variable widgetOptions my variable label_txt if {$widgetOptions(-showvalue) == 0} { set label_txt "" } else { set label_txt [$scalewidget cget -value] } } method SetVariable { varname } { my variable scalewidget my variable widgetOptions set widgetOptions(-variable) $varname $scalewidget configure -variable $varname if { $varname ne {} } { upvar #0 $varname tracevar if { ![info exists tracevar] } { set tracevar [$scalewidget cget -from] } set oldval $tracevar } } method ResolutionCmd { val } { my variable widgetOptions my variable oldval my variable label_txt # round value to nearest multiple of resolution set res $widgetOptions(-resolution) set hopval [expr {$res * floor(double($val) / $res + 0.5)}] if { $widgetOptions(-variable) ne {} } { upvar #0 $widgetOptions(-variable) var set var $hopval } # run callback as in standard scale # only for a different value == integer hop if { $hopval != $oldval } { set oldval $hopval if { $widgetOptions(-command) ne {} } { set command_with_value [linsert $widgetOptions(-command) end $hopval] uplevel #0 $command_with_value } } # round the return value ! set hopval [expr {double(round(100*$hopval))/100}] # puts "hopval: $hopval" if {$widgetOptions(-showvalue) == 1} { set label_txt $hopval } return $hopval } method Build {win} { my variable scalewidget my variable widgetOptions my variable oldval my variable label_txt ttk::label $win.lbl \ -textvariable "[namespace current]::label_txt" ttk::scale $win.sc \ -command "[namespace code {my ResolutionCmd}]" # compound left: # pack $win.lbl -side left # pack $win.sc -side right -fill x -expand true # compound right (default) pack $win.sc -side left -fill x -expand true pack $win.lbl -side right set scalewidget $win.sc set oldval [$scalewidget cget -value] # need to overwrite the ttk binding: # note: # the original bindings in the tcl distribution # uses "Press" instead of "Jump" which, when clicking # with the mouse, has no effect! # bind TScale <ButtonPress-1> { ttk::scale::Jump %W %x %y } } } }
xcale_test.tcl
# # xscale test # set dir [file dirname [info script]] set auto_path [linsert $auto_path 0 [file join $dir "."]] package require Tk package require xscale 0.1 # ---test --- catch {console show} proc echo1 {args} { global test1 puts "echo1: $test1" } proc echo2 {args} { puts "echo2: $args" } set top [toplevel .test] $top configure -bg [ttk::style lookup TLabel -background] wm withdraw . wm geometry $top "400x200" # --- S C A L E label $top.scale -text "tk::scale:" pack $top.scale set test1 8 scale $top.scl \ -orient "horizontal" \ -from -10 -to 10 -resolution 2.0 \ -variable test1 \ -command echo1 \ -showvalue 1 pack $top.scl -expand yes -fill x # --- TTK :: S C A L E label $top.ttkscale -text "ttk::scale:" pack $top.ttkscale set test2 8 ttk::scale $top.scl0 \ -from -10 -to 10 \ -variable test2 \ -command echo2 \ -value 8 pack $top.scl0 -expand yes -fill x # --- X S C A L E label $top.xscale -text "xscale:" pack $top.xscale set test3 8 xscale::xscale $top.scl1 \ -from -10 \ -to 10 \ -resolution 2.0 \ -variable test3 \ -command echo2 \ -showvalue 1 \ -compound "right" pack $top.scl1 -expand yes -fill x set test4 10 xscale::xscale $top.scl2 \ -from 0 -to 40 -resolution 2.0 \ -variable test4 \ -command echo2 \ -showvalue 1 pack $top.scl2 -expand yes -fill x