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

As suggested by ww in his response to my wxPerl Simulated 7 Segment LCD Display post, it's now available in clock form.

The LCD, Angular Meter, and Linear Meter are all now in module format using Class::Accessor. Sure makes changes easier/quicker. There is also an additional process control example displaying 4 linear meters and 2 angular(round) meters. The updated code will be(has been) posted to github http://github.com/jmlynesjr/wxPerl-Module-Examples in a couple of days. For now, enjoy the clock code below.

Update1: Need to add another flag to signal 12/24 hour display format. And light one of the decimal points on am/pm. Creeping elegance..:).

Update2: Made the fix for 12/24 hour display and realised that I have polluted the LCDdisplay class with data that is only needed by the LCDClock program. I need to derive a sub-class of LDCdisplay to hold this data, It's always something...

Update3: Derived class version done and will be the version posted to github when I can get to it.

Update4: Alarm Clock version completed. Uses a wxMediaControl within an AudibleAlarm.pm class to play a selected MP3 file as the wakeup alarm sound.

Update5: Latest code now posted to GitHub. A new repository was created, so the URL above was changed.

LCDClock.pl

#! /home/pete/CitrusPerl/perl/bin/perl # LCDClock.pl # # Uses LCDdisplay.pm module to display an LCD Clock # Includes a flashing colon(:) to indicate seconds, if enabled # LCDdisplay.pm can display 0-F, :, -, _, and a few other special char +acters # Up/down count, display your favorite nationa debt, etc. # # James M. Lynes, Jr # Last Modified: February 4, 2013 # 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 Wx::Event qw(EVT_PAINT EVT_SIZE EVT_TIMER); use LCDdisplay; use Data::Dumper; sub new { my($self) = @_; $self = $self->SUPER::new(undef, -1, "LCD Clock", wxDefaultPosition, [400, 200]); EVT_PAINT( $self, \&onPaint ); # Initialize event h +andlers EVT_SIZE( $self, \&onSize ); EVT_TIMER( $self, -1, \&onTimer); $self->{LCD} = LCDdisplay::Data->new(); # Create an LCD + object $self->{LCD}->mBlinkFlag(1); # Blink colon ? 1->yes $self->{LCD}->mNumberDigits(5); # 5 digit display i +ncluding : $self->{LCD}->mValue(""); # Initialize the disp +lay $self->SetBackgroundColour($self->{LCD}->mDefaultColour); # Bla +ck background my $timer = Wx::Timer->new($self); # Initialize 1 s +econd timer $timer->Start(1000); return $self; } 1; # # Dismiss a size event # sub onSize{ my($self, $event) = @_; $event->Skip(); } # # Draw the LCD # sub onPaint { my($self, $event) = @_; LCDdisplay->Draw($self, $self->{LCD}); } # # Format the time for display # sub onTimer { my($self, $event) = @_; my($min, $hour) = (localtime)[1,2]; my $colon = ":"; if($self->{LCD}->mBlinkFlag) { # Blink the colon? if($self->{LCD}->mToggle) { # yes, toggle the : $self->{LCD}->mToggle(0); $colon = ":"; } else { $self->{LCD}->mToggle(1); $colon = " "; } } my $minstr = sprintf("%02d", $min); my $hourstr = sprintf("%02d", $hour); $self->{LCD}->mValue($hourstr . $colon . $minstr); $self->Refresh(0); }

LCDdisplay.pm

#! /home/pete/CitrusPerl/perl/bin/perl # # LCDdisplay.pm # This script draws a simulated seven segment LCD Display. # Segments are drawn as 4 or 6 sided polygons # The colon(:) is drawn as 2 ellipses and takes up a character space # The decimal(.) is drawn as 1 ellipse and does not take a character s +pace # A leading decimal(.) must be prefaced by a 0 as 0.1234 # The string value to be displayed is stored in mValue # # Written in wxPerl. Tested on Citrus Perl 5.16 with wxWidgets 2.8.x. # Ported by: James M. Lynes. Jr. # Last Modified Date: February 4, 2013 # # Adapted from LCDWindow.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 # # package LCDdisplay; use 5.010; # Using Given/When use strict; use warnings; use Wx qw(:everything); use Data::Dumper; my %defaults = ( # Object initialization Data mSegmentLen => 40, mSegmentWidth => 10, mSpace => 5, mNumberDigits => 6, # width of mValue includ +ing . or : mValue => "", # Default string to be d +isplayed LCD_Number_Segments => 8, # Segment 7 is the decim +al point mLightColour => Wx::Colour->new(0, 255, 0), # Brigh +t green mGrayColour => Wx::Colour->new(0, 64, 0), # Dim gr +een mDefaultColour => Wx::Colour->new(0, 0, 0), # Black mCounter => 0, # use for up/down c +ounter mToggle => 0, # use to toggle the : mBlinkFlag => 0, # use to enable blinking +the : ); my %wxDigitData = ( # Actual string to be displ +ayed value => "", decimal => 0, # 1=true turns on the deci +mal point for digit ); my %ctbl = ( # Map character to segments - 0 => 0x3F, # Not defined in the 7-segme +nt "abcdefg" format 1 => 0x14, 2 => 0x6D, # ***0*** Bit Num +bers -654 3210 3 => 0x75, # * * 4 => 0x56, # 1 2 5 => 0x73, # * * 6 => 0x7B, # ***6*** 7 => 0x15, # * * 8 => 0x7F, # 3 4 9 => 0x77, # * * A => 0x5F, # ***5*** 7 B => 0x7A, C => 0x2B, D => 0x7C, E => 0x6B, F => 0x4B, '-' => 0x40, '_' => 0x20, '^' => 0x47, # code for degree symbol '=' => 0x61, # code for undefined symbo +l ' ' => 0x00, # code for a space ); # Other options could be added # # Paint the simulated LCD Display ------------------------------------ +---------------------- # sub Draw { my($class, $self, $lcd) = @_; my $disp = Wx::PaintDC->new($self); # Create paint +device context my( $dw, $dh) = $disp->GetSizeWH(); # Calculate dis +play scaling my $bw = GetBitmapWidth(); my $bh = GetBitmapHeight(); my $xs = $dw/$bw; my $ys = $dh/$bh; my $as = $xs > $ys ? $ys : $xs; $disp->SetUserScale($as, $as); $disp->SetDeviceOrigin((($dw-$bw*$as)/2), (($dh-$bh*$as)/2)); DoDrawing($disp, $lcd); # Paint the display } sub DoDrawing { my($dc, $lcd) = @_; my @cbuf = reverse(split(//,$lcd->mValue)); # Process o +ne character at a time my $cbuflen = @cbuf; if($cbuflen > $lcd->mNumberDigits) { # Truncate string +to max display width $cbuflen = $lcd->mNumberDigits; } my $ctr = 0; my $seg = 0; while($ctr < $cbuflen) { if($cbuf[$ctr] ne '.') { # Skip decimal point, +not drawn as a character $wxDigitData{value} = $cbuf[$ctr]; $wxDigitData{decimal} = 0; if($cbuf[$ctr-1] eq '.') {$wxDigitData{decimal} = 1;} # Turn o +n decimal point for this digit DrawDigit($dc, $seg, $lcd); $seg++; } $ctr++ } } sub DrawDigit { my($dc, $digit, $lcd) = @_; my $value = $wxDigitData{value}; my $dec = Decode($value); if($value eq ':') { # Draw a colon(:) DrawTwoDots($dc, $digit, $lcd); } else { my $ctr = 0; while($ctr < $lcd->LCD_Number_Segments-1) { DrawSegment($dc, $digit, $ctr, ($dec>>$ctr)&1, $lcd); $ctr++; } DrawSegment($dc, $digit, 7, $wxDigitData{decimal}, $lcd); # + Draw the decimal point } } sub DrawTwoDots { # Draws a colon(:) my($dc, $digit, $lcd) = @_; my $sl = $lcd->mSegmentLen; my $sw = $lcd->mSegmentWidth; my $sp = $lcd->mSpace; my $x = DigitX($digit); my $y = DigitY($digit); $x += ($sl/2)-$sw; $y += ($sl/2)-$sw; my $brushOn = Wx::Brush->new($lcd->mLightColour, wxSOLID); $dc->SetBrush($brushOn); $dc->SetPen(Wx::Pen->new($lcd->mDefaultColour, 1, wxSOLID)); $dc->DrawEllipse($x, $y, $sw*2, $sw*2); $y += $sl; $dc->DrawEllipse($x, $y, $sw*2, $sw*2); } sub DrawSegment { my($dc, $digit, $segment, $state, $lcd) = @_; my $sl = $lcd->mSegmentLen; my $sw = $lcd->mSegmentWidth; my $x = DigitX($digit); my $y = DigitY($digit); my $brushOn = Wx::Brush->new($lcd->mLightColour, wxSOLID); my $brushOff = Wx::Brush->new($lcd->mGrayColour, wxSOLID); if($state) { # bit set for On segment $dc->SetBrush($brushOn); # Bright color for On +segment } else { # bit cleared for Off segment $dc->SetBrush($brushOff); # Dim color for Off s +egment } $dc->SetPen(Wx::Pen->new($lcd->mDefaultColour, 1, wxSOLID)); my @points; # Verticies for 4 sided seg +ments my @p6; # Verticies for the 6 sided seg +ment given($segment) { when(0) { $points[0] = Wx::Point->new($x, $y); $points[1] = Wx::Point->new($x+$sl, $y); $points[2] = Wx::Point->new($x+$sl-$sw, $y+$sw); $points[3] = Wx::Point->new($x+$sw, $y+$sw); } when(1) { $points[0] = Wx::Point->new($x, $y); $points[1] = Wx::Point->new($x, $y+$sl); $points[2] = Wx::Point->new($x+$sw, $y+$sl-$sw/2); $points[3] = Wx::Point->new($x+$sw, $y+$sw); } when(2) { $x += $sl-$sw; $points[0] = Wx::Point->new($x,$y+$sw); $points[1] = Wx::Point->new($x+$sw, $y); $points[2] = Wx::Point->new($x+$sw, $y+$sl); $points[3] = Wx::Point->new($x, $y+$sl-$sw/2); } when(3) { $y += $sl; $points[0] = Wx::Point->new($x, $y); $points[1] = Wx::Point->new($x, $y+$sl); $points[2] = Wx::Point->new($x+$sw, $y+$sl-$sw); $points[3] = Wx::Point->new($x+$sw, $y+$sw-$sw/2); } when(4) { $x += $sl-$sw; $y += $sl; $points[0] = Wx::Point->new($x, $y+$sw/2); $points[1] = Wx::Point->new($x+$sw, $y); $points[2] = Wx::Point->new($x+$sw, $y+$sl); $points[3] = Wx::Point->new($x, $y+$sl-$sw); } when(5) { $y += 2*$sl-$sw; $points[0] = Wx::Point->new($x+$sw, $y); $points[1] = Wx::Point->new($x+$sl-$sw, $y); $points[2] = Wx::Point->new($x+$sl, $y+$sw); $points[3] = Wx::Point->new($x, $y+$sw); } when(6) { $y += $sl-$sw/2; $p6[0] = Wx::Point->new($x, $y+$sw/2); $p6[1] = Wx::Point->new($x+$sw, $y); $p6[2] = Wx::Point->new($x+$sl-$sw, $y); $p6[3] = Wx::Point->new($x+$sl, $y+$sw/2); $p6[4] = Wx::Point->new($x+$sl-$sw, $y+$sw); $p6[5] = Wx::Point->new($x+$sw, $y+$sw); } default {} } if($segment < 6) { # Draw the 4 sided segme +nts(0-5) $dc->DrawPolygon(\@points, 0, 0); } elsif($segment == 6) { # Draw the 6 sided segme +nt(6) $dc->DrawPolygon(\@p6, 0, 0); } else { # Draw the decimal point(7) $y += 2*$sl; $x += $sl; $dc->DrawEllipse($x+1, $y-$sw, $sw, $sw); } } sub Decode { $ctbl{$_[0]} // $ctbl{'='}} # Table lookup # for character translation # # Support subs ------------------------------------------------------- +--------------------- # sub GetDigitWidth { return $defaults{mSegmentLen} + $defaults{mSegmentWidth} + $defaul +ts{mSpace}; } sub GetDigitHeight { return ($defaults{mSegmentLen}*2) + ($defaults{mSpace}*2); } sub GetBitmapWidth { return ($defaults{mNumberDigits}*GetDigitWidth()) + $defaults{mSpa +ce}; } sub GetBitmapHeight { return GetDigitHeight(); } sub DigitX { my($digit) = @_; return GetBitmapWidth()-(($digit+1)*GetDigitWidth()); } sub DigitY { my($digit) = @_; return $defaults{mSpace}; } sub SetNumberDigits { my $ndigits = @_; $defaults{mNumberDigits} = $ndigits; } sub SetValue { my $value = @_; $defaults{mValue} = $value; } sub GetValue { return $defaults{mValue}; } sub GetNumberDigits { return $defaults{mNumberDigits}; } sub SetLightColour { my($ref) = @_; $defaults{mLightColour} = $ref; } sub SetGrayColour { my($ref) = @_; $defaults{mGrayColour} = $ref; } sub GetLightColour { return $defaults{mLightColour}; } sub GetGrayColour { return $defaults{mGrayColour}; } sub GetDigitsNeeded { my($string) = @_; $string =~ s/\.//g; return strlen($string); } package LCDdisplay::Data; use strict; use warnings; use Class::Accessor::Fast; use base qw(Class::Accessor::Fast); __PACKAGE__->mk_accessors(qw(mSegmentLen mSegmentWidth mSpace mNumberD +igits mValue LCD_Number_Segments mLightColour m +GrayColour mDefaultColour mCounter mToggle mBlinkFla +g)); sub new {shift->SUPER::new(@_, \%defaults);} 1;

James

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