你的方法有几个问题。首先,序列“禁用按钮”、“做一些非 Tk-ish”、“启用按钮”不起作用,并且按钮始终保持启用状态。update()
原因是只有在回调返回或显式调用或被调用时才会执行显示更改idletasks()
。为了证明这一点,在以下示例中,该按钮永远不会被禁用:
use Tk;
my $mw = tkinit;
my $b; $b = $mw->Button(-text => 'Click me!', -command => sub {
$b->configure(-state => 'disabled');
# $mw->idletasks; # or alternatively: $mw->update;
sleep 10; # simulate some task
$b->configure(-state => 'normal');
})->pack;
MainLoop;
但是如果idletasks()
orupdate()
调用被激活,那么事情就会按预期进行。
示例脚本有一个缺陷:当sleep()
(或任何繁忙的任务)处于活动状态时,无法对 GUI 进行更新。应用程序对用户来说似乎是冻结的。甚至窗口刷新也不再发生。如果这是一个问题,则必须以非阻塞方式重组整个脚本。在这种情况下,可以在后台启动一个新进程(正如您已经使用start
命令执行的那样),但是您必须知道后台进程何时完成,然后再次启用该按钮。一种简单的方法是使用某种“信号文件”并定期检查该信号文件是否是从后台进程创建的。这是一个适用于 Unix 系统的示例(但也应该适用于 Windows,如果system()
调用中使用了自定义脚本):
use Tk;
my $mw = tkinit;
my $b; $b = $mw->Button(-text => 'Click me!', -command => sub {
$b->configure(-state => 'disabled');
# no idletasks/update necessary now
system("(rm -f /tmp/signal_file; sleep 10; touch /tmp/signal_file) &");
my $repeater; $repeater = $mw->repeat(1000, sub {
if (-e "/tmp/signal_file") {
unlink "/tmp/signal_file";
$b->configure(-state => 'normal');
$repeater->cancel;
}
});
})->pack;
MainLoop;
这种方法不是很优雅,因为它需要定期检查。可以使用该Tk::IO
模块编写更优雅的方式,但这可能仅适用于 Unix 平台:
use Tk;
use Tk::IO;
my $exec_fh;
my $mw = tkinit;
my $b; $b = $mw->Button(-text => 'Click me!', -command => sub {
$b->configure(-state => 'disabled');
# no idletasks/update necessary now
$exec_fh = Tk::IO->new(-linecommand => sub {
# empty, we're not interested in the output
}, -childcommand => sub {
$b->configure(-state => 'normal');
});
$exec_fh->exec("sleep 10");
})->pack;
MainLoop;
Pod是进一步的好读物Tk::fileevent
,也许你可以AnyEvent
试一试。