http://www.perlmonks.org?node_id=338959

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

Hi Fellow Monks,

This is more of a Perl::Tk theory question. In normal procedural-flow programming one event leads on from the next, the output it processed onto the screen or console - its pretty standard. Tk is event driven and I'm coming to terms with its nature and how to program it. In doing so one develops a different way of having to think about structuring a program. If you click a button, X-event happens, screen changes, new widget developed, a small action taken, next button pushed and so on and so on ...

But my theory questions begins here ... what is the best way to program a long procedural like flow in Tk???


Do you:
1. Do a foreach loop and create and destroy a MainWindow for each directory. (Doesn't sound very gui pretty). In essence this is saying - to create a procedural like flow do you create and destroy the MainWindow to create the appearance of a procedural flow?
2. Can you get Tk to wait until one "fileevent" is done? Not sure how this would be achieved.
3. Do you string each event together via subroutines, creating a subroutine stack. That is, when one subroutine finishes it then calls the next tar, in effect creating a procedural-like flow from one subroutine to the next (replicating the procedural like flow of normal programming but giving the gui its fluid setp by step appearance?

For example I have a program that does a massive "tar" archiving. This requires calling the tar command using "fileevent" and then piping the output from all this to a text widget. "Fileevent" is used so the gui doesn't give the appearance of freezing. But my problem is I want to do consecutive tar's on different directories!!

In normal procedural-flow programming you'd do a "foreach" loop for each directory you want to tar. When one tar finishes it moves onto the next one. In Tk using "fileevent" this isn't possible because it doesn't suspend the background process, thereby keeping the gui "up-to-date". The event queuing is stuffed. Being event driven the "tar"'s get called rapidly in succession within the MainWindow and don't work.

How do I resolve this? I can give code but I think my explanation is enough for anyone who knows Tk.

Dean
The Funkster of Mirth
Programming these days takes more than a lone avenger with a compiler. - sam
RFC1149: A Standard for the Transmission of IP Datagrams on Avian Carriers

Replies are listed 'Best First'.
Re: Perl::Tk - Event sequencing question
by Corion (Patriarch) on Mar 23, 2004 at 12:15 UTC

    I see two possible ways:

    Create your processing chain as follows:

    1. Produce list of all items to be processed
    2. Fire an event PROCESS_ONE_ITEM to yourself
    3. On PROCESS_ONE_ITEM do:
      1. Process that one item.
      2. Fire an event ITEM_PROCESSED to yourself
    4. On ITEM_PROCESSED do:
      1. update your status
      2. Fire an event PROCESS_ONE_ITEM to yourself, if there are more items in the queue

    That way, you will process all items as long as there are items in the queue. The ugly part of this (and all event oriented processing) is, that all program flow logic gets moved to the events instead of the sequential ordering in the source code.

    The other way is to simply spawn a "worker" Perl script that produces the "right" output by procedurally processing the list of items and interfacing to that single process from Tk via the one single Fileevent. This way has the advantage of having the program flow in a nice procedural way, and the disadvantage that you can't display nice progress unless you parse the output of your other program.

Re: Perl::Tk - Event sequencing question
by castaway (Parson) on Mar 23, 2004 at 12:28 UTC
    Its difficult to answer this one in a generic way. If you're using an event driven interface, you simply don't do that sort of thing, since the point is to respond to input from the user. What would you do, if the user decided all the tarring was taking too long, and wants to hit cancel somewhere? If you're just looping around a list of things to tar, you can't stop it.

    The answer also depends upon how this tarring of things gets started. Assuming you have a file-explorer type dialog, in which the user can chose a bunch of directories to tar up, and then hit a 'Go' button, I would do something like: Start a bunch of subprocesses asynchronuously, keeping track of their process IDs, and calling a fileevent with a callback on each. (The callback can be the same function, with parameters process ID and filename, or similar). The callback updates a dialog box showing a list of directories the user wanted to tar, showing which are finished.

    If the user hits cancel, you can then kill any processes that arent finished yet, and delete any parts of files they may have created.

    In effect, you then are doing a foreach loop, but not waiting for the results, just starting a process and ignoring it until it reports ready.

    An alternative would be to go through the list of picked directory names, calling tar, waiting until finished, updating the dialog box (a checkbox or something), to say so, then going on to the next. Im not sure how a 'Cancel' would interrupt this one though, it would probably have to check another variable to see if the cancel button has been hit.

    Its certainly nicer to keep a dialog window up, and update it with a status, than flash up new windows for each process, since in the end there will be a whole bunch of them, or if you wait for the user to click them away, you're wasting time, and they might not, in which case the process will wait forever.

    Code would be useful, to see where you are trying to integrate this. And if my assumptions are anywhere near what you need.

    C.

Re: Perl::Tk - Event sequencing question
by periapt (Hermit) on Mar 23, 2004 at 13:57 UTC
    Typically for a long procedural sequence in Tk, I farm the procedural step out to a subroutine, usually with a go button. The subroutine can then loop through all the steps I want done without the need to return to the GUI event model. The GUI can be updated periodically from the subroutine (pass mainwindow in to the subroutine as a parameter) in a couple of different ways.

    If shelling out to command line, I often just capture the output with qx or backticks and post that capture back to the mainwindow. This has the drawback that the GUI will not be updated until the command completes. This hasn't been a problem for me since most of my GUI apps also involve counters of some kind. Updating those prevents the user from wondering if the screen has frozen.

    If you want to pipe the output of tar to a textbox, you could map STDIN to the text box. I haven't done this yet, just read about it (Mastering Perl/Tk I think) but I keep looking for a chance to use it.

        Yeah, I've had that problem with long running processes too. I once tried to implement an archive process one file at a time with a counter updating the main screen but it was an ugly, inefficient thing. I went with a command line instead.

        Thanks for the link. It answered several questions I've had kicking around in the back of my mind. Maybe I'll be more motivated to try a solution sooner rather than later.

        PJ
Re: Perl::Tk - Event sequencing question
by eserte (Deacon) on Mar 23, 2004 at 15:18 UTC
    Maybe you can create a "wizard"-like interface --- there's even a CPAN module Tk::Wizard which might be suitable for your task.

    If all you want is blocking an execution thread, then you want to look at $mw->waitVariable.