<?xml version="1.0" encoding="windows-1252"?>
<node id="1014312" title="wxPerl Simulated Round Panel Meter" created="2013-01-20 12:54:41" updated="2013-01-20 12:54:41">
<type id="1042">
CUFP</type>
<author id="982107">
jmlynesjr</author>
<data>
<field name="doctext">
&lt;p&gt;Another wxPerl example ported from the "wxBook" and wxIndustrial Controls. This one draws a round panel meter. Move your mouse across the meter display to move the needle.&lt;/p&gt;

&lt;p&gt;Use this meter with Device::Serialport::Arduino to display analog data from your Arduino of choice.&lt;/p&gt;

&lt;readmore&gt;&lt;code&gt;#! /home/pete/CitrusPerl/perl/bin/perl
#
# AngularMeter.pl
# This script draws a simulated round panel meter.
#
#
# Written in wxPerl. Tested on Citrus Perl 5.16 with wxWidgets 2.8.x.
# Ported by: James M. Lynes. Jr.
# Last Modified Date: January 20, 2013
#
# Adapted from AngularMeter.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
#
# Added limit checking with green/red zones - Normal/Hi-limit
#   could reverse colors to alarm on a low limit if needed.
# Added animation - ***Needle follows left/right mouse movement***
# Added Meter identification label
# Could add mouse click event to capture value at that point
#   and use the meter as an input device.
#
use 5.010;
use strict;
use warnings;
use Wx qw(:everything :allclasses);
use Wx::Event qw( EVT_PAINT EVT_SIZE EVT_MOTION);
use Math::Trig;
#
# Configuration Data ------------------------------------------------------------------
#
my $MeterWidth = 350;					# Define the meter size
my $MeterHeight = $MeterWidth;				# Must be square to look right

my $ScaledVal = 0;
my $RealVal = 0;

my $Tick = 9;						# Number of tic marks
my $Sec = 2;						# Number of sections - green/red

my $RangeStart = 0;					# Define Meter Span - Min
my $RangeEnd = 100;					#                   - Max
my $AlarmLimit = 65;					# Alarm Limit
my $AngleStart = -20;					# East is 0 degrees, + is CCW
my $AngleEnd = 200;

my @SectorColours = (wxGREEN, wxRED);			# Only using two sections
my $BackColour = wxLIGHT_GREY;
my $NeedleColour = wxBLACK;
my $BorderColour = wxBLUE;

my $PI = 4.0*atan(1.0);
my $Font = Wx::Font-&gt;new(10, wxFONTFAMILY_SWISS, wxNORMAL, wxNORMAL);
my $DrawCurrent = 1;					# Turn on/off value displayed as text
my $Label = "Process Temperature - degC";		# Label Meter at bottom of display
my $ramp = 25;						# Initial value/needle position
my $lastpos = 0;					# Last mouse position
#
# Main Application -----------------------------------------------------------------------
#
my $app = Wx::SimpleApp-&gt;new;
my $frame = Wx::Frame-&gt;new(undef, -1, "Simulated Round Panel Meter",
			   wxDefaultPosition, [$MeterWidth, $MeterHeight]);
SetValue($ramp);					# Initial value/needle position
EVT_PAINT( $frame, \&amp;onPaint );
EVT_SIZE( $frame, \&amp;onSize );
EVT_MOTION( $frame, \&amp;onMotion );
$frame-&gt;Show;
$app-&gt;MainLoop;
#
# Dismiss a size event
#
sub onSize{
    my($self, $event) = @_;
    $event-&gt;Skip();
}
sub onMotion {						# Change the setpoint value and redraw
    my($self, $event) = @_;
    my $x = $event-&gt;GetPosition-&gt;x;			# Current mouse position
    if($x &gt; $lastpos) {$ramp += 1; $lastpos = $x}	# Mouse right, increment needle
    if($x &lt; $lastpos) {$ramp -= 1; $lastpos = $x}	# Mouse left, decrement needle
    if($ramp &gt;= $RangeEnd) {$ramp = $RangeEnd;}		# Clamp to max range
    if($ramp &lt;= $RangeStart) {$ramp = $RangeStart;}	# Clamp to min range
    SetValue($ramp);
    $self-&gt;Refresh;
    $self-&gt;Update;
}
#
# Paint the simulated LCD Display ----------------------------------------------------------
#
sub onPaint {
    my($self, $event) = @_;
    my $dc = Wx::PaintDC-&gt;new($self);
    my( $w, $h) = $dc-&gt;GetSizeWH();
    my $memdc = Wx::MemoryDC-&gt;new();
    $memdc-&gt;SelectObject(Wx::Bitmap-&gt;new($MeterWidth, $MeterHeight));
    my $brush = Wx::Brush-&gt;new($BackColour, wxSOLID);
    $memdc-&gt;SetBackground($brush);
    $memdc-&gt;SetBrush($brush);
    $memdc-&gt;Clear();
    my $pen = Wx::Pen-&gt;new($BorderColour, 2, wxSOLID);
    $memdc-&gt;SetPen($pen);
    $memdc-&gt;DrawRectangle(0, 0, $w, $h);
    DrawSectors($memdc);
    if($Tick &gt; 0) {DrawTicks($memdc);}
    DrawNeedle($memdc);
    if($DrawCurrent) {					# Display current value as text
        my $valuetext = sprintf("%d", $RealVal);
        my @te = $memdc-&gt;GetTextExtent($valuetext);
        my $x = ($w-$te[0])/2;
        $memdc-&gt;SetFont($Font);
        $memdc-&gt;DrawText($valuetext, $x, ($h/2)+20);
    }
    DrawLabel($memdc);
    $dc-&gt;Blit(0, 0, $w, $h, $memdc, 0, 0);
}
sub DrawNeedle {
    my($dc) = @_;
    my($w, $h) = $dc-&gt;GetSizeWH();
    my $pen = Wx::Pen-&gt;new($NeedleColour, 1, wxSOLID);
    $dc-&gt;SetPen($pen);
    my $brush = Wx::Brush-&gt;new($NeedleColour, wxSOLID);
    $dc-&gt;SetBrush($brush);
    my $val = ($ScaledVal + $AngleStart) * $PI/180;
    my $dyi = sin($val-90)*2;
    my $dxi = cos($val-90)*2;
    my @points;
    $points[0] = Wx::Point-&gt;new($w/2-$dxi, $h/2-$dyi);
    $dxi = cos($val) * ($h/2-4);
    $dyi = sin($val) * ($h/2-4);
    $points[2] = Wx::Point-&gt;new($w/2-$dxi, $h/2-$dyi);
    $dxi = cos($val+90)*2;
    $dyi = sin($val+90)*2;
    $points[4] = Wx::Point-&gt;new($w/2-$dxi, $h/2-$dyi);
    $points[5] = $points[0];
    $val = ($ScaledVal + $AngleStart + 1) * $PI/180;
    $dxi = cos($val) * ($h/2-10);
    $dyi = sin($val) * ($h/2-10);
    $points[3] = Wx::Point-&gt;new($w/2-$dxi, $h/2-$dyi); 
    $val = ($ScaledVal + $AngleStart - 1) * $PI/180;
    $dxi = cos($val) * ($h/2-10);
    $dyi = sin($val) * ($h/2-10);
    $points[1] = Wx::Point-&gt;new($w/2-$dxi, $h/2-$dyi);
    $dc-&gt;DrawPolygon(\@points, 0, 0, wxODDEVEN_RULE); 
    $brush = Wx::Brush-&gt;new(wxWHITE, wxSOLID);			# Draw white dot at base of needle
    $dc-&gt;SetBrush($brush);
    $dc-&gt;DrawCircle($w/2, $h/2, 4);  
}
sub DrawSectors {
    my($dc) = @_;
    my($w, $h) = $dc-&gt;GetSizeWH();
    my $pen = Wx::Pen-&gt;new(wxBLACK, 1, wxSOLID);
    $dc-&gt;SetPen($pen);
    my $starc = $AngleStart;
    my $endarc = $starc + (($AngleEnd - $AngleStart) * ($AlarmLimit/$RangeEnd));
    my $ctr = 0;
    while($ctr &lt; $Sec) {
          my $brush = Wx::Brush-&gt;new($SectorColours[$ctr], wxSOLID);
          $dc-&gt;SetBrush($brush);
          $dc-&gt;DrawEllipticArc(0, 0, $w, $h, 180-$endarc, 180-$starc);
          $starc = $endarc;
          $endarc = $AngleEnd;
          $ctr++;
    }
    my $val = $AngleStart * $PI / 180;
    my $dx = cos($val) * $h/2;
    my $dy = sin($val) * $h/2;
    $dc-&gt;DrawLine($w/2, $h/2, $w/2-$dx, $h/2-$dy);
    $val = $AngleEnd * $PI / 180;
    $dx = cos($val) * $h/2;
    $dy = sin($val) * $h/2;
    $dc-&gt;DrawLine($w/2, $h/2, $w/2-$dx, $h/2-$dy);
}
sub DrawTicks {
    my($dc) = @_;
    my($w, $h) = $dc-&gt;GetSizeWH();
    my $interval = ($AngleEnd - $AngleStart) / ($Tick +1);
    my $valint = $interval + $AngleStart;
    my $ctr = 0;
    while($ctr &lt; $Tick) {
        my $val = $valint * $PI/180;
        my $dx = cos($val) * $h/2;
        my $dy = sin($val) * $h/2;
        my $tx = cos($val) * (($h/2)-10);
        my $ty = sin($val) * (($h/2)-10);
        $dc-&gt;DrawLine($w/2-$tx, $h/2-$ty, $w/2-$dx, $h/2-$dy);
        my $DeltaRange = $RangeEnd - $RangeStart;
        my $DeltaAngle = $AngleEnd - $AngleStart;
        my $Coeff = $DeltaAngle / $DeltaRange;
        my $rightval = (($valint - $AngleStart) / $Coeff) + $RangeStart;
        my $string = sprintf("%d", $rightval+.5);
        my($tew, $teh, $dct, $ext) = $dc-&gt;GetTextExtent($string);
        $val = ($valint - 4) * $PI/180;
        $tx = cos($val) * (($h/2)-12);
        $ty = sin($val) * (($h/2)-12);
        $dc-&gt;SetFont($Font);
        $dc-&gt;DrawRotatedText($string, $w/2-$tx, $h/2-$ty, 90-$valint);
        $valint = $valint + $interval;
        $ctr++;
    }
}
sub SetValue {							# Scale the value for display
    my($Value) = @_;
    my $DeltaRange = $RangeEnd - $RangeStart;
    my $RangeZero = $DeltaRange - $RangeStart;
    my $DeltaAngle = $AngleEnd - $AngleStart;
    my $Coeff = $DeltaAngle / $DeltaRange;
    $ScaledVal = ($Value - $RangeStart) * $Coeff;
    $RealVal = $Value;
}
sub DrawLabel {							# Draw a label at bottom of meter
    my($dc) = @_;
    my($w, $h) = $dc-&gt;GetSizeWH();
    my @te = $dc-&gt;GetTextExtent($Label);
    my $x = ($w-$te[0])/2;
    $dc-&gt;DrawText($Label, $x, $h-25);
}&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>
