【问题标题】:Embed a matplotlib plot in a pyqt5 gui在 pyqt5 gui 中嵌入 matplotlib 图
【发布时间】:2019-04-08 23:31:21
【问题描述】:

我正在使用 PyQt5 尝试为我的数据分析工具生成 GUI。我的问题是我不明白如何嵌入具有完整功能的 matplotlib 图。

所有关于 PyQt5 以及如何嵌入 matplotlib 的教程都展示了一种非常简单的方法,即直接在代码中创建所有图形对象。我不想这样做,因为我的 GUI 是用 Qt Designer 生成的。我创建了一个 QWidget 来在其中绘制数据。因此,我在代码中导入了UI文件:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import sys
import numpy as np

from PyQt5.QtGui import QPixmap
from PyQt5 import QtCore, QtWidgets
from PyQt5.QtWidgets import QMainWindow, QLabel, QGridLayout, QWidget, QTableWidget, QTableWidgetItem
from PyQt5.QtWidgets import QApplication, QWidget, QInputDialog, QLineEdit, QFileDialog
from PyQt5.QtWidgets import QPushButton
from PyQt5.QtCore import QSize
from PyQt5 import QtCore, QtGui, uic

import matplotlib
matplotlib.use('QT5Agg')

import matplotlib.pylab as plt

from matplotlib.backends.qt_compat import QtCore, QtWidgets, is_pyqt5
from matplotlib.backends.backend_qt5agg import FigureCanvas,     NavigationToolbar2QT as NavigationToolbar
from matplotlib.figure import Figure

class MyWindow(QMainWindow):
    def __init__(self):

        super(MyWindow, self).__init__()

        self.ui = uic.loadUi('test.ui', self) 

        # test data
        data = np.array([0.7,0.7,0.7,0.8,0.9,0.9,1.5,1.5,1.5,1.5])        
        fig, ax1 = plt.subplots()
        bins = np.arange(0.6, 1.62, 0.02)
        n1, bins1, patches1 = ax1.hist(data, bins, alpha=0.6, density=False, cumulative=False)

        # plot
        self.plotWidget = FigureCanvas(fig)        

        # add toolbar
        self.addToolBar(QtCore.Qt.BottomToolBarArea, NavigationToolbar(self.plotWidget, self))        

    #########################################

    # show window
    self.show() 

    #########################################

if __name__ == '__main__':

    app = QtWidgets.QApplication(sys.argv)
    window = MyWindow()
    sys.exit(app.exec_())

这是 Qt Designer 创建的 test.ui 文件:

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>Form</class>
 <widget class="QWidget" name="Form">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>772</width>
    <height>650</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>Form</string>
  </property>
  <widget class="QWidget" name="plotWidge" native="true">
   <property name="geometry">
    <rect>
     <x>20</x>
     <y>20</y>
     <width>721</width>
     <height>571</height>
    </rect>
   </property>
  </widget>
 </widget>
 <resources/>
 <connections/>
</ui>

发生的事情是我可以在我的窗口中看到一个工具栏,但现在是数据。我可以使用“保存”图标将绘图保存为图像。我认为我正在创建一个与我在 Qt Designer 中创建的对象不相关的小部件对象的第二个实例。

我该如何解决这个问题? 提前致谢!

【问题讨论】:

  • @eyllanesc 我添加了一个完整的例子
  • 分享 test.ui..
  • 小部件需要添加到 pyqt 中的布局中。我没有看到您在代码中的任何位置为 FigureCanvas 执行此操作。
  • @eyllanesc 我添加了用户界面代码

标签: python matplotlib pyqt5


【解决方案1】:

我认为您在 Qt Designer 中创建了一个名为 plotWidge 的小部件而您正在使用 self.plotWidget = FigureCanvas(fig) 替换它(即使名称一致,该操作并未完成),所以它不会导致混淆在 Qt Designer 中将 plotWidge 更改为 content_plot,因此必须借助布局将 plotWidget 放置在 content_plot 中。

test.ui

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>Form</class>
 <widget class="QWidget" name="Form">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>772</width>
    <height>650</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>Form</string>
  </property>
  <widget class="QWidget" name="content_plot" native="true">
   <property name="geometry">
    <rect>
     <x>20</x>
     <y>20</y>
     <width>721</width>
     <height>571</height>
    </rect>
   </property>
  </widget>
 </widget>
 <resources/>
 <connections/>
</ui>

*.py

import sys
import numpy as np

from PyQt5 import QtCore, QtWidgets, uic

import matplotlib
matplotlib.use('QT5Agg')

import matplotlib.pylab as plt
from matplotlib.backends.backend_qt5agg import FigureCanvas 
from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar

class MyWindow(QtWidgets.QMainWindow):
    def __init__(self):
        super(MyWindow, self).__init__()
        uic.loadUi('test.ui', self) 
        # test data
        data = np.array([0.7,0.7,0.7,0.8,0.9,0.9,1.5,1.5,1.5,1.5])        
        fig, ax1 = plt.subplots()
        bins = np.arange(0.6, 1.62, 0.02)
        n1, bins1, patches1 = ax1.hist(data, bins, alpha=0.6, density=False, cumulative=False)
        # plot
        self.plotWidget = FigureCanvas(fig)
        lay = QtWidgets.QVBoxLayout(self.content_plot)  
        lay.setContentsMargins(0, 0, 0, 0)      
        lay.addWidget(self.plotWidget)
        # add toolbar
        self.addToolBar(QtCore.Qt.BottomToolBarArea, NavigationToolbar(self.plotWidget, self))

if __name__ == '__main__':

    app = QtWidgets.QApplication(sys.argv)
    window = MyWindow()
    window.show()
    sys.exit(app.exec_())

【讨论】:

    【解决方案2】:

    通过在 Python 中使用 super(),GUI 设计变得更加灵活。使用 Qt 设计器创建 GUI (.ui) 后,我们可以通过将其转换为 python (.py) 文件来进一步扩展其功能: pyuic5 -x test.ui -o test.py

    对于那些寻找从 Qt 设计器开始到类似本教程 code 的解决方案的人:

    test.ui

    <?xml version="1.0" encoding="UTF-8"?>
    <ui version="4.0">
     <class>MainWindow</class>
     <widget class="QMainWindow" name="MainWindow">
      <property name="geometry">
       <rect>
        <x>0</x>
        <y>0</y>
        <width>800</width>
        <height>600</height>
       </rect>
      </property>
      <property name="windowTitle">
       <string>MainWindow</string>
      </property>
      <widget class="QWidget" name="centralwidget">
       <layout class="QGridLayout" name="gridLayout">
        <item row="0" column="0">
         <layout class="QHBoxLayout" name="horizontalLayout">
          <item>
           <widget class="QLabel" name="label">
            <property name="text">
             <string>Select Theme</string>
            </property>
           </widget>
          </item>
          <item>
           <widget class="QComboBox" name="comboBox"/>
          </item>
          <item>
           <widget class="QPushButton" name="pushButton">
            <property name="text">
             <string>Open</string>
            </property>
           </widget>
          </item>
          <item>
           <spacer name="horizontalSpacer">
            <property name="orientation">
             <enum>Qt::Horizontal</enum>
            </property>
            <property name="sizeHint" stdset="0">
             <size>
              <width>40</width>
              <height>20</height>
             </size>
            </property>
           </spacer>
          </item>
         </layout>
        </item>
        <item row="1" column="0">
         <layout class="QVBoxLayout" name="verticalLayout">
          <item>
           <spacer name="verticalSpacer">
            <property name="orientation">
             <enum>Qt::Vertical</enum>
            </property>
            <property name="sizeHint" stdset="0">
             <size>
              <width>20</width>
              <height>40</height>
             </size>
            </property>
           </spacer>
          </item>
         </layout>
        </item>
       </layout>
      </widget>
      <widget class="QMenuBar" name="menubar">
       <property name="geometry">
        <rect>
         <x>0</x>
         <y>0</y>
         <width>800</width>
         <height>22</height>
        </rect>
       </property>
       <widget class="QMenu" name="menuFile">
        <property name="title">
         <string>File</string>
        </property>
        <addaction name="actionOpen_csv_file"/>
        <addaction name="actionExit"/>
       </widget>
       <addaction name="menuFile"/>
      </widget>
      <widget class="QStatusBar" name="statusbar"/>
      <action name="actionOpen_csv_file">
       <property name="text">
        <string>Open csv file</string>
       </property>
      </action>
      <action name="actionExit">
       <property name="text">
        <string>Exit</string>
       </property>
      </action>
     </widget>
     <resources/>
     <connections/>
    </ui>
    
    

    以及完整编辑的test.py

    from PyQt5 import QtCore, QtGui, QtWidgets
    import matplotlib
    import matplotlib.pyplot as plt
    matplotlib.use('Qt5Agg')
    from PyQt5 import QtCore, QtWidgets
    from PyQt5.QtWidgets import QFileDialog
    from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg, NavigationToolbar2QT as Navi
    from matplotlib.figure import Figure
    import seaborn as sns
    import pandas as pd
    import sip # can be installed : pip install sip
    
    # We require a canvas class
    import platform
    
    # Use NSURL as a workaround to pyside/Qt4 behaviour for dragging and dropping on OSx
    op_sys = platform.system()
    if op_sys == 'Darwin':
        from Foundation import NSURL
    
    class MatplotlibCanvas(FigureCanvasQTAgg):
        def __init__(self,parent=None, dpi = 120):
            fig = Figure(dpi = dpi)
            self.axes = fig.add_subplot(111)
            super(MatplotlibCanvas,self).__init__(fig)
            fig.tight_layout()
            
            
    
    class Ui_MainWindow(object):
        def setupUi(self, MainWindow):
            MainWindow.setObjectName("MainWindow")
            MainWindow.resize(1440, 1000)
            self.centralwidget = QtWidgets.QWidget(MainWindow)
            self.centralwidget.setObjectName("centralwidget")
            self.gridLayout = QtWidgets.QGridLayout(self.centralwidget)
            self.gridLayout.setObjectName("gridLayout")
            self.horizontalLayout = QtWidgets.QHBoxLayout()
            self.horizontalLayout.setObjectName("horizontalLayout")
            self.label = QtWidgets.QLabel(self.centralwidget)
            self.label.setObjectName("label")
            self.horizontalLayout.addWidget(self.label)
            self.comboBox = QtWidgets.QComboBox(self.centralwidget)
            self.comboBox.setObjectName("comboBox")
            self.horizontalLayout.addWidget(self.comboBox)
            self.pushButton = QtWidgets.QPushButton(self.centralwidget)
            self.pushButton.setObjectName("pushButton")
            self.horizontalLayout.addWidget(self.pushButton)
            spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
            self.horizontalLayout.addItem(spacerItem)
            self.gridLayout.addLayout(self.horizontalLayout, 0, 0, 1, 1)
            self.verticalLayout = QtWidgets.QVBoxLayout()
            self.verticalLayout.setObjectName("verticalLayout")
            self.spacerItem1 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
            self.verticalLayout.addItem(self.spacerItem1)
            self.gridLayout.addLayout(self.verticalLayout, 1, 0, 1, 1)
            
            MainWindow.setCentralWidget(self.centralwidget)
            self.menubar = QtWidgets.QMenuBar(MainWindow)
            self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 22))
            self.menubar.setObjectName("menubar")
            self.menuFile = QtWidgets.QMenu(self.menubar)
            self.menuFile.setObjectName("menuFile")
            MainWindow.setMenuBar(self.menubar)
            self.statusbar = QtWidgets.QStatusBar(MainWindow)
            self.statusbar.setObjectName("statusbar")
            MainWindow.setStatusBar(self.statusbar)
            self.actionOpen_csv_file = QtWidgets.QAction(MainWindow)
            self.actionOpen_csv_file.setObjectName("actionOpen_csv_file")
            self.actionExit = QtWidgets.QAction(MainWindow)
            self.actionExit.setObjectName("actionExit")
            self.menuFile.addAction(self.actionOpen_csv_file)
            self.menuFile.addAction(self.actionExit)
            self.menubar.addAction(self.menuFile.menuAction())
    
            self.retranslateUi(MainWindow)
            QtCore.QMetaObject.connectSlotsByName(MainWindow)
            
            self.filename = ''
            self.canv = MatplotlibCanvas(self)
            self.df = []
            
            self.toolbar = Navi(self.canv,self.centralwidget)
            self.horizontalLayout.addWidget(self.toolbar)
            
            self.themes = ['bmh', 'classic', 'dark_background', 'fast', 
            'fivethirtyeight', 'ggplot', 'grayscale', 'seaborn-bright',
             'seaborn-colorblind', 'seaborn-dark-palette', 'seaborn-dark', 
             'seaborn-darkgrid', 'seaborn-deep', 'seaborn-muted', 'seaborn-notebook',
             'seaborn-paper', 'seaborn-pastel', 'seaborn-poster', 'seaborn-talk',
             'seaborn-ticks', 'seaborn-white', 'seaborn-whitegrid', 'seaborn',
             'Solarize_Light2', 'tableau-colorblind10']
             
            self.comboBox.addItems(self.themes)
            
            self.pushButton.clicked.connect(self.getFile)
            self.comboBox.currentIndexChanged['QString'].connect(self.Update)
            self.actionExit.triggered.connect(MainWindow.close)
            self.actionOpen_csv_file.triggered.connect(self.getFile)
            
        def Update(self,value):
            print("Value from Combo Box:",value)
            plt.clf()
            plt.style.use(value)
            try:
                self.horizontalLayout.removeWidget(self.toolbar)
                self.verticalLayout.removeWidget(self.canv)
                
                sip.delete(self.toolbar)
                sip.delete(self.canv)
                self.toolbar = None
                self.canv = None
                self.verticalLayout.removeItem(self.spacerItem1)
            except Exception as e:
                print(e)
                pass
            self.canv = MatplotlibCanvas(self)
            self.toolbar = Navi(self.canv,self.centralwidget)
            
            self.horizontalLayout.addWidget(self.toolbar)
            self.verticalLayout.addWidget(self.canv)
            
            self.canv.axes.cla()
            ax = self.canv.axes
            self.df.plot(ax = self.canv.axes)
            legend = ax.legend()
            legend.set_draggable(True)
            
            ax.set_xlabel('X axis')
            ax.set_ylabel('Y axis')
            ax.set_title(self.Title)
            
            self.canv.draw()
            
            
            
            
        
        def getFile(self):
            """ This function will get the address of the csv file location
                also calls a readData function 
            """
            self.filename = QFileDialog.getOpenFileName(filter = "csv (*.csv)")[0]
            print("File :", self.filename)
            self.readData()
        
        def readData(self):
            """ This function will read the data using pandas and call the update
                function to plot
            """
            import os
            base_name = os.path.basename(self.filename)
            self.Title = os.path.splitext(base_name)[0]
            print('FILE',self.Title )
            self.df = pd.read_csv(self.filename,encoding = 'utf-8').fillna(0)
            self.Update(self.themes[0]) # lets 0th theme be the default : bmh
        
    
        
        def retranslateUi(self, MainWindow):
            _translate = QtCore.QCoreApplication.translate
            MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
            self.label.setText(_translate("MainWindow", "Select Theme"))
            self.pushButton.setText(_translate("MainWindow", "Open"))
            self.menuFile.setTitle(_translate("MainWindow", "File"))
            self.actionOpen_csv_file.setText(_translate("MainWindow", "Open csv file"))
            self.actionExit.setText(_translate("MainWindow", "Exit"))
    
    
    if __name__ == "__main__":
        import sys
        app = QtWidgets.QApplication(sys.argv)
        MainWindow = QtWidgets.QMainWindow()
        ui = Ui_MainWindow()
        ui.setupUi(MainWindow)
        
        MainWindow.show()
        sys.exit(app.exec_())
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-04-20
      • 2018-06-14
      • 2019-04-14
      • 1970-01-01
      • 2021-12-04
      • 2021-09-30
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多