in reply to Re: Handling child process and close window exits in Perl/Tk
in thread Handling child process and close window exits in Perl/Tk
Update: did some minor tag/formatting edits
There are lots of ways to reuse code in modules. My preferred approach when using Tk is to extend existing Tk widgets. I will either extend Frame and create Composites (or megawidgets as they are sometimes called), or I will extend other widgets that do most of what I want.
Typically, I will also throw most non-gui functionality into other non-tk modules, and I will try to keep the two varieties of modules as separate as possible with no bleedover. I do this to make it easier to test one without the other, and also in case I decide to move to a different kind of GUI at a later time.I will even adhere to this separation within a single script if the appliciation is anything more than a trival example or snippet... but that mostly boils down to personal taste on my part, and there are many other approaches you could take. So given your case (and without knowing more details) I might have done something like this:
## This would be my main script, and I'll keep it ## fairly simple. Notice that I'm using a module ## called ContinueBox. ContinueBox is trivial, but let's ## say that ContinueBox maps to the your child script. It ## contains the functionality that you want to be easily ## reused. use strict; use Tk; use ContinueBox; my $mw = MainWindow->new; my $dialog = $mw->ContinueBox; $mw->Button( -text => "start child", -command => sub { my $result = $dialog->Show; ## I'm not doing much with the result here ## but I could use it terminate the entire ## script if I wante if the result was "Exit". print "$result\n"; })->pack; MainLoop;
Now I will create a module called ContinueBox. The first example assumes that I want to reinvent the wheel and create my own version of Tk's DialogBox. Perhaps I don't like that DialogBox is a modal window...
package ContinueBox; use Tk; use base qw(Tk::Toplevel); Tk::Widget->Construct('ContinueBox'); sub Populate { my ($cw, $args) = @_; $cw->SUPER::Populate($args); $cw->{Result} = ""; ## SETUP CONTROLS $cw->Label( -text => "Would you like to continue?" )->pack(-side => 'top'); my $bFrame = $cw->Frame->pack(-side => 'bottom'); $bFrame->Button( -text => "Continue", -command => sub { $cw->{Result} = "Continue"; } )->pack(-side => 'left'); $bFrame->Button( -text => "Exit", -command => sub { $cw->{Result} = "Exit"; } )->pack(-side => 'left'); $cw->protocol(WM_DELETE_WINDOW => sub { $cw->{Result} = "Exit"; }); $cw->withdraw; } sub Show { my $cw = shift; $cw->{Result} = ""; $cw->deiconify; $cw->waitVariable(\$cw->{Result}); $cw->withdraw; return $cw->{Result}; } 1;
There are a few lines of interest here:
package ContinueBox; use base qw(Tk::Toplevel);
With these two lines, I'm saying that my new Widget, ContinueBox is a Toplevel widget. It has all the same methods that Toplevel has.
Tk::Widget->construct('ContinueBox');
This is a bit of syntactic sugar that allows me to create ContinueBox the same way that other Tk widgets are created, like: $mw->ContinueBox which I do in the main script. This line is purely optional. I will often omit this line when I'm creating "private" widgets that I don't want to be used outside of my application. They can still be instantiated, but it forces me to create a new Instance of my widget like this:
my $dialog = ContinueBox->new($parent, %args);
There are other times when this can be useful, but I digress...
sub Populate { ... }
This is something like a template method. In Tk, the new method already has an implementation that does lots of work for you under the hood. You typically do not want to override new. It calls several other methods some of which are NoOp, and others that do things for you. Populate is one of these methods, and usually the one most override. It is usually where the setup/layout of the custom widget occurrs. Note that in the first line of my Populate method, I call SUPER::Populate($args). I do this because I want my new Widget to have the same setup/options that a Toplevel has.
My ContinueBox widget also features a very useful construct: $cw->waitVariable. Consider waitVariable as a sort of event processing loop within a MainLoop. That is, it acts almost exactly like MainLoop, and will continue to process Tk events until the variable it watches changes. Until the variable changes however, execution will not continue past waitVariable.
In the next implementation of ContinueBox,I will extend DialogBox instead to take advantage of its existing functionality:
package ContinueBox; use Tk; use base qw(Tk::DialogBox); Tk::Widget->Construct('ContinueBox'); sub Populate { my ($cw, $args) = @_; $args->{-buttons} = [qw/Continue Exit/], $args->{-default_button} = "Continue"; $cw->SUPER::Populate($args); $cw->add("Label", -text => "Do you want to continue?")->pack; $cw->protocol(WM_DELETE_WINDOW => sub { $cw->Subwidget('B_Exit')->invoke; }); } 1;
This allows me to accomplish the same basic purpose as the previous implementation of ContinueBox, but now I'm benefitting from DialogBox's code, which reduces what I have to write, myself. Now i could have accomplished all of this simply by using DialogBox within the main, but that's besides the point. If there is substantial functionality within that main, then it is harder for other scripts to easily reuse.
|
|---|