Scale widget

By default the value of a scale widget is changed either by dragging the slider or by left-clicking in the trough. The increment by which the left-click changes is the same as the resolution, by default one unit. You can increase the increment by which a click moves by increasing the resolution value (which decreases the resolution in the normal sense). However, sometimes it is convenient to have a finer resolution available while also being able to move in larger increments. A solution is to bind to <B3>, which is by default unbound, a larger movement. This procedure moves by the specified multiplier k times the resolution of the scale.

 proc ScaleMoveBigIncrement {w k x y} {
    set part [$w identify $x $y]
    switch -exact -- $part {
        trough1 {
            set dir -1;
        }
        trough2 {
            set dir  1;
        }
        default {
             return
        }
    }
    set Resolution [$w cget -resolution]
    set CurrentValue [$w get]
    set Delta [expr $dir * $k * $Resolution]
    $w set [expr $CurrentValue + $Delta]
 }

The coordinates x and y are supplied by Tk when the binding is triggered. w and k are, from the point of view of the individual binding, constants. They represent the scale widget and the desired resolution multiplier. You supply them when you create the bindng. A typical binding might look like this:

 bind .play.vol <<B3>> {ScaleMoveBigIncrement .play.vol 5 %x %y}

Of course you can bind to some other event if you wish to.

WJP

ARR

I think here is an unwanted behaviour of the scale widget! I want to use a scale to define values from 3 to 13 in increments of 2. So I define the scale like this:

   pack [scale .s -orient hor] -fill x
   .s configure -from 3 -to 15 
   .s configure -tick 2 -resolution 2 

What I get is a scale changing values from 4 to 14 in increments of 4. This is not what I expected. The reason is the -resolution option which causes the scale to change the -from value to a multiple of the resolution. Any comments? Is this a tk bug or can I do it another way?

WJP I would call this a bug since the observed behavior is not in agreement with the documentation and since the desired behavior is perfectly reasonable.

It looks like this behavior is caused by the following two lines in tkScale.c:

        scalePtr->fromValue = TkRoundToResolution(scalePtr,
                scalePtr->fromValue);
        scalePtr->toValue = TkRoundToResolution(scalePtr, scalePtr->toValue);

where TkRoundToResolution rounds a given floating-point value to the nearest multiple of the scale's resolution. Offhand, I'm guessing that the intended purpose is to guarantee that the distance from "from" to "to" is an integral multiple of the resolution. I suggest eliminating these two lines, testing for satisfaction of this constraint, and throwing an error if it is not satisfied.

SDP

I've seen the same behavior and filed a bug report about it. The bug report also includes a patch for tkScale.c that fixes the problem:

http://sourceforge.net/tracker/index.php?func=detail&aid=1899040&group_id=12997&atid=112997

The patched version should look like this:

  double
  TkRoundToResolution(scalePtr, value)
    TkScale *scalePtr; /* Information about scale widget. */
    double value; /* Value to round. */
  {
    double rem, new, tick;
  
    if (scalePtr->resolution <= 0) {
      return value;
    }
    value = value + scalePtr->fromValue;
    tick = floor(value/scalePtr->resolution);
    new = scalePtr->resolution * tick;
    rem = value - new;
    if (rem < 0) {
      if (rem <= -scalePtr->resolution/2) {
        new = (tick - 1.0) * scalePtr->resolution;
      }
    } else {
      if (rem >= scalePtr->resolution/2) {
        new = (tick + 1.0) * scalePtr->resolution;
      }
    }
    new = new - scalePtr->fromValue;
    return new;
  }