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

Dear Monks, I want to create a GUI allowing the user to apply several different processing steps to a string. For each step, there are some settings to adjust (in this simple example the option to export the data after the processing step). My general proble is caused by the fact, that the widgets are created in a loop. I don't know how to do specific callbacks to other widgets also created in this loop. You may understand my problem better by running this code:
#! /usr/bin/perl -w use Tk; $mw=MainWindow->new(); @type_of_processing=("type1","type2","type1","type1","type2"); $step_no=0; foreach(@type_of_processing) { $step_no++; if($_ eq "type1") { type1_settings(); } elsif($_ eq "type2") { type2_settings(); } } MainLoop; sub type1_settings { $label{$step_no}=$mw->Label(-text=>"This is processing step no. $s +tep_no. (type1)")->pack; $entry{$step_no}=$mw->Entry(-width=>20,-state=>'disabled')->pack; $checkbutton{$step_no}=$mw->Checkbutton ( -text=>"export results to: ", -variable=>\$status, -command=>sub { if($status==1) { $entry{$step_no}->configure(-state=>'normal') } else { $entry{$step_no}->configure(-state=>'disabled') } } )->pack(-before=>$entry{$step_no}); } sub type2_settings { $label{$step_no}=$mw->Label(-text=>"This is processing step no. $s +tep_no. (type2)")->pack; $entry{$step_no}=$mw->Entry(-width=>20,-state=>'disabled')->pack; $checkbutton{$step_no}=$mw->Checkbutton ( -text=>"export results to: ", -variable=>\$status, -command=>sub { if($status==1) { $entry{$step_no}->configure(-state=>'normal') } else { $entry{$step_no}->configure(-state=>'disabled') } } )->pack(-before=>$entry{$step_no}); }

Replies are listed 'Best First'.
Re: Perl::Tk Problems with creating widgets using a loop
by kcott (Archbishop) on Oct 14, 2010 at 10:30 UTC

    You're main problem here is that you're relying on the autovivification of the variable $status.

    Every instance of that variable is the same one: $main::status. Any change you make in one widget will be propagated to all of them (via -variable=>\$status).

    What you need is a separate lexical variable to be captured by the closure given by -command.

    The first thing you need to do is add use strict; to the top of your code: immediately after #!/usr/bin/perl -w

    Rerun your code and you'll get a series of warning messages highlighting what I've described above.

    The next step is to add my in front of all the variables you've just been warned about (e.g. my $mw = MainWindow->new();).

    I always find it's good practice to specify which widgets you are using so that Tk doesn't have to guess. You'll need something like:

    use Tk; use Tk::Label; use Tk::Entry; use Tk::Checkbutton;

    Basically, if you get a message like "assuming you meant XXX, then tell Tk exactly what you did mean. That way there won't be any surprises; you also get rid of a lot of annoying messages.

    You may be able to work things out from here. If not, repost your updated code and all output and we can have a further look at it.

    -- Ken

Re: Perl::Tk Problems with creating widgets using a loop
by thundergnat (Deacon) on Oct 14, 2010 at 14:56 UTC

    You (probably but not necessarily) need to have some way to keep track your widgets as they are created so you can refer back to them, and, you need to wrap your callbacks in a closure.

    #! /usr/bin/perl use warnings; use strict; use Tk; my %widgets; my $mw = MainWindow->new(); my @type_of_processing = ( "type1", "type2", "type1", "type1", "type2" + ); for my $step_no ( 1 .. 5 ) { settings( $step_no, $type_of_processing[ $step_no - 1 ] ); } MainLoop; sub settings { my ( $step, $type ) = @_; $widgets{label}{$step} = $mw->Label( -text => "This is processing step no. $step. ($type) +" )->pack; $widgets{entry}{$step} = $mw->Entry( -width => 20, -state => 'disabled' )->pack; $widgets{checkbutton}{$step} = $mw->Checkbutton( -text => "export results to: ", -variable => \$widgets{status}{$step}, -command => [ sub { if ( $widgets{status}{$step} == 1 ) { $widgets{entry}{$step}->configure( -state => 'norm +al' ); } else { $widgets{entry}{$step}->configure( -state => 'disa +bled' ); } }, $_[0] ] )->pack( -before => $widgets{entry}{$step} ); }
      THANKS ALLOT FOR YOUR HELP!!! Your comments gave me the necesary hints to solve my problem.