12

I've been running a small script like this

from Tkinter import *
root = Tk()
def callback(event):
    print "callback"
w = Canvas(root, width=300, height=300)
w.bind("<Key>", callback)
w.pack()
root.mainloop()

However, the keyboard event is not handled in my situation (I use python 2.7 on window 7)

If I use

w.bind("<Button-1>", callback)

Things work fine.

So, this really puzzles me. Please anyone tell me why this's happening, thanks in advance.

Robert Bean
  • 851
  • 2
  • 11
  • 21

3 Answers3

19

Key bindings only fire when the widget with the keyboard focus gets a key event. The canvas by default does not get keyboard focus. You can give it focus with the focus_set method. Typically you would do this in a binding on the mouse button.

Add the following binding to your code, then click in the canvas and your key bindings will start to work:

w.bind("<1>", lambda event: w.focus_set())
Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685
  • 3
    Thanks Bryan, it is the focus problem. Though the code is a little different. I make it work by inline ` w.focus_set() w.bind('', function)` – Robert Bean Mar 08 '13 at 08:27
  • 2
    @RobertBean: just to make it clear - you don't have to do the focus_set before the bindings. The focus_set just has to be done before people start using the keyboard. – Bryan Oakley Mar 08 '13 at 11:59
  • 1
    I also don't understand why @BryanOakley stresses to bind a mouse click with w.focus_set(), instead of using a inline w.focus_set(). – wsysuper May 14 '15 at 04:08
  • 1
    @wsysuper: I didn't _stress_ it, I simply said it's typical. You can do either. It mostly depends on whether you have other widgets in the UI that can take focus. If you do, you will probably want to set a binding so that the user can switch the focus once the canvas has lost focus due to them clicking in another focusable widget. – Bryan Oakley May 14 '15 at 09:14
4

To avoid the "clicking on the canvas to activate the key bindings", I found simpler code at the following site:

http://ubuntuforums.org/showthread.php?t=1378609

He is attempting to bind a frame, but I implemented it in my own code and the canvas widget works as well. Your code will look like the following:

w.focus_set()
w.bind(<Key>, callback)
Donald
  • 633
  • 5
  • 16
2

There is a handy event in tkinter called "Enter", that tracks when the mouse enters a widget. If you bind it to the canvas, and in the binding's callback for that event you canvas.focus_set, then whenever the mouse is on the canvas you will have focus, and thus the keyboard bindings will work.

This will work even if the canvas looses focus (say through entering text in another widget), because when the mouse enters again the canvas, it will regain focus.