【问题标题】:Tkinter key-binds not working after message boxTkinter 键绑定在消息框后不起作用
【发布时间】:2017-07-24 21:40:02
【问题描述】:

我正在使用计算器,但遇到了与我的键绑定有关的问题。在我的 GUI 类启动结束时,会创建一个消息框来解释与用户的绑定。

messagebox.showinfo("Guide", "Key bindings include: 1, 2, 3, 4, 5, 6, 7, 8, 9, 
0, ., +, -, *, /, (, ), Enter, Backspace, Insert and Delete.")

具有讽刺意味的是,这会导致所有绑定在消息框关闭并且 tkinter 窗口被取消选择然后重新选择之前不响应。绑定编码如下:

master.bind('<Delete>', self.delete)
master.bind('<BackSpace>', self.back)
master.bind('<Return>', self.equals)
master.bind('<Insert>', self.add_answer)

我曾尝试使用 focus_set() 但它没有帮助。关闭消息框后,如何使键盘绑定立即响应?

这是我的整个上下文代码。

from tkinter import *
from tkinter.ttk import *
from tkinter import messagebox  # added by @martineau

class Logic:
    def add_digit(self, *args):
        if type(args[0]) == str:
            self.expression.append(args[0])
        else:
            self.expression.append(args[0].char)
        self.labels[1].config(text="".join(self.expression))

    def add_operation(self, *args):
        if type(args[0]) == str:
            self.expression.append(args[0])
        else:
            self.expression.append(args[0].char)
        self.labels[1].config(text="".join(self.expression))

    def add_answer(self, *args):
        self.expression.extend(list(self.labels[0]['text']))
        self.labels[1].config(text="".join(self.expression))

    def delete(self, *args):
        if self.expression:
            self.expression = list()
            self.labels[1].config(text="".join(self.expression))
        else:
            self.labels[0].config(text="")

    def back(self, *args):
        self.expression = self.expression[:-1]
        self.labels[1].config(text="".join(self.expression))

    def equals(self, *args):
        equation = list()
        number = list()

        if not self.expression:
            self.labels[0].config(text='')

        for value in self.expression:
            if value in self.numpad_texts:
                number.append(value)
            else:
                if number:
                    equation.append(str(float("".join(number))))
                    number = list()
                equation.append(value)

        if number:
            try:
                equation.append(str(float("".join(number))))
            except ValueError:
                messagebox.showerror("Error", "Syntax error: Your expression has incorrect syntax")

        for i in range(len(equation)):
            if equation[i] == '(' and i != 0:
                if equation[i-1] not in self.operation_texts:
                    equation.insert(i, '*')
            elif equation[i] == ')' and i != len(equation)-1:
                if equation[i+1] not in self.operation_texts:
                    equation.insert(i+1, '*')

        if equation:
            try:
                self.labels[0].config(text=str(eval(''.join(equation))))
            except ZeroDivisionError:
                messagebox.showerror("Error", "Zero division error: Your expression has a division by zero")
            except SyntaxError:
                messagebox.showerror("Error", "Syntax error: Your expression has incorrect syntax")

class GUI(Logic):
    numpad_texts = ('7', '8', '9', '4', '5', '6', '1', '2', '3', '0', '.', 'Equals')
    operation_texts = ('/', '*', '-', '+', '(', ')')
    function_texts = ('Delete', 'Back')

    def __init__(self, master):
        master.title('Calculator')

        self.expression = list()

        self.label_frame = Frame(master)
        self.label_frame.grid(columnspan=2)

        self.labels = list()

        for i in range(2):
            self.labels.append(Label(self.label_frame))
            self.labels[i].grid(row=i, column=0, columnspan=4)

        self.labels[0].bind("<Button-1>", self.add_answer)

        self.numpad_frame = Frame(master)
        self.numpad_frame.grid(row=1, rowspan=2)

        self.numpad_buttons = list()

        for i in range(len(self.numpad_texts)):
            self.numpad_buttons.append(Button(self.numpad_frame, text=self.numpad_texts[i], command=lambda i=i: self.add_digit(self.numpad_texts[i])))
            self.numpad_buttons[i].grid(row=i//3, column=i%3)
            if self.numpad_texts != 11:
                master.bind(self.numpad_texts[i], self.add_digit)

        self.numpad_buttons[-1].config(command=self.equals)

        self.operations_frame = Frame(master)
        self.operations_frame.grid(row=1, column=1)

        self.operation_buttons = list()

        for i in range(len(self.operation_texts)):
            self.operation_buttons.append(Button(self.operations_frame, text=self.operation_texts[i], command=lambda i=i: self.add_operation(self.operation_texts[i])))
            self.operation_buttons[i].grid(row=i//2, column=i%2)
            master.bind(self.operation_texts[i], self.add_operation)

        self.functions_frame = Frame(master)
        self.functions_frame.grid(row=2, column=1)

        self.function_buttons = list()

        for i in range(len(self.function_texts)):
            self.function_buttons.append(Button(self.functions_frame, text=self.function_texts[i]))
            self.function_buttons[i].grid(row = 0, column=i%2)

        self.function_buttons[0].config(command=self.delete)
        self.function_buttons[1].config(command=self.back)

        master.bind('<Delete>', self.delete)
        master.bind('<BackSpace>', self.back)
        master.bind('<Return>', self.equals)
        master.bind('<Insert>', self.add_answer)

        messagebox.showinfo("Guide", "Key bindings include: 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, ., +, -, *, /, (, ), Enter, Backspace, Insert and Delete.")

if __name__ == '__main__':
    root = Tk()
    calculator = GUI(root)
    root.mainloop()

【问题讨论】:

  • 您发布的代码无法运行 (NameError: name 'messagebox' is not defined)
  • 当我修复丢失的导入时,这对我来说一切正常。你在哪个平台上运行?
  • @Bryan:即使添加了缺少的import,它对我在 Win 7 上也不起作用。绑定的键不起作用(即使您手动使键盘窗口成为焦点)。 @Sierra Mountain Tech 的回答解决了这个问题(仅此而已)。
  • @R. Oliver:键绑定仅将事件发送到指定为参数的函数,而不发送其他函数。这些被称为“事件处理程序”,与许多小部件支持的command= 回调类型函数不同。这些函数不接收任何参数。我认为您的代码的(另一个)问题是您试图将许多 class Logic 方法用作 command-type 回调 事件处理程序。可以做到,但你没有做到。
  • @martineau:您对事件处理的解释不太正确。由于事件绑定到根窗口,因此只要主窗口中的任何小部件具有焦点,它们就会生效。在注释框中解释太复杂了,但是根窗口和顶层窗口的绑定有些特殊。问题只是其中一个窗口失去焦点。

标签: python python-3.x tkinter messagebox key-bindings


【解决方案1】:

Tkinters messagebox 需要单独导入:

from tkinter import messagebox

然后在绑定消息框的行下方添加以下内容:

master.focus_force()

这将在用户关闭消息框后将焦点移回根窗口,并且您的所有绑定将继续工作。

【讨论】:

  • 好吧,master.focus_force() 已经足够了。无需全部更改为self.master。此外,关于消息框,它不在您的尽头。这就是它的工作原理。以下答案,解释它。 stackoverflow.com/questions/24738104/…
  • 我认为使用 master 参数首先将其定义为类属性会是一个问题。我猜感觉它只有在没有问题时才会被调用。
  • 我认为如果您更多地关注使代码正常工作所必需的内容,这个答案会更好。将 master 更改为 self.master 与问题完全无关,并且更难看出有哪些重大变化。
  • @BryanOakley:我已经更新了我的代码以反映您的建议。感谢您的反馈。
  • @Lafexlos:我已经更新了我的答案,以便在这个问题上更加重要。感谢您的反馈。
【解决方案2】:

我在这里发帖是因为我还没有评论的特权.. @R Oliver,我的条目小部件也有类似的问题。由 Brian Oakley 作为 question 中的评论提供的以下解决方案对我有用,使用 after_idle() 并将消息作为函数。尝试这个: 为信息创建一些函数

def message():
    messagebox.showinfo("Guide", "Key bindings include: 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, ., +, -, *, /, (, ), Enter, Backspace, Insert and Delete.")

然后在 main 中使用 after_idle

if __name__ == '__main__':
    root = Tk()
    calculator = GUI(root)
    root.after_idle(message)
    root.mainloop()

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-12-24
    • 1970-01-01
    • 2016-05-24
    • 1970-01-01
    • 2017-01-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多