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