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

Hi monks, I am here seeking for your wisdom. I am trying to develop a interface in perl-Tk of some old scripts I wrote before. One of the features I would like to have is to present in a text box, the log information of the called script while it is been executed. The problem I have is that when I call the script, the interface script waits until the called one finished to present its STDOUT, and I want this log to be showed while the script is been executed. To explain my self better, I wrote a simple test case composed by two files: test.pl and test3.pl. The first one have a textbox and a button, when the button is pressed, the second script is called and the log information is presented in the textbox. The second just grabs the STDIN and prints it back until the STDIN have a “aaa”chain.

test.pl

#!/usr/bin/perl -w use Tk; $main = MainWindow -> new(); $text_box = $main -> Scrolled ("Text",-spacing2=> 1, -spacing3 => 1, -scrollbars => "e", -height => 20, -background => "white", -relief => "ridge") +->pack(); $start = $main->Button()->pack(); $start-> configure (-text => "Start", -command => [\&count]); MainLoop(); sub count { open(README, "test3.pl |") or die "Can't run program: $!\n"; while(<README>) { $text_box -> insert ("end", $_); } close(README); }

test3.pl

#!/usr/bin/perl -w print “Write something: \n”; while (<>) { print; if ($_ =~ /aaa/){ exit; } }
Them the problem is that test.pl has to wait until test3.pl is finished, to show the log in the textbox. What I want is that the STDOUT of test3.pl is showed in the textbox while it is been executed. Thanks in advance for your help, and excuse me if it is silly question.

Replies are listed 'Best First'.
Re: Processing STDOUT of a called perl script during execution
by moritz (Cardinal) on Mar 10, 2008 at 14:47 UTC
Re: Processing STDOUT of a called perl script during execution
by almut (Canon) on Mar 10, 2008 at 15:16 UTC

    In addition to what moritz said, you also need to explicitly call $main->update(); after inserting the text in the text box within your while loop.

      update: Scratch that... ;-) Except that $main->update() just queues another Tk event, which will be handled after exiting the sub...

        If so, why does it update then (before exiting the while loop and the handler) if you $main->update, and not if you don't?

Re: Processing STDOUT of a called perl script during execution
by shmem (Chancellor) on Mar 10, 2008 at 16:12 UTC
    While your main program is executing sub count the Tk MainLoop doesn't get control, so it can't update the text box. With the $text_box->insert() calls you are enqueuing events which don't get handled until after the sub is finished, and the main loop has control again.

    Two solutions - 1.) use Tk::fileevent, 2.) call the Tk event loop yourself.

    Ad 1:

    $start-> configure (-text => "Start", -command => [\&count]); MainLoop(); { my $handle; sub count { if ($handle) { # get rid of previous file-event amd -handle $handle->DESTROY; undef *README; } open(README, "./test3.pl |") or die "Can't run program: $!\n"; $handle = $text_box->fileevent( \*README, 'readable', sub { my $line = <README>; if($line =~ /aaa/) { close README; wait; # reap child } $text_box -> insert ("end", $line); } ); } }

    Ad 2: for calling the event loop yourself, see Re: Do I need threading for my GUI?

    Note that for both solutions, you have to turn autoflush on in your external script, since output will be block buffered if you don't do so:

    #!/usr/bin/perl -w $| = 1; # make line buffered print "Write something: \n"; while (<>) { print; if ($_ =~ /aaa/){ exit; } }

    --shmem

    _($_=" "x(1<<5)."?\n".q·/)Oo.  G°\        /
                                  /\_¯/(q    /
    ----------------------------  \__(m.====·.(_("always off the crowd"))."·
    ");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}
Re: Processing STDOUT of a called perl script during execution
by halfcountplus (Hermit) on Mar 10, 2008 at 15:18 UTC
    Was test3 suppose to actually run in the terminal?

    I do not think what you wrote will work this way (it didn't for me).

    However, if we change test3.pl to produce something simple with a delay (sleep) like this:
    #!/usr/bin/perl -w # the new test3.pl use strict; $|=1; #NECESSARY print "Write something: \n"; my $it=1; until ($it == 5) { print "line $it...\n"; sleep 1; $it++ }
    Then your problem becomes comprehensible: run alone, this test3 produces five lines of text 1 second apart, but run from test.pl (the Tk) you wait five seconds and then see the five lines.

    In addition to adding "$|=1;" to test3.pl, you need to add this Tk command to test.pl, in the loop after the text insert:
    $text_box -> insert ("end", $_); $main->update;
    Now you can watch the five lines appear one at a time, the same way they would if they were logging a time-consuming process.
Re: Processing STDOUT of a called perl script during execution
by zentara (Cardinal) on Mar 10, 2008 at 16:29 UTC
    You need to use fileevent with Tk to read filehandles. (On win32, fileevent only works on sockets). There are many other complexities that can set in, but this is the basic.
    #!/usr/bin/perl use warnings; use strict; use Tk; $|=1; open( T,"top -d 1 -b & |"); my $mw = new MainWindow; my $t = $mw->Scrolled("Text",-width => 80, -height => 25, -wrap => 'no +ne'); $t->pack(-expand => 1); $mw->fileevent(\*T, 'readable', [\&fill_text_widget,$t]); MainLoop; sub fill_text_widget { my($widget) = @_; my $text = <T>; $widget->insert('end',$text); $widget->yview('end'); }

    I'm not really a human, but I play one on earth. Cogito ergo sum a bum
Re: Processing STDOUT of a called perl script during execution
by jcorso (Initiate) on Mar 10, 2008 at 18:18 UTC
    Thanks a lot to all for your help. I get to solve my problem following your advices. Putting the $main-> update and $|=1. I will also study the fileevent and filehandle.