<?xml version="1.0" encoding="windows-1252"?>
<node id="1016975" title="wxPerl LCD Clock" created="2013-02-04 11:41:36" updated="2013-02-04 11:41:36">
<type id="1042">
CUFP</type>
<author id="982107">
jmlynesjr</author>
<data>
<field name="doctext">
&lt;p&gt;As suggested by [ww] in his response to my [wxPerl Simulated 7 Segment LCD Display] post, it's now available in clock form.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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..:).&lt;/p&gt;

&lt;p&gt;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...&lt;/p&gt;

&lt;p&gt;Update3: Derived class version done and will be the version posted to github when I can get to it.&lt;/p&gt;

&lt;p&gt;Update4: Alarm Clock version completed. Uses a wxMediaControl within an AudibleAlarm.pm class to play a selected MP3 file as the wakeup alarm sound.&lt;/p&gt;

&lt;p&gt;Update5: Latest code now posted to GitHub. A new repository was created, so the URL above was changed.&lt;/p&gt;

&lt;p&gt;LCDClock.pl&lt;/p&gt;
&lt;readmore&gt;&lt;code&gt;#! /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 characters
# 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-&gt;new();
$app-&gt;MainLoop;

package App;
use strict;
use warnings;
use base 'Wx::App';
sub OnInit {
    my $frame = Frame-&gt;new();
    $frame-&gt;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-&gt;SUPER::new(undef, -1, "LCD Clock", 
                              wxDefaultPosition, [400, 200]);

    EVT_PAINT( $self, \&amp;onPaint );				# Initialize event handlers
    EVT_SIZE( $self, \&amp;onSize );
    EVT_TIMER( $self, -1, \&amp;onTimer);

    $self-&gt;{LCD} = LCDdisplay::Data-&gt;new();			# Create an LCD object
    $self-&gt;{LCD}-&gt;mBlinkFlag(1);				# Blink colon ? 1-&gt;yes
    $self-&gt;{LCD}-&gt;mNumberDigits(5);				# 5 digit display including :
    $self-&gt;{LCD}-&gt;mValue("");					# Initialize the display
    $self-&gt;SetBackgroundColour($self-&gt;{LCD}-&gt;mDefaultColour);	# Black background

    my $timer = Wx::Timer-&gt;new($self);				# Initialize 1 second timer
    $timer-&gt;Start(1000);			

    return $self;
}
1;
#
# Dismiss a size event
#
sub onSize{
    my($self, $event) = @_;
    $event-&gt;Skip();
}
#
# Draw the LCD
#
sub onPaint {
    my($self, $event) = @_;
    LCDdisplay-&gt;Draw($self, $self-&gt;{LCD});
}
#
# Format the time for display
#
sub onTimer {
    my($self, $event) = @_;
    my($min, $hour) = (localtime)[1,2]; 
    my $colon = ":";
    if($self-&gt;{LCD}-&gt;mBlinkFlag) {				# Blink the colon?
        if($self-&gt;{LCD}-&gt;mToggle) {				# yes, toggle the :
            $self-&gt;{LCD}-&gt;mToggle(0);
            $colon = ":";
        }
        else {
            $self-&gt;{LCD}-&gt;mToggle(1);
            $colon = " ";
        }
    }
    my $minstr = sprintf("%02d", $min);
    my $hourstr = sprintf("%02d", $hour);
    $self-&gt;{LCD}-&gt;mValue($hourstr . $colon . $minstr);
    $self-&gt;Refresh(0);
}&lt;/code&gt;&lt;/readmore&gt;
&lt;p&gt;LCDdisplay.pm&lt;/p&gt;
&lt;readmore&gt;&lt;code&gt;#! /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 space
# 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, &amp; Csomor
#
#
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         =&gt; 40,
		 mSegmentWidth       =&gt; 10,
		 mSpace              =&gt; 5,
		 mNumberDigits       =&gt; 6,			# width of mValue including . or :
		 mValue		     =&gt; "",			# Default string to be displayed
		 LCD_Number_Segments =&gt; 8,			# Segment 7 is the decimal point
		 mLightColour        =&gt; Wx::Colour-&gt;new(0, 255, 0),	# Bright green
		 mGrayColour         =&gt; Wx::Colour-&gt;new(0, 64, 0),	# Dim green
		 mDefaultColour      =&gt; Wx::Colour-&gt;new(0, 0, 0),	# Black
                 mCounter	     =&gt; 0,			# use for up/down counter
		 mToggle	     =&gt; 0,			# use to toggle the :
		 mBlinkFlag	     =&gt; 0,			# use to enable blinking the :
	       );

my %wxDigitData = (						# Actual string to be displayed
		   value		     =&gt; "",
		   decimal		     =&gt; 0,		# 1=true turns on the decimal point for digit
	          );

my %ctbl = (							# Map character to segments -
		0		     =&gt; 0x3F,			# Not defined in the 7-segment "abcdefg" format
		1		     =&gt; 0x14,
		2		     =&gt; 0x6D,			#    ***0***         Bit Numbers -654 3210
		3		     =&gt; 0x75,			#   *       *
		4		     =&gt; 0x56,			#   1       2
		5		     =&gt; 0x73,			#   *       *
		6		     =&gt; 0x7B,			#    ***6***
		7		     =&gt; 0x15,			#   *       *
		8		     =&gt; 0x7F,			#   3       4
		9		     =&gt; 0x77,			#   *       *
		A		     =&gt; 0x5F,			#    ***5*** 7
		B		     =&gt; 0x7A,
		C		     =&gt; 0x2B,
		D		     =&gt; 0x7C,
		E		     =&gt; 0x6B,
		F		     =&gt; 0x4B,
		'-'		     =&gt; 0x40,
		'_'		     =&gt; 0x20,
		'^'		     =&gt; 0x47,			# code for degree symbol
		'='		     =&gt; 0x61,			# code for undefined symbol
		' '		     =&gt; 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-&gt;new($self);				# Create paint device context
    my( $dw, $dh) = $disp-&gt;GetSizeWH();				# Calculate display scaling
    my $bw = GetBitmapWidth();
    my $bh = GetBitmapHeight();
    my $xs = $dw/$bw;
    my $ys = $dh/$bh;
    my $as = $xs &gt; $ys ? $ys : $xs;
    $disp-&gt;SetUserScale($as, $as);
    $disp-&gt;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-&gt;mValue));			# Process one character at a time
    my $cbuflen = @cbuf;
    if($cbuflen &gt; $lcd-&gt;mNumberDigits) {			# Truncate string to max display width
        $cbuflen = $lcd-&gt;mNumberDigits;
    }
    my $ctr = 0;
    my $seg = 0;
    while($ctr &lt; $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 on 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 &lt; $lcd-&gt;LCD_Number_Segments-1) {
            DrawSegment($dc, $digit, $ctr, ($dec&gt;&gt;$ctr)&amp;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-&gt;mSegmentLen;
    my $sw = $lcd-&gt;mSegmentWidth;
    my $sp = $lcd-&gt;mSpace;
    my $x = DigitX($digit);
    my $y = DigitY($digit);
    $x += ($sl/2)-$sw;
    $y += ($sl/2)-$sw;
    my $brushOn = Wx::Brush-&gt;new($lcd-&gt;mLightColour, wxSOLID);
    $dc-&gt;SetBrush($brushOn);
    $dc-&gt;SetPen(Wx::Pen-&gt;new($lcd-&gt;mDefaultColour, 1, wxSOLID));
    $dc-&gt;DrawEllipse($x, $y, $sw*2, $sw*2);
    $y += $sl;
    $dc-&gt;DrawEllipse($x, $y, $sw*2, $sw*2);
}
sub DrawSegment {
    my($dc, $digit, $segment, $state, $lcd) = @_;
    my $sl = $lcd-&gt;mSegmentLen;
    my $sw = $lcd-&gt;mSegmentWidth;
    my $x = DigitX($digit);
    my $y = DigitY($digit);
    my $brushOn = Wx::Brush-&gt;new($lcd-&gt;mLightColour, wxSOLID);
    my $brushOff = Wx::Brush-&gt;new($lcd-&gt;mGrayColour, wxSOLID);
    if($state) {						# bit set for On segment
        $dc-&gt;SetBrush($brushOn);				# Bright color for On segment
    }
    else {							# bit cleared for Off segment
        $dc-&gt;SetBrush($brushOff);				# Dim color for Off segment
    }
    $dc-&gt;SetPen(Wx::Pen-&gt;new($lcd-&gt;mDefaultColour, 1, wxSOLID));
    my @points;							# Verticies for 4 sided segments
    my @p6;							# Verticies for the 6 sided segment
    given($segment) {
        when(0) {
                  $points[0] = Wx::Point-&gt;new($x, $y);
                  $points[1] = Wx::Point-&gt;new($x+$sl, $y);
                  $points[2] = Wx::Point-&gt;new($x+$sl-$sw, $y+$sw);
                  $points[3] = Wx::Point-&gt;new($x+$sw, $y+$sw);
         }
        when(1) {
                  $points[0] = Wx::Point-&gt;new($x, $y);
                  $points[1] = Wx::Point-&gt;new($x, $y+$sl);
                  $points[2] = Wx::Point-&gt;new($x+$sw, $y+$sl-$sw/2);
                  $points[3] = Wx::Point-&gt;new($x+$sw, $y+$sw);
         }
        when(2) {
                  $x += $sl-$sw;
                  $points[0] = Wx::Point-&gt;new($x,$y+$sw);
                  $points[1] = Wx::Point-&gt;new($x+$sw, $y);
                  $points[2] = Wx::Point-&gt;new($x+$sw, $y+$sl);
                  $points[3] = Wx::Point-&gt;new($x, $y+$sl-$sw/2);
         }
        when(3) {
                  $y += $sl;
                  $points[0] = Wx::Point-&gt;new($x, $y);
                  $points[1] = Wx::Point-&gt;new($x, $y+$sl);
                  $points[2] = Wx::Point-&gt;new($x+$sw, $y+$sl-$sw);
                  $points[3] = Wx::Point-&gt;new($x+$sw, $y+$sw-$sw/2);
         }
        when(4) {
                  $x += $sl-$sw;
                  $y += $sl;
                  $points[0] = Wx::Point-&gt;new($x, $y+$sw/2);
                  $points[1] = Wx::Point-&gt;new($x+$sw, $y);
                  $points[2] = Wx::Point-&gt;new($x+$sw, $y+$sl);
                  $points[3] = Wx::Point-&gt;new($x, $y+$sl-$sw);
         }
        when(5) {
                  $y += 2*$sl-$sw;
                  $points[0] = Wx::Point-&gt;new($x+$sw, $y);
                  $points[1] = Wx::Point-&gt;new($x+$sl-$sw, $y);
                  $points[2] = Wx::Point-&gt;new($x+$sl, $y+$sw);
                  $points[3] = Wx::Point-&gt;new($x, $y+$sw);
         }
        when(6) {
                  $y += $sl-$sw/2;
                  $p6[0] = Wx::Point-&gt;new($x, $y+$sw/2);
                  $p6[1] = Wx::Point-&gt;new($x+$sw, $y);
                  $p6[2] = Wx::Point-&gt;new($x+$sl-$sw, $y);
                  $p6[3] = Wx::Point-&gt;new($x+$sl, $y+$sw/2);
                  $p6[4] = Wx::Point-&gt;new($x+$sl-$sw, $y+$sw);
                  $p6[5] = Wx::Point-&gt;new($x+$sw, $y+$sw);
        }
        default {}
    }
    if($segment &lt; 6) {						# Draw the 4 sided segments(0-5)
       $dc-&gt;DrawPolygon(\@points, 0, 0);
    }
    elsif($segment == 6) {					# Draw the 6 sided segment(6)
        $dc-&gt;DrawPolygon(\@p6, 0, 0);
    }
    else {							# Draw the decimal point(7)
        $y += 2*$sl;
        $x += $sl;
        $dc-&gt;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} + $defaults{mSpace};
}
sub GetDigitHeight {
    return ($defaults{mSegmentLen}*2) + ($defaults{mSpace}*2);
}
sub GetBitmapWidth {
    return ($defaults{mNumberDigits}*GetDigitWidth()) + $defaults{mSpace};
}
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__-&gt;mk_accessors(qw(mSegmentLen mSegmentWidth mSpace mNumberDigits
                             mValue LCD_Number_Segments mLightColour mGrayColour
                             mDefaultColour mCounter mToggle mBlinkFlag));

sub new {shift-&gt;SUPER::new(@_, \%defaults);}
1;&lt;/code&gt;&lt;/readmore&gt;




&lt;p&gt;James&lt;/p&gt;
&lt;div class="pmsig"&gt;&lt;div class="pmsig-982107"&gt;
&lt;p&gt;There's never enough time to do it right, but always enough time to do it over...&lt;/p&gt;
&lt;/div&gt;&lt;/div&gt;</field>
</data>
</node>
