(Python)在子线程中使用Matplotlib进行绘图(100%解决问题)

首先

  今天在那边做一个UI界面的时候,想往里面做一个功能:“主线程上进行当前数据的监控、并将数据存入MongoDB中。当按下UI界面上的一个按钮后,创建一个子线程,让他去查MongoDB中的过往数据,并通过Matplotlib将其做成折线图进行数据可视化”。
  我想这么做的理由是:我不想在我画图、展示图片的时候,我的监控停下来,所以我想把画图、展示图片的那一部分放到另一个线程里面去,这样我主线程的数据监控就不会停下来了。
  一开始以为,这是个非常简单的事情,结果写了代码出来后发现,他爆了一个错误:

“Starting a Matplotlib GUI outside of the main thread will likely fail.”

  当时瞬间就感觉,事情不太对的样子。果然,他告诉我matplotlib只能运行在主线程里面:

“set_wakeup_fd only works in main thread”

  于是我,停止了思考(bushi
  

尝试方法1:使用multiprocessing(最后未使用)

  我开始百度,最先看到了这个方法,然后我去试了试但好像,对于我这个问题,解决不太了,效果不太好,所以就没用这个方法。当时参考的文章是这篇,感兴趣的可以自行去尝试下:https://www.it1352.com/1606767.html
  

尝试方法2:主线程中画图,然后子线程进行展示(最后未使用)

  这个思路我参考的是下面这个帖子:https://ask.csdn.net/questions/7465991
  这个的话,我当时测过后,是没啥技术问题的,我当时根据这个方法,在点击UI界面上的按钮后,先在主线程里面使用pymongo和matplotlib库把图画好后,没有使用plt.show函数,而是变成:

plt.savefig("final.png")

  之后的话,在子线程里面,通过PIL库来进行展示即可。当时我的部分代码如下:

-----------------------------------mongo.py中(这个文件是我自己写的)-----------------------------------
from PIL import Image
import threading

# 因为画图和存照片的部分在主线程里,所以这一块就只管显示图片
def draw_final():
	img=Image.open('final.png')
	img.show()

# 多线程类,主要执行的命令就是展示图片
class myDraw_final(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)
	# 线程启动后,执行的操作
    def run(self):
        draw_final()

# UI界面上进行绑定的函数
def start_draw_final():
	# find_final函数就是使用matplotlib、pymongo进行数据读取、画图、存储的函数
	find_final()  # 它会把最终结果以“final.png”的形式存储到本地
	# 但上面那个函数执行的地方,并不是子线程中,而是在主线程中
	# 下面的my.start中执行的,才是在子线程里执行的命令
    my = myDraw_final()
    my.start()

-----------------------------------我的UI界面py文件中-----------------------------------
# 该按钮点击后,执行mongo.py下的start_draw_final函数
............
............
bu1 = Button(self.window, textvariable=self.st1, font=('Arial', 10), command= \
            lambda: mongo.start_draw_final())
............
 ............

  这样的话,是差不多可以实现我想要的效果的,现在是:子线程中显示结果,显示结果的时候,也不堵塞我主线程中的数据监测了,但是这样写还有一点缺陷,就是它绘图还是在我当前程序的主线程中进行的,当他数据读取、绘图、保存的时候,我主线程还是被堵塞了,数据监听中断了。只有显示的时候不堵塞。这样我觉得还是不太够的。

尝试方法3:欺诈(最终采用)

  正如开始的时候所说,我们的matplotlib只能在主线程中使用,那么我们的解决方法就是:就让他在主线程里面跑。
  这个时候你可能会说:我们方法2里面,不就是在主线程里面跑的matplotlib嘛?那我们这里的方法3和方法2有什么区别呢?
  这就不对了。方法2里面,是在我们的当前UI程序的主线程里面跑的,方法3里面,我想让matplotlib觉得自己跑在主线程里,但是其实它跑在我的UI程序的子线程里。(这感觉咋和云计算里面的虚拟化一样)
  为了实现上述的效果,我把画图、显示的那一部分,写在另一个py文件,叫做draw_final.py里面,作为那个py文件的主函数。
  然后我的UI程序里面,创造一个子线程,而这个子线程的作用,是启动draw_final.py,这样就能让matplotlib跑在它想要的主线程里面,且不影响我UI程序上数据的监测了。双赢。

  draw_final.py中的主要代码如下所示:

import matplotlib.pyplot as plt
import pymongo


# 对ori数据进行查询
def find_final():
	--- 去mongodb里面查数据 ---
	--- 用matplotlib画图 ---
	plt.show()


if __name__ == "__main__":
	find_final()

  然后是mongo.py文件,里面主要包含多线程的内容,下面为部分代码:

import threading
import os


# 调用上面画图的那个draw_final.py程序
def draw_final():
    os.system("python E:\\desktop\\net9\\draw_final.py")


# 多线程,多出来的一条线程用于调用另一个程序
class myDraw_final(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)
	# 子线程用来调用那个py文件,此时,那个被调用的py文件中的程序运行在那个py文件的主线程里
	# 但是那个程序,是在我这个程序的子线程里被调用的
    def run(self):
        draw_final()


# UI窗口上的按钮与这个函数进行绑定,那个按钮按下后,创建子线程并开始执行上面那个类的run函数
def start_draw_final():
    my = myDraw_final()
    my.start()

  UI界面上的程序中,将按钮与其绑定:

# 按下按钮后,跑去执行mongo.py中的start_draw_final函数
bu1 = Button(self.window, textvariable=self.st1, font=('Arial', 10), command= \
            lambda: mongo.start_draw_final())

  通过上述的一轮阴险操作,成功欺骗了matplotlib,顺利实现了我们想要的结果。

总结

  希望上述的内容,能帮助遇到相同问题情况的人,谢谢大家。

  • 16
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值