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

Hi, I have the following code:
use strict; use warnings; use Tk; use Tk::DialogBox; my $mw = MainWindow->new(); $mw->OnDestroy(\&exit_app); $mw->Button(-text => 'Exit', -command=> sub {$mw->destroy})->pack(-sid +e=>'right', -ipadx => 10, -padx=>30); MainLoop; sub exit_app{ print "1"; my $db = $mw->DialogBox(-title => 'Error',-buttons => ['CSV',' +Abandon']); $db->Label(-text => "Do you wish to save a new CSV file or aba +ndon changes?")->pack(); my $button = $db->Show(); if ($button eq 'CSV'){ save_csv(); } } sub save_csv{ return 1; }
When I exit (either using the button or using the X in the top right) I get a 1 printed but no dialog box. The documentation for OnDestroy says
OnDestroy accepts a standard perl/Tk callback. When the window associated with $widget is destroyed then the callback is invoked. Unlike $widget->('<Destroy>',...) the widgets methods are still available when callback is executed, so (for example) a Text widget can save its contents to a file.
This doesn't seem to be the case here, the DialogBox is not created. I am aware that I could remove $mw->OnDestroy() and call exit_app() directly from the button and then do $mw->destroy() at the end of the sub. However I then need to be able to disable (or preferably trap) the use of the X button. Any ideas?

Replies are listed 'Best First'.
Re: $mw->OnDestroy and Dialog Box
by liverpole (Monsignor) on Mar 15, 2007 at 12:53 UTC
    Hi Graham Drabble,

    It's true that you can use OnDestroy to invoke some code when a widget is destroyed, but only if it's not the MainWindow.  If it's the MainWindow that's destroyed, it's too late, as you've discovered.

    Here's a way to test it.  With some slight modifications to your code and the addition of a boolean variable $b_destroy_button you can try running the following:

    use strict; use warnings; use Tk; use Tk::DialogBox; # User-defined my $b_destroy_button = 0; # If set, destroy the Button, not the MainW +indow # Main program my $mw = MainWindow->new(); my $button = $mw->Button(-text => 'Exit'); # Choose which widget to destroy my $psub; ($b_destroy_button) or $psub = sub { $mw->destroy() }; ($b_destroy_button) and $psub = sub { $button->destroy() }; $button->configure(-command => $psub); $button->pack(-side => 'right', -ipadx => 10, -padx => 30); $button->OnDestroy(\&exit_app); MainLoop; # Subroutines sub exit_app{ print "1\n"; my $db = $mw->DialogBox(-title => 'Error',-buttons => ['CSV','Aban +don']); my $text = "Do you wish to save a new CSV file or abandon changes? +"; $db->Label(-text => $text)->pack(); my $button = $db->Show(); if ($button eq 'CSV'){ save_csv(); } } sub save_csv{ return 1; }

    Now if you run it "as is", you'll get the same behavior, because destroying the MainWindow causes a problem.

    But when you destroy just the Button instead, by changing the value of $b_destroy_button to 1, it will successfully give you the DialogBox you want.

    An alternative way to do this (if you don't want to have to destroy a widget before taking the action of possibly writing the CSV file), is to just put the call to exit_app() in the callback for the Button.  Then, after you're done writing the CSV file (or not), you can have the callback destroy the application, like so:

    use strict; use warnings; use Tk; use Tk::DialogBox; # Main program my $mw = MainWindow->new(); my $button = $mw->Button(-text => 'Exit'); $button->configure(-command => \&exit_app); $button->pack(-side => 'right', -ipadx => 10, -padx => 30); MainLoop; # Subroutines sub exit_app{ print "1\n"; my $db = $mw->DialogBox(-title => 'Error',-buttons => ['CSV','Aban +don']); my $text = "Do you wish to save a new CSV file or abandon changes? +"; $db->Label(-text => $text)->pack(); my $button = $db->Show(); if ($button eq 'CSV'){ save_csv(); } $mw->destroy(); } sub save_csv{ return 1; }

    s''(q.S:$/9=(T1';s;(..)(..);$..=substr+crypt($1,$2),2,3;eg;print$..$/

      I had tried the latter example. The problem is that the exit_app() sub gets bypassed if you shut down using the 'X' button rather than the 'Exit' button.

      I have come up with

      use strict; use warnings; use Tk; use Tk::DialogBox; my $exit_pressed = 0; my $mw = MainWindow->new(); $mw->OnDestroy(\&exit_x); $mw->Button(-text => 'Exit', -command=> \&exit_app)->pack(-side=>'righ +t', -ipadx => 10, -padx=>30); MainLoop; sub exit_app{ print "1"; $exit_pressed = 1; my $db = $mw->DialogBox(-title => 'Save?',-buttons => ['CSV',' +Abandon']); $db->Label(-text => "Do you wish to save a new CSV file or aba +ndon changes?")->pack(); my $button = $db->Show(); if ($button eq 'CSV'){ save_csv(); } $mw->destroy(); } sub save_csv{ return 1; } sub exit_x{ unless ($exit_pressed){ print "2"; my $check = MainWindow->new(-title=> 'Save?'); $check->Label(-text => "Do you wish to save a new CSV file + or abandon changes?")->pack(); $check->Button(-text => 'CSV', -command => sub { save_csv( +);$check->destroy();})->pack(-side=>'bottom'); $check->Button(-text => 'Abandon', -command => sub {$check +->destroy();})->pack(-side=>'bottom'); } }

      However it a) isn't elegant from a code point of view, b) means that the screen (which will contain data) is closed before the option to have it is displayed and c)It will be hard to make the 2 save windows look the same.

Re: $mw->OnDestroy and Dialog Box
by Graham Drabble (Initiate) on Mar 15, 2007 at 14:24 UTC

    Solved it.

    Replace $mw->Destroy(\&exit_app) with $mw->protocol('WM_DELETE_WINDOW' => \&exit_app)

    use strict; use warnings; use Tk; use Tk::DialogBox; my $mw = MainWindow->new(); my $button = $mw->Button(-text => 'Exit', -command=> \&exit_app)->pack +(-side=>'right', -ipadx => 10, -padx=>30); $mw->protocol('WM_DELETE_WINDOW' => \&exit_app) ; MainLoop; sub exit_app{ print "1"; my $db = $mw->DialogBox(-title => 'Save?',-buttons => ['CSV',' +Abandon']); $db->Label(-text => "Do you wish to save a new CSV file or aba +ndon changes?")->pack(); my $button = $db->Show(); if ($button eq 'CSV'){ save_csv(); } $mw->destroy(); } sub save_csv{ return 1; }