I have GUI developed with PyQt5 with an interactive scatterplot in a widget and 2 lineEdit. By clicking a dot, I can get the x and y values but I can't populate my two lineEdit with the two values.
Front_end code:
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(539, 400)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.gridLayout = QtWidgets.QGridLayout(self.centralwidget)
self.gridLayout.setObjectName("gridLayout")
self.lineEdit = QtWidgets.QLineEdit(self.centralwidget)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.lineEdit.sizePolicy().hasHeightForWidth())
self.lineEdit.setSizePolicy(sizePolicy)
self.lineEdit.setObjectName("lineEdit")
self.gridLayout.addWidget(self.lineEdit, 0, 0, 1, 1)
self.widget = QtWidgets.QWidget(self.centralwidget)
self.widget.setObjectName("widget")
self.gridLayout.addWidget(self.widget, 0, 1, 2, 1)
self.lineEdit_2 = QtWidgets.QLineEdit(self.centralwidget)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.lineEdit_2.sizePolicy().hasHeightForWidth())
self.lineEdit_2.setSizePolicy(sizePolicy)
self.lineEdit_2.setObjectName("lineEdit_2")
self.gridLayout.addWidget(self.lineEdit_2, 1, 0, 1, 1)
MainWindow.setCentralWidget(self.centralwidget)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
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_())
Back_end code:
import sys
import matplotlib.pyplot as plt
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import QMainWindow, QApplication, QWidget, QSizePolicy
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar
from matplotlib.figure import Figure
from front_test import Ui_MainWindow
class Graph_init(FigureCanvas):
def __init__(self, parent=None):
fig = Figure()
fig.patch.set_facecolor("None")
self.axes = fig.add_subplot(111)
self.compute_initial_figure()
FigureCanvas.__init__(self, fig)
self.setParent(parent)
FigureCanvas.setSizePolicy(self, QSizePolicy.Expanding, QSizePolicy.Expanding)
FigureCanvas.updateGeometry(self)
FigureCanvas.mpl_connect(self, 'pick_event', self.onclick)
def onclick(self, event):
ind = event.ind[0]
data = event.artist.get_offsets()
xdata, ydata = data[ind,:]
print ((xdata, ydata))
index = [10,20,30].index(xdata)
x = [10,20,30]
y = [100,100,150]
size = [1000,2000,3000]
x_high = x[index]
y_high = y[index]
size_high = size[index]
del x[index]
del y[index]
del size[index]
self.axes.clear()
self.axes.scatter(x, y, s=size, color='blue', picker=1, alpha=0.3)
self.axes.scatter(x_high, y_high, s=size_high, color='red', picker=1, alpha=0.3)
self.axes.figure.canvas.draw()
#self.lineEdit.setText(xdata)
#self.lineEdit_2.setText(ydata)
class Graph_populate(Graph_init):
def compute_initial_figure(self):
x = [10,20,30]
y = [100,100,150]
size = [1000,2000,3000]
self.axes.scatter(x,y,s=size,color='blue', picker=1, alpha=0.3)
class GUI(QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
super(GUI, self).__init__(parent)
self.setupUi(self)
self.sc = Graph_populate(self.widget)
self.gridLayout.addWidget(self.sc, 0, 1, 2, 1)
if __name__ == '__main__':
app = QApplication(sys.argv)
prog = GUI()
prog.showMaximized()
sys.exit(app.exec_())
Thank you
You can not update the QLineEdits in Graph_init since they do not exist in that context, in those cases it is advisable to send the information where the context exists through a signal, this signal must be created:
posClicked = QtCore.pyqtSignal(tuple)
And when you want to send emit() is used
self.posClicked.emit((xdata, ydata))
Then it must be connected to a slot where it is used to fill the QLineEdits as shown below:
class Graph_init(FigureCanvas):
posClicked = QtCore.pyqtSignal(tuple)
def __init__(self, parent=None):
[...]
def onclick(self, event):
ind = event.ind[0]
data = event.artist.get_offsets()
xdata, ydata = data[ind,:]
print ((xdata, ydata))
[...]
self.axes.figure.canvas.draw()
self.posClicked.emit((xdata, ydata))
class GUI(QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
super(GUI, self).__init__(parent)
self.setupUi(self)
self.sc = Graph_populate(self.widget)
self.gridLayout.addWidget(self.sc, 0, 1, 2, 1)
self.sc.posClicked.connect(self.onPosClicked)
def onPosClicked(self, values):
xdata, ydata = values
self.lineEdit.setText(str(xdata))
self.lineEdit_2.setText(str(ydata))
Related
I am trying to create a gui with several live plot EEG/ECG graphs (each plot on different axes).
From what I understand, I need to create multiple PlotWidgets inside a grid layout.
I have created a live plot using pyqtgraph, below however I am unsure how to merge this into the GUI:
from pyqtgraph.Qt import QtGui, QtCore
import pyqtgraph as pg
import collections
import random
import time
import math
import numpy as np
class DynamicPlotter:
def __init__(self, sampleinterval=0.1, timewindow=10., size=(600, 350)):
# Data stuff
self.interval = int(sampleinterval * 1000)
self.bufsize = int(timewindow / sampleinterval)
self.databuffer = collections.deque([0.0] * self.bufsize, self.bufsize)
self.x = np.linspace(-timewindow, 0.0, self.bufsize)
self.y = np.zeros(self.bufsize, dtype=float)
# PyQtGraph stuff
self.app = QtGui.QApplication([])
self.plt = pg.plot(title='EEG/ECG Live Plot')
self.plt.resize(*size)
self.plt.showGrid(x=True, y=True)
#self.plt.setXRange(5,20, padding=0)
self.plt.setLabel('left', 'Amplitude', 'uVrms')
self.plt.setLabel('bottom', 'Time', 's')
self.curve = self.plt.plot(self.x, self.y, pen=(255, 0, 0))
# QTimer
self.timer = QtCore.QTimer()
self.timer.timeout.connect(self.updateplot)
self.timer.start(self.interval)
def getdata(self):
frequency = 0.5
noise = random.normalvariate(0., 1.)
new = 10. * math.sin(time.time() * frequency * 2 * math.pi) + noise
return new
def updateplot(self):
self.databuffer.append(self.getdata())
self.y[:] = self.databuffer
self.curve.setData(self.x, self.y)
self.app.processEvents()
def run(self):
self.app.exec_()
if __name__ == '__main__':
livePlot = DynamicPlotter(sampleinterval=0.05, timewindow=5.)
livePlot.run()
Here is the basic GUI (3 plot widgets inside grid, and a few labels in mainWindow):
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(845, 727)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.labelTitle = QtWidgets.QLabel(self.centralwidget)
self.labelTitle.setGeometry(QtCore.QRect(280, 0, 291, 51))
font = QtGui.QFont()
font.setPointSize(12)
font.setBold(True)
font.setWeight(75)
self.labelTitle.setFont(font)
self.labelTitle.setObjectName("labelTitle")
self.labelCh1 = QtWidgets.QLabel(self.centralwidget)
self.labelCh1.setGeometry(QtCore.QRect(20, 90, 31, 51))
self.labelCh1.setObjectName("labelCh1")
self.labelCh2 = QtWidgets.QLabel(self.centralwidget)
self.labelCh2.setGeometry(QtCore.QRect(20, 180, 31, 51))
self.labelCh2.setObjectName("labelCh2")
self.labelCh3 = QtWidgets.QLabel(self.centralwidget)
self.labelCh3.setGeometry(QtCore.QRect(20, 260, 31, 51))
self.labelCh3.setObjectName("labelCh3")
self.widget = QtWidgets.QWidget(self.centralwidget)
self.widget.setGeometry(QtCore.QRect(70, 70, 741, 261))
self.widget.setObjectName("widget")
self.gridLayout = QtWidgets.QGridLayout(self.widget)
self.gridLayout.setContentsMargins(0, 0, 0, 0)
self.gridLayout.setObjectName("gridLayout")
self.ch1PlotWidget = PlotWidget(self.widget)
self.ch1PlotWidget.setObjectName("ch1PlotWidget")
self.gridLayout.addWidget(self.ch1PlotWidget, 0, 0, 1, 1)
self.ch2PlotWidget = PlotWidget(self.widget)
self.ch2PlotWidget.setObjectName("ch2PlotWidget")
self.gridLayout.addWidget(self.ch2PlotWidget, 1, 0, 1, 1)
self.ch3PlotWidget = PlotWidget(self.widget)
self.ch3PlotWidget.setObjectName("ch3PlotWidget")
self.gridLayout.addWidget(self.ch3PlotWidget, 2, 0, 1, 1)
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 845, 21))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.labelTitle.setText(_translate("MainWindow", "EEG/ECG Recording GUI"))
self.labelCh1.setText(_translate("MainWindow", "Ch 1"))
self.labelCh2.setText(_translate("MainWindow", "Ch 2"))
self.labelCh3.setText(_translate("MainWindow", "Ch 3"))
from pyqtgraph import PlotWidget
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_())
My question is how do I integrate these two so I can plot the live graphs in each widget?
Ideally I want to use a super class so I can simply import the unedited gui.
I have tried importing the gui.Ui_MainWindow into the class and then overwriting the self.plt to self.Ch1PlotWidget
from pyqtgraph.Qt import QtGui, QtCore, QtWidgets
import gui as gui
import sys
import pyqtgraph as pg
import collections
import random
import time
import math
import numpy as np
class MainWindow(QtWidgets.QMainWindow, gui.Ui_MainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent=parent)
self.setupUi(self)
# Data stuff
self.interval = 100
self.bufsize = int(10 / self.interval)
self.databuffer = collections.deque([0.0] * self.bufsize, self.bufsize)
self.x = np.linspace(-10, 0.0, self.bufsize)
self.y = np.zeros(self.bufsize, dtype=float)
# PyQtGraph stuff
self.app = QtGui.QApplication([])
self.ch1PlotWidget = pg.plot(title='Live Plot')
self.ch1PlotWidget.resize(600, 350)
self.ch1PlotWidget.showGrid(x=True, y=True)
# self.plt.setXRange(5,20, padding=0)
self.ch1PlotWidget.setLabel('left', 'Amplitude', 'uVrms')
self.ch1PlotWidget.setLabel('bottom', 'Time', 's')
self.curve = self.ch1PlotWidget.plot(self.x, self.y, pen=(255, 0, 0))
# QTimer
self.timer = QtCore.QTimer()
self.timer.timeout.connect(self.updateplot)
self.timer.start(self.interval)
def getdata(self):
frequency = 0.5
noise = random.normalvariate(0., 1.)
new = 10. * math.sin(time.time() * frequency * 2 * math.pi) + noise
return new
def updateplot(self):
self.databuffer.append(self.getdata())
self.y[:] = self.databuffer
self.curve.setData(self.x, self.y)
self.app.processEvents()
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())
Sorry for all the code, I am just very confused on how to implement logic to the gui.
Here is an option where you can use both classes with minimal changes.
Change the DynamicPlotter constructor to accept a PlotWidget as a argument instead of creating a new one, since they are created and added to a layout in Ui_Mainwindow. In the MainWindow class, create a DynamicPlotter object for each plot (and keep a persistent reference, in this case I added them to a list self.plots).
class DynamicPlotter:
def __init__(self, plot, sampleinterval=0.1, timewindow=10., size=(600, 350)):
# Data stuff
self.interval = int(sampleinterval * 1000)
self.bufsize = int(timewindow / sampleinterval)
self.databuffer = collections.deque([0.0] * self.bufsize, self.bufsize)
self.x = np.linspace(-timewindow, 0.0, self.bufsize)
self.y = np.zeros(self.bufsize, dtype=float)
# PyQtGraph stuff
self.plt = plot
self.plt.setTitle('EEG/ECG Live Plot')
self.plt.resize(*size)
self.plt.showGrid(x=True, y=True)
#self.plt.setXRange(5,20, padding=0)
self.plt.setLabel('left', 'Amplitude', 'uVrms')
self.plt.setLabel('bottom', 'Time', 's')
self.curve = self.plt.plot(self.x, self.y, pen=(255, 0, 0))
# QTimer
self.timer = QtCore.QTimer()
self.timer.timeout.connect(self.updateplot)
self.timer.start(self.interval)
def getdata(self):
frequency = 0.5
noise = random.normalvariate(0., 1.)
new = 10. * math.sin(time.time() * frequency * 2 * math.pi) + noise
return new
def updateplot(self):
self.databuffer.append(self.getdata())
self.y[:] = self.databuffer
self.curve.setData(self.x, self.y)
class MainWindow(QtWidgets.QMainWindow, gui.Ui_MainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent=parent)
self.setupUi(self)
self.plots = []
for plot in (self.ch1PlotWidget, self.ch2PlotWidget, self.ch3PlotWidget):
self.plots.append(
DynamicPlotter(plot, sampleinterval=0.05, timewindow=5.)
)
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())
I'm trying to connect two python folder together the two files contain PYQT5 UI code both of the two files run the codes good but I'm trying to connect the two file with a pushbutton:
file_1 called graph_window.py
contains a pushbutton which when I click it will show file_2 output UI this a proportion of the code :
from output_graph import Dialog # file_2
def view_graph_output(self):
self.w = Dialog()
self.w.show()
def setupUi(self, Dialog):
self.pushButton = QtWidgets.QPushButton(Dialog)
self.pushButton.setGeometry(QtCore.QRect(304, 162, 91, 31))
self.pushButton.setObjectName("pushButton")
# click function for the push button
self.pushButton.clicked.connect(self.view_graph_output)
file two called output_graph.py
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_())
when I run the code it gives me this error:
TypeError: 'QDialog' object is not callable
it seems I'm doing wrong when I call the output_graph from the Dialog class and I try to put the QtWidgets.QDialog with the calls and it didn't work
is there a way to call the dialog class from the pushbutton form file_1?
I think it should go like this:
import sys
from output_graph import Dialog # file_2
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5 import QtCore
from PyQt5 import QtWidgets
class Ui_Dialog(QtWidgets.QDialog):
def __init__(self, parent=None):
super().__init__(parent)
self.setupUi()
# self.canvas = Canvas(self, width=8, height=4)
# self.canvas.move(0, 0)
def view_graph_output(self):
self.w = Dialog()
self.w.show()
def setupUi(self):
self.pushButton = QtWidgets.QPushButton('push', self)
self.pushButton.setGeometry(QtCore.QRect(304, 162, 91, 31))
self.pushButton.setObjectName("pushButton")
# click function for the push button
self.pushButton.clicked.connect(self.view_graph_output)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
w = Ui_Dialog()
w.show()
sys.exit(app.exec_())
and output_graph.py should be:
import sys
from matplotlib.figure import Figure
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5 import QtCore
from PyQt5 import QtWidgets
import numpy as np
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")
# self.pushButton.clicked.connect(lambda: funcion(12))
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_())
PS: it's a messy code so requires cleaning up, but works as you wanted, as to my understanding of the problem
I'm drawing a graph in the matplotlib by converting to FFT with Arduino data. However, the same error does not cause a graph. I think it's because of self in update() what should I do? I don't know how to change when there is a Typeerror in my code. If you find a problem, can you tell me?
+ I solved the error. However, the graph is not printed. The code has been modified as a whole. What happen?
from PyQt5.QtWidgets import*
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
import matplotlib.pyplot as plt
import random
from PyQt5 import QtCore, QtGui, QtWidgets
import datetime
import serial
import time
import random
import numpy as np
from matplotlib import animation
from collections import deque # import a "circular" list
from threading import Thread, Lock
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(1212, 600)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.gridLayout = QtWidgets.QGridLayout()
self.gridLayout.setContentsMargins(0, 0, 0, 0)
self.gridLayout.setObjectName("gridLayout")
self.widget = matplotlibWidget()
self.widget.setObjectName("widget")
self.gridLayout.addWidget(self.widget, 0, 0, 1, 1)
self.centralwidget.setLayout(self.gridLayout)
MainWindow.setCentralWidget(self.centralwidget)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
#from matplotlibwidgetFile import matplotlibWidget
class matplotlibWidget(QWidget):
def __init__(self, parent = None):
QWidget.__init__(self,parent)
self.i = 0
self.data = []
lock = Lock()
freq = 2000 # 1/T
self.guarda = 100 # 200
r = range(0, int(freq/2+1), int(freq/self.guarda))
self.frequencia = np.fft.fftfreq(self.guarda, d=1/freq)
self.acelx = deque([], maxlen=self.guarda)
self.fig = plt.figure()
self.ax = self.fig.add_subplot(111)
self.canvas = FigureCanvas(self.fig)
self.right_layout = QVBoxLayout()
self.right_layout.addWidget(self.canvas)
self.setLayout(self.right_layout)
self.ax.set_xlim((0,int(freq/2)))
self.ax.set_ylim((0,1000))
self.line, = self.ax.plot([],[])
self.ax.grid(True)
def data_input():
for line in arduinoData:
try:
self.i+=1
self.acelx.append(float(line))
with lock:
if self.i > len(self.acelx):
self.data = np.fft.fft(self.acelx)
except ValueError:
pass
t = Thread(target=data_input)
t.daemon = True
t.start()
S = Scope(self.line,self.frequencia, self.guarda, self.data, self.ax, self.acelx, self.i )
timer = QtCore.QTimer()
timer.timeout.connect(S.update)
timer.start(0)
class Scope(matplotlibWidget) :
def __init__(self, line, frequencia, guarda, data, ax, acelx, i) :
self.line, = line,
self.ax = ax
self.frequencia = frequencia
self.guarda = guarda
self.data = data
self.acelx = acelx
self.i = i
def update(self) :
if self.i > len(self.acelx) :
self.line.set_data(self.frequencia[:int(self.guarda/2)], abs(np.real(self.data[:int(self.guarda/2)])))
self.ax.figure.canvas.draw()
return (self.line,)
if __name__ == "__main__":
import sys
value = [0,0,0,0,0,0,0,0,0,0]
arduinoData = serial.Serial('com5', 9600)
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())
You have to create instance:
s = Scope()
timer = QtCore.QTimer()
timer.timeout.connect(s.update)
or
timer = QtCore.QTimer()
timer.timeout.connect(Scope().update)
I try to add the datacursor functionality to my bar chart by using the following code:
datacursor(hover=True, formatter=self.formatter)
No error happens but tooltip doesn't show when I hover my mouse pointer over a bar.
I try to put the following code (and this one works perfectly) into a QWidget:
import numpy as np
import matplotlib.pyplot as plt
from mpldatacursor import datacursor
label = ['a', 'b', 'c', 'd']
x = [1, 2, 3, 4]
y = [10, 20, 30, 40]
fig, ax = plt.subplots()
ax.bar(x, y, align='center', color='lightblue')
ax.margins(0.05)
ax.set_ylim(bottom=0)
def formatter(**kwargs):
dist = abs(np.array(x) - kwargs['x'])
i = dist.argmin()
return '\n'.join(label[i])
datacursor(hover=True, formatter=formatter)
plt.show()
Here my current code.
back_end:
from PyQt5 import QtCore, QtGui, QtWidgets
import sys
from PyQt5.QtWidgets import QMainWindow, QApplication, QWidget
from front_end import Ui_MainWindow
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
import matplotlib.pyplot as plt
from mpldatacursor import datacursor
class Ui_MainWindow(QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
super(Ui_MainWindow, self).__init__(parent)
self.setupUi(self)
self.graph = MyCanvas()
self.gridLayout.addWidget(self.graph, 0, 0, 1, 1)
self.populate()
def populate(self):
self.graph.figure.clf()
self.axes= self.graph.figure.add_subplot(111)
label = ['a', 'b', 'c', 'd']
x = [1, 2, 3, 4]
y = [10, 20, 30, 40]
datacursor(hover=True, formatter=self.formatter)
self.axes.bar(x, y, align='center', bottom=0, color='b')
def formatter(**kwargs):
x = [1, 2, 3, 4]
dist = abs(np.array(x) - kwargs['x'])
i = dist.argmin()
return '\n'.join(label[i])
class MyCanvas(FigureCanvas):
def __init__(self, *args, **kwargs):
self.figure = plt.figure()
FigureCanvas.__init__(self, self.figure)
self.figure.patch.set_facecolor("None")
self.figure.subplots_adjust(left=0.08, bottom=0.10, right=0.99, top=0.97)
if __name__ == '__main__':
app = QApplication(sys.argv)
prog = Ui_MainWindow()
prog.show()
sys.exit(app.exec_())
front_end:
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(625, 460)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.gridLayout = QtWidgets.QGridLayout(self.centralwidget)
self.gridLayout.setObjectName("gridLayout")
self.widget = QtWidgets.QWidget(self.centralwidget)
self.widget.setObjectName("widget")
self.gridLayout.addWidget(self.widget, 0, 0, 1, 1)
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 625, 21))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
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_())
There are several issues.
x and label that are used inside of the formatter method need to be defined. A good solution would be to make them class variables.
The formatter method should be a method of the class. Hence it would best be defined as one by using the self argument.
Naming the class in use the same as an imported class may lead to confusion, best name it differently.
The datacursor needs to know which objects to work on, hence one would need to first define the bar plot and later call the datacursor function with those bars as argument.
There is no need to join anything on the return of the formatter, just return the label to show.
This should then look like
class MyWindow(QMainWindow, Ui_MainWindow):
# ....
def populate(self):
self.graph.figure.clf()
self.axes= self.graph.figure.add_subplot(111)
self.label = ['a', 'b', 'c', 'd']
self.x = [1, 2, 3, 4]
self.y = [10, 20, 30, 40]
bars = self.axes.bar(self.x, self.y, align='center', bottom=0, color='b')
datacursor(bars, hover=True, formatter=self.formatter)
def formatter(self, **kwargs):
dist = abs(np.array(self.x) - kwargs['x'])
i = dist.argmin()
return self.label[i]
I tried to integrate the code of this Radar graphic (see link) into a GUI containing a widget.
Instead of plotting x and y into the widget, I would like to plot a radar graphic but I can't solve this problem.
The idea is to replace this piece of code:
x = [2000,2001,2002,2003,2004]
y = [10,20,30,40,50]
self.axes.plot(x, y
With the code from the link above.
Below the code of the back end of my GUI:
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import QMainWindow, QApplication, QWidget, QSizePolicy
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar
from matplotlib.figure import Figure
from front_end import Ui_MainWindow
class Graph_init(FigureCanvas):
def __init__(self, parent=None):
fig = Figure()
self.axes = fig.add_subplot(111)
self.compute_initial_figure()
FigureCanvas.__init__(self, fig)
self.setParent(parent)
FigureCanvas.setSizePolicy(self, QSizePolicy.Expanding, QSizePolicy.Expanding)
FigureCanvas.updateGeometry(self)
class Graph_populate(Graph_init):
def compute_initial_figure(self):
x = [2000,2001,2002,2003,2004]
y = [10,20,30,40,50]
self.axes.plot(x, y)
class GUI(QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
super(GUI, self).__init__(parent)
self.setupUi(self)
self.sc = Graph_populate(self.widget)
self.gridLayout.addWidget(self.sc, 0, 1, 1, 1)
if __name__ == '__main__':
app = QApplication(sys.argv)
prog = GUI()
prog.showMaximized()
sys.exit(app.exec_())
and the front end:
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(595, 393)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.gridLayout = QtWidgets.QGridLayout(self.centralwidget)
self.gridLayout.setObjectName("gridLayout")
self.pushButton = QtWidgets.QPushButton(self.centralwidget)
self.pushButton.setMaximumSize(QtCore.QSize(100, 16777215))
self.pushButton.setObjectName("pushButton")
self.gridLayout.addWidget(self.pushButton, 0, 0, 1, 1)
self.widget = QtWidgets.QWidget(self.centralwidget)
self.widget.setMinimumSize(QtCore.QSize(0, 200))
self.widget.setStyleSheet("background-color: rgb(255, 255, 255);")
self.widget.setObjectName("widget")
self.gridLayout.addWidget(self.widget, 0, 1, 1, 1)
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 595, 21))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.pushButton.setText(_translate("MainWindow", "PushButton"))
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())
Thank you
the radar type graph is a graph with polar projection, so when creating the axes you must activate that property.
self.axes = fig.add_subplot(111, projection="polar")
When using the plot function, the first parameter is the angle in radians, and the second the length of the radius.
class Graph_init(FigureCanvas):
def __init__(self, parent=None):
fig = Figure()
self.axes = fig.add_subplot(111, projection="polar")
self.compute_initial_figure()
FigureCanvas.__init__(self, fig)
self.setParent(parent)
FigureCanvas.setSizePolicy(self, QSizePolicy.Expanding, QSizePolicy.Expanding)
FigureCanvas.updateGeometry(self)
class Graph_populate(Graph_init):
def compute_initial_figure(self):
import math
r =[10, 20, 30, 40, 50]
theta = [0, 50, 90, 120, 180]
angles = [angle*math.pi/180 for angle in theta]
self.axes.plot(angles, r)