我是Tk的新手,我想知道相关问题是否是正常的Tk行为。
简而言之:我有一个Perl / Tk(Tk版本804.028)脚本,该脚本使用两个Tk :: ExecuteCommand(v1.6)小部件。这样的对象有一个execute_command方法,它使用定义的fileevent回调来读取执行的命令的标准输出并在完成后返回。它通过waitVariable的使用来解决。但似乎两个ExecuteCommand一起启动它们只在较慢返回时才返回。我可能会在完成后立即获得更快的回报。
我做了一个小测试Perl / Tk脚本来演示问题:
arr
这会绘制一个ROText小部件和4个按钮([Do 0] [Stop 0] [Do 1] [Stop 1])(参见附图)。单击“执行”按钮,将调用函数#!/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();
,该函数将一直等到指定的标量发生更改。按下“停止”按钮时,变量会发生变化。
如果在[Do 0] [Stop 0] [Do 1] [Stop 1]命令中按下按钮,则输出似乎正常(参见第2-7行)。但如果"任务"并行启动然后两个回调完成只有两个都停止。因此按下[Do 0] [Do 1] [Stop 0] [Stop 1]中的按钮(见第8-13行)会得到一个奇怪的结果(见图)。
我对第二次测试的期望是第一个回调函数在按下第一个停止按钮后立即返回。所以我认为输出应该是:
doo
它在Linux机器上运行。
我错过了什么吗?提前谢谢!
更新
为了绕过这个waitVariable问题,我重新编写了这个小部件来改为使用回调(感谢Tantalus!)。现在execute_command立即返回。有两个回调,一个用于取消,一个用于完成。现在通过这些回调通知呼叫者。无论如何,我在某处读到(我现在找不到源代码)在Tk中长时间等待回调并不是一个好主意。新解决方案符合此要求。
感谢您的帮助!
答案 0 :(得分:3)
$小窗口> waitVariable(\ $名)
$小窗口> waitVisibility
$小窗口> waitWindow
tk等待方法等待几件事之一发生,然后返回而不采取任何其他操作。返回值始终为空字符串。 waitVariable期望对perl变量的引用,并且命令等待修改该变量。此表单通常用于等待用户完成与对话框的交互,该对话框将变量设置为交互的一部分(可能是最终)部分。 waitVisibility等待$ widget的可见性状态发生变化(如VisibilityNotify事件的到达所示)。此表单通常用于在执行某些操作之前等待新创建的窗口出现在屏幕上。 waitWindow等待$ widget被销毁。此表单通常用于等待用户在使用该交互的结果之前完成与对话框的交互。请注意,每次需要对话框时创建和销毁窗口会使代码模块化,但会产生开销,这可以通过撤消窗口而不是使用waitVisibility来避免。
当tk等待方法等待时,它们以正常方式处理事件,因此应用程序将继续响应用户交互。 如果事件处理程序再次调用tkwait,则必须在外部调用完成之前完成对tkwait的嵌套调用。
强调我的。
答案 1 :(得分:2)
您不应该在事件循环中等待。发生的事情是你生成一个涉及进入前一个等待循环的callstack。例如,如果您在顶部添加use Carp;
,然后像这样更改pr
功能:
sub pr { # Write into ROText widget
$text->insert('end', Carp::longmess(join '', @_)); $text->yview('end');
}
然后你会看到waitVariable
出现 - 我们无法回到那里,直到我们回到那个循环中,你最终会回到里面的循环循环。
要做你想做的事情而不将所有事情都反映到事件中,你可能想尝试Coro,它可以反转这样的事件。
此外,在现代perls中,qw
并不意味着括号,因此pack
调用需要围绕qw/.../
列表括号。