【问题标题】:Perl Tkx: How to pass a variable as a parameter to a button's callbackPerl Tkx:如何将变量作为参数传递给按钮的回调
【发布时间】:2015-09-29 14:46:13
【问题描述】:

鉴于这个 Perl/Tkx 代码片段:

@itemList = ({'attrib1' => 'name1', 'attrib2' => 'value1'},
             {'attrib1' => 'name2', 'attrib2' => 'value2'});
$row = 0;
foreach $item (@itemList) {
  push(@btn_list, new_ttk__button(-text => $item->{'attrib1'}, -command => sub {do_something($item->{'attrib2'});}));
  $btn_list[-1]->g_grid(-column => 0, -row => $row);
  $row++;
}

(在实际程序中,@itemList 是从用户可编辑的配置文件中填充的。)

我确实看到了两个标记为“name1”和“name2”的按钮。但是当我点击任一按钮时,似乎传递给回调的参数总是$itemList[1]->{'attrib2'};即@itemList 数组的最后一个元素的“attrib2”。我想要的是第一个按钮调用do_something($itemList[0]->{'attrib2'} 和第二个调用do_something($itemList[1]->{'attrib2'}

请问我做错了什么,谢谢?

【问题讨论】:

  • 总是use strict; use warnings;。它将为您省去很多麻烦。

标签: perl tkx


【解决方案1】:

您在 Perl 中遇到了 for 循环的一个微妙特性。首先解决方案:在 for 循环中使用my。然后$item 将能够在您稍后在循环中声明的匿名子中创建适当的闭包。

for my $item (@itemlist) {
    push(@btn_list, new_ttk__button(
                       -text => $item->{'attrib1'}, 
                       -command => sub {do_something($item->{'attrib2'});}));
    $btn_list[-1]->g_grid(-column => 0, -row => $row);
    $row++;
}

进一步解释:Perl 隐式本地化了 for 循环的主题变量。如果您不在 for 循环中使用 my,则循环将使用包变量的本地化版本。这使您的代码相当于:

package main;
$main::item = undef;
@itemList = ({'attrib1' => 'name1', 'attrib2' => 'value1'},
             {'attrib1' => 'name2', 'attrib2' => 'value2'});
$row = 0;
foreach (@itemList) {
    local $main::item = $_;
    push(@btn_list, new_ttk__button(
        -text => $main::item->{'attrib1'},
        -command => sub {do_something($main::item->{'attrib2'});}));
    $btn_list[-1]->g_grid(-column => 0, -row => $row);
    $row++;
}
# at the end of the loop, value of $main::item restored to  undef

您的匿名 subs 仍然引用 $main::item 包变量,无论该变量在调用这些子例程时保持的值是什么,都可能是 undef

更短的解决方案:use strict

额外的概念验证。尝试猜测以下程序的输出:

@foo = ( { foo => 'abc', bar => 123 },
         { foo => 'def', bar => 456 } );

my @fn;
foreach $foo (@foo) {
    push @fn, sub { "42" . $foo->{bar} . "\n" };
}
foreach my $foo (@foo) {
    push @fn, sub { "19" . $foo->{foo} . "\n" };
}
print $_->() for @fn;

答案如下:

42
42
19abc
19def

【讨论】:

  • 啊。谢谢你,黑帮!那成功了。两个小字母和一个空格造成的差异如此之大。 :) 可以发誓我以前试过这个。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-12-12
相关资源
最近更新 更多