Search code examples
python-3.xtkinterfocusbindsetfocus

Using Python 3.7.3, how do I disable Tkinter's background "Tab" key binding


I'm trying to intercept the Tab character press without explicitly binding to it at the root or frame level, as this would increase complexity more than desired. I really don't want to bind a simple handler to every button.

I want to know two things:

  1. How do I intercept/disable the background "Tab" key binding?
  2. The Space Bar triggers the command associated with that button after tabbed (even if you manually call widget.focus_set() on a different widget that the current tabbed one): how do I prevent this?

For some reason, Tkinter processes the Tab key to advance to the next object automatically. I don't want this in my case. I also tried "return 'break'" based on this answer but it doesn't seem to work, or the "_get_key" event handler occurs after the background binding.

class testFocus:
    btns = { }
    string = ''
    NumberCharList = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'period', 'comma', 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    def __init__(self, root):
        self.top = root
        self.top.bind('<Key>', self._get_key)
        self.frame = tk.Frame(self.top)
        self.frame.pack()

        self.var = tk.StringVar(value='Type: ')
        self.lbl = tk.Label(self.frame, textvariable=self.var)
        self.lbl.pack()

        for i in range(15):
            self.btns[i] = tk.Button(self.frame, text=f'Click {i}', command=lambda x=i: print(x))
            self.btns[i].pack()

    def _get_key(self, event):
        print('Root._get_key.event.keysym: ', event.keysym)
        print(root.focus_get())
        if event.keysym in self.NumberCharList:
            self.string += event.keysym
            self.var.set(self.string)
        elif event.keysym == 'Tab':
            print('TAB!') # NOT WORKING...WHY?
        elif event.keysym == 'Delete':
            self.string = ''
            self.var.set(self.string)
        elif event.keysym == 'BackSpace':
            self.string = self.string[:-1]
            self.var.set(self.string)
        # ... other code that handles specific key presses.
        print(root.focus_get())


if __name__ == '__main__':

    root = tk.Tk()
    root.geometry(f"800x480+{int(root.winfo_screenwidth() / 8)}+{int(root.winfo_screenheight() / 6)}")
    root.attributes('-fullscreen', False)
    root.title('Tests')
    root.resizable(width=False, height=False)
    app = testFocus(root)
    root.mainloop()

Tab Output:

Root._get_key.event.keysym:  Tab
.
.
Root._get_key.event.keysym:  Tab
.!frame.!button
.!frame.!button
Root._get_key.event.keysym:  Tab
.!frame.!button2
.!frame.!button2
Root._get_key.event.keysym:  Tab
.!frame.!button3
.!frame.!button3
Root._get_key.event.keysym:  Tab
.!frame.!button7
.!frame.!button7
Root._get_key.event.keysym:  Tab
.!frame.!button8
.!frame.!button8
Root._get_key.event.keysym:  Tab
.!frame.!button9
.!frame.!button9
Root._get_key.event.keysym:  Tab
.!frame.!button10
.!frame.!button10
Root._get_key.event.keysym:  Tab
.!frame.!button12
.!frame.!button12

Solution

  • How do I intercept/disable the background "Tab" key binding?

    The default tab behavior is based on a binding to the "all" binding tag. You can remove that binding to remove the default tab behavior.

    root.unbind_all("<Tab>")
    

    Depending on the underlying version of tk your python is using, it might be using a binding to <<NextWindow>> rather than directly on the tab key. In that case you need to unbind the virtual event <<NextWindow>> in a similar way:

    root.unbind_all("<<NextWindow>>")
    

    If you're wanting to remove the behavior tabbing to the next window, you're probably going to want to remove it for the previous window too. That binding is for the virtual event <<PrevWindow>>

    root.unbind_all("<<PrevWindow>>")
    

    The Space Bar triggers the command associated with that button after tabbed (even if you manually call widget.focus_set() on a different widget that the current tabbed one): how do I prevent this?

    That absolutely will not happen. The space key can only trigger a button if it has focus, or you've explicitly set your own binding to handle the space key in other widgets.