2008 年 6 月 30 日
引言
Perl 语言经常由 IBM? AIX? 操作系统管理员和开发人员使用,几乎每个成功的网站和大多数 AIX 系统上都使用了该语言。尽管Perl 脚本的功能非常强大,但它产生的 Web 界面却缺少图形化前端,用户必须键入信息,而不能使用鼠标,对于客户来说,这一点非常不尽人意。随着 Perl 中 Tk 模块的引入,此问题已得到解决。管理员或开发人员可以利用 Tk 模块快速为其 Perl 脚本带来新的生机,从而满足客户对X11 产品的需要。
小部件
正如在本系列文章的第 1 部分中所讨论的,可以将小部件定义为执行特定功能的图形对象。Perl/Tk模块中的任何图形对象都可以视为一个小部件。对于 GUI 应用程序而言,按钮、文本、框架和滚动条都是小部件。本文是该系列文章的第三部分,将讨论诸如 DirTree、LabEntry、LabFrame 和 Table 等小部件。
DirTree
编程的一个主要可视部分是如何处理文件和目录搜索。对此难题的一个解决方案是使用 DirTree 小部件。
使用 Perl/Tk 模块创建目录清单是非常简单的:
#!/usr/bin/perl –w #create a directory listing with DirTree use Tk; use strict; use Tk::DirTree; use Cwd; my $mw = MainWindow->new; $mw->geometry("300x400"); $mw->title("DirTree Example"); my $CWD = Cwd::cwd(); my $DIR_TREE = $mw->Scrolled('DirTree', -scrollbars => "osoe", -width => 30, -height => 25, -exportselection => 1, -browsecmd => sub {$CWD = shift}, -command => \&show_cwd)->pack(-fill => "both", -expand => 1); $DIR_TREE->chdir($CWD); my $button_frame = $mw->Frame()->pack(-side => "bottom"); $button_frame->Button(-text => "Ok", -command => \&show_cwd)->pack(-side => "left"); $button_frame->Button(-text => "Exit", -command => sub{exit})->pack(-side => "left"); sub show_cwd { $mw->messageBox(-message => "Directory Selected: $CWD", -type => "ok"); } MainLoop; |
执行此脚本将生成如图 1 和 2 所示的 GUI 应用程序:
图 1. DirTree 小部件示例
图 2. 显示选定的目录
下面让我们分析一下该脚本。
为了回顾一下本系列第 1 部分和第 2部分中讨论的内容,下面仅对此代码的第一部分讨论一次,除非后面的示例对其进行了更改。此代码行的第一部分 (/usr/bin/perl) 定义Perl 可执行文件在计算机上的驻留位置,并指示计算机使用该 Perl 可执行文件副本来执行文件multiple_windows_at_once-demo.pl:
#!/usr/bin/perl -w |
此命令行的第二部分 (-w) 是 Perl 中的一个重要工具。它支持在执行脚本时发出警告,通知最终用户可能发现的任何错误。
在执行期间不应计算的注释和文本前面加上了一个 # 号:
#create a directory listing with DirTree |
为了使 Perl 脚本使用 Tk 模块,必须包含该模块;因此添加了 use Tk 语句。将 use strict 语句添加到 Perl 脚本还有助于发现任何可能的输入或逻辑错误:
use Tk; use strict; |
要使用 DirTree 小部件,必须将其包含在 Perl 脚本中,因为它不是基本 Perl 模块中的普通小部件。第二个包含是 Cwd。使用此包含语句,该脚本就可以查找和存储 CWD(或当前工作目录):
use Tk::DirTree; use Cwd; |
要创建应用程序的主窗口,可以使用 MainWindow,并将其分配给 $mw。$mw 用作所创建的所有其他小部件的父窗口(本文将对此作进一步的讨论):
my $mw = MainWindow->new; |
将主窗口大小设置为 300x400,并将主窗口标题设置为 DirTree Example:
$mw->geometry("300x400"); $mw->title("DirTree Example"); |
查找 CWD,并将其存储在名为 $CWD 的变量中:
my $CWD = Cwd::cwd(); |
创建一个可滚动的目录树。browsecmd 选项在最终用户每次选择某个目录时重置 $CWD。如果最终用户在某个目录上双击或按 Enter 键,则 command 选项将执行子例程 show_cmd:
my $DIR_TREE = $mw->Scrolled('DirTree', -scrollbars => "osoe", -width => 30, -height => 25, -exportselection => 1, -browsecmd => sub {$CWD = shift}, -command => \&show_cwd)->pack(-fill => "both", -expand => 1); |
刷新显示了用户的 CWD 的目录树:
$DIR_TREE->chdir($CWD); |
创建一个框架,并在其中放置两个按钮。标签为“OK”的第一个按钮执行子例程 show_cmd;第二个按钮退出该脚本:
my $button_frame = $mw->Frame()->pack(-side => "bottom"); $button_frame->Button(-text => "Ok", -command => \&show_cwd)->pack(-side => "left"); $button_frame->Button(-text => "Exit", -command => sub{exit})->pack(-side => "left"); |
执行子例程 show_cmd 时,将显示一个 messageBox 小部件,其中显示了用户的 CWD:
sub show_cwd { $mw->messageBox(-message => "Directory Selected: $CWD", -type => "ok"); } |
在执行 MainLoop 之前,可以读取、定义和随时执行该脚本中的所有内容。然后,在调用 MainLoop 时,将执行先前读取的所有功能和数据,并显示 GUI:
MainLoop; |
LabEntry
许多 GUI 应用程序需要来自最终用户的手动输入。显示说明和请求输入的一种方法是使用 Label 和 Entry 小部件(已在本系列的第 1 部分中进行了讨论)。另一种方法是使用 LabEntry 小部件。LabEntry 将 Label、Entry 和 Frame小部件组合为单个易于使用的小部件:
#!/usr/bin/perl -w use Tk; use Tk::LabEntry; use strict; my $mw = MainWindow->new; $mw->geometry("300x100"); $mw->title("LabEntry Example"); my $name = ""; $mw->LabEntry(-label => "Enter your name: ", -labelPack => [ -side => "left" ], -textvariable => \$name)->pack(); my $button_frame = $mw->Frame()->pack(-side => "bottom"); $button_frame->Button(-text => "Ok", -command => \&show_greeting)->pack(-side => "left"); $button_frame->Button(-text => "Exit", -command => sub{exit})->pack(-side => "left"); sub show_greeting { my $msg = "Who are you?"; if ($name ne "") { $msg = "Nice to meet you $name!"; } $mw->messageBox(-message => "$msg\n", -type => "ok"); } MainLoop; |
执行此脚本将生成如图 3 和 4 所示的 GUI 应用程序:
图 3. LabEntry 小部件示例
图 4. LabEntry 小部件的结果
下面让我们分析一下该脚本。
与前一个使用 DirTree 的示例一样,需要包含 LabEntry 小部件。如果没有包含该小部件,Perl 脚本将不知道如何解释或执行 LabEntry 小部件:
use Tk::LabEntry; |
定义一个名为 $name 的变量,并将其值设置为 NULL 或设置为空。在定义变量之后,使用标签 Enter your name: 创建 LabEntry 小部件,对输入框左侧的标签打包,并将输入的值分配给变量 $name:
my $name = ""; $mw->LabEntry(-label => "Enter your name: ", -labelPack => [ -side => "left" ], -textvariable => \$name)->pack(); |
创建一个框架,并在其中包括两个 Button 小部件。标签为“OK”的第一个按钮执行子例程 show_greeting;第二个按钮退出该脚本:
my $button_frame = $mw->Frame()->pack(-side => "bottom"); $button_frame->Button(-text => "Ok", -command => \&show_greeting)->pack(-side => "left"); $button_frame->Button(-text => "Exit", -command => sub{exit})->pack(-side => "left"); |
单击 OK 按钮时,将执行子例程 show_greeting。变量 $msg 定义了值“Who are you?”。为该变量定义此值是为了以防最终用户忘记在 LabEntry 中输入其姓名。如果用户忘记,则变量 $name 仍然设置为 NULL。如果用户输入了姓名,则为 $msg 设置值“Nice to meet you $name!”,在下一行代码中可以看到这点。最后,向用户显示 messageBox 小部件,无论是向他致意还是告诉他该程序不知道他是谁:
sub show_greeting { my $msg = "Who are you?"; if ($name ne "") { $msg = "Nice to meet you $name!"; } $mw->messageBox(-message => "$msg\n", -type => "ok"); } |
LabFrame
本系列文章中到处都使用到了 Frame 小部件。Frames 用于组织其他小部件,使应用程序看起来更整洁和更有条理。与框架配套的一个便利的小部件是 LabFrame。使用 LabFrame 小部件,您只需做很少的工作即可将标签放在框架上或框架旁边:
#!/usr/bin/perl -w use Tk; use Tk::LabFrame; use strict; my $mw = MainWindow->new; $mw->geometry("300x200"); $mw->title("LabFrame Example"); my $labeled_frame1 = $mw->LabFrame(-label => "Caption Across Top of Frame", -labelside => "acrosstop")->pack(); my $labeled_frame2 = $mw->LabFrame(-label => "Caption Below Frame", -labelside => "bottom")->pack(-fill => "x"); $labeled_frame1->Label(-text => "Inside Frame #1")->pack(); $labeled_frame2->Label(-text => "Inside Frame #2")->pack(); my $button_frame = $mw->Frame()->pack(-side => "bottom"); $button_frame->Button(-text => "Exit", -command => sub{exit})->pack(); MainLoop; |
执行此脚本将生成如图 5 所示的 GUI 应用程序。
图 5. LabFrame 小部件示例
下面让我们分析一下该脚本。
LabFrame 与本文前面的小部件示例没有什么不同,并且需要包含在脚本中:
use Tk::LabFrame; |
创建一个框架,将其标签设置为 Caption Across Top of Frame。要按照该标题所暗示的那样在框架顶部放置标题,您必须为选项 labelside 配置值“acrosstop”,从而配置 LabFrame 小部件:
my $labeled_frame1 = $mw->LabFrame(-label => "Caption Across Top of Frame", -labelside => "acrosstop")->pack(); |
创建第二个 LabFrame 小部件,但不是将标题放在框架顶部,而是通过将选项 labelside 设置为“bottom”,从而在框架下面放置标签 Caption Below Frame:
my $labeled_frame2 = $mw->LabFrame(-label => "Caption Below Frame", -labelside => "bottom")->pack(-fill => "x"); |
为了演示 LabFrame 中的小部件,可以在父 LabFrame 小部件旁边创建两个 Label 小部件:
$labeled_frame1->Label(-text => "Inside Frame #1")->pack(); $labeled_frame2->Label(-text => "Inside Frame #2")->pack(); |
Table
Table 小部件是对 Perl 脚本的极大增强。此小部件创建小部件的二维表。与显示未组织的长数据清单不同,您可以使用表:
#!/usr/bin/perl -w use Tk; use Tk::Table; use strict; my $mw = MainWindow->new; $mw->geometry("475x125"); $mw->resizable(0,0); $mw->title("Table Example"); my $table_frame = $mw->Frame()->pack(); my $table = $table_frame->Table(-columns => 8, -rows => 4, -fixedrows => 1, -scrollbars => 'oe', -relief => 'raised'); foreach my $col (1 .. 8) { my $tmp_label = $table->Label(-text => "COL " . $col, -width => 8, -relief =>'raised'); $table->put(0, $col, $tmp_label); } foreach my $row (1 .. 8) { foreach my $col (1 .. 8) { my $tmp_label = $table->Label(-text => $row . "," . $col, -padx => 2, -anchor => 'w', -background => 'white', -relief => "groove"); $table->put($row, $col, $tmp_label); } } $table->pack(); my $button_frame = $mw->Frame( -borderwidth => 4 )->pack(); $button_frame->Button(-text => "Exit", -command => sub {exit})->pack(); MainLoop; |
执行此脚本将生成如图 6 所示的 GUI 应用程序。
图 6. Table 小部件示例
下面让我们分析一下该脚本。
您一定猜到了!必须包含另一个新的小部件,以便让 Perl 知道如何处理 Table 小部件:
use Tk::Table; |
在前面的示例中,最终用户可以调整应用程序的窗口大小。此脚本禁止用户调整窗口的大小:
$mw->resizable(0,0); |
创建一个框架来包含新的表。然后创建 Table 小部件,其中显示八列和四行:
my $table_frame = $mw->Frame()->pack(); my $table = $table_frame->Table(-columns => 8, -rows => 4, -fixedrows => 1, -scrollbars => 'oe', -relief => 'raised'); |
要在表中放置数据,可以使用 put 操作。循环八次,将文本“COL”添加到当前列的第一(0)行:
foreach my $col (1 .. 8) { my $tmp_label = $table->Label(-text => "COL " . $col, -width => 8, -relief =>'raised'); $table->put(0, $col, $tmp_label); } |
现在已经创建了标题,下面将把坐标放在各个单元格中。同样,使用 put 操作,循环遍历每行和每列并分配单元格的文本。然后,对完成后的表进行打包 (pack):
foreach my $row (1 .. 8) { foreach my $col (1 .. 8) { my $tmp_label = $table->Label(-text => $row . "," . $col, -padx => 2, -anchor => 'w', -background => 'white', -relief => "groove"); $table->put($row, $col, $tmp_label); } } $table->pack(); |
Canvas
Canvas 是 Perl/Tk 模块中的一个非常有用的绘图工具。使用此小部件,用户可以绘制和操作不同的形状和对象,例如直线、椭圆、矩形和多边形:
#!/usr/bin/perl -w use Tk; use strict; my $mw = MainWindow->new; $mw->geometry("400x400"); $mw->title("Canvas Example"); my $canvas = $mw->Canvas(-relief => "sunken", -background => "blue"); $canvas->createLine(2, 3, 350, 100, -width => 10, -fill => "black"); $canvas->createLine(120, 220, 450, 200, -fill => "red"); $canvas->createOval(30, 80, 100, 150, -fill => "yellow"); $canvas->createRectangle(50, 20, 100, 50, -fill => "cyan"); $canvas->createArc(40, 40, 200, 200, -fill => "green"); $canvas->createPolygon(350, 120, 190, 160, 250, 120, -fill => "white"); $canvas->pack(); $mw->Button(-text => 'Exit', -command => sub {exit})->pack();; MainLoop; |
执行此脚本将生成如图 7 所示的 GUI 应用程序。
图 7. Canvas 小部件示例
下面让我们分析一下该脚本。
以下代码创建 Canvas 小部件:
my $canvas = $mw->Canvas(-relief => "sunken", -background => "blue"); |
创建一条宽度为 10 的黑线,并将其从 (2, 3) 绘制到 (350, 100)。在处理 Canvas 小部件中的对象和形状时,第一组数值是坐标。最适合使用公式 (x1, y1, x2, y2, ....) 来查看某个对象:
$canvas->createLine(2, 3, 350, 100, -width => 10, -fill => "black"); $canvas->createLine(120, 220, 450, 200, -fill => "red"); |
在 Canvas 小部件上创建一个从 (30, 80) 到 (100, 150) 的黄色椭圆:
$canvas->createOval(30, 80, 100, 150, -fill => "yellow"); |
在 Canvas 小部件上创建一个从 (50, 20) 到 (100, 50) 的青色矩形:
$canvas->createRectangle(50, 20, 100, 50, -fill => "cyan"); |
在 Canvas 小部件上创建一个从 (40, 40) 到 (200, 200) 的绿色弧形:
$canvas->createArc(40, 40, 200, 200, -fill => "green"); |
在 Canvas 小部件上创建一个从 (350, 120) 到 (190, 160) 和 (250, 120) 的白色多边形:
$canvas->createPolygon(350, 120, 190, 160, 250, 120, -fill => "white"); |
在创建所有对象之后,与以前一样,对该小部件打包:
$canvas->pack(); |
结束语
将带有 Perl/Tk 模块的 Perl 引入 AIX环境可以让开发人员、管理员、客户和最终用户受益。客户起初觉得很枯燥的脚本,可以增强为具有专业外观的 GUI应用程序。熟悉这些小部件可能需要一些时间,但是,您掌握了这些技术之后,就会感到付出精力确实值得!