I'm doing my first tests with pyqtchart but becouse of the poor documentation about animated charts I've encountered some issuses. I built a chart that shows the 'sin', 'cos' and 'tan' functions (approximating the value of the tangent) and to make it live I've built a thread that clear and repaint the chart every time.
It works but I don't know if it's the correct way or the most efficent way to do it. I found an example hosted on github but it's not realy clear for me.
I'dont understand if this's the 'offical way' to do it or if pyqtgraph provides some built-in functions to automate it.
I will be realy greatful for anyone who could give me some advice.
This is my code:
from PyQt5.QtWidgets import *
from PyQt5.QtCore import QThread, pyqtSignal
import pyqtgraph as pg
import math
import numpy as np
import sys
import time
class Gui(QWidget):
def __init__(self):
super().__init__()
self.setupUI()
def setupUI(self):
pg.setConfigOption('background', 0.95)
pg.setConfigOptions(antialias=True)
self.plot = pg.PlotWidget()
self.plot.setAspectLocked(lock=True, ratio=0.01)
self.plot.setYRange(-3, 3)
self.widget_layout = QVBoxLayout()
self.widget_layout.addWidget(self.plot)
self.setLayout(self.widget_layout)
def plot_data(self, data):
self.plot.clear()
self.plot.plot(range(0, 720), data[0], pen=pg.mkPen(color='g', width=2))
self.plot.plot(range(0, 720), data[1], pen=pg.mkPen(color='r', width=2))
self.plot.plot(range(0, 720), data[2], pen=pg.mkPen(color='y', width=2))
class Thread(QThread):
sig_plot = pyqtSignal(list)
def __init__(self):
super().__init__()
self.sig_plot.connect(gui.plot_data)
def run(self):
sin_func = np.empty(720)
cos_func = np.empty(720)
tan_func = np.empty(720)
cont = 0
while True:
indx = 0
for ang in range(cont, cont + 720):
rad = math.radians(ang)
cos = math.cos(rad)
sin = math.sin(rad)
if cos != 0: tan = sin / cos
else: tan = sin / 0.00000000001
sin_func[indx] = sin
cos_func[indx] = cos
if tan >= -3 and tan <= 3: tan_func[indx] = tan
else: tan_func[indx] = np.NaN
indx += 1
data = [sin_func, cos_func, tan_func]
self.sig_plot.emit(data)
time.sleep(0.01)
if cont == 720: cont = 0
else: cont += 1
if __name__ == '__main__':
app = QApplication(sys.argv)
gui = Gui()
gui.show()
thread = Thread()
thread.start()
sys.exit(app.exec_())
There is no official way to make animations in pyqtgraph, but the one you sample is not the best because the threads in a GUI are only necessary when there is a heavy task but the task of creating the arrays is not, another mistake is to clean and create the plots, in these cases it is better to reuse. And finally it is better to use the power of calculation of numpy at the level of matrices and arrays than to make a loop.
Considering the above I have implemented a class that calls the function generate_data every certain interval of time with the appropriate index and generates an infinite loop with the help of a QTimer.
from PyQt5 import QtCore, QtWidgets
import pyqtgraph as pg
import numpy as np
class TimeLine(QtCore.QObject):
frameChanged = QtCore.pyqtSignal(int)
def __init__(self, interval=60, loopCount=1, parent=None):
super(TimeLine, self).__init__(parent)
self._startFrame = 0
self._endFrame = 0
self._loopCount = loopCount
self._timer = QtCore.QTimer(self, timeout=self.on_timeout)
self._counter = 0
self._loop_counter = 0
self.setInterval(interval)
def on_timeout(self):
if self._startFrame <= self._counter < self._endFrame:
self.frameChanged.emit(self._counter)
self._counter += 1
else:
self._counter = 0
self._loop_counter += 1
if self._loopCount > 0:
if self._loop_counter >= self.loopCount():
self._timer.stop()
def setLoopCount(self, loopCount):
self._loopCount = loopCount
def loopCount(self):
return self._loopCount
interval = QtCore.pyqtProperty(int, fget=loopCount, fset=setLoopCount)
def setInterval(self, interval):
self._timer.setInterval(interval)
def interval(self):
return self._timer.interval()
interval = QtCore.pyqtProperty(int, fget=interval, fset=setInterval)
def setFrameRange(self, startFrame, endFrame):
self._startFrame = startFrame
self._endFrame = endFrame
#QtCore.pyqtSlot()
def start(self):
self._counter = 0
self._loop_counter = 0
self._timer.start()
class Gui(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.setupUI()
def setupUI(self):
pg.setConfigOption('background', 0.95)
pg.setConfigOptions(antialias=True)
self.plot = pg.PlotWidget()
self.plot.setAspectLocked(lock=True, ratio=0.01)
self.plot.setYRange(-3, 3)
widget_layout = QtWidgets.QVBoxLayout(self)
widget_layout.addWidget(self.plot)
self._plots = [self.plot.plot([], [], pen=pg.mkPen(color=color, width=2)) for color in ("g", "r", "y")]
self._timeline = TimeLine(loopCount=0, interval=10)
self._timeline.setFrameRange(0, 720)
self._timeline.frameChanged.connect(self.generate_data)
self._timeline.start()
def plot_data(self, data):
for plt, val in zip(self._plots, data):
plt.setData(range(len(val)), val)
#QtCore.pyqtSlot(int)
def generate_data(self, i):
ang = np.arange(i, i + 720)
cos_func = np.cos(np.radians(ang))
sin_func = np.sin(np.radians(ang))
tan_func = sin_func/cos_func
tan_func[(tan_func < -3) | (tan_func > 3)] = np.NaN
self.plot_data([sin_func, cos_func, tan_func])
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
gui = Gui()
gui.show()
sys.exit(app.exec_())
Related
I'm trying to plot a large among of signal in a matplotlib figure which is embedded in a Qt environment.
The plots are updated according to QScrollBar which modify the part of signals that I need to show.
My issue is the update of the figure takes a pretty long time, especially because I have 250 signals to update. So, I'm seeking for a way to optimize the EEG_plot.update function to reduce its draw time.
I don't know how I could use an animate function to speed up the process or something else.
My concern is I need to update the time axis ticks and probably also the y axis label positions.
The other thing is if the last segment that I need to plot does not correspond exactly with the window size chosen I need to plot only a part of the window (for instance the last segment will be 5s but the window size is 10s)
I give the entire script right below
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
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(250,105*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):
self.mascene.update()
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_idle()
def main():
app = QApplication(sys.argv)
app.setStyle('Windows')
ex = Viewer(app)
ex.showMaximized()
sys.exit(app.exec())
if __name__ == '__main__':
main()
Update
I made a new implementation where I try to update data instead of reploting all the figure each time (update_set_data function), I don't plot all the point of the curve (for instance if the number of point > 10000 points, I take only 50% of them) I used decimate = len(self.t[ts:te]) // 10000 + 1 to compute the decimation, and last I don't replot the figure when the user is draging the slider.
When I use the old version I get thos time to update the figure:
time old: 4.148899078369141
time old: 4.117990255355835
time old: 4.152893781661987
With the new version I get:
time new: 2.0400094985961914
time new: 2.0248610973358154
time new: 2.0305933952331543
I have to say, I expected more than a 50% time reduction.
Does someone have idea to optimize this more?
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.sliderReleasedfun)
self.horizontalSliders.sliderPressed.connect(self.sliderPressedfun)
self.horizontalSliders.sliderMoved.connect(self.sliderMovedfun)
self.horizontalSliders.sliderReleased.connect(self.sliderReleasedfun)
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(250,105*self.Fs)
self.t = np.arange(self.Sigs_dict.shape[1])/self.Fs
self.parent.processEvents()
self.update()
def sliderPressedfun(self):
self.horizontalSliders.valueChanged.disconnect()
def sliderMovedfun(self,e):
self.horizontalSliders.setValue(e)
def sliderReleasedfun(self):
self.horizontalSliders.valueChanged.connect(self.movesliderfun)
self.movesliderfun()
def movesliderfun(self):
t0 = time.time()
self.horizontalSliders.setEnabled(False)
self.update_data()
self.horizontalSliders.setEnabled(True)
print('time new:', time.time()-t0)
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):
self.mascene.update()
def update_data(self):
self.mascene.update_set_data()
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)
self.win=10
def modify_sigs(self):
self.Sigs_dict = self.parent.Sigs_dict
self.t = self.parent.t
self.Fs= self.parent.Fs
def update_set_data(self):
win_num = self.parent.horizontalSliders.value()
gain = float(self.parent.e_gain.text())
win= float(self.parent.e_win.text())
if not self.spacing == float(self.parent.e_spacing.text()):
self.spacing = float(self.parent.e_spacing.text())
spacing = True
else:
spacing = False
self.linewidth = float(self.parent.e_linewidth.text())
ts = int(self.win * (win_num) * self.Fs)
te = ts + int(self.win * self.Fs)
if te > len(self.t):
diff = te - len(self.t)
ts = ts - diff
te = len(self.t)
decimate = len(self.t[ts:te]) // 10000 + 1
for i in range(self.Sigs_dict.shape[0]):
self.Lines[i].set_data(self.t[ts:te:decimate], gain*(self.Sigs_dict[i,ts:te:decimate]-np.mean(self.Sigs_dict[i,ts:te:decimate]))+i*self.spacing )
self.Lines[i].set_linewidth(self.linewidth)
if spacing:
self.axes.set_ylim((-self.spacing,(self.Sigs_dict.shape[0]+1)*self.spacing))
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.axes.set_xlim((ts/ self.Fs, ts / self.Fs + win ))
# self.canvas.draw_idle()
self.canvas.draw()
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(self.win * (win_num) * self.Fs)
te = ts + int(self.win * self.Fs)
if te > len(self.t):
diff = te - len(self.t)
ts = ts - diff
te = len(self.t)
decimate = len(self.t[ts:te]) // 10000 + 1
self.Lines = []
for i in range(self.Sigs_dict.shape[0]):
line, = plt.plot(self.t[ts:te:decimate], gain*(self.Sigs_dict[i,ts:te:decimate]-np.mean(self.Sigs_dict[i,ts:te:decimate]))+i*self.spacing, linewidth=linewidth )
self.Lines.append(line)
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_idle()
def main():
app = QApplication(sys.argv)
app.setStyle('Windows')
ex = Viewer(app)
ex.showMaximized()
sys.exit(app.exec())
if __name__ == '__main__':
main()
You can try this with multi threading,
so that you can break the whole code in many sub code
and your all code will run in same time with multi threading
I'm trying to write a program that gets serial data from an arduino, via serial, and plots it in real time. I wrote code using matplotlib but I want happy with the results so I am trying to get it to work on pyqtgraph (there are much fewer resources to learn how to use it). my problem is that the code shows an empty graph. it seems _update is being called just once, but when I put it in a loop the graph doesn't even show.
I've written some other code that does what I want, which is plot the data in real time and after the data passes a threshold it plots new lines over the data showing a linear regression. I got an example from here (https://github.com/JaFeKl/joystick_real_time_plot_with_pyqtgraph/blob/master/real_time_plot.py) because I wanted my code to be callable (in a function, but I can't get it to work. so far I'm generating data from within python to simplify debugging
import sys
import pyqtgraph as pg
import pyqtgraph.exporters
from pyqtgraph.Qt import QtGui, QtCore
import numpy as np
import serial
# test
import math
import time
class Graph(QtGui.QMainWindow):
def __init__(self, parent=None):
super(Graph, self).__init__(parent)
self.n = 3
self.mainbox = QtGui.QWidget()
self.setCentralWidget(self.mainbox)
self.mainbox.setLayout(QtGui.QVBoxLayout())
self.canvas = pg.GraphicsLayoutWidget() # create GrpahicsLayoutWidget obejct
self.mainbox.layout().addWidget(self.canvas)
# Set up plot
self.analogPlot = self.canvas.addPlot(title='Signal from serial port')
self.analogPlot.setYRange(-1,1123) # set axis range
self.analogPlot.setXRange(-1,1123)
self.analogPlot.showGrid(x=True, y=True, alpha=0.5) # show Grid
x_axis = self.analogPlot.getAxis('bottom')
y_axis = self.analogPlot.getAxis('left')
font=QtGui.QFont()
font.setPixelSize(20)
x_axis.tickFont = font
y_axis.tickFont = font
x_axis.setLabel(text='Tensão [V]') # set axis labels
y_axis.setLabel(text='Corrente [mA]')
self.plts = []
self.intplts = []
colors = ['r', 'b', 'w', 'y', 'g', 'm', 'c', 'k']
for i in range(self.n):
self.plts.append([])
self.intplts.append([])
for i in range(self.n):
if len(self.plts) <= len(colors):
self.plts[i]=(self.analogPlot.plot(pen= pg.mkPen(colors[i], width=6)))
for i in range(self.n):
if len(self.plts) <= len(colors)*2:
self.intplts.append(self.analogPlot.plot(pen= pg.mkPen(colors[i+3], width=3)))
#Data
self.datay = []
self.datax = []
for i in range(self.n):
self.datax.append([])
self.datay.append([])
# set up image exporter (necessary to be able to export images)
QtGui.QApplication.processEvents()
self.exporter=pg.exporters.ImageExporter(self.canvas.scene())
self.image_counter = 1
# start updating
self.t=0
self._update()
def _update(self):
time.sleep(0.01)
if self.t<= 30:
#line = raw.readline()
#data.append(int(line))
self.datay[0].append(math.sin(self.t+(math.pi/2)))
self.datay[1].append(math.sin(self.t+(5*math.pi/4)))
self.datay[2].append(math.sin(self.t))
self.datax[0].append(self.t)
self.datax[1].append(self.t)
self.datax[2].append(self.t)
self.t+=0.1
self.plts[0].setData(self.datax[0], self.datay[0])
self.plts[1].setData(self.datax[1], self.datay[1])
self.plts[2].setData(self.datax[2], self.datay[2])
app.processEvents()
elif self.t>=30 and self.t<=30.1 :
self.t+=1
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
plot = Graph()
plot.show()
sys.exit(app.exec_())
I expect results similar to this code( only without the linear regression)
import pyqtgraph as pg
import pyqtgraph.exporters
from pyqtgraph.Qt import QtGui, QtCore
import numpy as np
# linear regression
from scipy import stats
#Arduino
#import find_arduino
#import find_buad
import serial
import math
import time
#port = find_arduino.FindArduino()
#baud = find_buad.FindBaudRate()
ard=None
def Con():
global ard
ard = serial.Serial(port,baud,timeout=5)
time.sleep(2) # wait for Arduino
ard.close()
# define the data
theTitle = "pyqtgraph plot"
datay = [[],[],[]]
datax = [[],[],[]]
x2 = []
T=[]
t=0
y1L=[]
x1L=[]
# create plot
### START QtApp #####
app = QtGui.QApplication([]) # you MUST do this once (initialize things)
####################
win = pg.GraphicsWindow(title="Signal from serial port") # creates a window
plt = win.addPlot(title="Realtime plot") # creates empty space for the plot in the window
font=QtGui.QFont()
font.setPixelSize(20)
plt.getAxis("bottom").tickFont = font
plt.getAxis("left").tickFont = font
plt1 = plt.plot(pen=pg.mkPen('r', width=6))
plt2= plt.plot(pen=pg.mkPen('b', width=6))
plt3= plt.plot(pen=pg.mkPen('w', width=6))
plt1I = plt.plot(pen=pg.mkPen('y', width=3))
plt2I = plt.plot(pen=pg.mkPen('g', width=3))
plt3I = plt.plot(pen=pg.mkPen('m', width=3))
plt.showGrid(x=True,y=True)
def update():
global plt1,plt2,plt3, t, plt1I, plt2I, plt3I
if t<= 30:
#line = raw.readline()
#data.append(int(line))
datay[0].append(math.sin(t+(math.pi/2)))
datay[1].append(math.sin(t+(5*math.pi/4)))
datay[2].append(math.sin(t))
datax[0].append(t)
datax[1].append(t)
datax[2].append(t)
t+=0.1
plt1.setData(datax[0],datay[0])
plt2.setData(datax[1],datay[1])
plt3.setData(datax[2],datay[2])
app.processEvents()
time.sleep(0.01)
elif t>=30 and t<=30.1 :
#plt1I.setData([0,1,2],[5,3,1])
#app.processEvents()
interp(plt1I, plt2I, plt3I)
t+=1
else:
app.processEvents()
def interp(pt1, pt2, pt3):
slope, intercept, r_value, p_value, std_err = stats.linregress(datax[0][10:],datay[0][10:])
x=[]
y=[]
print(slope)
for i in datax[0][10:]:
x.append(i)
y.append(intercept+slope*i)
pt1.setData(x,y)
slope, intercept, r_value, p_value, std_err = stats.linregress(datax[1][10:],datay[1][10:])
x=[]
y=[]
print(slope)
for i in datax[0][10:]:
x.append(i)
y.append(intercept+slope*i)
pt2.setData(x, y)
slope, intercept, r_value, p_value, std_err = stats.linregress(datax[2][10:],datay[2][10:])
x=[]
y=[]
print(slope)
for i in datax[0][10:]:
x.append(i)
y.append(intercept+slope*i)
pt3.setData(x,y)
app.processEvents()
timer = QtCore.QTimer()
timer.timeout.connect(update)
timer.start(0)
### MAIN PROGRAM #####
# this is a brutal infinite loop calling your realtime data plot
# make this interpret the incoming data
#Con()
#Communicate(1)
while True: update()
### END QtApp ####
pg.QtGui.QApplication.exec_() # you MUST put this at the end
##################
I don't have an Arduino hooked up to grab data from so for this example I used random data to plot. When plotting data, you want to avoid using time.sleep() since it causes the GUI to freeze. Instead, use a QtGui.QTimer() connected to an update handler to plot data. Also as an optimization, you can use a thread to poll data and then update it in a separate timer.
from pyqtgraph.Qt import QtCore, QtGui
from threading import Thread
import pyqtgraph as pg
import numpy as np
import random
import sys
import time
"""Scrolling Plot Widget Example"""
# Scrolling plot widget with adjustable X-axis and dynamic Y-axis
class ScrollingPlot(QtGui.QWidget):
def __init__(self, parent=None):
super(ScrollingPlot, self).__init__(parent)
# Desired Frequency (Hz) = 1 / self.FREQUENCY
# USE FOR TIME.SLEEP (s)
self.FREQUENCY = .004
# Frequency to update plot (ms)
# USE FOR TIMER.TIMER (ms)
self.TIMER_FREQUENCY = self.FREQUENCY * 1000
# Set X Axis range. If desired is [-10,0] then set LEFT_X = -10 and RIGHT_X = 0
self.LEFT_X = -10
self.RIGHT_X = 0
self.X_Axis = np.arange(self.LEFT_X, self.RIGHT_X, self.FREQUENCY)
self.buffer = int((abs(self.LEFT_X) + abs(self.RIGHT_X))/self.FREQUENCY)
self.data = []
# Create Plot Widget
self.scrolling_plot_widget = pg.PlotWidget()
# Enable/disable plot squeeze (Fixed axis movement)
self.scrolling_plot_widget.plotItem.setMouseEnabled(x=False, y=False)
self.scrolling_plot_widget.setXRange(self.LEFT_X, self.RIGHT_X)
self.scrolling_plot_widget.setTitle('Scrolling Plot Example')
self.scrolling_plot_widget.setLabel('left', 'Value')
self.scrolling_plot_widget.setLabel('bottom', 'Time (s)')
self.scrolling_plot = self.scrolling_plot_widget.plot()
self.scrolling_plot.setPen(197,235,255)
self.layout = QtGui.QGridLayout()
self.layout.addWidget(self.scrolling_plot_widget)
self.read_position_thread()
self.start()
# Update plot
def start(self):
self.position_update_timer = QtCore.QTimer()
self.position_update_timer.timeout.connect(self.plot_updater)
self.position_update_timer.start(self.get_scrolling_plot_timer_frequency())
# Read in data using a thread
def read_position_thread(self):
self.current_position_value = 0
self.old_current_position_value = 0
self.position_update_thread = Thread(target=self.read_position, args=())
self.position_update_thread.daemon = True
self.position_update_thread.start()
def read_position(self):
frequency = self.get_scrolling_plot_frequency()
while True:
try:
# Add data
self.current_position_value = random.randint(1,101)
self.old_current_position_value = self.current_position_value
time.sleep(frequency)
except:
self.current_position_value = self.old_current_position_value
def plot_updater(self):
self.dataPoint = float(self.current_position_value)
if len(self.data) >= self.buffer:
del self.data[:1]
self.data.append(self.dataPoint)
self.scrolling_plot.setData(self.X_Axis[len(self.X_Axis) - len(self.data):], self.data)
def clear_scrolling_plot(self):
self.data[:] = []
def get_scrolling_plot_frequency(self):
return self.FREQUENCY
def get_scrolling_plot_timer_frequency(self):
return self.TIMER_FREQUENCY
def get_scrolling_plot_layout(self):
return self.layout
def get_current_position_value(self):
return self.current_position_value
def get_scrolling_plot_widget(self):
return self.scrolling_plot_widget
if __name__ == '__main__':
# Create main application window
app = QtGui.QApplication([])
app.setStyle(QtGui.QStyleFactory.create("Cleanlooks"))
mw = QtGui.QMainWindow()
mw.setWindowTitle('Scrolling Plot Example')
# Create scrolling plot
scrolling_plot_widget = ScrollingPlot()
# Create and set widget layout
# Main widget container
cw = QtGui.QWidget()
ml = QtGui.QGridLayout()
cw.setLayout(ml)
mw.setCentralWidget(cw)
# Can use either to add plot to main layout
#ml.addWidget(scrolling_plot_widget.get_scrolling_plot_widget(),0,0)
ml.addLayout(scrolling_plot_widget.get_scrolling_plot_layout(),0,0)
mw.show()
# Start Qt event loop unless running in interactive mode or using pyside
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
QtGui.QApplication.instance().exec_()
I am using the pyqtgraph module to make a nice and simple real-time graph. I want to make it as a class/object that can take in a data buffer, and on update, re-read the data buffer to draw the graph. I'm having some trouble getting data buffer values from outside the class code into the object.
Code below:
import pyqtgraph as pg
# pip install pyqtgraph
class App(QtGui.QMainWindow):
def __init__(self, buffer_size, data_buffer, graph_title, parent=None):
super(App, self).__init__(parent)
#### Create Gui Elements ###########
self.mainbox = QtGui.QWidget()
self.setCentralWidget(self.mainbox)
self.mainbox.setLayout(QtGui.QVBoxLayout())
self.canvas = pg.GraphicsLayoutWidget()
self.mainbox.layout().addWidget(self.canvas)
self.label = QtGui.QLabel()
self.mainbox.layout().addWidget(self.label)
self.view = self.canvas.addViewBox()
self.view.setAspectLocked(True)
self.view.setRange(QtCore.QRectF(0,0, 100, 100))
self.numDstreams = 1
self.bufferLength = buffer_size
self.dataBuffer = data_buffer
self.graphTitle = graph_title
self.otherplot = [[self.canvas.addPlot(row=i,col=0, title=self.graphTitle)] # , repeat line for more
for i in range(0,self.numDstreams)]
self.h2 = [[self.otherplot[i][0].plot(pen='r')] for i in range(0,self.numDstreams)] # , self.otherplot[i][1].plot(pen='g'), self.otherplot[i][2].plot(pen='b')
self.ydata = [[np.zeros((1,self.bufferLength))] for i in range(0,self.numDstreams)] # ,np.zeros((1,self.bufferLength)),np.zeros((1,self.bufferLength))
for i in range(0,self.numDstreams):
self.otherplot[i][0].setYRange(min= -100, max= 100)
self.counter = 0
self.fps = 0.
self.lastupdate = time.time()
#### Start #####################
self._update()
def _update(self):
for i in range(0,self.numDstreams):
self.ydata[i][0] = np.array(self.dataBuffer)
self.h2[i][0].setData(self.ydata[i][0])
now = time.time()
dt = (now-self.lastupdate)
if dt <= 0:
dt = 0.000000000001
fps2 = 1.0 / dt
self.lastupdate = now
self.fps = self.fps * 0.9 + fps2 * 0.1
tx = 'Mean Frame Rate: {fps:.3f} FPS'.format(fps=self.fps )
self.label.setText(tx)
QtCore.QTimer.singleShot(1, self._update)
self.counter += 1
def CreateGraph(buffer_size, data_buffer, graph_title):
app1 = QtGui.QApplication(sys.argv)
thisapp1 = App(buffer_size, data_buffer, graph_title)
thisapp1.show()
sys.exit(app1.exec_())
return app1
if __name__ == "__main__":
test_buffer = np.random.randn(100,)
app = CreateGraph(100, test_buffer, "Activity Score")
while 1:
test_buffer = np.random.randn(100,)
app._update()
The code works in that it draws an initial graph of the random data. However, it doesn't update in the loop as I'd want. When I use this object, I want it to update its graph data buffer based on an outside variable, as I'm trying. Instead it's stacking, i.e. it only reads the data for the first time.
Edit
To be clear, I was expecting test_buffer = np.random.randn(100,) app._update() to update the graph continuously in the loop. I need the graph to be able to read a buffer variable in real-time and draw new data.
How can I do this?
In the comments, indicate that your calculations are incorrect so I will eliminate the _update() method from my answer.
Going to the point, the method exec_() creates an event loop that conceptually is a while True so that after that line no other line of code is executed, so your code from while 1: is never executed.
On the other hand if we eliminate it we could not place the while 1: inside the GUI thread since it blocks it and does not let the GUI review the various events or update the GUI for example the painting task.
Also if you use test_buffer = np.random.randn(100,) that does not imply that self.dataBuffer is updated, they are not linked.
The solution is to place the while 1: inside a new thread and send the data by signals to the main thread.
import sys
import threading
import numpy as np
from pyqtgraph.Qt import QtGui, QtCore
import pyqtgraph as pg
class App(QtGui.QMainWindow):
def __init__(self, buffer_size=0, data_buffer=[], graph_title="", parent=None):
super(App, self).__init__(parent)
#### Create Gui Elements ###########
self.mainbox = QtGui.QWidget()
self.setCentralWidget(self.mainbox)
self.mainbox.setLayout(QtGui.QVBoxLayout())
self.canvas = pg.GraphicsLayoutWidget()
self.mainbox.layout().addWidget(self.canvas)
self.label = QtGui.QLabel()
self.mainbox.layout().addWidget(self.label)
self.view = self.canvas.addViewBox()
self.view.setAspectLocked(True)
self.view.setRange(QtCore.QRectF(0,0, 100, 100))
self.numDstreams = 1
self.bufferLength = buffer_size
self.graphTitle = graph_title
self.otherplot = [[self.canvas.addPlot(row=i,col=0, title=self.graphTitle)] # , repeat line for more
for i in range(0,self.numDstreams)]
self.h2 = [[self.otherplot[i][0].plot(pen='r')] for i in range(0,self.numDstreams)] # , self.otherplot[i][1].plot(pen='g'), self.otherplot[i][2].plot(pen='b')
self.ydata = [[np.zeros((1,self.bufferLength))] for i in range(0,self.numDstreams)] # ,np.zeros((1,self.bufferLength)),np.zeros((1,self.bufferLength))
for i in range(0,self.numDstreams):
self.otherplot[i][0].setYRange(min= -100, max= 100)
self.update_plot(data_buffer)
def update_plot(self, data):
self.dataBuffer = data
for i in range(0, self.numDstreams):
self.ydata[i][0] = np.array(self.dataBuffer)
self.h2[i][0].setData(self.ydata[i][0])
def CreateGraph(graph_title):
thisapp1 = App(graph_title=graph_title)
thisapp1.show()
return thisapp1
class Helper(QtCore.QObject):
bufferChanged = QtCore.pyqtSignal(object)
def generate_buffer(helper):
while 1:
test_buffer = np.random.randn(100,)
helper.bufferChanged.emit(test_buffer)
QtCore.QThread.msleep(1)
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
graph = CreateGraph("Activity Score")
helper = Helper()
threading.Thread(target=generate_buffer, args=(helper, ), daemon=True).start()
helper.bufferChanged.connect(graph.update_plot)
if sys.flags.interactive != 1 or not hasattr(QtCore, 'PYQT_VERSION'):
sys.exit(app.exec_())
I'm trying to plot data of diferentes sensors en realtime,so I decided to plot the data using PyQtGraph in PyQt, in order to make it work with several sensor's data from differnte sources.
Searching example on the internet, i found one and i tried to adapt it ,
Because of QtGui.QApplication.instance().exec_(), which carries the inconvenient side effect of blocking the execution of the rest of the code after it. I tried to manage to used threads using Multiproccessing. I could make the rest of the code works, but how coud I update the plot using external data (Plo2D.update2),I tried to used multiprocessing.Queue , but I didn't work, instead appears massage of the window must be closed.
from pyqtgraph.Qt import QtGui, QtCore
import numpy as np
from numpy import arange
import pyqtgraph as pg
import sys
import multiprocessing
class Plot2D():
def __init__(self,):
self.traces = dict()
self.app = QtGui.QApplication([])
self.win = pg.GraphicsWindow(title="Dibujar")
self.win.resize(1000, 600)
self.win.setWindowTitle('Ejemplo')
pg.setConfigOptions(antialias=True)
#self.canvas = self.win.addPlot(title="Pytelemetry")
self.waveform1 = self.win.addPlot(title='WAVEFORM1', row=1, col=1)
self.waveform2 = self.win.addPlot(title='WAVEFORM2', row=2, col=1)
def start(self):
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
QtGui.QApplication.instance().exec_()
def set_plotdata(self, name, datax, datay):
if name in self.traces:
self.traces[name].setData(datax, datay)
else:
if name == '910D':
self.traces[name] = self.waveform1.plot(pen='c', width=3)
if name == 'MPU':
self.traces[name] = self.waveform2.plot(pen='c', width=3)
def update2(self):
# Trying to get external data
ptm1 = globals()['DatExt1']
ptm2 = globals()['DatExt2']
while ptm1.empty() is False:
self.data1 = ptm1.get()
self.set_plotdata('MPU', self.data1[0], self.data1[1])
# csvWriterG910D.writerows(Informa)
# file1.flush()
while ptm2.empty() is False:
self.data2 = ptm2.get()
self.set_plotdata('910D', self.data1[0], self.data1[1])
def animation(self):
timer = QtCore.QTimer()
timer.timeout.connect(self.update2)
timer.start(60)
self.start()
# It is thread started from main.py
def ShowData(Data1, Data2): # Data1,Data2 : multiprocessing.Queue
DatExt1 = Data1
DatExt2 = Data2
p = Plot2D()
p.animation()
the main.py:
if __name__ == '__main__':
Data1 = multiprocessing.Queue()
Data2 = multiprocessing.Queue()
Plottingdata = Process(target=PlotData.ShowData, args=(Data1, Data2, ))
Plottingdata.start()
t = np.arange(-3.0, 2.0, 0.01)
i = 0.0
while True:
s = np.sin(2 * 2 * 3.1416 * t) / (2 * 3.1416 * t + i)
time.sleep(1)
Data1.put([t, s])
i = i + 0.1
thanks ind advanced for help
Instead of using MultiProcessing you should use MultiThreading, that is, create threads that are responsible for collecting data (in your example emulate data) and then send the data to the GUI by signals.
PlotData.py
from pyqtgraph.Qt import QtGui, QtCore
import numpy as np
import pyqtgraph as pg
import sys
class Plot2D(pg.GraphicsWindow):
def __init__(self):
pg.GraphicsWindow.__init__(self, title="Dibujar")
self.traces = dict()
self.resize(1000, 600)
pg.setConfigOptions(antialias=True)
#self.canvas = self.win.addPlot(title="Pytelemetry")
self.waveform1 = self.addPlot(title='WAVEFORM1', row=1, col=1)
self.waveform2 = self.addPlot(title='WAVEFORM2', row=2, col=1)
def set_plotdata(self, name, x, y):
if name in self.traces:
self.traces[name].setData(x, y)
else:
if name == "910D":
self.traces[name] = self.waveform1.plot(x, y, pen='y', width=3)
elif name == "MPU":
self.traces[name] = self.waveform2.plot(x, y, pen='y', width=3)
#QtCore.pyqtSlot(str, tuple)
def updateData(self, name, ptm):
x, y = ptm
self.set_plotdata(name, x, y)
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
plot = Plot2D()
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
QtGui.QApplication.instance().exec_()
main.py
import sys
from pyqtgraph.Qt import QtCore, QtGui
import threading
import numpy as np
import time
from PlotData import Plot2D
class Helper(QtCore.QObject):
changedSignal = QtCore.pyqtSignal(str, tuple)
def create_data1(helper, name):
t = np.arange(-3.0, 2.0, 0.01)
i = 0.0
while True:
s = np.sin(2 * 2 * 3.1416 * t) / (2 * 3.1416 * t + i)
time.sleep(.1)
helper.changedSignal.emit(name, (t, s))
i = i + 0.1
def create_data2(helper, name):
t = np.arange(-3.0, 2.0, 0.01)
i = 0.0
while True:
s = np.cos(2 * 2 * 3.1416 * t) / (2 * 3.1416 * t - i)
time.sleep(.1)
helper.changedSignal.emit(name, (t, s))
i = i + 0.1
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
helper = Helper()
plot = Plot2D()
helper.changedSignal.connect(plot.updateData, QtCore.Qt.QueuedConnection)
threading.Thread(target=create_data1, args=(helper, "910D"), daemon=True).start()
threading.Thread(target=create_data2, args=(helper, "MPU"), daemon=True).start()
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
QtGui.QApplication.instance().exec_()
I am trying to build an application using PyQt. A part of the application runs a thread which takes some time to complete. How can I add a waiting indicator (preferably circular) to indicate running of the process?
I know I can go with a progress bar, or perhaps a splash screen. I have looked into both but I am trying to opt for one of them only as a last resort. Can someone please help me with this?
Thank You
I have converted the code from the QtWaitingSpinner from C ++ to PyQt4/PyQt5
from math import ceil
"""from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *"""
from PyQt4.QtCore import *
from PyQt4.QtGui import *
class QtWaitingSpinner(QWidget):
mColor = QColor(Qt.gray)
mRoundness = 100.0
mMinimumTrailOpacity = 31.4159265358979323846
mTrailFadePercentage = 50.0
mRevolutionsPerSecond = 1.57079632679489661923
mNumberOfLines = 20
mLineLength = 10
mLineWidth = 2
mInnerRadius = 20
mCurrentCounter = 0
mIsSpinning = False
def __init__(self, centerOnParent=True, disableParentWhenSpinning=True, *args, **kwargs):
QWidget.__init__(self, *args, **kwargs)
self.mCenterOnParent = centerOnParent
self.mDisableParentWhenSpinning = disableParentWhenSpinning
self.initialize()
def initialize(self):
self.timer = QTimer(self)
self.timer.timeout.connect(self.rotate)
self.updateSize()
self.updateTimer()
self.hide()
#pyqtSlot()
def rotate(self):
self.mCurrentCounter += 1
if self.mCurrentCounter > self.numberOfLines():
self.mCurrentCounter = 0
self.update()
def updateSize(self):
size = (self.mInnerRadius + self.mLineLength) * 2
self.setFixedSize(size, size)
def updateTimer(self):
self.timer.setInterval(1000 / (self.mNumberOfLines * self.mRevolutionsPerSecond))
def updatePosition(self):
if self.parentWidget() and self.mCenterOnParent:
self.move(self.parentWidget().width() / 2 - self.width() / 2,
self.parentWidget().height() / 2 - self.height() / 2)
def lineCountDistanceFromPrimary(self, current, primary, totalNrOfLines):
distance = primary - current
if distance < 0:
distance += totalNrOfLines
return distance
def currentLineColor(self, countDistance, totalNrOfLines, trailFadePerc, minOpacity, color):
if countDistance == 0:
return color
minAlphaF = minOpacity / 100.0
distanceThreshold = ceil((totalNrOfLines - 1) * trailFadePerc / 100.0)
if countDistance > distanceThreshold:
color.setAlphaF(minAlphaF)
else:
alphaDiff = self.mColor.alphaF() - minAlphaF
gradient = alphaDiff / distanceThreshold + 1.0
resultAlpha = color.alphaF() - gradient * countDistance
resultAlpha = min(1.0, max(0.0, resultAlpha))
color.setAlphaF(resultAlpha)
return color
def paintEvent(self, event):
self.updatePosition()
painter = QPainter(self)
painter.fillRect(self.rect(), Qt.transparent)
painter.setRenderHint(QPainter.Antialiasing, True)
if self.mCurrentCounter > self.mNumberOfLines:
self.mCurrentCounter = 0
painter.setPen(Qt.NoPen)
for i in range(self.mNumberOfLines):
painter.save()
painter.translate(self.mInnerRadius + self.mLineLength,
self.mInnerRadius + self.mLineLength)
rotateAngle = 360.0 * i / self.mNumberOfLines
painter.rotate(rotateAngle)
painter.translate(self.mInnerRadius, 0)
distance = self.lineCountDistanceFromPrimary(i, self.mCurrentCounter,
self.mNumberOfLines)
color = self.currentLineColor(distance, self.mNumberOfLines,
self.mTrailFadePercentage, self.mMinimumTrailOpacity, self.mColor)
painter.setBrush(color)
painter.drawRoundedRect(QRect(0, -self.mLineWidth // 2, self.mLineLength, self.mLineLength),
self.mRoundness, Qt.RelativeSize)
painter.restore()
def start(self):
self.updatePosition()
self.mIsSpinning = True
self.show()
if self.parentWidget() and self.mDisableParentWhenSpinning:
self.parentWidget().setEnabled(False)
if not self.timer.isActive():
self.timer.start()
self.mCurrentCounter = 0
def stop(self):
self.mIsSpinning = False
self.hide()
if self.parentWidget() and self.mDisableParentWhenSpinning:
self.parentWidget().setEnabled(True)
if self.timer.isActive():
self.timer.stop()
self.mCurrentCounter = 0
def setNumberOfLines(self, lines):
self.mNumberOfLines = lines
self.updateTimer()
def setLineLength(self, length):
self.mLineLength = length
self.updateSize()
def setLineWidth(self, width):
self.mLineWidth = width
self.updateSize()
def setInnerRadius(self, radius):
self.mInnerRadius = radius
self.updateSize()
def color(self):
return self.mColor
def roundness(self):
return self.mRoundness
def minimumTrailOpacity(self):
return self.mMinimumTrailOpacity
def trailFadePercentage(self):
return self.mTrailFadePercentage
def revolutionsPersSecond(self):
return self.mRevolutionsPerSecond
def numberOfLines(self):
return self.mNumberOfLines
def lineLength(self):
return self.mLineLength
def lineWidth(self):
return self.mLineWidth
def innerRadius(self):
return self.mInnerRadius
def isSpinning(self):
return self.mIsSpinning
def setRoundness(self, roundness):
self.mRoundness = min(0.0, max(100, roundness))
def setColor(self, color):
self.mColor = color
def setRevolutionsPerSecond(self, revolutionsPerSecond):
self.mRevolutionsPerSecond = revolutionsPerSecond
self.updateTimer()
def setTrailFadePercentage(self, trail):
self.mTrailFadePercentage = trail
def setMinimumTrailOpacity(self, minimumTrailOpacity):
self.mMinimumTrailOpacity = minimumTrailOpacity
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
dial = QDialog()
w = QtWaitingSpinner(dial)
dial.show()
w.start()
QTimer.singleShot(1000, w.stop)
sys.exit(app.exec_())
In the following link you can find a complete example
In addition to the above response...
If you are using Qt Designer you'll need to create a QWidget and promote it to a QtWaitingSpinner. You can follow this for promoting QWidgets. In this case, your Promoted Class name is QtWaitingSpinner and Header file is the file where the QtWaitingSpinner class is located.
Once your GUI is set you can call
your_QtWaitingSpinnerObject.start()