Tkinter - 如果子窗口小部件获得焦点,则键绑定到框架将不起作用

Tkinter - key-binding to frame won't work if a child widget gets focus

我正在尝试为不同的帧编写一个具有不同键绑定的程序。

如果框架处于焦点状态,这会很好地工作,但如果另一个小部件处于焦点状态,则键绑定不起作用。

例如,在此试用代码中,如果没有小部件处于焦点,键绑定将起作用,但如果按钮处于焦点(以编程方式或通过使用 TAB wkey 使按钮聚焦),则框架上的键绑定不再有效。在按钮小部件上设置焦点后,将焦点设置在框架上没有帮助。

import tkinter as tk


class TestGUI(tk.Frame):
    def __init__(self, parent):
        super().__init__(parent)

        self.edit_text = tk.StringVar()

        self.intro_label = tk.Label(self, text="Press button to say Hello")
        self.hello_button = tk.Button(self, text='Say Hello (H)', command=self.press_button)
        self.text_area = tk.Label(self, textvariable=self.edit_text, width=20, height=5)

        self.intro_label.pack(padx=10, pady=5)
        self.hello_button.pack(pady=5)
        self.text_area.pack(padx=10, pady=(5, 10))

        self.bind('<Key>', self.press_key)
        self.hello_button.focus()
        self.focus()

    def press_button(self):
        txt = self.edit_text.get()
        txt += "Hello World!\n"
        self.edit_text.set(txt)

    def press_key(self, event):
        key_pressed = event.char.lower()
        if key_pressed == "h":
            self.hello_button.invoke()


if __name__ == "__main__":
    root = tk.Tk()
    TestGUI(root).pack()
    root.mainloop()

我知道如果我将绑定放在应用程序级别,按键绑定仍然有效:

parent.bind('<Key>', self.press_key)

但是,我想在应用程序的不同框架上使用不同的键绑定。有没有办法做到这一点,而不会在另一个小部件获得焦点时丢失键绑定?

抱歉,不,那不是 tkinter 的功能。绑定到 window 是一种特殊情况,允许 window 中的任何 child 对绑定做出反应。

听起来你想为每一帧使用不​​同的键?所以第一帧响应 'h',第二帧响应 's' 之类的?在那种情况下,您可以简单地将每个帧绑定到您想要的,而不是所有键。这也避免了需要第二种方法来调用按钮按下:

import tkinter as tk

class TestGUI(tk.Frame):
    def __init__(self, parent):
        super().__init__(parent)

        self.edit_text = tk.StringVar()

        self.intro_label = tk.Label(self, text="Press button to say Hello")
        self.hello_button = tk.Button(self, text='Say Hello (H)', command=self.press_button)
        self.text_area = tk.Label(self, textvariable=self.edit_text, width=20, height=5)

        self.intro_label.pack(padx=10, pady=5)
        self.hello_button.pack(pady=5)
        self.text_area.pack(padx=10, pady=(5, 10))

        self.bind_all('<h>', self.press_button) # bind to the 'h' key
        # ~ parent.bind('<h>', self.press_button) # alternative way
        self.hello_button.focus()
        self.focus()

    def press_button(self, event=None):
        txt = self.edit_text.get()
        txt += "Hello World!\n"
        self.edit_text.set(txt)

if __name__ == "__main__":
    root = tk.Tk()
    TestGUI(root).pack()
    root.mainloop()

否则我想你可以做一个快速循环来绑定到框架中的所有 children。

def set_child_bind(widget, event, callback):
    widget.bind(event, callback)
    for child in widget.children.values():
        set_child_bind(child, event, callback)

class TestGUI(tk.Frame):
    def __init__(self, parent):
        super().__init__(parent)
        # all the other stuff
        set_child_bind(self, '<Key>', self.press_key)

或者您可以使用 FocusIn 事件获取 application-wide 绑定:

class TestGUI(tk.Frame):
    def __init__(self, parent):
        super().__init__(parent)
        # all the other stuff
        self.bind('<FocusIn>', lambda e:self.bind_all('<Key>', self.press_key))
        self.bind('<FocusOut>', lambda e:self.unbind_all('<Key>'))