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

I'm using Text widgets in a TableMatrix table to display text, and I'm trying to set correct row heigths with word wrap.
Essentially, I need some way to query the Text widget and find out how many lines it's been broken up into, so that I can set the correct row height in TableMatrix. You can't query that directly, but there may be a trick that works here... I just can't find it. I can't even get some of the features of Tk::Text to work: dlineinfo and bbox fail for me with any index other than 1.0. If I could get the position of the first and last character with bbox, maybe I could calculate the height.
Here's some of my stabs in the dark:

use strict; use warnings; use Tk; use Tk::TableMatrix::Spreadsheet; my $mw = Tk::MainWindow->new; $mw->geometry('400x300'); # loaded from setup if given, defaults to + 1000x600 my %arrayVar; my $arrayVar = \%arrayVar; # Ref to hash my $t = $mw->Spreadsheet( -rows => 1, -cols => 2, -width => 16, -variable => $arrayVar, )->pack(-expand => 1, -fill => 'both');; $t->rowHeight(0, 6); $arrayVar->{"0,1"} = "some text directly inserted into the cell"; my $tw = $mw->Text( -wrap => "word"); $tw->insert("1.0", "here is some text in a Text widget; some more text + some more text some moooore text and then some more text"); $tw->tagConfigure('highlight', -background => "yellow"); $tw->tagAdd('highlight', '1.23', '1.27'); # can also use 'end' and 'l +ineend' $tw->pack(); # loose text widget for testing # $t->windowConfigure("0,0", -window => $tw); # -sticky => 's', #p +ut the Text widget in the tablematrix table my $cgetheight = $tw->cget(-height); print "height based on cget $cgetheight"; my ($x,$y,$w,$h,$b)=$tw->dlineinfo('1.0'); print "\nHeight based on dlineinfo: $h"; my $widgetheight = $tw->height; print "\nWidget height: $widgetheight\n"; my ( $width, $height, $deltax, $deltay ) = split /[+x]/, $tw->geometry +; print "Height based on geometry: $height\n"; ($x,$y,$w,$h)=$tw->bbox('1.0'); print "X: $x\n"; print "Y: $y\n"; print "W: $w\n"; print "Height based on bbox: $h\n"; MainLoop;

Replies are listed 'Best First'.
Re: Query the height of Tk::Text widget with word wrap
by Anonymous Monk on Nov 30, 2013 at 22:45 UTC
    some tips (i don't know what they are)
    #!/usr/bin/perl -- use strict; use warnings; use Tk; use Data::Dump qw/ dd pp /; my $mw = tkinit(qw/ -width 30 -height 30 /); my $t = $mw->Text->pack(qw/ -expand 1 -fill both /); $t->insert('end', q{"There must be some kind of way out of here," Said the joker to the thief. "There's too much confusion, I can't get no relief. Businessmen, they drink my wine, Plowmen dig my earth. None of them along the line Know what any of it is worth." "No reason to get excited," The thief he kindly spoke. "There are many here among us Who feel that life is but a joke. But you and I, we've been through that, And this is not our fate. So let us not talk falsely now, The hour is getting late." All along the watchtower, Princes kept the view, While all the women came and went -- Barefoot servants too. Outside in the cold distance, A wildcat did growl. Two riders were approaching, and The wind began to howl. http://www.reasontorock.com/tracks/watchtower.html }); $t->tagConfigure('yellow', -background => "yellow"); my $b = $mw->Button( -text => 'dlineinfo', -command => [ sub { my( $mw, $t ) = @_; ffd( $mw, $t, qw/ 1.20 2.12 12.30 15.28 22.16 24.20 +/); }, $mw, $t, ], )->pack( -before => $t ); $b->focus; #~ use Tk::WidgetDump; $mw->WidgetDump; MainLoop(); sub ffff { my( $mw, $t ) = @_; my $geom = $mw->geometry; my( $trooty, $theight ) = ( $t->rooty, $t->height ); my $ttaily = $trooty + $theight; "mw(geo{$geom}) trooty($trooty)+theight($theight)= $ttaily"; } sub ffd { my( $mw, $t ) = splice @_,0,2; print "\n", ffff( $mw, $t ), "\n"; for my $index( @_ ){ $t->tagAdd('yellow', "$index wordstart", "$index wordend", ); my @aline = qw/ lTx lTy wpx hpx bsT /; my @dline = $t->dlineinfo("$index wordstart"); my $theword = $t->get( "$index wordstart", "$index wordend" ); if( not @dline ){ print "($index $theword) SKIP not visible\n"; next; } printf "%-10s %-10s %5s %-5s %5s %-5s %5s %-5s %5s %-5s %5s %- +5s\n", $index, $theword, map { $aline[$_], $dline[$_] } 0 .. $#aline, ;;;;; my @bbLine = qw/ lTx lTy lTw lTh /; my( @bbLineStart ) = $t->bbox("$index linestart" ); my( @bbLineEnd ) = $t->bbox("$index lineend" ); printf "%-10s %-10s %5s %-5s %5s %-5s %5s %-5s %5s %-5s %5s %- +5s\n",, $index, 'end', ( map { $bbLine[$_], $bbLineEnd[$_] } 0 .. $#bbLine,), ('') x 2, ;;;;; printf "%-10s %-10s %5s %-5s %5s %-5s %5s %-5s %5s %-5s %5s %- +5s\n", $index, 'start', ( map { $bbLine[$_], $bbLineEnd[$_] } 0 .. $#bbLine,), ('') x 2, ;;;;; print "\n"; } #~ my $width = (200,400,600)[rand 2]; #~ $mw->geometry( $width."x$width"); } __END__
      Wow, that's written in a complicated way. As written, it uses input that is explicitly broken up into lines but dlineinfo does give meaningful info when I run it on running text with automatic word wrap:

      #!/usr/bin/perl -- use strict; use warnings; use Tk; use Data::Dump qw/ dd pp /; my $mw = tkinit(qw/ -width 30 -height 30 /); my $t = $mw->Text(-wrap=>'word')->pack(qw/ -expand 1 -fill both /); $t->insert('end', q{"There must be some kind of way out of here," Said the joker to the +thief. "There's too much confusion, I can't get no relief. Businessme +n, they drink my wine, Plowmen dig my earth. None of them along the l +ine Know what any of it is worth." "No reason to get excited," The th +ief he kindly spoke. "There are many here among us Who feel that life + is but a joke. But you and I, we've been through that, And this is n +ot our fate. So let us not talk falsely now, The hour is getting late +." All along the watchtower, Princes kept the view, While all the wom +en came and went -- Barefoot servants too. Outside in the cold distan +ce, A wildcat did growl. Two riders were approaching, and The wind be +gan to howl. http://www.reasontorock.com/tracks/watchtower.html}); # q{"There must be some kind of way out of here," Said the joker to th +e thief.}); $t->tagConfigure('yellow', -background => "yellow"); my $b = $mw->Button( -text => 'dlineinfo', -command => [ sub { my( $mw, $t ) = @_; # ffd( $mw, $t, qw/ 1.20 1.12 12.30 15.28 22.16 24.2 +0 /); ffd( $mw, $t, qw/ 1.20 1.119 1.333 1.650 /); # 1.1000 to g +et the last character }, $mw, $t, ], )->pack( -before => $t ); $b->focus; #~ use Tk::WidgetDump; $mw->WidgetDump; MainLoop(); sub ffff { my( $mw, $t ) = @_; my $geom = $mw->geometry; my( $trooty, $theight ) = ( $t->rooty, $t->height ); my $ttaily = $trooty + $theight; "mw(geo{$geom}) trooty($trooty)+theight($theight)= $ttaily"; } sub ffd { my( $mw, $t ) = splice @_,0,2; print "\n", ffff( $mw, $t ), "\n"; for my $index( @_ ){ $t->tagAdd('yellow', "$index wordstart", "$index wordend", ); my @aline = qw/ lTx lTy wpx hpx bsT /; my @dline = $t->dlineinfo("$index wordstart"); my $theword = $t->get( "$index wordstart", "$index wordend" ); if( not @dline ){ print "($index $theword) SKIP not visible\n"; next; } printf "%-10s %-10s %5s %-5s %5s %-5s %5s %-5s %5s %-5s %5s %- +5s\n", $index, $theword, map { $aline[$_], $dline[$_] } 0 .. $#aline, ;;;;; my @bbLine = qw/ lTx lTy lTw lTh /; my( @bbLineStart ) = $t->bbox("$index linestart" ); my( @bbLineEnd ) = $t->bbox("$index lineend" ); # printf "%-10s %-10s %5s %-5s %5s %-5s %5s %-5s %5s %-5s %5s +%-5s\n",, # $index, # 'end', # ( map { $bbLine[$_], $bbLineEnd[$_] } 0 .. $#bbLine, +), # ('') x 2, # ;;;;; # printf "%-10s %-10s %5s %-5s %5s %-5s %5s %-5s %5s %-5s %5s +%-5s\n", # $index, # 'start', # ( map { $bbLine[$_], $bbLineEnd[$_] } 0 .. $#bbLine, +), # ('') x 2, # ;;;;; print "\n"; } #~ my $width = (200,400,600)[rand 2]; #~ $mw->geometry( $width."x$width"); } __END__


      lTy increases by 14 pixels in every new (auto-wrapped) line:
      1.20 kind lTx 3 lTy 3 wpx 525 hpx 14 + bsT 11 1.119 relief lTx 3 lTy 17 wpx 553 hpx 14 + bsT 11 1.333 feel lTx 3 lTy 59 wpx 518 hpx 14 + bsT 11 1.650 riders lTx 3 lTy 115 wpx 553 hpx 14 + bsT 11


      So thank you, that brings me closer. I can get the number of lines by doing a ($no - 3) / 14 on an lTy value obtained from the last line. Now I need to try and implement it in Text widgets that are stuck into a TableMatrix table. I expect there could be issues (maybe dlineinfo won't return correct values if the row height isn't tall enough to fit the whole widget when the calculation is done, maybe I'll need to basically draw the table twice, once for calculation and once for display and try and hide it the first time around somehow).
        I still can't get dlineinfo to work. It works in the code you posted, but in my own code, which does the same thing as far as I can tell, it only works if the index is 1.0. I have no idea what is going on.
        use strict; use warnings; use Tk; my $mw = Tk::MainWindow->new; $mw->geometry('400x300'); my $tw = $mw->Text(-wrap=>'word')->pack(qw/ -expand 1 -fill both /); $tw->insert('end', q{"There must be some kind of way out of here," Said the joker to the +thief. "There's too much confusion, I can't get no relief. Businessme +n, they drink my wine, Plowmen dig my earth. None of them along the l +ine Know what any of it is worth." "No reason to get excited," The th +ief he kindly spoke. "There are many here among us Who feel that life + is but a joke. But you and I, we've been through that, And this is n +ot our fate. So let us not talk falsely now, The hour is getting late +." All along the watchtower, Princes kept the view, While all the wom +en came and went -- Barefoot servants too. Outside in the cold distan +ce, A wildcat did growl. Two riders were approaching, and The wind be +gan to howl. http://www.reasontorock.com/tracks/watchtower.html}); $tw->pack(); my @indexes = qw/ 1.0 1.11 1.20 /; for my $index (@indexes) { my @dline = $tw->dlineinfo("$index wordstart"); print "dline info for $index: " . join (' ', @dline) . "\n"; } MainLoop;


        dline info for 1.0: 3 3 7 1 11 dline info for 1.11: dline info for 1.20: