I have devices connected to my serial port and I need to poll them and then display that data in a plot. I currently have this working (slowly) using matplotlib. I could have up to 64 devices connected and each device could have 20 pieces of data to update. I've set it up so that a new window can be created and a piece of data can be added to be plotted. With each additional plotting window that is opened my update rate slows considerably.
I've tried using blit animation in matplotlib, but it's not real smooth and I can see anomolies in the update. I've tried PyQtGraph, but can't find any documentation on how to use this package, and now I'm trying PyQwt, but can't get it installed (mostly because my company won't let us install a package that will handle a .gz file).
Any ideas or suggestions would be greatly appreciated.
import sys
from PyQt4.QtCore import (Qt, QModelIndex, QObject, SIGNAL, SLOT, QTimer, QThread, QSize, QString, QVariant)
from PyQt4 import QtGui
from matplotlib.figure import Figure
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from plot_toolbar import NavigationToolbar2QT as NavigationToolbar
import matplotlib.dates as md
import psutil as p
import time
import datetime as dt
import string
import ui_plotting
import pickle
try:
_fromUtf8 = QString.fromUtf8
except AttributeError:
_fromUtf8 = lambda s: s
class Monitor(FigureCanvas):
"""Plot widget to display real time graphs"""
def __init__(self, timenum):
self.timenum=timenum
self.main_frame = QtGui.QWidget()
self.timeTemp1 = 0
self.timeTemp2 = 0
self.temp = 1
self.placeHolder = []
self.y_max = 0
self.y_min = 100
# initialization of the canvas
# self.dpi = 100
# self.fig = Figure((5.0, 4.0), dpi=self.dpi)
self.fig = Figure()
FigureCanvas.__init__(self, self.fig)
# self.canvas = FigureCanvas(self.fig)
# self.canvas.setParent(self.main_frame)
# first image setup
# self.fig = Figure()
# self.fig.subplots_adjust(bottom=0.5)
self.ax = self.fig.add_subplot(111)
self.mpl_toolbar = NavigationToolbar(self.fig.canvas, self.main_frame,False)
self.mpl_toolbar.setFixedHeight(24)
# set specific limits for X and Y axes
# now=dt.datetime.fromtimestamp(time.mktime(time.localtime()))
# self.timenum = now.strftime("%H:%M:%S.%f")
self.timeSec = 0
self.x_lim = 100
self.ax.set_xlim(0, self.x_lim)
self.ax.set_ylim(0, 100)
self.ax.get_xaxis().grid(True)
self.ax.get_yaxis().grid(True)
# and disable figure-wide autoscale
self.ax.set_autoscale_on(False)
self.ax.set_xlabel('Time in Seconds')
# generates first "empty" plots
self.timeb = []
self.user = []
self.l_user = []
self.l_user = [[] for x in xrange(50)]
for i in range(50):
self.l_user[i], = self.ax.plot(0,0)
# add legend to plot
# self.ax.legend()
def addTime(self,t1,t2):
timeStamp = t1+"000"
# print "timeStamp",timeStamp
timeStamp2 = t2+"000"
test = string.split(timeStamp,":")
test2 = string.split(test[2],".")
testa = string.split(timeStamp2,":")
testa2 = string.split(testa[2],".")
sub1 = int(testa[0])-int(test[0])
sub2 = int(testa[1])-int(test[1])
sub3 = int(testa2[0])-int(test2[0])
sub4 = int(testa2[1])-int(test2[1])
testing = dt.timedelta(hours=sub1,minutes=sub2,seconds=sub3,microseconds=sub4)
self.timeSec = testing.total_seconds()
def timerEvent(self, evt, timeStamp, val, lines):
temp_min = 0
temp_max = 0
# Add user arrays for each user_l array used, don't reuse user arrays
if self.y_max<max(map(float, val)):
self.y_max = max(map(float, val))
if self.y_min>min(map(float, val)):
self.y_min = min(map(float, val))
# print "val: ",val
if lines[len(lines)-1]+1 > len(self.user):
for k in range((lines[len(lines)-1]+1)-len(self.user)):
self.user.append([])
# append new data to the datasets
# print "timenum=",self.timenum
self.addTime(self.timenum, timeStamp)
self.timeb.append(self.timeSec)
for j in range((lines[len(lines)-1]+1)):
if j >49:
break
if j not in lines:
del self.user[j][:]
self.user[j].extend(self.placeHolder)
self.user[j].append(0)
else:
if len(self.timeb) > (len(self.user[j])+1):
self.user[j].extend(self.placeHolder)
self.user[j].append(str(val[lines.index(j)]))
for i in range(len(lines)):
if i>49:
break
self.l_user[lines[i]].set_data(self.timeb, self.user[lines[i]])
# force a redraw of the Figure
# if self.y_max < 2:
# self.y_max = 2
# if self.y_min < 2:
# self.y_min = 0
if self.y_min > -.1 and self.y_max < .1:
temp_min = -1
temp_max = 1
else:
temp_min = self.y_min-(self.y_min/10)
temp_max = self.y_max+(self.y_max/10)
self.ax.set_ylim(temp_min, temp_max)
if self.timeSec >= self.x_lim:
if str(self.x_lim)[0]=='2':
self.x_lim = self.x_lim * 2.5
else:
self.x_lim = self.x_lim * 2
self.ax.set_xlim(0, self.x_lim)
# self.fig.canvas.restore_region(self.fig.canvas)
# self.ax.draw_artist(self.l_user[lines[0]])
# self.fig.canvas.blit(self.ax.bbox)
self.fig.canvas.draw()
# self.draw()
self.placeHolder.append(None)
class List(QtGui.QListWidget):
def __init__(self, parent):
super(List, self).__init__(parent)
font = QtGui.QFont()
font.setFamily(_fromUtf8("Century Gothic"))
font.setPointSize(7)
self.setFont(font)
self.setDragDropMode(4)
self.setAcceptDrops(True)
self.row = []
self.col = []
self.disName = []
self.lines = []
self.counter = 0
self.setStyleSheet("background-color:#DDDDDD")
self.colors = ["blue", "green", "red", "deeppink", "black", "slategray", "sienna", "goldenrod", "teal", "orange", "orchid", "lightskyblue", "navy", "darkgreen", "indigo", "firebrick", "deepskyblue", "lightskyblue", "darkseagreen", "gold"]
def dragEnterEvent(self, e):
if e.mimeData().hasFormat("application/x-qabstractitemmodeldatalist"):
# print "currentRow : ", self.currentRow()
# print "self.col: ", self.col
# print "self.row: ", self.row
# print "self.col[]: ", self.col.pop(self.currentRow())
# print "self.row[]: ", self.row.pop(self.currentRow())
self.col.pop(self.currentRow())
self.row.pop(self.currentRow())
self.disName.pop(self.currentRow())
self.lines.pop(self.currentRow())
self.takeItem(self.currentRow())
if e.mimeData().hasFormat("application/pubmedrecord"):
e.accept()
else:
e.ignore()
def dropEvent(self, e):
items = 0
data = e.mimeData()
bstream = data.retrieveData("application/pubmedrecord", QVariant.ByteArray)
selected = pickle.loads(bstream.toByteArray())
e.accept()
# print selected
# if self.count() != 0:
# j = (self.lines[self.count()-1]%len(self.colors))+1
# else:
# j=0
while items < len(selected):
j=self.counter
if j >= len(self.colors)-1:
j = self.counter%len(self.colors)
m = len(self.lines)
self.lines.append(self.counter)
# if m != 0:
# n = self.lines[m-1]
# self.lines.append(n+1)
# else:
# self.lines.append(0)
self.col.append(str(selected[items]))
items = items+1
self.row.append(str(selected[items]))
items = items+1
self.disName.append(str(selected[items]))
listItem = QtGui.QListWidgetItem()
listItem.setText(str(selected[items]))
listItem.setTextColor(QtGui.QColor(self.colors[j]))
self.addItem(listItem)
items = items+1
self.counter += 1
def dragLeaveEvent(self, event):
event.accept()
class PlotDlg(QtGui.QDialog):
NextID = 0
filename = 'Plot'
def __init__(self,time, callback, parent=None):
super(PlotDlg, self).__init__(parent)
self.id = PlotDlg.NextID
PlotDlg.NextID += 1
self.callback = callback
self.setWindowFlags(Qt.Window | Qt.WindowMinimizeButtonHint | Qt.WindowMaximizeButtonHint)
self.setAttribute(Qt.WA_DeleteOnClose,True)
self.value = []
print "time=",time
self.time = time
self.dc = Monitor(self.time)
# self.threadPool = []
self.listWidget = List(self)
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.MinimumExpanding)
sizePolicy.setHorizontalStretch(0)
self.listWidget.setSizePolicy(sizePolicy)
self.listWidget.setMaximumSize(QSize(150, 16777215))
grid = QtGui.QGridLayout()
grid.setSpacing(0)
grid.setContentsMargins(0, 0, 0, 0)
grid.addWidget(self.dc.mpl_toolbar,0,0,1,12)
grid.addWidget(self.listWidget,1,1)
grid.addWidget(self.dc,1,0)
grid.setColumnMinimumWidth(1,110)
self.setLayout(grid)
def update(self, clear=0):
if clear == 1:
now=dt.datetime.fromtimestamp(time.mktime(time.localtime()))
self.dc.timenum = now.strftime("%H:%M:%S.%f")
self.dc.timeSec = 0
self.dc.x_lim = 100
self.dc.y_max = 0
self.dc.y_min = 100
del self.dc.timeb[:]
del self.dc.user[:]
del self.dc.placeHolder[:]
# del self.dc.l_user[:]
# self.dc.l_user = [[] for x in xrange(50)]
# for i in range(50):
# self.dc.l_user[i], = self.dc.ax.plot(0,0)
for i in range(50):
self.dc.l_user[i].set_data(0, 0)
# print self.dc.l_user
# print self.dc.user
self.dc.ax.set_xlim(0, self.dc.x_lim)
self.dc.fig.canvas.draw()
# print self.value
# print str(self.time)
# print "time:",str(self.time)
# self.threadPool.append( GenericThread(self.dc.timerEvent,None, str(self.time), self.value, self.listWidget.lines) )
# self.threadPool[len(self.threadPool)-1].start()
self.dc.timerEvent(None, str(self.time), self.value, self.listWidget.lines)
def closeEvent(self, event):
# self.update(1)
self.callback(self.id)
PlotDlg.NextID -= 1
class GenericThread(QThread):
def __init__(self, function, *args, **kwargs):
QThread.__init__(self)
self.function = function
self.args = args
self.kwargs = kwargs
def __del__(self):
self.wait()
def run(self):
self.function(*self.args,**self.kwargs)
return
See Question&Answers more detail:
os