Feb 27

Wx::Perl lessons learned

Category: Linux,Perl   — Published by tengo on February 27, 2009 at 1:04 pm

WxPerl (Wx::App on cpan) is a great tool to design visually rich graphical user interfaces for your scripts. But the downside is that the learning curve is quite steep. There were many incident when I was banging my head against the wall for hours until I finally got what I intended. This post is meant as a work-in-progress, a living document where I (hopefully) will publish lessons learned while developing with Wx::Perl.

Understand the hierarchical nature of Wx:: classes

and perl modules in general. Each Wx class has its own set of Functions, Events and Styles. They tell you pretty much the basics of what is possible and can be done with a certain class. But while looking up and down the list of functions, you might think "a basic function I need seems to be missing". That's where the base classes come in. More specific classes, like wxGrid, are in general derived from more basic, more universal classes. You can see from which classes under the"Derived from" headline. Click on one of those classes and you get to the man page for the class the former class is based on. And all functions available for the base class are inherited into the namespace of the more specific derived class. So wxGrid does have the wxGrid::GetSelectedCols and wxGrid::GetSelectedRows, yes, but it also inherited wxScrolledWindow::SetScrollRate and more from wxScrolledWindow.

Handling Wx::StaticText overflows

Yesterday, I encountered a strange bug in my application: a Wx::StaticText element had a filename in it. When I filled it with a filename that was excessively large, larger than the underlying panel, the text nicely disappeared at the border of the panel, just as in CSS "overflow: hidden" markup would render it.The error cam when I resized the Window, suddenly the underlying Wx::Panel resized to the full width of the long filename - which was undesireable.

After some online research, I couldn't find anyone else with this problem, so I turned to the documentation, and guess what, sometime RTFM really does help! Wx:StaticText has this insightful flag:

"wxST_NO_AUTORESIZE - By default, the control will adjust its size to exactly fit to the size of the text when SetLabel is called. If this style flag is given, the control will not change its size (this style is especially useful with controls which also have wxALIGN_RIGHT or CENTER style because otherwise they won't make sense any longer after a call to SetLabel) "

But don't expect it to work out of the box! Depending on your configuration of nested elements, either the Wx::StaticText element will need a fixed width (so the contained text will actually be visible, now as the element will no autoresize to the needed width of the text) or similar.

I am lost in my nested sizers/ elements/ panels/ etc.

Most "invisble" elements of a window actually are derrived from the base class Wx::Window. A nifty style that WX::Window has to offer is wxSIMPLE_BORDER, which is very helpful while handcoding Wx applets: on Panels, Notebooks, Grids etc. just add the wxSIMPLE_BORDER on creation and you can see where it is, what its boundaries are or check the padding/ border settings!

Properly displaying an image

Getting an image to actually show up in the GUI of your app can get a bit tricky. You can try it with Wx:StaticBitmap, or as I did here with Wx::PaintDC:

my $panel = Wx::Panel->new($this->{mainwindow}, -1, wxDefaultPosition, [100, 200], wxSUNKEN_BORDER);

my $dc = Wx::PaintDC->new($panel);

my $bmp = Wx::Bitmap->new("image.jpg", wxBITMAP_TYPE_ANY );

$dc->DrawBitmap($bmp, 0, 0, 0);

If you don't know the size of your input image, but know the destination size, we can add a bit of code to do a dynamic resize, as shown here, with a combination of Wx::Image and Wx::PaintDC:

my $dc = Wx::PaintDC->new($this->{Thumbnail_panel});

my $bmp = Wx::Bitmap->new(Wx::Image->new("image.jpg", wxBITMAP_TYPE_ANY)->Scale(100,200));

$dc->DrawBitmap($bmp, 0, 0, 1);

If you image does not show up, check your ->Update, ->Refresh and ->Redraw calls. I had it once that, although the PaintDC kicks in on redraws, I fired another (redundant) manually forced Redraw and thus deleted the PaintDC element I just created again.

My (compiled) application only shows the standard Wx logo as icon

You need to set an icon-bitmap for use by Wx:: as the application's icon (visible in the task bar, the desktop, links, etc):

# load an icon and set it as app's/frame's icon

$this->SetIcon(Wx::Icon->new("path/to/my/icon.gif", wxBITMAP_TYPE_GIF) );