With that in mind I have written a module WxSimple. The main window and all the sub-windows, menus, toolbar, and other gadgets are to be created in an XML resource file, using any XRC editor. The simplest program is just two lines, which displays the window, and nothing else.
To add functionality, you simple create a hash consisting of menuitem names associated with a coderef to the handler. Then simply define all the handler subs and voila, you have a working program. Even then I have added some file open/save support, which can be ignored if not needed.
I have tried to keep it absolutely minimal. I was tempted to throw in basic document-view functionality, but resisted in order to get to the common denominator.
Since I have never written modules other than for myself, I am sure it needs work. I just read the tutorials about writing modules today, and see that there is much that I need to do. So I will be improving it stylistically, but my request was for more functional comments.
Am I supposed to put the code here? Currently you can see a fully runnable sample, along with documentation on b4swine's scratchpad.
Update: I have pasted the code right here
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.
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.
Since I use ActivePerl for MS Windows, I have provided complete instructions for that. For *nix users it should be easier. Use ppm to download the Wx 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/.
You also need to get some kind of a XML resource compiler. I like XRCed (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
You are now ready to use the XRCed program included with WxPython. If you open the file WxSample.xrc (file located at the end), in XRCed, you can explore how this is structured.
Here is the code for WxSample.pl
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'); } }
use WxSimple qw($xrc $frmID $sbar %menu $OpenFile $SaveFile $Close +Win $icon);
$xrc refers to the path to the file that was created by XRCed.
$frmID is the XMLid that was given to the main frame created in the xrc file.
$sbar is the number of panels in the status bar at the bottom of the window. If this is zero, no status bar is created.
%menu 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. File--Open, File--Save, File--SaveAs, 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.
$OpenFile and $SaveFile are described above in %menu.
&$CloseWin is the routine called when the window is closed. It should Destroy all top level windows. If you create other top level windows, you should redefine this.
$icon is a default WxPerl icon, which you can change as you like.
The $id 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.my ($window, $id) = WxSimple::FindWindowByXid('xmlid')
If you are going to be using files, you need to define $WxSimple::FileOpen, or $WxSimple::FileSave, as appropriate. Initially these are set to dummy routines, but you can refer them to your own routine, as illustrated above for FileOpen.
You may now define routines for each of the menu items, as well as other event handlers you have defined.
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 handl +ers wxID_SAVE => \&Save, # that just get a file-name for r +eading 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 wind +ows # If there are any toplevel dialogs, close them here, otherwise th +e # 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 usin +g 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 over +ride 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, f +ollowed by setting a few parameters. Then the C<%WxSimple::menu> hash usua +lly 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::x +r> 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.
<?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> <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>
|
|---|
| Replies are listed 'Best First'. | |
|---|---|
|
Re: RFC: WxPerl Simplified
by ww (Archbishop) on Sep 12, 2007 at 14:49 UTC | |
|
Re: RFC: WxPerl Simplified
by BerntB (Deacon) on Sep 13, 2007 at 00:53 UTC | |
|
Re: RFC: WxPerl Simplified
by spectre9 (Beadle) on Sep 13, 2007 at 01:39 UTC | |
by ademmler (Novice) on Jan 20, 2009 at 21:38 UTC | |
|
Re: RFC: WxPerl Simplified
by wilsond (Scribe) on Jan 21, 2009 at 11:38 UTC |