Beefy Boxes and Bandwidth Generously Provided by pair Networks
The stupid question is the question not asked
 
PerlMonks  

I need help making loop-created buttons and entries match up...

by bblustein (Beadle)
on Mar 25, 2005 at 11:55 UTC ( [id://442308]=perlquestion: print w/replies, xml ) Need Help??

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

I'm still trying to explore TK's abilities. Right now, I'm working on an input screen that features multiple rows of "parts", with a current inventory on hand, and an additional box used for adding to the total for each part. For purposes of clarity on my intent, I'll put it this way: I'll be recieving reports of inventory on X number of parts (it will be variable by day) from different storerooms. Each one will have a random amount of each part. So I need a form that will list each part, and have a running total column, and an entry to add new qty in each time. So, here's the code:
#!/usr/bin/perl use Tk; @parts = ("11111", "22222", "33333", "44444", "55555"); @labels = (); @entries = (); @add_boxes = (); $main = new MainWindow; $i = 0; $k = 0; while ($parts[$i]) { $labels[$i] = $main->Label(-text => "$parts[$i]")->grid(-column => + '0', -row => $k); $entries[$i] = $main->Entry(-background => 'white', -width => 8)-> +grid(-column => '1', -row => $k); $entries[$i]->insert('end', '0'); $add_boxes[$i] = $main->Entry(-background => 'white', -width => 8) +->grid(-column => '2', -row => $k); $add_boxes[$i]->bind("<Return>", sub { $currentqty = $entries[$i]->get; $addqty = $add_boxes[$i]->get; $resultqty = $currentqty + $addqty; $entries[$i]->delete('0.0', 'end'); $add_boxes[$i]->delete('0.0', 'end'); $entries[$i]->insert('end', $resultqty); $main->update }); $i++; $k++; } MainLoop;
This doesn't work - the sub routine does not add the number in. What's worse, I know why it doesn't work (it evaluates $i at the time you run, and at that point, $i is not a valid value for those arrays). I just don't know how to make this work as I intend.

UPDATE: Thank you very much, merlyn and jdporter. You have given me the solution that evaded me (and as usual, seems so simple in retrospect. My hindsight remains 20/20). I thank you both for your excellent advice. <Bows>

Replies are listed 'Best First'.
Re: I need help making loop-created buttons and entries match up...
by merlyn (Sage) on Mar 25, 2005 at 14:09 UTC
    You're creating a closure on $i (with your callback), but then you change $i later in the loop. What you need to do is create a closure on something that will stay locally with that individual closure:
    for my $index (0..$#parts) { my $i = $index; # unique per loop iteration ... rest of your loop here ... }
    This causes a unique $i per iteration, and thus will be closed properly with each callback.

    -- Randal L. Schwartz, Perl hacker
    Be sure to read my standard disclaimer if this is a reply.

Re: I need help making loop-created buttons and entries match up...
by jdporter (Paladin) on Mar 25, 2005 at 14:34 UTC
    You have correctly identified the problem. The solution is to create a variable, like $i, that is "private" for each iteration through the loop. In the code below, I have created the variable $j for exactly this purpose. Note, however, that for this to work, the variable ($j) MUST be a lexical declared inside the loop. I highly recommend using strict, and making all your variables lexical — declared with my — as close the point of use as possible. As I have done here: For fun, here's how I would do things a little differently. The main gist is that you can avoid all the indexing into the @labels, @entries, and @add_boxes arrays by making the widget variables lexical in the loop, just like $j. In this case, I've saved them anyway, in the %part_widgets hash.

    Edit by castaway - added readmore tags

Re: I need help making loop-created buttons and entries match up...
by zentara (Archbishop) on Mar 25, 2005 at 16:24 UTC
    Hi, I just want to point out that you are better off designing things with hashes, instead of arrays. The hashes are easier to deal with in loops, and if you ever decide to turn it into a module, your hash is ready to turn into a namespace. Also, you should let the "-textvariables" do the hard work of updating for you. You can keep your %parts hash in a separate file too, and when you load your script, you can load the %parts hash, and another file containing $parts{$part}{'needed'} values, and they will just fill themselves in, because of the -textvariables. Look at this:
    #!/usr/bin/perl use warnings; use strict; use Tk; require Tk::Pane; my %parts = ( 11111 => { 'total' => 95, 'add' => 0, 'needed' => 2 }, 22222 => { 'total' => 100, 'add' => 0, 'needed' => 0 }, 33333 => { 'total' => 800, 'add' => 0, 'needed' => 20 }, 44444 => { 'total' => 1000, 'add' => 0, 'needed' => 25 }, 55555 => { 'total' => 10, 'add' => 0, 'needed' => 2 }, 66666 => { 'total' => 95, 'add' => 0, 'needed' => 2 }, 77777 => { 'total' => 100, 'add' => 0, 'needed' => 0 }, 88888 => { 'total' => 800, 'add' => 0, 'needed' => 20 }, 99999 => { 'total' => 1000, 'add' => 0, 'needed' => 25 }, 10101 => { 'total' => 10, 'add' => 0, 'needed' => 2 }, ); my $mw = new MainWindow; my $pane = $mw->Scrolled( 'Pane', -scrollbars => 'e', ) ->grid; #header for my $part ( keys %parts ){ $pane->Label( -text => ' part ' )->grid( -row => 0, -column => 0 ) +; $pane->Label( -text => ' total ' )->grid( -row => 0, -column => 1 ) +; $pane->Label( -text => ' needed ' )->grid( -row => 0, -column => 2 ) +; $pane->Label( -text => ' add ' )->grid( -row => 0, -column => 3 ) +; } my $row = 0; for my $part ( sort keys %parts ) { $row++; $pane->Label( -text => $part )->grid( -row => $row, -column => 0 ); $pane->Label( -textvariable => \$parts{$part}{'total'} ) ->grid( -row => $row, -column => 1 ); $pane->Label( -textvariable => \$parts{$part}{'needed'} ) ->grid( -row => $row, -column => 2 ); $parts{$part}{'entry'} = $pane->Entry( -textvariable => \$parts{$part}{'add'} ) ->grid( -row => $row, -column => 3 ); $parts{$part}{'entry'}->bind ("<Return>", sub { process_add( $part ) }); } MainLoop; sub process_add { my $part = shift; $parts{$part}{'total'} += $parts{$part}{'add'}; $parts{$part}{'needed'} -= $parts{$part}{'add'}; $parts{$part}{'add'} = 0; }

    I'm not really a human, but I play one on earth. flash japh

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://442308]
Approved by Joost
Front-paged by Courage
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others goofing around in the Monastery: (5)
As of 2024-04-25 10:52 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found