Python Tkinter Label每10秒重绘一次

时间:2013-07-25 02:10:24

标签: python multithreading tkinter label

我有以下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的一般情况是什么?

2 个答案:

答案 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()