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

'lo all...

I've only done a little programming with Perl/Tk, although I've been programming with perl for quite a while. Hence, I'm having a little trouble with the 'event-driven' thing.

The attached code does what it does... but instead of clicking the "Pop It!" button to bring up the popup window, I actually want the sequence of things to be more like:

- setup the main window

- click the "Pop It!" button and call 'AddItem'

- once in AddItem, I want the popup to be displayed, and to stay there whilst I do things in 'AddItem'. Once 'AddItem' is more-or-less done, the popup is removed (like when the button is pressed) and we're back in the main window.

The popup is like a 'status' window... but I'm not sure how to actually -do it-... It's simple enough to do things with a button action but this is where I get confused with the 'event-driven' thing.

I'd appreciate any pointers...

Thanks.

John

use strict; use Tk; my $mw = MainWindow->new; # Main window my $top = $mw->Frame( -background => '#239867', -borderwidth => 2, )->pack(-fill => 'both'); my $lbltxt = $top->Label( -text => 'Just some text', -background => 'white', )->pack(-side => 'right', -padx => 10); my $GoBtn = $top->Button( -text => ' Pop It! ', -command => \&AddItem )->pack(-side => 'left', -padx => 10, -pady => 5); my $DoneBtn = $top->Button( -text => ' Exit ', -command => \&Cleanup )->pack(-side => 'right', -padx => 10, -pady => 5); $mw->focus; # Ensure we're talking to the main window MainLoop; # Run the program, watching for events # ---- sub Cleanup { $mw->destroy; } # ---- sub AddItem { my $tp = $mw->Toplevel( -title => ' AddItem' ); # ->pack(-fill => 'both'); my $tpfrm = $tp->Frame( -background => '#22cc00', )->pack(-side => 'top', -fill => 'x'); my $tplbl = $tpfrm->Label( -text => 'Working...', -background => 'white', -foreground => 'black' )->pack(-side => 'top', -padx => 10); my $tpbtn = $tpfrm->Button( -text => 'Close', -command => [$tp => 'destroy'], )->pack(-side => 'top', -padx => 10, -pady => 5); }

Replies are listed 'Best First'.
Re: How to PopUp a 'Status' window in Perl/Tk
by pg (Canon) on Aug 27, 2005 at 05:36 UTC

    You said that it was sort of a status window, then try progressbar:

    use strict; use Tk; use Tk::ProgressBar; my $mw = MainWindow->new; my $top = $mw->Frame()->pack(); my $GoBtn = $top->Button(-text => 'Pop It!', -command => \&AddItem)->p +ack(); my $DoneBtn = $top->Button(-text => ' Exit ', -command => \&Cleanup)-> +pack(); my $progress = $top->ProgressBar( -width => 20, -length => 100, -anchor => 'w', -from => 0, -to => 9, -blocks => 1, -colors => [0, 'green'] )->pack(); $mw->focus; MainLoop; sub Cleanup { $mw->destroy; } sub AddItem { $GoBtn->configure(-state => "disabled"); for (0 .. 9) { sleep 1; $progress->value($_); $progress->update(); } $GoBtn->configure(-state => "normal"); $progress->value(0); $progress->update(); }
      Thanks for the suggestion... but the progress bar approach will only work if repeated actions are done within the sub{}; in my case, I only have one action (spawn) and I have to wait for it to finish, THEN I can continue on, so the progress bar isn't really suitable for what I need.

      Thanks again, tho..

      John

Re: How to PopUp a 'Status' window in Perl/Tk
by spiritway (Vicar) on Aug 27, 2005 at 03:14 UTC

    I'm not clear what it is you're having a problem with. Your code looks OK, as far as it goes. What, exactly, is the problem? Or, what is it you want your code to do, that you don't seem to be able to make it do?

    Event-driven code isn't much different from any other kind, except that you don't call it directly. You more or less put it into a queue to await its being called.

      Let me rephrase the OP's question. I believe the OP wants to remove the cancel button from the AddItem sub so that the popup window it is creating has no way to exit from the user (well, the window close button that the window manager puts in automatically may be there, but we can work on that later). This window will be visible for the duration while the perl program continues to do stuff in the background to prepare whatever, e.g., sleep 3.

      And then, once this setup is done, the OP wishes that this extra status window disappears as if the user had pressed the (now-removed) cancel button.

      In other languages, my first thought is to create a background thread to do the real work in, leaving the main loop on the main thread free to handle events. However, I've not done GUI programming in ages - last time I did any GUI programming on Unix was when I was learning Java, and, before that, all GUI programming had been Win16, OS/2, and Win32 - in C and Delphi Pascal. Java handles threads in a relatively transparent method, with sensible sharing, IMO. Perl's threads ... well, I'd love to hear the answer to the OP to see how I can take advantage of it in other stuff I'd like to do ;-)

      Ok, I'll try a bit more detail...

      I have a command line program that I run under WinXP and ActivePerl... but my work colleagues are... 'loathe'... to use non-Window applications. Hence, I'm trying to build a very simple GUI around it.

      So my GUI (that is, Perl/Tk) application displays a window that captures user input in fields, etc. The user clicks a 'Do it!' button and that button calls a sub{}.

      In the sub, I create a command line from the user input. I then do a `$cmd`, system($cmd) or a Win32::Process::Create(...$cmd...) to run my command line program and when it's finished and control returns to my Perl/Tk app, I then further process the file created by my command line app.

      When the 'spawn' (or 'fork') runs, it takes a little too long to do its thing to just have nothing being displayed in the window I have up. So, I thought I'd pop-up a window saying "Working" whilst the 'spawn' is running and then get rid of the popup when the 'spawn' terminates (the people this is for won't read status lines on a window - they don't 'see' such things - but popup windows are more obvious for them and they generally notice popups).

      Does this make my intent any clearer?

      Ta

      John

Re: How to PopUp a 'Status' window in Perl/Tk
by zentara (Cardinal) on Aug 27, 2005 at 10:34 UTC
    There are alot of ways to do what you want. Basically you need to test for some condiition(i.e. a finished flag), then close the window. Usually you use a timer to do this, but there are other ways. Also, if you call "PoP-It" more than once, you will be gaining memory-size with each call, since you destroy (then recreate) $tp. Only create $tp once, and call withdraw and deiconify on it to reuse the widget.
    sub AddItem { my $tp = $mw->Toplevel( -title => ' AddItem' ); # ->pack(-fill => 'both'); my $tpfrm = $tp->Frame( -background => '#22cc00', )->pack(-side => 'top', -fill => 'x'); my $tplbl = $tpfrm->Label( -text => 'Working...', -background => 'white', -foreground => 'black' )->pack(-side => 'top', -padx => 10); # my $tpbtn = $tpfrm->Button( # -text => 'Close', # -command => [$tp => 'destroy'], # )->pack(-side => 'top', -padx => 10, -pady => 5); #start a timer to simulate some working being done my $count = 0; my $timer = $mw->repeat(1000,sub{ $count++; if($count == 5){ $tp->withdraw } }); }

    I'm not really a human, but I play one on earth. flash japh
      ?!?! I must not be expressing myself well.

      These examples are all good... but a basic point is being missed. In these examples, the AddItem sub{} is controlling when the 'Working' message window is to be destroyed. The real problem is that the... 'activity'... that's going on that needs to be monitored is running *outside* of the context of the current process. This means the 'mainline' Perl/Tk program doesn't know when the 'activity' is terminated.

      As I understand it, when MainLoop is called, everything is basically 'sitting still' until (in this case) a 'button press event' happens. When that event comes from the 'Pop It!' button, the AddItem sub{} is called, which creates a new window that shows a 'Working' message. Effectively at the same time, a new process is spawned... and everything in the current process goes back to 'sleep', until another event occurs.

      The 'full and complete' way to do what I want would be to use Win32::Process::Create(...$cmd...) and set-up something with signals... and when the spawned process terminates, if would issue a signal to its parent process -- that would be the 'event' that is trapped and the 'Working' message window would be destroyed.

      ...but I'm trying to shortcut all that work somehow (I don't even know if such a thing is possible, although I would expect it would be)... and I'm not sure how to do it. In 'normal' (non-event-driven programming), simply including 'system($cmd)' in the code would mean the main program would pause, the system() task would complete and control would be returned to the main program again... but this doesn't happen here.

      Perhaps the simplest way is to forget about spawning another process and just include the code from the command -line version I have into the Tk version -- Ugh. More maintenance dramas...

      John

        This means the 'mainline' Perl/Tk program doesn't know when the 'activity' is terminated.

        You have a basic design flaw, and you probably should consider a different approach. Even if the process is ouside the AddItem sub, you will have to somehow detect that the thing has ended and set a flag, or something.

        I'm not using Windows, and the Win32::ProcessCreate idea is what you are after. Also IPC::Run is supposed to work on windows, which would allow you to run the process thru IPC, then monitor it's STDOUT, and wait to detect something which signals the "end condition". Then you can set a flag.

        Perhaps the simplest way is to forget about spawning another process and just include the code from the command -line version I have into the Tk version -- Ugh. More maintenance dramas...

        You can also run the process through threads, and use threads:shared to signal back to the main thread that the thread has finished running the code.


        I'm not really a human, but I play one on earth. flash japh
      Well, given the practicalities involved with this project, I've made an executive decision(!)...

      I'll use the idea that has been suggested of updating a progress bar on a pop-up window... and just get it to run a 'resonable time' (which I'll determine by a few test runs), after which I'll check if the file I'm expecting to see has been created. If it's not there or some other error occurs, I'll deal with that AFTER the 'Working' pop-up has gone.

      As this program I'm building is just a 'make-do' thing until some other people fix-up the problems we've reported in their software, what I've described will probably be sufficient; it doesn't have to be 'perfect'(!)... and it certainly doesn't justify all the development/debugging required for IPC/process communications.

      Many thanks for all your assistance, folks... I appreciate it a lot.

      BTW... The final sample code I'll further massage follows:

      use strict; use Tk; use Tk::ProgressBar; # WHY is this required, given the prior line? # If it's not included I get warnings!? my $MAXTIME = 10; my $mw = MainWindow->new; # Main window my $top = $mw->Frame( -background => '#239867', -borderwidth => 2, )->pack(-fill => 'both'); my $lbltxt = $top->Label( -text => 'Just some text', -background => 'white', )->pack(-side => 'right', -padx => 10); my $GoBtn = $top->Button( -text => ' Pop It! ', -command => \&AddItemNew )->pack(-side => 'left', -padx => 10, -pady => 5); my $DoneBtn = $top->Button( -text => ' Exit ', -command => \&Cleanup )->pack(-side => 'right', -padx => 10, -pady => 5); $mw->focus; # Ensure we're talking to the main window MainLoop; # Run the program, watching for events # ---- sub Cleanup { $mw->destroy; } # ---- sub AddItem { my $tp = $mw->Toplevel( -title => ' AddItem' ); # ->pack(-fill => 'both'); my $tpfrm = $tp->Frame( -background => '#22cc00', )->pack(-side => 'top', -fill => 'x'); my $tplbl = $tpfrm->Label( -text => 'Working...', -background => 'white', -foreground => 'black' )->pack(-side => 'top', -padx => 10); my $tpbtn = $tpfrm->Button( -text => 'Close', -command => [$tp => 'destroy'], )->pack(-side => 'top', -padx => 10, -pady => 5); } # ---- sub AddItemNew { my $tp = $mw->Toplevel( -title => ' AddItem' ); # ->pack(-fill => 'both'); my $tpfrm = $tp->Frame( -background => '#22cc00', )->pack(-side => 'top', -fill => 'x'); my $tplbl = $tpfrm->Label( -text => 'Working...', -background => 'white', -foreground => 'black' )->pack(-side => 'top', -padx => 10); $GoBtn->configure(-state => "disabled"); my $progress = $tpfrm->ProgressBar( -width => 20, -length => 100, -anchor => 'w', -state => 'disabled', -value => 0, -from => 0, -to => $MAXTIME-1, -blocks => 1, -colors => [0, 'blue'] )->pack(); for (0..$MAXTIME-1) { $progress->value($_); $progress->update(); sleep 1; } $tp->withdraw; $GoBtn->configure(-state => "normal"); }
        Sometimes you just have to be practical. :-)

        I'm not really a human, but I play one on earth. flash japh