现在的位置: 首页 > 综合 > 正文

《TCL/TK编程实践》第四版 第24章 TK例子 24.1 ExecLog

2012年03月16日 ⁄ 综合 ⁄ 共 5788字 ⁄ 字号 评论关闭
24        TK例子
    本章通过一系列较短的子来介绍TkExecLog在后台运行一个程序并显示其输出。浏览器的例子从书中显示TCL例子。Tcl Shell例子让你键入TCL命令并在一从此从解释器中执行。
    Tk提供了一种快速有效的方式产生来用户界面。在本章我们通过一系列小例子让你对你能做什么有一个感性的认识。这里掩盖了一些细节并在后面进行详细的描述。特别地,pack布器管理器在第25间涵盖,事件绑定在第29间讨论。Tk控件会在后面的章节中进行详细的讨论。
24.1 ExecLog
我们的第一个例子提供了一个用exec命令运行另一个程序的简单用户界面。此界面包括两个按钮(Run itQuit)一个用于输入命令的entry控件,一个用于记录程序运行结果的文本控件。此脚本在一个管道中运行这个程序,并使用fileevent命令来等待输出。此结构使程序在执行时用户界面仍然保持响应。比如你可以运行make,并使结果保存在log中。完整的例子先给出,其中的命令在后面详细的讨论。
24.1.124-1使用exec记录一个程序的输出
 

#!/usr/local/bin/wish
# execlog - run a program with exec and log the output
# Set window title
wm title . ExecLog
 
# Create a frame for buttons and entry.
 
frame .top -borderwidth 10
pack .top -side top -fill x
 
# Create the command buttons.
 
button .top.quit -text Quit -command exit
set but [button .top.run -text "Run it" -command Run]
pack .top.quit .top.run -side right
 
# Create a labeled entry for the command
 
label .top.l -text Command: -padx 0
entry .top.cmd -width 20 -relief sunken /
   -textvariable command
pack .top.l -side left
pack .top.cmd -side left -fill x -expand true
 
# Set up key binding equivalents to the buttons
 
bind .top.cmd <Return> Run
bind .top.cmd <Control-c> Stop
focus .top.cmd
 
# Create a text widget to log the output
 
frame .t
set log [text .t.log -width 80 -height 10 /
   -borderwidth 2 -relief raised -setgrid true /
   -yscrollcommand {.t.scroll set}]
scrollbar .t.scroll -command {.t.log yview}
pack .t.scroll -side right -fill y
pack .t.log -side left -fill both -expand true
pack .t -side top -fill both -expand true
 
# Run the program and arrange to read its input
 
proc Run {} {
   global command input log but
   if [catch {open "|$command |& cat"} input] {
      $log insert end $input/n
   } else {
      fileevent $input readable Log
      $log insert end $command/n
      $but config -text Stop -command Stop
   }
}
 
# Read and log output from the program
 
proc Log {} {
   global input log
   if [eof $input] {
      Stop
   } else {
      gets $input line
      $log insert end $line/n
      $log see end
   }
}
 
# Stop the program and fix up the button
 
proc Stop {} {
   global input but
   catch {close $input}
   $but config -text "Run it" -command Run
}
 
 
24.1.2窗口标题
第一个命令设置出现在由窗口管理器实现的标题栏。记住那个点(也就是.)就是主窗口的名字:

wm title . ExecLog
wm命令与窗口管理器交互。窗口管理器就是让你打开、关闭、调节窗口大小的程序。它为窗口实现标题栏,可能也有一些用来关闭或调节大小的小按钮。不同的窗口管理器有完全不同的外表;此图显示了来自X窗口管理器twm的标题栏。
 
24.1.3按钮的框架
创建一个框架用来装出现在界面顶部的控件。此框架有一个为控件周围提供一些空间的边界:

frame .top -borderwidth 10
框架位于主窗口的顶部。默认的堆叠边是顶部,所以这里-side top是多余的,但是使用是为了清晰化。-fill x堆叠选项使框架将主窗口整个宽填满:

pack .top -side top -fill x
 
 
24.1.4命令按钮
创建了两个按钮:一个用来运行命令,另一个用来退出程序。它们的名字.top.quit.top.run暗示它们是.top框架的子控件。这个影响pack命令,它默认将控件放置在父控件中:

button .top.quit -text Quit -command exit
set but [button .top.run -text "Run it" /
   -command Run]
pack .top.quit .top.run -side right
 
 
24.1.5标签与Entry
标签和entry也作为.top框架的子控件创建。标签创建时在X方向没有填充空间,以便可以放置在后面的entry控件。Entry控件的大小根据字符来确定。Relief属性给予entry控件一些外观使它在显示上分开来。Entry控件的内容关联到TCL变量command上:

label .top.l -text Command: -padx 0
entry .top.cmd -width 20 -relief sunken /
   -textvariable command
标签和entry控件停靠在.top框架内部的左边。Entry控件的另外停靠参数允许它扩展其停靠空间并在显示上填充其额外的空间。停靠空间和显示空间的区别在339页第25章中讨论:

pack .top.l -side left
pack .top.cmd -side left -fill x -expand true
 
24.1.6按键绑定与焦点
Entry控件的按键绑定提供了激活应用程序功能的另外一种方式。Bind命令将TCL命令与一个指定控件的事件关联起来。当用户在键盘上按回车键时,<Return>事件会被触发。当Control键已经按下时,字母c被键入时<Control-c>事件被产生。为使事件到达entry控件.top.cmd,必须将输入焦点给这个控件。默认情况下,当用鼠标左键点击它时就得到焦点。对于焦点跟随鼠标模式的用户来说显示使用focus命令是非常有益的。当鼠标在主窗口上时,用户可以向entry中打字:

bind .top.cmd <Return> Run
bind .top.cmd <Control-c> Stop
focus .top.cmd
 
24.1.7可调大小文本和滚动条
创建一个文本控件并连同一个滚动条停靠到一个主框架中。文本控件的宽和高分别用字符和行数来指定。文本的setgrid属性是打开的。这限制其大小调节以便使整数行、平均大小的字符能被显示。
Tk中,滚动条是一个单独的控件,它可以使用和这里相同的设置连到不同的控件上。当文本控件被修改时,文本的yscrollcommand更新滚动条的显示,且当用户操纵滚动行时,滚动条的command滚动与其关联的控件:

frame .t
set log [text .t.log -width 80 -height 10 /
   -borderwidth 2 -relief raised -setgrid true/
   -yscrollcommand {.t.scroll set}]
scrollbar .t.scroll -command {.t.log yview}
pack .t.scroll -side right -fill y
pack .t.log -side left -fill both -expand true
pack .t -side top -fill both -expand true
创建一个Tk控件的副作用是创建了一个操作此控件的新的TCL命令。此TCL命令与控件的路径名相同。在此脚本中,文本控件命令,也就是.t.log,在许多地方需要。可是,将一个重要控件的路径名存到一个变量中是一个好主意,因为如果你重新组织用户界面,路径名可以改变。这样做的缺点是你必须在过程内部要用global声明此变量。为了证明这种方式此例中的变量log就是用于这个目的。
 
24.1.8Run过程
Run过程开始在命令entry中指定的程序。因为entrytextvariable属性,此值在全局command命令中可以得到,命令在管道中运行以便它在后台运行。Open命令参数的前导符|表示创建一个管道。Catch命令作为非法命令输入的保护。变量input设置成错误消息或正常的open返回的文件描述符。程序像这样开始:

if [catch {open "|$command |& cat"} input] {
² 从管道中捕获错误
管道通过cat程序从命令转移错误输出。如果你不像这里使用cat,当管道关闭时管道中的错误会输出弹出一个错误消息。在本例中,令人尴尬的是如何区分程序产生的错误和由于Stop过程实现的方式所产生的错误。不但如此某些程序间隔正常和错误输出,而且你可能想按顺序查看错误输出而不是在最后一下子查看。
    如果管道成功打开,则使用fileevent命令设置一个回调函数。当管道产生输出时,脚本可以从中读出数据。Log注册为当管道可读时被调用的过程:

fileevent $input readable Log
    Command(或错误消息)被插入到log中。这个通过使用文本控件的名字来实现,名字以一个TCL命令存储在log变量中。命令的值被追加到log,并加了一个新行以便其输出可以出现在下一行。

$log insert end $command/n
文本控件的insert函数带有两个参数:一个标记和一个在此标记上要插入的字串。符号标记end代表文本控件内容的末尾。
    运行按钮在程序开始后变成停止按钮。这个避免界面的混乱,也证明了Tk界面的动态特性。再次,因为这个按钮在脚本中几个不同的地方使用,其路径名已经被存储在变量中but

but config -text Stop -command Stop
 
 
24.1.9Log过程
当管道中有数据可读或达到文件末尾时,Log过程就被激活。此条件首先被检查,且Stop过程被调用来进行清理。否则,一行数据被读取并插入到log中。文本控件的see操作用来将文本的视图定位以使新行对用户可见:

if [eof $input] {
   Stop
} else {
   gets $input line
   $log insert end $line/n
   $log see end
}
 
 
24.1.10   Stop过程
Stop过程通过关闭管道来终止程序。Close用一个catch进行包装。这个可以制止当进程上的管道过早地关闭时产生的错误。最后,按钮被恢复到其运行状态以便用户可以运行另一个命令:

catch {close $input}
$but config -text "Run it" -command Run
    在多数情况下,关闭管道等同于杀死任务。在UNIX中,这个将导致下一次向其标准输出写操作时发送一个SIGPIPE信号到程序。没有一个杀死进程的内嵌方式,但是你可以exec UNIXkill程序。Pid命令返回管道中进程的ID

foreach pid [pid $input] {
   catch {exec kill $pid}
}
    如果你需要对另一个进程更复杂的控制,你应该看一下在Explring Expect(Don Libes, O'Reilly & Associates, Inc., 1995)中描述的TCLexpect扩展。Expect提供了对交互式程序的强有力的控制。你可以写发送输入到交互式程序并对其输出进行模式匹配的TCL脚本。Expect被设计为自动使用交互式应用为目的的程序。
 
24.1.11   跨平台讨论
这个脚本可以在UNIXWindows上运行,但是不能运行在Macintosh上因为那没有exec命令。另一个问题是取消任务的<Control-c>的绑定。这是类UNIX的,但Windows用户指望<Escape>来取消一项任务,Macontosh用户指望<Command-period>Platform_CancelEvent定义了一个虚拟事件、<<Cancel>>Stop绑定其上:
 
24.1.12   24-2一个指定平台的取消事件

proc Platform_CancelEvent {} {
   global tcl_platform
   switch $tcl_platform(platform) {
      unix {
         event add <<Cancel>> <Control-c>
      }
      windows {
         event add <<Cancel>> <Escape>
      }
      macintosh {
         event add <<Cancel>> <Command-period>
      }
   }
}
bind .top.entry <<Cancel>> Stop
    Tk也已经也定义了虚拟事件。event命令和虚拟事件446页中描述。
 

抱歉!评论已关闭.