8

I have this following Python Tkinter code which redraw the label every 10 second. My question is , to me it seems like it is drawing the new label over and over again over the old label. So, eventually, after a few hours there will be hundreds of drawing overlapping (at least from what i understand). Will this use more memory or cause problem?

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

Here in my example, i am just using a single label.I am aware that i can use textvariable to update the text of the label or even text.config. But what am actually doing is to refresh a grid of label(like a table)+buttons and stuffs to match with the latest data available.

From my beginner understanding, if i wrote this Draw() function as class, i can destroy the frame by using frame.destroy whenever i execute Refresher function. But the code i currently have is written in functions without class ( i don't wish to rewrite the whole code into class).

The other option i can think of is to declare frame in the Draw() as global and use frame.destroy() ( which i reluctant to do as this could cause name conflict if i have too many frames (which i do))

If overdrawing over the old drawing doesn't cause any problem (except that i can't see the old drawing), i can live with that.

These are all just my beginner thoughts. Should i destroy the frame before redraw the updated one? if so, in what way should i destroy it if the code structure is just like in my sample code? Or overdrawing the old label is fine?

EDIT

Someone mentioned that python tkinter is not thread safe and my code will likely to fail randomly.

I actually took this link as a reference to use threading as my solution and i didn't find anything about thread safety in that post.

I am wondering what are the general cases that i should not use threading and what are the general cases i could use threading?

4

2 回答 2

16

在 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,并且永远不需要线程。

于 2013-07-25T15:30:04.920 回答
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()
于 2013-07-25T13:48:22.060 回答