The Min Max Scale Example

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.

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.

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.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.119.28.237