Grab Tips

grab is one of those really awkward commands that lets you do things that are extremely difficult to handle otherwise, but which can do tremendous damage to your application or even to your session/GUI/OS (when you're using global grabs.) It is probably better style to not use grab if at all possible - it isn't always possible to avoid it of course, but that doesn't mean you want to go looking for trouble!

The simplest technique is when you have a dialog box whose code deletes the dialog on exit. Making that dialog modal (so it must be responded to before continuing to use the rest of the appication) is trivial. Just do a [grab] on the dialog's toplevel immediately before you do [tkwait window] and everything will work out fine so long as you make sure that the window is mapped to the screen before you ask for the grab. [tkwait visibility] is of great use there.

And the reason the above scheme is so effective? Deleting a window automatically releases any grabs it holds...

Next, if you are releasing a grab on a window without deleting it, [grab release] will do the business. The only tricky bit is making sure that you call it, no matter how you exit the code...

Debugging global grabs is a complete PITA. They are one of the few ways that an application can genuinely and completely lock a user's session up. Don't use them unless you must, as the difference to a local grab is minimal in terms of basic usability of your application and they reduce the usability of the rest of the user's desktop.

You get an automatic global grab whenever you hold a mouse button down until such time as the button is released. This is crucial for drag-and-drop.

(A possible example of a use for a global grab is a menu option or button that changes the cursor to a help arrow and lets you select an area of the screen to get some context-sensitive help. I don't have an example conveniently at hand though...)

One last use for a grab is when you want to drain the event queue. This is useful when you have some long-running operation which locks an application's display for a few seconds occasionally and where you want to make sure that no erroneous (GUI) events are generated by the user in the mean-time. To do this, just grab (and focus, to catch keypresses) on a convenient window without any bindings (an easy technique is to use [bindtags] to shave off all the binding tags from an existing label widget - it doesn't disrupt any existing code unless you're doing something really funky), do an [update] to let all the queued events fall into the great bit-bucket, and then release the grab and set the focus back where it belongs. The flickering of the focus indicator will tend to vanish next to any other redrawing of the display going on.

In all cases, remember that:

  • you can only ever [grab] a mapped window,
  • the children of that window will still also be able to receive events,
  • keypresses follow a completely different mechanism, [focus],
  • [grab] requests are queued just like other state changes; you need to use [update idletasks] to make sure that any grab is actually being enforced - especially important with a global grab...

Anything I've missed? Please add it!


MAK (July 3, 2003) - Is it safe to use [tk::SetFocusGrab] and [tk::RestoreFocusGrab] or are they for meant for internal use only? If safe, perhaps some information on them is in order. There are no man pages for them.

DKF: They're internal only, so use at your own risk. If you think they should be public, that'll need a (short) TIP and a bit of documentation...