在 PyQt5 中嵌入 Matplotlib 图形

Posted

技术标签:

【中文标题】在 PyQt5 中嵌入 Matplotlib 图形【英文标题】:Embed Matplotlib Graph in a PyQt5 【发布时间】:2021-06-30 08:40:36 【问题描述】:

我正在尝试将 matplotlib 图形嵌入到 PqQt5 中,我有一个现成的函数可以调用它们,但是我没有成功。图的代码如下:

import re
import os
import sys
import json 
import numpy as np
import pylab
import mpl_toolkits.mplot3d.axes3d as p3
import matplotlib.pyplot as plt



BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
DATA_DIR = BASE_DIR + os.path.sep + 'data'
if not os.path.exists(DATA_DIR ):
    popup_critical('Грешен път до данните')

with open('./data/71161.json', 'r') as f:
    data = json.load(f)

for distro in data:
    downwelling_radiance = np.array(distro['spectra']['ld']['data'])
    irradiance_reflectance = np.array(distro['spectra']['r0minuscorr']['data'])
    downwelling_irradiance = np.array(distro['spectra']['ed']['data'])
    upwelling_radiance = np.array(distro['spectra']['lu']['data'])
   wavelength = np.array(distro['spectra']['wavelength']['data'])
   reflectance = np.array(distro['spectra']['r0minus']['data'])
   date = np.array(distro['meta']['date'])
   name = np.array(distro['meta']['id'])


def radiance_plot():

    fig = plt.figure(figsize=(14, 8))
    plt.title("Plot")
    plt.xlabel('Wavelength [nm]')
    plt.ylabel('Radiance [W/(m^2xnmxsr)]')

    ax=plt.gca()
    ax.set_xlim(400,800) 

    plt.grid(True, color='skyblue')

    plt.plot(
        wavelength, downwelling_radiance,  'orange',
    wavelength, upwelling_radiance,  'burlywood',
    lw=3,
    )

    print(np.arange(0, max(downwelling_radiance + 0.02) if max(downwelling_radiance) > 
    max(upwelling_radiance) else max(upwelling_radiance + 0.02), step=0.02))
    print(list(np.arange(0, max(downwelling_radiance + 0.02) if max(downwelling_radiance) > 
    max(upwelling_radiance) else max(upwelling_radiance + 0.02), step=0.02)))

    plt.ylim(0.00)
    plt.yticks(list(np.arange(0, max(downwelling_radiance + 0.02) if max(downwelling_radiance) 
    > max(upwelling_radiance) else max(upwelling_radiance + 0.02), step=0.02)))

    plt.legend(['Upwelling radiance', 'Downwelling radiance'], loc='upper left')

    plt.twinx(ax = None)
    plt.plot(
        wavelength, downwelling_irradiance, 'c',
        lw=3, 
    )

    plt.grid(True)
    plt.ylabel('Irradiance [W/(m^2xnm)]')
    plt.yticks(list(np.arange(0, max(downwelling_irradiance + 0.2), 0.2)))
    plt.legend(['Downwelling irradiance'], loc='upper right')
    fig.tight_layout()
    return plt.show()

def reflectance_plot():

    fig = plt.figure(figsize=(14, 8))
    plt.title("Plot")
    plt.xlabel('Wavelength [nm]')
    plt.ylabel('Reflectance [-]')

    ax=plt.gca()
    ax.set_xlim(400,800) 

    plt.grid(True)
    plt.plot(
        wavelength, reflectance,  'burlywood',
        lw=3,
        )

    plt.ylim(0.00)
    plt.yticks(list(np.arange(0, max(reflectance + 0.02), step=0.01)))
    plt.legend(['Radiance'], loc='upper right')
    fig.tight_layout()
    return plt.show()

def date_print():
    return date


def name_print():
    return name

#radiance_plot()
#reflectance_plot()
#name_print()
#date_print()

上面的代码打印图表,现在我想在 PyQt5 中打印图表,这样当用户按下按钮 1 时,它会打印第一个图表,第二个按钮打印第二个图表。有 PyQt5 的代码

from PyQt5.QtWidgets import QApplication, QWidget, QLabel, QVBoxLayout, QListWidget, 
QMainWindow, QPushButton
import sys
from PyQt5.QtGui import QIcon, QFont
from read import date_print, name_print, radiance_plot, reflectance_plot
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
import matplotlib.pyplot as plt
import numpy as np

class Window(QWidget):
    def __init__(self):
        super().__init__()


        self.setGeometry(200, 200, 400, 300)
        self.setWindowTitle("Python and Matplotlib")
        self.create_list()
        self.myUI()
        #chart = Canvas(self)

    def create_list(self):
        vbox = QVBoxLayout()

        self.list_widget = QListWidget()


        self.list_widget.insertItem(0, "File1")
        self.list_widget.insertItem(1, "File2")
        self.list_widget.insertItem(2, "file3")
        self.list_widget.setStyleSheet("background-color:red")
        self.list_widget.setFont(QFont("Sanserif", 15))

        self.list_widget.clicked.connect(self.item_clicked)

        self.label = QLabel("")
        self.setFont(QFont("Sanserif", 14))
        self.label.setStyleSheet("color:green")

        vbox.addWidget(self.list_widget)
        vbox.addWidget(self.label)
        self.setLayout(vbox)
    

    def item_clicked(self):
        item = self.list_widget.currentItem()
        self.label.setText("You have selected file:" + str(name_print()) + "; Date: " + 
        str(date_print()))

    def myUI(self):

        #canvas = Canvas(self, width=8, height=4)
        #canvas.move(0,0)

        butt = QPushButton("Click Me", self)
        butt.move(100, 100)

        butt2 = QPushButton("Click Me 2", self)
        butt2.move(250, 100)
"""
class Canvas(FigureCanvas):
    def __init__(self, parent):
        fig, self.ax = plt.subplots(figsize=(5,4), dpi = 50)
        super().__init__(fig)
        self.setParent(parent)
    
        self.plot()

    def plot(self):

        ref = self.figure.add_subplot(111)
        ref.pie(reflectance_plot(), labels=labels)
    
"""

App = QApplication(sys.argv)
window = Window()
window.show()
sys.exit(App.exec_())

我尝试连接它们,但无法打印任何内容。我找到了一种打印图表的方法,但是在我的情况下它不起作用

import sys
import matplotlib
matplotlib.use('Qt5Agg')

from PyQt5 import QtCore, QtWidgets

from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg, NavigationToolbar2QT as NavigationToolbar
from matplotlib.figure import Figure
from read import wavelength, downwelling_radiance, upwelling_radiance

import pandas as pd


class MplCanvas(FigureCanvasQTAgg):

    def __init__(self, parent=None, width=5, height=4, dpi=100):
        fig = Figure(figsize=(width, height), dpi=dpi)
        self.axes = fig.add_subplot(111)
        super(MplCanvas, self).__init__(fig)


class MainWindow(QtWidgets.QMainWindow):

    def __init__(self, *args, **kwargs):
        super(MainWindow, self).__init__(*args, **kwargs)

        # Create the maptlotlib FigureCanvas object,
        # which defines a single set of axes as self.axes.
        sc = MplCanvas(self, width=10, height=5, dpi=100)
        
        # Create our pandas DataFrame with some simple
        # data and headers. 

        

        df = pd.DataFrame([
              [0, 10], [5, 15], [2, 20], [15, 25], [4, 10],
        ], columns=['A', 'B'])

        # plot the pandas DataFrame, passing in the
        # matplotlib Canvas axes.
        
        df.plot(ax=sc.axes)

        # Create toolbar, passing canvas as first parament, parent (self, the MainWindow) as second.
        toolbar = NavigationToolbar(sc, self)

        layout = QtWidgets.QVBoxLayout()
        layout.addWidget(toolbar)
        layout.addWidget(sc)

        

        # Create a placeholder widget to hold our toolbar and canvas.
        widget = QtWidgets.QWidget()
        widget.setLayout(layout)
        self.setCentralWidget(widget)
        self.show()


app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
app.exec_()

知道如何连接它们

【问题讨论】:

【参考方案1】:

我为您创建了这个最小的示例,展示了如何将 matplotlib 绘图嵌入到您的 pyqt5 应用程序中(同样适用于 pyside2)。 我在您的代码中看到,您一直在调用 matplotlib.pyplot。嵌入 gui 后端时,您不应该与 pyplot 交谈。 该示例还说明了如何通过调用轴/图形对象上的方法而不是通过 plt 来设置网格、图例等。 有 3 个按钮,玩弄它,您将很快了解如何嵌入 matplotlib 以及如何处理您的“自定义绘图小部件”。 编辑:使用键盘键 1、2、3,您也可以调用绘图方法。

from PyQt5 import QtWidgets as qtw
from PyQt5 import QtCore as qtc
# in case of pyside: just replace PyQt5->PySide2

from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg
from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT
from matplotlib.figure import Figure

class MplWidget(qtw.QWidget):
  
    def __init__(self, parent=None):
        
        super().__init__(parent)

        fig = Figure(figsize=(5, 5))
        self.can = FigureCanvasQTAgg(fig)
        self.toolbar = NavigationToolbar2QT(self.can, self)
        layout = qtw.QVBoxLayout(self)
        layout.addWidget(self.toolbar)
        layout.addWidget(self.can)

        # here you can set up your figure/axis
        self.ax = self.can.figure.add_subplot(111)

    def plot_basic_line(self, X, Y, label):
        # plot a basic line plot from x and y values.
        self.ax.cla() # clears the axis
        self.ax.plot(X, Y, label=label)
        self.ax.grid(True)
        self.ax.legend()
        self.can.figure.tight_layout()
        self.can.draw()

class MyQtApp(qtw.QWidget):
    def __init__(self):
        super(MyQtApp, self).__init__()

        # layout
        self.mpl_can = MplWidget(self)
        self.btn_plot1 = qtw.QPushButton('plot1', self)
        self.btn_plot2 = qtw.QPushButton('plot2', self)
        self.btn_plot3 = qtw.QPushButton('scatter', self)
        self.layout = qtw.QVBoxLayout(self)
        self.layout.addWidget(self.mpl_can)
        self.layout.addWidget(self.btn_plot1)
        self.layout.addWidget(self.btn_plot2)
        self.layout.addWidget(self.btn_plot3)
        self.setLayout(self.layout)

        # connects
        self.btn_plot1.clicked.connect(self.plot1)
        self.btn_plot2.clicked.connect(self.plot2)
        self.btn_plot3.clicked.connect(self.plot_scatter)

    def keyPressEvent(self, event):
        if event.key() == qtc.Qt.Key_1: self.plot1()
        elif event.key() == qtc.Qt.Key_2: self.plot2()
        elif event.key() == qtc.Qt.Key_3: self.plot_scatter()

    def plot1(self):
        X, Y = (1, 2), (1, 2)
        self.mpl_can.plot_basic_line(X, Y, label='plot1')

    def plot2(self):
        X, Y = (10, 20), (10, 20)
        self.mpl_can.plot_basic_line(X, Y, label='plot2')
    
    def plot_scatter(self):
        X, Y = (1, 7, 9, 3, 2), (3, 6, 8, 2, 11)
        self.mpl_can.ax.scatter(X, Y, label='scatter')
        self.mpl_can.ax.legend()
        self.mpl_can.can.draw()


if __name__ == '__main__':
    app = qtw.QApplication([])
    qt_app = MyQtApp()
    qt_app.show()
    app.exec_()

https://www.mfitzp.com/tutorials/plotting-matplotlib/ 是关于如何将 matplotlib 嵌入 pyqt5 的更深入的教程,但我认为出于您的目的,上面非常基本的示例就足够了。

【讨论】:

以上是关于在 PyQt5 中嵌入 Matplotlib 图形的主要内容,如果未能解决你的问题,请参考以下文章

将 Matplotlib 图形嵌入到小部件中 - PyQt5

Matplotlib 将图形嵌入到 UI PyQt5

在 PyQt5 中嵌入 Matplotlib:工具栏不起作用

为啥我不能在 pyqt5 gui 中嵌入的 matplotlib 颜色图中成功绘制感兴趣区域?

无法在 pyqt5 中嵌入的 matplotlib 上绘制线条

使用 Qt Designer 表单和 PyQt5 在 QWidget 中绘制 matplotlib 图形