How to relate QGraphicsScene position to position on a matplotlib plot axis - python

I have a window which holds two plots, one is a 2D plot and the other is a selection of a cut along the y-axis of that plot. I would like to be able to select what cut I want by moving a horizontal bar up to a position and having the 1D plot update. I am having trouble relating the position from the scene where the line item is, to the axes on the plot. The part where I would be figuring this out is in the main window in the function defined plot_position. I am also open to other ideas of how to go about this. Here is a screen-shot for reference:
import sys
from PyQt5.Qt import Qt, QObject, QPen, QPointF
from PyQt5.QtCore import pyqtSignal
from PyQt5.QtWidgets import QSizePolicy
from PyQt5.QtWidgets import QApplication, QMainWindow, QGraphicsLineItem, QGraphicsView, \
QGraphicsScene, QWidget, QHBoxLayout
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
import xarray as xr
import numpy as np
class Signals(QObject):
bttnReleased = pyqtSignal(float, float)
class HLineItem(QGraphicsLineItem):
def __init__(self, signals):
super(HLineItem, self).__init__()
self.signals = signals
self.setPen(QPen(Qt.red, 3))
self.setFlag(QGraphicsLineItem.ItemIsMovable)
self.setCursor(Qt.OpenHandCursor)
self.setAcceptHoverEvents(True)
def mouseMoveEvent(self, event):
orig_cursor_position = event.lastScenePos()
updated_cursor_position = event.scenePos()
orig_position = self.scenePos()
updated_cursor_y = updated_cursor_position.y() - \
orig_cursor_position.y() + orig_position.y()
self.setPos(QPointF(orig_position.x(), updated_cursor_y))
def mouseReleaseEvent(self, event):
x_pos = event.scenePos().x()
y_pos = event.scenePos().y()
self.signals.bttnReleased.emit(x_pos, y_pos)
class PlotCanvas(FigureCanvas):
def __init__(self, parent=None, width=5, height=4, dpi=100):
self.fig = Figure(figsize=(width, height), dpi=dpi)
super(PlotCanvas, self).__init__(self.fig)
self.setParent(parent)
FigureCanvas.setSizePolicy(self,
QSizePolicy.Expanding,
QSizePolicy.Expanding)
FigureCanvas.updateGeometry(self)
self.data = xr.DataArray()
self.axes = None
def plot(self, data):
self.data = data
self.axes = self.fig.add_subplot(111)
self.data.plot(ax=self.axes)
self.axes.set_xlim(-.5, .5)
self.draw()
class MainWindow(QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.signals = Signals()
x = np.linspace(-1, 1, 51)
y = np.linspace(-1, 1, 51)
z = np.linspace(-1, 1, 51)
xyz = np.meshgrid(x, y, z, indexing='ij')
d = np.sin(np.pi * np.exp(-1 * (xyz[0] ** 2 + xyz[1] ** 2 + xyz[2] ** 2))) * np.cos(np.pi / 2 * xyz[1])
self.xar = xr.DataArray(d, coords={"slit": x, 'perp': y, "energy": z}, dims=["slit", "perp", "energy"])
self.cut = self.xar.sel({"perp": 0}, method='nearest')
self.edc = self.cut.sel({'slit': 0}, method='nearest')
self.canvas = PlotCanvas()
self.canvas_edc = PlotCanvas()
self.canvas.plot(self.cut)
self.canvas_edc.plot(self.edc)
self.view = QGraphicsView()
self.scene = QGraphicsScene()
self.line = HLineItem(self.signals)
self.line_pos = [0, 0]
self.layout1 = QHBoxLayout()
self.layout2 = QHBoxLayout()
self.connect_scene()
self.layout1.addWidget(self.view)
self.layout2.addWidget(self.canvas_edc)
self.central = QWidget()
self.main_layout = QHBoxLayout()
self.main_layout.addLayout(self.layout1)
self.main_layout.addLayout(self.layout2)
self.central.setLayout(self.main_layout)
self.setCentralWidget(self.central)
self.signals.bttnReleased.connect(self.plot_position)
def connect_scene(self):
s = self.canvas.figure.get_size_inches() * self.canvas.figure.dpi
self.view.setScene(self.scene)
self.scene.addWidget(self.canvas)
# self.scene.setSceneRect(0, 0, s[0], s[1])
# self.capture_scene_change()
self.line.setLine(0, 0, self.scene.sceneRect().width(), 0)
self.line.setPos(self.line_pos[0], self.line_pos[1])
self.scene.addItem(self.line)
def handle_plotting(self):
self.clearLayout(self.layout2)
self.refresh_edc()
def refresh_edc(self):
self.canvas_edc = PlotCanvas()
self.canvas_edc.plot(self.edc)
self.layout2.addWidget(self.canvas_edc)
def clearLayout(self, layout):
while layout.count():
child = layout.takeAt(0)
if child.widget():
child.widget().deleteLater()
def plot_position(self, x, y):
self.line_pos = [x, y]
plot_bbox = self.canvas.axes.get_position()
# something here to relate the hline position in the scene to the axes positions/plot axes
sel_val = min(self.cut.slit, key=lambda f: abs(f - y)) # this should not be y, but rather
# the corresponding value on the y-axis
self.edc = self.cut.sel({"slit": 0}, method='nearest') # this should not be 0, but rather
# the sel_val
self.handle_plotting()
class App(QApplication):
def __init__(self, sys_argv):
super(App, self).__init__(sys_argv)
self.setAttribute(Qt.AA_EnableHighDpiScaling)
self.mainWindow = MainWindow()
self.mainWindow.setWindowTitle("arpys")
self.mainWindow.show()
def main():
app = App(sys.argv)
sys.exit(app.exec_())
if __name__ == "__main__":
main()

This took me quite a while to figure out. The frustrating part was dealing with Matplotlib's positions of their objects and understanding what classes include what. For a while I was trying to use axes.get_position but this was returning a value for the height that was way too small. I am still not sure for this function what they define as y1 and y0 to give the height. Next I looked at axes.get_window_extent (had a similar problem) and axes.get_tightbbox. the tightbbox function returns the bounding box of the axes including their decorators (xlabel, title, etc) which was better but was actually giving me a height that was beyond the extent I wanted since it included these decorators. Finally, I found that what I really wanted with the length of the spine! In the end I used the spine.get_window_extent() function and this was exactly what I needed. I have attached the updated code.
It was helpful to look at this diagram of the inheritances to see what I was dealing with/looking for.
import sys
from PyQt5.Qt import Qt, QObject, QPen, QPointF
from PyQt5.QtCore import pyqtSignal
from PyQt5.QtWidgets import QSizePolicy
from PyQt5.QtWidgets import QApplication, QMainWindow, QGraphicsLineItem, QGraphicsView, \
QGraphicsScene, QWidget, QHBoxLayout
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
import xarray as xr
import numpy as np
class Signals(QObject):
bttnReleased = pyqtSignal(float)
class HLineItem(QGraphicsLineItem):
def __init__(self, signals):
super(HLineItem, self).__init__()
self.signals = signals
self.setPen(QPen(Qt.red, 3))
self.setFlag(QGraphicsLineItem.ItemIsMovable)
self.setCursor(Qt.OpenHandCursor)
self.setAcceptHoverEvents(True)
def mouseMoveEvent(self, event):
orig_cursor_position = event.lastScenePos()
updated_cursor_position = event.scenePos()
orig_position = self.scenePos()
updated_cursor_y = updated_cursor_position.y() - \
orig_cursor_position.y() + orig_position.y()
self.setPos(QPointF(orig_position.x(), updated_cursor_y))
def mouseReleaseEvent(self, event):
y_pos = event.scenePos().y()
self.signals.bttnReleased.emit(y_pos)
class PlotCanvas(FigureCanvas):
def __init__(self, parent=None, width=5, height=4, dpi=100):
self.fig = Figure(figsize=(width, height), dpi=dpi)
super(PlotCanvas, self).__init__(self.fig)
self.setParent(parent)
FigureCanvas.setSizePolicy(self,
QSizePolicy.Expanding,
QSizePolicy.Expanding)
FigureCanvas.updateGeometry(self)
self.data = xr.DataArray()
self.axes = None
def plot(self, data):
self.data = data
self.axes = self.fig.add_subplot(111)
self.data.plot(ax=self.axes)
self.fig.subplots_adjust(left=0.2)
self.fig.subplots_adjust(bottom=0.2)
self.axes.set_xlim(-.5, .5)
self.draw()
class MainWindow(QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.signals = Signals()
x = np.linspace(-1, 1, 51)
y = np.linspace(-1, 1, 51)
z = np.linspace(-1, 1, 51)
xyz = np.meshgrid(x, y, z, indexing='ij')
d = np.sin(np.pi * np.exp(-1 * (xyz[0] ** 2 + xyz[1] ** 2 + xyz[2] ** 2))) * np.cos(np.pi / 2 * xyz[1])
self.xar = xr.DataArray(d, coords={"slit": x, 'perp': y, "energy": z}, dims=["slit", "perp", "energy"])
self.cut = self.xar.sel({"perp": 0}, method='nearest')
self.edc = self.cut.sel({'slit': 0}, method='nearest')
self.canvas = PlotCanvas()
self.canvas_edc = PlotCanvas()
self.canvas.plot(self.cut)
self.canvas_edc.plot(self.edc)
self.view = QGraphicsView()
self.scene = QGraphicsScene()
self.line = HLineItem(self.signals)
self.line_pos = [0, 0]
self.layout1 = QHBoxLayout()
self.layout2 = QHBoxLayout()
self.connect_scene()
self.layout1.addWidget(self.view)
self.layout2.addWidget(self.canvas_edc)
self.central = QWidget()
self.main_layout = QHBoxLayout()
self.main_layout.addLayout(self.layout1)
self.main_layout.addLayout(self.layout2)
self.central.setLayout(self.main_layout)
self.setCentralWidget(self.central)
self.signals.bttnReleased.connect(self.plot_position)
def connect_scene(self):
s = self.canvas.figure.get_size_inches() * self.canvas.figure.dpi
self.view.setScene(self.scene)
self.scene.addWidget(self.canvas)
self.scene.setSceneRect(0, 0, s[0], s[1])
# self.capture_scene_change()
self.line.setLine(0, 0, self.scene.sceneRect().width(), 0)
self.line.setPos(self.line_pos[0], self.line_pos[1])
self.scene.addItem(self.line)
def handle_plotting(self):
self.clearLayout(self.layout2)
self.refresh_edc()
def refresh_edc(self):
self.canvas_edc = PlotCanvas()
self.canvas_edc.plot(self.edc)
self.layout2.addWidget(self.canvas_edc)
def clearLayout(self, layout):
while layout.count():
child = layout.takeAt(0)
if child.widget():
child.widget().deleteLater()
def plot_position(self, y):
rel_pos = lambda x: abs(self.scene.sceneRect().height() - x)
bbox = self.canvas.axes.spines['left'].get_window_extent()
plot_bbox = [bbox.y0, bbox.y1]
if rel_pos(y) < plot_bbox[0]:
self.line.setPos(0, rel_pos(plot_bbox[0]))
elif rel_pos(y) > plot_bbox[1]:
self.line.setPos(0, rel_pos(plot_bbox[1]))
self.line_pos = self.line.pos().y()
size_range = len(self.cut.slit)
r = np.linspace(plot_bbox[0], plot_bbox[1], size_range).tolist()
corr = list(zip(r, self.cut.slit.values))
sel_val = min(r, key=lambda f: abs(f - rel_pos(self.line_pos)))
what_index = r.index(sel_val)
self.edc = self.cut.sel({"slit": corr[what_index][1]}, method='nearest')
self.handle_plotting()
class App(QApplication):
def __init__(self, sys_argv):
super(App, self).__init__(sys_argv)
self.setAttribute(Qt.AA_EnableHighDpiScaling)
self.mainWindow = MainWindow()
self.mainWindow.setWindowTitle("arpys")
self.mainWindow.show()
def main():
app = App(sys.argv)
sys.exit(app.exec_())
if __name__ == "__main__":
main()

Related

Matching the scatter points colour with the Image colour bar

I have made this GUI with PyQt5:
How can I make the colour of the points in the bottom plot match the colour bar in the 2D plot above? And change accordingly when I change the bar max/min ?
MWE:
from PyQt5 import QtGui, QtCore
import pyqtgraph as pg
import sys
import numpy as np
width = 1000
height = 500
x = np.linspace(-10,10,100)
def func():
X, Y = np.meshgrid(x, x)
return np.exp(-X**2/10 - Y**2/2)
array = func()
sumxaxis = np.sum(array, axis=0)
sumyaxis = np.sum(array, axis=1)
class layout():
def setup(self, window):
self.window = window
self.window.resize(width, height)
self.centralwidget = QtGui.QWidget(self.window)
self.horizontallayout = QtGui.QHBoxLayout(self.centralwidget)
self.window.setCentralWidget(self.centralwidget)
self.plot = pg.GraphicsLayoutWidget(self.window)
self.horizontallayout.addWidget(self.plot)
self.view = self.plot.addPlot()
self.img = pg.ImageItem(border='k')
self.img.setImage(array, border = 'k')
self.view.addItem(self.img)
self.viewbox = self.view.getViewBox()
self.hist = pg.HistogramLUTItem()
self.hist.setImageItem(self.img)
self.hist.setLevels(0, 1)
self.hist.gradient.loadPreset('viridis')
self.plot.addItem(self.hist, colspan=1)
self.plot.nextRow()
self.plot2 = self.plot.addPlot(colspan=1)
self.plot2.setMaximumHeight(200)
self.plot2.plot(-x,sumyaxis,symbol='o',symbolSize=1, symbolBrush=('w'), pen=None, clear=True)
class Window(pg.Qt.QtGui.QMainWindow, layout):
def __init__(self, shot = None):
super(Window, self).__init__()
self.setup(self)
self.show()
if __name__ == '__main__':
app = pg.Qt.QtGui.QApplication([])
Window()
sys.exit(app.exec_())
UPDATE
I managed to change the scatter plot to the same colourbar as the 2D image, but I have not yet managed to make it change its colour as I drag the colour bar cursor!
New MWE:
from PyQt5 import QtGui, QtCore
import pyqtgraph as pg
import sys
import numpy as np
width = 1000
height = 500
x = np.linspace(-10,10,100)
def func():
X, Y = np.meshgrid(x, x)
return np.exp(-X**2/10 - Y**2/2)
array = func()
sumyaxis = array[:, len(array)//2]
class layout():
def setup(self, window):
self.window = window
self.window.resize(width, height)
self.centralwidget = QtGui.QWidget(self.window)
self.horizontallayout = QtGui.QHBoxLayout(self.centralwidget)
self.window.setCentralWidget(self.centralwidget)
self.plot = pg.GraphicsLayoutWidget(self.window)
self.horizontallayout.addWidget(self.plot)
self.view = self.plot.addPlot()
self.img = pg.ImageItem(border='k')
self.img.setImage(array, border = 'k')
self.view.addItem(self.img)
self.viewbox = self.view.getViewBox()
self.hist = pg.HistogramLUTItem()
self.hist.setImageItem(self.img)
self.hist.setLevels(0, 1)
self.hist.gradient.loadPreset('viridis')
self.plot.addItem(self.hist, colspan=1)
self.colours = self.hist.getLookupTable(img=array)
self.cmap = pg.ColorMap(pos=np.linspace(self.hist.getLevels()[0], self.hist.getLevels()[1], len(self.colours)), color=self.colours)
self.plot.nextRow()
self.plot2 = self.plot.addPlot(colspan=1)
self.plot2.setMaximumHeight(200)
self.c = self.cmap.map(sumyaxis, 'qcolor')
self.plot2.plot(-x,sumyaxis,symbol='o',symbolSize=10, symbolBrush = self.c, pen=None, clear=True)
class Window(pg.Qt.QtGui.QMainWindow, layout):
def __init__(self, shot = None):
super(Window, self).__init__()
self.setup(self)
self.show()
if __name__ == '__main__':
app = pg.Qt.QtGui.QApplication([])
w = Window()
sys.exit(app.exec_())
You have to use the sigLookupTableChanged signal and implement the logic to update the symbolBrush of PlotDataItem:
# ...
self.plot.nextRow()
self.plot2 = self.plot.addPlot(colspan=1)
self.plot2.setMaximumHeight(200)
self.curve = self.plot2.plot(
-x,
sumyaxis,
symbol="o",
symbolSize=10,
pen=None,
clear=True,
)
self.hist.sigLookupTableChanged.connect(self.handle_sigLookupTableChanged)
self.handle_sigLevelsChanged()
def handle_sigLookupTableChanged(self):
colours = self.hist.getLookupTable(img=array)
cmap = pg.ColorMap(
pos=np.linspace(*self.hist.getLevels(), len(colours)),
color=colours,
)
c = cmap.map(sumyaxis, "qcolor")
self.curve.opts["symbolBrush"] = c
self.curve.updateItems()

Matplotlib integrated in a QScrollArea isn't refresh curves properly on mac OS

I have an issue with a matplotlib figure that I've embedded in a pyqt5 QScrollArea.
My issue is when I'm scrolling down, it is like only the top of the matplotlib has been updated.
For instance, in the image below, the curves below the curve number 35 are not updated properly.
This only happens with mac OS but it run well on Windows 10.
I don't find the issue in the code.
Update
To me, it looks like the matplotlib figure doesn't match with the QSrollBar. I try to explain more. When I scroll down with the Qsrollbar, the Matplotlib figure is going down but not as fast as the scrollbar. I see that with the number of curve that is increasing when I scroll down. It is like the Qscrollbar go down too fast with respect to the matplotlib figure update.
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
import sys
import matplotlib
matplotlib.use('Qt5Agg')
import matplotlib.pyplot as plt
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
import numpy as np
import time
class Viewer(QMainWindow):
def __init__(self, parent=None):
super(Viewer, self).__init__()
self.parent = parent
#######################################
self.centralWidget = QWidget()
self.setCentralWidget(self.centralWidget)
self.mainVBOX_param_scene = QVBoxLayout()
self.mascene = plot(self)
self.paramPlotV = QVBoxLayout()
self.horizontalSliders = QScrollBar(Qt.Horizontal)
self.horizontalSliders.setFocusPolicy(Qt.StrongFocus)
self.horizontalSliders.valueChanged.connect(self.update_plot)
self.horizontalSliders.setMinimum(0)
self.horizontalSliders.setMaximum(1)
self.paramPlot = QHBoxLayout()
l_gain = QLabel('Gain')
self.e_gain = QLineEdit('5')
l_win = QLabel('Window')
self.e_win = QLineEdit('10')
l_spacing = QLabel('vertical spacing')
self.e_spacing = QLineEdit('10')
l_linewidth = QLabel('linewidth')
self.e_linewidth = QLineEdit('1')
self.e_gain.returnPressed.connect(self.update_plot)
self.e_win.returnPressed.connect(self.udpate_plot_plus_slider)
self.e_spacing.returnPressed.connect(self.update_plot)
self.e_linewidth.returnPressed.connect(self.update_plot)
self.paramPlot.addWidget(l_gain)
self.paramPlot.addWidget(self.e_gain)
self.paramPlot.addWidget(l_win)
self.paramPlot.addWidget(self.e_win)
self.paramPlot.addWidget(l_spacing)
self.paramPlot.addWidget(self.e_spacing)
self.paramPlot.addWidget(l_linewidth)
self.paramPlot.addWidget(self.e_linewidth)
self.paramPlotV.addWidget(self.horizontalSliders)
self.paramPlotV.addLayout(self.paramPlot)
self.mainVBOX_param_scene.addWidget(self.mascene)
self.mainVBOX_param_scene.addLayout(self.paramPlotV)
self.centralWidget.setLayout(self.mainVBOX_param_scene)
self.Fs = 1024
self.Sigs_dict = np.random.rand(50,20*self.Fs)
self.t = np.arange(self.Sigs_dict.shape[1])/self.Fs
self.parent.processEvents()
self.update()
def updateslider(self):
self.horizontalSliders.setMinimum(0)
self.horizontalSliders.setMaximum(np.ceil(self.t[-1]/int(self.e_win.text()))-1)
self.horizontalSliders.setPageStep(1)
self.horizontalSliders.update()
def udpate_plot_plus_slider(self):
self.updateslider()
self.mascene.update()
def update_plot(self):
t0 = time.time()
self.mascene.update()
print('time old:', time.time()-t0)
def update(self):
self.updateslider()
self.mascene.modify_sigs()
self.mascene.update()
class plot(QGraphicsView):
def __init__(self, parent=None):
super(plot, self).__init__(parent)
self.parent = parent
self.scene = QGraphicsScene(self)
self.setScene(self.scene)
self.figure = plt.figure(facecolor='white')#Figure()
self.canvas = FigureCanvas(self.figure)
self.widget = QWidget()
self.widget.setLayout(QVBoxLayout())
self.widget.layout().setContentsMargins(0, 0, 0, 0)
self.widget.layout().setSpacing(0)
self.scroll = QScrollArea(self.widget)
self.scroll.setWidget(self.canvas)
layout = QVBoxLayout()
layout.addWidget(self.scroll)
self.setLayout(layout)
def modify_sigs(self):
self.Sigs_dict = self.parent.Sigs_dict
self.t = self.parent.t
self.Fs= self.parent.Fs
def update(self):
win_num = self.parent.horizontalSliders.value()
self.figure.clear()
plt.figure(self.figure.number)
plt.subplots_adjust(left=0.1, bottom=0.01, right=1, top=1, wspace=0.0 , hspace=0.0 )
self.axes = plt.subplot(1, 1, 1)
gain = float(self.parent.e_gain.text())
win= float(self.parent.e_win.text())
self.spacing = float(self.parent.e_spacing.text())
linewidth = float(self.parent.e_linewidth.text())
ts = int(win*(win_num) * self.Fs)
te = ts + int(win * self.Fs)
if te > len(self.t):
te=len(self.t)
for i in range(self.Sigs_dict.shape[0]):
line, = plt.plot(self.t[ts:te], gain*(self.Sigs_dict[i,ts:te]-np.mean(self.Sigs_dict[i,ts:te]))+i*self.spacing, linewidth=linewidth )
self.axes.autoscale(enable=True, axis='both', tight=True)
self.axes.set_ylim((-self.spacing,(self.Sigs_dict.shape[0]+1)*self.spacing))
self.axes.set_xlim((ts/ self.Fs, ts / self.Fs + win ))
self.axes.set_yticks(np.arange(self.Sigs_dict.shape[0]) * self.spacing)
self.axes.set_yticklabels([str(n) for n in np.arange(self.Sigs_dict.shape[0])])
self.canvas.setGeometry(0, 0, self.parent.width()-100, (self.parent.height()-100)*self.spacing)
self.canvas.draw()
def main():
app = QApplication(sys.argv)
app.setStyle('Windows')
ex = Viewer(app)
ex.showMaximized()
sys.exit(app.exec())
if __name__ == '__main__':
main()
I update the version of my matplotlib module from 3.1.1 to 3.2.0rc1 and it solves the issue.

How to organize layout using PyQt

I'm very new to PyQt and I'm finding ordering the widgets in the window pretty hard.
I have a plot that updates in real-time, a table that updates every time that the user clicks on the plot, and two buttons (start/stop).
The current layout is behaving this way:
I want it to look something like this:
Where "other widgets" correspond to future Widgets I'll add when this is working (such as a slider or a dropdown list). The table should also be as thin as possible, to maximize the size of the plot and don't have the table occupying unnecessary space. My current code is the following one (it's self-contained):
import sys
import numpy as np
from matplotlib.backends.qt_compat import QtWidgets
from matplotlib.backends.backend_qt5agg import FigureCanvas
from matplotlib.figure import Figure
def onclick(event):
global clicks
clicks.append(event.xdata)
return
class ApplicationWindow(QtWidgets.QMainWindow):
def __init__(self):
super(ApplicationWindow, self).__init__()
self._title = 'Prueba real-time'
self.setWindowTitle(self._title)
self._main = QtWidgets.QWidget()
self.setCentralWidget(self._main)
layout = QtWidgets.QHBoxLayout(self._main)
dynamic_canvas = FigureCanvas(Figure(figsize=(10, 10)))
layout.addWidget(dynamic_canvas)
self._dynamic_ax = dynamic_canvas.figure.subplots()
dynamic_canvas.figure.canvas.mpl_connect('button_press_event', onclick)
self._dynamic_ax.grid()
self._timer = dynamic_canvas.new_timer(
100, [(self._update_window, (), {})])
self._timer.start()
button_stop = QtWidgets.QPushButton('Stop', self)
layout.addWidget(button_stop)
button_stop.clicked.connect(self.button_pressed)
button_start = QtWidgets.QPushButton('Start', self)
layout.addWidget(button_start)
button_start.clicked.connect(self.button_pressed)
self.table_clicks = QtWidgets.QTableWidget()
self.table_clicks.setRowCount(0)
self.table_clicks.setColumnCount(2)
layout.addWidget(self.table_clicks)
def button_pressed(self):
if self.sender().text() == 'Stop':
self._timer.stop()
if self.sender().text() == 'Start':
self._timer.start()
def _update_window(self):
self._dynamic_ax.clear()
global x, y1, y2, y3, N, count_iter, last_number_clicks
x.append(x[count_iter] + 0.01)
y1.append(np.random.random())
idx_inf = max([count_iter-N, 0])
if last_number_clicks < len(clicks):
for new_click in clicks[last_number_clicks:(len(clicks))]:
rowPosition = self.table_clicks.rowCount()
self.table_clicks.insertRow(rowPosition)
self.table_clicks.setItem(rowPosition,0, QtWidgets.QTableWidgetItem(str(new_click)))
self.table_clicks.setItem(rowPosition,1, QtWidgets.QTableWidgetItem("Descripcion"))
last_number_clicks = len(clicks)
self._dynamic_ax.plot(x[idx_inf:count_iter], y1[idx_inf:count_iter],'-o', color='b')
count_iter += 1
self._dynamic_ax.figure.canvas.draw()
#%%
if __name__ == "__main__":
pressed_key = {}
clicks = []
last_number_clicks = len(clicks)
N = 25
y1 = [np.random.random()]
x = [0]
count_iter = 0
qapp = QtWidgets.QApplication(sys.argv)
app = ApplicationWindow()
app.show()
qapp.exec_()
The structure that you want is a grid so you should use a QGridLayout.
QGridLayout has the addWidget() method where you can indicate the row and column where the widget will be placed, the same with addLayout()
void QGridLayout::addWidget(QWidget *widget, int row, int column, Qt::Alignment alignment = ...)
void QGridLayout::addLayout(QLayout *layout, int row, int column, Qt::Alignment alignment = ...)
In your case the structure should be the following:
QGridLayout
├── Plot (0, 0)
├── Table (0, 1)
├── Other Widgets (1, 0)
└── QVBoxLayout (1, 1)
    ├── button_start
   └── button_stop
Then you can indicate the weight of each column, that is, say that the column width will have a proportional width with respect to column 2 using setColumnStretch():
layout.setColumnStretch(0, 2)
layout.setColumnStretch(1, 1)
Considering the above we have the following:
import sys
import numpy as np
from matplotlib.backends.qt_compat import QtCore, QtGui, QtWidgets
from matplotlib.backends.backend_qt5agg import FigureCanvas
from matplotlib.figure import Figure
def onclick(event):
global clicks
clicks.append(event.xdata)
class ApplicationWindow(QtWidgets.QMainWindow):
def __init__(self):
super(ApplicationWindow, self).__init__()
self._title = 'Prueba real-time'
self.setWindowTitle(self._title)
self._main = QtWidgets.QWidget()
self.setCentralWidget(self._main)
dynamic_canvas = FigureCanvas(Figure(figsize=(10, 10)))
self._dynamic_ax = dynamic_canvas.figure.subplots()
dynamic_canvas.figure.canvas.mpl_connect('button_press_event', onclick)
self._dynamic_ax.grid()
self._timer = dynamic_canvas.new_timer(
100, [(self._update_window, (), {})])
self._timer.start()
button_stop = QtWidgets.QPushButton('Stop', self)
button_stop.clicked.connect(self._timer.stop)
button_start = QtWidgets.QPushButton('Start', self)
button_start.clicked.connect(self._timer.start)
self.table_clicks = QtWidgets.QTableWidget(0, 2)
self.table_clicks.horizontalHeader().setSectionResizeMode(QtWidgets.QHeaderView.Stretch)
other_widget = QtWidgets.QLabel("Other widgets",
font=QtGui.QFont("Times", 60, QtGui.QFont.Bold),
alignment=QtCore.Qt.AlignCenter)
# layouts
layout = QtWidgets.QGridLayout(self._main)
layout.addWidget(dynamic_canvas, 0, 0)
layout.addWidget(self.table_clicks, 0, 1)
layout.addWidget(other_widget, 1, 0)
button_layout = QtWidgets.QVBoxLayout()
button_layout.addWidget(button_stop)
button_layout.addWidget(button_start)
layout.addLayout(button_layout, 1, 1)
layout.setColumnStretch(0, 2)
layout.setColumnStretch(1, 1)
def _update_window(self):
self._dynamic_ax.clear()
global x, y1, y2, y3, N, count_iter, last_number_clicks
x.append(x[count_iter] + 0.01)
y1.append(np.random.random())
idx_inf = max([count_iter-N, 0])
if last_number_clicks < len(clicks):
for new_click in clicks[last_number_clicks:(len(clicks))]:
rowPosition = self.table_clicks.rowCount()
self.table_clicks.insertRow(rowPosition)
self.table_clicks.setItem(rowPosition,0, QtWidgets.QTableWidgetItem(str(new_click)))
self.table_clicks.setItem(rowPosition,1, QtWidgets.QTableWidgetItem("Descripcion"))
last_number_clicks = len(clicks)
self._dynamic_ax.plot(x[idx_inf:count_iter], y1[idx_inf:count_iter],'-o', color='b')
count_iter += 1
self._dynamic_ax.figure.canvas.draw()
#%%
if __name__ == "__main__":
pressed_key = {}
clicks = []
last_number_clicks = len(clicks)
N = 25
y1 = [np.random.random()]
x = [0]
count_iter = 0
qapp = QtWidgets.QApplication(sys.argv)
app = ApplicationWindow()
app.show()
sys.exit(qapp.exec_())
I would personally separate the different parts of the program in their own class and use several vertical and horizontal layouts to position the widgets. This especially helps if you want to add features. Especially for large applications this helps to keep the MainWindow, which always tends to become a mess, to be more simple and easier to understand
import sys
import numpy as np
from matplotlib.backends.qt_compat import QtWidgets
from matplotlib.backends.backend_qt5agg import FigureCanvas
from matplotlib.figure import Figure
def onclick(event):
global clicks
clicks.append(event.xdata)
return
class TableWidget(QtWidgets.QWidget):
def __init__(self,parent,*args,**kwargs):
super().__init__(*args,**kwargs)
self.parent = parent
layout = QtWidgets.QVBoxLayout(self)
self.table_clicks = QtWidgets.QTableWidget()
self.table_clicks.setRowCount(0)
self.table_clicks.setColumnCount(2)
layout.addWidget(self.table_clicks)
button_widget = QtWidgets.QWidget(self)
layout.addWidget(button_widget)
button_layout = QtWidgets.QHBoxLayout(button_widget)
button_stop = QtWidgets.QPushButton('Stop', self)
button_layout.addWidget(button_stop)
button_stop.clicked.connect(self.parent.plot_widget.button_pressed)
button_start = QtWidgets.QPushButton('Start', self)
button_layout.addWidget(button_start)
button_start.clicked.connect(self.parent.plot_widget.button_pressed)
class PlotWidget(QtWidgets.QWidget):
def __init__(self,parent,*args,**kwargs):
super().__init__(parent,*args,**kwargs)
self.parent = parent
layout = QtWidgets.QVBoxLayout(self)
dynamic_canvas = FigureCanvas(Figure(figsize=(10, 10)))
layout.addWidget(dynamic_canvas)
self._dynamic_ax = dynamic_canvas.figure.subplots()
dynamic_canvas.figure.canvas.mpl_connect('button_press_event', onclick)
self._dynamic_ax.grid()
self._timer = dynamic_canvas.new_timer(
100, [(self._update_window, (), {})])
self._timer.start()
def button_pressed(self):
if self.sender().text() == 'Stop':
self._timer.stop()
if self.sender().text() == 'Start':
self._timer.start()
def _update_window(self):
self._dynamic_ax.clear()
global x, y1, y2, y3, N, count_iter, last_number_clicks
x.append(x[count_iter] + 0.01)
y1.append(np.random.random())
idx_inf = max([count_iter-N, 0])
if last_number_clicks < len(clicks):
for new_click in clicks[last_number_clicks:(len(clicks))]:
rowPosition = self.parent.table_widget.table_clicks.rowCount()
self.parent.table_widget.table_clicks.insertRow(rowPosition)
self.parent.table_widget.table_clicks.setItem(rowPosition,0, QtWidgets.QTableWidgetItem(str(new_click)))
self.parent.table_widget.table_clicks.setItem(rowPosition,1, QtWidgets.QTableWidgetItem("Descripcion"))
last_number_clicks = len(clicks)
self._dynamic_ax.plot(x[idx_inf:count_iter], y1[idx_inf:count_iter],'-o', color='b')
count_iter += 1
self._dynamic_ax.figure.canvas.draw()
class ApplicationWindow(QtWidgets.QMainWindow):
def __init__(self):
super(ApplicationWindow, self).__init__()
self._title = 'Prueba real-time'
self.setWindowTitle(self._title)
self._main = QtWidgets.QWidget()
self.setCentralWidget(self._main)
main_layout = QtWidgets.QHBoxLayout(self._main)
self.plot_widget = PlotWidget(self)
main_layout.addWidget(self.plot_widget)
self.table_widget = TableWidget(self)
main_layout.addWidget(self.table_widget)
#%%
if __name__ == "__main__":
pressed_key = {}
clicks = []
last_number_clicks = len(clicks)
N = 25
y1 = [np.random.random()]
x = [0]
count_iter = 0
qapp = QtWidgets.QApplication(sys.argv)
app = ApplicationWindow()
app.show()
qapp.exec_()

Python Updating Variables Based on Current Widget Selected

There are a few examples out there of how to do this, but after trying all of them I don't understand how to correctly implement it. I have a program that a button creates a widget with contents inside and assigns it to a grid layout, and it also creates a figure on a canvas. Clicking on the button again creates another widget filled with the same contents and another figure and assigns it to the layout.
One of the contents is a spin box which controls the rotation of the figure. I want each spin box to be able to control the figure that was created with it individually. Here is where I am stuck.
How do I have a general button that can create several widgets, but then on every value change of each spin box, be able to tell which widget it came from so it will rotate the correct figure? I want the widget id or name or however I can access it. Here is what I have so far - Thanks in advance!:
from PyQt4.QtGui import *
from PyQt4.QtCore import *
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas, NavigationToolbar2QT as NavigationToolbar
from matplotlib.figure import Figure
import numpy as np
import math
from scipy.optimize import fsolve
from mplwindow5 import Ui_mplMainWindow
from cU_widget import cU_Widget
class Viewer(QMainWindow, Ui_mplMainWindow):
def __init__(self, parent = None):
super(Viewer, self).__init__(parent)
self.setupUi(self)
self.count = 0
self.cU_widget = []
self.cU_rotate = []
self.lbl_cU_rotate = []
self.lbl_cU = []
self.btn_cU.clicked.connect(self.add_cU)
def add_cU(self):
self.cU_widget.append(int(self.count))
self.cU_widget[self.count] = QWidget(self.scrollAreaWidgetContents)
self.cU_widget[self.count].setMinimumSize(QSize(101, 81))
self.cU_widget[self.count].setMaximumSize(QSize(101, 81))
self.cU_widget[self.count].setObjectName("cU_widget" + str(self.count+1))
self.lbl_cU.append(int(self.count))
self.lbl_cU[self.count] = QLabel("cU " + str(self.count+1), self.cU_widget[self.count])
self.lbl_cU[self.count].setGeometry(QRect(0, 0, 101, 27))
self.lbl_cU[self.count].setObjectName("lbl_cU_" + str(self.count+1))
self.lbl_cU_rotate.append(int(self.count))
self.lbl_cU_rotate[self.count] = QLabel("R", self.cU_widget[self.count])
self.lbl_cU_rotate[self.count].setGeometry(QRect(6, 50, 20, 20))
self.lbl_cU_rotate[self.count].setObjectName("lbl_cU_rotate" + str(self.count+1))
self.cU_rotate.append(int(self.count))
self.cU_rotate[self.count] = QDoubleSpinBox(self.cU_widget[self.count])
self.cU_rotate[self.count].setGeometry(QRect(20, 40, 71, 27))
self.cU_rotate[self.count].setObjectName("cU_rotate" + str(self.count+1))
self.cU_rotate[self.count].valueChanged.connect(self.cU) # ??? What to use here
self.gridLayout.addWidget(self.cU_widget[self.count], self.count, 0)
self.cU()
def cU(self):
self.cU_rotate[self.count] = self.cU_rotate[self.count].value() # ?? What to use here
rotate = 1
tt = np.arange(0,1, 0.001)
lco_x0 = 0
lco_x1 = 4
lco_y0 = 1
lco_y1 = 3
cU_L_x0 = (lco_x0 * math.cos(math.radians(self.cU_rotate[self.count] + rotate))) - (lco_y0 * math.sin(math.radians(self.cU_rotate[self.count] + rotate)))
cU_L_x1 = (lco_x1 * math.cos(math.radians(self.cU_rotate[self.count] + rotate))) - (lco_y1 * math.sin(math.radians(self.cU_rotate[self.count] + rotate)))
#...
cU_L_y0 = (lco_x0 * math.sin(math.radians(self.cU_rotate[self.count] + rotate))) + (lco_y0 * math.cos(math.radians(self.cU_rotate[self.count] + rotate)))
cU_L_y1 = (lco_x1 * math.sin(math.radians(self.cU_rotate[self.count] + rotate))) + (lco_y1 * math.cos(math.radians(self.cU_rotate[self.count] + rotate)))
#...
cU_L_ax = ( 1 * cU_L_x0)
cU_L_bx = ((-6 * cU_L_x0) +(30 * cU_L_x1))
# ...
cU_L_ay = ( 1 * cU_L_y0)
cU_L_by = ((-6 * cU_L_y0) +(30 * cU_L_y1))
#...
cU_L_xtt = (cU_L_ax * tt**2) + (cU_L_bx * tt) + 1
cU_L_ytt = (cU_L_ay * tt**2) + (cU_L_by * tt) + 1
self.mplContainer.canvas.ax.plot(cU_L_xtt, cU_L_ytt, 'r')
self.mplContainer.canvas.ax.set_ylim([-5, 5])
self.mplContainer.canvas.ax.set_xlim([0, 10])
self.mplContainer.canvas.ax.set_aspect(1)
self.mplContainer.canvas.draw()
self.count += 1
app = QApplication(sys.argv)
viewer = Viewer()
viewer.show()
sys.exit(app.exec_())
here is mplwindow5:
from PyQt4 import QtCore, QtGui
class Ui_mplMainWindow(object):
def setupUi(self, mplMainWindow):
mplMainWindow.setObjectName("mplMainWindow")
mplMainWindow.resize(1171, 826)
self.centralwidget = QtGui.QWidget(mplMainWindow)
self.centralwidget.setObjectName("centralwidget")
self.mplContainer = MplWidget(self.centralwidget)
self.mplContainer.setGeometry(QtCore.QRect(259, 20, 861, 741))
self.mplContainer.setObjectName("mplContainer")
self.inputContainer = QtGui.QWidget(self.centralwidget)
self.inputContainer.setGeometry(QtCore.QRect(10, 20, 251, 741))
self.inputContainer.setObjectName("inputContainer")
self.scrollArea = QtGui.QScrollArea(self.inputContainer)
self.scrollArea.setGeometry(QtCore.QRect(0, 160, 241, 581))
self.scrollArea.setFrameShape(QtGui.QFrame.WinPanel)
self.scrollArea.setLineWidth(1)
self.scrollArea.setMidLineWidth(10)
self.scrollArea.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
self.scrollArea.setWidgetResizable(True)
self.scrollArea.setObjectName("scrollArea")
self.scrollAreaWidgetContents = QtGui.QWidget()
self.scrollAreaWidgetContents.setGeometry(QtCore.QRect(0, 0, 226, 577))
self.scrollAreaWidgetContents.setObjectName("scrollAreaWidgetContents")
self.scrollLayout = QtGui.QVBoxLayout(self.scrollAreaWidgetContents)
self.gridLayout = QtGui.QGridLayout()
self.gridLayout.setObjectName("formLayout")
self.gridLayout.setColumnStretch(0, 0)
self.gridLayout.setColumnStretch(2, 4)
self.scrollLayout.addLayout(self.gridLayout)
self.scrollLayout.addStretch()
self.scrollArea.setWidget(self.scrollAreaWidgetContents)
mplMainWindow.setCentralWidget(self.centralwidget)
self.btn_cU = QtGui.QPushButton("cU", self.inputContainer)
self.btn_cU.setGeometry(QtCore.QRect(0, 0, 31, 27))
self.btn_cU.setObjectName("btn_cU")
QtCore.QMetaObject.connectSlotsByName(mplMainWindow)
from mplwidget import MplWidget
mplwidget:
from PyQt4.QtGui import *
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas, NavigationToolbar2QT as NavigationToolbar
from matplotlib.figure import Figure
class MplCanvas(FigureCanvas):
def __init__(self):
self.fig = Figure()
self.ax = self.fig.add_subplot(111)
FigureCanvas.__init__(self, self.fig)
FigureCanvas.setSizePolicy(self, QSizePolicy.Expanding, QSizePolicy.Preferred)
FigureCanvas.updateGeometry(self)
class MplWidget(QWidget):
def __init__(self, parent = None):
QWidget.__init__(self, parent)
self.main_widget = QWidget(self)
self.canvas = MplCanvas()
self.ntb = NavigationToolbar(self.canvas, self.main_widget)
self.vbl = QGridLayout()
self.vbl.addWidget(self.canvas)
self.vbl.addWidget(self.ntb)
self.setLayout(self.vbl)
As it's not necessary to make extensive mathematical manipulations to solve this issue, I ignored that part. So let's assume that we want to change the slope of several lines using SpinBoxes.
An option would be to make the lines be part of a class that controls the SpinBoxes as well as the matplotlib lines. I called it LineWidget in the code below. When the button is pressed a new LineWidget instance is created and added to a scroll area, where you can manipulate the parameter. Once the parameter changes the line is updated.
Here is a full example where I also simplified the rest of the code.
import sys
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt4agg import NavigationToolbar2QT as NavigationToolbar
from matplotlib.figure import Figure
import numpy as np
from PyQt4 import QtGui , QtCore
class Viewer(QtGui.QMainWindow):
def __init__(self, parent = None):
super(Viewer, self).__init__(parent)
self.setupUI()
self.ax = self.fig.add_subplot(111)
self.count = 0
self.container = []
self.button.clicked.connect(self.addLine)
def setupUI(self):
self.centralwidget = QtGui.QWidget(self)
self.setCentralWidget(self.centralwidget)
self.centralwidget.setLayout(QtGui.QHBoxLayout())
self.leftWidget = QtGui.QWidget(self)
self.leftWidget.setMinimumWidth(200)
self.leftWidget.setLayout(QtGui.QVBoxLayout())
self.mplWidget = QtGui.QWidget(self)
self.mplWidget.setLayout(QtGui.QVBoxLayout())
self.fig = Figure()
self.canvas = FigureCanvas(self.fig)
self.ntb = NavigationToolbar(self.canvas, self.mplWidget)
self.mplWidget.layout().addWidget(self.canvas)
self.mplWidget.layout().addWidget(self.ntb)
self.button = QtGui.QPushButton("Push")
self.scrollWidget = QtGui.QWidget()
self.scrollLayout = QtGui.QVBoxLayout()
self.scrollWidget.setLayout(self.scrollLayout)
self.scrollLayout.addStretch()
self.scrollArea = QtGui.QScrollArea()
self.scrollArea.setWidgetResizable(True)
self.scrollArea.setWidget(self.scrollWidget)
self.leftWidget.layout().addWidget(self.button)
self.leftWidget.layout().addWidget(self.scrollArea)
self.centralwidget.layout().addWidget(self.leftWidget)
self.centralwidget.layout().addWidget(self.mplWidget)
def addLine(self):
b = LineWidget(self.count, self.ax)
self.container.append(b)
self.scrollLayout.insertWidget(self.scrollLayout.count() - 1, b)
self.count += 1
class LineWidget(QtGui.QWidget):
def __init__( self, number, ax, R=0, parent=None, **kwargs):
super(LineWidget, self).__init__(parent)
self.number = number
label = QtGui.QLabel("cU " + str(self.number))
self.spin = QtGui.QDoubleSpinBox()
self.spin.setSingleStep(0.2)
self.spin.setRange(-100,100)
self.setLayout(QtGui.QHBoxLayout())
self.layout().addWidget(label)
self.layout().addWidget(self.spin)
self.R = R
self.t = np.linspace(0,1)
self.f = lambda t, R: R*t
self.ax = ax
self.line, = self.ax.plot([],[], **kwargs)
self.update()
self.spin.valueChanged.connect(self.changed)
def changed(self):
self.R = self.spin.value()
self.update()
def update(self):
self.line.set_data(self.t, self.f(self.t, self.R))
self.ax.relim()
self.ax.autoscale_view()
self.ax.figure.canvas.draw_idle()
app = QtGui.QApplication(sys.argv)
viewer = Viewer()
viewer.show()
sys.exit(app.exec_())

Slider widget for PyQtGraph

Is it possible to get a slider widget for PyQtGraph? So that e.g. one or more widgets could control parameters from a plot. Like in this example below (made with enaml/chaco):
I could find nothing like this in the examples or in the list of PyQtGraph widgets. Perhaps one can use a widget from Qt and link it to the PyQtGraph plot object?
It is possible to use any widget of Qt next to pyqtgraph, the graphic objects of pyqtgraph inherit from QWidget, that is to say, they are generics widgets. Here is an example.
import sys
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QApplication, QHBoxLayout, QLabel, QSizePolicy, QSlider, QSpacerItem, \
QVBoxLayout, QWidget
import pyqtgraph as pg
import numpy as np
class Slider(QWidget):
def __init__(self, minimum, maximum, parent=None):
super(Slider, self).__init__(parent=parent)
self.verticalLayout = QVBoxLayout(self)
self.label = QLabel(self)
self.verticalLayout.addWidget(self.label)
self.horizontalLayout = QHBoxLayout()
spacerItem = QSpacerItem(0, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)
self.horizontalLayout.addItem(spacerItem)
self.slider = QSlider(self)
self.slider.setOrientation(Qt.Vertical)
self.horizontalLayout.addWidget(self.slider)
spacerItem1 = QSpacerItem(0, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)
self.horizontalLayout.addItem(spacerItem1)
self.verticalLayout.addLayout(self.horizontalLayout)
self.resize(self.sizeHint())
self.minimum = minimum
self.maximum = maximum
self.slider.valueChanged.connect(self.setLabelValue)
self.x = None
self.setLabelValue(self.slider.value())
def setLabelValue(self, value):
self.x = self.minimum + (float(value) / (self.slider.maximum() - self.slider.minimum())) * (
self.maximum - self.minimum)
self.label.setText("{0:.4g}".format(self.x))
class Widget(QWidget):
def __init__(self, parent=None):
super(Widget, self).__init__(parent=parent)
self.horizontalLayout = QHBoxLayout(self)
self.w1 = Slider(-10, 10)
self.horizontalLayout.addWidget(self.w1)
self.w2 = Slider(-1, 1)
self.horizontalLayout.addWidget(self.w2)
self.w3 = Slider(-10, 10)
self.horizontalLayout.addWidget(self.w3)
self.w4 = Slider(-10, 10)
self.horizontalLayout.addWidget(self.w4)
self.win = pg.GraphicsWindow(title="Basic plotting examples")
self.horizontalLayout.addWidget(self.win)
self.p6 = self.win.addPlot(title="My Plot")
self.curve = self.p6.plot(pen='r')
self.update_plot()
self.w1.slider.valueChanged.connect(self.update_plot)
self.w2.slider.valueChanged.connect(self.update_plot)
self.w3.slider.valueChanged.connect(self.update_plot)
self.w4.slider.valueChanged.connect(self.update_plot)
def update_plot(self):
a = self.w1.x
b = self.w2.x
c = self.w3.x
d = self.w4.x
x = np.linspace(0, 10, 100)
data = a + np.cos(x + c * np.pi / 180) * np.exp(-b * x) * d
self.curve.setData(data)
if __name__ == '__main__':
app = QApplication(sys.argv)
w = Widget()
w.show()
sys.exit(app.exec_())

Categories

Resources