为什么即使使用不同的变量 ref,waitVariable 调用也不是独立的?
Why are waitVariable calls not independent even if different variable ref is used?
我是 Tk 的新手,我想知道所讨论的问题是否属于正常的 Tk 行为。
简而言之:我有一个 Perl/Tk(Tk 版本 804.028)脚本,它使用两个 Tk::ExecuteCommand (v1.6) 小部件。这样的对象有一个 execute_command 方法,它使用定义的文件事件回调来读取执行命令的标准输出和 returns 完成后。它通过使用 waitVariable 来解决。但似乎如果两个 ExecuteCommand 一起启动它们 returns 只有当较慢 returns 时。我可能希望更快 returns 完成后立即。
我做了一个小测试Perl/Tk脚本来演示问题:
#!/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();
这会绘制一个 ROText 小部件和 4 个按钮 ([Do 0][Stop 0][Do 1][Stop 1])(见附图)。单击“执行”按钮,调用函数 doo
,该函数会等待分配的标量发生变化。当按下停止按钮时,变量会发生变化。
如果按下 [Do 0][Stop 0][Do 1][Stop 1] 命令中的按钮,输出似乎没问题(参见第 2-7 行)。但是,如果 "tasks" 并行启动,则只有当两者都停止时,这两个回调才会完成。所以按下 [Do 0][Do 1][Stop 0][Stop 1] 中的按钮(见第 8-13 行)给出了一个奇怪的结果(见图)。
我对第二次测试的期望是第一个回调函数 returns 在按下第一个停止按钮后立即出现。所以我认为输出应该是:
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
它在 Linux 机器上运行。
我错过了什么吗?提前致谢!
更新
为了绕过这个 waitVariable 问题,我重写了这个小部件以改为使用回调(感谢 Tantalus!)。现在 execute_command returns 马上。回调有两种,一种是Cancel,一种是Finish。现在通过这些回调通知调用者。无论如何,我在某处读到(我现在找不到来源),长时间等待回调在 Tk 中不是一个好主意。新的解决方案符合这一点。
感谢您的帮助!
您不应该在事件循环中等待。正在发生的事情是您正在生成一个调用堆栈,该调用堆栈涉及之前的等待循环。例如,如果您在顶部添加 use Carp;
,然后像这样更改 pr
函数:
sub pr { # Write into ROText widget
$text->insert('end', Carp::longmess(join '', @_)); $text->yview('end');
}
然后你会看到 waitVariable
出现 - 在我们回到那个循环之前我们无法回到那里,你最终会在循环中循环。
要在不将所有内容反转为事件的情况下做您想做的事,您可能想尝试 Coro 可以像这样反转事件。
此外,在现代 perls 中,qw
并不意味着括号,因此您的 pack
调用需要在 qw/.../
列表两边加上括号。
$widget->waitVariable($name)
$widget->waitVisibility
$widget->waitWindow
The tk wait methods wait for one of several things to happen, then it returns without taking any other actions. The return value is always an empty string. waitVariable expects a reference to a perl variable and the command waits for that variable to be modified. This form is typically used to wait for a user to finish interacting with a dialog which sets the variable as part (possibly final) part of the interaction. waitVisibility waits for a change in $widget's visibility state (as indicated by the arrival of a VisibilityNotify event). This form is typically used to wait for a newly-created window to appear on the screen before taking some action. waitWindow waits for $widget to be destroyed. This form is typically used to wait for a user to finish interacting with a dialog box before using the result of that interaction. Note that creating and destroying the window each time a dialog is required makes code modular but imposes overhead which can be avoided by withdrawing the window instead and using waitVisibility.
While the tk wait methods are waiting they processes events in the normal fashion, so the application will continue to respond to user interactions. If an event handler invokes tkwait again, the nested call to tkwait must complete before the outer call can complete.
强调我的。
我是 Tk 的新手,我想知道所讨论的问题是否属于正常的 Tk 行为。
简而言之:我有一个 Perl/Tk(Tk 版本 804.028)脚本,它使用两个 Tk::ExecuteCommand (v1.6) 小部件。这样的对象有一个 execute_command 方法,它使用定义的文件事件回调来读取执行命令的标准输出和 returns 完成后。它通过使用 waitVariable 来解决。但似乎如果两个 ExecuteCommand 一起启动它们 returns 只有当较慢 returns 时。我可能希望更快 returns 完成后立即。
我做了一个小测试Perl/Tk脚本来演示问题:
#!/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();
这会绘制一个 ROText 小部件和 4 个按钮 ([Do 0][Stop 0][Do 1][Stop 1])(见附图)。单击“执行”按钮,调用函数 doo
,该函数会等待分配的标量发生变化。当按下停止按钮时,变量会发生变化。
如果按下 [Do 0][Stop 0][Do 1][Stop 1] 命令中的按钮,输出似乎没问题(参见第 2-7 行)。但是,如果 "tasks" 并行启动,则只有当两者都停止时,这两个回调才会完成。所以按下 [Do 0][Do 1][Stop 0][Stop 1] 中的按钮(见第 8-13 行)给出了一个奇怪的结果(见图)。
我对第二次测试的期望是第一个回调函数 returns 在按下第一个停止按钮后立即出现。所以我认为输出应该是:
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
它在 Linux 机器上运行。
我错过了什么吗?提前致谢!
更新
为了绕过这个 waitVariable 问题,我重写了这个小部件以改为使用回调(感谢 Tantalus!)。现在 execute_command returns 马上。回调有两种,一种是Cancel,一种是Finish。现在通过这些回调通知调用者。无论如何,我在某处读到(我现在找不到来源),长时间等待回调在 Tk 中不是一个好主意。新的解决方案符合这一点。
感谢您的帮助!
您不应该在事件循环中等待。正在发生的事情是您正在生成一个调用堆栈,该调用堆栈涉及之前的等待循环。例如,如果您在顶部添加 use Carp;
,然后像这样更改 pr
函数:
sub pr { # Write into ROText widget
$text->insert('end', Carp::longmess(join '', @_)); $text->yview('end');
}
然后你会看到 waitVariable
出现 - 在我们回到那个循环之前我们无法回到那里,你最终会在循环中循环。
要在不将所有内容反转为事件的情况下做您想做的事,您可能想尝试 Coro 可以像这样反转事件。
此外,在现代 perls 中,qw
并不意味着括号,因此您的 pack
调用需要在 qw/.../
列表两边加上括号。
$widget->waitVariable($name)
$widget->waitVisibility
$widget->waitWindow
The tk wait methods wait for one of several things to happen, then it returns without taking any other actions. The return value is always an empty string. waitVariable expects a reference to a perl variable and the command waits for that variable to be modified. This form is typically used to wait for a user to finish interacting with a dialog which sets the variable as part (possibly final) part of the interaction. waitVisibility waits for a change in $widget's visibility state (as indicated by the arrival of a VisibilityNotify event). This form is typically used to wait for a newly-created window to appear on the screen before taking some action. waitWindow waits for $widget to be destroyed. This form is typically used to wait for a user to finish interacting with a dialog box before using the result of that interaction. Note that creating and destroying the window each time a dialog is required makes code modular but imposes overhead which can be avoided by withdrawing the window instead and using waitVisibility.
While the tk wait methods are waiting they processes events in the normal fashion, so the application will continue to respond to user interactions. If an event handler invokes tkwait again, the nested call to tkwait must complete before the outer call can complete.
强调我的。