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

Hi I am replacing an Access db with an MySQL backend and perl/Tk frontend, but i dont have a good idea how to replace access list-forms. So basicaly i need a way to repeat a set of widgets 1 to infinite times (in theory), once for evry row of data + once for a new row. Is there an easy way to do this? My only idea so far is a loop wich adds a frame of widgets each cycle and binding array elements to these widgets, but it feels a bit awkward to me.
UPDATE The infinity here is pure theory, in reality it will max out at 10-15 rows. The program does time accounting and i need to enter a process number, a description and the time used and i wanted to use a BroseEntry and two Entry widgets. Access has a nice feature where you only design a form for one row of data and it gets automaticaly repeated for each row of data present plus one empty row to create a new row of data.

Replies are listed 'Best First'.
Re: repeating Tk widgets for data editing
by jdtoronto (Prior) on Jul 11, 2006 at 12:58 UTC
    Woodman

    Like zentara I am not sure what you want to do either, but for displaying lists or tanbles of data in Tk there are some interesting mega-widgets, and you can make your own should you need to. One that I like is Tk::DBI::Table, just give it some SQL and it displays the data! Like this:

    use Tk; use Tk::DBI::Table; my $dbh = createDBconnection(); my $SQL = qq~SELECT * FROM mytable~; my $window = doDBItable( $dbh, $SQL, 'My display window' ); sub doDBItable { my ( $dbh, $SQL, $title ) = @_; if ( not defined $title ) { $title = "List Preview" } my $top = MainWindow->new( -title => $title ); $top->geometry("600x400+50+50"); my $tkdbi = $top->DBITable( -sql => $SQL, -dbh => $dbh, -display_id => 0, -debug => 0, )->pack( -expand => 1, -fill => 'both' ); debug("-doDBItable"); return $top; } sub createDBconnection { use DBI; my %attr = ( PrintError => 0, RaiseError => 0. ); if ( my $dbh = DBI->connect( 'DBI:mysql:mydatabase', 'username', 'pa +ssword', \%attr ) ) { return ($dbh); } else { print "Cannot open MySQL connection: " . DBI::errstr() . "\n"; return 0; } }
    Of course you can look at the code for the module to see how the mega-widget is created. I hope this is of some use, it is a LONG time since I looked at Access (basically 5 years ago when I transitioned my only client who was using it to MySQL).

    jdtoronto

    Caveat Example taken from working code but not tested, in fact it is guaranteed not to work and has hidden magic that will make your coffee go sour as well!

Re: repeating Tk widgets for data editing
by zentara (Cardinal) on Jul 11, 2006 at 12:32 UTC
    I don't really understand what you are trying to do, because I don't really know what access list-forms are. But when you say you want to repeat a set of widgets 1 to infinite times, it sets off a red flag in my mind. You can overload a Tk script with too many widgets, which can cause sluggishness. I think you would be better off explaining what the list-forms are, and looking for a "single widget" that can handle them line by line. Some possible widgets are Tk::TableMatrix , the Tk::Canvas, or maybe even a Tk::Text widget.

    As an example to the Matrix style widgets.......

    #!/usr/bin/perl use strict; use warnings; use Tk; use Tk::TableMatrix; use Tk::TableMatrix::Spreadsheet; my $top = MainWindow->new; my $arrayVar = {}; print "Filling Array...\n"; my ($rows,$cols) = (40000, 10); foreach my $row (0..($rows-1)){ $arrayVar->{"$row,0"} = "$row"; } foreach my $col (0..($cols-1)){ $arrayVar->{"0,$col"} = "$col"; } print "Creating Table...\n"; sub colSub{ my $col = shift; return "OddCol" if( $col > 0 && $col%2) ; } my $label = $top->Label(-text => "TableMatrix v2 Example") ->pack( -expand => 1, -fill => 'both'); my $t = $top->Scrolled('Spreadsheet', -rows => $rows, -cols => $cols, -width => 6, -height => 12, -titlerows => 1, -titlecols => 1, -variable => $arrayVar, -coltagcommand => \&colSub, -colstretchmode => 'last', -flashmode => 1, -flashtime => 2, -wrap=>1, -rowstretchmode => 'last', -selectmode => 'extended', -selecttype=>'cell', -selecttitles => 0, -drawmode => 'slow', -scrollbars=>'se', -sparsearray=>0 )->pack(-expand => 1, -fill => 'both'); #my $realmatrix = $t->Subwidget('scrolled'); $top->Button( -text => "Clear", -command => sub{&clear})->pack(-expand => 1, -fill => 'x'); $top->Button( -text => "Fill", -command => sub{&fill})->pack(-expand => 1, -fill => 'x'); $top->Button( -text => "Exit", -command => sub{$top->destroy})->pack(-expand => 1, -fill => 'x' +); $t->colWidth( -2 => 8, -1 => 9, 0=> 12, 4=> 14); $arrayVar->{"1,1"} = 42; Tk::MainLoop; ###################################################################### +#### sub TMRefresh { #Required input TableMatrix object. #use to force matrix to update, a code trick return if (!$_[0]); $_[0]->configure(-padx =>($_[0]->cget(-padx))); #$realmatrix->update; #$t->update; #$top->update; #$t->see("100,100"); #trick to force update? #$t->see("end"); #$t->see("1,1"); } ###################################################################### +### sub clear{ #$t->clearAll('0,0','end'); foreach my $row(1..$rows){ foreach my $col(1..$cols){ $arrayVar->{"$row,$col"} = 0; } } &TMRefresh($t); } ###################################################################### +#### sub fill{ foreach my $row(1..$rows){ foreach my $col(1..$cols){ $arrayVar->{"$row,$col"} = 1000; } } &TMRefresh($t); } ###################################################################### +#####

    I'm not really a human, but I play one on earth. Cogito ergo sum a bum
Re: repeating Tk widgets for data editing
by zentara (Cardinal) on Jul 11, 2006 at 15:37 UTC
    Hi again. Just to show you the problem you will run into with alot of widgets, test this snippet. It runs fine as is (40 rows), but change the rows to 40000, and compare it's performance to the TableMatrix::Spreadsheet snippet. ( Hint: you will get 99% cpu usage for a minute or two or more!)
    #!/usr/bin/perl use Tk; use Tk::Table; use Data::Dumper; use strict; # by pg of perlmonks.org # As you can see, it is very easy to set/get the cell values, # and modification to data is automatically reflected on screen # (you don't need to do anything). If you only want to display # data, but not editing, replace Entry with Label. my ($row, $col); #data, in your case, come from database my @cell_vars; foreach $row (0 .. 9) { my @row_vars; foreach $col (0 .. 9) { push @row_vars, $row * $col; } push @cell_vars, \@row_vars; } #presentation my $mw = MainWindow->new; $mw->geometry("600x250"); my $table = $mw->Table(-rows => 9, -columns => 9, -scrollbars => "se", -fixedrows => 1, -fixedcolumns => 1, -takefocus => 1)->pack; foreach $col (1 .. 9) { my $col_header = $mw->Button(-text => "Column " . $col); $table->put(0, $col, $col_header); } # foreach my $row (1..40000){ foreach $row (1 .. 40) { my $row_header = $mw->Button(-text => "Row " . $row); $table->put($row, 0, $row_header); foreach $col (1 .. 9) { my $cell = $mw->Entry(-width => 10, -textvariable => \$cell_var +s[$row][$col]); $table->put($row, $col, $cell); } } #manipulate data $cell_vars[5][6] = "foo"; MainLoop;

    I'm not really a human, but I play one on earth. Cogito ergo sum a bum
Re: repeating Tk widgets for data editing
by jhourcle (Prior) on Jul 11, 2006 at 15:28 UTC

    I can't help in an actual solution, as I've never worked with perl/Tk, but I have done enough MS Access work that I think I can describe what Woodman is asking for --

    In MS Access, you have a few different ways of displaying forms -- there's the typical 'one record, one page' view, and there's a form where you have a series of records one after the other that you scroll through.

    So, you design a layout that is typically significantly shorter than the full height of the screen, with the necessary fields, and then MS Access will place the header, footer, and n+1 copies of the record details. (n records, +1 to create a new record)

    This is different from the simple spreadsheet looking view, as you can specify a header, footer, events, insert sub-records, etc. (everything you can do in the normal form, just with multiple records displayed on the screen at once, with a little slider to jump between records)