tkinter以多线程方式用matplot绘图崩溃问题

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()
    
ThreadPoolExecutor是Python标准库concurrent.futures中的一个类,它提供了一种简单的方式来在多个线程中执行可调用对象。在Tkinter使用ThreadPoolExecutor可以实现多线程操作,从而提高程序的效率。以下是一个使用ThreadPoolExecutor的Tkinter多线程示例: ```python import tkinter as tk from concurrent.futures import ThreadPoolExecutor class App(tk.Tk): def __init__(self): super().__init__() self.title("Tkinter多线程示例") self.geometry("300x200") self.label = tk.Label(self, text="点击按钮开始计算") self.label.pack(pady=20) self.button = tk.Button(self, text="开始", command=self.start_calculation) self.button.pack(pady=10) self.executor = ThreadPoolExecutor(max_workers=2) def start_calculation(self): self.button.config(state="disabled") self.label.config(text="正在计算...") self.executor.submit(self.calculation_complete) def calculation_complete(self): # 模拟计算 import time time.sleep(5) self.label.config(text="计算完成") self.button.config(state="normal") if __name__ == "__main__": app = App() app.mainloop() ``` 在这个示例中,我们创建了一个Tkinter应用程序,包含一个标签和一个按钮。当用户点击按钮时,我们使用ThreadPoolExecutor在后台启动一个新线程来模拟计算,并在计算完成后更新标签的文本。注意,在Tkinter中更新UI必须在主线程中进行,因此我们使用submit方法将计算任务提交给ThreadPoolExecutor,而不是直接在新线程中更新UI。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值