Why are waitVariable calls not independent, even if another ref variable is used?

I am new to Tk and I would like to know if the issue in question is normal Tk behavior or not.

In short: I have a Perl / Tk (Tk version 804.028) script that uses two Tk :: ExecuteCommand (v1.6) widgets. Such objects have the execute_command method, which uses a specific fileevent callback to read the stdout executable and returns after it completes. It is solved using waitVariable. But it seems that if two ExecuteCommand start together, they return only when it returns more slowly. I can expect faster to return right after completion.

I did a small Perl / Tk script test to demonstrate the problem:

#!/usr/bin/perl use strict; use warnings; use Tk; use Tk::ROText; my $MAIN = new MainWindow -title => "TEST"; my $text = $MAIN->Scrolled('ROText')->pack(qw/-expand 1 -fill both/); sub pr { # Write into ROText widget $text->insert('end', join '', @_); $text->yview('end'); } pr "Tk version ", Tk->VERSION, "\n"; my @v = (100, 200); sub doo { # Button callback my ($rv, $txt) = @_; pr "B4 wait: $txt, ref=$rv, val=", $$rv, "\n"; $MAIN->waitVariable($rv); pr "Aft wait: $txt, ref=$rv, val=", $$rv, "\n"; } $MAIN->Button(-text => 'Do 0', -command => [\&doo, \$v[0], "Do 0" ] )->pack(qw/-expand 1 -side left -fill both/); $MAIN->Button(-text => 'Stop 0', -command => [sub {++$v[0]; pr "Stop 0\n";} ] )->pack(qw/-expand 1 -side left -fill both/); $MAIN->Button(-text => 'Do 1', -command => [\&doo, \$v[1], "Do 1" ] )->pack(qw/-expand 1 -side left -fill both/); $MAIN->Button(-text => 'Stop 1', -command => [sub {++$v[1]; pr "Stop 1\n";} ] )->pack(qw/-expand 1 -side left -fill both/); MainLoop(); 

This draws a ROText widget and 4 buttons ([Do 0] [Stop 0] [Do 1] [Stop 1]) (see attached image). By pressing the "To" button, it calls the doo function, which waits until the assigned scalar is changed. Variables change when the Stop button is pressed.

If the buttons are pressed in the order of [Do 0] [Stop 0] [Do 1] [Stop 1], the output appears normal (see lines 2-7). But if the "tasks" started in parallel, both callbacks end only if both of them are stopped. Therefore, pressing the buttons in [Do 0] [Do 1] [Stop 0] [Stop 1] (see Lines 8-13) gives a strange result (see Figure).

enter image description here

My expectation of the second test was that the first callback function returns immediately after pressing the first Stop button. Therefore, I think the conclusion should be:

 B4 wait: Do 0, ref=SCALAR(0x9970560), val=101 B4 wait: Do 1, ref=SCALAR(0x9970bfc), val=201 Stop 0 Aft wait: Do 0, ref=SCALAR(0x9970560), val=102 Stop 1 Aft wait: Do 1, ref=SCALAR(0x9970bfc), val=202 

It runs on a Linux machine.

Am I missing something? Thanks in advance!

UPDATE

To get around this waitVariable problem, I rewrote this widget to use callbacks instead (thanks Tantalus!). Now execute_command returns immediately. There are two callbacks: one for Cancel, one for Done. The caller is now informed through these callbacks. Anyway, I read somewhere (I canโ€™t find the source now) that waiting a long time in the callback is not a good idea in Tk. The new solution matches this.

Thank you for your help!

+5
source share
2 answers

$ widget-> waitVariable (\ $ name)

$ widget-> waitVisibility

$ widget-> waitWindow

Tk wait methods wait for one of several things to happen, and it returns without any other action. The return value is always an empty string. waitVariable expects a reference to the perl variable, and the command expects this variable to change. This form is typically used to wait for the user to complete an interaction with a dialog that sets the variable as part of the (possibly final) part of the interaction. waitVisibility expects a change in the visibility state of $ widget (as evidenced by the arrival of the VisibilityNotify event). This form is usually used to wait for a newly created window to appear on the screen before taking some action. waitWindow expects $ widget to be destroyed. This form is usually used to wait for the user to complete the interaction with the dialog box before using the result of this interaction. Note that creating and destroying a window each time a dialog is required makes the code modular, but imposes overhead that can be avoided by removing the window and using waitVisibility.

While tk wait methods are waiting, they handle events in the usual way, so the application will continue to respond to user interactions. If the event handler calls tkwait again, the nested tkwait call must end before the external call ends.

Emphasis is mine.

+3
source

You do not have to wait in an event loop. What happens is that you create a column that includes the previous wait loop. For example, if you added use Carp; above and then change your pr function as follows:

 sub pr { # Write into ROText widget $text->insert('end', Carp::longmess(join '', @_)); $text->yview('end'); } 

then you will see the waitVariable showing - we cannot return there until we return to this cycle and you finish the cycles inside the cycles.

To do what you want to do without inverting everything in events, you can try Coro , which can invert events like this.

Also, in modern perls, qw does not mean parentheses, so your pack calls need parentheses around qw/.../ lists.

+2
source

Source: https://habr.com/ru/post/1262381/


All Articles