scratchpad
b4swine
<h1>WxSimple Tutorial</h1>
This is a tutorial for WxSimple, a package I wrote to make
throwaway WxPerl programs using XRC.
<p>GUI programming always looks rather bulky. This package
makes the code much smaller, making it easy to write throwaway, one-time programs. This package is very useful for the absolute beginner as well as for experienced WxPerlers.
<p>For beginners, the advantage of using XRC, is that you can use a GUI to create you menus, toolbar, and your buttons windows, panels, etc, along with sizers, and can test them live. This frees you up to concentrate on programming. Later on, when dynamic objects are needed, you can learn the internals of sizers and creation/deletion of windows at your own pace.
<h2>Getting Started</h2>
To get started, you need to get some components together,
becuase unfortuantely, Wx is not a standard package.
<p>Since I use ActivePerl for MS Windows, I have provided complete instructions for that. For *nix users it should be easier. Use [perldoc://perlmodinstall.html|ppm] to download the <c>Wx</c> package. Unfortunately, most repositories have very ancient binaries for WxPerl, so to get the latest version, you will need to add the repositories described in [http://www.wxperl.co.uk/wxppm/].
<p>You also need to get some kind of a XML resource compiler. I like <c>XRCed</c> (because it is the only one I have used), but it requires you to install Python as well. You need to download and install python (about 10 megs) from
[http://www.python.org/download/], and then download and install WxPython (about 5 megs) from [http://www.wxpython.org/download.php]
<p>You are now ready to use the XRCed program included with WxPython. If you open the file <c>WxSample.xrc</c> (file located at the end), in XRCed, you can explore how this is structured.
<h2>Sample Program: For those than know WxPerl</h2>
For those who are already familiar with WxPerl, a sample program illustrating the use of <c>WxSimple</c> is provided here. It will need two other supporting files namely the package <c>WxSimple.pm</c> and the resource file <c>WxSample.xrc</c> that was created using <c>XRCed</c>. Both of these files are given in the last section.
<p>Here is the code for <c>WxSample.pl</c>
<code>
use strict;
use warnings;
use WxSimple qw(FindWindowByXid MsgBox $xrc $sbar $OpenFile $frame);
#
# set up parameters
#
$xrc = 'WxSample.xrc'; # location of xrc file
$sbar = 2; # status bar with 2 sections
$OpenFile = \&OpenFile; # funtion that opens a file
#
# create an app
#
my $app = WxSimple->new(); # create app and main frame
#
# now, find controls inside the main window
# and set up event handlers for them.
#
my ($checkbox, $id) = FindWindowByXid('CheckBox');
Wx::Event::EVT_CHECKBOX($frame, $id, \&OnCheck );
my $smalltext = FindWindowByXid('SmallText');
#
# start the main loop
#
$app->MainLoop();
#
# -------------------------------------
# here are the event handlers
# run the program. Open a file. Check the checkbox
#
sub OpenFile
{ my $file = shift;
local $/;
my $textbox = FindWindowByXid('Text');
open F, '<', $file;
$textbox->ChangeValue(<F>);
close F;
MsgBox('The file is loaded in the text box');
}
sub OnCheck
{ my ($this, $evt) = @_;
if ($evt->IsChecked()) { $smalltext->ChangeValue('checked'); }
else { $smalltext->ChangeValue('unchecked'); }
}
</code>
<h2>A Typical Sequence</h2>
We go through a typical sequence needed to create a program using WxSimple.
<h3>1. Use XRCed to create Menu and a Window Layout</h3>
First a resource file is created using <c>XRCed</c>. In this , there is a main frame, and inside the frame you may have a menu and possibly a toolbar as well. You can also put any other containers, buttons, sliders, etc. as you like. Each object needs an XMLid, which is just a name, if you are going to be referring to it in the Perl program. For example, you usually do not need to give an ID for StaticText, because there is no need to refer to it. Finally save this as an <c>xrc</c> file.
There are a set of default XMLid's for many of the standard menu items (you can find them in the index under "Window Identifiers"). there is an advantage in using the standard names although you can .
<h3>2. Set Parameters</h3>
A typical programs first sets up parameters. This is done by
<code>
use WxSimple qw($xrc $frmID $sbar %menu $OpenFile $SaveFile $CloseWin $icon);
</code>
<p><c>$xrc</c> refers to the path to the file that was created by <c>XRCed</c>.
<p><c>$frmID</c> is the XMLid that was given to the main frame created in the <c>xrc</c> file.
<p><c>$sbar</c> is the number of panels in the status bar at the bottom of the window. If this is zero, no status bar is created.
<p><c>%menu</c> is a hash with the XMLids of the menu items as keys, and coderefs to subs that will be called as values. Four items in this hash are predefined, but may be overwritten. <c>File--Open, File--Save, File--SaveAs</c>, have predefined handlers which offer a file dialog and get a file name, after which the call the code referenced by &$OpenFile or &$SaveFile, as appropriate. You should make an entry for every one of the menu items that you want to be active. You can override the predefined items as well, by redefining them.
<p><c>$OpenFile</c> and <c>$SaveFile</c> are described above in <c>%menu</c>.
<p><c>&$CloseWin</c> is the routine called when the window is closed. It should <c>Destroy</c> all top level windows. If you create other top level windows, you should redefine this.
<p><c>$icon</c> is a default WxPerl icon, which you can change as you like.
<h3>3. Create an App</h3>
Creating a WxSimple app by <c>my $app = WxSimple->new()</c> .
<h3>4. Add more Handlers</h3>
After that, the variables <c>$WxSimple::frame</c> and <c>$WxSimple::xr</c> get set to the top-level frame and the XML resource, respectively. Any other item within the frame can be located using its XML id, with
<code>
my ($window, $id) = WxSimple::FindWindowByXid('xmlid')
</code>
The <c>$id</c> is a numeric ID that is associated with that control or window. It is needed if you want to respond to other events from that control.
<h3>5. Define Handler Routines</h3>
<p>If you are going to be using files, you need to define <c>$WxSimple::FileOpen</c>, or <c>$WxSimple::FileSave</c>, as appropriate. Initially these are set to dummy routines, but you can refer them to your own routine, as illustrated above for <c>FileOpen</c>.
<p>You may now define routines for each of the menu items, as well as other event handlers you have defined.
<h2>For those learning WxPerl</h2>
This module is actually a useful way to learn WxPerl, even for a beginner. You can get a very fast start, and then learn the hard way of doing things at your own pace. It is useful to be aware of the resources available to learn WxPerl.
<h3>Manual</h3>
A [http://www.wxperl.co.uk/wxWidgets-2.8.5-CHM.zip|manual] intended for <c>C</c> programmers, with one-line notes for Perl and Python is available. This is not up to date, but this is all there is.
<h3>Tutorials</h3>
There are a nice set of [id://520074|tutorials] here.
<h3>Samples</h3>
Along with WxPerl, two kinds of sample programs are provided. One is a very large sample program called <c>demo.pl</c> which illustrates many of the various controls, in one integrated program. The other is directory <c>samples</c> which contains a set of small independent programs, each of which illustrates one control.
<h3>Wiki</h3>
A [http://wxperl.pvoice.org/kwiki/|wiki] devoted to wxperl, is a bit undermaintained, and out of date, but you can find many things here.
<h3>Groups</h3>
<h2>Supporting Code</h2>
In order to run, this needs two additional support files,
<c>WxSimple.pm</c> and <c>WxSample.xrc</c>. The first is the package, while the second is an xml file that was created (and should be edited) using XRCed. The files are provided below. If you put all three in a single directory, you can run <c>WxSample.pl</c>.
<code>
package WxSimple; use base qw(Wx::App Exporter);
use strict;
use Exporter;
our $VERSION = 0.10;
our @EXPORT_OK = qw(StartApp FindWindowByXid MsgBox $frame $xr
$xrc $frmID $sbar %menu $OpenFile $SaveFile $CloseWin $icon);
use Wx;
use Carp;
our $frame;
our $xr;
our $xrc = 'res/res.xrc'; # location of resource file
our $frmID = 'Frame'; # XML ID of the main frame
our $sbar = 0; # number of status bar sections
our %menu = # menu handlers,
# you can replace them with your own
# or add your own menu items
( wxID_OPEN => \&Open, # these are four readymade handlers
wxID_SAVE => \&Save, # that just get a file-name for reading
wxID_SAVEAS => \&SaveAs,# or writing
wxID_EXIT => \&Exit, # and quit just exits.
);
our $OpenFile = \&OpenFile; # A routine to read data from a file
our $SaveFile = \&SaveFile; # A routine to write data to a file
our $CloseWin = \&CloseWin; # this is not a menu option
# it is the routine called before the end
# it needs to Destroy() all top level dialogs
our $icon = Wx::GetWxPerlIcon();
my $file; # the name of the file used in Open/Save
sub StartApp
{ WxSimple->new()->MainLoop();
}
sub OnInit
{ my $app = shift;
#
# Load XML Resources
#
use Wx::XRC;
$xr = Wx::XmlResource->new();
$xr->InitAllHandlers();
croak "No Resource file $xrc" unless -e $xrc;
$xr->Load( $xrc );
#
# Load the main frame from resources
#
$frame = 'Wx::Frame'->new;
croak "No Frame named $frmID in $xrc" unless
$xr->LoadFrame($frame, undef, $frmID);
if ($sbar)
{ $frame->CreateStatusBar( $sbar );
$frame->SetStatusWidths(-1,200);
}
$frame->SetIcon( $icon );
#
# Set event handlers
#
use Wx::Event qw(EVT_MENU EVT_CLOSE);
while (my ($xrcid, $handler) = each %menu)
{ my $id = Wx::XmlResource::GetXRCID($xrcid, -2);
if ($id == -2)
{ carp "No MenuItem named $xrcid";
next;
}
EVT_MENU( $frame, $id, $handler );
}
EVT_CLOSE( $frame, $CloseWin );
#
# Show the window
#
$app->SetTopWindow( $frame );
$frame -> Show(1);
1;
}
sub FindWindowByXid
{ my $id = Wx::XmlResource::GetXRCID($_[0], -2);
return undef if $id == -2;
my $win = Wx::Window::FindWindowById($id, $frame);
return wantarray ? ($win, $id) : $win;
}
sub MsgBox
{ use Wx qw (wxOK wxICON_EXCLAMATION);
my @args = @_;
$args[1] = 'Message' unless defined $args[1];
$args[2] = wxOK | wxICON_EXCLAMATION unless defined $args[2];
my $md = Wx::MessageDialog->new($frame, @args);
$md->ShowModal();
}
sub Open
{ use Wx qw (wxID_CANCEL wxFD_FILE_MUST_EXIST);
my $dlg = Wx::FileDialog->new($frame,
'Select one or more Files', '', '',
'Text Files|*.txt|All Files|*.*',
wxFD_FILE_MUST_EXIST);
if ($dlg->ShowModal() == wxID_CANCEL) { return }
$file = $dlg->GetPath();
$frame->SetStatusText("Opening...$file", 0);
my $busy = Wx::BusyCursor->new();
$OpenFile->($file);
$frame->SetStatusText("Opening...$file...Done", 0);
}
sub Save
{ $frame->SetStatusText("Saving...$file", 0);
my $busy = Wx::BusyCursor->new();
$SaveFile->($file);
$frame->SetStatusText("Saving...$file...Done", 0);
}
sub SaveAs
{ use Wx qw (wxID_CANCEL wxFD_OVERWRITE_PROMPT wxFD_SAVE);
my $dlg = Wx::FileDialog->new($frame,
'Select one or more Files', '', '',
'Text Files|*.txt|All Files|*.*',
wxFD_OVERWRITE_PROMPT | wxFD_SAVE);
if ($dlg->ShowModal() == wxID_CANCEL) { return }
$file = $dlg->GetPath();
Save();
}
sub OpenFile
{ MsgBox "Add Code to Open $file";
}
sub SaveFile
{ MsgBox "Add Code to Save $file";
}
sub Exit
{ CloseWin();
}
#
# Close is not called by the menu, but is called to close all windows
# If there are any toplevel dialogs, close them here, otherwise the
# program will not exit.
#
sub CloseWin
{ $frame->Destroy();
}
1;
__END__
=head1 NAME
WxSimple: A package to make throwaway WxPerl programs using C<XRC>
=head1 SYNOPSIS
use WxSimple;
# one-liner (use all default options)
WxSimple::StartApp();
...
# with modified defaults
use WxSimple qw(qw(StartApp FindWindowByXid MsgBox $frame $xr
$xrc $frmID $sbar %menu $OpenFile $SaveFile $CloseWin $icon);
$xrc = 'res/res.xrc';
$frmID = 'MainFrame';
$sbar = 1;
$menu{OpenFile} = \&::Open;
$menu{Options} = \&::OnOptions;
...
my $app = WxSimple->new();
...
$app->MainLoop();
=head1 DESCRIPTION
GUI programming always looks rather bulky. This package makes the
code much smaller, making it easy to write throwaway, one-time use
WxPerl programs. The windows, menus, toolbars are all defined using
C<XRC>.
To simplify menu event handling, just define the hash C<%WxSimple::menu>
with keys that are the XML ID's for each menuitem, and values that are
references to the handlers for that menuitem.
The menu events for C<wxID_OPEN>, C<wxID_SAVE>, C<wxID_SAVEAS> and
C<wxID_EXIT> have default definitions. The first three events get a
file name (using a file dialog for Open and Save As) and then call
the coderef pointed to by $FileOpen, or $FileSave, with that file name
as the argument. Quit will close the window and exit. You can override
any of these definitions, by redefining C<%WxSimple::menu>.
The XML ID for the main frame is given in C<$WxSimple::frmID>.
The number of sections in the status bar is given by C<$WxSimple::sbar>.
If this is C<0>, no status bar is created.
The name of the XML resource file generated by XRC is set in
C<$WxSimple::xrc>.
The variables C<$WxSimple::frame> and C<$WxSimple::xr> contain the main
frame and the XML resource, once C<new> has been called
=head2 Structure
The main program usually looks like a C<use WxSimple> statement, followed
by setting a few parameters. Then the C<%WxSimple::menu> hash usually has
to be defined, providing a handler for every menu event.
This is followed by creating a C<$app> using a call to C<WxSimple->new()>.
At this point the variables C<$WxSimple::frame> and C<$WxSimple::xr> are
created. If further things need to be added to the frame, this is the time
to do that.
Finally a call to C<MainLoop()> starts the window loop.
</code>
<code>
<?xml version="1.0" ?>
<resource>
<object class="wxFrame" name="Frame">
<title>DataProg</title>
<object class="wxToolBar" name="XRCtbar">
<bitmapsize>32</bitmapsize>
<size>32,32</size>
<readmore>
<object class="tool" name="wxID_NEW">
<bitmap stock_id="wxART_NEW"></bitmap>
<tooltip>New</tooltip>
<longhelp>Start a new file</longhelp>
</object>
<object class="tool" name="wxID_OPEN">
<bitmap stock_id="wxART_FILE_OPEN"></bitmap>
<tooltip>Open</tooltip>
<longhelp>Open a File</longhelp>
</object>
<object class="tool" name="wxID_SAVE">
<bitmap stock_id="wxART_FILE_SAVE"></bitmap>
<tooltip>Save currently opened File</tooltip>
<longhelp></longhelp>
</object>
<object class="tool" name="wxID_SAVEAS">
<bitmap stock_id="wxART_FILE_SAVE_AS"></bitmap>
<tooltip>Save File under a different name</tooltip>
</object>
<object class="tool" name="wxID_EXIT">
<bitmap stock_id="wxART_QUIT"></bitmap>
<tooltip>Exit Program</tooltip>
<longhelp>Exit Program</longhelp>
</object>
<object class="separator"/>
<object class="tool" name="wxID_CUT">
<bitmap stock_id="wxART_CUT"></bitmap>
</object>
<object class="tool" name="wxID_COPY">
<bitmap stock_id="wxART_COPY"></bitmap>
</object>
<object class="tool" name="wxID_PASTE">
<bitmap stock_id="wxART_PASTE"></bitmap>
</object>
<object class="tool" name="wxID_DELETE">
<bitmap stock_id="wxART_DELETE"></bitmap>
</object>
<object class="separator"/>
<object class="tool" name="wxID_HELP">
<bitmap stock_id="wxART_HELP"></bitmap>
<tooltip>Help</tooltip>
<longhelp>Usage Instructions</longhelp>
</object>
<object class="tool" name="wxID_ABOUT">
<bitmap stock_id="wxART_INFORMATION"></bitmap>
<tooltip>About</tooltip>
<longhelp>Information about this program</longhelp>
</object>
</object>
<object class="wxMenuBar" name="XRCmenu">
<object class="wxMenu" name="menuFile">
<label>File</label>
<object class="wxMenuItem" name="wxID_NEW">
<label>New</label>
<bitmap stock_id="wxART_NEW"></bitmap>
<accel>Ctrl-N</accel>
<help>Start a new file</help>
</object>
<object class="wxMenuItem" name="wxID_OPEN">
<label>Open</label>
<bitmap stock_id="wxART_FILE_OPEN"></bitmap>
<accel>Ctrl-O</accel>
<help>Open a File</help>
</object>
<object class="wxMenuItem" name="wxID_REVERT">
<label>Revert to Saved</label>
</object>
<object class="wxMenuItem" name="wxID_SAVE">
<label>Save</label>
<bitmap stock_id="wxART_FILE_SAVE"></bitmap>
<accel>ctrl-S</accel>
<help>Save currently opened File</help>
</object>
<object class="wxMenuItem" name="wxID_SAVEAS">
<label>Save As</label>
<bitmap stock_id="wxART_FILE_SAVE"></bitmap>
<accel>ctrl-S</accel>
<help>Save File under a different name</help>
</object>
<object class="wxMenuItem" name="wxID_EXIT">
<label>Exit</label>
<bitmap stock_id="wxART_QUIT"></bitmap>
<accel>Ctrl-X</accel>
<help>Exit Program</help>
</object>
</object>
<object class="wxMenu" name="menuEdit">
<label>Edit</label>
<object class="wxMenuItem" name="wxID_CUT">
<label>Cut</label>
<bitmap stock_id="wxART_CUT"></bitmap>
</object>
<object class="wxMenuItem" name="wxID_COPY">
<label>Copy</label>
<bitmap stock_id="wxART_COPY"></bitmap>
</object>
<object class="wxMenuItem" name="wxID_PASTE">
<label>Paste</label>
<bitmap stock_id="wxART_PASTE"></bitmap>
</object>
<object class="wxMenuItem" name="wxID_DELETE">
<label>Delete</label>
<bitmap stock_id="wxART_DELETE"></bitmap>
</object>
</object>
<object class="wxMenu" name="menuHelp">
<label>Help</label>
<object class="wxMenuItem" name="wxID_HELP">
<label>Help</label>
<bitmap stock_id="wxART_HELP"></bitmap>
<accel>F1</accel>
<help>Usage instructions</help>
</object>
<object class="wxMenuItem" name="wxID_ABOUT">
<label>About</label>
<bitmap stock_id="wxART_INFORMATION"></bitmap>
<help>Information about this program</help>
</object>
</object>
</object>
<object class="wxSplitterWindow">
<object class="wxTextCtrl" name="Text">
<style>wxTE_MULTILINE</style>
</object>
<object class="wxPanel">
<object class="wxBoxSizer">
<orient>wxVERTICAL</orient>
<object class="sizeritem">
<object class="wxCheckBox" name="CheckBox">
<label>A little checkbox</label>
</object>
</object>
<object class="sizeritem">
<object class="wxTextCtrl" name="SmallText"/>
</object>
</object>
</object>
<orientation>vertical</orientation>
<sashpos>100</sashpos>
</object>
<style></style>
</object>
</resource>
</readmore>
</code>