Perl/Tk 模块,第 2 部分: 中级小部件
引言
Perl 语言经常由 IBM® AIX® 操作系统 (AIX) 管理员和开发人员使用。在几乎每个成功的网站和大多数 AIX 系统上都可以发现 Perl。尽管 Perl 脚本的功能非常强大,但它产生的 Web 界面却缺少图形化前端,用户必须键入信息,而不能使用鼠标,对于客户来说,这一点非常不尽人意。随着 Perl 中 Tk 模块的引入,此问题已得到解决。管理员或开发人员可以使用 Tk 模块迅速为 Perl 脚本带来新的生机,并满足客户对 X11 产品的需要。
了解小部件
正如在第 1 部分中所讨论的, 小部件 是执行特定功能的图形对象。Perl/Tk 模块中的任何图形对象都可以视为一个小部件。在 GUI 应用程序中,按钮、文本、框架和滚动条全都是小部件。此第二篇文章将讨论如下小部件:
- Radiobutton
- Checkbutton
- Menu
- Menubutton
- Scrollbar
包装小部件
pack
是最常用的功能之一,同时也是最复杂的方法之一。pack
功能是 Perl/Tk 模块中的几何或布置管理器。当开发人员定义某个小部件时,它只是经过了定义。在几何管理器适当分配空间之前,小部件不会显示出来——这就是 pack
发挥作用的地方。pack
功能计算小部件的父级对象上分配的空间并显示小部件。
创建多个窗口
取决于最终用户的需要,应用程序可能需要多个窗口。创建多个窗口非常简单。它扩展了第 1 部分中使用的方法。要回答的第一个问题在于,应用程序应该同时创建并显示窗口,还是应该由用户的操作触发新窗口的创建和显示。本部分将同时讨论这两种方法。创建一个同时显示多个窗口的应用程序是创建单个窗口的延伸,只是另外添加了 TopLevel 小部件:
#!/usr/bin/perl -w # Create multiple windows at once use Tk; use strict; my $mw = MainWindow->new; $mw->geometry("200x100"); $mw->title("Multiple Windows Test"); my $subwin1 = $mw->Toplevel; $subwin1->title("Sub Window #1"); my $subwin2 = $mw->Toplevel; $subwin2->title("Sub Window #2"); MainLoop;
运行此脚本将生成如图 1 所示的 GUI 应用程序。
图 1. 一次打开多个窗口的示例
本文回顾了一遍第一部分中的代码,以便复习本系列的第 1 部分中讨论的内容。此代码行的第一部分 (/usr/bin/perl) 定义 Perl 可执行文件在计算机上的驻留位置,并指示计算机使用该 Perl 可执行文件副本来执行文件 multiple_windows_at_once-demo.pl:
#!/usr/bin/perl -w
此代码行的第二部分 (-w
) 是 Perl 中的一个重要工具:它支持在执行脚本时发出警告,通知最终用户可能发现的任何错误。在执行时不进行求值的注释和文本前面加有一个磅字符 (#
)。例如:
# Create multiple windows at once
为了使 Perl 脚本使用 Tk 模块,必须包括该模块:因此要添加 use Tk
代码。例如:
use Tk; use strict;
向 Perl 脚本添加 use strict
语句可帮助查找输入错误和逻辑错误。要创建应用程序的主窗口,可以使用 MainWindow
,并将其分配给 $mw
。例如:
my $mw = MainWindow->new;
$mw
充当所有其他小部件的父窗口,本文将对此作进一步的讨论。将主窗口大小设置为 200x100,并将主窗口标题设置为 Multiple Windows Test。例如:
$mw->geometry("200x100"); $mw->title("Multiple Windows Test");
要在应用程序中创建附加窗口,可以使用 TopLevel
并将其分配给 $subwin1
。例如:
my $subwin1 = $mw->Toplevel; $subwin1->title("Sub Window #1");
与创建第一个附加窗口类似,创建另一个窗口并将其分配给 $subwin2
。例如:
my $subwin2 = $mw->Toplevel; $subwin2->title("Sub Window #2");
在执行 MainLoop
之前,可以读取、定义和随时执行该脚本中的所有内容。然后,在调用 MainLoop
时,将执行先前读取的所有功能和数据,并显示 GUI。例如:
MainLoop;
创建一个应用程序,并让该应用程序显示由用户触发的附加窗口,这同样是非常容易的。例如:
#!/usr/bin/perl -w # Create a sub window at the request of the user use Tk; use strict; my $mw = MainWindow->new; $mw->geometry("400x100"); $mw->title("Multiple Windows Test"); my $button1 = $mw->Button(-text => "Open new window", -command => \&button1_sub)->pack(-side => "top"); $mw->Button(-text => "Exit", -command => sub{exit})->pack(); sub button1_sub { my $subwin1 = $mw->Toplevel; $subwin1->geometry("300x150"); $subwin1->title("Sub Window #1"); my $subwin_button = $subwin1->Button(-text => "Close window", -command => [$subwin1 => 'destroy'])->pack(); } MainLoop;
执行此脚本将生成如图 2 所示的 GUI 应用程序。
图 2. 单独打开多个窗口的示例
当此脚本执行时,将仅显示主窗口。但是,单击 Open new window 按钮将创建并显示子窗口(请参见图 3)。
图 3. 打开的子窗口
首先,创建一个标签为 Open new window 的按钮,并将其功能分配给子例程 button1_sub
。同时创建一个 Exit 按钮,此按钮执行退出 Perl 脚本的子例程。例如:
my $button1 = $mw->Button(-text => "Open new window", -command => \&button1_sub)->pack(-side => "top"); $mw->Button(-text => "Exit", -command => sub{exit})->pack();
button1_sub
子例程创建一个新窗口,其中具有设置大小和标题的 TopLevel 小部件。它还创建一个 Close Window 按钮,此按钮允许用户销毁子窗口。例如:
sub button1_sub { my $subwin1 = $mw->Toplevel; $subwin1->geometry("300x150"); $subwin1->title("Sub Window #1"); my $subwin_button = $subwin1->Button(-text => "Close window", -command => [$subwin1 => 'destroy'])->pack(); }
使用中级小部件
本部分讨论中级小部件,这一部分的讨论是第 1 部分中讨论的入门级小部件的延续。这些中级小部件包括 radiobutton、checkbutton、menu、menubutton 和 scrollbar 小部件。
Radiobutton
radiobutton 小部件显示一个或多个单选按钮;用户必须在按钮之间做出决定。下面的示例演示如何将 radiobutton 小部件引入应用程序。
#!/usr/bin/perl -w # create radio buttons use Tk; use strict; my $mw = MainWindow->new; $mw->geometry("300x100"); $mw->title("Radio Button Test"); my $color = "Red"; my $radio_frame = $mw->Frame()->pack(-side => "top"); $radio_frame->Label(-text=>"My favorite primary color is ")->pack(-side => "left"); my $radio_blue = $radio_frame->Radiobutton(-text => "Blue", -value => "Blue", -variable=> \$color)->pack(-side => "right"); my $radio_yellow = $radio_frame->Radiobutton(-text => "Yellow", -value => "Yellow", -variable=> \$color)->pack(-side => "right"); my $radio_red = $radio_frame->Radiobutton(-text => "Red", -value => "Red", -variable=> \$color)->pack(-side => "right"); my $button_frame = $mw->Frame()->pack(-side => "bottom"); my $button_color = $button_frame->Button(-text => "OK", -command => \&color_sub)->pack(-side => "left"); my $button_exit = $button_frame->Button(-text => "Exit", -command => sub{exit})->pack(-side => "right"); sub color_sub { $mw->messageBox(-message => "You selected $color!", -type => "ok"); } MainLoop;
执行此脚本将生成如图 4 所示的 GUI 应用程序。
图 4. radiobutton 小部件示例
您将首先定义变量 $color
,并将其值设置为“Red”:
my $color = "Red";
创建一个框架来容纳单选按钮和标签可以为小部件提供整洁、对齐和专业的外观。创建一个 label 小部件以允许用户了解程序预期用户提供的输入。例如:
my $radio_frame = $mw->Frame()->pack(-side => "top"); $radio_frame->Label(-text=>"My favorite primary color is ")->pack(-side => "left"); my $radio_blue = $radio_frame->Radiobutton(-text => "Blue", -value => "Blue", -variable=> \$color)->pack(-side => "right"); my $radio_yellow = $radio_frame->Radiobutton(-text => "Yellow", -value => "Yellow", -variable=> \$color)->pack(-side => "right"); my $radio_red = $radio_frame->Radiobutton(-text => "Red", -value => "Red", -variable=> \$color)->pack(-side => "right");
创建三个 radiobutton 小部件,并将它们分配给预定义的变量 $color
。每个单选按钮都具有文本和一个指定为某种颜色的值。由于为变量$color
定义了值“Red”,因此 $radio_red
小部件是缺省值,并设置为 true。现在添加第二个框架以容纳窗口的按钮。在该框架内,创建两个按钮。第一个按钮的标签为 OK ,并在用户单击时执行子例程 color_sub
。第二个按钮的标签为 Exit,并在单击时退出程序。例如:
my $button_frame = $mw->Frame()->pack(-side => "bottom"); my $button_color = $button_frame->Button(-text => "OK", -command => \&color_sub)->pack(-side => "left"); my $button_exit = $button_frame->Button(-text => "Exit", -command => sub{exit})->pack(-side => "right");
下面的子例程显示一个消息框,通知用户他们选择的颜色。当用户单击 OK 时,该消息框将销毁。例如:
sub color_sub { $mw->messageBox(-message => "You selected $color!", -type => "ok"); }
Checkbutton
checkbutton 小部件显示一个或多个复选框。用户可以选择其中一个或多个复选框。下面的示例演示了 checkbutton 小部件的使用:
#!/usr/bin/perl -w use Tk; use strict; my $mw = MainWindow->new; $mw->geometry("300x150"); $mw->title("Check Button Test"); my $check1 = 'NOT CHECKED'; my $check2 = 'NOT CHECKED'; my $check3 = 'NOT CHECKED'; my $check_frame = $mw->Frame()->pack(-side => "top"); $check_frame->Label(-text=>"Select some check buttons.")->pack(-side => "top")->pack(); my $chk1 = $check_frame->Checkbutton(-text => 'Check #1', -variable => \$check1, -onvalue => 'CHECKED', -offvalue => 'NOT CHECKED')->pack(); my $chk2 = $check_frame->Checkbutton(-text => 'Check #2', -variable => \$check2, -onvalue => 'CHECKED', -offvalue => 'NOT CHECKED')->pack(); my $chk3 = $check_frame->Checkbutton(-text => 'Check #3', -variable => \$check3, -onvalue => 'CHECKED', -offvalue => 'NOT CHECKED')->pack(); my $button_frame = $mw->Frame()->pack(-side => "bottom"); my $ok_button = $button_frame->Button(-text => 'OK', -command => \&check_sub)->pack(-side => "left"); my $exit_button = $button_frame->Button(-text => 'Exit', -command => sub{exit})->pack(-side => "right"); sub check_sub { my $check_msg = "Check #1: $check1\nCheck #2: $check2\nCheck #3: $check3"; $mw->messageBox(-message => "Check Button Summary:\n$check_msg", -type => "ok"); } MainLoop;
执行此脚本将生成如图 5 所示的 GUI 应用程序。
图 5. checkbutton 小部件示例
首先,为 checkbutton 定义三个变量,并设置为缺省值“NOT CHECKED”。例如:
my $check1 = 'NOT CHECKED'; my $check2 = 'NOT CHECKED'; my $check3 = 'NOT CHECKED';
下一步,创建一个框架来组织 label 小部件和 checkbutton 小部件。同时创建标签并对其进行包装,使其与框架顶部对齐。例如:
my $check_frame = $mw->Frame()->pack(-side => "top"); $check_frame->Label(-text=>"Select some check buttons.")->pack(-side => "top")->pack();
创建三个 checkbutton,标签分别为 Check #1、Check #2 和 Check #3。向它们分配预定义变量 $check1
至 $check3
,并设置 onvalue
和offvalue
属性以向用户显示适当的文本。例如:
my $chk1 = $check_frame->Checkbutton(-text => 'Check #1', -variable => \$check1, -onvalue => 'CHECKED', -offvalue => 'NOT CHECKED')->pack(); my $chk2 = $check_frame->Checkbutton(-text => 'Check #2', -variable => \$check2, -onvalue => 'CHECKED', -offvalue => 'NOT CHECKED')->pack(); my $chk3 = $check_frame->Checkbutton(-text => 'Check #3', -variable => \$check3, -onvalue => 'CHECKED', -offvalue => 'NOT CHECKED')->pack();
创建第二个框架来组织按钮。在该框架中创建两个按钮。第一个按钮的标签为 OK,用于触发子例程 check_sub
。第二个按钮执行该子例程以退出 Perl 脚本。例如:
my $button_frame = $mw->Frame()->pack(-side => "bottom"); my $ok_button = $button_frame->Button(-text => 'OK', -command => \&check_sub)->pack(-side => "left"); my $exit_button = $button_frame->Button(-text => 'Exit', -command => sub{exit})->pack(-side => "right");
子例程 check_sub
定义了一个名为 $check_msg
的变量,其值为每个 checkbutton 的状态,并使用 messageBox 小部件显示到变量$check_msg
的输出。例如:
sub check_sub { my $check_msg = "Check #1: $check1\nCheck #2: $check2\nCheck #3: $check3"; $mw->messageBox(-message => "Check Button Summary:\n$check_msg", -type => "ok"); }
Menu 和 menubutton
menu 小部件是一种在单个整洁的列中显示项的简便方法。大多数复杂的 GUI 应用程序都包含某种形式的菜单,其中可能仅包含一个退出功能或包含 20 个选项。下面的脚本演示了创建菜单有多么容易:
#!/usr/bin/perl -w use Tk; use strict; my $mw = MainWindow->new; $mw->geometry("300x150"); $mw->title("Menu Test"); my $main_menu = $mw->Menu(); $mw->configure(-menu => $main_menu); my $file_menu = $main_menu->cascade(-label=>"File", -underline => 0, -tearoff=>0); $file_menu->command(-label=>"Exit", -underline=>0, -command=>sub{exit}); $main_menu->command(-label=>"Say Hello", -underline => 0, -command=>sub{$mw->messageBox(-message=>"Hello!", -type => "ok")}); MainLoop;
执行此脚本将生成如图 6 所示的 GUI 应用程序。
图 6. menu 小部件示例
创建 menu 小部件,并开始配置它,从而为菜单项做准备。例如:
my $main_menu = $mw->Menu(); $mw->configure(-menu => $main_menu);
现在创建 File 菜单。例如:
my $file_menu = $main_menu->cascade(-label=>"File", -underline => 0, -tearoff=>0);
使用一个退出脚本的子例程,在 File 菜单下创建一个 Exit 命令。例如:
$file_menu->command(-label=>"Exit", -underline=>0, -command=>sub{exit});
创建另一个菜单,并将其标签设置为 Say Hello。例如:
$main_menu->command(-label=>"Say Hello", -underline => 0, -command=>sub{$mw->messageBox(-message=>"Hello!", -type => "ok")});
与将命令包括在此菜单之下不同,可以在此菜单本身之上配置一个命令。当您选择此菜单时,将会出现一个显示“Hello!”的消息框。menubutton 小部件与 menu 小部件类似,只不过它包括一种显示与菜单关联的文本或图像的方式。下面的示例描述如何将 menubutton 小部件添加到应用程序中,以及如何为命令添加一点颜色。
#!/usr/bin/perl -w use Tk; use strict; my $mw = MainWindow->new; $mw->geometry("300x150"); $mw->title("Menubutton Test"); my $main_menu = $mw->Menu(); $mw->configure(-menu => $main_menu); my $btn = $main_menu->Menubutton(-text => "Colorful Buttons...", -underline => 0, -tearoff=>0); $btn->command(-label => "Button #1", -activebackground => "blue", -foreground => "blue", -command => sub{$mw->messageBox(-message => "Button #1 Pressed")}); $btn->command(-label => "Button #2", -activebackground => "red", -activeforeground => "black", -background => "yellow", -foreground => "green", -command => sub{$mw->messageBox(-message => "Button #2 Pressed")}); $btn->command(-label => "Exit", -command => sub{exit}); MainLoop;
运行此脚本将生成如图 7、8 和 9 所示的 GUI 应用程序。
图 7. menubutton 小部件示例
图 8. Menubutton 小部件示例:将鼠标移动到 Button #1 上
图 9. Menubutton 小部件示例:将鼠标移动到 Button #2 上
首先,创建 menubutton 小部件,并准备向其添加命令。例如:
my $btn = $main_menu->Menubutton(-text => "Colorful Buttons...", -underline => 0, -tearoff=>0);
现在在 $btn menubutton 上创建三个命令。例如:
$btn->command(-label => "Button #1", -activebackground => "blue", -foreground => "blue", -command => sub{$mw->messageBox(-message => "Button #1 Pressed")}); $btn->command(-label => "Button #2", -activebackground => "red", -activeforeground => "black", -background => "yellow", -foreground => "green", -command => sub{$mw->messageBox(-message => "Button #2 Pressed")}); $btn->command(-label => "Exit", -command => sub{exit});
第一个命令具有蓝色背景:当该命令未突出显示时,背景保持缺省的灰色,文本为蓝色。该命令还具有活动蓝色背景:当突出显示该命令时,其背景变为蓝色,而文本则保持为缺省的灰色。当激活 menubutton 时,将出现一个消息框并声明已单击了该按钮。第二个按钮在被选择时执行类似的操作。但是,其活动背景为红色,活动前景为黑色,背景为黄色,前景为绿色。第三个命令退出脚本。
Scrollbar
scrollbar 小部件控制其他小部件的视图,例如 text 和 entry 小部件。添加此小部件使得用户可以使用滚动条上下移动目标小部件。将 scrollbar 小部件添加到应用程序是非常方便快捷的。例如:
#!/usr/bin/perl -w use Tk; use strict; my $mw = MainWindow->new; $mw->geometry("200x100"); $mw->title("Scrollbar Test"); my $scroll_text = $mw->Scrollbar(); my $main_text = $mw->Text(-yscrollcommand => ['set', $scroll_text], -background => 'black', -foreground => 'red'); $scroll_text->configure(-command => ['yview', $main_text]); $scroll_text->pack(-side=>"right", -expand => "no", -fill => "y"); $main_text->pack(-side => "left", -anchor => "w", -expand => "yes", -fill => "both"); MainLoop;
运行此脚本将生成如图 10 所示的 GUI 应用程序。
图 10. Scrollbar 小部件示例
首先创建 scrollbar 小部件。例如:
my $scroll_text = $mw->Scrollbar();
创建一个非缺省颜色的 text 小部件,并作为应用程序 y(垂直)轴上的 scrollcommand 绑定 scrollbar 小部件 ($scroll_text)。例如:
my $main_text = $mw->Text(-yscrollcommand => ['set', $scroll_text], -background => 'black', -foreground => 'red');
允许用户通过移动滚动条来控制目标小部件的移动,从而与 scrollbar 小部件交互。例如:
$scroll_text->configure(-command => ['yview', $main_text]);
包装 text 以及 scrollbar 小部件,并很好地将它们对齐。例如:
$scroll_text->pack(-side=>"right", -expand => "no", -fill => "y"); $main_text->pack(-side => "left", -anchor => "w", -expand => "yes", -fill => "both");
实际运用
现在您已经在一个脚本中集合了几个中级小部件。例如:
#!/usr/bin/perl -w use Tk; use strict; my $ver = "1.0.0"; my $mw = MainWindow->new; $mw->geometry("500x150"); $mw->title("All-In-One Demo #2"); my $main_menu = $mw->Menu(); $mw->configure(-menu => $main_menu); # give the user a way to exit the script my $file_menu = $main_menu->cascade(-label=>"File", -underline => 0, -tearoff=>0); $file_menu->command(-label=>"Exit", -underline=>0, -command => sub{exit}); # everyone needs a little help my $help_menu = $main_menu->cascade(-label => "Help", -underline => 0, -tearoff => 0); $help_menu->command(-label => "Version", -underline => 0, -command => sub{$mw->messageBox(-message => "Version: $ver", -type => "ok")}); $help_menu->command(-label => "About Program", -underline => 0, -command => \&show_about); my $greeting_frame = $mw->Frame()->pack(-side => "top"); $greeting_frame->Label(-text => "Tell me a little about yourself...")->pack(); my $info_frame = $mw->Frame()->pack(-side => "top"); my $last_name = $info_frame->Entry()->pack(-side => "right"); $info_frame->Label(-text => "Last Name")->pack(-side => "right"); my $stat = "Mr"; $info_frame->Radiobutton(-text => "Mr", -value => "Mr", -variable => \$stat)->pack(-side => "right"); $info_frame->Radiobutton(-text => "Mrs", -value => "Mrs", -variable => \$stat)->pack(-side => "right"); $info_frame->Radiobutton(-text => "Miss", -value => "Miss", -variable => \$stat)->pack(-side => "right"); my $pet_info_frame = $mw->Frame()->pack(-side => "top"); $pet_info_frame->Label(-text => "Check all pets you like?")->pack(-side => "left"); my $chk1 = "no"; my $chk2 = "no"; my $chk3 = "no"; my $chk4 = "no"; my $chk5 = "no"; my $pet1_chk = $pet_info_frame->Checkbutton(-text => "Cat", -variable => \$chk1, -onvalue => "yes", -offvalue => "no")->pack(-side => "right"); my $pet2_chk = $pet_info_frame->Checkbutton(-text => "Dog", -variable => \$chk2, -onvalue => "yes", -offvalue => "no")->pack(-side => "right"); my $pet3_chk = $pet_info_frame->Checkbutton(-text => "Fish", -variable => \$chk3, -onvalue => "yes", -offvalue => "no")->pack(-side => "right"); my $pet4_chk = $pet_info_frame->Checkbutton(-text => "Snake", -variable => \$chk4, -onvalue => "yes", -offvalue => "no")->pack(-side => "right"); my $pet5_chk = $pet_info_frame->Checkbutton(-text => "Hamster", -variable => \$chk5, -onvalue => "yes", -offvalue => "no")->pack(-side => "right"); my $button_frame = $mw->Frame()->pack(-side => "top"); $button_frame->Button(-text => "Ok", -command => \&update_output)->pack(); my $output_frame = $mw->Frame()->pack(-side => "bottom"); my $output_scroll = $output_frame->Scrollbar(); my $output_text = $output_frame->Text(-yscrollcommand => ['set', $output_scroll]); $output_scroll->configure(-command => ['yview', $output_text]); $output_scroll->pack(-side => "right", -expand => "no", -fill => "y"); $output_text->pack(); sub update_output { my $lname = $last_name->get(); if ($lname eq "") { $lname = "No Name"; } my $output = "Hello $stat. $lname!\nI like the following too!"; if ( $chk1 eq "yes" ) { $output = "$output\nCats"; } if ( $chk2 eq "yes" ) { $output = "$output\nDogs"; } if ( $chk3 eq "yes" ) { $output = "$output\nFish"; } if ( $chk4 eq "yes" ) { $output = "$output\nSnakes"; } if ( $chk5 eq "yes" ) { $output = "$output\nHamsters"; } $output_text->delete('0.0', 'end'); $output_text->insert("end", $output); } sub show_about { my $help_win = $mw->Toplevel; $help_win->geometry("300x50"); $help_win->title("About Program"); my $help_msg = "This help page is an example of using multiple windows."; $help_win->Label(-text => $help_msg)->pack(); $help_win->Button(-text => "Ok", -command => [$help_win => 'destroy'])->pack(); } MainLoop;
运行此脚本将生成如图 11 所示的 GUI 应用程序。