Beefy Boxes and Bandwidth Generously Provided by pair Networks
Don't ask to ask, just ask
 
PerlMonks  

RFC: WxPerl Simplified

by b4swine (Pilgrim)
on Sep 12, 2007 at 13:53 UTC ( [id://638554]=perlmeditation: print w/replies, xml ) Need Help??

I like WxPerl (but haven't used any other Perl GUI to compare). Whenever I start a new project, I often cut and paste a large portion of another project, to set up the initial window, set up the menu bar, tool bar, status bar, etc. I thought it would be nice to have a module to do all the basics, so that you could immediately get to the programming part.

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

WxSimple Tutorial

This is a tutorial for WxSimple, a package I wrote to make throwaway WxPerl programs using XRC.

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.

Getting Started

To get started, you need to get some components together, becuase unfortuantely, Wx is not a standard package.

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.

Sample Program: For those than know WxPerl

For those who are already familiar with WxPerl, a sample program illustrating the use of WxSimple is provided here. It will need two other supporting files namely the package WxSimple.pm and the resource file WxSample.xrc that was created using XRCed. Both of these files are given in the last section.

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'); } }

A Typical Sequence

We go through a typical sequence needed to create a program using WxSimple.

1. Use XRCed to create Menu and a Window Layout

First a resource file is created using XRCed. 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 xrc 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 .

2. Set Parameters

A typical programs first sets up parameters. This is done by
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.

3. Create an App

Creating a WxSimple app by my $app = WxSimple->new() .

4. Add more Handlers

After that, the variables $WxSimple::frame and $WxSimple::xr 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
my ($window, $id) = WxSimple::FindWindowByXid('xmlid')
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.

5. Define Handler Routines

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.

For those learning WxPerl

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.

Manual

A manual intended for C programmers, with one-line notes for Perl and Python is available. This is not up to date, but this is all there is.

Tutorials

There are a nice set of tutorials here.

Samples

Along with WxPerl, two kinds of sample programs are provided. One is a very large sample program called demo.pl which illustrates many of the various controls, in one integrated program. The other is directory samples which contains a set of small independent programs, each of which illustrates one control.

Wiki

A wiki devoted to wxperl, is a bit undermaintained, and out of date, but you can find many things here.

Groups

There are some groups also, where you can ask questions and get answers, but none as nice as this.

Supporting Code

In order to run, this needs two additional support files, WxSimple.pm and WxSample.xrc. 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 WxSample.pl.
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.
Here is WxSample.xrc
<?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 your final question, "yes."

    Were you to delete the code from your scratch pad, the question, and possibly the replies, provide insufficient info for future readers.

    And <readmore> tags may be useful.

Re: RFC: WxPerl Simplified
by BerntB (Deacon) on Sep 13, 2007 at 00:53 UTC

    A nice effort! One of the GUI toolkits has been on my tolearn-list for a while.

    I hope 'b4swine' doesn't think me ungracious if I ask if anyone could list the practical differences between WxPerl and gtk2-perl? :-)

    Both seems capable enough, from checking Google and monks. I found nice tutorials (on 'monks and elsewhere), but...

    I assume that WxPerl runs on more platforms? Which is easier to learn? Any install problems?

    This doesn't seem to be a flame-war subject, or I wouldn't ask. (I am going to write a small demo app and consider being ambitious and not doing a console program.)

    (Update: No answer? I assume this is an old flame war subject. :-) Forget I asked.)

Re: RFC: WxPerl Simplified
by spectre9 (Beadle) on Sep 13, 2007 at 01:39 UTC
    One of your questions was "Does the code go here?"
    While I have only recently joined PerlMonks, I have read it for years, and find that putting the code in your scratchpad or other node away from the front page may get more responses.
    It's just too distracting to read code alongside all the other juicy tidbits floating around the gates. -- Patrick
    spectre#9 -- more dangerous than acme
      Hi, this seems to be a verry good tool. Now I have tried to start the sample and got hits error: Usage: Wx::XmlResource::GetXRCID(str_id) at WxSimple.pm line 62 may I get some help here. Thanks Alexander
Re: RFC: WxPerl Simplified
by wilsond (Scribe) on Jan 21, 2009 at 11:38 UTC

    I couldn't find any real usable resources on how to use XRC files with Wx until I found your entry (I'm probably just looking in the wrong places, of course). Thanks for putting this up. It's helped me greatly.


    While I ask a lot of Win32 questions, I hate Windows with a passion. That's the problem with writing a cross-platform program. I'm a Linux user myself. I wish more people were.
    If you want to do evil, science provides the most powerful weapons to do evil; but equally, if you want to do good, science puts into your hands the most powerful tools to do so.
    - Richard Dawkins

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlmeditation [id://638554]
Approved by moritz
Front-paged by moritz
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others making s'mores by the fire in the courtyard of the Monastery: (2)
As of 2024-03-19 07:14 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found