http://qs321.pair.com?node_id=1213064

jsteng has asked for the wisdom of the Perl Monks concerning the following question:

I have this loop. As it adds labels into a Pane, it binds <1> and <Double-1> to perform specific tasks.
my @items; for my $text ( qw( one two three four five six seven eight nine ten ) +) { my $label = $pane->Label( -text => $text, -anchor => 'w', -fg => '#000000', -bg => '#C0C0C0', -font => 'courier 20 normal', )->pack(-fill => 'x', -expand => 1); push @items, $label; $label->bind('<Button-1>' => sub { $_->configure( -bg => '#C0C0C0') for @items; #unhighlight e +verything $selectedtext = $text; $label->configure( -bg=>'#CCE8FF'); #highlight this } ); $label->bind('<Double-1>' => sub { if (${$label->cget("-font")} eq 'courier 20 normal') { $label->configure( -font => 'courier 20 bold', -fg +=>'#0000FF'); } else { $label->configure( -font => 'courier 20 normal', - +fg=>"black"); } } ); }
My problem is that, I want to put that whole loop into a function because it will be performed by different parts of the script.
my @items; InitializeList(); sub InitializeList { undef @items; for my $text ( qw( one two three four five six seven eight nine te +n ) ) { ... } }
But doing so, those Bindings dont work anymore.


Moreso, I also want to isolate the bindings into separate subroutine on their own as well to compartmentalize everything for ease of maintenance:
sub BindButton1 { } sub BindDouble1 { }
It appears I am having parameter passing problem.

Replies are listed 'Best First'.
Re: Binding within a sub
by Eily (Monsignor) on Apr 18, 2018 at 08:04 UTC

    To me it looks like you have a scope problem, because your code contains closures and I'm pretty sure that this is not intended. A closure is a subroutine that keeps access to a lexical (my) variable that it uses but that is declared somewhere else. For example, your first bind sub uses the variable $text which is declared in the for loop. In each iteration of the loop, the variable is the same (it's not a new $text) and only its value is changed, so all your bind sub actually will keep access to the same $text, meaning they may potentially (and probably) read the same value when called. To avoid this problem, you can create a new variable in each iteration of the loop, so that it isn't shared:

    for my $text ( qw( one two three four five six seven eight nine ten ) +) { my $uniq_text = $text; # Copy ... # Your code, where $uniq_text is a new variable that was never u +sed before, and noone else has access to. }

    The other issue I see is $panel and $selectedtext which aren't declared anywhere in your example, so I don't know if they are lexicals (my) or package variables (do you use strict?) but in all cases they are shared among all the bind subs, and again I don't know if this is by design or mistake.

    And I'm not sure about why you would define @items outside of your InitializeList sub, I suppose it is because you want to read its content after the call? If that's the case, maybe you can return it from the sub, rather handle the same variable in different places:

    sub InitializeList { my @items; # no need to undef it for my $text ( qw( one two three four five six seven eight nine ten +) ) ... return @items; } my @res = InitializeList(); # get the items from that call

Re: Binding within a sub
by LanX (Cardinal) on Apr 17, 2018 at 14:27 UTC
Re: Binding within a sub
by tybalt89 (Prior) on Apr 18, 2018 at 20:47 UTC

    How separate are the functions going to be?

    Are they subs in the same file, or in different files. It matters for scoping and closure issues.

    Please show a simple example that fails.

    Note that putting things into separate files may make maintenance harder rather than easier.