Beefy Boxes and Bandwidth Generously Provided by pair Networks
Your skill will accomplish
what the force of many cannot
 
PerlMonks  

Auto-adjust row height to line wrap in Tk-Tablematrix

by elef (Friar)
on Feb 09, 2013 at 13:06 UTC ( #1017960=perlquestion: print w/ replies, xml ) Need Help??
elef has asked for the wisdom of the Perl Monks concerning the following question:

I'm using line wrap to display variable length text in Tk::TableMatrix. It works fine, lines are wrapped as needed, and the line wrap adjusts automatically when the columns are resized.
The problem is that row height can only be set manually. I can set all rows to be 2 lines in height, but I can't set them to be as high as it takes to display all text in each row. As a result I either get a bunch of text that's not visible, or an awful lot of wasted empty space.
If I were to use a monospace font and set a fixed column width, maybe I could write some code that calculates the height required for each row and sets it, but I want to avoid fixed column widths if at all possible.
Is there a solution? My googling turned up no answers except this, which is for TkTable in Tcl/Tk and I can't make much sense of it:
set maxr [.table cget -rows] set maxc [.table cget -cols] for {set r 1} {$r < $maxr} {incr r} { set maxh 1 for {set c 0} {$c < $maxc} {incr c} { if {[set n [llength [split [.table get $r,$c] \n]]]>1 && $maxh<$n +} { set maxh $n } } .table height $r $maxh }

From http://wiki.tcl.tk/1877

Comment on Auto-adjust row height to line wrap in Tk-Tablematrix
Download Code
Re: Auto-adjust row height to line wrap in Tk-Tablematrix
by BrowserUk (Pope) on Feb 09, 2013 at 14:51 UTC

    Making no claim to understanding tcl; what the code appears to be doing is:

    1. for each row in the table;
    2. query the text for each column within that row;
    3. split that text on newlines;
    4. find the maximum number of lines in any column within that row;
    5. set the row height for that row accordingly.

    SOmething roughly equivalent in Perl terms to:

    my $maxRows = $tableMatrix->cget( -rows ); my $maxCols = $tableMatrix->cget( -cols ); for my $r ( 1 .. $maxRows -1 ) { set $maxHeight = 1; for my $col ( 0 .. $maxCols - 1 ) { my $text = $tableMatrix->getText( -row => $row, -col => $col ) +; my $nLines = scalar split "\n", $text; if( $nLines > 1 and $maxHeight < $nLines ) { $maxHeight = $nLines; } } $tableMatrix->cset( row => $row, height => $maxHeight ); }

    You'll have to work out the appropriate methods to call for what I've shown as getText() & cset().


    With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.
      This seems to assume that there are \n characters in the strings. I am using -wrap => 1, to get automatic line wraps, and I don't think that it inserts \n in the string. It just displays it wrapped without altering the string itself, so I don't think my $nLines = scalar split "\n", $text; would work. But I've been wrong before...
      Of course I could perhaps query the width of each column, use a monospace font and do the line wraps myself with \n, in which case of course I would know how many lines each cell is wrapped into, and I could set the height accordingly. But that would essentially mean that I'm rewriting the functionality of -wrap from scratch. There should be a better way - to be honest, I don't really see the usefulness of -wrap if there is no height auto-adjustment functionality. Instead of hanging off the cell to the right, text just ends up hanging off the top and bottom.
        This seems to assume that there are \n characters in the strings. I am using -wrap => 1, to get automatic line wraps, and I don't think that it inserts \n in the string.

        Hm. I could be wrong, but I'd bet my bottom dollar that wrap is implemented by inserting newlines into the text.

        This part of the tcl code:if {[set n [llength [split [.table get $r,$c] \n]]]>1 && $maxh<$n} { breaks down to:

        1. Get the text from cell $r, $c: [.table get $r,$c]
        2. Split that text on newlines: [split $text \n]
        3. Calculate the list length of the result from split: [llength <list> ]
        4. Set n to that value: [set n $length ]
        5. If the number of lines is greater than one; and greater than the current value of maxh(eight), then set maxh to n;

          (Note: that first test is redundant.)

        You would be wasting your time doing font calculations yourself, cos the tablematrix must already be doing them in order to perform the wrapping.


        With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        "Science is about questioning the status quo. Questioning authority".
        In the absence of evidence, opinion is indistinguishable from prejudice.
Re: Auto-adjust row height to line wrap in Tk-Tablematrix
by elef (Friar) on Feb 19, 2013 at 11:40 UTC
    A note for posterity: John Cerney, the author of TableMatrix got back to me by email. Essentially, there is no better solution than the one outlined above (get column width and character numbers for each cell and calculate the required height based on that).

    Regarding the tcl/TkTable solution I posted above he wrote the following:
    I don't think the base TkTable widget has a better way of doing this either. I saw the link you supplied ( http://wiki.tcl.tk/1877 )
    This solution implies you can get the text for a wrapped cell, and it will have newlines (\n) where the text is wrapped. I tried this using the tcl/tk TkTable widget and it didn't work for me. So it appears there isn't really a solution for TkTable either.
    What is really needed is access to the Tk_ComputeTextLayout utility function, which is what TkTable.c uses internally to determine where to wrap a string. Unfortunately this interface is not exposed to perl.
Re: Auto-adjust row height to line wrap in Tk-Tablematrix
by elef (Friar) on Feb 21, 2013 at 18:08 UTC
    Here's the code I came up with for auto-adjusting row height in case anyone needs it. It sets the height of each row to accommodate the tallest cell in the row, based on the width of each column and the content of each cell. Of course it's an approximation so it's occasionally off by one line. You can tune it by changing the -8 value. It's reasonably fast, it reflows 5000 rows in about 2 sec.
    On another note, anyone know how I can automatically call the setheight sub when the width of a column is changed (by drag and drop)? If I could detect the change of the width I wouldn't need a separate button for this.

    #!/usr/bin/perl use strict; use warnings; use Tk; use Tk::Dialog; use Tk::TableMatrix; use utf8; sub setheight; my $mw = MainWindow->new; $mw->minsize(800, 500); # minimum size of main window in pixels + my $textfont = '-*-Courier-Medium-R-Normal--*-140-*-*-*-*-*-*'; my $arrayVar = {}; while (<DATA>) { my $i = $. - 1; /^([^\t]*)\t([^\t]*)/; $arrayVar->{"$i,0"} = "$1"; $arrayVar->{"$i,1"} = "$2"; } my $rows = $.; # print "\nrows: $rows\n"; my $cols = 2; # number of columns my $t = $mw->Scrolled('TableMatrix', -scrollbars => "osoe", -font => $textfont, -resizeborders => 'col', # none, col, row -bordercursor => "sb_h_double_arrow", -rows => $rows, -cols => $cols, -wrap => 1, -justify => 'left', # this only affects mul +tiline cells -anchor => 'nw', # this aligns text to +the top left # -rowheight => 1, -colwidth =>-400, -variable => $arrayVar, # -colstretchmode => 'all', )->pack(-expand => 1, -fill => 'both'); setheight; # initial height adjustment; height will be readjusted l +ater when necessary by calling the same sub my $buttexit = $mw->Button( -text => "Exit", -command => sub{ $mw->des +troy}); my $buttheight = $mw->Button( -text => "Set height", -command => sub { +setheight;}); $buttheight->pack(); $buttexit->pack(); $buttexit->focus( -force ); # my $cursorpos = $t->icursor(); # my $activeindex = $t->index('active'); # my $activecont = $t->get('active'); Tk::MainLoop; sub setheight { for my $i (0 .. $rows - 1) { # print "\n\nrow $i"; for my $col (0 .. $cols - 1) { $t->activate("$i,$col"); my $activecont = $t->get('active'); # print "\n$i,$col"; my $charcount = length($activecont); # print " length: $charcount"; my $width = $t->colWidth($col,) / -8; # approx width in ch +aracters my $reqheight = int(length($activecont) / $width) + 1; # print ", required height: $reqheight"; my $currheight = $t->rowHeight($i); $t->rowHeight($i, $reqheight) if ( ($col == 0) or ($reqhei +ght > $currheight)); # set row height for the height of the tallest c +ell in the row } } } __DATA__ this is a relatively long string that will probably end up displaye +d on two lines if the window isn't wide enough this is a relatively l +ong string that will probably end up displayed on two lines if the wi +ndow isn't wide enough this is not that short, eitherrr lalala)(=/%!+"'§΄&#729;`&#731;°^&# +711;~ well, this cell contains quite a lot of text so I'm expecting it will +be wrapped lalalaια&#369;ϊ&#337;νσόΦ)(=/%!+"'§΄&#729;`&#731;°^&#71 +1;~

      I know that the post is a bit old, but I have the following problem. The script works just fine, but if I change the font dimension, it fails. What can I do to have it working even with, let say:

      my $textfont = '-*-Courier-Medium-R-Normal--*-180-*-*-*-*-*-*';

Log In?
Username:
Password:

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

How do I use this? | Other CB clients
Other Users?
Others musing on the Monastery: (3)
As of 2014-08-31 08:59 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    The best computer themed movie is:











    Results (294 votes), past polls