Search code examples
pythonwindowstkinter

Tkinter how to distinguish between <Key> and <Control-Key> bindings?


This might be a simple answer.

I have a text widget which i want to bind any alphanumeric keyboard key (a-zA-Z0-9_ and rest of regular keys) to a specific method, and Ctrl+F to another method.

Using widget.bind("<Key>", method) will create the following:

  1. Click Ctrl+F will invoke method twice, one for Ctrl (event.keysym = Control_L, event.char = None) and second for F (event.keysym = f, event.char = <invalid>)
  2. Click F will invoke method (event.keysym = f, event.char = f)

Is there a way to distinguish between the two scenarios?


Solution

  • You can bind <Control-f> and <f> (and the same with other keys).

    import tkinter as tk
    
    # --- functions ---
    
    def key_f(event):
        print('key: f')
    
    def key_shift_f(event):
        print('key: Shift + f')
    
    def key_control_f(event):
        print('key: Control + f')
    
    def key_control_shift_f(event):
        print('key: Control+Shift + f')
    
    # --- main ---
    
    root = tk.Tk()
    
    #root.bind("<f>", key_f) # it works too
    #root.bind("<F>", key_shift_f) # it works too
    root.bind("f", key_f)
    root.bind("F", key_shift_f)
    root.bind("<Control-f>", key_control_f)
    root.bind("<Control-F>", key_control_shift_f)
    
    root.mainloop()
    

    Or you can use event.state & 4 to recognize key with Control.

    You need bit-wise & because it keeps information about other special keys

    (see Masks/Modifier on http://infohost.nmt.edu/tcc/help/pubs/tkinter/web/event-handlers.html)

    import tkinter as tk
    
    # --- functions ---
    
    def method(event):
        print('-----')
        print('[DEBUG] event.char  :', event.char)
        print('[DEBUG] event.keysym:', event.keysym)
        print('[DEBUG] event.state :', event.state, '=', bin(event.state))
    
        if event.char: # skip Control_L, etc.
    
            # if you need `& 5` then it has to be before `& 4` 
    
            if event.state & 5 == 5: # it needs `== 5` because `& 5` can give results `5`, `4` or `1` which give `True` or `0` which gives `False`
                print('method: Control+Shift +', event.keysym)
    
            elif event.state & 4: # it doesn't need `== 4` because `& 4` can give only results `4` or `0`
                print('method: Control +', event.keysym)
    
            elif event.state & 1: # it doesn't need `== 1` because `& 1` can give only results `1` or `0`
                print('method: Shift +', event.keysym)
    
            else:
                print('method:', event.keysym)
    
    # --- main ---
    
    root = tk.Tk()
    
    root.bind("<Key>", method)
    
    root.mainloop()