引言
TK写界面较为复杂,且perl语言的TK模块网上资料较少,下面介绍一种使用perl语言的TK模块实现不同组件之间状态更改的方式,同时由于python的Tk模块与perl的Tk模块类似,对于python的Tk界面开发有一定参考意义。
案例
背景
公司有需求开发一个互动界面用于填写变更的文本内容,其中一个要求是点击完某个确认按钮之后已填写的文本内容就不允许修改了。
实现效果
点击同一个Frame内的CheckButton使对应Entry的状态参数转变为disabled,再次点击后使对应Entry的状态参数重新变为normal
难点
TK的各组件有绘画顺序,要使一个组件的功能可以修改另一个组件要注意绘画顺序,起码要等被修改的组件被创建出来了才能对它进行修改。
需要注意的是,当只写固定数量个组件相互影响时可以直接在创建后一个组件时添加影响前一个组件的command。
但是如果使用数组的循环,并且组件中需要使用循环中的变量时,command会失效,具体原因不明,猜测是由于Tk界面的MainLoop影响了数组循环时的变量回收机制。
# 固定组件个数时相互影响的方式
use Tk;
use strict;
my $layerFrame = new MainWindow;
$layerFrame->optionAdd("*font"=>"Times 12 bold","userDefault");
$layerFrame->geometry("+100+200");
my $modifyTextEntry = $layerFrame->Entry(
)->grid(
-row => 0,
-column => 1,
);
my $confirmValue;
my $confirmButton = $layerFrame->Checkbutton(
-variable => \$confirmValue,
-onvalue => 1,
-offvalue => 0,
-command => sub {
# 点确定时锁定不允许再更改
$modifyTextEntry->configure(
-state => ( $confirmValue == 1 ) ? "disabled" : "normal"
);
}
)->grid(
-row => 0,
-column => 2,
);
MainLoop;
# 依据数组画的问题代码,command失效
use Tk;
use strict;
my @array = (
{
modifyText => "abc",
confirmValue => 0,
} ,
{
modifyText => "abcd",
confirmValue => 0,
} ,
);
my $mw = new MainWindow;
$mw->optionAdd("*font"=>"Times 12 bold","userDefault");
$mw->geometry("+100+200");
for( my $row = 0 ; $row <= $#array ; $row++ ){
my $layerFrame = $mw->LabFrame(
-label => $row,
-labelside => "acrosstop"
)->pack(
-side =>"top",
-fill =>"both",
-expand => 1,
);
my $modifyTextEntry = $layerFrame->Entry(
-textvariable => \$array[$row]->{modifyText},
)->grid(
-row => $row,
-column => 1,
);
my $confirmButton = $layerFrame->Checkbutton(
-variable => \$array[$row]->{confirmValue},
-onvalue => 1,
-offvalue => 0,
-command => sub {
# 点确定时锁定不允许再更改
my $confirmValue = $array[$row]->{confirmValue};
$modifyTextEntry->configure(
-state => ( $confirmValue == 1 ) ? "disabled" : "normal"
);
}
)->grid(
-row => $row,
-column => 2,
);
}
MainLoop;
实现方法
主要使用Tk布局方式中的grid,在绘画完所有的组件之后,通过gridSlaves函数获取组件对象,再通过configure函数给CheckButton加上修改Entry状态的command。
通过这种方式可以解决难点中所述的数组循环导致command失效的问题。
# 绘画组件代码节选
$layerFrame->Label(
-text=> "$text =>",
-state => "normal",
-justify =>'center',
-font =>'ArialBlack 15 bold',
)->grid(
-row => $i,
-column => 0,
-sticky => 'n',
-ipadx => 1,
-ipady => 1
);
my $modifyTextEntry = $layerFrame->Entry(
-textvariable => \$syePnInfo{ $step }{ $layer }->[$i]->{modifyText},
-justify =>'center',
-font =>'ArialBlack 15 bold',
-exportselection => 1,
-takefocus => 1,
)->grid(
-row => $i,
-column => 1,
-sticky => 'n',
-ipadx => 1,
-ipady => 1
);
my $confirmButton = $layerFrame->Checkbutton(
-variable => \$syePnInfo{ $step }{ $layer }->[$i]->{confirm},
-onvalue => 1,
-offvalue => 0,
-text => dealCode("确认"),
-justify =>'center',
-font =>'ArialBlack 15 bold',
)->grid(
-row => $i,
-column => 2,
-sticky => 'n',
-ipadx => 1,
-ipady => 1
);
# 添加互动功能的代码节选
my @widget= $layerFrame->gridSlaves();
for( my $i = 0 ; $i <= $#widget ; $i++ ){
my $widget = $widget[ $i ];
my $affectWidget = $widget[ $i + 1 ];
my %info = convertGridInfo( $widget->gridInfo() );
my $widgetRow = $info{ "-row" };
my $widgetColumn = $info{ "-column" };
next if( $widgetColumn != 2 );
$widget->configure(
-command => sub {
# 点确定时锁定不允许再更改
my $confirmValue = $syePnInfo{ $step }{ $layer }->[$widgetRow]->{confirm};
$affectWidget->configure(
-state => ( $confirmValue == 1 ) ? "disabled" : "normal"
);
}
);
}
解析
关键点在于在创建完所有组件之后,再构建组件间的互动关系,使用grid可以不需自己另创哈希记录组件对象。
gridSlaves获取得到的是对象的一维数组,要判断哪个对象是想要修改的对象需要根据gridInfo函数得到对象的描述信息,比较方便的方法就是如案例所示,通过行和列来定位对象。
另外,convertGridInfo是我自己写的转换函数,实际不用也行。