Beefy Boxes and Bandwidth Generously Provided by pair Networks
laziness, impatience, and hubris
 
PerlMonks  

Perl/Tk and exit(0)

by saw55 (Novice)
on Apr 02, 2020 at 22:55 UTC ( [id://11114965]=perlquestion: print w/replies, xml ) Need Help??

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;}); } #####################################

Replies are listed 'Best First'.
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.

    — Ken

      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.

        "Thanks you so much"

        You are very welcome.

        "I knew there were standard popups ... but I think I couldn't figure out how to close them programatically."

        Take a look at the invoke() method of Tk::Button. You could give the option to close manually via a button or, if that option isn't taken, close programmatically after the specified delay.

        — Ken

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"); } #####################################
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.

      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.

      — Ken

        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.

        As I understand, the two exit procedures only matter if you fork. Calling Tk's exit from a forked child causes the parent to also exit, while calling CORE::exit causes only that forked child to exit.

      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;}); } #####################################

        All UI activity in Tk occurs in the Tk event loop. You are exiting the program from an event handler before returning control to the event loop. Try replacing that handler with sub {&checkDays; $mw->after(5000, sub {exit(0)});} and see Tk::after for more details.

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.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://11114965]
Approved by GrandFather
Front-paged by kcott
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others wandering the Monastery: (3)
As of 2024-04-25 12:39 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found