Python GUI事件顺序错误
from Tkinter import *
from tkMessageBox import *
class Gui:
def __init__(self, root):
self.container = Frame(root)
self.container.grid()
self.inputText = Text(self.container, width=50, height=8)
self.outputText = Text(self.container, width=50, height=8, bg='#E0E0E0', state=DISABLED)
self.inputText.grid(row=0, column=0)
self.outputText.grid(row=0, column=1)
self.inputText.bind("<Key>", self.translate)
def translate(self, event):
input = self.inputText.get(0.0, END)
output = self.outputText.get(0.0, END)
self.outputText.config(state=NORMAL)
self.outputText.delete(0.0, END)
self.outputText.insert(INSERT, input)
self.outputText.config(state=DISABLED)
showinfo(message="Input: %s characters\nOutput: %s characters" % (len(input), len(input)))
root = Tk() #toplevel object
app = Gui(root) #call to the class where gui is defined
root.mainloop() #enter event loop
我在用tkinter做一个图形界面,关于事件处理程序的执行顺序我有点困惑。如果你运行上面的代码,你可能会看到……
1) 编辑文本框会触发事件处理程序,但似乎在没有记录实际变化的情况下就触发了,
2) 即使文本框被清空(比如一直按退格键),它似乎仍然保持着一个字符的长度,
3) 输出框只有在下一个事件触发时才会更新,尽管数据是在上一个事件中就已经来了。
这是tkinter中绑定的正常工作方式吗,还是我漏掉了什么?
我希望在更新输入框时的行为是:
1) 显示变化,
2) 进入事件处理程序,
3) 更新输出框,
4) 显示消息框。
1 个回答
这就是绑定是怎么工作的(这其实是件好事),但你的问题很容易解决。
绑定是按照小部件的绑定标签(也叫绑定标签或bindtags)中指定的顺序触发的。除非你另有说明,绑定的顺序通常是这样的:
- 如果小部件上有直接的绑定,它会在其他绑定之前触发。
- 如果小部件的类上有绑定,它会接着触发。
- 如果包含这个小部件的顶层小部件上有绑定,它会接着触发(注意:根窗口在这个情况下被视为顶层窗口)。
- 如果有一个“所有”的绑定,它会最后触发。
这个顺序可以在任何时候被某个事件处理程序停止,但这不是我们这次讨论的重点。
在默认情况下,你在 <Key>
上的绑定会在类绑定之前触发,而文本实际上是通过类绑定插入到小部件中的。这就是为什么你的绑定总是显得落后一个字符的原因。
通常,这个顺序是完全正确的,因为更具体的绑定有机会覆盖默认行为。如果不是这样的话,即使你不想要,你也总是会得到默认行为。有时候你想要增强默认绑定,而不是替换它们,这时这个顺序可能就不太合适了。
你可以调整绑定标签的顺序,让类绑定先触发。或者,给你的文本小部件添加一个额外的绑定标签,并把它放在类绑定之后的顺序中,然后绑定到那个标签上。通常来说,添加一个绑定标签是更好的解决方案,但并不总是如此。
要更改绑定标签,你可以这样做:
self.inputText.bindtags(((str(self.inputText)), "Text", "post-insert", ".", "all"))
要绑定到“后插入”,可以使用 bind_class 方法:
self.inputText.bind_class("post-insert", "<Key>", self.translate)
这可能听起来有点奇怪,但绑定标签是最强大的绑定机制之一。它们让你完全控制绑定的顺序,而用其他工具包做到这一点要困难得多。
顺便提一下,如果你把所有字符都放到文本小部件的末尾,最后总会有一个额外的换行符。要么到 end-1c
,要么从文本中去掉一个换行符。