Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl: the Markov chain saw

POE/Tk/Fileevent Strangeness

by cmv (Chaplain)
on Jun 26, 2009 at 21:52 UTC ( #775181=perlquestion: print w/replies, xml ) Need Help??
cmv has asked for the wisdom of the Perl Monks concerning the following question:


I'm trying to write a GUI with Tk and POE, that simply reads in and parses text from a file, then puts up some GUI elements to represent the data. I want the data to be read in a line at a time, and for the data input, and GUI output to be done in real time (reason for POE). The GUI elements should always be active (ie the scroll bar should always scroll).

The program below shows my first attempt at a simple read-it-in-and-print-it-out scenario. This program seems to act strangely.

As you can see, I chose to use a single POE session (this may be the root of my problem), and use Tk's fileevent to handle the input. I assumed that POE would take care of sharing between reading input and doing GUI stuff (I suspect I'm wrong here for some reason).

When I run this, the Text window comes up blank, then I see the entire contents of the input file on STDOUT, then the Text window is updated.

Trying to scroll the Text window is very slow once all the text is there.

Whatever happened to the sleep?

I also don't understand why I should have to put "update" lines in there.

If I comment out the "update" lines, then the sleep get's hit, but the text window never shows up.

Any help or pointers is very much appreciated!



use English; use strict; use warnings; use Data::Dumper; use Tk; use POE; my $TXT; # Create file descriptor from input file name... my $infile = shift || die "Missing input file"; open(IFILE, "<$infile") || die "Cannot open $infile: $!"; my $IN = *IFILE{IO}; my $session = POE::Session->create( inline_states=>{ _start=>sub { $TXT = $poe_main_window->Scrolled('Text')->pack; $poe_main_window->update; $poe_main_window->fileevent ( $IN , 'readable', sub { _GetInput($IN) } ); }, }, ); # Create a dummy postback so the session won't die... my $subref = $session->postback('DontDie'); $poe_kernel->run(); sub _GetInput { my $ifile = shift || die "Missing File Descriptor"; if(eof($ifile)) { $poe_main_window->fileevent($ifile, 'readable', ''); return; } my $line = <$ifile> || die "Cannot read $!"; print STDERR $line; $TXT->insert('end', $line); $TXT->update; sleep 1; }

Replies are listed 'Best First'.
Re: POE/Tk/Fileevent Strangeness
by rcaputo (Chaplain) on Jun 26, 2009 at 22:20 UTC

    A single session should work. Per your question on POE's mailing list, you can run POE::Wheel::FollowTail instances in the same session that updates your GUI.

    If Tk fileevents are higher priority than GUI updates, then the code to read the file will pre-empt the GUI updates. Disk files always report ready-to-read, which is why file tailing often uses other things.

    POE::Wheel::FollowTail is better for this if you expect the file to grow later, or if you want to tail a named pipe.

    sleep() will prevent both Tk and POE from doing anything. Avoid it if possible. POE::Kernel provides a delay() method that can be used instead and won't stop the whole program.

    POE::Kernel also provides a select_read() method that works a bit like Tk's fileevent, except that it's portable. If you switch to Gtk later, select_read() should continue to work. Any code using Tk::Fileevent directly would need to be rewritten. Tk and Gtk are not your only options. See the CPAN for additional POE::Loop modules.

    No idea why Tk scrolling is slow. It could be a number of things.

    The update() call is necessary if fileevents are higher priority than GUI update events. update() tells Tk to refresh the display at times when it normally wouldn't. It's not the perfect solution, though. For example, it may not allow you to actually do anything until the fileevents stop firing.

    I'm going to dismiss your question on POE's mailing list because you've also asked it here. You should probably ping me in #poe (or on the mailing list) when you have more information or questions. While PerlMonks' RSS feed notifies about new articles, it's not so good at telling me when someone's replied to them. I don't camp out here refreshing the page, so I may not notice the continuation of this thread for days. If I'm doing PerlMonks wrong, and there's a better way to keep tabs on things, please let me know.

Re: POE/Tk/Fileevent Strangeness
by cmv (Chaplain) on Jun 29, 2009 at 15:59 UTC
    rcaputo++ Thanks for your help! It makes using POE fun...

    Everything you say makes sense to me, and I've redone my test program to use select_read() - see below. It works great on unix, but I get no text showing up either on STDOUT or in the text window under activestate (after re-commenting the use POE statements appropriately). Any pointers here?



    use English; use strict; use warnings; use Tk; use POE; #use POE(qw(Loop::TkActiveState)); # Create file descriptor from input file name... my $infile = shift || die "Missing input file"; open(IFILE, "<$infile") || die "Cannot open $infile: $!"; my $ifd = *IFILE{IO}; my $TXT; my $session = POE::Session->create( inline_states=>{ _start=>sub { $TXT = $poe_main_window->Scrolled('Text')->pack; $_[KERNEL]->select_read($ifd, '_GetInput'); }, _GetInput=>sub { if (my $line = <$ifd>) { print STDERR $line; $TXT->insert('end', "-$.- $line"); $poe_main_window->update; } else { $_[KERNEL]->select_read($ifd) }; }, }, ); # Create dummy postback so the session won't die once input is done... my $subref = $session->postback('DontDie'); # Go... $poe_kernel->run();

      You should find a different way to read from plain files, for at least two reasons.

      Multiplexing plain files is generally pointless because the operating system always reports them as "ready to read". That is, your select_read() event will fire immediately and constantly. You may as well do:

      _start=>sub { $TXT = $poe_main_window->Scrolled('Text')->pack; $_[KERNEL]->yield('_GetInput'); }, _GetInput=>sub { if (defined(my $line = <$ifd>)) { print STDERR $line; $TXT->insert('end', "-$.- $line"); $poe_main_window->update; $_[KERNEL]->yield('_GetInput'); } },

      Your example works on UNIX because that OS treats everything as a file. Sockets, plain files, pipes, terminals, etc. have their quirks, but deep down UNIX treats them equally.

      As you're discovering, Windows isn't as nice. Even Perl's select() is limited to only working with sockets. This is why you can't get input notification for your plain file handle. The yield() based example above should be portable everywhere.

      A third, bonus issue: You've been mixing unbuffered, multiplexed I/O with buffered reads. This is a classical problem that tends to end badly. For example, the file handle may report not-ready-to-read because the data you're looking for is in the input buffer. The yield() based example will avoid it.

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://775181]
Front-paged by Arunbear
[erix]: he has obfuscated it effectively
[erix]: I give up. Actually got some useful stuff to do :)

How do I use this? | Other CB clients
Other Users?
Others romping around the Monastery: (13)
As of 2018-06-22 14:44 GMT
Find Nodes?
    Voting Booth?
    Should cpanminus be part of the standard Perl release?

    Results (124 votes). Check out past polls.