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

I am using Perl Tk to build a short database of contact information which I have stored in a hash of arrays. See code below please. Everything works fine except for the part related to traversing my records. While my next and previous buttons are fine, my records are not processed properly. Only one field is updated! What am I missing? I admit my script is very clunky. Any suggestions are very much welcome. Thanks a lot guys.

Tk maestroes out there, a tk database book would be a great seller. I know of a number of people, including myself, who would pay anything for a book entitled SQLite and Tk! Any volunteers:}

use strict; use Tk; use Tie::IxHash; tie %hash, "Tie::IxHash"; my $i=1; my %hash =( 1=>["Sappy", "Jewell", "2515 E Princeton", "Visalia", "CA" ], 2=>["Roomy", "Hilton", "12803 Westledge Ln", "Fifa", "Transvania"], 3=>[ "Lala", "Deena", "423 Northland Street", "St. Louis", "Misery" ], 4=>[ "LOLO", "Homy", "444 Wold City", "Foxy", "Flowerida" ], ); my @fields = qw/First_Name Last_Name Address City State /; my $mw=MainWindow->new (-title=>"Data Test"); my $rec_no=keys %hash; my $label = $mw->Label(-text=>"$array[$i]"); $label->pack; my $t=$mw->Scrolled("Text",-width=>50,-wrap=>'none')->pack(-expand=>1, +-fill=>'both'); $w=$t->Label(-text=>"$fields[0]",-relief=>'groove',-width=>20); $t->windowCreate('end',-window=>$w); $w=$t->Entry(-width=>20,-textvariable=>\$hash{$i}[0]); $t->windowCreate('end',-window=>$w); $t->insert('end',"\n"); $w=$t->Label(-text=>"$fields[1]",-relief=>'groove',-width=>20); $t->windowCreate('end',-window=>$w); $w=$t->Entry(-width=>20,-textvariable=>\$hash{$i}[1]); $t->windowCreate('end',-window=>$w); $t->insert('end',"\n"); $w=$t->Label(-text=>"$fields[2]",-relief=>'groove',-width=>20); $t->windowCreate('end',-window=>$w); $w=$t->Entry(-width=>20,-textvariable=>\$hash{$i}[2]); $t->windowCreate('end',-window=>$w); $t->insert('end',"\n"); $w=$t->Label(-text=>"$fields[3]",-relief=>'groove',-width=>20); $t->windowCreate('end',-window=>$w); $w=$t->Entry(-width=>20,-textvariable=>\$hash{$i}[3]); $t->windowCreate('end',-window=>$w); $t->insert('end',"\n"); $w=$t->Label(-text=>"$fields[4]",-relief=>'groove',-width=>20); $t->windowCreate('end',-window=>$w); $w=$t->Entry(-width=>20,-textvariable=>\$hash{$i}[4]); $t->windowCreate('end',-window=>$w); $t->insert('end',"\n"); $next =$mw->Button ( -relief=>'raised', -text=>"Next >>", -state=>'nor +mal',-command=>\&next ); $back =$mw->Button(-relief=>'raised', -text=>"<< Back", -state=>'norma +l',-command=>\&back ); &process_rows(); MainLoop; sub process_rows{ if ($i ==1){ $back->configure(-text=>"Back", -state=>'disabled',-command=>\&back); $next ->configure(-text=>"Next >>", -state=>'normal',-command=>\&next +); } elsif ($i == $rec_no){ $next->configure(-text=>"Next", -state=>'disabled',-command=>\&next); $back->configure(-text=>"Back", -state=>'normal',-command=>\&back); } else{ $next->configure(-text=>"Next", -state=>'normal',-command=>\&next); $back->configure(-text=>"Back", -state=>'normal',-command=>\&back); } for my $k(0..4){ $label->configure(-text=>"$i"); $label->pack; $w->configure(-textvariable=>\$hash{$i}[$k]); $t->configure(); $back->pack(-anchor=>'center', -side=>'left'); $next->pack(-anchor=>'center', -side=>'right'); } } sub next{ $i++ unless $i == $rec_no; &process_rows(); } sub back{ --$i unless $i == 1; &process_rows(); }

Replies are listed 'Best First'.
Re: Hash of Arrays using Perl::Tk
by davidj (Priest) on Dec 01, 2004 at 03:38 UTC
    Your problem is this: in your sub process_rows you are assigning the values to $w and $w refers to the label holding the state value. Actually all the values in each array are getting (re)assigned to it. The solution is to have a unique name for each of the labels. You can do it using an array. See my modification below (and notice that each label is identified by an array element of @w:
    use Tk; use Tie::IxHash; tie %hash, "Tie::IxHash"; my $i=1; my @w; #array to hold labels my %hash =( 1=>["Sappy", "Jewell", "2515 E Princeton", "Visalia", "CA" ], 2=>["Roomy", "Hilton", "12803 Westledge Ln", "Fifa", "Transvania"], 3=>[ "Lala", "Deena", "423 Northland Street", "St. Louis", "Misery" ], 4=>[ "LOLO", "Homy", "444 Wold City", "Foxy", "Flowerida" ], ); my @fields = qw/First_Name Last_Name Address City State /; my $mw=MainWindow->new (-title=>"Data Test"); my $rec_no=keys %hash; my $label = $mw->Label(-text=>"$array[$i]"); $label->pack; my $t=$mw->Scrolled("Text",-width=>50,-wrap=>'none')->pack(-expand=>1, +-fill=>'both'); $w[0]=$t->Label(-text=>"$fields[0]",-relief=>'groove',-width=>20); $t->windowCreate('end',-window=>$w[0]); $w[0]=$t->Entry(-width=>20,-textvariable=>\$hash{$i}[0]); $t->windowCreate('end',-window=>$w[0]); $t->insert('end',"\n"); $w[1]=$t->Label(-text=>"$fields[1]",-relief=>'groove',-width=>20); $t->windowCreate('end',-window=>$w[1]); $w[1]=$t->Entry(-width=>20,-textvariable=>\$hash{$i}[1]); $t->windowCreate('end',-window=>$w[1]); $t->insert('end',"\n"); $w[2]=$t->Label(-text=>"$fields[2]",-relief=>'groove',-width=>20); $t->windowCreate('end',-window=>$w[2]); $w[2]=$t->Entry(-width=>20,-textvariable=>\$hash{$i}[2]); $t->windowCreate('end',-window=>$w[2]); $t->insert('end',"\n"); $w[3]=$t->Label(-text=>"$fields[3]",-relief=>'groove',-width=>20); $t->windowCreate('end',-window=>$w[3]); $w[3]=$t->Entry(-width=>20,-textvariable=>\$hash{$i}[3]); $t->windowCreate('end',-window=>$w[3]); $t->insert('end',"\n"); $w[4]=$t->Label(-text=>"$fields[4]",-relief=>'groove',-width=>20); $t->windowCreate('end',-window=>$w[4]); $w[4]=$t->Entry(-width=>20,-textvariable=>\$hash{$i}[4]); $t->windowCreate('end',-window=>$w[4]); $t->insert('end',"\n"); $next =$mw->Button ( -relief=>'raised', -text=>"Next >>", -state=>'nor +mal',-command=>\&next ); $back =$mw->Button(-relief=>'raised', -text=>"<< Back", -state=>'norma +l',-command=>\&back ); &process_rows(); print Dumper(%hash); MainLoop; sub process_rows{ if ($i ==1){ $back->configure(-text=>"Back", -state=>'disabled',-command=>\ +&back); $next ->configure(-text=>"Next >>", -state=>'normal',-command= +>\&next); } elsif ($i == $rec_no){ $next->configure(-text=>"Next", -state=>'disabled',-command=>\ +&next); $back->configure(-text=>"Back", -state=>'normal',-command=>\&b +ack); } else { $next->configure(-text=>"Next", -state=>'normal',-command=>\&n +ext); $back->configure(-text=>"Back", -state=>'normal',-command=>\&b +ack); } for my $k (0..4){ $label->configure(-text=>"$i"); $label->pack; print $hash{$i}[$k] . "\n"; $w[$k]->configure(-textvariable=>\$hash{$i}[$k]); $t->configure(); $back->pack(-anchor=>'center', -side=>'left'); $next->pack(-anchor=>'center', -side=>'right'); } } sub next{ $i++ unless $i == $rec_no; &process_rows(); } sub back{ --$i unless $i == 1; &process_rows(); }
    Hope this helps,
    davidj

      His code does not work, a well as yours. Other than that my is needed for quite a few places, if you search his code, $array appeared only once, and it is on the rght side of some assignment.

        You are incorrect. My code works. First, if you notice I removed use strict;, so my is not needed. Second, with the modifications I made, it runs perfectly on my system.
        davidj
      Davidj, Thank you for fixing the code. It does work in fact. I only had to add "use Data::Dumper" for it to work without errors, which I am assuming was added for debugging purposes. Thank you for alerting me to the array of labels I should have used. The original code compiled just fine on my Windows machine, even though I am using use strict. I believe it has to do with my IDE (PerlBuilder) which does not throw errors sometimes. I really appreciate your help.
Re: Hash of Arrays using Perl::Tk
by graff (Chancellor) on Dec 01, 2004 at 05:00 UTC
    I think davidj is onto something there, but his version of your code won't work any better than your original, because there are too many other mistakes that he didn't try to fix.

    You say "everything works fine except..." but the code you posted doesn't actually compile -- you're using a bunch of variables that have not been declared with "my", including "@array", whose "$i'th" element (which is undefined) is being assigned as text for a Label widget, and $w (or @w in davidj's version), which is being assigned two different widgets; you "tie %hash" and then later declare "my %hash" (and assign values).

    How about you post the code that actually runs? Also, look at the four blocks of 5 lines each, where the only difference from one block to the next is an array index. That's where a "for" loop comes in real handy -- saves a lot of repetitive typing (or copy/paste/editing)

    If your database will be using actual key fields (rather than just numbers like 1..4) then keep using %hash -- but if you're just going to be handling records by means of a sequenctial record number, use an array-of-arrays.

    Reconfiguring the "normal" and "disabled" states of the "Next" and "Last" buttons can be done a lot more simply -- e.g.:

    my $nxtState = my $prvState = 'normal'; if ( $i == $first_index ) { $prvState = 'disabled'; } elsif ( $i == $last_index ) { $nxtState = 'disabled'; } $next->configure(-state => $nxtState ); $back->configure(-state => $prvState );
      Your advice is well taken. I have substituted my part of code which deals with reconfiguring the normal and disabled states of the back and next buttons. And It's working beautifully. The one thing I am still struggling with is tying my hash so that it displays in the original order of the data structure. It seems that the ordering only affects the keys of the hash of hashes not the element keys which store anonymous arrays. Any way around that? This is just beyond my poor human grasp. Thanks for all your help.
      What other mistakes are you refering to? As I replied to another monk above, my code runs perfectly with the modifications I made. Run it and you will see. I agree that his design is not ideal, but with the corrections I made it does work as he wants it to. My intention was not to redesign what he has, but to fix the error that generated the incorrect output.
      davidj
        Well, yes, removing "use strict" is one way to "fix" the compilation errors I referred to, and I failed to notice that your version had taken this approach.

        Of course, this is usually not the approach that we would want to recommend for people who are having trouble getting their code right.

Re: Hash of Arrays using Perl::Tk
by si_lence (Deacon) on Dec 01, 2004 at 08:24 UTC
    As others have pointed out there are some problems with
    your code. But here is a version that seems to work..
    use strict; use warnings; use Tk; use Tie::IxHash; my %hash; my @w; my $i=1; tie %hash, "Tie::IxHash"; %hash =( 1=>["Sappy", "Jewell", "2515 E Princeton", "Visalia", "CA" ], 2=>["Roomy", "Hilton", "12803 Westledge Ln", "Fifa", "Transvania"], 3=>[ "Lala", "Deena", "423 Northland Street", "St. Louis", "Misery" ], 4=>[ "LOLO", "Homy", "444 Wold City", "Foxy", "Flowerida" ], ); my @fields = qw/First_Name Last_Name Address City State /; my $mw=MainWindow->new (-title=>"Data Test"); my $rec_no=keys %hash; my $label = $mw->Label(-text=>"dummy_label"); $label->pack; my $t=$mw->Scrolled("Text",-width=>50,-wrap=>'none')->pack(-expand=>1, +-fill=>'both'); for my $index (0..4) { $w[$index]=$t->Label(-text=>"$fields[$index]",-relief=>'groove',-w +idth=>20); $t->windowCreate('end',-window=>$w[$index]); $w[$index]=$t->Entry(-width=>20,-textvariable=>\$hash{$i}[$index]) +; $t->windowCreate('end',-window=>$w[$index]); $t->insert('end',"\n"); } my $next =$mw->Button ( -relief=>'raised', -text=>"Next >>", -state=>' +normal',-command=>\&next ); my $back =$mw->Button(-relief=>'raised', -text=>"<< Back", -state=>'no +rmal',-command=>\&back ); &process_rows(); MainLoop; sub process_rows{ $next->configure(-text=>"Next", -state=>'normal',-command=>\&next) +; $back->configure(-text=>"Back", -state=>'normal',-command=>\&back) +; $back->configure(-text=>"Back", -state=>'disabled',-command=>\&bac +k) if $i == 1; $next->configure(-text=>"Next", -state=>'disabled',-command=>\&nex +t) if $i == $rec_no; for my $k(0..4){ $label->configure(-text=>"$i"); $label->pack; $w[$k]->configure(-textvariable=>\$hash{$i}[$k]); $t->configure(); $back->pack(-anchor=>'center', -side=>'left'); $next->pack(-anchor=>'center', -side=>'right'); } } sub next{ $i++ unless $i == $rec_no; &process_rows(); } sub back{ --$i unless $i == 1; &process_rows(); }
    There still is room for improvement ;-)
    si_lence
Re: Hash of Arrays using Perl::Tk
by zentara (Cardinal) on Dec 01, 2004 at 12:54 UTC
    I know of a number of people, including myself, who would pay anything for a book entitled SQLite and Tk! Any volunteers:}

    I wrote a general purpose rolodex frontend to sqlite(or postgres or mysql) with Tk.

    Check out ztkdb-sql

    Or if you want to just stick with Storable: ztkdb


    I'm not really a human, but I play one on earth. flash japh
Re: Hash of Arrays using Perl::Tk
by zentara (Cardinal) on Dec 01, 2004 at 13:44 UTC
    It may be slightly OT, but a mini-tutorial on sqlite and c,c++, and perl was just recently announced. sqlite-examples

    Or tutorial.html


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