tkinter中以多线程方式用matplotlib绘图出现程序崩溃问题
需求:需要绘制一幅动态图(大约3000帧左右)。但是数据量太大,约莫绘制到60-200帧不等时,出现程序未响应。
解决:开启线程去解决这个需求
问题:当使用线程,并用matplotlib画图之后,绘制到tkinter窗体上,出现程序直接崩溃
一个以多线程方式用plt绘图到tkinter上程序崩溃的小demo
import tkinter as tk
import threading # 导入多线程模块
class GUI:
def __init__(self):
self.root = tk.Tk()
self.root.title('演示窗口')
self.root.geometry("1024x960+1100+150")
self.interface()
def interface(self):
""""界面编写位置"""
self.Button0 = tk.Button(self.root, text="确定", command=self.start)
self.Button0.grid(row=0, column=0)
self.w1 = tk.Text(self.root, width=80, height=10)
self.w1.grid(row=1, column=0)
def event(self):
'''按钮事件,一直循环'''
a = 0
while True:
a += 1
self.w1.insert(1.0, str(a) + '\n')
self.figure = Figure(figsize=(5, 5), dpi=100)
self.subplot = self.figure.add_subplot(1, 1, 1)
self.subplot.set(xlim=[0, 20], ylim=[0, 20])
# self.subplot.xaxis.set_ticks(np.arange(0, 26, 1))
self.canvas = tk.Canvas(self.root,bg="red")
self.canvas_fig = FigureCanvasTkAgg(self.figure, master=self.canvas)
# 3.2 将这个中间件放到图像绘制
self.subplot.plot(np.array(range(10)))
self.canvas_fig.draw()
self.canvas.place(relx=0.2, rely=0.2, relwidth=0.8, relheight=0.8)
break
def start(self):
self.T = threading.Thread(target=self.event) # 多线程
self.T.setDaemon(True) # 线程守护,即主进程结束后,此线程也结束。否则主进程结束子进程不结束
self.T.start() # 启动
if __name__ == '__main__':
a = GUI()
a.root.mainloop()
-
运行示例:
根据代码,点击确定会以线程方式在白框位置输出信息,同时在窗体的右下角出现一幅图像。
-
点击确定按钮之后
-
白框位置出现输入之后,过一会还未出现绘图,程序直接闪退
-
经过测试,发现问题出现在
self.canvas_fig.draw()
。每当运行到此处时,程序崩溃 -
解决程序崩溃方案:执行draw()方法时,用after()方式。如:
self.root.after(100, self.canvas_fig.draw)
-
改动后的demo代码
import tkinter as tk import threading # 导入多线程模块 class GUI: def __init__(self): self.root = tk.Tk() self.root.title('演示窗口') self.root.geometry("1024x960+1100+150") self.interface() def interface(self): """"界面编写位置""" self.Button0 = tk.Button(self.root, text="确定", command=self.start) self.Button0.grid(row=0, column=0) self.w1 = tk.Text(self.root, width=80, height=10) self.w1.grid(row=1, column=0) def event(self): '''按钮事件,一直循环''' a = 0 while True: a += 1 self.w1.insert(1.0, str(a) + '\n') self.figure = Figure(figsize=(5, 5), dpi=100) self.subplot = self.figure.add_subplot(1, 1, 1) self.subplot.set(xlim=[0, 20], ylim=[0, 20]) # self.subplot.xaxis.set_ticks(np.arange(0, 26, 1)) self.canvas = tk.Canvas(self.root,bg="red") self.canvas_fig = FigureCanvasTkAgg(self.figure, master=self.canvas) # 3.2 将这个中间件放到图像绘制 self.subplot.plot(np.array(range(10))) self.root.after(100, self.canvas_fig.draw) # self.canvas_fig.draw() self.canvas.place(relx=0.2, rely=0.2, relwidth=0.8, relheight=0.8) break def start(self): self.T = threading.Thread(target=self.event) # 多线程 self.T.setDaemon(True) # 线程守护,即主进程结束后,此线程也结束。否则主进程结束子进程不结束 self.T.start() # 启动 if __name__ == '__main__': a = GUI() a.root.mainloop()