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

Fellow Monks,

I have somewhat of a dillema with how to pack a Text widget into my Tk program. Let's say that I have a very simple GUI with a Text widget on top and a Frame on the bottom. The text widget must contain X characters of text at font Y and the window can be resized to size Z. In other words, the text in the Text widget can span a varying number of lines. What I'd like to do is to make the Text widget only as large as I need to given the number of lines that are in the widget at the time. So, what I have so far is:

my $t = $mw->Text(-height=>2, -font=>'helvetica 12')->pack(-side=>'top +', -expand=>'1', -fill=>'both'); $t->insert('end', "Blah " x 10); my $f = $mw->Frame(-background=>"#FF0000")->pack(-side=>'top', -expand +=>'1', -fill=>'both');

The problem that I have is that I need to make the Text widget large enough at all times to display all of the lines of text, even if the window is resized so that the text wraps around to, in this example, 3rd line. At the same time, I don't want the Text box to be any more lines than it has to be (and if possible not even expand when the window is resized and the text can fully fit in the alloted number of lines).

Basically, what I want is the ability to catch the MW_RESIZE events and use that as the trigger to get the line number of the 'end' character in the Text widget, so I could then configure the height accordingly. Is this possible in Tk?

Replies are listed 'Best First'.
Re: Tk:Text resize dillema
by eserte (Deacon) on Dec 19, 2006 at 22:18 UTC
    Sounds difficult. For trapping resize events (and other window manager-related events like exposing) you can bind the <Configure> event. You have to be careful when setting the size of the text widget: if geometry propagation is turned on (which by default is), then any change to the child windows will change the size of the toplevel window, which in turn could cause a resize (if your geometry calculation were not exact) and this would again cause a <Configure> event and so on.

    The other difficult part is determining the total line number or the xy position of the last character in the text widget. It's easy if the text is completely visible. In this case you can use a method like dlineinfo or bbox. But if the end of text is invisible because it's larger than the widget, then I don't know of any way to get information about the end.

      Thanks for the reply. I've played around with the dlineinfo() method, but it returns information in Pixels. How do I convert from pixels to actual width of characters and height of lines?
        You can use the ascent/descent information from the fontMetrics call. Of course, this does not help if you have embedded images or widgets in your text widget.
Re: Tk:Text resize dillema
by zentara (Cardinal) on Dec 20, 2006 at 18:51 UTC
    Hi again, I'm not sure exactly what you are trying to do, but the thought just occured to me, that if you really want scalable text, you might be better off using Tk::Zinc. It will let you scale text (which most other Toolkits don't allow). Now the example below is very simplistic, BUT you could work out an automatic sizing sub, where if you are given the pixel dimensions of the entire box, you could keep scaling the text, and checking it's bbox values, until it just fit into the display box. This is all frought with difficulties, like some fonts look ugly when scaled, etc. Anyways, left-click will zoom, right-click will shrink.
    #!/usr/bin/perl use warnings; use strict; use Tk; use Tk::Zinc; my $mw = MainWindow->new(); $mw->geometry($mw->screenwidth . 'x' . $mw->screenheight . '+0+20'); $mw->title("Text Scale Test"); my $zinc = $mw->Zinc( -height => $mw->screenheight, -width => $mw->screenwidth, -backcolor => 'black', -relief => 'raised' )->pack(); $zinc->fontCreate( "fonta", -family => 'arial', -size => -30, -weight => 'bold' ); my $text = $zinc->add('text',1, -position => [$mw->screenwidth/2 , 75 ], -text => 'foobar foobaz', -composescale => 1, -anchor => 'center', -color => 'hotpink', -font => 'fonta', ); $mw->bind( '<ButtonPress-1>', sub { &move } ); $mw->bind( '<ButtonPress-3>', sub { &move1; } ); #my $id = $mw->repeat(75, sub{ &move } ); MainLoop; sub move { # $zinc->translate($text,0, 1); $zinc->scale( $text, 1.1, 1.1); } sub move1 { # $zinc->translate($text,0, 1); $zinc->scale( $text, .9, .9); } __END__

    I'm not really a human, but I play one on earth. Cogito ergo sum a bum
Re: Tk:Text resize dillema
by zentara (Cardinal) on Dec 20, 2006 at 12:58 UTC
    I'd like to do is to make the Text widget only as large as I need to given the number of lines that are in the widget at the time.

    I don't think that should be too hard, since the text widget's height and width options are in terms of lines and character width. If you say -height=>4, -width=>25, that is 4 lines and 25 characters.

    I havn't tried a $mw configure yet, but as esserte said, you may run into an infinite feedback loop if you try to reconfigure the $text widget as the $mw is being resized. You might be able to work out a flag, that gets set when the $mw resize starts, and is unset on the Button-1 Release. Then do your $text resize after the $mw resize is finished.

    But back to the point of pixel size -vs -character size, look at this script, you don't need to worry about pixels. I didn't adjust for line width, but all you would need to do is loop through each line, take it's 'length' then reconfigure your $text widget -width option to the max length.

    P.S. I guess there would be some trouble if you went to a very small window size, and you needed to search for a font that would fit and still be legible. All systems don't have the same fonts installed, so that is another problem.

    #!/usr/bin/perl use warnings; use strict; use Tk; my $mw = tkinit; $mw->fontCreate('big', -family=>'courier', -weight=>'bold', -size=>int(-18*18/14)); my $tx = $mw->Text( -height => 4, #initialy set it small -width => 25, )->pack(); foreach (1 .. 20 ) { $tx->insert( 'end', "line$_\n" ); } $tx->insert('end', "Blah " x 10); my $end = $tx->index('insert'); #get last line $tx->configure(-height=>$end); my $button = $mw->Button(-text=>'Reconfigure', -command=>sub{ $tx->configure(-font => 'big'); $tx->update; $tx->configure(-height => $end); }, )->pack(); MainLoop;

    I'm not really a human, but I play one on earth. Cogito ergo sum a bum