Python:从线程事件更新tkinter canvas小部件

时间:2018-10-16 04:25:45

标签: python multithreading events tkinter

我正在尝试在我的Tkinter GUI上显示状态灯。在这一点上,我只希望它从绿色变为红色,以表明脚本尚未冻结。我所有的python追溯错误都指向我不理解的__libraries。我觉得这一定是一个名称空间问题,但是我正在扯头发试图将手指放在上面。

在创建画布对象并尝试将c传递给它之前,eventCheck()方法在创建一个可在0和1之间切换的标签时非常有用。关于我要做什么的信息很少,也许有更好的方法?

这是我的脚本的精简版:

import tkinter as tk
from tkinter import Canvas
import time
import threading

class myGUI(tk.Frame):

    def __init__(self, master, event):
        self.master = master
        self.event = event
        super().__init__(master)

        self.label = tk.Label(self, text="")
        self.label.grid()
        self.after(1, self.eventCheck)

        c = tk.Canvas(self, bg='white', width=80, height=80)
        c.grid()
        self.eventCheck(c)

    def redCircle(self, c):
        c.create_oval(20, 20, 80, 80, width=0, fill='red')
        print("redCircle Called")

    def greenCircle(self,c):
        c.create_oval(20, 20, 80, 80, width=0, fill='green')
        print("greenCircle Called")

    def eventCheck(self, c):
        self.label['text'] = self.event.is_set()
        if self.label['text'] == 0:
            self.redCircle(c)
        else:
            self.greenCircle(c)
        self.after(2000, self.eventCheck(c))

def timingLoop(event):
    while True:
        event.set()
        time.sleep(2)
        event.clear()
        time.sleep(2)

def main():
    root = tk.Tk()
    root.title("myFirst GUI")
    event = threading.Event()
    t=threading.Thread(target=timingLoop, args=(event,))
    t.daemon = True
    t.start()
    app = myGUI(root, event)
    root.mainloop()

if __name__=="__main__":
    main()

1 个答案:

答案 0 :(得分:1)

我发现您的代码存在两个主要问题。首先,这没有按照您认为的那样做:

def eventCheck(self, c):
        # ...
        self.after(2000, self.eventCheck(c))

因为您将对self.eventCheck(c)的调用结果而不是方法after()传递给了self.eventCheck,所以这是立即发生的无限递归。

第二个问题是,如果您注释掉所有计时和事件内容,则实际上不会出现界面,因此就看不到任何东西。我已经将您的示例脚本进一步精简(简化)为基本上可以工作的脚本:

import tkinter as tk
import threading
import time

class myGUI:

    def __init__(self, master, event):
        self.master = master
        self.event = event

        self.label = tk.Label(master, text="")
        self.label.pack()

        self.canvas = tk.Canvas(master, bg='white', width=80, height=80)
        self.canvas.pack()

        self.eventCheck()

    def redCircle(self):
        self.canvas.create_oval(20, 20, 80, 80, width=0, fill='red')
        print("redCircle Called")

    def greenCircle(self):
        self.canvas.create_oval(20, 20, 80, 80, width=0, fill='green')
        print("greenCircle Called")

    def eventCheck(self):
        flag = self.event.is_set()

        self.label['text'] = flag

        if flag:
            self.greenCircle()
        else:
            self.redCircle()

        self.master.after(2000, self.eventCheck)

def timingLoop(event):
    while True:
        event.set()
        time.sleep(2)
        event.clear()
        time.sleep(2)

def main():
    root = tk.Tk()
    root.title("myFirst GUI")

    event = threading.Event()
    t = threading.Thread(target=timingLoop, args=(event,))
    t.daemon = True
    t.start()

    app = myGUI(root, event)
    root.mainloop()

if __name__ == "__main__":
    main()

enter image description here

现在,您应该可以重新添加您的Frame超类。确保将myGUI的框架添加到根对象中。