saw55 has asked for the wisdom of the Perl Monks concerning the following question:
I have a several hundred line Perl/Tk program with a subroutine (timedDialog) that opens a window with a message such as “starting backup”, “creating tar file”, “finished”, etc. and then the window closes after a few seconds. This all works fine, but when I try to use this sub to display an “exiting” message after the user hits a cancel button the script exits before the message displays. This seems to be because of the “exit(0)” statement immediately following it—I tried putting “sleep 5” in between, but that didn’t help.
The following demonstrates my problem. If the exit statement in sub checkDays is commented out the message is displayed, if it is uncommented you never see the message. Can someone please tell me what I am doing wrong?
#!/usr/bin/perl
######################################################################
+##
use strict;
use warnings;
use Tk;
my $mw = MainWindow -> new;
my $timedDialogTitle = '';
my $timedDialogText = '';
my $svBtn = undef; #Option window SAVE button.
&setupGUI;
$mw->deiconify();
$mw->raise();
MainLoop;
exit(0);
################################################
################################################
sub setupGUI{
$timedDialogTitle = "STARTING BACKUP";
$timedDialogText = "Backing up files...";
$svBtn = $mw->Button( -text => "SAVE", -command => sub {&checkDays
+; &timedDialog($timedDialogTitle, $timedDialogText, 5_000);});
$svBtn->grid(-row => 9, -column => 2, -sticky => 'e');
$mw->bind('<KeyPress-Return>' => sub {&checkDays; &timedDialog($ti
+medDialogTitle, $timedDialogText, 5_000);});
$mw-> withdraw();
}
#####################################
sub checkDays {
&timedDialog("Exiting", "O.K., no backup will be made,
+ then....Exiting", 5_000);
exit(0);
}
#####################################
sub timedDialog
{
my $subwindow = MainWindow->new;
$subwindow->geometry("490x150+400+400");
$subwindow->title($_[0]);
my $label = $subwindow->Label(-text => $_[1]);
$label->pack;
$subwindow->after($_[2], sub {$subwindow->destroy;});
}
#####################################
Re: Perl/Tk and exit(0)
by kcott (Archbishop) on Apr 03, 2020 at 08:03 UTC
|
G'day saw55,
Welcome to the Monastery.
I see the reason for your problem has been explained by ++jcb.
Instead of calling exit(0) as you currently do,
consider passing a flag that instructs &timedDialog to either
destroy the dialogue or exit the application.
Here's a fully functional, albeit extremely barebones, example of what I mean.
#!/usr/bin/env perl
use strict;
use warnings;
use Tk;
{
my $mw = MainWindow::->new();
$mw->Button(
-text => 'Transient message',
-command => sub { out_msg(\$mw, 'Message ...', 2_000) },
)->pack();
$mw->Button(
-text => 'No backup',
-command => sub { out_msg(\$mw, 'Exiting ...', 2_000, 1) },
)->pack();
}
sub out_msg {
my ($mw_ref, $msg, $delay, $exit) = @_;
my $tl = $$mw_ref->Toplevel();
$tl->Label(-textvariable => \$msg)->pack();
my $handle_msg = $exit
? sub { exit }
: sub { $tl->destroy };
$tl->after($delay, $handle_msg);
}
MainLoop;
Note that the flag is only needed for those callbacks where you want to exit:
you may only need to make minimal changes if you adopt this technique.
Here's a few other points that aren't directly related to your current problem:
-
Avoid referencing external variables in your subroutines; pass them in as arguments.
Prefer references to variables rather than the variable's value;
this will avoid unexpected bugs, that are often hard to track down, in your Tk scripts.
I've shown \$mw and $mw_ref in my code as an example;
admittedly, this was somewhat contrived but there were few opportunities for examples
in such a short script — it's still a valid example and, if you run that code,
you'll see it works without any problems.
-
You might also note that $mw does not have file scope.
It is constrained to an anonymous block and completely inaccessible by &out_msg.
-
Prefer calling subroutines without a leading ampersand; i.e. subname() in favour of &subname.
The &subname and &subname(@arg_list) forms are generally not what you want and can lead to problems.
See perlsub for details.
-
Probably more for future reference: there are many dialogue and messaging widgets
that are part of the core Tk distribution.
See the Popups and Dialogs section of the Tk documentation.
| [reply] [d/l] [select] |
|
Thanks you so much, and thanks to jcb as well. As I am sure you you can tell, I am new to Tk, but this gives me something to work with. I knew there were standard popups available--I use them elsewhere in the program--but I think I couldn't figure out how to close them programatically.
| [reply] |
|
| [reply] [d/l] |
Re: Perl/Tk and exit(0)
by tybalt89 (Monsignor) on Apr 03, 2020 at 01:24 UTC
|
#!/usr/bin/perl
use strict; # https://perlmonks.org/?node_id=11114965
use warnings;
use Tk;
my $mw = MainWindow -> new;
$mw->geometry( '+600+400' );
my $timedDialogTitle = '';
my $timedDialogText = '';
my $svBtn = undef; #Option window SAVE button.
&setupGUI;
#$mw->deiconify();
#$mw->raise();
MainLoop;
#exit(0);
################################################
################################################
sub setupGUI{
$timedDialogTitle = "STARTING BACKUP";
$timedDialogText = "Backing up files...";
$svBtn = $mw->Button( -text => "SAVE",
-command => sub {&checkDays;
# exit(0);
});
$svBtn->grid(-row => 9, -column => 2, -sticky => 'e');
$mw->bind('<KeyPress-Return>' => sub {checkDays();
# exit(0);
});
# $mw-> withdraw();
}
#####################################
sub checkDays
{
timedDialog("Exiting",
"O.K., no backup will be made, then....Exiting",
2_000);
}
#####################################
sub timedDialog
{
print ("in timedDialog\n");
# my $subwindow = MainWindow->new;
my $subwindow = $mw->Toplevel;
$subwindow->geometry("490x150+400+400");
$subwindow->title($_[0]);
my $label = $subwindow->Label(-text => $_[1]);
$label->pack;
$subwindow->after($_[2], sub {$subwindow->destroy;});
print ("after timedDialog\n");
}
#####################################
| [reply] [d/l] |
Re: Perl/Tk and exit(0)
by 1nickt (Canon) on Apr 02, 2020 at 23:15 UTC
|
Hi, I am not a Tk programmer, but ... exit exits :-) If you want to return from a sub, return :-)
(And commenting out the exit statement makes your script work because you don't need to use an explicit return statement to return from a sub, if you are not returning anything.)
Hope this helps!
The way forward always starts with a minimal test.
| [reply] [d/l] [select] |
|
G'day 1nickt,
The link you posted labeled "exit" is to a Google search
(http://www.google.com/search?q=exit%20site%3Aperldoc.perl.org).
The first item returned from that search is to perldoc (https://perldoc.perl.org/functions/exit.html).
I couldn't see anything else pertinent, so I'm wondering why the intermediary Google step.
Anyway, the point is somewhat moot as that's the wrong "exit".
"I am not a Tk programmer ..."
In my experience, even amongst Tk programmers, it's a little known fact that
Tk exports, by default, its own exit:
Tk::exit.
Knowing that doesn't help the OP but I thought it was worth mentioning.
| [reply] [d/l] |
|
Thanks Ken, I'll update the link. Not sure how that happened.
Update: I fixed the links by changing from [perldoc:// to [doc://. I wonder if the behaviour of the former syntax is new, broken, or something I just imagined was different. The links as I originally posted definitely went to a Goog search. Maybe a topic for PM Discussion...
The way forward always starts with a minimal test.
| [reply] [d/l] [select] |
|
|
|
| [reply] [d/l] [select] |
|
|
|
|
|
Thanks for the reply. I know exit causes the program to exit, but I believe the call to the subroutine preceding the exit statement should cause the message window to display before the program exits. I know sub timedDialog executes because I inserted a print statement in it and it does indeed print but the message window never displays; and I put a sleep statement between the sub call and exit in hope of allowing time for the message to display but no go.
I just noticed I called timedDialog both in sub checkDays and in sub setupGUI I fixed this and still no message shows
Fixed code follows
#!/usr/bin/perl
######################################################################
+##
use strict;
use warnings;
use Tk;
my $mw = MainWindow -> new;
my $timedDialogTitle = '';
my $timedDialogText = '';
my $svBtn = undef; #Option window SAVE button.
&setupGUI;
$mw->deiconify();
$mw->raise();
MainLoop;
exit(0);
################################################
################################################
sub setupGUI{
$svBtn = $mw->Button( -text => "SAVE", -command => sub {&checkDays
+; exit(0);});
$svBtn->grid(-row => 9, -column => 2, -sticky => 'e');
$mw->bind('<KeyPress-Return>' => sub {&checkDays; exit(0);});
$mw-> withdraw();
}
#####################################
sub checkDays {
&timedDialog("Exiting", "O.K., no backup will be made,
+ then....Exiting", 25_000);
sleep 15;
}
#####################################
sub timedDialog
{
print ("in timedDialog\n");
my $subwindow = MainWindow->new;
$subwindow->geometry("490x150+400+400");
$subwindow->title($_[0]);
my $label = $subwindow->Label(-text => $_[1]);
$label->pack;
$subwindow->after($_[2], sub {$subwindow->destroy;});
}
#####################################
| [reply] [d/l] |
|
| [reply] [d/l] |
|
|
|
Re: Perl/Tk and exit(0)
by Anonymous Monk on Apr 03, 2020 at 12:46 UTC
|
Especially if the program is about to exit anyway, you can insert another call to the event-loop handler so that the event queue is run through one more time. The drawing never happens because the event is queued but never processed. | [reply] |
|
|