2

I am attempting to insert a MatPlotLib graph into a QWidget type widget in order to graph real-time data from a sensor bank. The idea is to pull data asynchronously, dump it into numpy, then graph it using MatPlotLib.

My issue is that MatPlotLib doesn't want to graph into the form I've built. I've tried separating the widget out into it's own class, but that did not work either. All tutorials online seem to begin with a blank form and then built it from code instead of referencing a ready built .ui file.

The code below is stripped down to simplify things:

from PyQt5 import uic, QtCore
from PyQt5.QtWidgets import QApplication, QMainWindow, QVBoxLayout, QAction

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

import numpy as np


class MyWindow(QMainWindow):

    def __init__(self):
        super(MyWindow, self).__init__()
        # importing ui that is made in Qt Designer
        uic.loadUi('MainForm.ui', self)

        # linking Exit option from File menu to exit action
        exitButton = QAction('Exit', self)
        exitButton.setShortcut('Ctrl+Q')
        exitButton.setStatusTip('Exit the program.')
        self.actionExit.triggered.connect(QApplication.quit)

        # add 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(plt.subplot)
        lay = QVBoxLayout(self.plotWidget)
        lay.setContentsMargins(0, 0, 0, 0)
        lay.addWidget(self.plotWidget)
        # add toolbar
        self.addToolBar(QtCore.Qt.BottomToolBarArea, NavigationToolbar(self.plotWidget, self))


if __name__ == "__main__":
    import sys

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

The MainForm.ui code is:

<?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>736</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>MainWindow</string>
  </property>
  <widget class="QWidget" name="centralWidget">
   <widget class="QWidget" name="plotWidgetHolder" native="true">
    <property name="geometry">
     <rect>
      <x>11</x>
      <y>11</y>
      <width>771</width>
      <height>484</height>
     </rect>
    </property>
    <property name="styleSheet">
     <string notr="true">background-color: rgb(255, 255, 255);</string>
    </property>
   </widget>
   <widget class="QPushButton" name="pushButton">
    <property name="geometry">
     <rect>
      <x>320</x>
      <y>570</y>
      <width>93</width>
      <height>28</height>
     </rect>
    </property>
    <property name="text">
     <string>PushButton</string>
    </property>
   </widget>
  </widget>
  <widget class="QMenuBar" name="menubar">
   <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
     <width>800</width>
     <height>26</height>
    </rect>
   </property>
   <widget class="QMenu" name="menuFile">
    <property name="title">
     <string>File</string>
    </property>
    <addaction name="actionNew"/>
    <addaction name="actionOpen"/>
    <addaction name="separator"/>
    <addaction name="actionExit"/>
   </widget>
   <widget class="QMenu" name="menuData">
    <property name="title">
     <string>Data</string>
    </property>
    <addaction name="actionOpen_Data_Folder"/>
    <addaction name="actionOpen_in_Excel"/>
   </widget>
   <addaction name="menuFile"/>
   <addaction name="menuData"/>
  </widget>
  <widget class="QStatusBar" name="statusbar"/>
  <action name="actionNew">
   <property name="text">
    <string>New</string>
   </property>
  </action>
  <action name="actionOpen">
   <property name="text">
    <string>Open</string>
   </property>
  </action>
  <action name="actionOpen_in_Excel">
   <property name="text">
    <string>Open in Excel</string>
   </property>
  </action>
  <action name="actionOpen_Data_Folder">
   <property name="text">
    <string>Open Data Folder</string>
   </property>
  </action>
  <action name="actionExit">
   <property name="text">
    <string>Exit</string>
   </property>
  </action>
 </widget>
 <resources/>
 <connections/>
</ui>

With this code, I typically get an error code "AttributeError: 'function' object has no attribute 'set_canvas'", and the graph pops up in it's own MatPlotLib generated window instead of inside the larger form.

Any help would be greatly appreciated.

2
  • replace self.plotWidget = FigureCanvas(plt.subplot) with self.plotWidget = FigureCanvas(fig) Commented Mar 3, 2019 at 18:16
  • In addition, don't use pylab or pyplot at all. Remove that import so you are not tempted to use it. Instead create a matplotlib.figure.Figure() as shown in all the tutorials or examples you find on that matter. Commented Mar 3, 2019 at 18:20

1 Answer 1

1

You need to replace

self.plotWidget = FigureCanvas(plt.subplot)
lay = QVBoxLayout(self.plotWidget)
lay.setContentsMargins(0, 0, 0, 0)
lay.addWidget(self.plotWidget)

with

self.plotWidget = FigureCanvas(fig)
self.setCentralWidget(self.plotWidget)

The constructor of FigureCanvasQTAgg wants a figure and a QMainWidget already has a layout.

As ImportanceOfBeingErnest correctly said you should also construct your figure with fig = matplotlib.figure.Figure(). (You can then get your axis with ax1 = fig.gca())

Edit: To add more widgets you must add the figure to a layout which needs a parent widget:

self.plotWidget = FigureCanvas(fig)
self.someButton = QPushButton('click me')
container = QWidget()
layout = QGridLayout()
layout.addWidget(self.plotWidget,0,0)
layout.addWidget(self.someButton,1,0)
container.setLayout(layout)
self.setCentralWidget(container)
Sign up to request clarification or add additional context in comments.

2 Comments

It's pretty important to state what fig would be in this case. Did you see my comment below the question?
This answer got me 90% of the way there. The figure is now inside of the application, but it takes up the entire window and does not allow any of the other widgets to be viewed or used. Any thing else I'm missing? Do I set expansion properties for the QWidget class? I need some control buttons on the main UI at some point. Thank you for the help!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.