2011-04-25 52 views
3

我有一个运行在版本5.10上的Perl脚本,在windows xp上运行ActiveStates Active Perl,它创建了一个UI,然后在按下按钮后运行一个长进程。在这个过程中,我想更新UI(一个列表框),其中包含执行此线程期间发生的事情的状态。这是一个精简版的代码。长线程上的Perl更新UI

#!/usr/local/bin/perl 

use warnings; 
use strict; 
use Tkx; 
use threads; 
use threads::shared; 

my $outputText = " {a} {b}"; 

my $mw = Tkx::widget->new("."); 
$mw->g_wm_title("MD5 Checker"); 
$mw->g_wm_minsize(300,200); 
my $content = $mw->new_ttk__frame(-padding => "12 12 12 12"); 
my $btnCompare = $content->new_ttk__button(-text => "Compare", -command => sub{startWork()}); 
my $lstbxOutput = $content->new_tk__listbox(-listvariable => \$outputText, -height => 5); 
my $scollListBox = $content->new_ttk__scrollbar(-orient => 'vertical', -command => [$lstbxOutput, 'yview']); 
$lstbxOutput->configure(-yscrollcommand => [$scollListBox, 'set']); 

sub startWork() 
{ 
    print "Starting thread \n"; 
    my $t = threads->create(\&doWork, 1); 
    sleep (5); 
    print $outputText . "\n"; 
} 

sub doWork() 
{ 
    for (my $a = 0; $a<10; $a++) 
    { 
     $outputText .= " {$a}"; 
     print "Counting $a\n"; 
     sleep(2); 
    } 
    print "End thread\n"; 
} 

当前打印命令用于我的调试,所以我知道主线程和子线程在做什么。从我已阅读的关于线程的内容中,我需要use threads::shared;来允许线程共享变量。此刻,我的列表框在子线程执行期间不会更新,也不会在线程结束时更新。如果没有线程,在主线程完成循环后列表框会更新。在线程执行过程中,我缺少什么让UI更新?

由于

韦斯利

回答

2

一个问题是,列表框变量需要在线程之间共享。 Tk似乎并不满意直接共享列表框变量,所以我制作了两个副本,并设置了一个定期状态更新以将共享版本复制到非共享版本。

但是,使用Tkx的线程可能会有点冒险。我在尝试使用线程join而不是detach时遇到了段错误,并且如果我将my $t移动到startWork()的内部,则会收到以下代码的段错误。 This discussion建议您在创建任何Tk小部件之前可能需要启动线程才能可靠地工作。

这里是我结束了代码:

my $outputTextShared :shared = " {a} {b}"; 
my $outputText = " {a} {b}"; 

my $t; 
sub startWork() 
{ 
    print "Starting thread \n"; 
    $t = threads->create(\&doWork, 1); 
} 

sub updateStatus() 
{ 
    $outputText = $outputTextShared; 
} 

sub doWork() 
{ 
    threads->detach(); 
    for (my $a = 0; $a<10; $a++) 
    { 
     $outputTextShared .= " {$a}"; 
     print "Counting $a\n"; 
     sleep(1); 
    } 
    print "End thread\n"; 
} 

my $update; 
$update = sub { 
    Tkx::after (1000, $update); 
    updateStatus(); 
}; 
Tkx::after (1000, $update); 

Tkx::MainLoop(); 
+0

我做了这个改变,现在我无法与我的列表框进行交互,也没有使用另一个线程插入到该变量的文本进行更新。它看起来像列表框已经被遗忘了。我的用户界面的其余部分工作正常。 – Wesley 2011-04-25 20:28:18

+0

@韦斯利我猜''listvariable'不能直接在线程之间共享,我会看看我是否可以重现这个问题。 – Andy 2011-04-25 21:00:55

+0

谢谢,如果有帮助,我可以发布我的所有代码,以便为您提供基于目前必须查看的内容,以确定它是否可在您的系统上重复使用。 – Wesley 2011-04-25 21:04:58

1

线程是很好的,因为用户界面不会阻止,你可以做的事情一样,如果它的时间太长杀子进程。尽管如此,这种力量却带有复杂性。如果你所关心的是更新UI中的任务状态,那么你可以不使用线程来完成;你只需要手动完成。

$outputText = 'some message'; 
Tkx::update('idletasks');