bind

bind , a built-in Tk command, arranges for X events to invoke Tcl scripts.

Documentation

official reference
keysyms
the keyboard symbols to which one can potentially bind things. Of course, the specific keysyms available to you on any particular platform/hardware depend on factors outside of Tk's control. (See below for a simple script for determining what the specific keysym for a character is.)

Synopsis

bind tag
bind tag sequence
bind tag sequence script
bind tag sequence +script

Description

bind (part of Tk) is used to associate Tcl commands to execute upon the press of specific keys. Also, bind may be driven by other events, both real and virtual, such as enter, leave, focus in, focus out, etc. See the documentation for specific details.

tag is formally a bindtags tag, but for most user code it is best to use widget names. The optional sequence says what event to work with; if omitted, bind returns what bindings are set on tag. If script is given, it replaces (or deletes, if empty, or appends, if it starts with a “+”) the binding script, and if script is omitted (but sequence is given) then the current binding script is returned.

Keyboard symbols are the Tk values associated with particular sequences of keyboard keys that one can press, such as Shift, or A or special function keys such as Page Up, etc.

Examples

Only in tcl...
A nice simple example of bind in action
Vogel spiral
uses apply as the script for bind
Salvatore Sanfilippo
has an example on his page of using a keyboard n "raw" mode

Eggdrop

If you are looking for the eggdrop bind command, try bind

  Discussion

Discussion

Unanswered Questions

Question: Is there an introspective method that a Tk application can determine whether a keysym is currently available for it to use?

Answered Questions

bind Syntax Example

RS writes on comp.lang.tcl: The bind syntax is actually very easy. With x set to a widget (e.g. .mytext) or a class (e.g. Text), call

bind $x

to get a list of defined bindings. This list contains <Key-Prior> which is the correct name for the Page Up key. To see what it is bound to, call

bind $x <Key-Prior>

To mirror this binding to another event, make that

bind $x <Control-r> [bind $x <Key-Prior>]

like you tried, just with the incorrect name. BTW, you normally need not include variable names in braces - only if they contain characters other than A-Z a-z 0-9 _.

Finding keysyms

KBK: You can find the keysym for a key on an unfamiliar keyboard by running wish against the one-line script:

bind . <KeyPress> {puts %K} ; focus .

directing focus at the (empty) wish window, and touching the key in question. On Windows, you'll need to add

console show

to the script so that you can see its output.

Arjen Markus: You may also put up a text widget and display the keysym by the following script:

bind .textwidget <KeyPress> { %W insert end "%K\n" }
focus .textwidget

Specifying a Key to bind

Arjen Markus: There are several ways to define a specific key, for instance:

<Key-keysym>
<KeyPress-keysym>
<KeyRelease-keysym> (subtle differences here!)
<keysym>
<A>                (if the character is plain ASCII)

For a space, you will need to use:

<Key-space>

anything else seems to give problems.

Also note that keysyms are case-sensitive: "Down" for the downward arrow and "space" for a space ("down" and "Space" do not work; quotation marks for convenience only)

bind to a Mouse Action

LV: If you are wanting to bind actions to a mouse button action, rather than a keyboard press, then you appear to be able to use:

bind $x <ButtonRelease-1>  { puts "Unmodified button" }
bind $x <Shift-ButtonRelease-1> { puts "Shift button 1 release" }

Idea: <Invoke> event

Martin Lemburg 2002-07-02: Why isn't there an event Invoke for invokeable widgets, like buttons? Wouldn't make it sense to have such an event? To bind widgets using the Invoke event, like to connect a widget with the -command option to an event handler?

Than ...

button .b -text exit -command {cmdProc .b [clock seconds]};

... would be equal to ...

bind .b <Invoke> {cmdProc %W %t};

It shouldn't be a problem to use the substitution capabilities during the usage of a binding, like:

button .b -text exit -command {cmdProc %W %t};

Wouldn't that be nice and consequent?

Wouldn't it be consequent (for example) to use bindings to scrollbars or entries too? ...

entry     .e   -textvariable filter;
button    .b   -text "filter";
listbox   .lb  -listvar data -selectmode extended;
scrollbar .sbx -orient horizontal;
scrollbar .sby -orient vertical;

bind .e   <Validate> {validateFilter %V %s};
bind .b   <Invoke>   {filter .e .lb};
bind .lb  <XScroll>  {.sbx set};
bind .lb  <YScroll>  {.sby set};
bind .sbx <Invoke>   {.lb xview};
bind .sby <Invoke>   {.lb yview};

There would be the chance to elimate all (event)handlers from that code, that only builds up the GUI. The code to handle events could use now use bindings.

Caution: Abbreviated bind Descriptions

Ken Jones writes on comp.lang.tcl, in response to a developer trying to bind actions on numeric keys using

bind .bu1 <Alt-2> {focus .bu2}

What you're experiencing is one of those traps that people encounter when they use abbreviated binding descriptions. For many KeyPress events, you can get by with providing only the keysym. So, <a> is equivalent to <KeyPress-a>, and <Control-a> is equivalent to <Control-KeyPress-a>.

On the other hand, Tcl also allows you to abbreviate ButtonPress events, so <1> is equivalent to <ButtonPress-1>. And <Alt-1> through <Alt-5> is equivalent to <Alt-ButtonPress-1> through <Alt-ButtonPress-5>.

Solution? bind to <Alt-KeyPress-1> and <Alt-KeyPress-2>. And be very careful relying on abbreviated event descriptions in bindings.

Keypad Keys

LV: I am trying to get the following sample program to display the numbers 0-9 when pressed on the keypad:

#! /usr/tcl84/bin/tclsh

package require Tk

entry .e
bind .e <KeyPress-1> { puts 1 }
pack .e
focus .e

The expectation was that I would get an entry widget into which I could press the keys on the keypad and get, in this case, the number 1 into the entry widget. What I experience is that after I mouse into the widget and click, I can type alphas and the numbers on top row of the main keyboard, but the keypad keys are generating nothing...

Peter Newman 2004-04-30: NumLock switches the keypad between numbers and cursor keys. I presume you tried both settings - and still no joy. If so, then it would appear that Tk doesn't support the keyboard properly.

Pierre Coueffin 2004-04-12: Try <KeyPress-KP_1>

Thomas Guettler 26 April 2006: Or Try <KP_End>

You can query the bindings interactively with this: (See above)

bind . <KeyPress> { puts %K } ; focus .

Canonicalize a bind Specification

Q. Given a string specifying an event sequence, how to obtain some sort of "normal form" for that event sequence, so that, for example, 1 and Mousebutton-1 (or whatever the long form is) both map to the same normal form indicating a single click of mouse button 1?

A. KBK: Hmm, bind it to a nonexistent bindtag and then query the bindings on the tag?

% bind Nothing <Button1-ButtonPress-2> {;}
% bind Nothing
<B1-Button-2>

Control Sequences

Dossy 2005-03-29: I just spent a good hour only trying to understand bind only to discover that bind . <Control-a> {script} is NOT the same as bind . <Control-A> {script}. the former works, the latter doesn't.

RS: would expect that <Control-A> is equivalent to <Control-Shift-a> ... case matters, doesn't it?

MG: The bindings for this, after a little playing, seem slightly strange. Without Caps Lock on, Shift and the a (to get an upper-case A) fires the binding for Shift and (uppercase) A. The only way to fire a binding for Shift and (lowercase) a that I can find is to turn Caps Lock on, and -then- press the 'a' key with Shift held down.

CJL The minimal example of this mildly unexpected behaviour is to switch Caps Lock on, then in a Wish console (I'm talking Windoze here) type something, highlight it and press Ctrl-C. Now try to paste your text somewhere else (e.g. Notepad) using Ctrl-V - you'll discover that the copy didn't happen, but even though Caps Lock is still on, Notepad will quite happily obey the paste request (by pasting whatever was already in the clipboard). To me 'Ctrl-C' means "the physical key labelled with a 'C' was pressed while ctrl was down", and should have nothing to do with the state of Caps Lock, only the combination of keys pressed.

WHD: On both Windows and Mac OS X, normal apps accept both <Control-v> and <Control-V> (<Command-v> and <Command-V>) as the Paste key. For Tk, it appears that you need to bind both. On the other hand, if you bind both you'll find that all of the following combinations will paste:

  • <Control-v>
  • <Control-V>
  • <Shift-Control-v>
  • <Shift-Control-V>

If you want to support Shift-Control sequences in your keyset, you need to bind both <Shift-Control-v> and <Shift-Control-V>. Here's a simple solution that results in CapsLock-independent letter key bindings:

proc bindletterkey {tag modifier letter binding} {
    set upper [string toupper $letter]
    set lower [string tolower $letter]

    bind $tag <$modifier-$upper> $binding
    bind $tag <$modifier-$lower> $binding
} 

bindletterkey .text Control F       {puts Control-F}
bindletterkey .text Shift-Control F {puts Shift-Control-F}

Case Insensitive Binding

MG 2005-06-06: And, where the bindings already exist (ie, for the Text widget) and you want to make them case-insensitive, something like this will help.

proc mirror {class {dir 1}} {
    if {$dir eq {1}} {
        set range a-z
        set case toupper
    } else {
        set range A-Z
        set case tolower
    }
    foreach x [bind $class] {
        if {[regexp "^<(.+-)?\[$range]>$" $x]} {
            set y [string range $x end-1 end-1]
            set y [string range $x 0 end-2][string $case $y]>
            if {[bind $class $y] ne {}} {
                continue
            }
            bind $class $y [bind $class $x]
        }
    }
};# mirror

Then run, for instance:

mirror Text 1;  # copy all lower-class bindings for the Text  widget to upper-class versions
mirror Text 0;  # copy all upper-class bindings for the Text  widget to lower-class versions
mirror Entry 1; # copy all lower-class bindings for the Entry widget to upper-class versions

It's also careful not to overwrite bindings - if you have an <a> and an <A> binding for the Text widget, and run mirror Text 1, <A> will be left as it is, rather than being overwritten with <a>'s binding.

This raises one question for me, though - what's better practice? Should you bind to

bind Text <A> [bind Text <a>]

and copy the binding, or use something like

bind Text <A> {event generate %W <a>}

to mirror <a>'s state at the time the event happens?

Unbound bind

Some of the best bind coding doesn't involve bind at all. DKF, for example, astutely remarked that

event add <<Copy>> <Control-C>

trumps

bind $class <Control-Key-C> [bind $type <<Copy>>]
bind $class <Control-Key-c> [bind $type <<Copy>>]

... [Correct; elaborate]

bind to a plain keystroke only (not modified by Ctrl or Alt)

proc bind'plainkey {tag key script} { 
    bind $tag <Control-$key> {} 
    bind $tag <Alt-$key>     {} 
    bind $tag $key $script 
} 

Example:

bind'plainkey all x {puts Hello} 

RUJ: If bind leave command to main window it is not (bindings) grabbing to its associated widgets. e.g.:

toplevel $wa -bd 1 -relief ridge
wm geometry $wa 600x40+$mousex+$mousey
wm resizable $wa 0 0
wm overrideredirect $wa yes
wm iconname $wa "menu"
wm group $wa .
focus $wa
grab set $wa

entry $wa.ent01 -width 6 -background white 

bind $wa <1> [list after cancel destroy .base]
bind $wa <Leave> [list destroy .base]

But it is destroying base if cursor goes near to entry. Can anybody solve the problem.

Disable Pasting

2008-03-17: How can I disable pasting into a text widget? I tried the following:

bind $theText <<Paste>> {tk_messageBox -message {Paste Attempt}}
bind $theText <Control-v> {tk_messageBox -message {Paste Attempt}}
bind $theText <Button-2> {tk_messageBox -message {Paste Attempt}}
bind $theText <Shift-Insert> {tk_messageBox -message {Paste Attempt}}

The message boxes are shown, so the event handling works. But after the message box has been clicked away, the pasting takes place anyway - at least for Shift-Insert.

Answer: You are adding bindings to the widget but aren't addressing the bindings associated with the widget class. Read up on bindtags. One solution is to add ;break to each binding to prevent the class bindings from firing:

bind $theText <<Paste>> {tk_messageBox -message "Paste Attempt"; break}

Of course, you can also just set the -state option to "disabled".

Class binding, do they work?

Janic: 2009-07-01T09:48:17:

I tried to set a class binding but it never works. For example:

checkbutton .a -text test
pack .a
bind .a <ButtonPress> break

works fine (the widget is still active but you can't check it anymore), but if write

bind Checkbutton <ButtonPress> break

it doesn't work. Did I miss something in the class binding behavior?

MG: Many different bindings are checked for a widget, based on its bindtags. The default order for a widget is: bindings on the widget itself, bindings on its class, its toplevel, and then on all. Also, bindings are checked from most specific match to least specific (so if you press the 'a' key, it checks for KeyPress-a before a more generic KeyPress, etc).

That's why you're seeing what you see; first the widget's own bindings are checked, and the generic <ButtonPress> binding matches and is run; break stops further bindings being checked.

When you bind that on the Checkbutton class, though, the default <ButtonPress-1> binding is more specific, and gets used instead of your <ButtonPress> binding. (You can see all the default bindings with bind Checkbutton, then see what a specific one does with bind Checkbutton $binding.) So, to override the default binding, you'd need to either replace the Button-1 binding, or add something higher up the bindtags chain.

DKF: You are advised to be very careful with changing bindings of widget classes, as that affects all widgets of that type in the application. (It's most useful if you're adding more editing keys to the text widget, or are making your own megawidget class.) It's far better to adjust the bindtags for the specific widgets in question as the results are a lot more predictable.

wdb: Here an example: button .b has new binding tag, Button1 instead of Button. Bindings are copied from Button to lButton1, then binding of <1> is modified:

% package require Tk
8.5.1
% pack button .b -text Howdy
% # button appears
% bindtags .b
.b Button . all
% bindtags .b {.b Button1 . all}
% apply {{old new} {
  foreach p bind $old {
      bind $new $p bind $old $p
    }
}} Button Button1
%
% bind Button1 <1> "+puts yep!"
% # now clicking on .b:
% yep!

% 

From now on, you can change arbitrary buttons without side-effects to "normal" buttons.


telgo 2010-07-29 15:22:47:

On a Mac OSX 10.6.4 I have a program with a text widget, and the binding

bind . <Command-f> {
    Find
    break
}

I also have a menu item that calls Find

$Test add command -label Find -accel Command-F -command Find

Find puts up a dialox box, but involves some sorting. However, it still takes less than a second to generate the data. Here it is:

proc Find {} {
    global body dict t
    
    set t1 [clock clicks -milliseconds]        
    set f .dictdisplay
    
    #see if we want to restrict display
    set target {}
    if {[$t tag ranges sel] ne {}} {
        set term [$t get sel.first sel.last]
        set target [::soundex::soundex $term]
    } 
    
    if [Dialog_Create $f Dictionary -borderwidth 10] {
        set text [Scrolled_Text $f.body -width 20 -font 16]
        set b [frame $f.buttons]

        #--------- convert the array to a list
        foreach index [array names dict] {
            lappend sort $index
        }
        set sorted [WolofSort $sort]
        foreach item $sorted {
            if {$target eq {} || $target eq [::soundex::soundex $item] } {
                $text insert end "$item : $dict($item)\n"
            }
        }

        pack $f.body  $f.buttons -side top -fill x
        button $b.ok -text OK -command {set dictdisplay(ok) 1}
        pack $b.ok -side right
    }
    set t2 [clock clicks -milliseconds]
    puts "[expr {($t2-$t1)/1000.0}] seconds"
    
    set dictdisplay(ok) 0
    Dialog_Wait $f dictdisplay(ok) 
    
    set t3 [clock clicks -milliseconds]
    puts "[expr {($t3-$t2)/1000}] seconds"
    Dialog_Dismiss $f
}

If I use the menu item, everything works as expected. If I use Command-f, there is a noticeable pause, then the dialog appears - undecorated - with the data in it and the ok button, but never returns from Dialog_Create. Dialog_Create, Dialog_Wait and Dialog_Dismiss are taken from Welch, Jones and Hobbs 2003 "Practical Programming in Tcl and Tk"p 606-607]

Is there an issue with putting up a dialog box from within a key-binding?

See Also

Bindings and why they are important
Bindings and variable substitution
Disable autorepeat under X11
mousewheel bindings
binding to a single mouse click
Key-press names
Portable keystate
uevent
Functionality similar to bind, but written in pure Tcl. Allows scripts to generate and respond to events.