我有以下Python Tkinter代码,它每10秒重绘一次标签。我的问题是,对我而言似乎是在旧标签上一遍又一遍地绘制新标签。因此,最终,几个小时后将有数百个绘图重叠(至少从我的理解)。这会占用更多内存还是会导致问题?
import Tkinter as tk
import threading
def Draw():
frame=tk.Frame(root,width=100,height=100,relief='solid',bd=1)
frame.place(x=10,y=10)
text=tk.Label(frame,text='HELLO')
text.pack()
def Refresher():
print 'refreshing'
Draw()
threading.Timer(10, Refresher).start()
root=tk.Tk()
Refresher()
root.mainloop()
在我的示例中,我只使用一个标签。我知道我可以使用textvariable
来更新标签的文本甚至是text.config
。但实际上要做的是刷新标签网格(如表格)+按钮和内容以匹配最新数据。
从我的初学者的理解,如果我将此Draw()
函数写为类,我可以在执行frame
函数时使用frame.destroy
销毁Refresher
。但是我目前拥有的代码是在没有类的函数中编写的(我不希望将整个代码重写为类)。
我能想到的另一个选择是将frame
中的Draw()
声明为全局并使用frame.destroy()
(我不愿意这样做,因为如果我也有这个可能会导致名称冲突许多帧(我这样做))
如果过度绘制旧图纸不会导致任何问题(除了我看不到旧图纸),我可以忍受。
这些都只是我初学者的想法。我应该在重绘更新的帧之前销毁帧吗?如果是这样,如果代码结构就像我的示例代码一样,我应该以什么方式销毁它?或者透支旧标签可以吗?
修改
有人提到python tkinter不是线程安全的,我的代码可能会随机失败。
我实际上将this link作为参考使用threading
作为我的解决方案,我在该帖子中找不到任何关于线程安全的内容。
我想知道我不应该使用threading
的一般情况是什么?我可以使用threading
的一般情况是什么?
答案 0 :(得分:10)
在tkinter中运行函数或更新标签的正确方法是使用after方法。这会将事件队列中的事件放在将来的某个时间执行。如果你有一个功能可以做一些工作,然后把自己放回事件队列,你已经创建了一些将永远运行的东西。
以下是基于您的示例的简单示例:
import Tkinter as tk
import time
def Draw():
global text
frame=tk.Frame(root,width=100,height=100,relief='solid',bd=1)
frame.place(x=10,y=10)
text=tk.Label(frame,text='HELLO')
text.pack()
def Refresher():
global text
text.configure(text=time.asctime())
root.after(1000, Refresher) # every second...
root=tk.Tk()
Draw()
Refresher()
root.mainloop()
从编码风格的角度来看,我会改变很多关于该程序的事情,但我希望尽可能地保持原始问题。关键是,您可以使用after
调用更新标签的函数,而无需创建新标签。另外,该功能可以安排自己在某个时间间隔再次调用。在这个例子中,我选择了一秒钟,以便更容易看到效果。
您还问“我想知道我不应该使用线程的一般情况是什么,以及我可以使用线程的一般情况是什么?”
如果你不得不问一个关于何时使用线程的问题,那么你就不应该使用线程。线程是一种先进的技术,它很复杂,容易出错。线程很可能使程序变慢而不是更快。它会产生微妙的后果,例如,如果你做的事情不是线程安全的,你的程序就会神秘地失败。
更具体地说明你的情况:你应该避免使用带有tkinter的线程。您可以使用它们,但您无法从这些其他线程访问小部件。如果需要对窗口小部件执行某些操作,则必须将指令放在线程安全的队列中,然后在主线程中需要定期检查该队列以查看是否有要处理的指令。如果您搜索它们,可以在本网站上找到相关示例。
如果听起来很复杂,那就是。对于您编写的大多数GUI,您无需担心这一点。如果您可以将大型进程分解为在100毫秒或更短时间内执行的块,则只需要after
,并且永远不需要线程。
答案 1 :(得分:6)
要允许使用最少的代码更改进行清理,您可以明确地传递先前的帧:
import Tkinter as tk
def Draw(oldframe=None):
frame = tk.Frame(root,width=100,height=100,relief='solid',bd=1)
frame.place(x=10,y=10)
tk.Label(frame, text='HELLO').pack()
frame.pack()
if oldframe is not None:
oldframe.destroy() # cleanup
return frame
def Refresher(frame=None):
print 'refreshing'
frame = Draw(frame)
frame.after(10000, Refresher, frame) # refresh in 10 seconds
root = tk.Tk()
Refresher()
root.mainloop()