Using the code below, I can plot basic matplotlib plots in a gui designed in pyqt5 (such as giving it a list of xs and ys and it plots the points). However, I cannot plug in advanced modules such as seaborn or networkx (lets focus on seaborn) that utilize the plotting functionality of matplotlib to display the data you generate with the functions.
from PyQt5 import QtCore, QtGui, QtWidgets
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(800, 600)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.widget = MplWidget(self.centralwidget)
self.widget.setGeometry(QtCore.QRect(200, 110, 391, 311))
self.widget.setObjectName("widget")
self.label = QtWidgets.QLabel(self.centralwidget)
self.label.setGeometry(QtCore.QRect(250, 40, 231, 61))
self.label.setObjectName("label")
self.pushButton = QtWidgets.QPushButton(self.centralwidget)
self.pushButton.setGeometry(QtCore.QRect(50, 180, 113, 32))
self.pushButton.setObjectName("pushButton")
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 22))
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)
self.pushButton.clicked.connect(self.Graphitdude)
def Graphitdude(self):
Lister3 = pd.read_csv("Betadata.csv",index_col=[0])#just a collection of labels and assorted correlation values ranging from 0 to 1 to be constructed into a heatmap.
plot = sns.heatmap(Lister3) # This is the problematic function, it does it, and I can display it IN LINE but not in the gui canvas?
plt.yticks(rotation=0)
self.widget.canvas.ax.plot()#if passed discreet x and y values, it graphs it, but it doesnt like to pass the seaborn figure?
self.widget.canvas.draw()
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.label.setText(_translate("MainWindow", "<html><head/><body><p align=\"center\"><span style=\" font-size:24pt; font-weight:600;\">Test</span></p></body></html>"))
self.pushButton.setText(_translate("MainWindow", "PushButton"))
from mplwidget import MplWidget
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_())
mplwidget.py is as follows:
from PyQt5 import QtWidgets
from matplotlib.figure import Figure
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as Canvas
import matplotlib
matplotlib.use('QT5Agg')
# Matplotlib canvas class to create figure
class MplCanvas(Canvas):
def __init__(self):
self.fig = Figure()
self.ax = self.fig.add_subplot(111)
Canvas.__init__(self, self.fig)
Canvas.setSizePolicy(self, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
Canvas.updateGeometry(self)
# Matplotlib widget
class MplWidget(QtWidgets.QWidget):
def __init__(self, parent=None):
QtWidgets.QWidget.__init__(self, parent) # Inherit from QWidget
self.canvas = MplCanvas() # Create canvas object
self.vbl = QtWidgets.QVBoxLayout() # Set box for plotting
self.vbl.addWidget(self.canvas)
self.setLayout(self.vbl)
My best idea is that these functions are designing their own matplotlib figure (or subplot, not sure), and this figure cannot be supplied to the widget code by the methods I have tried.
sns.heatmap(xxxx) returns "ax", which should be able to be used by the mplwidget.py script, right? Can I pass these to the canvas to display seaborn graphics?
According to the documentation:
seaborn.heatmap(data, vmin=None, vmax=None, cmap=None, center=None, robust=False, annot=None, fmt='.2g', annot_kws=None, linewidths=0, linecolor='white', cbar=True, cbar_kws=None, cbar_ax=None, square=False, xticklabels='auto', yticklabels='auto', mask=None, ax=None, **kwargs)
[...]
ax : matplotlib Axes, optional
Axes in which to draw the plot, otherwise use the currently-active Axes.
So then you should just pass the AxesSubplot of MplCanvas as parameter ax:
def Graphitdude(self):
Lister3 = pd.read_csv("Betadata.csv",index_col=[0])
plot = sns.heatmap(Lister3, ax=self.widget.canvas.ax)
plt.yticks(rotation=0)
self.widget.canvas.draw()
I have used many functions similar to heatmap and always provide that parameter.
Related
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;
so I just started a new project in which I need to present data I collected on a GUI. For that purpose I created a test script, that reads the data from a .mat file and then calculates the surface plots. So far everything is working. Now I need to start with the GUI. I already managed to create an other test program that can open the OpenFileName-Dialog and reads the data from the file.
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
import h5py
import mat73
import numpy as np
class MatplotlibCanvas(FigureCanvasQTAgg):
def __init__(self, parent=None, width = 5, height = 4, dpi = 120):
fig = Figure(figsize = (width,height))
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(1466, 910)
font = QtGui.QFont()
font.setFamily("Tahoma")
MainWindow.setFont(font)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.graphicsView = QtWidgets.QGraphicsView(self.centralwidget)
self.graphicsView.setGeometry(QtCore.QRect(280, 0, 561, 441))
self.graphicsView.setObjectName("graphicsView")
self.graphicsView_2 = QtWidgets.QGraphicsView(self.centralwidget)
self.graphicsView_2.setGeometry(QtCore.QRect(860, 0, 561, 441))
self.graphicsView_2.setObjectName("graphicsView_2")
self.graphicsView_3 = QtWidgets.QGraphicsView(self.centralwidget)
self.graphicsView_3.setGeometry(QtCore.QRect(280, 440, 561, 441))
self.graphicsView_3.setObjectName("graphicsView_3")
self.graphicsView_4 = QtWidgets.QGraphicsView(self.centralwidget)
self.graphicsView_4.setGeometry(QtCore.QRect(860, 440, 561, 441))
self.graphicsView_4.setObjectName("graphicsView_4")
self.pushButton = QtWidgets.QPushButton(self.centralwidget)
self.pushButton.setGeometry(QtCore.QRect(40, 90, 75, 23))
self.pushButton.setObjectName("pushButton")
MainWindow.setCentralWidget(self.centralwidget)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.action_Open = QtWidgets.QAction(MainWindow)
self.action_Open.setObjectName("action_Open")
self.action_Save = QtWidgets.QAction(MainWindow)
self.action_Save.setObjectName("action_Save")
self.action_Export = QtWidgets.QAction(MainWindow)
self.action_Export.setObjectName("action_Export")
self.action_Exit = QtWidgets.QAction(MainWindow)
self.action_Exit.setObjectName("action_Exit")
self.filename = ''
self.canv = MatplotlibCanvas(self)
self.df = []
self.toolbar = Navi(self.canv, self.centralwidget)
self.pushButton.clicked.connect(self.getFile)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def getFile(self):
""" this function will get the adress of the mat file location
also calls a readData function
"""
self.filename = QFileDialog.getOpenFileName(filter="mat (*.mat)")[0]
print("File: ", self.filename)
self.readData()
def readData(self):
self.df = mat73.loadmat(self.filename, use_attrdict=True)
struct = self.df['DemoData']
print(struct.Nr.data)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.pushButton.setText(_translate("MainWindow", "Open "))
self.action_Open.setText(_translate("MainWindow", "&Open"))
self.action_Save.setText(_translate("MainWindow", "&Save"))
self.action_Export.setText(_translate("MainWindow", "&Export"))
self.action_Exit.setText(_translate("MainWindow", "&Quit"))
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_())
Since I can read the data, next step would be to create the surface plots and display the data. My idea was to use the graphicsView elements to display the four needed plots, but I just can't find how to link the figures, I used in the first test program (without GUI) to the graphicsView element. For the figure I used the following code line:
fig, ax = plt.subplots(subplot_kw={"projection": "3d"})
surf = ax.plot_surface(pArray, rArray, trArray, alpha = 0.5)
surf = ax.plot_surface(pArray, rArray, tArray, cmap=cm.jet, alpha = 1)
Can someone give me a tip, how I could achieve that?
Edit: I uploaded a .mat File .mat File
I have to note that:
mat73 has trouble reading the provided .mat so I use scipy.
The data contained in the .mat are one-dimensional arrays so they could not form a surface since for this two-dimensional arrays are required so I will only show how to draw the points.
Due to the large number of dots, painting takes time and may freeze
The logic is to work with the FigureCanvas and create 3d plots:
from functools import cached_property
from PyQt5.QtWidgets import QApplication, QFileDialog, QMainWindow
from matplotlib.backends.backend_qt5agg import (
FigureCanvas,
NavigationToolbar2QT as NavigationToolbar,
)
from matplotlib.figure import Figure
from matplotlib import cm
import scipy.io as sio
class MainWindow(QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.ax = self.canvas.figure.add_subplot(projection="3d")
self.setCentralWidget(self.canvas)
self.addToolBar(self.toolbar)
file_menu = self.menuBar().addMenu("&File")
open_action = file_menu.addAction("&Open")
open_action.triggered.connect(self.handle_open_action_triggered)
#cached_property
def canvas(self):
return FigureCanvas()
#cached_property
def toolbar(self):
return NavigationToolbar(self.canvas, self)
def handle_open_action_triggered(self):
filename, _ = QFileDialog.getOpenFileName(self, filter="mat (*.mat)")
if filename:
self.load_mat(filename)
def load_mat(self, filename):
res = sio.loadmat(filename)
record = res["record"]
data = record["Data"]
temp_ref = data[0, 0]["TempRef"][0, 0][0, 0]["data"]
nr = data[0][0]["Nr"][0, 0][0, 0]["data"]
temp_act = data[0, 0]["TempAct"][0, 0][0, 0]["data"]
self.update_plot(temp_ref, nr, temp_act)
def update_plot(self, x, y, z):
print(x.shape, y.shape, z.shape)
self.ax.scatter(x, y, z)
self.canvas.draw()
if __name__ == "__main__":
import sys
app = QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())
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
What is the best way to embed an interactive 3D plot in a PySide GUI? I have looked at some examples on here of 2D plots embedded in a PySide GUI:
Getting PySide to Work With Matplotlib
Matplotlib Interactive Graph Embedded In PyQt
Python/Matplotlib/Pyside Fast Timetrace Scrolling
However, the functionality that I'm looking for is not quite the same. The figure needs to rotate and zoom based on mouse input from the user in the same way as if it were drawn in a separate window.
I'm trying to avoid having to go in manually and write functions for transforming mouse click + move into a figure rotate and canvas repaint--even if that's the only way, I'm not even sure how to do that. But I figure (no pun intended) that there should be a way to reuse the functionality already present for creating 3D plots in their own windows.
Here's my code. It works as intended, but the plot is not interactive. Any advice is appreciated!
EDIT: I fixed the use of FigureCanvas according to tcaswell's corrections. I also added a bit from the matplotlib Event Handling and Picking documentation to show that the figure seems to be getting the events upon mouseclick.
Final Edit: The following code now produces the plot as desired.
# -*- coding: utf-8 -*-
from PySide import QtCore, QtGui
import numpy as np
import matplotlib
import sys
# specify the use of PySide
matplotlib.rcParams['backend.qt4'] = "PySide"
# import the figure canvas for interfacing with the backend
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg \
as FigureCanvas
# import 3D plotting
from mpl_toolkits.mplot3d import Axes3D # #UnusedImport
from matplotlib.figure import Figure
# Auto-generated code from QT Designer ----------------------------------------
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(750, 497)
self.centralwidget = QtGui.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.horizontalLayout_2 = QtGui.QHBoxLayout(self.centralwidget)
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
self.frame_2 = QtGui.QFrame(self.centralwidget)
self.frame_2.setFrameShape(QtGui.QFrame.StyledPanel)
self.frame_2.setFrameShadow(QtGui.QFrame.Raised)
self.frame_2.setObjectName("frame_2")
self.verticalLayout = QtGui.QVBoxLayout(self.frame_2)
self.verticalLayout.setObjectName("verticalLayout")
self.label = QtGui.QLabel(self.frame_2)
self.label.setObjectName("label")
self.verticalLayout.addWidget(self.label)
self.label_2 = QtGui.QLabel(self.frame_2)
self.label_2.setObjectName("label_2")
self.verticalLayout.addWidget(self.label_2)
self.lineEdit = QtGui.QLineEdit(self.frame_2)
sizePolicy = QtGui.QSizePolicy(
QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(
self.lineEdit.sizePolicy().hasHeightForWidth())
self.lineEdit.setSizePolicy(sizePolicy)
self.lineEdit.setObjectName("lineEdit")
self.verticalLayout.addWidget(self.lineEdit)
spacerItem = QtGui.QSpacerItem(
20, 40, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding)
self.verticalLayout.addItem(spacerItem)
self.horizontalLayout_2.addWidget(self.frame_2)
self.frame_plot = QtGui.QFrame(self.centralwidget)
self.frame_plot.setMinimumSize(QtCore.QSize(500, 0))
self.frame_plot.setFrameShape(QtGui.QFrame.StyledPanel)
self.frame_plot.setFrameShadow(QtGui.QFrame.Raised)
self.frame_plot.setObjectName("frame_plot")
self.horizontalLayout_2.addWidget(self.frame_plot)
MainWindow.setCentralWidget(self.centralwidget)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
MainWindow.setWindowTitle(QtGui.QApplication.translate(
"MainWindow", "MainWindow", None, QtGui.QApplication.UnicodeUTF8))
self.label.setText(QtGui.QApplication.translate("MainWindow",
"This is a qlabel.", None, QtGui.QApplication.UnicodeUTF8))
self.label_2.setText(QtGui.QApplication.translate("MainWindow",
"And this is another one.", None, QtGui.QApplication.UnicodeUTF8))
self.lineEdit.setText(QtGui.QApplication.translate("MainWindow",
"Text goes here.", None, QtGui.QApplication.UnicodeUTF8))
# Auto-generated code from QT Designer ----------------------------------------
class MainWindow(QtGui.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
# intialize the window
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
# create the matplotlib widget and put it in the frame on the right
self.ui.plotWidget = Mpwidget(parent=self.ui.frame_plot)
class Mpwidget(FigureCanvas):
def __init__(self, parent=None):
self.figure = Figure(facecolor=(0, 0, 0))
super(Mpwidget, self).__init__(self.figure)
self.setParent(parent)
# plot random 3D data
self.axes = self.figure.add_subplot(111, projection='3d')
self.data = np.random.random((3, 100))
self.axes.plot(self.data[0, :], self.data[1, :], self.data[2, :])
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
mw = MainWindow()
mw.show()
# adjust the frame size so that it fits right after the window is shown
s = mw.ui.frame_plot.size()
mw.ui.plotWidget.setGeometry(1, 1, s.width() - 2, s.height() - 2)
sys.exit(app.exec_())
You are not using FigureCanvas right:
class Mpwidget(FigureCanvas):
def __init__(self, parent=None):
self.figure = Figure(facecolor=(0, 0, 0))
super(Mpwidget, self).__init__(self.figure) # this object _is_ your canvas
self.setParent(parent)
# plot random 3D data
self.axes = self.figure.add_subplot(111, projection='3d')
self.data = np.random.random((3, 100))
self.axes.plot(self.data[0, :], self.data[1, :], self.data[2, :])
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
mw = MainWindow()
mw.show()
# adjust the frame size so that it fits right after the window is shown
s = mw.ui.frame_plot.size()
mw.ui.plotWidget.setGeometry(1, 1, s.width() - 2, s.height() - 2)
sys.exit(app.exec_())
Every time you called FigureCanvas(..) you were attaching the figure to a new canvas (which were not the FigureCanvas you were seeing) hence the call-backs were never firing (because they were listening on a FigureCanvas that you couldn't see).
1) Create the FigureCanvas before adding the axes. See https://stackoverflow.com/a/9007892/3962328
canvas = FigureCanvas(fig)
ax = figure.add_subplot(111, projection='3d')
or
class MyFigureCanvas(FigureCanvas):
def __init__(self):
self.figure = Figure()
super(FigureCanvas, self).__init__(self.figure)
self.axes = self.figure.add_subplot(111, projection='3d')
2) Try ax.mouse_init() to restore the connection:
...
ax = fig.gca(projection="3d")
...
canvas = FigureCanvas(fig)
ax.mouse_init()
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.