perl/tk_在Perl / Tk中使用高级小部件

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小部件

此示例由以下代码产生:

清单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 。 前两个可用于注册回调,分别在首次创建选项卡和引发选项卡时分别调用它们; 而最后一个可以接受normaldisabled的值。 我们还使用了第二个选项卡上的-raisecmd回调,以便在第一次提升第二个选项卡后将其从禁用状态切换到活动状态。 我们使用封闭的NoteBook小部件上的pageconfigure()函数,将引用的选项卡的符号名称作为第一个参数传递。

多功能图形显示:Tk :: ProgressBar

Tk::ProgressBar是一个小部件,可显示标量值的图形表示。 进度条通常用于在下载大文件或执行类似的长期运行任务时向用户提供反馈。 Perl / Tk中的相应小部件提供了一些特殊功能,这些功能也可能使其对其他用途具有吸引力。

图2. ProgressBar小部件
图2. ProgressBar小部件

此示例包含两个ProgressBar小部件,它们与标准Scale耦合。 移动滑块可通过调用名为fct的回调来更改显示的颜色条的长度,该回调将调用ProgressBar上的value函数以设置新的长度。 值100对应于可见色条的全长。 可以分别使用-to-from属性来更改此值,也可以更改与零长度颜色条对应的值。

清单2展示了一些可以自定义颜色条外观的方式。 底部的ProgressBar分为十个块,每个块之间相隔一像素的间隙。 这些是默认值。 可以通过显式指定blocksgap属性的值来设置它们。 一些试验-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::DateEntryTk::PathEntry小部件通过提供有效输入的显示并允许用户从中选择来简化结构化数据的输入(分别为日期和文件路径)。

DateEntry小部件显示带有相邻按钮的文本输入字段。 单击该按钮将在下拉菜单中显示日历,然后使用鼠标选择日期将相应的字符串输入到文本输入字段中。

图3. 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小部件
清单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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值