【问题标题】:Tkinter - key-binding to frame won't work if a child widget gets focusTkinter - 如果子小部件获得焦点,则与框架的键绑定将不起作用
【发布时间】:2021-04-04 03:58:40
【问题描述】:

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

如果框架被聚焦,这很好,但如果另一个小部件被聚焦,键绑定不起作用。

例如,在这个试验代码中,如果没有小部件处于焦点,则键绑定将起作用,但如果按钮被置于焦点(以编程方式或通过使用 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('&lt;Key&gt;', self.press_key)

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

【问题讨论】:

    标签: python tkinter frame key-bindings


    【解决方案1】:

    抱歉,不,这不是 tkinter 的功能。绑定到窗口是一种特殊情况,它允许窗口中的任何子级对绑定做出反应。

    听起来您想为每一帧使用不​​同的键?所以第一帧响应'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()
    

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

    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 事件来获取应用程序范围的绑定:

    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>'))
    

    【讨论】:

    • 谢谢@Novel。 bind_all 和 看起来很有用。我想这不允许的唯一事情是,如果您愿意,请说出“h”键来根据您正在查看的帧执行不同的操作。之前看过child方法,但是看起来有点笨重!
    • @Andrew 所有这些方法都允许您通过在 press_button 方法中使用实例变量来调整“h”键的操作,类似于您设置要添加文本的小部件的方式。作为第一个示例,使用txt += f"Hello from {self}!\n"
    猜你喜欢
    • 2022-01-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-01-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多