http://www.perlmonks.org?node_id=1016180

What do you do with a Simulated Linear Panel Meter? You modify it into a module and build a simulated process control screen around it.

Need a break from "Select From...."? Give this a try.

Left mouse click to select/deselect a meter. Right mouse click to change a meter's limit

Many thanks to Mark Dootson for many excellent comments, not all of which are implemented yet in this example. All blame is mine.

#! /home/pete/CitrusPerl/perl/bin/perl # LM3.pl - wxPerl Process Control Example # uses the LinearMeter3.pm modular meter # # Last modified by James M. Lynes, Jr - January 30,2013 # # Draws and animates 8 Linear Meters, uses LinearMeter3.pm # Meters change green/red to indicate limit violations # Creates a 1 second timer to update the animation # Left click selects/deselects the meter and sets the border to yellow +/blue # Right click on a selected meter pops a limit change dialog # Right click on a deselected meter pops an error messsage box # Multiple meters may be selected at the same time - may want to limit # this in the future. # # The panel is created 40 units longer than the meter to allow space # for drawing the meter label # package main; use strict; use warnings; my $app = App->new(); $app->MainLoop; package App; use strict; use warnings; use base 'Wx::App'; sub OnInit { my $frame = Frame->new(); $frame->Show(1); } package Frame; use strict; use warnings; use Wx qw(:everything); use base qw(Wx::Frame); use LinearMeter3; use Data::Dumper; use Wx::Event qw(EVT_PAINT EVT_TIMER EVT_LEFT_DOWN EVT_RIGHT_DOWN); sub new { my ($self) = @_; my($meters, $width, $height) = (8, 100, 550); $self = $self->SUPER::new(undef, -1, "wxPerl Process Control Examp +le", wxDefaultPosition, [(($meters)+1)*10+20 + (($meters)*$width +),($height+90)]); my $font = Wx::Font->new(12, wxFONTFAMILY_SWISS, wxNORMAL, wxBOLD) +; $self->SetFont($font); Wx::StaticText->new($self, -1, "Boiler 1", [190, 15], wxDefaultSiz +e, wxALIGN_LEFT); Wx::StaticText->new($self, -1, "Boiler 2", [650, 15], wxDefaultSiz +e, wxALIGN_LEFT); # Create 8 panels to hold 8 meters - single row layout $self->{MP1} = Wx::Panel->new($self, wxID_ANY, [10 ,40], [$width, +$height+40]); $self->{MP2} = Wx::Panel->new($self, wxID_ANY, [($width*1)+20 ,40] +, [$width, $height+40]); $self->{MP3} = Wx::Panel->new($self, wxID_ANY, [($width*2)+30 ,40] +, [$width, $height+40]); $self->{MP4} = Wx::Panel->new($self, wxID_ANY, [($width*3)+40 ,40] +, [$width, $height+40]); $self->{MP5} = Wx::Panel->new($self, wxID_ANY, [($width*4)+70 ,40] +, [$width, $height+40]); $self->{MP6} = Wx::Panel->new($self, wxID_ANY, [($width*5)+80 ,40] +, [$width, $height+40]); $self->{MP7} = Wx::Panel->new($self, wxID_ANY, [($width*6)+90 ,40] +, [$width, $height+40]); $self->{MP8} = Wx::Panel->new($self, wxID_ANY, [($width*7)+100 ,40 +], [$width, $height+40]); # Create 8 meter objects - Override some default values $self->{LM1} = LinearMeter->new(); $self->{LM1}->InitialValue(73); $self->{LM1}->Limit(76); $self->{LM1}->Label("Temp 1"); $self->{LM1}->MeterHeight($height); $self->{LM1}->MeterWidth($width); $self->{LM2} = LinearMeter->new(); $self->{LM2}->InitialValue(28); $self->{LM2}->Limit(31); $self->{LM2}->Label("Flow 1"); $self->{LM2}->MeterHeight($height); $self->{LM2}->MeterWidth($width); $self->{LM3} = LinearMeter->new(); $self->{LM3}->InitialValue(42); $self->{LM3}->Limit(46); $self->{LM3}->Label("Pressure 1"); $self->{LM3}->MeterHeight($height); $self->{LM3}->MeterWidth($width); $self->{LM4} = LinearMeter->new(); $self->{LM4}->InitialValue(62); $self->{LM4}->Limit(66); $self->{LM4}->Label("Level 1"); $self->{LM4}->MeterHeight($height); $self->{LM4}->MeterWidth($width); $self->{LM5} = LinearMeter->new(); $self->{LM5}->InitialValue(24); $self->{LM5}->Limit(28); $self->{LM5}->Label("Temp 2"); $self->{LM5}->MeterHeight($height); $self->{LM5}->MeterWidth($width); $self->{LM6} = LinearMeter->new(); $self->{LM6}->InitialValue(63); $self->{LM6}->Limit(66); $self->{LM6}->Label("Flow 2"); $self->{LM6}->MeterHeight($height); $self->{LM6}->MeterWidth($width); $self->{LM7} = LinearMeter->new(); $self->{LM7}->InitialValue(33); $self->{LM7}->Limit(37); $self->{LM7}->Label("Pressure 2"); $self->{LM7}->MeterHeight($height); $self->{LM7}->MeterWidth($width); $self->{LM8} = LinearMeter->new(); $self->{LM8}->InitialValue(81); $self->{LM8}->Limit(85); $self->{LM8}->Label("Level 2"); $self->{LM8}->MeterHeight($height); $self->{LM8}->MeterWidth($width); # # Set up Event Handlers ---------------------------------------------- +--------- # # Timer my $timer = Wx::Timer->new( $self ); $timer->Start( 1000 ); # 1 second period EVT_TIMER($self, -1, \&onTimer); # Paint EVT_PAINT($self, \&onPaint); # Mouse EVT_LEFT_DOWN($self->{MP1}, sub{$self->_evt_left_down( $self->{LM1 +}, @_);}); EVT_LEFT_DOWN($self->{MP2}, sub{$self->_evt_left_down( $self->{LM2 +}, @_);}); EVT_LEFT_DOWN($self->{MP3}, sub{$self->_evt_left_down( $self->{LM3 +}, @_);}); EVT_LEFT_DOWN($self->{MP4}, sub{$self->_evt_left_down( $self->{LM4 +}, @_);}); EVT_LEFT_DOWN($self->{MP5}, sub{$self->_evt_left_down( $self->{LM5 +}, @_);}); EVT_LEFT_DOWN($self->{MP6}, sub{$self->_evt_left_down( $self->{LM6 +}, @_);}); EVT_LEFT_DOWN($self->{MP7}, sub{$self->_evt_left_down( $self->{LM7 +}, @_);}); EVT_LEFT_DOWN($self->{MP8}, sub{$self->_evt_left_down( $self->{LM8 +}, @_);}); EVT_RIGHT_DOWN($self->{MP1}, sub{$self->_evt_right_down( $self->{L +M1}, @_);}); EVT_RIGHT_DOWN($self->{MP2}, sub{$self->_evt_right_down( $self->{L +M2}, @_);}); EVT_RIGHT_DOWN($self->{MP3}, sub{$self->_evt_right_down( $self->{L +M3}, @_);}); EVT_RIGHT_DOWN($self->{MP4}, sub{$self->_evt_right_down( $self->{L +M4}, @_);}); EVT_RIGHT_DOWN($self->{MP5}, sub{$self->_evt_right_down( $self->{L +M5}, @_);}); EVT_RIGHT_DOWN($self->{MP6}, sub{$self->_evt_right_down( $self->{L +M6}, @_);}); EVT_RIGHT_DOWN($self->{MP7}, sub{$self->_evt_right_down( $self->{L +M7}, @_);}); EVT_RIGHT_DOWN($self->{MP8}, sub{$self->_evt_right_down( $self->{L +M8}, @_);}); return $self; } 1; # # Right Mouse Pressed Event - Change the Selected Meter's Limit ------ +----------- # sub _evt_right_down { my($frame, $meter, $panel, $event) = @_; if($meter->Selected()) { my $label = $meter->Label(); my $dialog = Wx::TextEntryDialog->new( $frame, "Select a New Limit", "Change the $label Alarm +Limit", $meter->Limit()); if($dialog->ShowModal == wxID_CANCEL) { $meter->BorderColour(wxBLUE); $meter->Selected(0); return; }; $meter->Limit($dialog->GetValue()); $meter->BorderColour(wxBLUE); $meter->Selected(0); } else { my $msg = Wx::MessageBox("No Meter Selected\nLeft Click a Mete +r to Select", "Meter Limit Entry Error", wxICON_ERROR, $frame); } $event->Skip(1); } # # Left Mouse Pressed Event - Selects a Meter - Selection will Toggle - +---------- # sub _evt_left_down { my($frame, $meter, $panel, $event) = @_; if($meter->Selected()) { $meter->BorderColour(wxBLUE); $meter->Selected(0); } else { $meter->BorderColour(Wx::Colour->new("yellow")); $meter->Selected(1); } $event->Skip(1); } # Simple version of Event Handler Closure # More complex version fits better for this application # EVT_LEFT_DOWN($self->{MP1}, sub{ # my($panel, $event) = @_; # $self->{LM1}->BordorColour(Wx::Colour->new("yellow")); # $event->Skip(1); # }); # # 1 second timer to simulate meter movement -------------------------- +------- # sub onTimer { my($self, $event) = @_; # Randomize for each meter # for a more natural look my $dir = (rand 10) < 5 ? -1 : 1; my $inc = (rand 1) * $dir; $self->{LM1}->InitialValue($self->{LM1}->InitialValue() + $inc); LinearMeter->Draw($self->{MP1}, $self->{LM1}); $dir = (rand 10) < 5 ? -1 : 1; $inc = (rand 1) * $dir; $self->{LM2}->InitialValue($self->{LM2}->InitialValue() + $inc); LinearMeter->Draw($self->{MP2}, $self->{LM2}); $dir = (rand 10) < 5 ? -1 : 1; $inc = (rand 1) * $dir; $self->{LM3}->InitialValue($self->{LM3}->InitialValue() + $inc); LinearMeter->Draw($self->{MP3}, $self->{LM3}); $dir = (rand 10) < 5 ? -1 : 1; $inc = (rand 1) * $dir; $self->{LM4}->InitialValue($self->{LM4}->InitialValue() + $inc); LinearMeter->Draw($self->{MP4}, $self->{LM4}); $dir = (rand 10) < 5 ? -1 : 1; $inc = (rand 1) * $dir; $self->{LM5}->InitialValue($self->{LM5}->InitialValue() + $inc); LinearMeter->Draw($self->{MP5}, $self->{LM5}); $dir = (rand 10) < 5 ? -1 : 1; $inc = (rand 1) * $dir; $self->{LM6}->InitialValue($self->{LM6}->InitialValue() + $inc); LinearMeter->Draw($self->{MP6}, $self->{LM6}); $dir = (rand 10) < 5 ? -1 : 1; $inc = (rand 1) * $dir; $self->{LM7}->InitialValue($self->{LM7}->InitialValue() + $inc); LinearMeter->Draw($self->{MP7}, $self->{LM7}); $dir = (rand 10) < 5 ? -1 : 1; $inc = (rand 1) * $dir; $self->{LM8}->InitialValue($self->{LM8}->InitialValue() + $inc); LinearMeter->Draw($self->{MP8}, $self->{LM8}); } # # Paint the Meters --------------------------------------------------- +------------------ # sub onPaint { my($self, $event) = @_; # Draw the 8 meters LinearMeter->Draw($self->{MP1}, $self->{LM1}); LinearMeter->Draw($self->{MP2}, $self->{LM2}); LinearMeter->Draw($self->{MP3}, $self->{LM3}); LinearMeter->Draw($self->{MP4}, $self->{LM4}); LinearMeter->Draw($self->{MP5}, $self->{LM5}); LinearMeter->Draw($self->{MP6}, $self->{LM6}); LinearMeter->Draw($self->{MP7}, $self->{LM7}); LinearMeter->Draw($self->{MP8}, $self->{LM8}); }
# LinearMeter3.pm - Linear Meter Object # # Last modified by James M. Lynes, Jr - January 30,2013 # # Creates a Linear Panel Meter # Can be drawn vertical or horizontal # Modified LinearMeter.pl into an object that can be used to create mu +ltiple meters # Adapted from LinearMeter.cpp by Marco Cavallini # based in part(mostly) on the work of # the KWIC project (http://www.koansoftware.com/kwic/index.htm). # Referenced on pg 596 of the "Wx Book" - # "Cross-Platform GUI Programming with wxWidgets", Smart, Hock, & Csom +or # # Added high-limit/alarm processing- red/green color change # Added label display between the bottom of the meter and the bottom o +f the panel # Set an initial value, high-limit, and tic array # Added DrawLimitBar to draw a tic mark at the current limit value # Deleted drawing the 1st and last tags to reduce crowding of the disp +lay # Added a "Selected" flag # Driver program implements mouse events for limit modification # and timer event to drive the animation. # # To-Do: Rework meter so that a wider border can be drawn. PANEL(BORDE +R(METER)) # Currently the border draws on top of the meter space. package LinearMeter; use strict; use warnings; use Wx qw(:everything); use Data::Dumper; # # Define the meter object hash --------------------------------------- +--------- # sub new { my $self = {}; $self->{METERHEIGHT} = 300; # Swap these f +or horizontal display $self->{METERWIDTH} = 100; $self->{ACTIVEBAR} = wxGREEN; $self->{PASSIVEBAR} = wxWHITE; $self->{VALUECOLOUR} = wxBLACK; $self->{BORDERCOLOUR} = wxBLUE; $self->{LIMITCOLOUR} = wxBLACK; $self->{ALARMLIMITCOLOUR} = wxRED; $self->{TAGSCOLOUR} = wxBLACK; $self->{SCALEDVALUE} = 0; $self->{REALVAL} = 0; $self->{LIMIT} = 75; # High-Limit se +tpoint $self->{TAGSVAL} = []; $self->{TAGSNUM} = 0; $self->{STARTTAG} = 0; $self->{NUMTAGS} = 10; $self->{INCTAG} = 10; $self->{MAX} = 100; # Span $self->{MIN} = 0; $self->{INITIALVALUE} = 0; # Initial value +displayed $self->{LASTPOS} = 0; # Last mouse pos +ition $self->{DIRHORIZFLAG} = 0; # 0-Verticle, 1- +Horizontal $self->{SHOWCURRENT} = 1; $self->{SHOWLIMITS} = 1; $self->{SHOWLABEL} = 1; $self->{FONT} = Wx::Font->new(8, wxFONTFAMILY_SWISS, w +xNORMAL, wxNORMAL); $self->{LABEL} = ""; $self->{SELECTED} = 0; bless($self); return $self; } # # Draw the Linear Meter ---------------------------------------------- +------------ # sub Draw { my($class, $panel, $Meter) = @_; my $dc = Wx::PaintDC->new($panel); my $memdc = Wx::MemoryDC->new(); $memdc->SelectObject(Wx::Bitmap->new($Meter->MeterWidth(), $Meter- +>MeterHeight())); my($w, $h) = $memdc->GetSizeWH(); my $brush = Wx::Brush->new($Meter->PassiveBar(), wxSOLID); $memdc->SetBackground($brush); $memdc->Clear(); SetUp($memdc, $Meter); # Set the initial va +lue and tic marks my $pen = Wx::Pen->new($Meter->BorderColour(), 3, wxSOLID); $memdc->SetPen($pen); $memdc->DrawRectangle(0, 0, $w, $h); $pen = Wx::Pen->new($Meter->BorderColour(), 3, wxSOLID); $memdc->SetPen($pen); $brush = Wx::Brush->new($Meter->ActiveBar(), wxSOLID); if($Meter->RealVal() > $Meter->Limit()) {$brush = Wx::Brush->new($ +Meter->AlarmLimitColour(), wxSOLID)} $memdc->SetBrush($brush); my $yPoint; my $rectHeight; if($Meter->DirHorizFlag()) { # Horizontal Orien +tation $memdc->DrawRectangle(1, 1, $Meter->ScaledValue(), $h-2); } else { # Verticle Orientation $yPoint = $h - $Meter->ScaledValue(); if($Meter->ScaledValue() == 0) { $rectHeight = $Meter->ScaledValue(); } else { if($Meter->RealVal() == $Meter->Max()) { $rectHeight = $Meter->ScaledValue(); $yPoint -= 1; } else { $rectHeight = $Meter->ScaledValue() - 1; } $memdc->DrawRectangle(1, $yPoint, $w-2, $rectHeight); } } if($Meter->ShowCurrent()) {DrawCurrent($memdc, $Meter)} if($Meter->ShowLimits()) {DrawLimits($memdc, $Meter)} if($Meter->TagsNum() > 0) {DrawTags($memdc, $Meter)} $dc->Blit(0, 0, $w, $h, $memdc, 0, 0); # Keep blit abov +e DrawLabel call if($Meter->ShowLabel()) {DrawLabel($dc, $Meter)} # <---- } sub SetUp { # Set and update the displayed +value my($dc, $Meter) = @_; SetValue($dc, $Meter->InitialValue(), $Meter); if($Meter->TagsNum() == 0) { # Build tic marks 1st +time through for($Meter->StartTag()..$Meter->NumTags()) { # Quick an +d dirty AddTag($_ * $Meter->IncTag(), $Meter); } } } sub DrawCurrent { # Draw the current value as t +ext my($dc, $Meter) = @_; my($w, $h) = $dc->GetSizeWH(); my $valuetext = sprintf("%d", $Meter->RealVal()); my ($tw, $th) = $dc->GetTextExtent($valuetext); $dc->SetTextForeground($Meter->ValueColour()); $dc->SetFont($Meter->Font()); $dc->DrawText($valuetext, $w/2-$tw/2, $h/2-$th/2); } sub DrawLimits { # Draw Min and Max as text my($dc, $Meter) = @_; my($w, $h) = $dc->GetSizeWH(); $dc->SetFont($Meter->Font()); $dc->SetTextForeground($Meter->LimitColour()); if($Meter->DirHorizFlag()) { my $valuetext = sprintf("%d", $Meter->Min()); my ($tw, $th) = $dc->GetTextExtent($valuetext); $dc->DrawText($valuetext, 5, $h/2-$th/2); $valuetext = sprintf("%d", $Meter->Max()); ($tw, $th) = $dc->GetTextExtent($valuetext); $dc->DrawText($valuetext, $w-$tw-5, $h/2-$th/2); } else { my $valuetext = sprintf("%d", $Meter->Min()); my ($tw, $th) = $dc->GetTextExtent($valuetext); $dc->DrawText($valuetext, $w/2-$tw/2, $h-$th-5); $valuetext = sprintf("%d", $Meter->Max()); ($tw, $th) = $dc->GetTextExtent($valuetext); $dc->DrawText($valuetext, $w/2-$tw/2, 5); } } sub DrawTags { # Draw tic marks and labels my($dc, $Meter) = @_; my($w, $h) = $dc->GetSizeWH(); my $tcoeff; if($Meter->DirHorizFlag()) { $tcoeff = ($w-2)/($Meter->Max()-$Meter->Min()); } else { $tcoeff = ($h-2)/($Meter->Max()-$Meter->Min()); } my $pen = Wx::Pen->new($Meter->TagsColour(), 1, wxSOLID); $dc->SetPen($pen); my $brush = Wx::Brush->new($Meter->TagsColour(), wxSOLID); $dc->SetBrush($brush); $dc->SetTextForeground($Meter->TagsColour()); my $tag = 1; while($tag < ($Meter->TagsNum()-1)) { my $scalval = (${$Meter->TagsVal()}[$tag]-$Meter->Min()) * $tc +oeff; my $textvalue = sprintf("%d", ${$Meter->TagsVal()}[$tag]); if($Meter->DirHorizFlag()) { $dc->DrawLine($scalval+1, $h-2, $scalval+1, $h-10); my($tw, $th) = $dc->GetTextExtent($textvalue); $dc->DrawText($textvalue, $scalval+1-($tw/2), $h-10-$th); } else { $dc->DrawLine($w-2, $h-$scalval-1, $w-10, $h-$scalval-1); my($tw, $th) = $dc->GetTextExtent($textvalue); $dc->DrawText($textvalue, $w-10-$tw, $h-$scalval-($th/2)); } $tag++; } DrawLimitBar($dc, $Meter); } sub DrawLimitBar { # Draw small bar at limit settin +g my($dc, $Meter) = @_; my($w, $h) = $dc->GetSizeWH(); my $tcoeff; if($Meter->DirHorizFlag()) { $tcoeff = ($w-2)/($Meter->Max()-$Meter->Min()); } else { $tcoeff = ($h-2)/($Meter->Max()-$Meter->Min()); } my $pen = Wx::Pen->new(Wx::Colour->new("orange"), 3, wxSOLID); $dc->SetPen($pen); my $brush = Wx::Brush->new($Meter->TagsColour(), wxSOLID); $dc->SetBrush($brush); $dc->SetTextForeground($Meter->TagsColour()); my $scalval = ($Meter->Limit()-$Meter->Min()) * $tcoeff; if($Meter->DirHorizFlag()) { $dc->DrawLine($scalval+1, $h-2, $scalval+1, $h-20); } else { $dc->DrawLine($w-2, $h-$scalval, $w-20, $h-$scalval); } } sub AddTag { # Add a tic mark to array my($val, $Meter) = @_; push(@{$Meter->TagsVal()}, $val); $Meter->TagsNum($#{$Meter->TagsVal()}+1); } sub SetValue { # Scale the value for display my($dc, $Value, $Meter) = @_; my($w, $h) = $dc->GetSizeWH(); my $coeff; if($Meter->DirHorizFlag()) { $coeff = ($w-2)/($Meter->Max()-$Meter->Min()); } else { $coeff = ($h-2)/($Meter->Max()-$Meter->Min()); } $Meter->ScaledValue(($Value-$Meter->Min()) * $coeff); $Meter->RealVal($Value); } sub DrawLabel { # Draw a label at bottom of met +er my($dc, $Meter) = @_; my $memdc = Wx::MemoryDC->new(); $memdc->SelectObject(Wx::Bitmap->new($Meter->MeterWidth(), 40)); my($w, $h) = $memdc->GetSizeWH(); my $brush = Wx::Brush->new(wxLIGHT_GREY, wxSOLID); $memdc->SetBackground($brush); my $pen = Wx::Pen->new($Meter->TagsColour(), 1, wxSOLID); $memdc->SetPen($pen); $memdc->SetTextForeground($Meter->TagsColour()); $memdc->SetFont($Meter->Font()); $memdc->Clear(); my @te = $memdc->GetTextExtent($Meter->Label()); my $x = (($w-$te[0])/2)-5; $memdc->DrawText($Meter->Label(), $x, 5); $dc->Blit(0, $Meter->MeterHeight(), $w, $Meter->MeterHeight()+40, + $memdc, 0, 0); } # # Object Accessors - Hand coded method ------------------------------- +-------------- # Replace with Class::Accessor in the future # sub MeterHeight { my $self = shift; if(@_) { $self->{METERHEIGHT} = shift } return $self->{METERHEIGHT}; } sub MeterWidth { my $self = shift; if(@_) { $self->{METERWIDTH} = shift } return $self->{METERWIDTH}; } sub ActiveBar { my $self = shift; if(@_) { $self->{ACTIVEBAR} = shift } return $self->{ACTIVEBAR}; } sub PassiveBar { my $self = shift; if(@_) { $self->{PASSIVEBAR} = shift } return $self->{PASSIVEBAR}; } sub ValueColour { my $self = shift; if(@_) { $self->{VALUECOLOUR} = shift } return $self->{VALUECOLOUR}; } sub BorderColour { my $self = shift; if(@_) { $self->{BORDERCOLOUR} = shift } return $self->{BORDERCOLOUR}; } sub LimitColour { my $self = shift; if(@_) { $self->{LIMITCOLOUR} = shift } return $self->{LIMITCOLOUR}; } sub AlarmLimitColour { my $self = shift; if(@_) { $self->{ALARMLIMITCOLOUR} = shift } return $self->{ALARMLIMITCOLOUR}; } sub TagsColour { my $self = shift; if(@_) { $self->{TAGSCOLOUR} = shift } return $self->{TAGSCOLOUR}; } sub ScaledValue { my $self = shift; if(@_) { $self->{SCALEDVALUE} = shift } return $self->{SCALEDVALUE}; } sub RealVal { my $self = shift; if(@_) { $self->{REALVAL} = shift } return $self->{REALVAL}; } sub Limit { my $self = shift; if(@_) { $self->{LIMIT} = shift } return $self->{LIMIT}; } sub TagsVal { my $self = shift; if(@_) { $self->{TAGSVAL} = @_ } return $self->{TAGSVAL}; } sub TagsNum { my $self = shift; if(@_) { $self->{TAGSNUM} = shift } return $self->{TAGSNUM}; } sub StartTag { my $self = shift; if(@_) { $self->{STARTTAG} = shift } return $self->{STARTTAG}; } sub NumTags { my $self = shift; if(@_) { $self->{NUMTAGS} = shift } return $self->{NUMTAGS}; } sub IncTag { my $self = shift; if(@_) { $self->{INCTAG} = shift } return $self->{INCTAG}; } sub Max { my $self = shift; if(@_) { $self->{MAX} = shift } return $self->{MAX}; } sub Min { my $self = shift; if(@_) { $self->{MIN} = shift } return $self->{MIN}; } sub InitialValue { my $self = shift; if(@_) { $self->{INITIALVALUE} = shift } return $self->{INITIALVALUE}; } sub lastpos { my $self = shift; if(@_) { $self->{LASTPOS} = shift } return $self->{LASTPOS}; } sub DirHorizFlag { my $self = shift; if(@_) { $self->{DIRHORIZFLAG} = shift } return $self->{DIRHORIZFLAG}; } sub ShowCurrent { my $self = shift; if(@_) { $self->{SHOWCURRENT} = shift } return $self->{SHOWCURRENT}; } sub ShowLimits { my $self = shift; if(@_) { $self->{SHOWLIMITS} = shift } return $self->{SHOWLIMITS}; } sub ShowLabel { my $self = shift; if(@_) { $self->{SHOWLABEL} = shift } return $self->{SHOWLABEL}; } sub Font { my $self = shift; if(@_) { $self->{FONT} = shift } return $self->{FONT}; } sub Label { my $self = shift; if(@_) { $self->{LABEL} = shift } return $self->{LABEL}; } sub Selected { my $self = shift; if(@_) { $self->{SELECTED} = shift } return $self->{SELECTED}; } 1;

James

There's never enough time to do it right, but always enough time to do it over...