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

I'm trying to use Tk::Table for the first time, but I get an unwanted empty box every time I clear the table.

When I first execute the code, everything looks as expected. I see the "update" button at the top of the window and a table with 3 rows and 5 columns below the button. The table is filled with random values (0-9).

When I click the "update" button for the 1st time, the table is updated with a new set of random values, as desired. However, a small empty box also appears between the "update" button and the table. Every subsequent click of the button adds another empty box. If I click the button 10 times, I end up with 10 empty boxes stacked vertically. This resizes the window and pushes the table further down. If I click enough times, the table disappears off the bottom of my screen. The clear method works, but I need to figure out how to prevent the empty boxes from appearing.

I adapted my code from this Perlmonks node: Re: Cursor position in Tk::Table

use warnings; use strict; use Tk; use Tk::Table; my $table; my $mw = MainWindow->new(); my $upper = $mw->Frame()->pack(-side => 'top'); my $but1 = $upper->Frame()->pack(); my $lower = $mw->Frame()->pack(-side => 'bottom'); upd_table(); $but1->Button( -text => 'update', -command => sub { upd_table() }, )->pack(); MainLoop(); exit; sub upd_table { $table->clear() if $table; $table = $lower->Table( -rows => 3, -columns => 5)->pack; foreach my $row ( 0 .. 2 ) { foreach my $col ( 0 .. 4 ) { my $x = int rand 10; my $cell = $table->Entry( -width => 4, -text => $x ); $table->put( $row, $col, $cell ); } } }

I'm using the latest version of Tk, just installed:

perl -MTk -le 'print $Tk::VERSION' 804.032

Can anyone else reproduce this? Did I describe the problem clearly? I don't see any relevant open bug reports. What am I doing wrong?


P.S. As a testament to the effectiveness of Super Search, this is the first time I've hit the "submit" button on SoPW (after more than 3000 posts). I've started composing SoPW posts several times, but SS has always given me the answer before I posted.

Replies are listed 'Best First'.
Re: Tk::Table clear creates unwanted boxes
by Athanasius (Archbishop) on Aug 05, 2014 at 15:42 UTC

    Hello toolic,

    Yes, I can reproduce this exactly. And from playing around with it, I found the following, which seems to work correctly:

    #! perl use strict; use warnings; use Tk; use Tk::Table; my $mw = MainWindow->new(); my $upper = $mw ->Frame()->pack(-side => 'top'); my $but1 = $upper->Frame()->pack(); my $lower = $mw ->Frame()->pack(-side => 'bottom'); my $table = $lower->Table(-rows => 3, -columns => 5)->pack; upd_table(); $but1->Button ( -text => 'update', -command => sub { upd_table() }, )->pack(); MainLoop(); exit; sub upd_table { $table->clear() if $table; for my $row (0 .. 2) { for my $col (0 .. 4) { my $x = int rand 10; my $cell = $table->Entry(-width => 4, -text => $x); $table->put($row, $col, $cell); } } }

    That is, I just moved the line

    $table = $lower->Table(-rows => 3, -columns => 5)->pack;

    from the subroutine to the main code. No idea why this works, though. :-O

    Hope that helps,

    Athanasius <°(((><contra mundum Iustus alius egestas vitae, eros Piratica,

      No idea why this works, though
      Because the OP creates a new table every time, but the old one is not deleted (as it is referenced by its parent widget, probably), only cleared.
      لսႽ† ᥲᥒ⚪⟊Ⴙᘓᖇ Ꮅᘓᖇ⎱ Ⴙᥲ𝇋ƙᘓᖇ
        choroba, I had tried using delete on the table as well, but that was not working for me. I can not figure out where to use it in my code. If you could demonstrate (especially on my more realistic code), that would be greatly appreciated.
      ++

      As Mr. Spock would say... fascinating. Much appreciated, Athanasius. Your modification works for me as well, even on an older version of Tk. That solves the problem for my original post.

      The real reason I have the sub is that I also need the number of rows to vary each time I click the button. So, the OP was reduced too much. Thanks to your recommendation, my more realistic code below works also. I happen to know up front that the maximum number of rows is a constant (32):

      use warnings; use strict; use Tk; use Tk::Table; my $mw = MainWindow->new(); my $upper = $mw->Frame()->pack(-side => 'top'); my $but1 = $upper->Frame()->pack(); my $lower = $mw->Frame()->pack(-side => 'bottom'); my $table = $lower->Table(-rows => 32, -columns => 5)->pack; upd_table(); $but1->Button( -text => 'update', -command => sub { upd_table() }, )->pack(); MainLoop(); exit; sub upd_table { $table->clear() if $table; my $r = 3 + int rand 3; foreach my $row ( 0 .. $r-1 ) { foreach my $col ( 0 .. 4 ) { my $x = int rand 10; my $cell = $table->Entry( -width => 4, -text => $x ); $table->put( $row, $col, $cell ); } } }

      It would be helpful if another Monk could explain this behavior and suggest a better method.

        suggest a better method.

        If it was me, I would put the varying number grid on a Tk::Canvas, and just use tags to control which table is currently showing. It is a very good way to do it.

        As to your method of recreating multiple tables, the following is probably somewhat along the way to do it. You have a packing problem, because the window will want to resize with each table. Maybe you can work that out.

        #!/usr/bin/perl use warnings; use strict; use Tk; use Tk::Table; my $mw = MainWindow->new(); my $upper = $mw->Frame()->pack(-side => 'top'); my $but1 = $upper->Frame()->pack(); my $lower = $mw->Frame()->pack(-side => 'bottom'); my $table = $lower->Table()->pack; my $new_row = int rand 4; my $new_col = int rand 5; upd_table($new_row,$new_col); $but1->Button( -text => 'update', -command => sub { $new_row = int rand 4; $new_col = int rand 5; upd_table( $new_row,$new_col) }, )->pack(); MainLoop(); exit; sub upd_table { $table->packForget(); my ($rowin,$colin) = @_; $table = $lower->Table(-rows => $rowin, -columns => $colin)->pack; foreach my $row ( 0 .. $rowin-1 ) { foreach my $col ( 0 .. $colin ) { my $x = int rand 10; my $cell = $table->Entry( -width => 4, -text => $x ); $table->put( $row, $col, $cell ); } } }
        as far as making a Canvas do it, look at this.
        #!/usr/bin/perl use warnings; use strict; use Tk; #by thundergnat on comp.lang.perl.tk my $top = MainWindow->new; my $canvas = $top->Canvas->pack(-expand => 1, -fill => 'both'); my (@dots, @lines); my ($gridx, $gridy) = (5, 6); for my $y(0 .. $gridx-1){ for my $x(0 .. $gridy-1){ $dots[$x][$y] = $canvas->createOval( 25 + $x * 55, 25 + $y * 55, 35 + $x * 55, 35 + $y * 55, -fill => 'red' ); } } current($dots[0][0]); $top->bind('<Up>' => sub{ my ($point, $x, $y) = get_coords('active'); return if ($point < $gridy); draw($dots[$x][$y], $dots[$x][$y-1]); } ); $top->bind('<Down>' => sub{ my ($point, $x, $y) = get_coords('active'); return if ($point > $gridy * ($gridx - 1)); draw($dots[$x][$y], $dots[$x][$y+1]); } ); $top->bind('<Left>' => sub{ my ($point, $x, $y) = get_coords('active'); return if ($point % $gridy == 1); draw($dots[$x][$y], $dots[$x-1][$y]); } ); $top->bind('<Right>' => sub{ my ($point, $x, $y) = get_coords('active'); return unless ($point % $gridy); draw($dots[$x][$y], $dots[$x+1][$y]); } ); $top->bind('<1>' => sub{ my (undef, $x1, $y1) = get_coords('active'); my (undef, $x2, $y2) = get_coords('current'); return unless defined $x2; draw($dots[$x1][$y1], $dots[$x2][$y2]); } ); $top->bind('<BackSpace>' => sub{ return unless @lines; my ($line, $start) = @{$lines[-1]}; $canvas->delete($line); current($start); pop @lines; } ); MainLoop; sub get_coords{ my $tag = shift; my $item = $canvas->find(withtag => $tag); return unless $item; return $item->[0], ($item->[0] - 1) % $gridy, int (($item->[0] - 1) / +$gridy); } sub current{ $canvas->itemconfigure('active', -fill => 'red'); $canvas->itemconfigure($_[0], -fill => 'yellow'); $canvas->dtag('active', 'active'); $canvas->addtag('active', 'withtag', $_[0]); } sub draw{ my ($start, $end) = @_; my @start_coords = $canvas->bbox($start); my @end_coords = $canvas->bbox($end); return unless @end_coords; my @line_coords = ( ($start_coords[0] + $start_coords[2]) / 2, ($start_coords[1] + $start_coords[3]) / 2, ($end_coords[0] + $end_coords[2]) / 2, ($end_coords[1] + $end_coords[3]) / 2 ); my $line = $canvas->createLine(@line_coords, -arrow => 'last'); push @lines, [$line, $start]; current($end); } __END__

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