Updated 2010-06-29 18:30:49 by AMG

There are many frequently-asked questions about Tk that are best answered with "Don't do that." Here are some of them:

How do I move the mouse pointer where I want it?

You can use event generate -warp to force the mouse cursor to the desired location; see event(n) for details. For example,
        pack [set w [button .b -text foo]]
        event generate $w <Motion> \
            -x [expr {[winfo width $w]/2}] \
            -y [expr {[winfo height $w]/2}] \
            -warp true ;

...But don't do that.

The ICCCM says that: In general, clients should not warp the pointer. Window managers, however, may do so (for example, to maintain the invariant that the pointer is always in the window with the input focus). Other window managers may want to preserve the illusion that the user is in sole control of the pointer. (Section 6.2)

Most toolkits follow this convention (Tk included), so users are not used to having the pointer jump around without them explicitly moving the mouse (or, depending on the window manager, shifting keyboard focus to a new window with a keyboard binding.) Although a few toolkits do use pointer warping to great advantage (notably Open Look), Tk is not one of them, so if your program warps the pointer it is more likely to confuse users than help them. See also warp.

How do I force a window to stay on top of all other applications?

You could use a global grab: grab -global .confirm. See grab(n) for details.

...But don't do that.

Global grabs are very user-hostile. They prevent the user from interacting with anything else on the desktop -- are you sure that your application is the most important thing that's running? Also, when something goes wrong (when, not if), it's all too easy to lock the desktop up entirely.
   From Larry Virden on comp.lang.tcl: "Boy I hate global grabs.
   Netscape does one of these when clicking on its menus.  Unfortunately,
   the version of netscape on my system has this annoying habit of
   malfunctioning.  This leaves me in a state where I have to reboot the
   machine!!!  Nasty stuff - sure to make people hate a program the first
   time they lose work because of some silly programming error.

   I HIGHLY encourage people not to use global grabs."

On a related topic, if you just want to make sure that a dialog box stays on top of another window belonging to the same application, then wm transient .dialog toplevel will usually do the trick. This depends on the window manager though.

How do I make my window active when it first pops up?

You could use focus -force .myWindow. The focus command normally controls the keyboard focus within the application; focus -force controls the keyboard focus across the entire desktop.

...But don't do that.

This is something else that is best left to the window manager. Some users like the keyboard focus to automatically shift to new windows as they are popped up; others like the focus to stay put until they change it themselves. Good window managers let the user choose which behaviour they want, but in any case users generally expect all applications to behave the same way on startup. If your application uses focus -force on startup, you'll be violating the expectations of some users.

LV It would be nice, however, if applications provided a method for the user to specify where focus should go. I recently tried to run tkchat using the latest tclkit on a MacOS 7.5.5 system. I found that the login screen doesn't work - it doesn't have focus - and I don't seem to be able to direct focus to it ... making the program useless to me.

LES before "not doing that", remember that most Tcl'ers seem to use Unix. Despite all remarkable effort we have seen in recent years to turn Linux/Unix into a desktop environment, these OSes were not really meant to be desktop OSes when they were designed. Most Unix applications do not require focus. Whoever said "don't do that" to making new windows active, certainly uses Unix. Windows that are not active when launched may annoy Microsoft Windows users because they expect new windows to become active. I, for one, hate Tcl-based applications that don't make new windows instantly active. Heck, if I launch an application, that means I am going to use it, not just leave it running for nothing's sake. And 99% of all applications I use (at least those that do have a visible window) require focus before they are useful. Why force me to move my hand over to the mouse and click on the window to get focus and then be able to use it? That becomes particularly irritating in comparison to all other non-Tcl-based Windows apps, that give me focus automatically. In this particular case, I'd say "don't do that" to don't do that.

Don't bind essential functions only to <Button-2> or <Button-3> -- these aren't available on all mice. Provide also some alternative bindings to a combination of keyboard modifiers and <Button-1>.

"Update considered harmful"

How do I substitute a variable in a binding/callback-command?

You could use:
 proc callback s {puts S:$s}; bind . <ButtonPress-1> "callback $var"

...But don't do that.

If var were to be set like: set var "Hello \"World" You will find a bgerror when the window is clicked. The solution is to construct the command using list, like so:
 set var "Hello \"World"
 proc callback s {puts S:$s}; bind . <ButtonPress-1> [list callback $var]

If you need to invoke multiple commands you can do:
  bind ... "[list callback $var] ; [list another.callback $var]"

or, preferably, write a helper proc
  proc callbackHelper {var} { callback $var ; another.callback $var }
  bind ... [list callbackHelper $var]

See also: