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

In the Tk program I've been working on, I have a few different subroutines bound to different keyboard events. I use $widget->Busy() inside of these routines to turn the cursor into an hour glass, as these events may occasionally take a couple of seconds to run without any noticeable changes on the screen.

So my issue is I noticed that when I typed several different event bound key sequences in rapid succession, the events occurred in exactly the reverse order, every time. Though they worked perfectly fine when I keyed in the bound sequences at a slower pace.

What seems to be happening is that when the code gets to the $widget->Busy statement, it pauses until the event queue is clear. So if I type fast enough to generate these events quickly enough, each event's code pauses itself once it gets to the Busy statement until the event after it has completed. capiche? See, the last event typed is the first event to reach it's Busy statement without any other events after it already in the queue, so it actually completes first even though it was generated last.

Now I couldn't actually make this happen on my own pc running activestate perl, I assume because the events were received and executed faster than I could type them. But on a Unix server across the internets I kept having that problem with my code and I wasn't even typing all that fast.

Specifically I was working on some undo/redo routines. And if I type control-z (undo) followed rapidly by control-y (redo) or vice versa they would execute in backwards order. Probably nobody will ever type Undo and Redo as deliberately quickly as I did, and if they do they probably will hardly notice, but still this seems like a big problem to me.

Is anybody else familiar with this situation? I am aware that the cursor can be changed to 'watch' without using Busy. I think that's probably what I will do. But I thought this situation was amusing. I thought it seemed quite intuitive to use the Busy method in my code, but in reality, and due to a feature of Busy that I did not see documented in the documentation for Busy, it turned out to be all wrong.

Unless I'm misunderstanding something and someone else has some insight?

  • Comment on Tk::Widget's Busy method can result in LIFO event execution order?

Replies are listed 'Best First'.
Re: Tk::Widget's Busy method can result in LIFO event execution order?
by Anonymous Monk on Nov 17, 2007 at 11:44 UTC
    How might we test that?

      while working on some example code and trying various things I found that I had been doing something wrong.

      I was working with a TableMatrix widget created with the $mw->Scrolled() method. And I was trying to call Busy on the object returned by Scrolled. But Scrolled('TableMatrix') doesn't actually return a TableMatrix, it returns a Frame that happens to contain a TableMatrix. Duh! You can imagine how stupid I felt when I realized this.

      So I was making the Frame Busy, and this does indeed ignore any future or already pending events generated by the Frame object or anything outside of it. But it doesn't ignore events generated by objects that are contained inside the Frame object. Not only does it not ignore them, but the current process must wait for these events from the child object to finish before it can proceed. Hence the LIFO behavior I was experiencing.

      Here's some example code that exhibits what I was talking about. Pressing the t key prints a global count. The count is incremented with t event, so FIFO execution will result in a numerically increasing output sequence. But if you press t rapidly enough then you should see a numerically decreasing sequence, evidence of LIFO execution order from the point of the Busy statement.

      note: you might want to adjust the loop count depending on the speed of your system to get the ideal delay

      #!/usr/bin/perl -w use warnings; use strict; use Tk; use Tk::TableMatrix; my $mw = MainWindow->new; my $data = {}; my $t = $mw->Scrolled('TableMatrix', -variable => $data)->pack; $t->focus; $mw->bind('Tk::TableMatrix', '<t>', \&test_busy); #global counter our $i; sub test_busy { #give time to generate multiple events via keyboard # before Busy statement is reached. for my $k (0..2000000) { #whatever my $l = $k; } #store current value of counter #right before entering Busy statement my $j = ++$i; $t->Busy(); print "$j\n"; $t->Unbusy; } MainLoop;

      What I should be doing is either calling Busy with the recurse option set to 1, or better yet using a reference to the actual TableMatrix widget to call Busy. Or proabably even better yet, both.

      #!/usr/bin/perl -w use warnings; use strict; use Tk; use Tk::TableMatrix; my $mw = MainWindow->new; my $data = {}; my $t = $mw->Scrolled('TableMatrix', -variable => $data)->pack; $t->focus; $mw->bind('Tk::TableMatrix', '<t>', \&test_busy); #global counter our $i; sub test_busy { my $w = shift; #give time to generate multiple events via keyboard # before Busy statement is reached. for my $k (0..2000000) { #whatever my $l = $k; } #store current value of counter #right before entering Busy statement my $j = ++$i; $w->Busy(-recurse => 1); print "$j\n"; $w->Unbusy; } MainLoop;

      Though I still have one issue I think. If for example, I wanted the same subroutine that I have bound to keypress-t to be the -command value for a button or menu command that exists on the main window, how do I pass to it the object reference to the TableMatrix? Or, what would be the best way? I admit, I haven't spent much time on this last issue, it just occurred to me, so I might figure out the answer on my own with a little reading, but if anybody wants to go ahead and show me the way, I wouldn't mind.