原文:
18.1. How does one get Perl/Tk to act on events that are not coming from X?
On 22 Nov 1995 (Yaniv Bargury) bargury@milcse.cig.mot.com wrote:
I need to write a GUI monitor, that displays the status and controls a set of processes running in the background. The idea is to have the GUI application start a few child processes, command the children through pipes from the GUI to the children, and display the children status coming on pipes from the children to the GUI in real time.
The GUI must not be busy waiting, because the CPU resources are limited. This excludes using the Tk_DoWhenIdle as explained in the manual.
The usual way to do this is to for the GUI process to have one select() in its main loop. That select() should wait for X events or input from the pipes leading from the children.
How do you do this?
To which Nick Ing-Simmons <nik@tiuk.ti.com> replied:
fileevent - it is the hook into the select() in the mainloop.
In addition Avi Deitcher <avi@morgan.com> replied with:
I wrote something similar to effectively do a tail -f on multiple hosts, displaying the result on separate text widgets. Do the following:
parent child child child ..
with a one-way pipe from each child to the parent. Set up the following:
$main->fileevent(FILEHANDLE,status,subroutine);
for each pipe that you have. This will cause pTk to monitor the FILEHANDLE and call 'subroutine' when an event happens on that handle. In this case: FILEHANDLE = pipenamestatus = 'readable' or 'writable' or 'exception' and subroutine = any subroutine that you want.
To provide a concrete example of fileevent usage Stephen O. Lidie wrote a wonderful little GUI tail monitor he calls tktail: #!/usr/local/bin/perl -w # # tktail pathname use English; use Tk; open(H, "tail -f -n 25 $ARGV[0]|") or die "Nope: $OS_ERROR"; $mw = MainWindow->new; $t = $mw->Text(-width => 80, -height => 25, -wrap => 'none'); $t->pack(-expand => 1); $mw->fileevent(H, 'readable', [\&fill_text_widget, $t]); MainLoop; sub fill_text_widget { my($widget) = @ARG; $ARG = <H>; $widget->insert('end', $ARG); $widget->yview('end'); } # end fill_text_widget
An example of how one might use such a script would be to create and monitor a file foo like so: echo Hello from foo! > foo tktail foo & echo "A ship then new they built for him/of mithril and of elven glass" --Bilbo \ >> foo
译文:
18.1. 如何让Perl/Tk响应来自X以外的事件?
1995年11月22日,Yaniv Bargury写道:
我想写一个图形用户界面的监控器,用来显示状态并控制一系列后台运行的进程。我的想法是让GUI程序启动一些子进程,并通过管道向这些子进程传递命令,然后实时的显示由子进程传递回来的状态信息。
我要求GUI程序不能因为CPU忙而出现等待状态。这就无法使用手册中提到的Tk_DoWhenIdle。
实现这个目的,常规的方法是在GUI程序的mainloop中使用一个select()。这个select()应该等待X事件或者来自子进程的管道的输入。
你如何实现这个呢?
对此,Tk的作者Nick Ing-Simmons回答到:
fileevent – 它是mainloop中实现select()的一种方法。
另外,Avi Deitcher还回答到:
我写了一个程序,实现类似于高效的对多个对象实现tail -f的功能,同时在各个文本组件中显示结果。实现如下:
父进程
子进程
子进程
……
从每个子进程到父进程使用一个单路管道。对每个管道设置如下:
$main -> fileevent(FILEHANDLE,status,subroutine);
这样就可以让pTk监控每一个文件句柄,并且当某个句柄中发生一个事件时调用子程序’subroutine’来处理。在这个例子中:文件句柄 FILEHANDLE可以是管道名称,状态status可以是‘readable’或者‘writable’或者‘exception’,而子程序 subroutine则可以是你希望的任何子程序。
为了提供一个关于fileevent用法的具体例子,Stephen O. Lidie写了一个非常出色的GUI监控器的小程序,他称之为tktail:
#!/usr/local/bin/perl -w
#
# tktail pathname
use English;
use Tk;
open(H, "tail -f -n 25 $ARGV[0]|") or die "Nope: $OS_ERROR";
$mw = MainWindow->new;
$t = $mw->Text(-width => 80, -height => 25, -wrap => 'none');
$t->pack(-expand => 1);
$mw->fileevent(H, 'readable', [\&fill_text_widget, $t]);
MainLoop;
sub fill_text_widget {
my($widget) = @ARG;
$ARG = <H>;
$widget->insert('end', $ARG);
$widget->yview('end');
} # end fill_text_widget
下面是关于如何使用创建一个文件foo,然后用上面的脚本来进行监控的例子:
echo Hello from foo! > foo
tktail foo &
echo "A ship then new they built for him/of mithril and of elven glass" --Bilbo >> foo
(译者注:因为上面的例子中使用了Unix系统中的tail命令,所以在Windows系统下无法运行……但是在Unix/Linux系统中确实是可以正常使用的。)