home *** CD-ROM | disk | FTP | other *** search
/ CD Actual Thematic 7: Programming / CDAT7.iso / Share / Editores / Perl5 / perl / lib / site / Tk / Dial.pm < prev    next >
Encoding:
Perl POD Document  |  1997-08-10  |  4.6 KB  |  194 lines

  1. package Dial;
  2. require Tk::Frame;
  3. @ISA = qw(Tk::Frame);           
  4.  
  5. $pi = atan2(1, 1) * 4;
  6.  
  7. Construct Tk::Widget 'Dial';
  8.  
  9. =head1 NAME
  10.  
  11. Dial - an alternative to the scale widget
  12.  
  13. =head1 SYNOPSIS
  14.  
  15.  use Tk::Dial;
  16.  
  17.  $dial = $top->Dial(-margin =>  20,
  18.             -radius =>  48,
  19.             -min    =>     0,
  20.             -max    => 100,
  21.             -value  =>   0,
  22.             -format => '%d');
  23.  
  24.  margin - blank space to leave around dial
  25.  radius - radius of dial
  26.  min, max - range of possible values
  27.  value  - current value
  28.  format - printf-style format for displaying format
  29.  
  30. Values shown above are defaults.
  31.  
  32. =head1 DESCRIPTION
  33.  
  34. A dial looks like a speedometer: a 3/4 circle with a needle indicating
  35. the current value.  Below the graphical dial is an entry that displays
  36. the current value, and which can be used to enter a value by hand.
  37.  
  38. The needle is moved by pressing button 1 in the canvas and dragging. The
  39. needle will follow the mouse, even if the mouse leaves the canvas, which
  40. allows for high precision. Alternatively, the user can enter a value in
  41. the entry space and press Return to set the value; the needle will be
  42. set accordingly.
  43.  
  44. =head1 TO DO
  45.  
  46.  Configure
  47.  Tick marks
  48.  Step size
  49.  
  50. =head1 AUTHORS
  51.  
  52. Roy Johnson, rjohnson@shell.com
  53.  
  54. Based on a similar widget in XV, a program by John Bradley,
  55. bradley@cis.upenn.edu
  56.  
  57. =head1 HISTORY 
  58.  
  59. August 1995: Released for critique by pTk mailing list
  60.  
  61. =cut 
  62.  
  63.  
  64. @flags = qw(-margin -radius -min -max -value -format);
  65.  
  66. sub Populate
  67. {
  68.   my ($w, $args) = @_;
  69.  
  70.   @$w{@flags} = (20, 48, (0, 100), 0, '%d');
  71.   for $key (@flags) {
  72.     my $val = delete $args->{$key};
  73.     if (defined $val) {
  74.       $$w{$key} = $val;
  75.     }
  76.   }
  77.  
  78.   # Pass other args on to Frame
  79.   $w->SUPER::Populate($args);
  80.  
  81.   # Convenience variables, based on flag settings
  82.   my ($margin, $radius, $min, $max, $format) = @$w{@flags};
  83.   my ($center_x, $center_y) = ($margin + $radius) x 2;
  84.  
  85.   # Create Widgets
  86.   my $c = $w->Canvas(-width => 2 * ($radius + $margin),
  87.              -height => 1.75 * $radius + $margin);
  88.  
  89.   $c->create('arc',
  90.          ($center_x - $radius, $center_y - $radius),
  91.          ($center_x + $radius, $center_y + $radius),
  92.          -start => -45, -extent => 270, -style => 'chord',
  93.          -width => 2);
  94.  
  95.   $c->pack(-expand => 1, -fill => 'both');
  96.  
  97.   $w->bind($c, '<1>' => \&drawPointer);
  98.   $w->bind($c, '<B1-Motion>' => \&drawPointer);
  99.  
  100.   my $e = $w->Entry(-textvariable => \$w->{-value});
  101.   $e->pack();
  102.  
  103.   $w->bind($e, '<Return>' => sub { &setvalue($c) });
  104.  
  105.   &setvalue($c);
  106. }
  107. #------------------------------
  108. sub drawPointer
  109. {
  110.   my $c = shift;
  111.   my $w = $c->parent;
  112.   my $e = $c->XEvent;
  113.  
  114.   # Convenience variables, based on flag settings
  115.   my ($margin, $radius, $min, $max, $value, $format) = @$w{@flags};
  116.   my ($center_x, $center_y) = ($margin + $radius) x 2;
  117.  
  118.   my ($delta_x, $delta_y) = ($e->x - $center_x, $e->y - $center_y);
  119.   my $distance = sqrt($delta_x**2 + $delta_y**2);
  120.   return if ($distance < 1);
  121.  
  122.   # atan2/pi returns the angle in pi-radians, but out-of-phase;
  123.   # here we correct it to be 0 at the start of the arc
  124.   my $angle = atan2($delta_y, $delta_x) / $pi + 1.25;
  125.   if ($angle > 2) { $angle -= 2 }
  126.  
  127.   if ($angle < 1.5) {
  128.     my $factor = $radius/$distance;
  129.     my $newx = $center_x + int($factor * $delta_x);
  130.     my $newy = $center_y + int($factor * $delta_y);
  131.  
  132.     $c->delete('oldpointer');
  133.     $c->create('line', ($newx, $newy, $center_x, $center_y),
  134.            -arrow => 'first', -tags => 'oldpointer',
  135.            -width => 2);
  136.  
  137.     $w->{-value} = sprintf($format,
  138.                $angle / 1.5 * ($max - $min) + $min);
  139.   } elsif ($angle < 1.75) {
  140.     if ($w->{-value} < $max) {
  141.       &setvalue($c);
  142.       $w->{-value} = $max;
  143.     }
  144.   } else {
  145.     if ($w->{-value} > $min) {
  146.       &setvalue($c);
  147.       $w->{-value} = $min;
  148.     }
  149.   }
  150.  
  151. }
  152.  
  153. #------------------------------
  154.  
  155. sub setvalue {
  156.   my $c = shift;
  157.   my $w = $c->parent;
  158.  
  159.   my $value = $w->{-value};
  160.  
  161.   # Convenience variables, based on flag settings
  162.   my ($margin, $radius, $min, $max, $dummy, $format) = @$w{@flags};
  163.   my ($center_x, $center_y) = ($margin + $radius) x 2;
  164.  
  165.   if ($value > $max) {
  166.     $value = $max;
  167.   } elsif ($value < $min) {
  168.     $value = $min;
  169.   }
  170.  
  171.   $w->{-value} = sprintf($format, $value);
  172.  
  173.   # value = (angle / 1.5) * (max-min) + min
  174.   # Solving backwards...
  175.   # value - min = angle / 1.5 * (max-min)
  176.   # (value - min) * 1.5 / (max-min) = angle
  177.  
  178.   my $angle = ($value - $min) * 1.5 / ($max - $min);
  179.   $angle -= 1.25;
  180.   $angle *= $pi;
  181.  
  182.   # Now just figure out X and Y where atan2 == $angle
  183.   my($x, $y) = (cos($angle) * $radius, sin($angle) * $radius);
  184.   $x += $center_x;
  185.   $y += $center_y;
  186.   $c->delete('oldpointer');
  187.   $c->create('line', ($x, $y, $center_x, $center_y),
  188.          -arrow => 'first', -tags => 'oldpointer',
  189.          -width => 2);
  190.  
  191. }
  192.  
  193. 1;
  194.