【发布时间】:2016-06-19 14:07:10
【问题描述】:
我正在使用 Python 3.4 创建一个 tkinter 应用程序,该应用程序从 API 收集帖子、过滤它们并允许用户查看它们并为每个帖子做出决定(忽略、删除、分享等)
用户应该选择一个日期和一些页面,然后单击“收集”按钮。然后程序从页面中获取帖子并将它们存储在“wholeList”中。
当用户点击第二个按钮“Review”时,帖子必须被过滤并传递给Reviewer。
我的问题是审稿人根本没有收到任何帖子,Filterer 也没有。我在某些地方添加了一些调试print() 语句,尤其是handlerCollect(),结果让我感到困惑,因此发了这篇文章。
当我单击“收集”时,程序并没有完成handlerCollect() 回调方法,而是将其置于“DEBUG->1”和“DEBUG->2”之间的某个位置。主窗口不会冻结或任何东西,因为我可以单击“查看”并让它打印“DEBUG->4”并打开Reviewer。当我关闭主窗口时,“DEBUG->0”、“DEBUG->2”和“DEBUG->3”最终会打印出来,同时执行handlerCollect() 方法的其余部分。
handlerChoosePage() 也会发生相同的行为,“DEBUG->0”会延迟到 tkinter 根 (TK()) 被销毁。我的结构化编程知识告诉我它应该是第一个打印出来的。相反,它是最后一个。我最好的结论是我不能正确地结束我的Toplevelmainloop()s。我不得不承认我以前从未遇到过这样的事情。我认为在Toplevels 上结束mainloop()s 的正确方法是使用destroy(),我很困惑为什么调用mainloop()s 的方法会被搁置直到Tk 根被销毁;不太实用。
from GUICollector import GUICollector as Collector
class Launcher(tk.Frame):
def __init__(self, *args, **kwargs):
...
self.allPagesCB = Checkbutton(self.dateFrame, text="Collect for all pages",
variable = self.allPagesVar, command=self.handlerChoosePage)
self.collectBtn = Button(self, text="Collect", command=self.handlerCollect)
self.reviewBtn = Button(self, text="Review", command=self.handlerReview)
def handlerChoosePage(self):
if self.allPagesVar.get() == 0:
child = tk.Toplevel(self)
selector = PageSelector(self.toCollect, child)
selector.pack(side="top", fill="both", expand=True)
selector.mainloop()
print("DEBUG->0")
def handlerCollect(self):
print("DEBUG->1")
self.collect()
print("DEBUG->4")
for post in self.collector.getPosts():
if post not in self.wholeList:
print("...")
self.wholeList.append(post.copy())
self.collector = None
print(len(self.wholeList), "posts in wholeList")
def collect(self):
window = tk.Toplevel(self)
self.collector = Collector(self.toCollect, self.sinceEpoch, window)
self.collector.grid(row=0,column=0)
self.collector.after(500, self.collector.start)
print("DEBUG->2")
self.collector.mainloop() # This is what seems to hang indefinetly
print("DEBUG->3")
def handlerReview(self):
print("DEBUG->5")
print(len(self.wholeList), "posts in wholeList")
filterer = Filterer(self.wholeList)
self.wholeList = filterer.done[:]
window = tk.Toplevel()
reviewer = Reviewer(self.wholeList[:], window)
reviewer.grid(row=0,column=0)
reviewer.mainloop()
GUICollector 模块根本不需要用户交互。 这个模块似乎工作得很好:完成它的工作,显示它已经完成,然后在指定的延迟后关闭。 由于 GuiCollector mainloop() 似乎是挂起的罪魁祸首,我是这样结束它的:
class GUICollector(tk.Frame):
def __init__(self, pagesList, since, *args, **kwargs):
tk.Frame.__init__(self, *args, **kwargs)
def start(self, event=None):
if some_logic:
self.after(250,self.start)
else:
self.done() # Does get called.
def done(self):
# Some StringVar update to display we are done on screen
self.after(1250, self.validate)
def validate(self):
self.master.destroy()
PageSelector 模块通过按下按钮的相同调用被销毁:self.master.destroy()
这是程序的相关输出:
DEBUG->1
DEBUG->2
=> collected data of page [PageName]
=> Found 3 posts in page
DEBUG->5
0 posts in wholeList
[The main window (Launcher) is manually closed at this point]
DEBUG->3
DEBUG->4
...
...
...
3 posts in wholeList
DEBUG->0
【问题讨论】:
-
你应该只有一个
mainloop,不多。如果你启动了多个,你可能会得到嵌套的事件循环,这是一种看到奇怪效果的可靠方法。 -
@schlenk: 不是可能:你肯定会得到嵌套的事件循环。
-
我看到很多 cmets 说应该只有一个
mainloop(因为如果我没记错的话,每个进程只有一个事件循环),我很乐意听从建议,但我不明白如何在没有在每个辅助窗口上调用mainloops 的情况下制作具有多个窗口的应用程序... -
简单。只是不要这样做。删除当前的 mainloop() 调用。仅致电
root=tk.Tk()一次。所有其他窗口都应该是 Toplevel,通常以root作为第一个参数(但对于与另一个小部件连接的弹出窗口,请使用小部件)。最后调用 root.mainloop,就像 pmod 的回答一样。
标签: python debugging tkinter destroy