Hi,

Apologies if this message arrives twice but my last few mails have been swallowed by the ether so sending via different mail host.

Attached are two files, KWxLinearMeter.pm and lmeterdemo.pl.
As suggested by the names KWxLinearMeter.pm is the wrapped LinearMeter control and lmeterdemo.pl is a simple app that displays some meter instances.

KWxLinearMeter is implemented as far as possible as a simple translation of LinearMeter.h and LinearMeter.cpp from the original source. It could be implemented in a more Perlish way but I wanted the translation to be obvious.

In your original code, you are creating window controls (or panels at least) in your EVT_PAINT handler. This is a mistake. The EVT_PAINT handler is called whenever part of your window needs refreshing. That would be whenever the window is shown, resized, covered / uncovered by other windows, amongst other things.

Hope it helps.

Mark

On 23/01/2013 15:43, James Lynes wrote:
Good morning:

I've recently ported several C++ wxIndustrial Controls to wxPerl and I'm
now abstracting the LinearMeter example  into a module so that multiple
meters can exist on a window. I have a couple of questions that I hope the
group can help me with.

First: I create a frame. Create 6 panels. Write a label on each panel(just
to see if the panel is created in the correct location, it is). Create 6
meter objects. And draw the 6 meters(for testing just a filled box).

Instead of getting a window with 6 boxes with labels, I get a blank window
that gets 6 boxes drawn on it. Then the window goes back to the default
background color with 6 labels written on it. The filled boxes are
overwritten. I'm looking for 6 filled boxes with a label on them. What have
I missed?

Second: In the original port I have the following construct that works fine:

              my @TagsVal;
              push(@TagsVal, $val);

             In my abstracted version I have translated this to:

             $self->{TAGSVAL} = [];                       # Object array
definition

             sub TagsVal {                                     # Object
accessor definition
             my $self = shift;
             if(@_) {$self->{TAGSVAL} = @_}
             return $self->{TAGSVAL};
             }

             push($Meter->TagsVal(), $val);             # Don't think this
is correct syntax???
                                                                      # want
to push $val into the object TAGSVAL array


Thanks for your input!

James

Source files attached: LinearMeter.pl      Original Port
                                  LinearMeter.pm    Object version
                                  LM.pl                   Object Main
Program

#########################################################################################
# Description:  Demo for KWx::LinearMeter
# Created       Fri Jan 25 21:07:51 2013
# svn id        $Id:$
# Copyright:    Copyright (c) 2013 Mark Dootson
# Licence:      This work is free software; you can redistribute it and/or modify it 
#               under the terms of the GNU General Public License as published by the 
#               Free Software Foundation; either version 3 of the License, or any later 
#               version.
#########################################################################################

use strict;
use warnings;
use Wx;

#------------------------------------------
 package Demo::MainWindow;
#------------------------------------------
use strict;
use warnings;
use Wx qw( wxTheApp :panel :window :id :misc :slider :colour :sizer :font);
use base qw( Wx::Frame );
use KWxLinearMeter;
use Wx::Event qw( EVT_COMMAND_SCROLL EVT_CLOSE );

sub new {
    my $class = shift;
    my $self = $class->SUPER::new( @_ );
    
    my @initdata = ( 70, 0, 200 ); # val, min, max
    
    #-----------------------------------
    # Create Controls
    #-----------------------------------
    
    my $mainpanel = Wx::Panel->new($self, wxID_ANY,
        wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL|wxBORDER_NONE);
    
    my $draglabel = Wx::StaticText->new($mainpanel, wxID_ANY, 'Drag Me');
    
    my $slider = Wx::Slider->new($mainpanel, wxID_ANY,
        @initdata, , wxDefaultPosition, wxDefaultSize, wxSL_VERTICAL);
    
    # add some Meters
    
    my @bars;
    for (my $i = 1; $i < 5; $i++) {
        my $name = qq(Meter $i);
        my $mlabel = Wx::StaticText->new($mainpanel, wxID_ANY, $name);
        my $meter = KWxLinearMeter->new($mainpanel);
        $meter->SetRangeVal($initdata[1], $initdata[2]);
	$meter->SetActiveBarColour(wxBLUE);
	$meter->SetPassiveBarColour(wxWHITE);
	$meter->SetTxtLimitColour(wxBLACK);
	$meter->SetTxtValueColour(wxRED);
	$meter->SetBorderColour(wxRED);
	$meter->SetTagsColour(wxGREEN) ;
	$meter->SetTxtFont(Wx::Font->new(8, wxFONTFAMILY_MODERN, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL ));
	$meter->SetOrizDirection(0);
	$meter->ShowCurrent(1) ;
	$meter->ShowLimits(1) ;
	$meter->SetValue($initdata[0]);
        
        for ( 20, 40, 60, 80, 120, 140, 160, 180 ) {
            $meter->AddTag($_) ;
        }
        
	push (@bars, [ $mlabel, $meter] );
    }
    $self->{_bars} = \@bars;
    
    #------------------------------------
    # Events
    #------------------------------------
    
    EVT_COMMAND_SCROLL($self, $slider, sub { shift->_evt_slider( @_ ); } );
    EVT_CLOSE($self,sub { shift->_evt_close( @_ ); } );
    
    #------------------------------------
    # Layout
    #------------------------------------
    
    my $mainsizer  = Wx::BoxSizer->new(wxVERTICAL);
    my $panelsizer = Wx::BoxSizer->new(wxHORIZONTAL);
    my $slsizer = Wx::BoxSizer->new(wxVERTICAL);
    
    $slsizer->Add($draglabel,0,wxALL|wxALIGN_CENTRE, 0);
    $slsizer->Add($slider, 1, wxALL, 0);
    $panelsizer->Add($slsizer, 0, wxEXPAND|wxALL, 5);
    
    # add every meter
    for my $meter ( @bars ) {
        my $msizer = Wx::BoxSizer->new(wxVERTICAL);
        $msizer->Add($meter->[0], 0, wxALL|wxALIGN_CENTRE, 0);
        $msizer->Add($meter->[1], 1, wxALL|wxEXPAND, 0);
        $panelsizer->Add($msizer, 1, wxEXPAND|wxALL, 5);
    }
    
    $mainpanel->SetSizer($panelsizer);
    $mainsizer->Add($mainpanel,1,wxEXPAND|wxALL, 0);
    $self->SetSizer($mainsizer);
    
    return $self;
}

sub _evt_slider {
    my( $self, $event) = @_;
    my $pos = $event->GetPosition;
    
    # update all the meters to the same value
    for my $meter ( @{ $self->{_bars} } ) {
        $meter->[1]->SetValue($pos);
    }
}

sub _evt_close {
    my( $self, $event) = @_;
    $event->Skip(1);
    $self->Destroy;
}


#------------------------------------------
 package Demo::Application;
#------------------------------------------

use strict;
use warnings;
use Wx qw( :id );
use base qw( Wx::App );

sub new {
    my $class = shift;
    my $self = $class->SUPER::new( @_ );
    return $self;
}

sub OnInit {
    my $self = shift;
    
    $self->SetVendorName('MarkDemos');
    $self->SetAppName('Linear Meter Demonstration');
    
    my $mwin = Demo::MainWindow->new(undef, wxID_ANY, $self->GetAppName );
    $mwin->Show(1);
    $self->SetTopWindow( $mwin );
    
    return 1;
}

#------------------------------------------
 package main;
#------------------------------------------

my $app = Demo::Application->new;
$app->MainLoop;


1;
#########################################################################################
# Package       KWxLinearMeter
# Description:  Linear Meter
# Created       Fri Jan 25 19:12:49 2013
# SVN Id        $Id:$
# Licence:      This work is free software; you can redistribute it and/or 
modify it 
#               under the terms of the GNU General Public License as published 
by the 
#               Free Software Foundation; either version 3 of the License, or 
any later 
#               version.
#
# The code is based on the C++ implementation provided in wxIndustrialControls:
# at http://www.koansoftware.com/en/prd_svil_wxdownload.htm
#  
#  Name:        LinearMeter.cpp
#  Purpose:     wxIndustrialControls Library
#  Author:      Marco Cavallini <m.cavallini AT koansoftware.com>
#  Modified by: 
#  Copyright:   (C)2004-2006 Copyright by Koan s.a.s. - www.koansoftware.com
#########################################################################################

#########################################################################################

package KWxLinearMeter;

#########################################################################################

use strict;
use warnings;
use Wx qw( :misc :id :colour :font :brush :pen );
use base qw( Wx::Window );
use Wx::Event qw( EVT_PAINT EVT_SIZE);
use POSIX qw( ceil );

#-------------------------------------------
# Constructor
#-------------------------------------------

sub new {
    my $class = shift;
    # $_[0] = parent
    $_[1] =  wxID_ANY if not exists( $_[1] );           # windowid
    $_[2] =  wxDefaultPosition if not exists( $_[2] );  # pos
    $_[3] =  wxDefaultSize if not exists( $_[3] );      # size
    $_[4] =  0 if not exists( $_[4] );                  # style
    
    my $self = $class->SUPER::new( @_ );
    
    $self->{_privatedata} = KWxLinearMeter::Data->new({
        ActiveBar    => wxGREEN,
        PassiveBar   => wxWHITE,
        ValueColour  => wxRED,
        BorderColour => wxRED,
        LimitColour  => wxBLACK,
        TagsColour   => wxGREEN,
        ScaledVal    => 0,
        RealVal      => 0,
        Max          => 100,
        Min          => 0,
        DirOrizFlag  => 1,
        ShowCurrent  => 1,
        ShowLimits   => 1,
        Font         => wxNORMAL_FONT,
        Tags         => [],
    });
    
    EVT_PAINT( $self, sub { shift->_evt_on_paint( @_ ); } );
    EVT_SIZE( $self, sub { shift->_evt_on_size( @_ ); } );
    
    return $self;
}

#---------------------------------------------------
# Simple Accessors
# Could be implemented using one of the many 'Class'
# CPAN modules - but implemented as below to closely
# match C++ method naming
#---------------------------------------------------

sub _pdata { $_[0]->{_privatedata}; } 

sub SetRangeVal {
    my ($self, $min, $max ) = @_;
    $self->_pdata->Min($min);
    $self->_pdata->Max($max);
}

sub SetActiveBarColour { $_[0]->_pdata->ActiveBar($_[1]); }
sub SetPassiveBarColour { $_[0]->_pdata->PassiveBar($_[1]); }
sub SetBorderColour { $_[0]->_pdata->BorderColour($_[1]); }
sub SetTagsColour { $_[0]->_pdata->TagsColour($_[1]); }
sub SetTxtLimitColour { $_[0]->_pdata->LimitColour($_[1]); }
sub SetTxtValueColour { $_[0]->_pdata->ValueColour($_[1]); }
sub SetTxtFont { $_[0]->_pdata->Font($_[1]); }
sub SetOrizDirection { $_[0]->_pdata->DirOrizFlag($_[1]); }
sub ShowCurrent { $_[0]->_pdata->ShowCurrent($_[1]); }
sub ShowLimits { $_[0]->_pdata->ShowLimits($_[1]); }

sub GetValue { $_[0]->_pdata->RealVal; }

#--------------------------------------------------
# Public methods with real implementations
#--------------------------------------------------

sub SetValue {
    my( $self, $value) = @_;
    $self->_pdata->RealVal($value);
    $self->_calc_scaled_value;
    return;
}

sub AddTag {
    my( $self, $value) = @_;
    push @{ $self->_pdata->Tags }, $value;
    return;
}

#---------------------------------------------------
# Private methods
#---------------------------------------------------

sub _calc_scaled_value {
    my $self = shift;
    my ($w, $h) = $self->GetClientSizeWH;
    my $oridim = ( $self->_pdata->DirOrizFlag ) ? $w - 2 : $h - 2;
    my $coeff = $oridim / ( $self->_pdata->Max - $self->_pdata->Min );
    $self->_pdata->ScaledVal(ceil(( $self->_pdata->RealVal - 
$self->_pdata->Min) * $coeff));
    $self->Refresh(0);
}

sub _draw_limits {
    my($self, $dc, $w, $h) = @_;
    # only ever called from within _evt_on_paint
    
    $dc->SetFont( $self->_pdata->Font );
    $dc->SetTextForeground( $self->_pdata->LimitColour);

    if($self->_pdata->DirOrizFlag){
        my $text = sprintf("%d", $self->_pdata->Min);
        my( $tw, $th, $td, $tel) = $dc->GetTextExtent($text);
        $dc->DrawText($text, 5, $h / 2 - $th / 2 );
        
        $text = sprintf("%d", $self->_pdata->Max);
        ( $tw, $th, $td, $tel) = $dc->GetTextExtent($text);
        $dc->DrawText($text, $w - $tw - 5, $h / 2 - $th / 2);
    } else {
        my $text = sprintf("%d", $self->_pdata->Min);
        my( $tw, $th, $td, $tel) = $dc->GetTextExtent($text);
        $dc->DrawText($text, $w / 2 - $tw / 2, $h - $th - 5 );
        
        $text = sprintf("%d", $self->_pdata->Max);
        ( $tw, $th, $td, $tel) = $dc->GetTextExtent($text);
        $dc->DrawText($text, $w / 2 - $tw / 2, 5);
    }
}

sub _draw_current {
    my($self, $dc, $w, $h) = @_;
    # only ever called from within _evt_on_paint
    
    my $text = sprintf("%d", $self->_pdata->RealVal);
    
    my( $tw, $th, $td, $tel) = $dc->GetTextExtent($text);
    
    $dc->SetTextForeground($self->_pdata->ValueColour);
    $dc->DrawText($text, $w / 2 - $tw / 2 , $h / 2 - $th / 2);
    
}

sub _draw_tags {
    # only ever called from within _evt_on_paint
    my($self, $dc, $w, $h) = @_;
    
    my $orival = ( $self->_pdata->DirOrizFlag ) ? $w - 2 : $h - 2;
    
    my $tcoeff = $orival / ($self->_pdata->Max - $self->_pdata->Min);
    
    $dc->SetPen( Wx::Pen->new($self->_pdata->TagsColour, 1, wxSOLID));
    $dc->SetBrush( Wx::Brush->new($self->_pdata->TagsColour, wxSOLID));
    $dc->SetTextForeground( $self->_pdata->TagsColour );
   
    for my $tag ( @{ $self->_pdata->Tags } ) {
        my $scalval = ceil(($tag - $self->_pdata->Min) * $tcoeff);
        my $text = sprintf("%d", $tag);
        
        if( $self->_pdata->DirOrizFlag ){
            $dc->DrawLine($scalval + 1, $h - 2 , $scalval + 1, $h - 10);
            my($tw, $th, $td, $tel) = $dc->GetTextExtent($text);
            $dc->DrawText($text, $scalval + 1 - ($tw / 2 ), $h - 10 - $th);
        } else  {
            $dc->DrawLine($w - 2, $h - $scalval , $w - 10 , $h - $scalval);
            my($tw, $th, $td, $tel) = $dc->GetTextExtent($text);
            $dc->DrawText($text, $w - 10 - $tw, $h - $scalval - ($th / 2) );
        }
    }
}

#----------------------------------------------------
# Event handlers
#----------------------------------------------------

sub _evt_on_size {
    my( $self, $event ) = @_;
    $event->Skip(1);
    $self->_calc_scaled_value;
}

sub _evt_on_paint {
    my( $self, $event ) = @_;
    my $dc = Wx::AutoBufferedPaintDC->new($self);
    
    my ($w, $h) = $self->GetClientSizeWH;;
    
    # Clear Background
    {
        my $bbrush = Wx::Brush->new($self->_pdata->PassiveBar, wxSOLID);
        $dc->SetBackground( $bbrush );
        $dc->SetBrush( $bbrush );
        $dc->Clear;
    }

    # Draw Border
    $dc->SetPen( Wx::Pen->new( $self->_pdata->BorderColour, 1, wxSOLID ));
    $dc->DrawRectangle(0, 0, $w, $h);
    
    # Draw Active Bar
    $dc->SetPen( Wx::Pen->new( $self->_pdata->ActiveBar, 1, wxSOLID ));
    $dc->SetBrush( Wx::Brush->new($self->_pdata->ActiveBar, wxSOLID ));
    
    my $scaledval = $self->_pdata->ScaledVal;
    
    if( $self->_pdata->DirOrizFlag ) {
        $dc->DrawRectangle(1, 1, $scaledval, $h - 2);
    } else {
        my $yPoint = $h - $scaledval ;
        my $rh; # height
        if ($scaledval == 0 ) {
            $rh = $scaledval ;
        } else {
            if ($self->_pdata->RealVal == $self->_pdata->Max) {
                $rh = $scaledval;
                $yPoint -= 1 ;
            } else {
                $rh = $scaledval - 1 ;
            }
        }
        $dc->DrawRectangle(1, $yPoint, $w - 2, $rh);
    }
    
    $self->_draw_current( $dc, $w, $h ) if $self->_pdata->ShowCurrent;
    $self->_draw_limits( $dc, $w, $h ) if $self->_pdata->ShowLimits;
    $self->_draw_tags( $dc, $w, $h ) if @{ $self->_pdata->Tags } > 0;
    return 1;
}


#########################################################################################

package KWxLinearMeter::Data;

#########################################################################################
use strict;
use warnings;
use Class::Accessor::Fast;
use base qw( Class::Accessor::Fast );

__PACKAGE__->mk_accessors( qw( ActiveBar PassiveBar ValueColour BorderColour
        LimitColour TagsColour ScaledVal RealVal Max Min DirOrizFlag ShowCurrent
        ShowLimits Font Tags ) );

sub new { shift->SUPER::new( @_ ); }


1;

Reply via email to