http://qs1969.pair.com?node_id=11140544


in reply to Re^2: Tk performance and "UpdateWrapper: Failed to create container"
in thread Tk performance and "UpdateWrapper: Failed to create container"

If this is a good example what you want to do (basically a spreadsheet), then consider using a different widget.

I built one application with the TableMatrix widget in a scrolled up/down, left/right frame. 100,000 cells was no problem -> eye-blink length of time. For operations on particular cells (like delete column, or even create new view into the database requiring yet another spreadsheet), I put in mouse a right click handler.

Back in the day in which I wrote that code, the target platform was Win XP laptops with limited memory and essentially single core P5 processor. My code ran great on that Win XP platform - much, much faster than this code.

I do remember that I did one ugly thing, I broke OO encapsulation and manipulated the TableMatrix object's storage directly. The "TableMatrix" is basically a hash table $hashptr->{"row","col"}=some string; update: $hashptr->{"row,$col"}=some value; That surprised me because I had expected an array of array as the fundamental data structure. I could write that hash table very quickly - the display of 100,000 cells on an ancient laptop was much, much faster than this code is on my more modern 64 bit machine. Probably breaking OO encapsulation is no longer necessary due to the vast increase in raw machine speeds.

My code went EOL (End of Life) years ago. But, if you are interested, I can find an archived copy from 2008 and build a demo for you.

The main idea is to look at TableMatrix which is a single widget instead of fiddling around with many thousands of widgets. (emphasis added in an update)

UPDATE: DEMO code added:

The code below has something like 100x (or even more) than the performance of the code using grid to pack in entry objects. When Perl starts, poof... the matrix is there!

This is a hack of some code from more than a decade ago. But it does demo the speed of TableMatrix.
This demo is for 1,000 rows and 20 columns: 20,000 cells. It displays almost instantly on my machine while the other code takes seconds for 70 rows.

Many details like how to dynamically re-size the number of rows displayed in the matrix screen have answers, but at the moment, my brain is unable to remember the details (geez, its been >10 years!) and the Perl I wrote a decade+ ago is not as easy to understand as the code that I write now. (Duh!).

The code below does break OO encapsulation for direct manipulation of the TableMatrix main data table internal structure - but not for any other aspects of the object (column widths, etc). This does circumvent any unwanted display updates when a massive re-organization the matrix is desired - perhaps sort a column? From memory, for a column sort, I transformed the entire hash table Matrix representation to an array of array, used an ST to sort that, then transformed back to the hash table representation. Then called my "set_col_width()" routine which I aliased to "refresh display()".

Anyway, this idea is something to consider...
To implement something like "delete column", I would use a standard Windows paradigm, like left click on a cell for selection, right click show menu with option to delete the column - not some button at the top of the GUI.

TableMatrix is a complicated critter. Plan on spending quite a few hours experimenting and fine tuning the code to get exactly what you want. Not every behavior is documented in detail (otherwise the man page would be 300 pages!).

Enjoy...

#!/usr/bin/perl use strict; # https://perlmonks.org/?node_id=11140544 use warnings; use Tk; use Tk::TableMatrix; my ($nrows, $ncolumns) = (1000, 20); my $mw = MainWindow->new(); $mw->configure(-title=> "TableMatrix Demo"); $mw->geometry("1000x400+0+0"); my $buttonbar = $mw->Frame(-bg => 'blue', )->pack(-fill => 'x'); $buttonbar->Button(-text => 'Exit', -command => sub{$mw->destroy}, )->pack(-side => 'right', -fill => 'x', -expand => 1); $buttonbar->Button(-text => 'Set Size', -command => sub{}, )->pack(-side => 'right', -fill => 'x', -expand => 1); $buttonbar->Button(-text => 'Remove Column', -command => sub{}, )->pack(-side => 'right', -fill => 'x', -expand => 1); my %MainHash; my $tMain=\%MainHash; #main data structure for the TableMatrix my $table_frame = $mw->Frame(-height=>'10',-width=>'30', -relief=>'groove',-borderwidth=>'3' )->pack(-expand=>1, -fill=>'both',-pady=>'0'); my @col_heads = map{"Col Head $_"}0..$ncolumns; ### ### Main TableMatrix object ### my $table = $table_frame->Scrolled('TableMatrix', -cols => scalar(@col_heads), -rows =>1000, #fixed number of rows!!! need to grow this dynamical +ly! #I forget how I did this, but it is possible # -width => 5, #minimum width in columns to be shown # -height => 10, #minimum number of rows to be shown - seems to limit! +! not Min!?? -titlerows => 1, -variable => $tMain, -selectmode => 'single', -state => 'disabled', # no direct editing of cells -resizeborders => 'col', -bg => 'white', -rowheight => 1, #make row display more compact.... -bd => [0,1,0,1], -justify => 'left', -drawmode => 'compatible', -wrap => 0, -relief => 'solid', -scrollbars=>'se', -exportselection =>0, )->pack(-expand =>1, -fill=>'both'); $table->rowHeight(0,2); #varies height of title row (0) $table->tagRow('title',0); $table->tagConfigure('title', -bd=>2, -relief=>'raised'); foreach my $row (0..$nrows-1) { foreach my $col (0..$ncolumns-1) { $tMain->{"$row,$col"} = "row $row col $col"; } } set_col_width($table,@col_heads); #THIS WILL CAUSE A SCREEN REFRESH! sub set_col_width{ (my $table, my @col_head) = @_; my $i=0; foreach my $col (@col_head){ $table->colWidth($i++, length($col)); } } MainLoop;
Update to Update:
I do note some differences in the Tk display. The "slider" bar now doesn't get "smaller" based upon the number of rows to scroll. That control used to compress to such a small height (based upon total number of rows) that it was hard to "get ahold of with the mouse". That is no longer true although I notice that scrolling appears to be not as smooth with say 2,000 rows.

One thing to be wary of, is that it can be hard to be absolutely sure that you have scrolled to the "end of all rows". In my application, I always added 5 blank rows to the end any data display. If you don't see at least one blank row, then you are not at the "end of data rows". These are the details that make a difference.

Replies are listed 'Best First'.
Re^4: Tk performance and "UpdateWrapper: Failed to create container"
by olgo (Acolyte) on Jan 20, 2022 at 15:24 UTC
    I have been struggling for the last couple of hours to get the column width working.
    I implemented a soft autosizing function that works sufficiently.
    What does not work, however, is when I have finished all my grids and just want to adapt the main window to cover all the TableMatrices (I have several, but the problem goes already for a single one). My legacy solution to this is to call reqwidth and then update the main window's geometry accordingly, to a certain limit. Geometry handlers should handle the rest.
    But it seems that the reqwidth reported is not the actual widths after calling ->colWidth. I guess it is some pixel/character issue.

    Appended some not working code for autosizing at the end of the above example:
    Strangely enough, the reqwidth is updated (637 without->827 with colWidth), but not to the value required to show the whole TableMatrix.
    use strict; # https://perlmonks.org/?node_id=11140544 use warnings; use Tk; use Tk::TableMatrix; my ($nrows, $ncolumns) = (10, 10); my $mw = MainWindow->new(); $mw->configure(-title=> "TableMatrix Demo"); $mw->geometry("600x800+0+0"); my $buttonbar = $mw->Frame(-bg => 'blue', )->pack(-fill => 'x'); $buttonbar->Button(-text => 'Exit', -command => sub{$mw->destroy}, )->pack(-side => 'right', -fill => 'x', -expand => 1); $buttonbar->Button(-text => 'Set Size', -command => sub{}, )->pack(-side => 'right', -fill => 'x', -expand => 1); $buttonbar->Button(-text => 'Remove Column', -command => sub{}, )->pack(-side => 'right', -fill => 'x', -expand => 1); my %MainHash; my $tMain=\%MainHash; #main data structure for the TableMatrix my $table_frame = $mw->Frame(-height=>'10',-width=>'10', -relief=>'groove',-borderwidth=>'3' )->pack(-expand=>1, -fill=>'both',-pady=>'0'); my @col_heads = map{"Col Head $_"}0..$ncolumns; ### ### Main TableMatrix object ### my $table = $table_frame->Scrolled('TableMatrix', -cols => 10, -rows =>10, #fixed number of rows!!! need to grow this dynamically +! #I forget how I did this, but it is possible # -width => 5, #minimum width in columns to be shown # -height => 10, #minimum number of rows to be shown - seems to limit! +! not Min!?? -titlerows => 1, -variable => $tMain, -selectmode => 'single', # -state => 'disabled', # no direct editing of cells -resizeborders => 'col', -bg => 'white', -rowheight => 1, #make row display more compact.... -bd => [0,1,0,1], -justify => 'left', -drawmode => 'compatible', -wrap => 1, -relief => 'solid', -scrollbars=>'se', -exportselection =>1, )->pack(-expand =>1, -fill=>'both'); foreach my $row (0..$nrows-1) { foreach my $col (0..$ncolumns-1) { $tMain->{"$row,$col"} = "row $row col $col"; } } set_col_width($table,@col_heads); #THIS WILL CAUSE A SCREEN REFRESH! sub set_col_width{ (my $table, my @col_head) = @_; my $i=0; foreach my $col (@col_head){ $table->colWidth($i++, length($col)+4); } } $mw->update; $table->update; $table_frame->update; #Needed my $rw = $table_frame->reqwidth; print "RW:".$rw."\n"; $mw->geometry($rw."x800+0+0"); MainLoop;