Version 25 of Answered Questions On: The Tk GUI

Updated 2004-10-27 06:14:06


TABLE OF CONTENTS (The Tk GUI)...

  • How Do I Maximize/Zoom A Window?
  • How Do I Enable/Disable Parts Of My GUI?
  • How Do I Detect When The Value Of An Entry/EntryField Type Widget Changes?
  • Why Do Changes I've Made To The Screen Not Appear #1?
  • Why Do Changes I've Made To The Screen Not Appear #2?
  • "Balloon Help"/"Tooltip" Type Widgets

How Do I Maximize/Zoom A Window?

(15/1/04) Hi all, does anyone know what is the TCL command for maximizing a window? I prefer not to use the wm geometry, since I wish the window to spread on the whole screen. Please send your reply to [email protected] 10x, Asi

FW: (also sent by email) Maximizing is discussed at Maximizing a toplevel window. [wm state . zoomed] is the standard command and works fine on Windows and (I think) Mac, but some window managers don't support it and you need to resize the window instead. That page provides commands that'll do it for you. If you're not targeting UNIX, of course, you can just do [wm state . zoomed] but you might as well.


How Do I Enable/Disable Parts Of My GUI?

Oct 11, 2004

Hi,

Does anyone know hot to enable/disable parts of a GUI being created in tk, based on user input on the same screen. What I mean is, I have all the frames created in a single window, and want to enable some frames as and when a user requires that option. For example, I have an option menu (tk_optionMenu) with 2 values, asking the user if he wants random numbers to be used. If he does, then I want the frame with the options of the type of random numbers to be enabled, otherwise, I want it to be disabled. Pls help. I cant seem to find anyway of doing this.

Thanks

GPS

Oct 11 2004 - MG You can watch the variable assigned to the tk_optionMenu via trace, and enable/disable your GUI when it changes. For instance...

  pack [frame .l] -side left
  pack [label .l.l -text "Random number?"] -side left
  pack [tk_optionMenu .l.m ranNum "Yes" "No"] -side left
  proc enableGUI {{args ""}} {
    global ranNum

    if { $ranNum == "Yes" } {
         # $gui configure -state normal
       } else {
         # $gui configure -state disabled
       }
  };# enableGUI
  trace add variable ranNum write enableGUI

Oct 12 2004 - Thanks alot MG. This piece of code is exactly what I needed. GPS


How Do I Detect When The Value Of An Entry/EntryField Type Widget Changes?

CT - Hi, I am having trouble with the 'entryfield' itk widget. I am trying to catch when the widgets value changes. So far I have been using the -validate command to notify when the field changes and this works fine unless you go straight into the field and paste or select some or all of the data and use the 'del' key to delete it. Somehow the -validate command is not picking this up and therefor not giving me the notification I need. I have accessed the underlying 'entry' component to use -validate all and -validatecommand but this fires an event even when I am initially setting the field which is undesirable. The -validate from the higher level of the itk widget was perfect until I discovered that it just completely ignores certain update events like pasting or using the 'del' key.

Anyone have any ideas on this please? Or any ideas on how I might be able to get a reliable notification of when the user changes the contents of the entry widget? Thanks in advance. - CT

CT * EDIT * No worries, I solved the problem by completely reworking it with a <KeyRelease> binding. I wanted to avoid this for other reasons but they were unfounded, so no problems now... - CT


Why Do Changes I've Made To The Screen Not Appear #1?

2004/09/21 pcor

Platform: Mandrake Linux 9.2

Interpretor: tixwish8.1.8.4

I have been trying to use a canvasprintdialog to print a canvas embedded in a Toplevel window but whenever the canvasprintdialog is positioned in front of the canvas I get the dialog in the printout. How can I exclude the dialog without having to position it elsewhere on the screen. I have tried hiding the dialog and using tkwait before issuing the "print" command but so far to no avail. Any help will be much appreciated. Thanks.

Peter Newman 21 September 2004: I haven't tested it, but surely:-

 ...hide the dialog ---or--- `raise canvasToplevel' ...
 update
 ...print the canvas...

would work.

pcor: Thanks Peter. Yes it does. Being a newbie with Tcl I didn't know about update.


Why Do Changes I've Made To The Screen Not Appear #2?

2004/10/22 KBH

I'm getting some behavior I don't expect and I was wondering if anyone could explain it. In the following example, I create a button and setup a binding so that it can be dragged around the window. Just before I move it to the new position, though, I move it to (0,0) so I can see what window it's over (in this case, just the main window). So I would expect it to print "." before moving the button back to the new drag location. However, it always prints ".b", as if the button were moved back before the winfo containing statement was executed. Is there something going on I don't know about?

 proc moveb { x y } {
     set lx [expr $x - [winfo rootx .]]
     set ly [expr $y - [winfo rooty .]]

     place configure .b -x 0 -y 0

     puts [winfo containing $x $y]

     place configure .b -x $lx -y $ly
 }

 wm geometry . 800x600
 place [button .b -text "Test"] -anchor c
 bind .b <B1-Motion> [list moveb %X %Y]

MG - I haven't been awake long enough to offer any code, but using a canvas widget with the button on it and moving it around that sounds like it might be a better idea. The reason it always prints .b is because, unless you click on the very last pixel at the edge of the button, moving the mouse one pixel still has the cursor over .b . If you used a canvas, you could use the find subcommand (I believe $canvas find overlapping $x $y, though might be wrong) to see which other objects/windows/widgets are at that location.

KBH - I agree that using a canvas to find the widgets underneath would be a more practical way to do what this code is attempting, but that wasn't the point of my question. I merely used this code to illustrate what seems to me to be a bug in Tk. What I expect when dragging the button is this:

  1. move the button to 0,0
  2. print the name of the window at x,y
  3. move the button back to x,y

It seems that step 1 isn't even being executed. Even more odd, if you remove the second place, the button does get moved to 0,0 (but then it just stays there, of course).

This was the real intention of my question. To ask if there were something else I hadn't realized that was causing it. And I found out what it is. Calling update just after the first place makes it work like I expected. I forgot that during callbacks the internal state isn't necessarily consistent because Tk might not have had time yet to update things.


"Balloon Help"/"Tooltip" Type Widgets

Florian Boesch 25 June 2004: The Menu widget can be used to make popup menues. What widget can be used to make small popup ( tooltip style ) text messages? And what about popup selection-lists?

Peter Newman 25 June 2004: Most Tcl'ers seem to refer to those pop-up help widgets as "balloon help". There are just over 3 zillion such widget packages available. Search for "balloon help" on the Wiki. But the ones I've tried were all way too complicated and unreliable. People seem to put zillions of binding on them, and Tk very quickly gets confused. Try some of the others. But you might be better off to roll your own. It's only about 10 lines of code. Something like (off the top of my head):-

 proc PopupHelp_forThisButton { } {
 toplevel .myPopup ;
 wm overrideredirect .myPopup          (Gets rid of the Border)
 frame .myPopup.onePixelBlackBorder -background Black -borderwidth 1
 label .myPopup.onePixelBlackBorder.text -background LightYellow -text {Blah blah blah}
 }

Bind it so that mouse entry/exit to the button/widget you want the help for, pops up the help box, with:-

 bind .thisButton <Enter> PopupHelp_forThisButton
 bind .thisButton <Leave> {destroy .myPopup}

And you'll need to position it near whatever it gives help for with winfo, etc. Sorry it's a bit crude & incomplete. But hopefully gives you the idea. Popup selection boxes are the same. But just use a listbox instead of a label.

Florian Boesch 25 June 2004: Thank you! This gives me a good idea how to go about it. I may translate a little.

 from Tkinter import *

 def coords( geometry ):
     return map( int, geometry.split( '+' )[1:] )

 class baloon( Toplevel ):
     def __init__( self, text ):
         Toplevel.__init__( self )
         Wm.overrideredirect( self, 1 )
         self.border = Frame( self, background='black', borderwidth=1 )
         self.border.text = Label( self.border, text=text )
         self.border.text.pack()
         self.border.pack()
     def set_pos( self, x, y ):
         Wm.geometry( self, '+%s+%s'%(x,y) )
     def show( self ):
         pass
     def hide( self ):
         pass

 class application( Tk ):
     def update_baloon( self, event ):
         x, y = coords(  Wm.geometry( self ) )
         self.baloon.set_pos( event.x+x, event.y+y )
     def __init__( self ):
         Tk.__init__( self )
         self.frame = Frame( self, width=512, height=512 )
         self.frame.pack()
         self.bind("<Button-3>", self.update_baloon)
         self.baloon = baloon( 'blahblubber' )

 app = application()
 app.mainloop()

This works for me. Now what I intend to do is to use this baloons to give hints about marks in my text, given that the position of a mark in the text lies within the windows borders and the user has choosen to show the baloons ( on a sidenote, can I make toplevel windows transparent? ). Also I have aproblem with this since on windows the baloon is spawned behind the main window, how to resolve this?

Peter Newman 26 June 2004: To position the balloon over the main toplevel, make the balloon toplevel a child of the main toplevel. Ie:-

 toplevel .myMainToplevel
 ...
 toplevel .myMainToplevel.balloonHelp

Alternatively, use the raise command.

As regards toplevel transparency, see Transparent Toplevel. But that technique's not really a good solution. On Windows (95 to XP inclusive) however, transparent toplevels are a piece of cake. See the absolutely brilliant "Win32 Window Skinning" tutorial by Vander Nunes at 4. Access the Win32 API with eg; Ffidl, critcl or SWIG.

An alternative to the toplevel method of creating balloon help, is to simply place the label (or text or html or canvas) widget containing your help text in the same place you'd pop-up the toplevel. It's faster and simpler to code than the toplevel - and better still is linked to the parent widget it's been placed in/over. In other words, if you move or minimise etc your text widget, the help popup does the same with it. And since you can place more than one widget anywhere you like over your main widget, your help pop-up is effectively a transparent toplevel.


I'm running a GUI to test some hardware. The GUI takes input from a touchscreen. Several buttons on the GUI control the action. All tests work fine, but there was a problem during an extreme temperature test. I think this has exposed a weakness in the button.tcl script.

The GUI contains a spinwheel, and 2 separate buttons that can also control the wheel.

This is the button code:

 button $a.prev -text "Prev" -command "$a.spin invoke buttondown"

This is the error message that pops up:

 expected boolean value but got ""
 expected boolean value but got ""
 while executing
 7        "if {[info exists Priv($w,relief)]} {
 8            if {[info exists Priv($w,prelief)] && \
                    $Priv($w,prelief) eq [$w cget -relief]} {
 9                $w configure -relief $Priv($w,relief)
 10            }
 11           unset -nocomplain Priv($w,relief) Priv($w,prelief)
 12        }
 "
 (procedure "tk::ButtonUp" line 8)

This code stems from button.tcl (tcl 8.4).

My theory is that the error is caused by the $Priv($w,prelief) expression, which is not defined (see below for explanation), and the fact that the expression is part of an if{} statement, where all expressions are evaluated before decision making. I know tcl is supposed to use a lazy &&, but only if the expression is enclosed in {} and the {} that is used here seems to belong to the if{} syntax.

My questions:

  1. Does this make sense? Would the reported error be caused by something like this?
  2. Since button.tcl is part of the tcl/tk package, who is going to fix this?

To fix this I would just break up the compound if into two statements, instead of using the &&.

Explanation for prelief not defined - I think our low temperature test on the touch screen caused the button down not to be sent out. The button down routine in button.tcl is the place where the prelief is actually set.

Thanks, Matthias

RS: && is short-circuit evaluated (e.g. stopped after the first 0) in if too, as can easily be tested:

 % if {[info exists Utopia] && $Utopia eq "here"} {puts yes!}

Also, the rror message is not about a missing variable, but "" as illegal Boolean value - but info exists returns 1 or 0. So, the error is a bit mysterious. Also, the second info exists test is certainly redundant.

Matthias: Thanks for the reply! I figured I'm getting the illegal Boolean, because the info exists failed, the var didn't exist and then an eq was attempted on the non-existing var. But if && is indeed lazy, even without the extra {} then that theory doesn't work out. Further testing showed another funky error, where a var seemed to disappear in the middle of a function, i.e. it got defined at the top, and a subsequent access couple lines later failed. I guess our memory chips don't do too well in extreme temperatures.


2004/02/11 sheila

I want to make a small map of the entire region of a GUI, with a box around the area that is currently in the area the user is looking at. I want it to update whenever the user pans to another area. I want the user to be able to pan on the big section, or pan on the small section. I've seen UIs do this before, but I'm not sure which key words to search on. Where do I look to learn about this?

GPS: I think I know what you are saying. Ghostview has a widget like this, and I believe Xaw has a builtin for such a thing (Panner?). This could probably be done with two canvas widgets. I'll try to come up with some code to demonstrate today that uses code from Getting the Canvas View Area in Pixels.

GPS: See: Panner