Memory leak when embedding matplotlib in PyQt5 GUI - python

I built a Matplotlib GUI with Qt Designer basically following this tutorial. I am using python 3.5.2 and pyqt 5.6.0 . The code you can see below is working. However when changing the plots the memory used by my system increases, at least according to Windows 10 Task Manager. To recreate this a little better one can increase the number of random values used in the plot commands.
It seems like the self.canvas.close() command inside the rmmppl function is not enough to actually free the used memory.
How can I prevent the increasing memory usage?
edit: here is a Screenshot of the GUI
from PyQt5 import QtCore, QtGui, QtWidgets
from matplotlib.figure import Figure
from matplotlib.backends.backend_qt5agg import (
FigureCanvasQTAgg as FigureCanvas,
NavigationToolbar2QT as NavigationToolbar)
import window
import numpy as np
class Plotter(QtWidgets.QMainWindow, window.Ui_MainWindow):
def __init__(self):
super(Plotter, self).__init__()
self.setupUi(self)
self.fig_dict = {}
self.mplfigs.itemClicked.connect(self.changefig)
def addmpl(self, fig):
self.canvas = FigureCanvas(fig)
self.mplvl.addWidget(self.canvas)
self.canvas.draw()
self.toolbar = NavigationToolbar(self.canvas,self.mplwindow, coordinates = True)
self.mplvl.addWidget(self.toolbar)
def rmmppl(self):
self.mplvl.removeWidget(self.canvas)
self.canvas.close()
self.mplvl.removeWidget(self.toolbar)
self.toolbar.close()
def addfig(self, name, fig):
self.fig_dict[name]=fig
self.mplfigs.addItem(name)
def changefig(self,item):
text = item.text()
self.rmmppl()
self.addmpl(self.fig_dict[text])
def main():
import sys
fig1 = Figure()
ax1f1= fig1.add_subplot(111)
ax1f1.plot(np.random.rand(5))
fig2 = Figure()
ax1f2 = fig2.add_subplot(121)
ax1f2.plot(np.random.rand(5))
ax1f2 = fig2.add_subplot(122)
ax1f2.plot(np.random.rand(10))
app=QtWidgets.QApplication(sys.argv)
main=Plotter()
main.addmpl(fig1)
main.addfig('Figure 1', fig1)
main.addfig('Figure 2', fig2)
main.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
The window.py which is the Basic GUI structure:
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(640, 432)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.horizontalLayout = QtWidgets.QHBoxLayout(self.centralwidget)
self.horizontalLayout.setObjectName("horizontalLayout")
self.mplwindow = QtWidgets.QWidget(self.centralwidget)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Preferred)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.mplwindow.sizePolicy().hasHeightForWidth())
self.mplwindow.setSizePolicy(sizePolicy)
self.mplwindow.setObjectName("mplwindow")
self.mplvl = QtWidgets.QVBoxLayout(self.mplwindow)
self.mplvl.setContentsMargins(0, 0, 0, 0)
self.mplvl.setObjectName("mplvl")
self.horizontalLayout.addWidget(self.mplwindow)
self.mplfigs = QtWidgets.QListWidget(self.centralwidget)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Expanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.mplfigs.sizePolicy().hasHeightForWidth())
self.mplfigs.setSizePolicy(sizePolicy)
self.mplfigs.setMaximumSize(QtCore.QSize(200, 16777215))
self.mplfigs.setMinimumSize(QtCore.QSize(200, 0))
self.mplfigs.setObjectName("mplfigs")
self.horizontalLayout.addWidget(self.mplfigs)
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 640, 31))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))

I have executed your code on Linux and I have observed the same memory leak.
Try with this:
Import the garbage collector with:
import gc
Then modify your rmmppl method:
def rmmppl(self):
self.canvas.close()
self.canvas.deleteLater()
self.toolbar.close()
self.toolbar.deleteLater()
gc.collect()
Here is the documentation for deleteLater QObject Class Documentation

Related

strange behaviour of pyplotlib upper menubar

I am willing to integrate a matplotlib figure into a GUI designed with pyqt5:
I wrote the code above:
from PyQt5 import QtCore, QtWidgets
from PyQt5.QtWidgets import QMainWindow
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar
import matplotlib.pyplot as plt
import random
class Ui_MainWindow(QMainWindow):
def setupUi(self, MainWindow):
self.vbox = QtWidgets.QVBoxLayout()
MainWindow.setObjectName("MainWindow")
MainWindow.resize(1056, 600)
self.vbox2 = MainWindow.layout()
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.lineEdit_x = QtWidgets.QLineEdit(self.centralwidget)
self.lineEdit_x.setGeometry(QtCore.QRect(550, 430, 40, 20))
self.lineEdit_x.setObjectName("lineEdit_x")
self.lineEdit_x.setText(str(1))
self.lineEdit_y = QtWidgets.QLineEdit(self.centralwidget)
self.lineEdit_y.setGeometry(QtCore.QRect(595, 430, 40, 20))
self.lineEdit_y.setObjectName("lineEdit_y")
self.lineEdit_y.setText(str(1))
self.label_3 = QtWidgets.QLabel(self.centralwidget)
self.label_3.setGeometry(QtCore.QRect(490, 430, 66, 13))
self.label_3.setObjectName("label_3")
self.figure = plt.figure()#figsize=(40, 10), dpi=18)
self.canvas = FigureCanvas(self.figure)
#self.canvas.mpl_connect("button_release_event", self.on_release)
self.canvas.mpl_connect("button_press_event", self.on_press)
self.canvas.setGeometry(QtCore.QRect(0,30, 400, 400))
self.toolbar = NavigationToolbar(self.canvas, self)
self.vbox2.addWidget(self.toolbar)
self.vbox2.addWidget(self.canvas)
self.plot()
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 1056, 21))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def plot(self):
data = [random.random() for i in range(23)]
ax = self.figure.add_subplot(111)
ax.plot(data, 'r-', linewidth=0.5)
ax.set_title('PyQt Matplotlib Example')
self.canvas.draw()
def on_press(self, event):
self.lineEdit_x.setText(str(event.x))
self.lineEdit_y.setText(str(event.y))
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.label_3.setText(_translate("MainWindow", "Center (x,y)"))
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_())
as one can see I have 2 issues:
the menu bar is detached from the figure and reduced
the axes are smaller than the figure field.
so can someone please check my code to address these two issues with minimum modifications
thanks
There are many issues with your code, and most of them are caused by the fact that you're editing a pyuic file, which is considered a bad practice, and one of the many reasons of that consideration is that the class pyuic provides is often being used in the wrong way, like in this case.
First of all, you're creating instances for both QMainWindow and Ui_MainWindow, which already inherits from QMainWindow, so the first one is completely pointless.
Then, you're trying to access the main window layout, but that's also wrong, as QMainWindow has its own private layout. The only proper way to add widgets to a main window is using the provided API, and, in your case:
setCentralWidget() to set the central widget, which is the main content of the window, and set its layout to which the actual widgets are being added;
addToolBar() to add tool bars;
So, this is a proper rewriting of your expected result.
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.resize(1056, 600)
self.figure = plt.figure()
self.canvas = FigureCanvas(self.figure)
self.canvas.mpl_connect('button_press_event', self.on_press)
self.toolbar = NavigationToolbar(self.canvas, self)
self.addToolBar(self.toolbar)
self.label = QtWidgets.QLabel('Center (x,y)')
self.lineEdit_x = QtWidgets.QLineEdit()
self.lineEdit_x.setText(str(1))
self.lineEdit_y = QtWidgets.QLineEdit()
self.lineEdit_y.setText(str(1))
central = QtWidgets.QWidget(self)
self.setCentralWidget(central)
mainLayout = QtWidgets.QVBoxLayout(central)
mainLayout.addWidget(self.canvas)
bottomLayout = QtWidgets.QHBoxLayout()
mainLayout.addLayout(bottomLayout)
bottomLayout.addWidget(self.label)
bottomLayout.addWidget(self.lineEdit_x)
bottomLayout.addWidget(self.lineEdit_y)
self.plot()
def plot(self):
data = [random.random() for i in range(23)]
ax = self.figure.add_subplot(111)
ax.plot(data, 'r-', linewidth=0.5)
ax.set_title('PyQt Matplotlib Example')
self.canvas.draw()
def on_press(self, event):
self.lineEdit_x.setText(str(event.x))
self.lineEdit_y.setText(str(event.y))
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
mainWindow = MainWindow()
mainWindow.show()
sys.exit(app.exec_())
Note that if you still want to use Designer, then keep in mind that:
as said above, you should not edit the pyuic generated file (but, instead, follow the official guidelines about using Designer;
add the canvas by code, or use a promoted widget (do some research on the subject);
always use layout managers for all widgets;

My PyQtGraph code can run with PyQt5, but not

I am trying to make program with PyQtGraph and Qt designer.
My code can run in PyQt5 as expected.
But with PySide2, my code plot the a line outside the plot area.
This is the screenshot of the matter:
I'm using Qt Designer to make .ui.
And this is my code made by pyuic5, qtGraphTestQt5.py.
from PyQt5 import QtCore, QtGui, QtWidgets
import sys
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(800, 606)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.pushButton = QtWidgets.QPushButton(self.centralwidget)
self.pushButton.setGeometry(QtCore.QRect(80, 420, 251, 111))
self.pushButton.setObjectName("pushButton")
self.pushButton_2 = QtWidgets.QPushButton(self.centralwidget)
self.pushButton_2.setGeometry(QtCore.QRect(450, 410, 251, 111))
self.pushButton_2.setObjectName("pushButton_2")
self.graphicsView = PlotWidget(self.centralwidget)
self.graphicsView.setGeometry(QtCore.QRect(70, 70, 511, 192))
self.graphicsView.setObjectName("graphicsView")
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 21))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
self.pushButton.clicked['bool'].connect(MainWindow.slot1)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.pushButton.setText(_translate("MainWindow", "PushButton"))
self.pushButton_2.setText(_translate("MainWindow", "PushButton"))
from pyqtgraph import PlotWidget
And, this is my main code.
from PyQt5 import QtWidgets, QtCore
#from PySide2 import QtWidgets, QtCore
from pyqtgraph import PlotWidget, plot
import pyqtgraph as pg
from qtGraphTestQt5 import Ui_MainWindow
import sys
import os
import numpy as np
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, *args, **kwargs):
super(MainWindow, self).__init__(*args, **kwargs)
self.graphWidget = pg.PlotWidget()
self.setCentralWidget(self.graphWidget)
self.umw = Ui_MainWindow()
self.umw.setupUi(self)
def slot1(self):
print("slot1")
# plot data: x, y values
crvItem = pg.PlotCurveItem()
self.umw.graphicsView.addItem(crvItem)
# plot y = x^2
crvItem.xData = np.array(range(100))*0.1
crvItem.yData = crvItem.xData**2
def main():
app = QtWidgets.QApplication.instance()
if app==None:
app = QtWidgets.QApplication(sys.argv)
main = MainWindow()
main.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
Does anyone have any knowledge on the matter?

How to link QGraphicsScene to QGraphicsView inside a QtDesigner generated code

I want to display an image using QGraphicsView in a window. I have a basic QGraphicsView and a button based test.ui file generated from QtDesigner tool. Corresponding test.py file:
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'test.ui'
#
# Created by: PyQt5 UI code generator 5.9.2
#
# WARNING! All changes made in this file will be lost!
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(640, 480)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.verticalLayout = QtWidgets.QVBoxLayout(self.centralwidget)
self.verticalLayout.setObjectName("verticalLayout")
self.graphicsView = QtWidgets.QGraphicsView(self.centralwidget)
self.graphicsView.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
self.graphicsView.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
self.graphicsView.setObjectName("graphicsView")
self.verticalLayout.addWidget(self.graphicsView)
self.pushButton = QtWidgets.QPushButton(self.centralwidget)
self.pushButton.setObjectName("pushButton")
self.verticalLayout.addWidget(self.pushButton)
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 640, 26))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.pushButton.setText(_translate("MainWindow", "PushButton"))
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_())
I made use of QGraphicsScene to display an image inside this QGraphicsView. My current working solution is to mess up the above .py file as follows:
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'test.ui'
#
# Created by: PyQt5 UI code generator 5.9.2
#
# WARNING! All changes made in this file will be lost!
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(640, 480)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.verticalLayout = QtWidgets.QVBoxLayout(self.centralwidget)
self.verticalLayout.setObjectName("verticalLayout")
self.scene = QtWidgets.QGraphicsScene(self.centralwidget) #added
self.graphicsView = QtWidgets.QGraphicsView(self.scene) #edited
self.graphicsView.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
self.graphicsView.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
self.graphicsView.setObjectName("graphicsView")
self.verticalLayout.addWidget(self.graphicsView)
self.pushButton = QtWidgets.QPushButton(self.centralwidget)
self.pushButton.setObjectName("pushButton")
self.verticalLayout.addWidget(self.pushButton)
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 640, 26))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.pushButton.setText(_translate("MainWindow", "PushButton"))
self.pushButton.clicked.connect(self.display)
def display(self):
import imageio, numpy
self.scene.clear()
input_image = imageio.imread('image.jpg').copy()
height, width, channels = input_image.shape
bytesPerLine = channels * width
qimg = QtGui.QImage(input_image, width, height, bytesPerLine, QtGui.QImage.Format_RGB888)
self.pixmap = QtGui.QPixmap.fromImage(qimg)
self.scene.addPixmap(self.pixmap) #add pixmap to scene
self.graphicsView.scale(0.5,0.5)
self.scene.update()
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_())
However, this solution is not systematic. I want to do the same without editing the original test.ui generated test.py file.
So, I tried following code which to me appears like a systematic translation of the previous working code. However, it fails to produce any display on button click:
from PyQt5 import QtCore, QtGui, QtWidgets
from test import Ui_MainWindow
class myWindow(QtWidgets.QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
QtWidgets.QMainWindow.__init__(self, parent=parent)
self.setupUi(self)
self.scene = QtWidgets.QGraphicsScene(self.centralwidget) #append scene method
self.graphicsView = QtWidgets.QGraphicsView(self.scene) #graphicsView re-assigned with scene
self.pushButton.clicked.connect(self.display)
def display(self):
import imageio, numpy
self.scene.clear()
input_image = imageio.imread('image.jpg').copy()
height, width, channels = input_image.shape
bytesPerLine = channels * width
qimg = QtGui.QImage(input_image, width, height, bytesPerLine, QtGui.QImage.Format_RGB888)
self.pixmap = QtGui.QPixmap.fromImage(qimg)
self.scene.addPixmap(self.pixmap) #add pixmap to scene
self.graphicsView.scale(0.5,0.5)
self.scene.update()
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w = myWindow()
w.show()
sys.exit(app.exec_())
I am likely messing up something due to my limited understanding of class inheritance, QGraphicsView, and QGraphicsScene. Any pointers to my mistakes would be very helpful.
Note: I am using a myWindow class to integrate some key press functionality in future. I think the issue is more to do with how to selectively append/redefine few lines inside a parent class function. Is this even possible without re-writing the whole setupUi method? Or, is there another smarter way to perform the same task?
You have to set the scene to the existing QGraphicsView instead of creating a new QGraphicsView:
class myWindow(QtWidgets.QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
QtWidgets.QMainWindow.__init__(self, parent)
self.setupUi(self)
self.scene = QtWidgets.QGraphicsScene(self.centralwidget)
self.graphicsView.setScene(self.scene)
self.pushButton.clicked.connect(self.display)
def display(self):
# ...

Why my QThread class drastically slows PyQT5 app?

I'm facing the problem with Threads. I'm displaying current CPU usage with progress bar and it seems to be working well but the performance of whole window is terrible. Can't even click the button without laggy behavior. Is there any simple solution to fix it?
Here is my main code
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import QThread, pyqtSignal
import progressBarUI
import sys
import sysnfo
class MainWindow(QtWidgets.QMainWindow, progressBarUI.Ui_MainWindow):
def __init__(self, parent = None):
super(MainWindow, self).__init__(parent)
self.setupUi(self)
self.threadclass = ThreadClass()
self.threadclass.start()
self.threadclass.signal.connect(self.updateProgressBar)
def updateProgressBar(self):
current_percentage = sysnfo.getCpuPercentage()
self.progressBar.setValue(current_percentage)
class ThreadClass(QThread):
signal = pyqtSignal(int)
def __init__(self, parent=None):
super(ThreadClass, self).__init__(parent)
def run(self):
while True:
current_percentage = sysnfo.getCpuPercentage()
self.signal.emit(current_percentage)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
mainAppWin = MainWindow()
mainAppWin.show()
app.exec_()
Here is sysnfo module:
import psutil as ps
def getCpuPercentage():
return ps.cpu_percent(interval=1)
And UI file (converted to .py file):
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(787, 203)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.progressBar = QtWidgets.QProgressBar(self.centralwidget)
self.progressBar.setGeometry(QtCore.QRect(370, 20, 381, 111))
self.progressBar.setProperty("value", 0)
self.progressBar.setObjectName("progressBar")
self.pushButton = QtWidgets.QPushButton(self.centralwidget)
self.pushButton.setGeometry(QtCore.QRect(20, 50, 151, 41))
self.pushButton.setObjectName("pushButton")
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 787, 21))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.pushButton.setText(_translate("MainWindow", "Click me"))
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_())
When you use the "interval" parameter you are indicating that it measures the information during that time and calculates the average causing the execution of that function to take "interval" seconds, and the correct thing is to execute it in another thread, but you make the mistake of executing it too in the updateProgressBar method that lives in the main thread blocking it, instead use the information that sends you the signal:
#QtCore.pyqtSlot(int)
def updateProgressBar(self, percentage):
self.progressBar.setValue(percentage)

Connecting Pyside with Matplotlib using QtDesigner - using pushButton to draw

This is a followup question from this one. Connecting Pyside with matplotlib
My PythonFu is failing me to do a simple thing: design a GUI using QtDesigner, convert it, and do a QPushButton to draw something. It works when QtDesigner is not being used, but QtDesigner will be needed as the application grows more complex, so it must be used. Here is the code:
** main.py **
import sys
import platform
import numpy as np
import PySide
from PySide import QtCore
from PySide.QtGui import QApplication, QMainWindow, QTextEdit,\
QPushButton, QMessageBox, QWidget, QVBoxLayout
__version__ = '0.0.1'
from mpl import Ui_MainWindow
class MainWindow(QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.main_frame = Ui_MainWindow()
self.main_frame.setupUi(self)
#self.button = QPushButton('Run')
def plot_stuff(self):
x = np.arange(1024)
self.main_frame.widget.axes.plot(np.exp(-x / 256) * np.cos(2 * np.pi * x / 32), 'g')
self.main_frame.widget.canvas.draw()
if __name__ == '__main__':
app = QApplication(sys.argv)
frame = MainWindow()
frame.main_frame.pushButton.clicked.connect(frame.plot_stuff)
frame.show()
app.exec_()
** matplotlibwidget.py **
import matplotlib
matplotlib.use('Qt4Agg')
matplotlib.rcParams['backend.qt4']='PySide'
from matplotlib.figure import Figure
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
class MatplotlibWidget(FigureCanvas):
def __init__(self, parent=None):
super(MatplotlibWidget, self).__init__(Figure())
self.setParent(parent)
self.figure = Figure()
self.canvas = FigureCanvas(self.figure)
self.axes = self.figure.add_subplot(111)
and ** mpl.py **, converted from mpl.ui
from PySide import QtCore, QtGui
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(640, 480)
self.centralwidget = QtGui.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.pushButton = QtGui.QPushButton(self.centralwidget)
self.pushButton.setGeometry(QtCore.QRect(20, 410, 74, 20))
self.pushButton.setObjectName("pushButton")
self.widget = MatplotlibWidget(self.centralwidget)
self.widget.setGeometry(QtCore.QRect(100, 40, 471, 321))
self.widget.setObjectName("widget")
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtGui.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 640, 17))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtGui.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
MainWindow.setWindowTitle(QtGui.QApplication.translate("MainWindow", "MainWindow", None, QtGui.QApplication.UnicodeUTF8))
self.pushButton.setText(QtGui.QApplication.translate("MainWindow", "Run", None, QtGui.QApplication.UnicodeUTF8))
from widgets.matplotlibwidget import MatplotlibWidget
The button connection works, but the graph is never shown.
Thanks!
In your main.py module replace
self.main_frame.widget.canvas.draw()
with
self.main_frame.widget.draw()
From my understanding of matplotlib you were drawing the wrong canvas. According to the docs the widget that you promoted is now your canvas and that's the one you're supposed to draw.

Categories

Resources