spatterson has asked for the wisdom of the Perl Monks concerning the following question:

Greetings fellow acolytes

I have recently started working with Wx as a generic yet more platform native toolkit than Tk and have hit a snag. I'm writing a small data entry application which is basically 2 screens. Screen 1, enter some data & click a save button, then screen 2 comes up with a confirmation message & button to get a new clean starting screen. Thats the theory. In practice, I can get up to the confirmation screen OK & wipe it but I can't redisplay the initial screen widgets.

The code I have (trimmed down & simplified as far as possible) is

#!/usr/bin/perl use Wx; ####################################### # # package MainFrame; # # define a frame inside the window # then a panel to draw controls on ####################################### use base qw(Wx::Frame); use Wx qw(wxDefaultPosition wxDefaultSize wxDEFAULT_FRAME_STYLE wxNO_FULL_REPAINT_ON_RESIZE wxCLIP_CHI +LDREN); use Wx::Event qw( EVT_BUTTON ); sub new { my $class = shift; my $self = $class->SUPER::new(@_); $self->{panel} = Wx::Panel->new($self, -1); $self->{txt} = Wx::StaticText->new($self->{panel}, 1, 'Test', [50,15]); # authorise button my $btnID = 'authBtn'; $self->{btn} = Wx::Button->new($self->{panel}, $btnID, 'Authorise' +, [50,50]); EVT_BUTTON($self, $btnID, \&auth_clicked); return $self; } sub auth_clicked { my $self = shift; $self->{panel}->Destroy; my $panel = Wx::Panel->new($self, -1); my $btnID = 66; $self->{btn} = Wx::Button->new($panel, $btnID, 'Restart', [50,50]); EVT_BUTTON($self, $btnID, \&newtran); } sub newtran { # return to start, place another transaction # doesn't work though. my $self = shift(); # the window handle print ref $self; $main::wxobj->Destroy; $main::wxobj->new; } ####################################### # # package MyApp; # # ####################################### use base qw(Wx::App); # Inherit from Wx::App sub OnInit # Every application has its own OnInit method that will # be called when the constructor is called. { my $self = shift; $frame = MainFrame->new(undef, # Parent window -1, # Window id 'Test App', # Title [1,1], # position X, Y [300, 400] # size X, Y ); $self->SetTopWindow($frame); # Define the toplevel window $frame->Show(1); # Show the frame } ########################################################### # # The main program # package main; use strict; use warnings; use vars qw/$wxobj/; $wxobj = MyApp->new(); # New HelloWorld application $wxobj->MainLoop;

just another cpan module author

Replies are listed 'Best First'.
Re: Clearing & Redrawing a Wx Window
by zentara (Cardinal) on Sep 25, 2006 at 16:44 UTC
    Hi, I'm not able to run your code, because I don't have Wx going, but from my experience with Tk and Gtk2, I would say to avoid the "destroy-create-new" cycle.
    $main::wxobj->Destroy; $main::wxobj->new;
    In most gui toolkits, you will gain memory by doing that( although a single cycle wouldn't be an enormous gain). You should look for $wxwidg->Withdraw (or it's equivalent) to just remove the window from view( without destroying it). Then while it is in it's withdrawn state, reconfigure all it's children to initial values, then raise it back into view when desired.

    Although I may be getting thrown off by all the package interelations, I think you are probably destroying the code which makes it possible to launch the new window. You call Destroy from within the package itself, on itself, and "itself" IS the program event loop. At that point you probably end the MainLoop and the program stops.

    # The main program $wxobj = MyApp->new(); # New HelloWorld application # when $destroy is called from within $self, # the MainLoop stops $wxobj->MainLoop;

    I'm not really a human, but I play one on earth. Cogito ergo sum a bum
Re: Clearing & Redrawing a Wx Window
by rcseege (Pilgrim) on Sep 26, 2006 at 06:39 UTC

    Updated: changed references to unpack, unplace, ungrid to packForget, placeForget, and gridForget respectively. That was what I intended, but brain must have misfired. Also, corrected a typo in the code example.

    I agree with Zentara that your application shouldn' t be trying to recreate itself, when it could recreate just the desired Panel. Better still, the application could create one instance of each screen and hide/show the appropriate screen on demand....

    First, by way of comparison, in Perl/Tk this sort of thing can be accomplished a lot of different ways but it usually amounts to variations of the following:

    1. A Toplevel window (modal or otherwise) is launched from the main application to do some work. This Toplevel can either be created on-the-fly or created once and reused over and over again. (withdrawn and deiconified rather than destroyed then recreated) as Zentara suggested. Typically, this is faster and generally better on memory consumption.

    2. A window can be shown/hidden with a combination of pack/packForget, grid/gridForget, place/placeForget, etc. This is a lot like playing with the CSS visibility attribute for DHTML. You see this idea used for Wizards, Notebook-style apps, and other apps that use few Toplevels.

    Of the two approaches, number 2 sounds closer to what you're trying to do. When using Wx, it's good to get familiar with the methods and Classes that have to do with Layout and display as quickly as possible.I recommend that you read up on the various Sizer implementations that provide Layout Management. These are slightly lower-level than what you might be used to using Tk, and closer to what's available in Java, Qt, and probably Gtk.

    Some other critical methods to learn are Show(boolan), which will toggle visibility for a widget. Show is perhaps the closest equivalent to pack/packForget. Also, Layout, Update, and Refresh are useful.

    Here's an example of one way to go about it -- there are plenty of other approaches. I think in your example, you were running afoul of a Layout issue.

    Some general advice: use Sizers over absolute positioning, go ahead and let Wx generate the id by supplying -1 as the Id (so long as you keep a reference to the widget). It doesn't take much to expand that example and take it further. I think one avenue I would have pursued would be to create a Panel subclass for each screen, create each one once, and then only Show the one that was needed.

    You might consider taking a look at: wxWizardPage (and wxWizard).

    Rob
      Thanks, that works a charm.

      As I'd done some Tk development in the past, I was trying to replicate some of the placement tricks where you place (pack) the widgets within a top level frame which you can then clear with packForget & recall a sub to fill the frame again.


      just another cpan module author