Add real time channel display to PyQt - python

-----EDIT # 1 -----
In the code box is my attempt at making this work, but I am unable to get my labels and text boxes to show in window... perhaps my Qgridlayout is wrong??? any help or direction would be great! thanks!
----END EDIT #1 ----
What I would like is to display the channels read from channel 1 through 8 below the matplotlib graph. The graph was easy (presuming what I did works) to embed and it refreshes every 5 seconds displaying the last 5 minutes of data. So, what I would like to have two rows to display all eight channels... something like below:
Channel 1: (RAW VALUE) Channel2: (RAW VALUE) .....
Channel 5: (RAW VALUE) Channel6: (RAW VALUE) .....
I am unsure how to have PyQt 'refresh' or 'fetch' the new values every 5 seconds.
Below is my code for what it does now,
#matplotlib and read/write aquisition
import Queue
import datetime as DT
import collections
import matplotlib.pyplot as plt
import numpy as np
import multiprocessing as mp
import time
import datetime
import os
import matplotlib.dates as mdates
import matplotlib.animation as animation
#ADC
from ABE_DeltaSigmaPi import DeltaSigma
from ABE_helpers import ABEHelpers
#PyQt
from PyQt4 import QtGui, QtCore
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
#ADC INFO
import sys
i2c_helper = ABEHelpers()
bus = i2c_helper.get_smbus()
adc = DeltaSigma(bus, 0x68, 0x69, 18)
#Rename file to date
base_dir = '/home/pi/Desktop/DATA'
ts = time.time()
filename_time = datetime.datetime.fromtimestamp(ts).strftime('%Y-%m-%d')
filename_base = os.path.join(base_dir, filename_time)
filename = '%s.txt' % filename_base
# you will want to change read_delay to 5000
read_delay = int(5000) # in milliseconds
write_delay = read_delay/1000.0 # in seconds
window_size = 60
nlines = 8
datenums = collections.deque(maxlen=window_size)
ys = [collections.deque(maxlen=window_size) for i in range(nlines)]
#PyQt window to display readings
class Window(QtGui.QDialog):
def __init__(self, parent=None):
super(Window, self).__init__(parent)
self.figure = plt.show()
self.canvas = FigureCanvas(self.figure)
#Labels
self.channel1 = QtGui.QLabel('Channel 1:')
self.channel2 = QtGui.QLabel('Channel 2:')
self.channel3 = QtGui.QLabel('Channel 3:')
self.channel4 = QtGui.QLabel('Channel 4:')
self.channel5 = QtGui.QLabel('Channel 5:')
self.channel6 = QtGui.QLabel('Channel 6:')
self.channel7 = QtGui.QLabel('Channel 7:')
self.channel8 = QtGui.QLabel('Channel 8:')
#textboxes
self.textbox1 = QtGui.QLineEdit()
self.textbox2 = QtGui.QlineEdit()
self.textbox3 = QtGui.QlineEdit()
self.textbox4 = QtGui.QlineEdit()
self.textbox5 = QtGui.QlineEdit()
self.textbox6 = QtGui.QlineEdit()
self.textbox7 = QtGui.QlineEdit()
self.textbox8 = QtGui.QlineEdit()
#timer to refresh textboxes
def refreshtext(self):
self.textbox1.setText(enumerate(row[1]))
self.textbox2.setText(enumerate(row[2]))
self.textbox3.setText(enumerate(row[3]))
self.textbox4.setText(enumerate(row[4]))
self.textbox5.setText(enumerate(row[5]))
self.textbox6.setText(enumerate(row[6]))
self.textbox7.setText(enumerate(row[7]))
self.textbox8.setText(enumerate(row[8]))
#Layout
layout = QtGui.QGridLayout()
layout.setAlignment(QtCore.Qt.AlignCenter)
layout.addWidget(self.canvas,0,0,1,4)
layout.addWidget(self.channel1,1,0,1,1)
layout.addWidget(self.channel2,1,1,1,1)
layout.addWidget(self.channel3,1,2,1,1)
layout.addWidget(self.channel4,1,3,1,1)
layout.addWidget(self.textbox1,2,0,1,1)
layout.addWidget(self.textbox2,2,1,1,1)
layout.addWidget(self.textbox3,2,2,1,1)
layout.addWidget(self.textbox4,2,3,1,1)
layout.addWidget(self.channel5,3,0,1,1)
layout.addWidget(self.channel6,3,1,1,1)
layout.addWidget(self.channel7,3,2,1,1)
layout.addWidget(self.channel8,3,3,1,1)
layout.addWidget(self.textbox5,4,0,1,1)
layout.addWidget(self.textbox6,4,1,1,1)
layout.addWidget(self.textbox7,4,2,1,1)
layout.addWidget(self.textbox8,4,3,1,1)
self.setLayout(layout)
def animate(i, queue):
try:
row = queue.get_nowait()
except Queue.Empty:
return
datenums.append(mdates.date2num(row[0]))
for i, y in enumerate(row[1:]):
ys[i].append(y)
for i, y in enumerate(ys):
lines[i].set_data(datenums, y)
ymin = min(min(y) for y in ys)
ymax = max(max(y) for y in ys)
xmin = min(datenums)
xmax = max(datenums)
if xmin < xmax:
ax.set_xlim(xmin, xmax)
ax.set_ylim(ymin, ymax)
fig.canvas.draw()
def write_data(filename, queue):
while True:
delay1 = DT.datetime.now()
row = []
for i in range(nlines):
# read from adc channels and print to screen
channel = adc.read_voltage(i)
row.append(channel)
queue.put([delay1]+row)
#print voltage variables to local file
with open(filename, 'a') as DAQrecording:
time1 = delay1.strftime('%Y-%m-%d')
time2 = delay1.strftime('%H:%M:%S')
row = [time1, time2] + row
row = map(str, row)
DAQrecording.write('{}\n'.format(', '.join(row)))
#Delay until next 5 second interval
delay2 = DT.datetime.now()
difference = (delay2 - delay1).total_seconds()
time.sleep(write_delay - difference)
def main():
global fig, ax, lines
queue = mp.Queue()
proc = mp.Process(target=write_data, args=(filename, queue))
# terminate proc when main process ends
proc.daemon = True
# spawn the writer in a separate process
proc.start()
fig, ax = plt.subplots()
xfmt = mdates.DateFormatter('%H:%M:%S')
ax.xaxis.set_major_formatter(xfmt)
# make matplotlib treat x-axis as times
ax.xaxis_date()
fig.autofmt_xdate(rotation=25)
lines = []
for i in range(nlines):
line, = ax.plot([], [])
lines.append(line)
ani = animation.FuncAnimation(fig, animate, interval=read_delay, fargs=(queue,))
app = QtGui.QApplication(sys.argv)
win = Window()
win.setWindowTitle('Real Time Data Aquisition')
win.show()
timer = QtCore.QTimer()
timer.timeout.connect(self.refreshtext)
timer.start(5000)
sys.exit(app.exec_())
if __name__ == '__main__':
main()

Related

Animating a real time breathing curve in python

I want to create an animation that people can use to align their breathing with. I have made a class with PyQt5 that does exactly this, and has the breathing period as parameter. (See code below).
It works well, apart from the timing. When setting a specific delta_t and window size during the FuncAnimation I can get accurate timings. But when I change the window size, it either speeds up or slows down...
Im probably going to model this in another language, but I am still curious if I can get this right in Python. Can anyone here point me in the right direction?
import os
import time
import sys
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.animation as animation
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
import PyQt5.QtGui
import PyQt5.QtCore
import PyQt5.QtWidgets
class BreathingAnimation(PyQt5.QtWidgets.QMainWindow):
# Could inherit from GenericInterface
# Animate three ways:
# o Fixed sinusoid -> moving ball
# o Fixed ball (up/down) -> moving wave
# o Expanding ball
def __init__(self, period=1, delta_t=0.01, animation_type='ball'):
super().__init__()
self._main = PyQt5.QtWidgets.QWidget()
self.setCentralWidget(self._main)
"""
Plot variables
sin(omega * x)
period = 2 * np.pi * omega
omega = period / (2 * np.pi)
"""
self.delta_t = delta_t
self.frequentie = 1/period
self.max_plot_time = 5
self.max_periods = 20
self.t_range = np.arange(0, self.max_periods * period, delta_t)
self.animation_type = animation_type
self.cool_down = 10
self.prev_time = time.time()
self.prev_time_cor_time = time.time()
"""
Graphical definitions
"""
self.canvas, self.fig, self.axes = self.get_figure(self.max_plot_time)
self.axes.set_axis_off()
self.line_obj, self.scatter_obj = self.get_plot_objects(self.axes)
# Get the animation object
self.anim_obj = self.get_animation()
h_layout = PyQt5.QtWidgets.QHBoxLayout(self._main)
# Create buttons
# self.button_box_layout = self.get_button_box_layout()
# write_button = self.get_push_button(name='Write', shortcut='W', connect=self.write_animation)
# self.edit_button = self.get_line_edit(name=f'{period}', connect=self.update_period, max_length=4)
# self.button_box_layout.addWidget(self.edit_button)
# self.button_box_layout.addWidget(write_button)
# Add canvas to the figure
temp_canvas_layout = PyQt5.QtWidgets.QVBoxLayout()
temp_canvas_layout.addWidget(self.canvas)
h_layout.addLayout(temp_canvas_layout, stretch=1)
# h_layout.addLayout(self.button_box_layout, stretch=0.01)
#staticmethod
def get_figure(max_plot_time, debug=False):
if debug:
fig = plt.figure()
else:
fig = Figure(figsize=(5, 5), dpi=100)
canvas = FigureCanvas(fig)
axes = canvas.figure.subplots()
# self.axes.set_axis_off()
axes.set_ylim(-2, 2)
axes.set_xlim(0, max_plot_time)
return canvas, fig, axes
#staticmethod
def get_plot_objects(axes):
# Create a line object
line_obj = axes.plot([], [], zorder=1)[0]
# Create a scatter object
scatter_obj = axes.scatter([], [], s=40, marker='o', c='r')
return line_obj, scatter_obj
def get_y_value(self, i_t):
omega = 2 * np.pi * self.frequentie
y_value = np.sin(omega * i_t)
return y_value
def animate_moving_ball(self, i, line_obj=None, scatter_obj=None):
i = i % len(self.t_range)
if line_obj is None:
line_obj = self.line_obj
if scatter_obj is None:
scatter_obj = self.scatter_obj
line_obj.set_data(self.t_range, self.get_y_value(self.t_range))
sel_time = self.t_range[i]
scatter_obj.set_offsets(np.c_[sel_time, self.get_y_value(sel_time)])
return scatter_obj
def animate_moving_wave(self, i, line_obj=None, scatter_obj=None):
i = i % len(self.t_range)
if line_obj is None:
line_obj = self.line_obj
if scatter_obj is None:
scatter_obj = self.scatter_obj
line_obj.set_data(self.t_range, np.roll(self.get_y_value(self.t_range), -i))
sel_time = self.t_range[i] + self.max_plot_time/2.
# print(f'max plot time {i}', self.max_plot_time/2, self.get_y_value(sel_time))
scatter_obj.set_offsets(np.c_[self.max_plot_time/2., self.get_y_value(sel_time)])
# self.cool_down -= 1
# # print(self.cool_down)
if ((1 - self.get_y_value(sel_time)) < 0.0001):
time_difference = time.time() - self.prev_time
self.prev_time = time.time()
print('Time interval in seconds ', time_difference)
return scatter_obj
def update_period(self):
new_periode = float(self.edit_button.text())
self.frequentie = 1./new_periode
self.t_range = np.arange(0, self.max_periods * new_periode, self.delta_t)
def get_animation(self):
if self.animation_type == 'ball':
# Return a moving ball..
animation_fun = self.animate_moving_ball
elif self.animation_type == 'wave':
# Return a wave..
animation_fun = self.animate_moving_wave
else:
animation_fun = None
self.animation_obj = animation.FuncAnimation(self.canvas.figure, animation_fun,
blit=False, repeat=True,
interval=self.delta_t, # Delay in ms
frames=len(self.t_range))
self.animation_obj.new_frame_seq()
return self.animation_obj
def write_animation(self):
num_frames = len(self.t_range)
max_time = np.max(self.t_range) # in seconds?
print('frames ', num_frames / max_time)
ffmpeg_writer = animation.FFMpegWriter(fps=num_frames / max_time)
self.animation_obj.save(os.path.expanduser('~/breathing_animation.mp4'), writer=ffmpeg_writer)
print('Written')
if __name__ == "__main__":
qapp = PyQt5.QtWidgets.QApplication(sys.argv)
app = BreathingAnimation(period=3, animation_type='wave', delta_t=0.009)
app.show()
qapp.exec_()

How to draw a large amont of signal faster in matplotlib embedded within Qt environment?

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

Animate pyqtgraph in class

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

Frames don't load in Tk. (Tk-Gui-Python)

I'm new to Python programming and I am doing this project for learning. My problem is, when I start the app, MainScreen is shown but after that other frames are not loading. These functions work seperately. Animate and texting functions work when executed on their own. I'm trying to put them together in a single app.
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import time
from time import sleep
import serial
from Tkinter import *
FO = open("SensorValues.txt", "w+")
ser = serial.Serial()
ser.baudrate = 9600
ser.port = '/dev/ttyUSB0'
ser.open()
data = ser.readline(5)
FO.write(data + '\n')
FO.flush()
def organizer(frame):
frame.tkraise()
root = Tk()
MainScreen = Frame(root)
LiveGraph = Frame(root)
LiveLabel = Frame(root)
for frame in (MainScreen,LiveGraph,LiveLabel):
frame.grid(row=0,column=0,sticky='news')
Label(MainScreen,text = 'Choose what do you want').pack()
def animate(i):
fig = plt.figure()
ax1 = fig.add_subplot(2,1,1)
ax2 = fig.add_subplot(2,1,2)
sleep(1)
dataArray = FO.split('\n')
xar = []
yar = []
for eachLine in dataArray:
if len(eachLine)>1:
x,y = eachLine.split(',')
xar.append(int(x))
yar.append(int(y))
ax1.clear()
ax2.clear()
ax1.plot(xar)
ax2.plot(yar)
ani = animation.FuncAnimation(fig, animate, interval=1000)
plt.show()
def texting():
LiveLabel.config(text=data)
root.after(1, texting)
organizer(MainScreen)
Button(MainScreen, text = 'Press for starting live graph!', width = 30, command = lambda:organizer(LiveGraph)).pack()
Button(MainScreen, text = 'Press for starting live label!', width = 30, command = lambda:organizer(LiveLabel)).pack()
Label(LiveGraph,text = '1st Table Temperature - 2nd Table Humidity').pack()
Button(LiveGraph,text = 'Stop',width = 10, command = root.destroy ).pack()
Button(LiveLabel,text = 'Stop',width = 10, command = root.destroy ).pack()
plt.show()
FO.close()
ser.close()
root.mainloop()
Screenshots :

Matplotlib axes formatting

I want to add the current date and time to the x-axes of a real time plot. I have tried almost everything beginning with custom ticks to custom axes, but I cannot seem to add it. How should I do this?
import sys
import pylab
from pylab import *
from PyQt4 import QtGui,QtCore
from matplotlib.figure import Figure
from matplotlib.backends.backend_qt4agg \
import FigureCanvasQTAgg as FigureCanvas
import MySQLdb as mdb
class CPUMonitor(FigureCanvas):
def __init__(self):
self.date = []
conn = mdb.connect("serv","user","pass","db")
self.cur = conn.cursor()
self.before = self.prepare_cpu_usage()
self.fig = Figure()
self.ax = self.fig.add_subplot(111)
FigureCanvas.__init__(self, self.fig)
self.ax.set_title("Pong | CPU Utilization")
self.ax.set_xlabel("Datetime")
self.ax.set_ylabel("CPU %")
self.ax.set_autoscale_on(False)
self.user =[]
self.l_user, = self.ax.plot([],self.user, label='Total %')
self.ax.legend()
self.fig.canvas.draw()
self.cnt = 0
self.timerEvent(None)
self.timer1 = QtCore.QTimer()
self.timer1.timeout.connect(self.get_database_data)
self.timer1.start(5)
self.timer = self.startTimer(5000)
def get_database_data(self):
self.cur.execute("SELECT cpu,date FROM status WHERE date = (SELECT MAX(date) FROM status);")
self.data_db = self.cur.fetchone()
return self.data_db
def prepare_cpu_usage(self):
t = self.get_database_data()
return [t[0],t[1]]
def get_cpu_usage(self):
now = self.prepare_cpu_usage()
self.before = now
print self.before
return self.before
def datetime(self):
self.dates = self.get_cpu_usage()
self.dates = self.dates[1]
self.date.append(self.dates)
return str(self.dates)
def timerEvent(self, evt):
result = self.get_cpu_usage()
self.user.append(result[0])
self.l_user.set_data(range(len(self.user)), self.user)
self.fig.canvas.draw()
CurrentXAxis=pylab.arange(len(self.user)-1000,len(self.user),1)
self.ax.axis([CurrentXAxis.min(),CurrentXAxis.max(),0,100])
self.cnt += 1
app = QtGui.QApplication(sys.argv)
widget = CPUMonitor()
widget.setWindowTitle("Pong: CPU Usage")
widget.show()
sys.exit(app.exec_())
You can check a simple example first to get an idea of how to make date markers work: http://matplotlib.sourceforge.net/examples/pylab_examples/finance_demo.html
First you will need to import the date plotting classes, for example:
from matplotlib.dates import DateFormatter, WeekdayLocator, MONDAY
The documentation is available at http://matplotlib.sourceforge.net/api/dates_api.html
Then in the definition of the figure, set the locator (for tick marks), and formatter (for tick labels). The code below sets tick marks on every monday:
self.ax.xaxis.set_major_locator(WeekdayLocator(MONDAY))
self.ax.xaxis.set_major_formatter(DateFormatter('%b %d'))
self.ax.xaxis_date()
You should use datetime.datetime now for X-axis values and ranges instead of integers. I expect MySQLdb to return datetime.datetime objects, otherwise you will have to convert the timestamps.
The date formatter will complain if you try to plot an empty graph. Don't forget to set reasonable initial limits.
Here is an example of your code, where I stripped the database code (and some more) and replaced it with generated values:
import sys
from pylab import *
from PyQt4 import QtGui
from matplotlib.figure import Figure
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.dates import DateFormatter, WeekdayLocator, MONDAY
import datetime
import random
class CPUMonitor(FigureCanvas):
def __init__(self):
# Dummy variable to simulate time.
self.delay = 0
self.fig = Figure()
self.ax = self.fig.add_subplot(111)
FigureCanvas.__init__(self, self.fig)
self.ax.set_title("Pong | CPU Utilization")
self.ax.set_xlabel("Datetime")
self.ax.set_ylabel("CPU %")
self.ax.xaxis.set_major_locator(WeekdayLocator(MONDAY))
self.ax.xaxis.set_major_formatter(DateFormatter('%b %d'))
self.ax.xaxis_date()
# Set resonable initial limits.
td = datetime.timedelta(1)
self.ax.set_xlim(datetime.datetime.now(), datetime.datetime.now() + td)
self.dates = []
self.user =[]
self.l_user, = self.ax.plot([],self.user, label='Total %')
self.ax.legend()
self.timer = self.startTimer(5000)
def get_database_data(self):
self.delay += 1
td = datetime.timedelta(0, self.delay * 5)
return [random.random(), datetime.datetime.now() + td]
def prepare_cpu_usage(self):
t = self.get_database_data()
return [t[0],t[1]]
def get_cpu_usage(self):
return self.prepare_cpu_usage()
def timerEvent(self, evt):
result = self.get_cpu_usage()
self.user.append(result[0])
self.dates.append(result[1])
self.l_user.set_data(self.dates, self.user)
if len(self.dates) >= 2:
self.ax.set_xlim(self.dates[0], self.dates[-1])
self.draw()
app = QtGui.QApplication(sys.argv)
widget = CPUMonitor()
widget.setWindowTitle("Pong: CPU Usage")
widget.show()
sys.exit(app.exec_())

Categories

Resources