【pyqt5】【多线程】【matplotlib】解决子线程上运行的matplotlib无法在主线程上的UI界面上绘制图形的问题

问题描述

在使用PyQt5为一个项目写UI界面的时候,我试图把matplotlib绘制的图像展示在UI界面的窗口中,但matplotlib是运行在一个子线程中的,这导致它出现了如下警告:UserWarning: Starting a Matplotlib GUI outside of the main thread will likely fail.并且我无法使用matplotlib.backends.backend_qt5agg 下的 FigureCanvasQTAgg直接把图像绘制在UI窗口中,经过我测试如果matplotlib运行在主线程中的话就不会出现该问题。

解决方法

我用了一个笨办法,那就是使用IO.BytesIO()把matplotlib绘制的图像以png或者jpeg格式的二进制流保存在内存中,然后用PIL.Image.open()打开为图片,然后把这张图片经过处理后传递给窗口显示。

buf = io.BytesIO()
fig.savefig(buf, format="png", dpi=100)
buf.seek(0)
img = Image.open(buf)

下面说说图片处理过程:我使用的是QLabel来展示图片,这就需要图片数据类型类型是PyQt5.QtGui.QImage。经常使用下面的代码来展示:

image = QImage(img.data, img.shape[1], img.shape[0], QImage.Format_RGB888)
label.setPixmap(QPixmap.fromImage(image))

其中的img必须是numpy.ndarray类型的,但目前我们获得的img是PIL.PngImagePlugin.PngImageFile类型的。我们可以使用img = numpy.asarray(img)来进行转换,这样就可以使用上面的代码进行展示了。

img = Image.open(buf)
img = np.asarray(img)
image = QImage(img.data, img.shape[1], img.shape[0], QImage.Format_RGB888)
label.setPixmap(QPixmap.fromImage(image))

奇怪的是不知道为什么,通过这样的方法展示出来的图片却总是损坏的,如下图。
损坏的图片

通过打印查看转换为numpy.ndarray类型后的图片数组发现里面大部分数据都是255,但是将这张图片再转换回PIL类型的数据展示或者使用opencv显示却都没问题,这一度让我束手无策。

直到我发现PIL.PngImagePlugin.PngImageFile类型的图片可以使用PIL.ImageQt直接转换为QImage类型进行展示,通过如下代码:

image = ImageQt.ImageQt(img1)
label.setPixmap(QPixmap.fromImage(image))

ok,到这里我的问题就解决啦!最终在UI界面得到了如下效果。
在这里插入图片描述

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
下面是一个简单的示例代码,使用 PyQT5 的 QLineEdit 控件和 Matplotlib绘制图形: ```python import sys import numpy as np from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget, QVBoxLayout, QLineEdit from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas from matplotlib.figure import Figure class MainWindow(QMainWindow): def __init__(self): super(MainWindow, self).__init__() # 创建一个 QWidget centralWidget = QWidget(self) self.setCentralWidget(centralWidget) # 创建一个 QVBoxLayout,并将 QWidget 设置为布局 layout = QVBoxLayout(centralWidget) # 创建一个 QLineEdit self.lineEdit = QLineEdit(self) layout.addWidget(self.lineEdit) # 创建一个 FigureCanvas self.figure = Figure() self.canvas = FigureCanvas(self.figure) layout.addWidget(self.canvas) # 在 Figure 中绘制图形 self.ax = self.figure.add_subplot(111) x = np.linspace(-10, 10, 100) self.ax.plot(x, np.sin(x)) # 将 QLineEdit 的文本更新时绘制新的图形 self.lineEdit.textChanged.connect(self.updatePlot) def updatePlot(self): # 获取 QLineEdit 的文本 text = self.lineEdit.text() # 在 Figure 中绘制新的图形 x = np.linspace(-10, 10, 100) y = eval(text) self.ax.clear() self.ax.plot(x, y) self.canvas.draw() if __name__ == '__main__': app = QApplication(sys.argv) mainWin = MainWindow() mainWin.show() sys.exit(app.exec_()) ``` 这个示例程序创建了一个包含 QLineEdit 控件和 Matplotlib 图形PyQT5 窗口。当用户在 QLineEdit 中输入表达式时,程序会绘制新的图形。注意,在这个示例中,我们使用了 eval 函数将用户的输入作为 Python 代码执行。为了安全起见,你应该在真实的应用程序中采用更加谨慎的方法来执行用户输入的代码。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值