Skip to content

Instantly share code, notes, and snippets.

@sciurius
Last active November 9, 2017 13:35
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save sciurius/54f4b3db3bf87743df90 to your computer and use it in GitHub Desktop.
Save sciurius/54f4b3db3bf87743df90 to your computer and use it in GitHub Desktop.
wxPerl program showing MVC design pattern
#!/usr/bin/perl
use strict;
use warnings;
# An example of trivial wxPerl application using a
# Model-View-Controller architecture.
# Copyright 2014 Johan Vromans. All rights reserved.
# License: Artistic.
package Publisher;
# Trivial Publish/Subscribe handler.
my %subs;
sub send_message {
my ( $self, $tag, $param ) = @_;
next unless $subs{$tag};
foreach ( @{ $subs{$tag} } ) {
eval { $_->( $tag, $param ) };
}
}
sub subscribe {
my ( $self, $sub, $tag ) = @_;
$subs{$tag} //= [];
push( @{ $subs{$tag} }, $sub );
}
package Model;
# This is where the money^Wdata and logic is.
# Nothing GUI oriented here.
sub new {
my ( $pkg ) = @_;
my $self = bless { money => 0 }, $pkg;
$self;
}
sub add_money {
my ( $self, $value ) = @_;
$self->{money} += $value;
# Now tell anyone who cares that the value has been changed.
Publisher->send_message( "MONEY CHANGED", $self->{money} );
}
sub remove_money {
my ( $self, $value) = @_;
$self->{money} -= $value;
# Now tell anyone who cares that the value has been changed.
Publisher->send_message( "MONEY CHANGED", $self->{money} );
}
package View;
# A GUI element -- a window (Wx::Frame) that shows the current value.
use Wx qw( :window :sizer );
use base qw( Wx::Frame );
sub new {
my ( $pkg, $parent ) = @_;
my $self = $pkg->SUPER::new( $parent, -1, "Main View" );
my $sizer = Wx::BoxSizer->new(wxVERTICAL);
my $text = Wx::StaticText->new( $self, -1, "My Money" );
my $ctrl = Wx::TextCtrl->new( $self, -1, "" );
$sizer->Add( $text, 0, wxEXPAND | wxALL );
$sizer->Add( $ctrl, 0, wxEXPAND | wxALL );
$self->{moneyCtrl} = $ctrl;
$ctrl->SetEditable(0);
$self->SetSizer($sizer);
$self;
}
sub set_money {
my ( $self, $money ) = @_;
$self->{moneyCtrl}->SetValue($money);
}
package ChangerWidget;
# A GUI element -- a window (Wx::Frame) that allows changing the value.
use Wx qw( :window :sizer );
use base qw( Wx::Frame );
sub new {
my ( $pkg, $parent ) = @_;
my $self = $pkg->SUPER::new ( $parent, -1, "Main View" );
my $sizer = Wx::BoxSizer->new(wxVERTICAL);
$self->{add} = Wx::Button->new( $self, -1, "Add Money" );
$self->{remove} = Wx::Button->new( $self, -1, "Remove Money" );
$sizer->Add( $self->{add}, 0, wxEXPAND | wxALL );
$sizer->Add( $self->{remove}, 0, wxEXPAND | wxALL);
$self->SetSizer($sizer);
$self;
}
sub register {
my ( $self, $op, $code ) = @_;
Wx::Event::EVT_BUTTON( $self, $self->{$op}->GetId, $code );
}
package Controller;
# This is the controller.
# Nothing GUI oriented here.
# No business logic, just connecting things.
sub new {
my ( $pkg, $app ) = @_;
my $self = bless {}, $pkg;
$self->{model} = Model->new;
# Set up the first frame which displays the current Model value.
my $view = View->new;
$view->set_money( $self->{model}->{money} );
# Set up the second frame which allows the user to modify the Model's value.
my $changer = ChangerWidget->new( $view );
# Register our functions to ChangerWidget operations.
$changer->register( "add", sub { $self->add_money } );
$changer->register( "remove", sub { $self->remove_money } );
# Subscribe to all "MONEY CHANGED" messages from the Model
# to subscribe to ALL messages (topics), omit the second argument below.
Publisher->subscribe( sub { money_changed( $self, @_ ) }, "MONEY CHANGED" );
$view->Show(1);
$changer->Show(1);
$self->{view} = $view;
$self;
}
sub add_money {
my ( $self, $evt ) = @_;
$self->{model}->add_money(10);
}
sub remove_money {
my ( $self, $evt ) = @_;
$self->{model}->remove_money(10);
}
sub money_changed {
my ( $self, $tag, $data ) = @_;
# This method is the handler for "MONEY CHANGED" messages,
# which Publisher will call as messages are sent from the model.
#
# We already know the topic is "MONEY CHANGED", but if we
# didn't, $tag would tell us.
$self->{view}->set_money($data);
}
package main;
# The main program. Fairly standard.
no warnings qw( redefine once );
local *Wx::App::OnInit = sub{1};
my $app = Wx::App->new();
Controller->new($app);
$app->MainLoop();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment