grab

Manipulates grabs, which direct events to a particular window rather than letting them go according to where the mouse is pointing.
http://www.purl.org/tcl/home/man/tcl/TkCmd/grab.htm

grab ?-global? window
grab current ?window?
grab release window
grab set ?-global? window
grab status window

Without a subcommand, grab on its own is equivalent to grab set. Note also that keyboard events follow focus, and that a grab is installed by default whenever a mouse button is pressed (essential for any drag-and-drop behavior).

  Warning

Programmers should be cautious when issuing grabs. This basically takes control from the user, leaving them at the mercy of a program that might not be operating as expected...

Not only that, but if the user needs for instance to copy and pass data from another window, or needs to handle some emergency, and the app has issued a grab, the app is not going to be looked upon kindly.

Best don't do that. Joe English wrote in comp.lang.tcl:

My two rules for [grab -global]:

(1) don't use it unless I'm absolutely sure I know what I'm doing, and

(2) if I think I need it this is a sure sign that I don't know what I'm doing :-)


Bryan Oakley's advice for working with global grabs:

While in development, always put in a failsafe. For example, I'll typically add code such as "after 60000 exit" to quit the application in one minute. That way, if my code has a bug and the grab doesn't get released, my system is only frozen for a minute. Otherwise, depending on circumstances, the system might require a reboot.


Later, Bryan writes in comp.lang.tcl:

I use a global grab in my combobox code with great effect. I've not once heard of a complaint related to the grab (though maybe those poor souls are still locked out of their machine and can't send me a message..).

If memory serves, the main reason was to make it so the combobox dropdown goes away with any mouse click anywhere outside the combobox. A global grab was just the ticket.

But yes, global grabs should be avoided if at all possible. Even then, they should be avoided unless absolutely, positively unavoidable. And yet, even then you need to think twice before doing it :-)

The few times I've played with global grabs I've always (except for the first time...) set a timer that releases the grab after a minute or so, to make sure I don't hose myself. The first time, I didn't do this and the experience wasn't very pleasant.


  Nested grab

In an entirely different direction, Geoff Battye raises subtle points about grab semantics in a newsgroup thread, the rough conclusion of which is that there ought to be a default mechanism to nest or stack them: "Donald Arseneau pointed out the Tk internal routines ::tk::SetFocusGrab and ::tk::RestoreFocusGrab to me ...

I went with a 'grab stack' in the end (not worrying about focus since it behaves as desired automatically for me).

One further issue is that grabs in included packages can also interfere. For this reason it may be a good idea to redefine the grab command to automatically nest grabs (renaming grab to do the actual grabbing within the new command). In fact, off the top of my head, I can't think why automatically nesting grabs shouldn't be the default behaviour of grab anyway. However, I don't encounter this issue at the moment."


HaO 2010-06-30 The following internal Tk routines (e.g. they may disapear at any moment from the tk lib) may be used by dialogs to set and restore grab and focus:

::tk::SetFocusGrab grab ?focus?
::tk::RestoreFocusGrab grab focus ?destroy?

Where grab is a toplevel which gets the grab, focus is a widget to get the focus and destroy is a flag how to handle toplevel grab and may have the values destroy (default) to destroy the toplevel or withdraw to withdraw (iconize) the toplevel.

The current focus and grab configuration is saved in an array with the index grab.focus. Thus it is important to use the same parameter values for both functions.

Use SetFocusGrab after dialog box creation and RestoreFocusGrab to destroy it and to restore former grab and focus.


  “Grab”bing a Screenshot

“Grab”bing a Screenshot

A completely different sense of "grab" is the one of the Macintosh application of that name, also regarded as a "Robot" function by Javaists (but see also "Capture a window into an image"): capture of the coloring of a portion of the screen. zoro2 suggests for this:

    proc snapsomearea {area} {
        set p [toplevel .tmp[incr ::someCounter]]
        pack [label $p.l -fill both -expand 1]
        wm overrideredirect $p 1
        wm geometry $p $area
        lower $p
        update
        set snap [blt::winop snap $p.l]
        destroy $p.l
        return $snap
    } 

and

    set fh [open "|xwd -root -silent | xwdtoppm | ppmtopng" r]
    fconfigure $fh -translation binary
    set fc [read $fh]
    close $fh
    image create photo -data $fc

while dkf takes the approach of

    exec xwd -root -silent | xwdtoppm | ppmtopng >screendump.png 

  Workaround for issues with MS-Windows "Minimize all" (Windows key + D)

HaO 2016-02-12: Having a transient toplevel window:

toplevel .c
wm transient .c .
pack [entry .c.e]
grab .c

and then press the "iconify all" button in the windows 10 taskbar or press "Windows-Key + d" in WIndows Vista to WIndows 10. It is not possible to restore the application, as only the main window (.) is in the taskbar, but restoring it fails...

This is Tk bug: 3009450

One may use the following function instead of grab to use the given workaround if necessary:

proc grabNew args {
    switch -glob -- [lindex $args 0] {
        set - .* - -* {
            # Workaround for "grab <Win>", "grab set" and "grab <option>" where option starts with "-" and <Win> with "."
            set Win [lindex $args end]
            # workaround if transient window
            if {[wm transient $Win] ne ""} {
                bind $Win <Unmap> {
                    if {[grab current] eq "%W"} {
                        grab release %W
                        bind %W <Map> [subst {
                            bind %W <Map> {}
                            grab set %W
                            catch { focus -force [focus -lastfor %W] }
                        }]
                    }
                }
            }
        }
    }
    tailcall grab {*}$args
}

aspect: Another version, suggested on the above ticket by danckaert (the below code can be found in tkImprover ):

# FIXME: this assumes the parent is normally -disabled 0.
rename grab _grab
proc grab {args} {
    switch -glob -- [lindex $args 0] {
        set - .* - -* {
            set win [lindex $args end]
            set parent [wm transient $win]
            if {$parent ne ""} {
                wm attributes $parent -disabled 1
                # HaO 2016-02-22: if grabbed window is destroyed before grab is released
                # (which is ok for standard grab), the parent window must be re-enabled
                bind $win <Destroy> "+wm attributes $parent -disabled 0"
            }
        }
        release {
            set win [lindex $args end]
            set parent [wm transient $win]
            if {$parent ne ""} {
                wm attributes $parent -disabled 0
            }
        }
    }
    tailcall _grab {*}$args
}