PyQt5 3D scatterplot widget not showing up - python

Goal: Generate a 3D scatter plot from radar data obtained from vTrig_DataCollect.
Scatterplot_widget is not being shown in the MainWindow.
Step 1 is to make the plot show up from a single data collection.
Step 2 is to repeatedly call vTrig_DataCollect and have the plot refresh at 1 Hz.
from PyQt5 import QtCore, QtGui, QtWidgets
import time
from PyQt5 import QtWidgets
from matplotlib.figure import Figure
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
import vTrig_DataCollect
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(800, 600)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
MainWindow.setCentralWidget(self.centralwidget)
self.scatterplot_widget = QtWidgets.QWidget(self.centralwidget)
self.scatterplot_widget.setGeometry(QtCore.QRect(0, 0, 800, 500))
self.scatterplot_widget.setObjectName("scatterplot_widget")
self.fig = Figure()
self.canvas = FigureCanvas(self.fig)
self.axes = self.fig.add_subplot(111, projection='3d')
payload_data = vTrig_DataCollect.data_collect()
x_points = payload_data['x']
y_points = payload_data['y']
z_points = payload_data['z']
intensity = payload_data['intensity']
self.scatterplot_widget = self.axes.scatter(x_points, y_points, z_points, s=4, c=intensity)
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", "Visualization_Window"))
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_())
The majority of the code was generated using Qt Designer, then I added the 9 lines of code related to my plot.

You must not modify the file generated by pyuic5 so you must restore the file by running: python -m pyuic5 your_ui.ui -o mainwindow.py -x, also I will assume that the file vTrig_DataCollect.py is:
import numpy as np
def data_collect():
N = 50
return {
"x": np.random.rand(N),
"y": np.random.rand(N),
"z": np.random.rand(N),
"intensity": np.random.rand(N),
}
The idea is to create the canvas (which is a QWidget too) and place it on top of another widget using a layout, then update the canvas with the new data.
from PyQt5.QtCore import QTimer
from PyQt5.QtWidgets import QApplication, QMainWindow, QVBoxLayout
from matplotlib.figure import Figure
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from mainwindow import Ui_MainWindow
import vTrig_DataCollect
class MainWindow(QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.canvas = FigureCanvas(Figure())
self.axes = self.canvas.figure.add_subplot(111, projection="3d")
lay = QVBoxLayout(self.ui.scatterplot_widget)
lay.addWidget(self.canvas)
timer = QTimer(self, interval=1000, timeout=self.handle_timeout)
timer.start()
def handle_timeout(self):
payload_data = vTrig_DataCollect.data_collect()
x_points = payload_data["x"]
y_points = payload_data["y"]
z_points = payload_data["z"]
intensity = payload_data["intensity"]
self.axes.clear()
self.axes.scatter(x_points, y_points, z_points, s=4, c=intensity)
self.canvas.draw()
def main():
app = QApplication([])
w = MainWindow()
w.show()
app.exec_()
if __name__ == "__main__":
main()

Related

Create multiple surface plots in GUI

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_())

My plot graph is not showing in my pyqt5.QtWidgets

I'm currently learning how can I use pyqt5 with matplotlib together so implement this code which will popup after I press a pushbutton in another window
I create another class called canvas in order to create multiple graphs in the future like bar chart and histogram
when I run this code the windows will appear and the pushButton also will appear but not the canvas that I did create
how can I show the canvas ( pie chart ) in the main window
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
import numpy as np
import PyQt5
from PyQt5 import QtCore, QtGui, QtWidgets
import sys
class window(QtWidgets.QMainWindow):
def setupUi(self,Dialog):
Dialog.setObjectName("Dialog")
Dialog.resize(900, 500)
self.pushButton = QtWidgets.QPushButton(Dialog)
self.pushButton.setGeometry(QtCore.QRect(0, 380, 141, 61))
self.pushButton.setObjectName("pushButton")
self.canvas = Canvas(self, width=8, height=4)
self.canvas.move(0,0)
self.retranslateUi(Dialog)
QtCore.QMetaObject.connectSlotsByName(Dialog)
def retranslateUi(self, Dialog):
_translate = QtCore.QCoreApplication.translate
Dialog.setWindowTitle(_translate("Dialog", "output window"))
class Canvas(FigureCanvas):
def __init__(self, parent = None, width = 5, height = 5, dpi = 100):
fig = Figure(figsize=(width, height), dpi=dpi)
self.axes = fig.add_subplot(111)
FigureCanvas.__init__(self, fig)
self.setParent(parent)
self.plot()
def plot(self):
x = np.array([50, 30,40])
labels = ["Apples", "Bananas", "Melons"]
ax = self.figure.add_subplot(111)
ax.pie(x, labels=labels)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
Dialog = QtWidgets.QDialog()
ui = window()
ui.setupUi(Dialog)
Dialog.show()
sys.exit(app.exec_())
The problem is caused because you are modifying the code generated by Qt Designer without understanding the consequences of it. The class generated by QtDesigner is not the window but a class that just populates the window. For example in your case with the window modification it is QMainWindow that has the canvas but you have created another widget called Dialog which is the one you show (if you check the QPushButton it is a child of Dialog so it is also shown). On the other hand, you only have to create a single axes.
Therefore, you should not modify the class generated by QtDesigner but rather create another class that inherits from a widget, and use the previous class to fill it.
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
import numpy as np
import PyQt5
from PyQt5 import QtCore, QtGui, QtWidgets
import sys
class Ui_Dialog:
def setupUi(self, Dialog):
Dialog.setObjectName("Dialog")
Dialog.resize(900, 500)
self.pushButton = QtWidgets.QPushButton(Dialog)
self.pushButton.setGeometry(QtCore.QRect(0, 380, 141, 61))
self.pushButton.setObjectName("pushButton")
def retranslateUi(self, Dialog):
_translate = QtCore.QCoreApplication.translate
Dialog.setWindowTitle(_translate("Dialog", "output window"))
class Canvas(FigureCanvas):
def __init__(self, parent=None, width=5, height=5, dpi=100):
fig = Figure(figsize=(width, height), dpi=dpi)
self.axes = fig.add_subplot(111)
FigureCanvas.__init__(self, fig)
self.setParent(parent)
self.plot()
def plot(self):
x = np.array([50, 30, 40])
labels = ["Apples", "Bananas", "Melons"]
self.axes.pie(x, labels=labels)
self.draw()
class Dialog(QtWidgets.QDialog, Ui_Dialog):
def __init__(self, parent=None):
super().__init__(parent)
self.setupUi(self)
self.canvas = Canvas(self, width=8, height=4)
self.canvas.move(0, 0)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
w = Dialog()
w.show()
sys.exit(app.exec_())

How to set yaxis tick label in a fixed position so that when i scroll left or right the yaxis tick label should be visible?

in my program i have taken figure first then i import figue into canvas. Then i import canvas into scroll area. when i run my program ,then if i turn left scroll the yaxis tick label goes hide or i turn right scroll the yaxis tick label goes also hide.
i have taken two axis. axis,axis2. i have set axes2.yaxis.tick_right() and axes.yaxis.tick_right().
I want that the yaxis tick label will stay at right side and alyas visible, whatever i turn scroll left or right .
import sys
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
import matplotlib.pyplot as plt
from matplotlib.figure import Figure
from PyQt5.QtWidgets import QMainWindow,QVBoxLayout
from PyQt5.QtWidgets import QApplication
from PyQt5 import QtCore, QtGui, QtWidgets
import datetime
from matplotlib.dates import num2date, date2num
from mpl_finance import candlestick_ochl as candlestick
import numpy as np
import matplotlib.ticker as ticker
import matplotlib.dates as mdates
import pylab as pl
class MainWindow_code_serarch(object):
def setup_code_serarch(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(870, 680)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.verticalLayoutWidget1 = QtWidgets.QWidget(self.centralwidget)
self.verticalLayoutWidget1.setGeometry(QtCore.QRect(17, 30, 741, 13))
self.verticalLayoutWidget1.setObjectName("verticalLayoutWidget")
self.verticalLayout1 = QtWidgets.QVBoxLayout(self.verticalLayoutWidget1)
self.verticalLayout1.setContentsMargins(0, 0, 0, 0)
self.verticalLayout1.setObjectName("verticalLayout1")
self.verticalLayoutWidget = QtWidgets.QWidget(self.centralwidget)
self.verticalLayoutWidget.setGeometry(QtCore.QRect(17, 10, 940, 603))
self.verticalLayoutWidget.setObjectName("verticalLayoutWidget")
self.verticalLayout = QtWidgets.QVBoxLayout(self.verticalLayoutWidget)
self.verticalLayout.setContentsMargins(0, 0, 0, 0)
self.verticalLayout.setObjectName("verticalLayout")
self.figure = Figure(figsize=(100,7.2), dpi=80, facecolor='k')
self.canvas = FigureCanvas(self.figure)
self.widget = QtWidgets.QWidget()
self.scroll_area = QtWidgets.QScrollArea(self.widget)
self.scroll_area.setWidget(self.canvas)
self.verticalLayout.addWidget(self.scroll_area)
axes,axes2 = self.figure.subplots(nrows=2, sharex=True)
data = {
'date': ['2018/10/30', '2018/11/03', '2018/11/04', '2018/11/05', '2018/11/07', '2018/11/10', '2018/11/11'],
'open': [8824, 8726.31, 8642.14, 8531.51, 8630.25, 8602.50, 8640.22],
'high': [8858, 8748.60, 8551.36, 8653.16, 8476.69, 8630, 8570.56],
'low': [8688, 8743.67, 8550.76, 8449.50, 8631.83, 8602.18, 8743.22],
'close': [8820, 8747.17, 8550.52, 8553., 8517.10, 8628.78, 8588.52],
'volume': [17759.56, 120000.17, 18739.52, 38599.50, 16517.10, 17723.78, 15588.52]
}
x = date2num([datetime.datetime.strptime(d, '%Y/%m/%d').date() for d in data['date']])
t= np.arange(len(data['date']))
candle_trace = zip(t, data['open'], data['high'], data['low'], data['close'], data['volume'])
candlestick(axes, candle_trace, width=.75, colorup='g', colordown='r')
axes2.plot(t, [1, 2, 3, 4, 7, 8, 9])
axes.set_position([0.02, 0.37, 0.88, 0.6])
axes2.set_position([0.02, 0.15, 0.88, 0.22])
axes.tick_params(axis='both', color='#ffffff', labelcolor='#ffffff')
axes.yaxis.tick_right()
axes2.tick_params(axis='both', color='#ffffff', labelcolor='#ffffff')
axes2.grid(color='lightgray', linewidth=.5, linestyle=':')
axes.grid(color='lightgray', linewidth=.5, linestyle=':')
axes2.yaxis.tick_right()
axes.autoscale_view()
axes2.autoscale_view()
axes.set_facecolor('#041105')
axes2.set_facecolor('#041105')
# N = len(dates)
axes.set_xticks(range(0, len((x)), 1))
axes.set_xticklabels([mdates.num2date(d).strftime('%b-%d') for d in x])
axes.set_xticklabels([mdates.num2date(d).strftime('%Y-%m-%d') for d in x])
axes2.set_xticklabels([mdates.num2date(d).strftime('%Y-%m-%d') for d in x])
self.canvas.draw()
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 246, 21))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
# self.pushButton.clicked.connect(self.graphShowCode)
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", "OK"))
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = MainWindow_code_serarch()
ui.setup_code_serarch(MainWindow)
MainWindow.show()
sys.exit(app.exec_())
Output image:
when i turn scroll left output image 2:
Here is a way to scroll the content of an axes with a PyQt QtScrollBar. This is done by changing the limits of the axes depending on the scroll bar's value. To this end, a callback to the QtScrollBar's actionTriggered method is registered that changes the limits of the axes.
import sys
import matplotlib
# Make sure that we are using QT5
matplotlib.use('Qt5Agg')
import matplotlib.pyplot as plt
from PyQt5 import QtWidgets, QtCore
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar
import numpy as np
class ScrollableWindow(QtWidgets.QMainWindow):
def __init__(self, fig, ax, step=0.1):
plt.close("all")
if not QtWidgets.QApplication.instance():
self.app = QtWidgets.QApplication(sys.argv)
else:
self.app = QtWidgets.QApplication.instance()
QtWidgets.QMainWindow.__init__(self)
self.widget = QtWidgets.QWidget()
self.setCentralWidget(self.widget)
self.widget.setLayout(QtWidgets.QVBoxLayout())
self.widget.layout().setContentsMargins(0,0,0,0)
self.widget.layout().setSpacing(0)
self.fig = fig
self.ax = ax
self.canvas = FigureCanvas(self.fig)
self.canvas.draw()
self.scroll = QtWidgets.QScrollBar(QtCore.Qt.Horizontal)
self.step = step
self.setupSlider()
self.nav = NavigationToolbar(self.canvas, self.widget)
self.widget.layout().addWidget(self.nav)
self.widget.layout().addWidget(self.canvas)
self.widget.layout().addWidget(self.scroll)
self.canvas.draw()
self.show()
self.app.exec_()
def setupSlider(self):
self.lims = np.array(self.ax.get_xlim())
self.scroll.setPageStep(self.step*100)
self.scroll.actionTriggered.connect(self.update)
self.update()
def update(self, evt=None):
r = self.scroll.value()/((1+self.step)*100)
l1 = self.lims[0]+r*np.diff(self.lims)
l2 = l1 + np.diff(self.lims)*self.step
self.ax.set_xlim(l1,l2)
print(self.scroll.value(), l1,l2)
self.fig.canvas.draw_idle()
# create a figure and some subplots
fig, ax = plt.subplots()
t = np.linspace(0,3000,101)
x = np.cumsum(np.random.randn(len(t)))
ax.plot(t,x, marker="o")
# pass the figure to the custom window
a = ScrollableWindow(fig,ax)
For a Scrollbar within the figure, see Scrollable Bar graph matplotlib
The above answer works well, but does not hand back control to the spyder window or python command line. Also in spyder it could be run only once. Added a few more points seen from another thread on exiting from QT window and improved to this.
import sys
import matplotlib
# Make sure that we are using QT5
matplotlib.use('Qt5Agg')
import matplotlib.pyplot as plt
from PyQt5 import QtWidgets, QtCore
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar
import numpy as np
from PyQt5.QtWidgets import QMainWindow, QLabel, QGridLayout, QWidget
from PyQt5.QtCore import QSize
class HelloWindow(QMainWindow):
def __init__(self, fig, ax , step = 0.1):
plt.close("all")
if not QtWidgets.QApplication.instance():
self.app = QtWidgets.QApplication(sys.argv)
else:
self.app = QtWidgets.QApplication.instance()
QMainWindow.__init__(self)
self.setMinimumSize(QSize(1500, 100))
self.setWindowTitle("Value Plotting")
self.step = step
self.widget = QWidget()
self.setCentralWidget( self.widget )
self.widget.setLayout(QtWidgets.QVBoxLayout())
self.widget.layout().setContentsMargins(0,0,0,0)
self.widget.layout().setSpacing(0)
self.fig = fig
self.ax = ax
self.canvas = FigureCanvas(self.fig)
self.canvas.draw()
self.scroll = QtWidgets.QScrollBar(QtCore.Qt.Horizontal)
self.step = step
self.setupSlider()
self.nav = NavigationToolbar(self.canvas, self.widget)# self.widget.layout().addWidget(self.nav)
self.widget.layout().addWidget(self.canvas)
self.widget.layout().addWidget(self.scroll)
menu = self.menuBar().addMenu('Action for quit')
action = menu.addAction('Quit')
action.triggered.connect(QtWidgets.QApplication.quit)
self.canvas.draw()
def setupSlider(self):
self.lims = np.array(self.ax.get_xlim())
self.scroll.setPageStep(self.step*100)
self.scroll.actionTriggered.connect(self.update)
self.update()
def update(self, evt=None):
r = self.scroll.value()/((1+self.step)*100)
l1 = self.lims[0]+r*np.diff(self.lims)
l2 = l1 + np.diff(self.lims)*self.step
self.ax.set_xlim(l1,l2)
print(self.scroll.value(), l1,l2)
self.fig.canvas.draw_idle()
if __name__ == "__main__":
def run_app(fig,ax):
app = QtWidgets.QApplication(sys.argv)
mainWin = HelloWindow(fig,ax)
mainWin.show()
app.exec_()
fig, ax = plt.subplots()
t = np.linspace(0,3000,101)
x = np.cumsum(np.random.randn(len(t)))
ax.plot(t,x, marker="o")
run_app(fig,ax)
Found it running well on the python console. Exiting roughly in Spyder. Top close button not working, but exiting when operating from menu.

Matplotlib figure/plot/canvas/layout hides buttons

Matplotlib figure/plot/canvas/layout hides buttons. The picture should explain the problem: I would like the container of the plot to leave space for the buttons on the right.
Here is a minimal working code example with some commented things that I tried:
import sys
from PyQt5.QtWidgets import QPushButton, QVBoxLayout, QApplication, QDialog
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
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_Dialog(QDialog):
def __init__(self, parent=None):
super(Ui_Dialog, self).__init__(parent)
self.setupUi(self)
self.setupPlot()
def setupUi(self, Dialog):
Dialog.setObjectName("Dialog")
Dialog.resize(400, 300)
Dialog.setToolTip("")
Dialog.setStatusTip("")
Dialog.setWhatsThis("")
Dialog.setAccessibleName("")
Dialog.setAccessibleDescription("")
self.buttonBox = QtWidgets.QDialogButtonBox(Dialog)
self.buttonBox.setGeometry(QtCore.QRect(320, 50, 81, 241))
self.buttonBox.setOrientation(QtCore.Qt.Vertical)
self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
self.buttonBox.setObjectName("buttonBox")
self.retranslateUi(Dialog)
self.buttonBox.accepted.connect(Dialog.accept)
self.buttonBox.rejected.connect(Dialog.reject)
QtCore.QMetaObject.connectSlotsByName(Dialog)
def retranslateUi(self, Dialog):
pass
def setupPlot(self):
self.figure = plt.figure()
self.canvas = FigureCanvas(self.figure)
#self.canvas.resize(self, 5, 5)
#self.resize(5,5)
self.toolbar = NavigationToolbar(self.canvas, self)
layout = QVBoxLayout()
layout.addWidget(self.toolbar)
layout.addWidget(self.canvas)
self.setLayout(layout)
self.plot()
def plot(self):
data = [random.random() for i in range(10)]
self.figure.clear()
#plt.figure(num=2, figsize=(3, 3), dpi=80, facecolor='w', edgecolor='k')
#self.figure.set_size_inches(5, 5, forward=True)
ax = self.figure.add_subplot(111)
#self.figure.subplots_adjust(left=1, bottom=1, right=1, top=1, wspace=1, hspace=1)
ax.plot(data, '*-')
self.canvas.draw()
if __name__ == '__main__':
app = QApplication(sys.argv)
main = Ui_Dialog()
main.show()
sys.exit(app.exec_())
Instead of placing the buttonbox in absolute coordinates, you may add it to a layout, just like you did with the canvas and the toolbar. To this end, you may use another widget, which contains the canvas and toolbar. This widget can be placed in a QHBoxLayout(); the QDialogButtonBox would be added second in the layout.
import sys
#from PyQt5.QtWidgets import QPushButton, QVBoxLayout, QApplication, QDialog
from PyQt4.QtGui import QPushButton, QVBoxLayout, QApplication, QDialog
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt4agg import NavigationToolbar2QT as NavigationToolbar
import matplotlib.pyplot as plt
import random
#from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt4 import QtCore, QtGui #, QtWidgets
class Ui_Dialog(QDialog):
def __init__(self, parent=None):
super(Ui_Dialog, self).__init__(parent)
self.setupUi(self)
self.setupPlot()
def setupUi(self, Dialog):
Dialog.setObjectName("Dialog")
Dialog.resize(400, 300)
self.buttonBox = QtGui.QDialogButtonBox(Dialog)
self.buttonBox.setOrientation(QtCore.Qt.Vertical)
self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel|QtGui.QDialogButtonBox.Ok)
self.buttonBox.setObjectName("buttonBox")
self.buttonBox.accepted.connect(Dialog.accept)
self.buttonBox.rejected.connect(Dialog.reject)
QtCore.QMetaObject.connectSlotsByName(Dialog)
self.setLayout(QtGui.QHBoxLayout())
self.layout().setContentsMargins(0,0,0,0)
def setupPlot(self):
self.figure = plt.figure()
self.figure.set_facecolor("none")
self.canvas = FigureCanvas(self.figure)
self.canvas.setContentsMargins(0,0,0,0)
self.widget = QtGui.QWidget()
self.widget.setContentsMargins(0,0,0,0)
self.toolbar = NavigationToolbar(self.canvas, self)
self.toolbar.setContentsMargins(0,0,0,0)
layout = QVBoxLayout()
layout.setSpacing(0)
layout.setContentsMargins(0,0,0,0)
layout.addWidget(self.toolbar)
layout.addWidget(self.canvas)
self.widget.setLayout(layout)
self.layout().addWidget(self.widget)
self.layout().addWidget(self.buttonBox)
self.plot()
def plot(self):
data = [random.random() for i in range(10)]
self.figure.clear()
ax = self.figure.add_subplot(111)
ax.plot(data, '*-')
self.canvas.draw()
if __name__ == '__main__':
app = QApplication(sys.argv)
main = Ui_Dialog()
main.show()
sys.exit(app.exec_())
Alternatively, you can first place the toolbar in a QVBoxLayout and beneath place a widget containing the canvas and the Button box in a QHBoxLayout.
import sys
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt4agg import NavigationToolbar2QT as NavigationToolbar
import matplotlib.pyplot as plt
import random
from PyQt4 import QtCore, QtGui
class Ui_Dialog(QtGui.QDialog):
def __init__(self, parent=None):
super(Ui_Dialog, self).__init__(parent)
self.setupUi(self)
def setupUi(self, Dialog):
Dialog.setObjectName("Dialog")
Dialog.resize(400, 300)
self.buttonBox = QtGui.QDialogButtonBox(Dialog)
self.buttonBox.setOrientation(QtCore.Qt.Vertical)
self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel|QtGui.QDialogButtonBox.Ok)
self.buttonBox.setObjectName("buttonBox")
self.buttonBox.accepted.connect(Dialog.accept)
self.buttonBox.rejected.connect(Dialog.reject)
QtCore.QMetaObject.connectSlotsByName(Dialog)
self.setLayout(QtGui.QVBoxLayout())
self.layout().setContentsMargins(0,0,0,0)
self.figure = plt.figure()
self.figure.set_facecolor("none")
self.canvas = FigureCanvas(self.figure)
self.widget = QtGui.QWidget()
self.toolbar = NavigationToolbar(self.canvas, self)
self.layout().addWidget(self.toolbar)
layout = QtGui.QHBoxLayout()
layout.addWidget(self.canvas)
layout.addWidget(self.buttonBox)
self.widget.setLayout(layout)
self.layout().addWidget(self.widget)
self.plot()
def plot(self):
data = [random.random() for i in range(10)]
self.figure.clear()
ax = self.figure.add_subplot(111)
ax.plot(data, '*-')
self.canvas.draw()
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
main = Ui_Dialog()
main.show()
sys.exit(app.exec_())

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