我正在开发一个模拟文章的 GUI 应用程序。除此之外,用户可以创建一个新主题,然后用注释填充该主题。目前,我有两种创建新主题的方法:通过菜单中的下拉选项(菜单命令)和通过主屏幕上的按钮(按钮命令)。该按钮以文本“新主题”开始。当用户按下按钮时,程序创建一个新主题,要求用户使用 tkSimpleDialog.askstring 命名主题。 , 然后将按钮的文本设置为主题名称和该主题中的注释数。该按钮的命令随后更改为向该主题添加注释。
在开发程序时,我首先验证了菜单命令是否有效。它调用askstring成功地创建了一个新的弹出窗口,以我想要的方式处理输入。但是,一旦我添加了按钮命令,就会调用 askstring失败,即使通过菜单命令调用也是如此。应该有 askstring 对话框的窗口变白了,程序挂起。如果我注释掉按钮命令,它会再次起作用。如果我注释掉菜单命令,它就会挂起。
这是我将命令添加到菜单的代码:
TopicBtn.menu.add_command(label="New Topic", underline=0,
command=self.newTopic)
这是 newTopic() 的代码:
def newTopic(self, button=None):
""" Create a new topic. If a Button object is passed, associate that Button
with the new topic. Otherwise, create a new Button for the topic. """
topicPrompt = "What would you like to call your new topic?"
topicName = tkSimpleDialog.askstring("New Topic", topicPrompt)
if topicName in self.topics.keys():
print "Error: topic already exists"
else:
newTopic = {}
newTopic["name"] = topicName
newTopic["notes"] = []
newTopic["button"] = self.newTopicButton(newTopic, button)
self.topics[topicName] = newTopic
self.addToTopicLists(newTopic)
这是 newTopicButton() 的代码:
def newTopicButton(self, topic, button=None):
""" If a Button object is passed, change its text to display the topic name.
Otherwise, create and grid a new Button with the topic name. """
if button is None:
button = Button(self.topicFrame)
index = len(self.topics)
button.grid(row=index/self.TOPICS_PER_ROW, column=(index %
self.TOPICS_PER_ROW), sticky=NSEW, padx=10, pady=10)
else:
button.unbind("<Button-1>")
buttonText = "%s\n0 notes" % topic["name"]
button.config(text=buttonText)
button.config(command=(lambda s=self, t=topic: s.addNoteToTopic(t)))
return button
最后,这是按钮命令的代码:
for col in range(self.TOPICS_PER_ROW):
button = Button(self.topicFrame, text="New Topic")
button.bind("<Button-1>", (lambda e, s=self: s.newTopic(e.widget)))
button.grid(row=0, column=col, sticky=NSEW, padx=10, pady=10)
任何人都知道为什么将 lambda 表达式绑定(bind)到按钮会生成 askstring挂?
编辑:感谢您的评论。这是展示该行为的最小示例:
from Tkinter import *
import tkSimpleDialog
class Min():
def __init__(self, master=None):
root = master
frame = Frame(root)
frame.pack()
button = Button(frame, text="askstring")
button.bind("<Button-1>", (lambda e, s=self: s.newLabel()))
button.grid()
def newLabel(self):
label = tkSimpleDialog.askstring("New Label", "What should the label be?")
print label
root = Tk()
m = Min(root)
root.mainloop()
请注意,从 button.bind("<Button-1>", (lambda e, s=self: s.newLabel())) 切换至 button = Button(frame, text="askstring", command=(lambda s=self: s.newLabel()))修复了错误(但没有给我按下按钮的引用)。我认为问题与将事件捕获为 lambda 的输入之一有关。
请您参考如下方法:
您在这里遇到的问题是因为调用了wait_window。在您正在使用的对话框中(您永远不会自己调用它,但实现该对话框的代码会调用它)。例如,以下代码在(可能)单击两次按钮后重现了该问题:
import Tkinter
def test(event=None):
tl = Tkinter.Toplevel()
tl.wait_window(tl)
root = Tkinter.Tk()
btn = Tkinter.Button(text=u'hi')
btn.bind('<Button-1>', test)
btn.pack(padx=10, pady=10)
root.mainloop()
此调用 wait_window有效地做了 update 的事情命令可以,并且是为什么调用 update 的典型示例是一件坏事。它与 <Button-1> 冲突正在处理的事件,然后挂起。问题是你将不得不忍受 wait_window正在使用,因为它属于对话框的代码。显然,如果你绑定(bind)到 <ButtonRelease-1>那么这种冲突永远不会发生。您也可以使用 command按钮中的参数,也可以正常工作。
最后,我建议根据您想要实现的目标以更简洁的方式创建按钮:
for i in range(X):
btn = Tkinter.Button(text=u'%d' % i)
btn['command'] = lambda button=btn: some_callback(button)
