The Min Max Scale Example

   

Practical Programming in Tcl & Tk, Third Edition
By Brent B. Welch

Table of Contents
Chapter 34.  The Canvas Widget


This section presents Example 34-3 that constructs a scale-like object with two sliders. The sliders represent the minimum and maximum values for some parameter. Clearly, the minimum cannot be greater than the maximum, and vice versa. The example creates three rectangles on the canvas. One rectangle forms the long axis of the slider that represents the range of possible values. The other two rectangles are markers that represent the values. Two text objects float below the markers to give the current values of the minimum and maximum.

The example introduces four canvas operations: bbox, coords, scale, and move. The bbox operation returns the bounding box of an object or of all objects with a given tag. The coords operation sets or queries the coordinates of an object. The scale operation stretches an object, and the move operation translates the position of an object.

graphics/tip_icon.gif

Use tags instead of object IDs.


Many of the canvas operations take an argument that identifies objects. The value can be a tag name, or it can be the numerical object identifier returned by the create operation. Example 34-3 does not use object IDs. Instead, it gives each object a symbolic identifier with a tag, plus it introduces more tags to represent classes of objects. The example uses the all tag to move all the items and to find out the bounding box of the image. The left box and the left hanging text both have the left tag. They can be moved together, and they share the same bindings. Similarly, the right tag is shared by the right box and the right hanging text. Each item has its own unique tag, so it can be manipulated individually, too. Those tags are slider, lbox, lnum, rbox, and rnum:

Example 34-3 A min max scale canvas example.

graphics/34fig01.gif

 proc Scale2 {w min max {width {}}} {    global scale2    if {$width == {}} {       # Set the long dimension, in pixels       set width [expr $max - $min]    }    # Save parameters    set scale2($w,scale) [expr ($max-$min)/$width.0]    set scale2($w,min) $min;# Current minimum    set scale2($w,max) $max    set scale2($w,Min) $min;# Lower bound to the scale    set scale2($w,Max) $max    set scale2($w,L) 10    set scale2($w,R) [expr $width+10]    # Build from 0 to 100, then scale and move it later.    # Distance between left edges of boxes is 100.    # The box is 10 wide, therefore the slider is 110 long.    # The left box sticks up, and the right one hangs down.    canvas $w    $w create rect 0 0 110 10 -fill grey -tag slider    $w create rect 0 -4 10 10 -fill black -tag {left lbox}    $w create rect 100 0 110 14 -fill red -tag {right rbox}    $w create text 5 16 -anchor n -text $min -tag {left lnum}    $w create text 105 16 -anchor n -text $max \       -tag {right rnum}-fill red    # Stretch/shrink the slider to the right length    set scale [expr ($width+10) / 110.0]    $w scale slider 0 0 $scale 1.0    # move the right box and text to match new length    set nx [lindex [$w coords slider] 2]    $w move right [expr $nx-110] 0    # Move everything into view    $w move all 10 10    # Make the canvas fit comfortably around the image    set bbox [$w bbox all]    set height [expr [lindex $bbox 3]+4]    $w config -height $height -width [expr $width+30]    # Bind drag actions    $w bind left  <Button-1> {Scale2Mark %W %x lbox}    $w bind right <Button-1> {Scale2Mark %W %x rbox}    $w bind left  <B1-Motion> {Scale2Drag %W %x lbox}    $w bind right <B1-Motion> {Scale2Drag %W %x rbox} } 

The slider is constructed with absolute coordinates, and then it is scaled to the desired width. The alternative is to compute the coordinates based on the desired width. I have found it clearer to use numbers when creating the initial layout as opposed to using expr or introducing more variables. The scale operation stretches the slider bar to the correct length. The scale operation takes a reference point, which in our case is (0, 0), and independent scale factors for the X and Y dimensions. The scale factor is computed from the width parameter, taking into account the extra length added (10) so that the distance between the left edge of the slider boxes is $width:

 set scale [expr ($width+10) / 110.0] $w scale slider 0 0 $scale 1.0 

The move operation repositions the right box and right hanging text. If the marker boxes are scaled, their shape gets distorted. The coords operation returns a list of four numbers: x1 y1 x2 y2. The distance to move is just the difference between the new right coordinate and the value used when constructing the slider initially. The box and text share the same tag, right, so they are both moved with a single move operation:

 set nx [lindex [$w coords slider] 2] $w move right [expr $nx-110] 0 

After the slider is constructed, it is shifted away from (0, 0), which is the upper-left corner of the canvas. The bbox operation returns four coordinates: x1 y1 x2 y2, that define the bounding box of the items with the given tag. In the example, y1 is zero, so y2 gives us the height of the image. The information returned by bbox can be off by a few pixels, and the example needs a few more pixels of height to avoid clipping the text. The width is computed based on the extra length added for the marker box, the 10 pixels the whole image was shifted, and 10 more for the same amount of space on the right side:

 set bbox [$w bbox all] set height [expr [lindex $bbox 3]+4] $w config -height $height -width [expr $width+30] 

Bindings are defined for the box and hanging text. The general tags left and right are used for the bindings. This means that you can drag either the box or the text to move the slider. The pathname of the canvas is passed into these procedures so that you could have more than one double slider in your interface:

 $w bind left  <Button-1> {Scale2Mark %W %x lbox} $w bind right <Button-1> {Scale2Mark %W %x rbox} $w bind left  <B1-Motion> {Scale2Drag %W %x lbox} $w bind right <B1-Motion> {Scale2Drag %W %x rbox} 
Example 34-4 Moving the markers for the min max scale.
 proc Scale2Mark {w x what } {    global scale2    # Remember the anchor point for the drag    set scale2($w,$what) $x } proc Scale2Drag { w x what } {    global scale2    # Compute delta and update anchor point    set x1 $scale2($w,$what)    set scale2($w,$what) $x    set dx [expr $x - $x1]    # Find out where the boxes are currently    set rx [lindex [$w coords rbox] 0]    set lx [lindex [$w coords lbox] 0]    if {$what == "lbox"} {       # Constrain the movement to be between the       # left edge and the right marker.       if {$lx + $dx > $rx} {          set dx [expr $rx - $lx]          set scale2($w,$what) $rx       } elseif {$lx + $dx < $scale2($w,L)} {          set dx [expr $scale2($w,L) - $lx]          set scale2($w,$what) $scale2($w,L)       }       $w move left $dx 0       # Update the minimum value and the hanging text       set lx [lindex [$w coords lbox] 0]       set scale2($w,min) [expr int($scale2($w,Min) + \          ($lx-$scale2($w,L)) * $scale2($w,scale))]       $w itemconfigure lnum -text $scale2($w,min)    } else {       # Constrain the movement to be between the       # right edge and the left marker       if {$rx + $dx < $lx} {          set dx [expr $lx - $rx]          set scale2($w,$what) $lx       } elseif {$rx + $dx > $scale2($w,R)} {          set dx [expr $scale2($w,R) - $rx]          set scale2($w,$what) $scale2($w,R)       }       $w move right $dx 0       # Update the maximum value and the hanging text       set rx [lindex [$w coords right] 0]       set scale2($w,max) [expr int($scale2($w,Min) + \          ($rx-$scale2($w,L)) * $scale2($w,scale))]       $w itemconfigure rnum -text $scale2($w,max)    } } proc Scale2Value {w} {    global scale2    # Return the current values of the double slider    return [list $scale2($w,min) $scale2($w,max)] } 

The Scale2Mark procedure initializes an anchor position, scale2($w,$what), and Scale2Drag uses this to detect how far the mouse has moved. The change in position, dx, is constrained so that the markers cannot move outside their bounds. The anchor is updated if a constraint was used, and this means that the marker will not move until the mouse is moved back over the marker. (Try commenting out the assignments to scale2($w,$what) inside the if statement.) After the marker and hanging text are moved, the value of the associated parameter is computed based on the parameters of the scale. The Scale2Value procedure queries the current values of the double slider.


       
    Top
     



    Practical Programming in Tcl and Tk
    Practical Programming in Tcl and Tk (4th Edition)
    ISBN: 0130385603
    EAN: 2147483647
    Year: 1999
    Pages: 478

    flylib.com © 2008-2017.
    If you may any questions please contact us: flylib@qtcs.net