perl/tk
与Perl语言的广泛使用相比,Perl的GUI工具集Perl / Tk不太受欢迎。 这很奇怪,因为它可以说是最容易编程的GUI工具包之一(至少对于UNIX平台而言),因此建议将其自身用于用户界面原型设计或快速为用户提供围绕隐含命令行工具的用户友好包装器。
对Perl / Tk相对漠不关心的一个原因似乎是人们认为它不是很强大,并且不适合复杂的应用程序。 但是,许多提供更复杂功能的小部件都可以作为用户在CPAN上的贡献。 在本文中,我将介绍其中的一些内容,并展示如何使用它们来创建更丰富的用户体验。 我还将指出一些用于Perl / Tk编程的更普遍有用的技术。
(当然,Perl / Tk的另一个问题是它的小部件看起来很丑陋,并且不具有“主题化”。不幸的是,我对此无能为力。)
先决条件和可用性
整个过程都假定您具有Perl的丰富知识以及至少具有Perl / Tk的基本经验以及一般的GUI编程概念(事件,小部件,回调,几何管理器)。 您可以在本文的“ 相关主题”部分中找到有关Perl / Tk的入门资源。
您可以从CPAN(综合Perl存档网络)免费下载本文讨论的所有小部件。 大多数选项的选项超出了此处所能解释的范围:每个小部件的perldoc以及(在某些情况下)源代码提供了最终的完整参考。
分页框架:Tk :: NoteBook
Tab是一个常见的GUI小部件,它有助于将大量选项组合为较小的子集,从而为例如复杂的对话框添加结构。
Tk::NoteBook
具有Tab外观和Perl / Tk语义的小部件是Tk::NoteBook
。 使用三个选项卡的示例如图1所示。 请注意第三个选项卡被禁用。
图1. NoteBook小部件
此示例由以下代码产生:
清单1:使用NoteBook小部件
use Tk;
use Tk::NoteBook;
$mw = MainWindow->new();
$mw->geometry( "400x100" );
$book = $mw->NoteBook()->pack( -fill=>'both', -expand=>1 );
$tab1 = $book->add( "Sheet 1", -label=>"Start", -createcmd=>\&getStartTime );
$tab2 = $book->add( "Sheet 2", -label=>"Continue", -raisecmd=>\&getCurrentTime );
$tab3 = $book->add( "Sheet 3", -label=>"End", -state=>'disabled' );
$tab1->Label( -textvariable=>\$starttime )->pack( expand=>1 );
$tab2->Label( -textvariable=>\$raisetime )->pack( expand=>1 );
$tab3->Button( -text=>'Quit', -command=>sub{ exit; } )->pack( expand=>1 );
MainLoop;
sub getStartTime {
$starttime = "Started at " . localtime;
}
sub getCurrentTime {
$raisetime = " Last raised at " . localtime;
$book->pageconfigure( "Sheet 3", -state=>'normal' );
}
像在任何Perl / Tk应用程序中一样,我们首先指定要使用的模块,然后创建MainWindow
。 请注意, Tk::NoteBook
以及CPAN上的其他贡献的小部件都不是标准Tk分发的一部分,因此需要明确指定。
我们创建一个NoteBook
小部件作为MainWindow
子级,然后添加三个选项卡。 add()
函数的第一个参数是符号名称,通过它可以在笔记本中引用生成的页面:我们将在下面使用它。
使用NoteBook
小部件时,我应该对几何管理进行两个评论。 首先,尽管选项卡本身就是窗口小部件,但是不需要pack
它们。 它们的几何形状管理由NoteBook
处理。 其次,如果NoteBook
小部件的父窗口是可调整大小的,则在pack
NoteBook
时同时指定-fill
和-expand
属性非常重要。 后者将确保NoteBook
的分配矩形将始终扩展以填充可用空间,而前者将确保实际的NoteBook
小部件将扩展以填充其分配矩形。
选项卡接受各种属性。 在这里,我们演示-createcmd
, -raisecmd
和-state
。 前两个可用于注册回调,分别在首次创建选项卡和引发选项卡时分别调用它们; 而最后一个可以接受normal
和disabled
的值。 我们还使用了第二个选项卡上的-raisecmd
回调,以便在第一次提升第二个选项卡后将其从禁用状态切换到活动状态。 我们使用封闭的NoteBook
小部件上的pageconfigure()
函数,将引用的选项卡的符号名称作为第一个参数传递。
多功能图形显示:Tk :: ProgressBar
Tk::ProgressBar
是一个小部件,可显示标量值的图形表示。 进度条通常用于在下载大文件或执行类似的长期运行任务时向用户提供反馈。 Perl / Tk中的相应小部件提供了一些特殊功能,这些功能也可能使其对其他用途具有吸引力。
图2. ProgressBar小部件
此示例包含两个ProgressBar
小部件,它们与标准Scale
耦合。 移动滑块可通过调用名为fct
的回调来更改显示的颜色条的长度,该回调将调用ProgressBar
上的value
函数以设置新的长度。 值100
对应于可见色条的全长。 可以分别使用-to
和-from
属性来更改此值,也可以更改与零长度颜色条对应的值。
清单2展示了一些可以自定义颜色条外观的方式。 底部的ProgressBar
分为十个块,每个块之间相隔一像素的间隙。 这些是默认值。 可以通过显式指定blocks
和gap
属性的值来设置它们。 一些试验-padx
, -pady
, -length
和-borderwidth
属性一般要求。
-colors
属性接受对包含位置和颜色对的数组的引用。 (请注意,位置必须按升序排序!)最后一个指定的颜色将用于颜色栏,直到到达下一个位置为止,此时颜色将更改。 因此,定义@colors = ( 0, "red", 50, "green" );
产生一个在左侧为红色,在另一侧为绿色的颜色条。 在这里,我提供了100个不同的值,跨越了顶部ProgressBar
的彩虹色。 请注意,调整窗口大小时,顶部的ProgressBar
更改其长度。
清单2:使用ProgressBar
小部件
use Tk;
use Tk::ProgressBar;
@colors = ( 0, '#ff002a', 1, '#ff0014', 2, '#ff000a', 3, '#ff0500', 4, '#ff1000',
5, '#ff1b00', 6, '#ff3000', 7, '#ff3b00', 8, '#ff4600', 9, '#ff5100',
10, '#ff6100', 11, '#ff7600', 12, '#ff8100', 13, '#ff8c00', 14, '#ff9700',
15, '#ffa100', 16, '#ffbc00', 17, '#ffc700', 18, '#ffd200', 19, '#ffdd00',
20, '#ffe700', 21, '#fffd00', 22, '#f0ff00', 23, '#e5ff00', 24, '#dbff00',
25, '#d0ff00', 26, '#baff00', 27, '#afff00', 28, '#9fff00', 29, '#95ff00',
30, '#8aff00', 31, '#74ff00', 32, '#6aff00', 33, '#5fff00', 34, '#54ff00',
35, '#44ff00', 36, '#2eff00', 37, '#24ff00', 38, '#19ff00', 39, '#0eff00',
40, '#03ff00', 41, '#00ff17', 42, '#00ff21', 43, '#00ff2c', 44, '#00ff37',
45, '#00ff42', 46, '#00ff57', 47, '#00ff67', 48, '#00ff72', 49, '#00ff7d',
50, '#00ff87', 51, '#00ff9d', 52, '#00ffa8', 53, '#00ffb8', 54, '#00ffc3',
55, '#00ffcd', 56, '#00ffe3', 57, '#00ffee', 58, '#00fff8', 59, '#00faff',
60, '#00eaff', 61, '#00d4ff', 62, '#00c9ff', 63, '#00bfff', 64, '#00b4ff',
65, '#00a9ff', 66, '#008eff', 67, '#0083ff', 68, '#0079ff', 69, '#006eff',
70, '#0063ff', 71, '#004eff', 72, '#003eff', 73, '#0033ff', 74, '#0028ff',
75, '#001dff', 76, '#0008ff', 77, '#0200ff', 78, '#1200ff', 79, '#1d00ff',
80, '#2800ff', 81, '#3d00ff', 82, '#4800ff', 83, '#5300ff', 84, '#5d00ff',
85, '#6e00ff', 86, '#8300ff', 87, '#8e00ff', 88, '#9900ff', 89, '#a300ff',
90, '#ae00ff', 91, '#c900ff', 92, '#d400ff', 93, '#df00ff', 94, '#e900ff',
95, '#f400ff', 96, '#ff00f3', 97, '#ff00e3', 98, '#ff00d9', 99, '#ff00ce' );
$mw = MainWindow->new();
$mw->geometry( '250x150' );
$mw->resizable( 1, 0 );
$bar1 = $mw->ProgressBar( -borderwidth=>2, -blocks=>100, -gap=>0,
-troughcolor=>'white',-colors=>\@colors,
-length=>106 )->pack( -padx=>5, -pady=>5, -fill=>'x' );
$slide = $mw->Scale( -orient=>'horizontal', -length=>150,
-showvalue=>0, -tickinterval=>20 )->pack;
$bar2 = $mw->ProgressBar( -padx=>2, -pady=>2, -borderwidth=>2,
-troughcolor=>'#BFEFFF', -colors=>[ 0, '#104E8B' ],
-length=>106 )->pack;
$mw->Button( -text=>'Quit', -command=>sub{ exit } )->pack( -padx=>15, -pady=>15,
-anchor=>'se' );
$slide->configure( -command=>[ \&fct, $slide, $bar1, $bar2 ] );
MainLoop;
sub fct {
my ( $slide, $bar1, $bar2 ) = @_;
my $val = $slide->get();
$bar1->value( 100 - $val );
$bar2->value( $val );
}
我们不在Scale
小部件的configure
函数中指定Scale
小部件的回调,而是在以后使用configure
函数指定它。 原因是我们需要同时引用两个ProgressBar
作为回调的参数,并且在调用Scale
构造函数时,尚未全部定义它们。 更改小部件构造函数的调用顺序是不可能的,因为这将更改小部件的打包顺序。
此代码还演示了一种指定回调参数的方法:作为匿名列表,其中对回调的引用作为第一个元素被调用,后跟必需的参数。 如果回调不需要参数,则不需要创建匿名列表,因为Perl将在列表上下文中评估的标量值视为单元素数组。 (有关在Perl列表的详细信息,请参见学习Perl中,第二版 ,在上市的相关信息 )。我们会遇到不同的方法来发送参数如下回调; 查看补充工具栏以讨论两种方法之间的区别。
为颜色条的任何部分选择任意颜色的自由使得ProgressBar
可以用作一般的显示小部件,例如:颜色可以用于指示显示的变量是否在“正常”参数范围内。 不幸的是,该小部件不支持-orient
属性。 ProgressBar
始终水平放置。
简化的数据输入:Tk :: DateEntry和Tk :: PathEntry
Tk::DateEntry
和Tk::PathEntry
小部件通过提供有效输入的显示并允许用户从中选择来简化结构化数据的输入(分别为日期和文件路径)。
DateEntry
小部件显示带有相邻按钮的文本输入字段。 单击该按钮将在下拉菜单中显示日历,然后使用鼠标选择日期将相应的字符串输入到文本输入字段中。
图3. DateEntry小部件
清单3显示了与此示例相关的代码。 选择“ Convert
计算自Unix时代开始以来的秒数,并将其显示在文本输入字段上方的“ Label
小部件中。
清单3:使用DateEntry小部件
use Tk;
use Tk::DateEntry;
use Time::Local;
%idx_for_mon = ( JAN=>1, FEB=>2, MAR=>3, APR=> 4, MAY=> 5, JUN=> 6,
JUL=>7, AUG=>8, SEP=>9, OCT=>10, NOV=>11, DEC=>12 );
$input = '01-APR-2004'; # Initial value for display
$mw = MainWindow->new();
$mw->geometry( '200x80' );
$mw->resizable( 0, 0 );
$label = $mw->Label( -text=>'' )->pack;
$entry = $mw->DateEntry( -textvariable=>\$input, -width=>11,
-parsecmd=>\&parse, -formatcmd=>\&format )->pack;
$mw->Button( -text=>'Quit', -command=>sub{ exit } )->pack( -side=>'right' );
$mw->Button( -text=>'Convert',
-command=>sub{ convert( $input, $label ) } )->pack( -side=>'left' );
MainLoop;
# called on dropdown with content of \$textvariable, must return ( $yr, $mon, $day )
sub parse {
my ( $day, $mon, $yr ) = split '-', $_[0];
return ( $yr, $idx_for_mon{$mon}, $day );
}
# called on user selection with ($yr, $mon, $day), must return formatted string
sub format {
my ( $yr, $mon, $day ) = @_;
my %mon_for_idx = reverse %idx_for_mon;
return sprintf( "%02d-%s-%2d", $day, $mon_for_idx{ $mon }, $yr );
}
# perform the conversion to epoch seconds when the corresponding button is pressed
sub convert {
my ( $input, $label ) = @_;
my ( $yr, $mon, $day ) = parse( $input );
my $output = "Epoch seconds: " . timelocal( 0, 0, 0, $day, $mon-1, $yr-1900 );
$label->configure( -text => $output );
}
日期输入很难,因为有很多方法可以用字符串形式表示相同的日期。 DateEntry
提供了三种标准日期格式(MM / DD / YYYY,YYYY / MM / DD和DD / MM / YYYY),可以使用-dateformat
选项进行选择。 如果需要不同的日期格式,则程序员必须使用-parsecmd
和-formatcmd
回调显式提供转换例程。 在上面的示例中,我们使用自定义日期格式,使用3个字母的首字母缩写词显示月份。 在将输入字符串解析为其数字组成部分时,我们使用哈希%idx_for_mon
,该哈希%idx_for_mon
保留每个月的数字索引(1..12)(以其缩写为准)。 当用户从下拉菜单中选择日期时,必须将其格式化为相应的字符串,需要相反的查找,即给定索引的首字母缩写。 我们使用reverse
命令在format
例程中动态建立了这样的数据结构。 由于此命令需要一个数组,因此将原始哈希散列到数组中,以使值遵循数组中各自的键。 然后将此数组反转(以便现在以前的键跟随其值)并转换回哈希。 这招在这里工作,因为这两个键和值是唯一的(再次,它的更多信息,请参见学习Perl,它是在列出的相关主题 )。
convert
函数将包含输入字符串的变量以及对Label
小部件的引用作为参数,以便它可以更改Label
所示的值。 这里,我们没有将对回调的引用以及匿名数组中的参数值传递给-command
属性; 取而代之的是,我们从匿名子例程( 闭包 )中直接调用回调函数。 原因与可变范围有关。 请参阅补充工具栏以获取完整说明。
最后, Tk::PathEntry
小部件非常简单:它为路径名提供了一个文本输入字段-但要Tk::PathEntry
! 与tcsh
或Emacs迷你缓冲区的行为类似,按Tab
键将尽可能完成输入字段的内容,如果无法明确完成当前内容,则会弹出一个可能选择的列表框。 奇怪的是,列表框的颜色无法更改-除非您想编辑基础Perl模块的代码。
图4. PathEntry小部件
清单4:使用PathEntry小部件
use Tk;
use Tk::PathEntry;
use Cwd;
$path = cwd();
$mw = MainWindow->new();
$mw->geometry( '300x80' );
$mw->resizable( 0, 0 );
$mw->PathEntry( -textvariable=>\$path )->pack;
$mw->Label( -textvariable=>\$path, -foreground=>'blue' )->pack;
$mw->Button( -text=>'Quit', -command=>sub{ exit } )->pack;
MainLoop;
使PathEntry
如此有趣的原因在于它只是一个小部件,而不是一个对话框。 它可以(实际上,必须)与程序中的其他小部件组合。 因此,它提供了一种非常轻巧的方式来向应用程序添加文件选择功能。
结论
这些只是Perl / Tk GUI工具包的一些“高级”小部件,允许开发人员使用Perl创建更丰富,更强大的用户界面。 此处讨论的所有小部件都可以作为CPAN的用户贡献免费提供。
翻译自: https://www.ibm.com/developerworks/opensource/library/l-ptkwidg/index.html
perl/tk