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

本文介绍了如何在Python的UI界面中,使用多线程解决主线程数据监控和子线程Matplotlib绘图的问题。作者尝试了multiprocessing和直接在子线程中绘制然后再主线程显示的方法,但都存在不足。最终,通过创建一个子线程来运行独立的Python脚本,欺骗matplotlib使其在子线程中认为自己在主线程运行,成功实现了数据监控和绘图的并行处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

首先

  今天在那边做一个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.youkuaiyun.com/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,顺利实现了我们想要的结果。

总结

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

colcon build /home/yebaibiyesheji/.local/lib/python3.10/site-packages/setuptools/_distutils/dist.py:289: UserWarning: Unknown distribution option: 'tests_require' warnings.warn(msg) Starting >>> mybot_description /home/yebaibiyesheji/.local/lib/python3.10/site-packages/setuptools/_distutils/dist.py:289: UserWarning: Unknown distribution option: 'tests_require' warnings.warn(msg) --- stderr: mybot_description /home/yebaibiyesheji/.local/lib/python3.10/site-packages/setuptools/_distutils/dist.py:289: UserWarning: Unknown distribution option: 'tests_require' warnings.warn(msg) Traceback (most recent call last): File "/home/yebaibiyesheji/myRobot/src/mybot_description/setup.py", line 5, in <module> setup( File "/home/yebaibiyesheji/.local/lib/python3.10/site-packages/setuptools/__init__.py", line 117, in setup return distutils.core.setup(**attrs) File "/home/yebaibiyesheji/.local/lib/python3.10/site-packages/setuptools/_distutils/core.py", line 186, in setup return run_commands(dist) File "/home/yebaibiyesheji/.local/lib/python3.10/site-packages/setuptools/_distutils/core.py", line 202, in run_commands dist.run_commands() File "/home/yebaibiyesheji/.local/lib/python3.10/site-packages/setuptools/_distutils/dist.py", line 1002, in run_commands self.run_command(cmd) File "/home/yebaibiyesheji/.local/lib/python3.10/site-packages/setuptools/dist.py", line 1104, in run_command super().run_command(command) File "/home/yebaibiyesheji/.local/lib/python3.10/site-packages/setuptools/_distutils/dist.py", line 1021, in run_command cmd_obj.run() File "/home/yebaibiyesheji/.local/lib/python3.10/site-packages/setuptools/command/egg_info.py", line 312, in run self.find_sources() File "/home/yebaibiyesheji/.local/lib/python3.10/site-packages/setuptools/command/egg_info.py", line 320, in find_sources mm.run() File "/home/yebaibiyesheji/.local/lib/python3.10/site-packages/setuptools/command/egg_info.py", line
最新发布
04-03
<think>好的,我需要解决用户在colcon build过程中遇到的setuptools相关警告和错误,具体是“Unknown distribution option: 'tests_require'”这个问题。根据用户提供的引用信息,特别是引用[1]、[4]和[5],这些错误通常与setuptools版本不兼容有关。 首先,用户提到引用[2]中的解决方案是降级setuptools到58.2.0版本。这可能是因为较新的setuptools版本移除了某些旧选项,比如'tests_require'。但需要确认58.2.0是否兼容当前的项目环境,避免引入其他问题。 另外,引用[5]中解决cffi_modules问题的方法是通过升级pip和安装cffi,这可能对当前问题也有帮助,因为依赖管理工具更新后可能更好地处理版本冲突。但当前问题更直接相关的是setuptools的版本。 接下来,用户遇到的错误信息提到tests_require是一个未知的分发选项,这说明项目中的setup.py或相关配置文件使用了已被弃用的参数。查看引用[3]可知,setuptools.installer和fetch_build_eggs已经被弃用,可能涉及项目配置需要更新。 可能的解决步骤包括: 1. 检查项目中的setup.py,将废弃的选项如tests_require替换为当前支持的参数,例如使用extras_require或者tox等工具管理测试依赖。 2. 降级setuptools到兼容版本,如58.2.0,但需注意其他依赖是否受影响。 3. 更新项目配置,遵循最新的setuptools文档,移除或替换过时的配置选项。 4. 确保pip和setuptools都是最新版本,以避免已知的兼容性问题。 另外,用户使用colcon构建系统,可能需要检查所有依赖包的setup文件,因为colcon会构建工作空间中的所有包,任何一个包使用旧版setuptools语法都可能导致问题。逐个排查并更新这些包的配置或提交补丁可能也是必要的。 总结来说,解决方案应优先考虑更新项目配置,其次是调整setuptools版本,同时确保环境中的工具如pip处于最新状态。需要分步骤指导用户操作,并验证每一步的效果。</think>### 解决方案:解决`colcon build`中setuptools的`Unknown distribution option: 'tests_require'`错误 #### 问题根源分析 1. **废弃参数警告**:`tests_require`是旧版`setuptools`的配置参数,新版本已弃用此选项[^1][^4]。 2. **版本兼容性问题**:项目配置文件(如`setup.py`)使用了过时的语法,而当前环境安装的`setuptools`版本过高[^2][^3]。 3. **依赖传递冲突**:若工作空间内其他ROS包也包含类似问题,会导致`colcon build`整体失败[^4]。 --- #### 分步解决方案 ##### 方法一:降级setuptools版本(快速修复) 1. **卸载当前版本**: ```bash pip uninstall setuptools ``` 2. **安装兼容版本**: ```bash pip install setuptools==58.2.0 # 此版本支持旧参数[^2] ``` 3. **重新编译**: ```bash colcon build --symlink-install ``` ##### 方法二:更新项目配置(推荐长期方案) 1. **修改`setup.py`文件**: - 将`tests_require`替换为现代依赖管理方式: ```python # setup.py示例 setup( ... extras_require={'test': ['pytest', '其他测试依赖']}, # 使用extras_require替代 ... ) ``` - 删除`tests_require`行[^5]。 2. **使用`tox`管理测试环境**(可选): ```ini # tox.ini示例 [testenv] deps = pytest commands = pytest tests/ ``` ##### 方法三:升级构建工具链 1. **更新`pip`和`setuptools`**: ```bash pip install --upgrade pip setuptools # 确保工具链最新[^5] ``` 2. **清理缓存并重新编译**: ```bash rm -rf build/ install/ log/ # 清除旧编译缓存 colcon build --symlink-install ``` --- #### 验证操作 1. **检查版本**: ```bash pip show setuptools # 确认版本为58.2.0或更高兼容版本 ``` 2. **运行单元测试**: ```bash colcon test # 验证测试依赖是否正常加载 ``` --- #### 附加说明 - 若项目中包含第三方库(如`paramiko`),需同步检查其`setup.py`是否包含类似问题,必要时手动提交补丁。 - 对于ROS 2项目,建议检查`package.xml`中的`<test_depend>`标签是否已正确声明测试依赖。
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值