Beefy Boxes and Bandwidth Generously Provided by pair Networks
Keep It Simple, Stupid
 
PerlMonks  

A PerlQt Calculator

by thunders (Priest)
on Jun 22, 2004 at 15:26 UTC ( #368763=CUFP: print w/ replies, xml ) Need Help??

Here's my first attempt at a calculator written in perl. I've been using Tk for a long time, but recently I discovered PerlQt. What's nice about PerlQt is that it allows layouts to be designed in the QT Designer program with the aid of the pqt-designer plugin.

I did the basic layout in pqt designer, which let me set up the geometry and colors easily, and figure out which functions were availible for each widget. I don't know anything about calculator design, so this implementation may be a bit naive, but it came together pretty nicely. The look is patterned after the version of kcalc that comes with mandrake 10.0.

After I had the look set up I used another pqt tool called puic to transform the .ui file from QtDesigner into a perl script. I added all of the event handlers and the Buffer class by hand in vim

There are probably bugs. For one thing it does not properly format long number strings to fit in the display window. Also it only catches and ignore divide by zero errors, it doesn't send an error message to the display. Also to enter numbers that begin with a decimal point you must press Key_0 followed by Key_Decimal. And worst of all no -/+ toggle yet.

I figured i'd put it up anyhow, since I don't see that many programs written in Perl Qt, and I found it a bit easier to use than Tk or Wx

Any comments are welcome

#!/usr/bin/perl use strict; use utf8; package Buffer; sub new { my $proto = shift; my $class = ref($proto) || $proto; my $self = { LEFT => undef, RIGHT => undef, MEMORY => undef, OP => undef, RESET => 0 }; bless( $self, $class ); return $self; } sub left { my $self = shift; if (@_) { $self->{LEFT} = shift; } return $self->{LEFT}; } sub right { my $self = shift; if (@_) { $self->{RIGHT} = shift; } return $self->{RIGHT}; } sub op { my $self = shift; if (@_) { my $op = shift; if ( $op =~ /^[+-\/*]$/ ) { $self->{OP} = $op; } } return $self->{OP}; } sub reset_display { my $self = shift; if (@_) { $self->{RESET} = shift; } return $self->{RESET}; } sub clear_all { my $self = shift; $self->{LEFT} = undef; $self->{RIGHT} = undef; $self->{OP} = undef; $self->{MEMORY} = undef; $self->reset_display(0); } 1; package PerlCalc; use Qt; use Qt::isa qw(Qt::Dialog); use Qt::slots Key_0_clicked => [], Key_1_clicked => [], Key_2_clicked => [], Key_3_clicked => [], Key_4_clicked => [], Key_5_clicked => [], Key_6_clicked => [], Key_7_clicked => [], Key_8_clicked => [], Key_9_clicked => [], Key_Clear_clicked => [], Key_Plus_clicked => [], Key_Minus_clicked => [], Key_Divide_clicked => [], Key_Times_clicked => [], Key_Equals_clicked => [], Key_Decimal_clicked => []; use Qt::attributes qw( Key_Divide Key_Clear Key_Decimal Key_Minus Key_Plus Key_1 Key_2 Key_3 Key_4 Key_5 Key_6 Key_7 Key_8 Key_9 Key_0 Key_Times Display Display_font Key_Equals ); our ($buffer) = Buffer->new(); sub NEW { shift->SUPER::NEW( @_[ 0 .. 3 ] ); if ( name() eq "unnamed" ) { setName("PerlCalc"); } Key_Divide = Qt::PushButton( this, "Key_Divide" ); Key_Divide->setGeometry( Qt::Rect( 90, 100, 51, 41 ) ); Key_Clear = Qt::PushButton( this, "Key_Clear" ); Key_Clear->setGeometry( Qt::Rect( 30, 100, 51, 41 ) ); Key_Decimal = Qt::PushButton( this, "Key_Decimal" ); Key_Decimal->setGeometry( Qt::Rect( 150, 300, 51, 41 ) ); Key_Minus = Qt::PushButton( this, "Key_Minus" ); Key_Minus->setGeometry( Qt::Rect( 210, 100, 51, 41 ) ); Key_Plus = Qt::PushButton( this, "Key_Plus" ); Key_Plus->setGeometry( Qt::Rect( 210, 151, 51, 90 ) ); Key_Times = Qt::PushButton( this, "Key_Times" ); Key_Times->setGeometry( Qt::Rect( 150, 100, 51, 41 ) ); Key_Equals = Qt::PushButton( this, "Key_Equals" ); Key_Equals->setGeometry( Qt::Rect( 210, 250, 51, 90 ) ); Key_0 = Qt::PushButton( this, "Key_0" ); Key_0->setGeometry( Qt::Rect( 30, 300, 110, 41 ) ); Key_1 = Qt::PushButton( this, "Key_1" ); Key_1->setGeometry( Qt::Rect( 30, 250, 51, 41 ) ); Key_2 = Qt::PushButton( this, "Key_2" ); Key_2->setGeometry( Qt::Rect( 90, 250, 51, 41 ) ); Key_3 = Qt::PushButton( this, "Key_3" ); Key_3->setGeometry( Qt::Rect( 150, 250, 51, 41 ) ); Key_4 = Qt::PushButton( this, "Key_4" ); Key_4->setGeometry( Qt::Rect( 30, 200, 51, 41 ) ); Key_5 = Qt::PushButton( this, "Key_5" ); Key_5->setGeometry( Qt::Rect( 90, 200, 51, 41 ) ); Key_6 = Qt::PushButton( this, "Key_6" ); Key_6->setGeometry( Qt::Rect( 150, 200, 51, 41 ) ); Key_7 = Qt::PushButton( this, "Key_7" ); Key_7->setGeometry( Qt::Rect( 30, 150, 51, 41 ) ); Key_8 = Qt::PushButton( this, "Key_8" ); Key_8->setGeometry( Qt::Rect( 90, 150, 51, 41 ) ); Key_9 = Qt::PushButton( this, "Key_9" ); Key_9->setGeometry( Qt::Rect( 150, 150, 51, 41 ) ); Display = Qt::Label( this, "Display" ); Display->setGeometry( Qt::Rect( 30, 20, 280, 51 ) ); Display->setBackgroundMode( &Qt::Label::PaletteBrightText() ); Display->setPaletteForegroundColor( Qt::Color( 255, 0, 0 ) ); Display->setPaletteBackgroundColor( Qt::Color( 0, 0, 0 ) ); my $Display_font = Qt::Font( Display->font ); $Display_font->setFamily("Monospace"); $Display_font->setPointSize(26); Display->setFont($Display_font); Display->setFrameShape( &Qt::Label::WinPanel() ); Display->setFrameShadow( &Qt::Label::Sunken() ); Display->setLineWidth( int(1) ); Display->setAlignment( int( &Qt::Label::AlignVCenter | &Qt::Label::AlignRight ) ); languageChange(); my $resize = Qt::Size( 327, 391 ); $resize = $resize->expandedTo( minimumSizeHint() ); resize($resize); clearWState(&Qt::WState_Polished); Qt::Object::connect( Key_0, SIGNAL "clicked()", this, SLOT "Key_0_clicked()" ); Qt::Object::connect( Key_1, SIGNAL "clicked()", this, SLOT "Key_1_clicked()" ); Qt::Object::connect( Key_2, SIGNAL "clicked()", this, SLOT "Key_2_clicked()" ); Qt::Object::connect( Key_3, SIGNAL "clicked()", this, SLOT "Key_3_clicked()" ); Qt::Object::connect( Key_4, SIGNAL "clicked()", this, SLOT "Key_4_clicked()" ); Qt::Object::connect( Key_5, SIGNAL "clicked()", this, SLOT "Key_5_clicked()" ); Qt::Object::connect( Key_6, SIGNAL "clicked()", this, SLOT "Key_6_clicked()" ); Qt::Object::connect( Key_7, SIGNAL "clicked()", this, SLOT "Key_7_clicked()" ); Qt::Object::connect( Key_8, SIGNAL "clicked()", this, SLOT "Key_8_clicked()" ); Qt::Object::connect( Key_9, SIGNAL "clicked()", this, SLOT "Key_9_clicked()" ); Qt::Object::connect( Key_Clear, SIGNAL "clicked()", this, SLOT "Key_Clear_clicked()" ); Qt::Object::connect( Key_Plus, SIGNAL "clicked()", this, SLOT "Key_Plus_clicked()" ); Qt::Object::connect( Key_Minus, SIGNAL "clicked()", this, SLOT "Key_Minus_clicked()" ); Qt::Object::connect( Key_Divide, SIGNAL "clicked()", this, SLOT "Key_Divide_clicked()" ); Qt::Object::connect( Key_Times, SIGNAL "clicked()", this, SLOT "Key_Times_clicked()" ); Qt::Object::connect( Key_Equals, SIGNAL "clicked()", this, SLOT "Key_Equals_clicked()" ); Qt::Object::connect( Key_Decimal, SIGNAL "clicked()", this, SLOT "Key_Decimal_clicked()" ); } # Sets the strings of the subwidgets using the current # language. sub languageChange { setCaption( trUtf8("PerlCalc") ); Key_Divide->setText( trUtf8("/") ); Key_Clear->setText( trUtf8("C") ); Key_Decimal->setText( trUtf8(".") ); Key_Minus->setText( trUtf8("-") ); Key_Plus->setText( trUtf8("+") ); Key_6->setText( trUtf8("6") ); Key_9->setText( trUtf8("9") ); Key_8->setText( trUtf8("8") ); Key_4->setText( trUtf8("4") ); Key_7->setText( trUtf8("7") ); Key_5->setText( trUtf8("5") ); Key_Times->setText( trUtf8("X") ); Key_1->setText( trUtf8("1") ); Display->setText( trUtf8("0") ); Key_Equals->setText( trUtf8("=") ); Key_0->setText( trUtf8("0") ); Key_2->setText( trUtf8("2") ); Key_3->setText( trUtf8("3") ); } sub Key_0_clicked { AppendDisplay("0"); } sub Key_1_clicked { AppendDisplay("1"); } sub Key_2_clicked { AppendDisplay("2"); } sub Key_3_clicked { AppendDisplay("3"); } sub Key_4_clicked { AppendDisplay("4"); } sub Key_5_clicked { AppendDisplay("5"); } sub Key_6_clicked { AppendDisplay("6"); } sub Key_7_clicked { AppendDisplay("7"); } sub Key_8_clicked { AppendDisplay("8"); } sub Key_9_clicked { AppendDisplay("9"); } sub Key_Divide_clicked { SetOp("/"); } sub Key_Minus_clicked { SetOp("-"); } sub Key_Times_clicked { SetOp("*"); } sub Key_Plus_clicked { SetOp("+"); } sub Key_Decimal_clicked { AppendDisplay("."); } sub Key_Clear_clicked { $buffer->clear_all; Display->setText("0"); } sub Key_Equals_clicked { my $left = $buffer->left; my $right = $buffer->right; my $result; SWITCH: for ( $buffer->op ) { /^[+]$/ && do { $result = $left + $right; last; }; /^[-]$/ && do { $result = $left - $right; last; }; /^[*]$/ && do { $result = $left * $right; last; }; /^[\/]$/ && do { eval { $result = $left / $right;}; warn $@ if $@; last; } } if ( $result =~ /^-?\d+\.?\d+$/ ) { $buffer->left($result); $buffer->reset_display(1); Display->setText($result); } } sub AppendDisplay { my $character = shift; my $text = Display->text; if ( $character eq '.' ) { return if $text =~ /\./; } Display->setText( $text && !$buffer->reset_display ? $text . $character : $character ); $buffer->reset_display(0); if ( !defined $buffer->op ) { $buffer->left( Display->text ); print STDERR "buffer left =" . $buffer->left . "\n"; } else { $buffer->right( Display->text ); print STDERR "buffer right =" . $buffer->right . "\n"; } } sub SetOp { my $op = shift; $buffer->op($op); $buffer->reset_display(1); } 1; package main; use Qt; use PerlCalc; my $a = Qt::Application( \@ARGV ); my $w = PerlCalc; $a->setMainWidget($w); $w->show; exit $a->exec;

Comment on A PerlQt Calculator
Download Code
Re: A PerlQt Calculator
by Plankton (Priest) on Jun 23, 2004 at 06:47 UTC
    Cool! I gotta learn more about Qt now. I did have problems installing Qt from via perl -MCPAN -eshell but was able to get it installed from by following the links you provided. I did have a problem doing 2 X 2 = ? But I really like your PerlQt Calc. ++

    Plankton: 1% Evil, 99% Hot Gas.

      Oops, good catch. That was my mistake. The display was filtering out results with only one digit.

      changing the filter fixes that.

      $result =~ /^-?\d+\.?\d+$/ to $result =~ /^-?\d+\.?\d*?$/
Re: A PerlQt Calculator
by thunders (Priest) on Jun 23, 2004 at 14:58 UTC
    here's an attempt at fixing some of the problems listed above. I moved most of the Math into the buffer, in an attept to divorce the Display from the logic, and I used Math::BigFloat to represent the digits, but I still need to play around with the precision on divides.
    #!/usr/bin/perl -w use strict; use utf8; package Buffer; use Math::BigFloat; sub new { my $proto = shift; my $class = ref($proto) || $proto; my $self = { LEFT => undef, RIGHT => undef, MEMORY => undef, OP => undef, RESET => 0 }; bless( $self, $class ); return $self; } sub left { my $self = shift; if (@_) { $self->{LEFT} = shift; } return $self->{LEFT}; } sub right { my $self = shift; if (@_) { $self->{RIGHT} = shift; } return $self->{RIGHT}; } sub op { my $self = shift; if (@_) { my $op = shift; if ( $op =~ /^[+-\/*]$/ ) { $self->{OP} = $op; } } return $self->{OP}; } sub reset_display { my $self = shift; if (@_) { $self->{RESET} = shift; } return $self->{RESET}; } sub clear_all { my $self = shift; $self->{LEFT} = undef; $self->{RIGHT} = undef; $self->{OP} = undef; $self->{MEMORY} = undef; $self->reset_display(0); } sub add{ my $self = shift; my $left = new Math::BigFloat($self->left); $left->badd($self->right); return $left->bstr; } sub subtract{ my $self = shift; my $left = new Math::BigFloat($self->left); $left->bsub($self->right); return $left->bstr; } sub divide{ my $self = shift; return "Error" unless $self->right; my $left = new Math::BigFloat($self->left); $left->bdiv($self->right); my $precision = 10 - $left->copy()->blog(10); $left->precision( - $precision->bfloor ); return $left->bstr; } sub multiply{ my $self = shift; my $left = new Math::BigFloat($self->left); $left->bmul($self->right); return $left->bstr; } 1; package PerlCalc; use Qt; use Qt::isa qw(Qt::Dialog); use Qt::slots Key_0_clicked => [], Key_1_clicked => [], Key_2_clicked => [], Key_3_clicked => [], Key_4_clicked => [], Key_5_clicked => [], Key_6_clicked => [], Key_7_clicked => [], Key_8_clicked => [], Key_9_clicked => [], Key_Clear_clicked => [], Key_Plus_clicked => [], Key_Minus_clicked => [], Key_Divide_clicked => [], Key_Times_clicked => [], Key_Equals_clicked => [], Key_Decimal_clicked => []; use Qt::attributes qw( Key_Divide Key_Clear Key_Decimal Key_Minus Key_Plus Key_1 Key_2 Key_3 Key_4 Key_5 Key_6 Key_7 Key_8 Key_9 Key_0 Key_Times Display Display_font Key_Equals ); our ($buffer) = Buffer->new(); sub NEW { shift->SUPER::NEW( @_[ 0 .. 3 ] ); if ( name() eq "unnamed" ) { setName("PerlCalc"); } Key_Divide = Qt::PushButton( this, "Key_Divide" ); Key_Divide->setGeometry( Qt::Rect( 90, 100, 51, 41 ) ); Key_Clear = Qt::PushButton( this, "Key_Clear" ); Key_Clear->setGeometry( Qt::Rect( 30, 100, 51, 41 ) ); Key_Decimal = Qt::PushButton( this, "Key_Decimal" ); Key_Decimal->setGeometry( Qt::Rect( 150, 300, 51, 41 ) ); Key_Minus = Qt::PushButton( this, "Key_Minus" ); Key_Minus->setGeometry( Qt::Rect( 210, 100, 51, 41 ) ); Key_Plus = Qt::PushButton( this, "Key_Plus" ); Key_Plus->setGeometry( Qt::Rect( 210, 151, 51, 90 ) ); Key_Times = Qt::PushButton( this, "Key_Times" ); Key_Times->setGeometry( Qt::Rect( 150, 100, 51, 41 ) ); Key_Equals = Qt::PushButton( this, "Key_Equals" ); Key_Equals->setGeometry( Qt::Rect( 210, 250, 51, 90 ) ); Key_0 = Qt::PushButton( this, "Key_0" ); Key_0->setGeometry( Qt::Rect( 30, 300, 110, 41 ) ); Key_1 = Qt::PushButton( this, "Key_1" ); Key_1->setGeometry( Qt::Rect( 30, 250, 51, 41 ) ); Key_2 = Qt::PushButton( this, "Key_2" ); Key_2->setGeometry( Qt::Rect( 90, 250, 51, 41 ) ); Key_3 = Qt::PushButton( this, "Key_3" ); Key_3->setGeometry( Qt::Rect( 150, 250, 51, 41 ) ); Key_4 = Qt::PushButton( this, "Key_4" ); Key_4->setGeometry( Qt::Rect( 30, 200, 51, 41 ) ); Key_5 = Qt::PushButton( this, "Key_5" ); Key_5->setGeometry( Qt::Rect( 90, 200, 51, 41 ) ); Key_6 = Qt::PushButton( this, "Key_6" ); Key_6->setGeometry( Qt::Rect( 150, 200, 51, 41 ) ); Key_7 = Qt::PushButton( this, "Key_7" ); Key_7->setGeometry( Qt::Rect( 30, 150, 51, 41 ) ); Key_8 = Qt::PushButton( this, "Key_8" ); Key_8->setGeometry( Qt::Rect( 90, 150, 51, 41 ) ); Key_9 = Qt::PushButton( this, "Key_9" ); Key_9->setGeometry( Qt::Rect( 150, 150, 51, 41 ) ); Display = Qt::Label( this, "Display" ); Display->setGeometry( Qt::Rect( 30, 20, 280, 51 ) ); Display->setBackgroundMode( &Qt::Label::PaletteBrightText() ); Display->setPaletteForegroundColor( Qt::Color( 255, 0, 0 ) ); Display->setPaletteBackgroundColor( Qt::Color( 0, 0, 0 ) ); my $Display_font = Qt::Font( Display->font ); $Display_font->setFamily("Monospace"); $Display_font->setPointSize(20); Display->setFont($Display_font); Display->setFrameShape( &Qt::Label::WinPanel() ); Display->setFrameShadow( &Qt::Label::Sunken() ); Display->setLineWidth( int(1) ); Display->setAlignment( int( &Qt::Label::AlignVCenter | &Qt::Label::AlignRight ) ); languageChange(); my $resize = Qt::Size( 327, 391 ); $resize = $resize->expandedTo( minimumSizeHint() ); resize($resize); clearWState(&Qt::WState_Polished); Qt::Object::connect( Key_0, SIGNAL "clicked()", this, SLOT "Key_0_clicked()" ); Qt::Object::connect( Key_1, SIGNAL "clicked()", this, SLOT "Key_1_clicked()" ); Qt::Object::connect( Key_2, SIGNAL "clicked()", this, SLOT "Key_2_clicked()" ); Qt::Object::connect( Key_3, SIGNAL "clicked()", this, SLOT "Key_3_clicked()" ); Qt::Object::connect( Key_4, SIGNAL "clicked()", this, SLOT "Key_4_clicked()" ); Qt::Object::connect( Key_5, SIGNAL "clicked()", this, SLOT "Key_5_clicked()" ); Qt::Object::connect( Key_6, SIGNAL "clicked()", this, SLOT "Key_6_clicked()" ); Qt::Object::connect( Key_7, SIGNAL "clicked()", this, SLOT "Key_7_clicked()" ); Qt::Object::connect( Key_8, SIGNAL "clicked()", this, SLOT "Key_8_clicked()" ); Qt::Object::connect( Key_9, SIGNAL "clicked()", this, SLOT "Key_9_clicked()" ); Qt::Object::connect( Key_Clear, SIGNAL "clicked()", this, SLOT "Key_Clear_clicked()" ); Qt::Object::connect( Key_Plus, SIGNAL "clicked()", this, SLOT "Key_Plus_clicked()" ); Qt::Object::connect( Key_Minus, SIGNAL "clicked()", this, SLOT "Key_Minus_clicked()" ); Qt::Object::connect( Key_Divide, SIGNAL "clicked()", this, SLOT "Key_Divide_clicked()" ); Qt::Object::connect( Key_Times, SIGNAL "clicked()", this, SLOT "Key_Times_clicked()" ); Qt::Object::connect( Key_Equals, SIGNAL "clicked()", this, SLOT "Key_Equals_clicked()" ); Qt::Object::connect( Key_Decimal, SIGNAL "clicked()", this, SLOT "Key_Decimal_clicked()" ); } # Sets the strings of the subwidgets using the current # language. sub languageChange { setCaption( trUtf8("PerlCalc") ); Key_Divide->setText( trUtf8("/") ); Key_Clear->setText( trUtf8("C") ); Key_Decimal->setText( trUtf8(".") ); Key_Minus->setText( trUtf8("-") ); Key_Plus->setText( trUtf8("+") ); Key_6->setText( trUtf8("6") ); Key_9->setText( trUtf8("9") ); Key_8->setText( trUtf8("8") ); Key_4->setText( trUtf8("4") ); Key_7->setText( trUtf8("7") ); Key_5->setText( trUtf8("5") ); Key_Times->setText( trUtf8("X") ); Key_1->setText( trUtf8("1") ); Display->setText( trUtf8("0") ); Key_Equals->setText( trUtf8("=") ); Key_0->setText( trUtf8("0") ); Key_2->setText( trUtf8("2") ); Key_3->setText( trUtf8("3") ); } sub Key_0_clicked {AppendDisplay(0);} sub Key_1_clicked {AppendDisplay(1);} sub Key_2_clicked {AppendDisplay(2);} sub Key_3_clicked {AppendDisplay(3);} sub Key_4_clicked {AppendDisplay(4);} sub Key_5_clicked {AppendDisplay(5);} sub Key_6_clicked {AppendDisplay(6);} sub Key_7_clicked {AppendDisplay(7);} sub Key_8_clicked {AppendDisplay(8);} sub Key_9_clicked {AppendDisplay(9);} sub Key_Divide_clicked { SetOp("/"); } sub Key_Minus_clicked { SetOp("-"); } sub Key_Times_clicked { SetOp("*"); } sub Key_Plus_clicked { SetOp("+"); } sub Key_Decimal_clicked { AppendDisplay("."); } sub Key_Clear_clicked { $buffer->clear_all; Display->setText("0"); } sub Key_Equals_clicked { my $result; SWITCH: for ( $buffer->op ) { /^[+]$/ && do { $result = $buffer->add; last; }; /^[-]$/ && do { $result = $buffer->subtract; last; }; /^[*]$/ && do { $result = $buffer->multiply; last; }; /^[\/]$/ && do {$result = $buffer->divide; last;}; } if ( $result =~ /^-?\d+\.?\d*?$/ ) { $buffer->left($result); $buffer->reset_display(1); Display->setText($result); }elsif($result eq 'Error'){ $buffer->reset_display(1); Display->setText($result); } print STDERR "buffer left =" . $buffer->left . "\n"; print STDERR "buffer right =" . $buffer->right . "\n"; } sub AppendDisplay { my $character = shift; my $text = Display->text; if ( $character eq '.' ) { return if $text =~ /\./; } Display->setText( $text && !$buffer->reset_display ? $text . $character : $character ); $buffer->reset_display(0); if ( !defined $buffer->op ) { $buffer->left( Display->text ); print STDERR "buffer left =" . $buffer->left . "\n"; } else { $buffer->right( Display->text ); print STDERR "buffer right =" . $buffer->right . "\n"; } } sub SetOp { my $op = shift; $buffer->op($op); $buffer->reset_display(1); } 1; package main; use Qt; use PerlCalc; my $a = Qt::Application( \@ARGV ); my $w = PerlCalc; $a->setMainWidget($w); $w->show; exit $a->exec;

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: CUFP [id://368763]
Approved by calin
Front-paged by Plankton
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others chanting in the Monastery: (11)
As of 2014-09-16 16:41 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    How do you remember the number of days in each month?











    Results (35 votes), past polls