Question

I'm working with Python 2.7 and a Tkinter GUI on a Win7 machine.

There are situations where I want to completely override the normal default behavior of the Tab key, but only so long as certain conditions exist. After that I want to revert to the default behavior. (Note that at the moment I'm interested in the Tab key but I may at some point need to do this for other keys as well.)

The code snippet below (not my actual app, just a stripped-down sample) gives me the full-override that I want, but it has the side effect of "permanently" eliminating the default behavior once I do the unbind, rendering the Tab key ineffective:

import Tkinter as tk

#Root window
root = tk.Tk()

tabBlock = ''

#Tab override handler
def overrideTab(*args):
    global tabBlock
    if (ctrlChk4.get()==1):
        tabBlock = root.bind_all('<Tab>',stopTab)
    else:
        root.unbind('<Tab>',tabBlock)

def stopTab(*args):
    print 'Tab is overridden'

#Control variable
ctrlChk4 = tk.IntVar()
ctrlChk4.trace('w',overrideTab)

#GUI widgets
fra1  = tk.Frame(root)
chk1 = tk.Checkbutton(fra1,
                      text='First checkbutton')
chk2 = tk.Checkbutton(fra1,
                      text='Second checkbutton')
chk3 = tk.Checkbutton(fra1,
                      text='Third checkbutton')
chk4 = tk.Checkbutton(fra1,
                      text='Tab override',
                      variable=ctrlChk4)

fra1.grid(row=0,column=0,sticky=tk.W,padx=10,pady=10)
chk1.grid(row=0,column=0,sticky=tk.W,padx=(10,0),pady=(5,0))
chk2.grid(row=1,column=0,sticky=tk.W,padx=(10,0),pady=(5,0))
chk3.grid(row=2,column=0,sticky=tk.W,padx=(10,0),pady=(5,0))
chk4.grid(row=3,column=0,sticky=tk.W,padx=(10,0),pady=(5,0))

tk.mainloop()

I've tried variations of doing a bind instead of a bind_all, and also setting the add parameter of the binding method to 1 or '+'. These variations all give me the same result: they let me get back the default behavior once I do the unbind but they also permit the default behavior to continue while the bind is in effect.

I've scoured various online sources for a way to "save and recover" the original binding, or to "non-destructively" do a full override of the default behavior, but no luck on either count.

Is there any way to accomplish what I'm trying to do?

EDIT: When it comes to the Tab key, I know that I can mimic/replace the original default behavior with

root.focus_get().tk_focusNext().focus_set()

...but this is also a generic question. If I needed to override a key -- any key -- in in the context of a certain module (say, one that contained my own custom class for my own custom-tweaked Tkinter widget), but then revert to the binding/behavior of that key as it was in the calling module, how could I do it? Is it possible?

Was it helpful?

Solution

Okay, I tried it out and got an answer for you. What I suggested in my comment works. In summary, return "break" when you want to override it. Don't when you don't. Use bind instead of bind_all. And, if you're not already doing so in your args, factor in the event parameter, as it won't work properly otherwise. You're actually still binding something to the key, but it only overrides the default functionality in some the specified circumstance. Here's the code (adapted for both Python 2.x and 3.x):

import sys
if sys.version_info[0]<3:
    import Tkinter as tk
else:
    import tkinter as tk

#Root window
root = tk.Tk()

#Tab override handler
def overrideTab(*args):
    root.bind('<Tab>', stopTab)

def stopTab(event=None, *args):
    if ctrlChk4.get()==1:
        print('Tab is overridden')
        return "break"
    else:
        print('Tab is not overridden') #Note that it still prints this.

#Control variable
ctrlChk4 = tk.IntVar()
ctrlChk4.trace('w',overrideTab)

#GUI widgets
fra1  = tk.Frame(root)
chk1 = tk.Checkbutton(fra1,
                      text='First checkbutton')
chk2 = tk.Checkbutton(fra1,
                      text='Second checkbutton')
chk3 = tk.Checkbutton(fra1,
                      text='Third checkbutton')
chk4 = tk.Checkbutton(fra1,
                      text='Tab override',
                      variable=ctrlChk4)

fra1.grid(row=0,column=0,sticky=tk.W,padx=10,pady=10)
chk1.grid(row=0,column=0,sticky=tk.W,padx=(10,0),pady=(5,0))
chk2.grid(row=1,column=0,sticky=tk.W,padx=(10,0),pady=(5,0))
chk3.grid(row=2,column=0,sticky=tk.W,padx=(10,0),pady=(5,0))
chk4.grid(row=3,column=0,sticky=tk.W,padx=(10,0),pady=(5,0))

tk.mainloop()
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top