显示来自其他文件的 Matplotlib 图

2022-01-12 00:00:00 python matplotlib pyqt pyqt5

问题描述

我有一个 PyQt 文件,只需单击一个按钮,它就会运行另一个 python 文件(我们称之为 calc.py)中的函数,该文件执行非常高级的计算并使用 matplotlib 进行绘图结果.

I have a PyQt file that, on the click of a button, runs a function from another python file (let's call calc.py) that carries out a really advanced calculation and uses matplotlib to plot the result.

我想在计算完成/生成图形后,在 PyQt 窗口中显示 calc.py 生成的这个图.不过,鉴于计算和数字是在另一个文件中进行的,我不确定执行此操作的最佳方法.

I would like to show this plot generated by calc.py in the PyQt window after the calculation is complete/the figure is generated. Although, I'm not sure the best way to carry this out given that the calculation and figure are made in another file.

pyqt.py

import PyQt5 . . .
from calc import do_calc

class Window(QMainWindow):
    def __init__(self, parent=None):
        super(Window, self).__init__(parent)
        self.title_label = QLabel("Label Text", self)  #random label in top of window
            
        ###code here to display matplotlib plot in my PyQt app####

if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = Window()
    window.show()
    sys.exit(app.exec_())

calc.py

import matplotlib.pyplot as plt
 
def do_calc():
   plt.figure()
   for i in x: 
      ...        #really complex calculation here
      plt.plot()

   plt.draw()

我一直在查看如何在 Qt 中显示 matplotlib 图的其他示例,但它们通常围绕在 PyQt 文件或小部件类中进行的计算,在这种情况下我无法真正做到.我可以更改 calc.py 文件以返回项目或执行任何其他可能有用的操作,但计算可能需要保留在该文件中,以便它可以独立于 PyQt 运行

I've been looking at other examples of how to display matplotlib plots in Qt but they usually orient around the calculation being done in the PyQt file or widget class which I can't really do in this instance. I can alter the calc.py file to return items or do anything else that might be helpful, but the calculations will likely need to stay in that file so it can be ran independently from the PyQt


解决方案

解决方案是一个hack(将来可能会失败)假设calc.py中matplotlib使用的后端使用PyQt5,为此需要先导入 PyQt5,再导入 calc.py.

The solution is a hack(may fail in the future) that assumes that the backend used by matplotlib in calc.py uses PyQt5, for this it is necessary to import PyQt5 first and then calc.py.

逻辑是使用plt.ion使matplotlib不阻塞eventloop,然后在以FigureCanvas作为centralWidget的顶层(窗口)中搜索.

The logic is to make matplotlib not block the eventloop using plt.ion, and then search among the toplevels (windows) that have a FigureCanvas as their centralWidget.

calc.py

import matplotlib.pyplot as plt
import numpy as np


def do_calc():
    t = np.arange(0.0, 2.0, 0.01)
    s = 1 + np.sin(2 * np.pi * t)

    fig, ax = plt.subplots()
    ax.plot(t, s)

    ax.set(
        xlabel="time (s)",
        ylabel="voltage (mV)",
        title="About as simple as it gets, folks",
    )
    ax.grid()
    plt.show()

ma​​in.py

import sys

from PyQt5.QtWidgets import QApplication, QLabel, QMainWindow, QVBoxLayout, QWidget

import matplotlib.pyplot as plt
from matplotlib.backends.backend_qt5agg import FigureCanvas

from calc import do_calc



class Window(QMainWindow):
    def __init__(self, parent=None):
        super(Window, self).__init__(parent)

        self.title_label = QLabel("Label Text")

        central_widget = QWidget()
        self.setCentralWidget(central_widget)
        lay = QVBoxLayout(central_widget)
        lay.addWidget(self.title_label)

        plt.ion()
        do_calc()

        for tl in QApplication.topLevelWidgets():
            if isinstance(tl, QMainWindow) and isinstance(
                tl.centralWidget(), FigureCanvas
            ):
                lay.addWidget(tl)


if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = Window()
    window.show()
    sys.exit(app.exec_())

另一个更好的选择是获取所有图形,然后是画布,最后是该画布的窗口:

Another better option is to get all the Figures and then the canvas and finally the window of that canvas:

class Window(QMainWindow):
    def __init__(self, parent=None):
        super(Window, self).__init__(parent)

        self.title_label = QLabel("Label Text")

        central_widget = QWidget()
        self.setCentralWidget(central_widget)
        lay = QVBoxLayout(central_widget)
        lay.addWidget(self.title_label)

        plt.ion()
        do_calc()

        for i in plt.get_fignums():
            canvas = plt.figure(i).canvas
            if isinstance(canvas, QWidget):
                lay.addWidget(canvas.window())

相关文章