Two axis plots w.r.t time in pyqtgraph - plot

Can some one help me on how do I create a two axis plots w.r.t time using pyqtgraph. For example plot velocity versus torque against time i.e. time is x axis and is moving and velocity is plotted against torque as a function of time.
from pyqtgraph.Qt import QtGui, QtCore
import numpy as np
import pyqtgraph as pg
from pyqtgraph.ptime import time
from numpy import *
from socket import *
import time
app = QtGui.QApplication([])
x = [0,1,2,3,4,5,6,7,8,9];
y = [0,2,4,6,8,10,12,16,18,20];
pg.mkQApp()
pw = pg.PlotWidget()
pw.show()
for i in range(1,20):
p1 = pw.plotItem
p2 = pg.ViewBox()
p1.showAxis('right')
p1.scene().addItem(p2)
p2.setGeometry(p1.vb.sceneBoundingRect())
p1.getAxis('right').linkToView(p2)
p2.setXLink(p1)
x.append(i)
y.append(i*2)
p1.plot(x)
#time.sleep(1)
p2.addItem(p1.plot(y, pen='b'))
#time.sleep(1)

Based on the discussion in this forum for this question, the below code snippet is what we were looking for and now is satifying our requirement. This is just a sample code which will be eventually modified and integrated to the intended application. Once again I appreciate the discussion in this forum which helped us in arriving at the right solution.
from pyqtgraph.Qt import QtGui, QtCore
import numpy as np
import pyqtgraph as pg
from pyqtgraph.ptime import time
from numpy import *
from socket import *
import time
app = QtGui.QApplication([])
plot_x = [0,1,2,3,4,5,6,7,8,9];
plot_y = [0,2,4,6,8,10,12,14,16,18];
loopcount = 0;
pg.mkQApp()
pw = pg.PlotWidget()
pw.show()
p1 = pw.plotItem
p2 = pg.ViewBox()
p1.showAxis('right')
p1.scene().addItem(p2)
p2.setGeometry(p1.vb.sceneBoundingRect())
p1.getAxis('right').linkToView(p2)
p2.setXLink(p1)
def update():
global pw, pg, loopcount, plot_x, plot_y, p1, p2
p1.setXRange(loopcount*10, loopcount*10+100)
p2.setXRange(loopcount*10, loopcount*10+100)
p1.plot(plot_x)
p2.addItem(p1.plot(plot_y, pen='b'))
loopcount = loopcount + 1
for update in range(loopcount*10, loopcount*10+100):
plot_x.append(update*loopcount)
plot_y.append(update*loopcount*2)
timer = QtCore.QTimer()
timer.timeout.connect(update)
timer.start(50)
Improved code based on Luke's comment
from pyqtgraph.Qt import QtGui, QtCore
import numpy as np
import pyqtgraph as pg
from pyqtgraph.ptime import time
from numpy import *
from socket import *
import time
app = QtGui.QApplication([])
plot_param1 = [0,2,4,6,8,10,12,14,16,18];
plot_param2 = [0,3,6,9,12,15,18,21,24,27];
samplesize = 10;
samples = range(0,samplesize)
framecount = 0;
pg.mkQApp()
pw = pg.PlotWidget()
pw.show()
p1 = pw.plotItem
p2 = pg.ViewBox()
p1.showAxis('right')
p1.scene().addItem(p2)
p2.setGeometry(p1.vb.sceneBoundingRect())
p1.getAxis('right').linkToView(p2)
p2.setXLink(p1)
def update():
global pw, pg, framecount, plot_param1, plot_param2, p1, p2, samples, samplesize
p1.plot(samples, plot_param1)
p2.addItem(p1.plot(samples, plot_param2, pen='b'))
pw.autoRange()
p1.setXRange(framecount*samplesize, framecount*samplesize+samplesize)
p2.setXRange(framecount*samplesize, framecount*samplesize+samplesize)
if framecount == 0:
flushloop = samplesize
else:
flushloop = samplesize+1
for flush in range(1,flushloop):
plot_param1.pop(0)
plot_param2.pop(0)
samples.pop(0)
# below code is to prepare for next sample
framecount = framecount + 1
for update in range(framecount*samplesize, framecount*samplesize+samplesize):
plot_param1.append(update*framecount*2)
plot_param2.append(update*framecount*3)
samples.append(update)
timer = QtCore.QTimer()
timer.timeout.connect(update)
timer.start(50)

What about something like this?
from pyqtgraph.Qt import QtGui, QtCore
import numpy as np
import pyqtgraph as pg
pg.setConfigOptions(antialias=True)
pg.setConfigOption('background', '#c7c7c7')
pg.setConfigOption('foreground', '#000000')
from pyqtgraph.ptime import time
app = QtGui.QApplication([])
p = pg.plot()
p.setXRange(0,10)
p.setYRange(-10,10)
p.setWindowTitle('Current-Voltage')
p.setLabel('bottom', 'Bias', units='V', **{'font-size':'20pt'})
p.getAxis('bottom').setPen(pg.mkPen(color='#000000', width=3))
p.setLabel('left', 'Current', units='A',
color='#c4380d', **{'font-size':'20pt'})
p.getAxis('left').setPen(pg.mkPen(color='#c4380d', width=3))
curve = p.plot(x=[], y=[], pen=pg.mkPen(color='#c4380d'))
p.showAxis('right')
p.setLabel('right', 'Dynamic Resistance', units="<font>Ω</font>",
color='#025b94', **{'font-size':'20pt'})
p.getAxis('right').setPen(pg.mkPen(color='#025b94', width=3))
p2 = pg.ViewBox()
p.scene().addItem(p2)
p.getAxis('right').linkToView(p2)
p2.setXLink(p)
p2.setYRange(-10,10)
curve2 = pg.PlotCurveItem(pen=pg.mkPen(color='#025b94', width=1))
p2.addItem(curve2)
def updateViews():
global p2
p2.setGeometry(p.getViewBox().sceneBoundingRect())
p2.linkedViewChanged(p.getViewBox(), p2.XAxis)
updateViews()
p.getViewBox().sigResized.connect(updateViews)
x = np.arange(0, 10.01,0.01)
data = 5+np.sin(30*x)
data2 = -5+np.cos(30*x)
ptr = 0
lastTime = time()
fps = None
def update():
global p, x, curve, data, curve2, data2, ptr, lastTime, fps
if ptr < len(x):
curve.setData(x=x[:ptr], y=data[:ptr])
curve2.setData(x=x[:ptr], y=data2[:ptr])
ptr += 1
now = time()
dt = now - lastTime
lastTime = now
if fps is None:
fps = 1.0/dt
else:
s = np.clip(dt*3., 0, 1)
fps = fps * (1-s) + (1.0/dt) * s
p.setTitle('%0.2f fps' % fps)
else:
ptr = 0
app.processEvents() ## force complete redraw for every plot. Try commenting out to see if a different in speed occurs.
timer = QtCore.QTimer()
timer.timeout.connect(update)
timer.start(0)
## Start Qt event loop unless running in interactive mode.
if __name__ == '__main__':
import sys
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
QtGui.QApplication.instance().exec_()

Related

M*N layout of Bokeh + Holoviews plots

I've N plots and i want to show the plots in M*N layout where M=1,2,3,.....
import holoviews as hv
from bokeh.layouts import layout as bk_lyout
from bokeh.layouts import row, column
from bokeh.models import (HoverTool, Panel, CustomJS)
from bokeh.models.widgets import (Tabs, Select, Button)
from bokeh.models.widgets.inputs import AutocompleteInput
from dask import dataframe as dd
from bokeh.core.enums import SizingMode
from holoviews.operation.datashader import datashade
def get_vmap(x, y, label=''):
if x not in cols or y not in cols:
return None
hover = HoverTool(tooltips=[('x-value', '#' + x + '{%F %H:%M:%Ss %6Nms}'),
('y-value', '$y')],
formatters={'#timestamp': 'datetime'})
curve_generated = curve(x, y, label=label)
vmap = datashade(curve_generated, normalization='linear').opts(width=400, height=400)
range_stream = hv.streams.RangeX(source=curve_generated)
filtered_zoom = curve_generated.apply(xrange_filter, streams=[range_stream])
hover_enabled = filtered_zoom.opts(tools=[hover])
# hover_enabled = hv.util.Dynamic(aggregate(curve_generated, width=50, height=50), operation=hv.QuadMesh).opts(tools=[hover], alpha=0, hover_alpha=0.1)
return vmap * hover_enabled
plot_layout = column(children=[])
def modify_doc(doc):
plots = []
for key, value in PLOT_INFO.items():
overlay_plots = []
for carrier in range(7):
car_value = value.format(carrier)
plot_label = "{}-Carrier_{}".format(key.split("_vs_")[1], carrier)
carrier_plot = get_vmap('timestamp', car_value, label=plot_label)
if carrier_plot is not None:
print('\n Found plot for Carrier {}, {}'.format(carrier, carrier_plot))
overlay_plots.append(carrier_plot)
overlaid_plot = None
for plot in overlay_plots:
overlaid_plot = overlaid_plot * plot if overlaid_plot is not None else plot
#overlaid_plot = hv.Overlay(overlay_plots).collate()
if overlaid_plot is not None:
try:
hv_overlay_plot = renderer.get_plot(overlaid_plot, doc)
hv_overlay_plot = bk_lyout([[hv_overlay_plot.state]], sizing_mode='fixed')
select.js_link('value', hv_overlay_plot, 'sizing_mode')
plots.append(hv_overlay_plot)
plot_layout.children.append(hv_overlay_plot)
except Exception as e:
print(e)
tab = Panel(child=plot_layout, title='Interactive Dashboard')
Here plot_layout.children has list of plots and i want to show them in M*N. for example 10 plots, 2 in each row and user should have option to modify it like select 5 plots then 2 rows.

synchronize selection of > 1 chart

How do I synchronize the selection in the 2 charts below? Also, how do I get the bounds of the box selection?
import holoviews as hv
import hvplot.pandas
from bokeh.sampledata.autompg import autompg
hv.extension('bokeh')
hv.Layout([autompg.hvplot.scatter(x='mpg', y='yr', tools=['box_select']), autompg.hvplot.scatter(x='mpg', y='yr', tools=['box_select'])]).cols(1)
The linked selection is easy:
import holoviews as hv
import hvplot.pandas
from bokeh.sampledata.autompg import autompg
hv.extension('bokeh')
from holoviews.plotting.links import DataLink
a = autompg.hvplot.scatter(x='mpg', y='yr', tools=['box_select'])
b = autompg.hvplot.scatter(x='mpg', y='yr', tools=['box_select'])
DataLink(a, b)
hv.Layout([a, b]).cols(1)
Documentation: https://www.holoviews.org/user_guide/Linking_Plots.html
Now for retrieving the bounds. You can use BoundsXY for that:
import numpy as np
import holoviews as hv
from holoviews.streams import BoundsXY
data = np.random.multivariate_normal((0, 0), [[1, 0.1], [0.1, 1]], (1000,))
points = hv.Points(data).opts(tools=['box_select'])
sel = BoundsXY(source=points)
def cog(bounds):
'Center of gravity'
if bounds is None:
bounds=(0, 0, 0, 0)
index = points.dframe().x.between(*bounds[::2]) & points.dframe().y.between(*bounds[1::2])
x = points.dframe().loc[index, 'x'].mean() if index.any() else []
y = points.dframe().loc[index, 'y'].mean() if index.any() else []
return hv.Points((x, y)).opts(size=10)
mean_sel = hv.DynamicMap(cog, kdims=[], streams=[sel])
points * mean_sel
(modelled on http://holoviews.org/reference/apps/bokeh/selection_stream.html)

push_notebook does not update bokeh chart

It is kind of a complex example, but I desperately hope to get help...
I'm using jupyter-notebook 5.2.0, bokeh version is 0.12.9 and ipywidgets is 7.0.1.
Here is my DataFrame df:
import numpy as np
import pandas as pd
import datetime
import string
start = int(datetime.datetime(2017,1,1).strftime("%s"))
end = int(datetime.datetime(2017,12,31).strftime("%s"))
# set parameters of DataFrame df for simualtion
size, numcats = 100,10
rints = np.random.randint(start, end + 1, size = size)
df = pd.DataFrame(rints, columns = ['zeit'])
df["bytes"] = np.random.randint(5,20,size=size)
df["attr1"] = np.random.randint(5,100,size=size)
df["ind"] = ["{}{}".format(i,j) for i in string.ascii_uppercase for j in string.ascii_uppercase][:len(df)]
choices = list(string.ascii_uppercase)[:numcats]
df['who']= np.random.choice(choices, len(df))
df["zeit"] = pd.to_datetime(df["zeit"], unit='s')
df.zeit = df.zeit.dt.date
df.sort_values('zeit', inplace = True)
df = df.reset_index(drop=True)
df.head(3)
Now, let's create a bar plot, also using hover tool:
from bokeh.io import show, output_notebook, push_notebook
from bokeh.models import ColumnDataSource, HoverTool
from bokeh.plotting import figure
import ipywidgets as widgets
output_notebook()
# setup figure
hover = HoverTool(tooltips=[
("index", "$index"),
("ind", "#ind"),
("who", "#who"),
("bytes", "#bytes"),
("attr1", "#attr1"),
])
fig = figure(x_range=list(df.ind), plot_height=250, title="Test Bars",
toolbar_location=None, tools=[hover])
x = fig.vbar(x="ind", top="bytes", width=0.9, source=ColumnDataSource(df))
h=show(fig, notebook_handle=True)
I'm using a ipywidgets.widgets.SelectionRangeSlider to select a range of dates:
import ipywidgets as widgets
# create slider
dates = list(pd.date_range(df.zeit.min(), df.zeit.max(), freq='D'))
options = [(i.strftime('%d.%m.%Y'), i) for i in dates]
index = (0, len(dates)-1)
myslider = widgets.SelectionRangeSlider(
options = options,
index = index,
description = 'Test',
orientation = 'horizontal',
layout={'width': '500px'}
)
def update_source(df, start, end):
x = df[(df.zeit >= start) & (df.zeit < end)]
#data = pd.DataFrame(x.groupby('who')['bytes'].sum())
#data.sort_values(by="bytes", inplace=True)
#data.reset_index(inplace=True)
#return data
return x
def gui(model, bars):
def myupdate(control1):
start = control1[0].date()
end = control1[1].date()
#display(update_source(model, start, end).head(4))
data = update_source(model, start, end)
return myupdate
widgets.interactive(gui(df, x), control1 = myslider)
The problem is, I can't get an update to the graph from the widget:
x.data_source = ColumnDataSource(update_source(df, myslider.value[0].date(), myslider.value[1].date()))
push_notebook(handle=h)
At least, it does something with the plot, as hover is not working anymore...
What am I missing? Or is this a bug?
Thanks for any help
Markus
Figured out how to do it using bokeh: https://github.com/bokeh/bokeh/issues/7082, but unfortunately it only works sometimes...
Best to use CDSViewer.

Patch glyph not updated when using multiple ColumnDataSources in bokeh app

I am trying to use the bokeh server to plot a time series together with a shaded percentile band around, and this, since bokeh does not support the fill_between function from matplotlib, requires the construction of a patch object of double dimension. Hence, I need two ColumnDataSources to hold the data. However, only the first curve is rendered correctly when the data changes. Although the data_source of the GlyphRenderer is updated, the figure does not change. I use bokeh 0.12.3, and have tried with several servers and browsers. A complete, and reasonably minimal example:
import numpy as np
from bokeh.plotting import figure
from bokeh.models import ColumnDataSource
from bokeh.layouts import column
from bokeh.io import curdoc
from bokeh.models.widgets import Select
class AppData:
def __init__(self, n):
self.p_source = None
self.c_source = None
self.x = np.linspace(0, 10, 20)
self.n = n
self.ys = [np.sin(self.x) - i for i in range(self.n)]
self.line = None
self.patch = None
def update_module(self, a, b):
assert b - a == 5
p_data = dict() if self.p_source is None else self.p_source.data
c_data = dict() if self.c_source is None else self.c_source.data
ys = [self.ys[j] for j in range(a, b)]
if "x" not in c_data:
c_data["x"] = self.x
p_data["x"] = c_data["x"].tolist() + c_data["x"][::-1].tolist()
n_r = len(ys[0])
n_p = 2*n_r
if "ys" not in p_data:
p_data["ys"] = np.empty((n_p))
p_data["ys"][:n_r] = ys[0]
p_data["ys"][n_r:] = np.flipud(ys[-1])
c_data["y"] = ys[2]
if self.p_source is None:
self.p_source = ColumnDataSource(data=p_data)
else:
self.p_source.data.update(p_data)
if self.c_source is None:
self.c_source = ColumnDataSource(data=c_data)
else:
self.c_source.data.update(c_data)
if self.line is not None:
print(max(self.line.data_source.data["y"]))
print(max(self.patch.data_source.data["ys"])) # The value changes, but the figure does not!
# initialize
app_data = AppData(10)
app_data.update_module(4, 4 + 5)
s1 = figure(width=500, plot_height=125, title=None, toolbar_location="above")
app_data.line = s1.line("x", "y", source=app_data.c_source)
app_data.patch = s1.patch("x", "ys", source=app_data.p_source, alpha=0.3, line_width=0)
select = Select(title="Case", options=[str(i) for i in range(5)], value="4")
def select_case(attrname, old, new):
a = int(select.value)
app_data.update_module(a, a + 5)
select.on_change('value', select_case)
layout = column(select, s1)
curdoc().add_root(layout)
curdoc().title = "Example of patches not being updated"
I am certainly not very experienced in using bokeh, so I could very well be using the system wrong. However, any help on this matter would be of great help!

using matplotlib or pyqtgraph to graph real time data

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
The pyqtgraph website has a comparison of plotting libraries including matplotlib, chaco, and pyqwt. The summary is:
Matplotlib is the de-facto standard plotting library, but is not built for speed.
Chaco is built for speed but is difficult to install / deploy
PyQwt is currently abandoned
PyQtGraph is built for speed and easy to install
I've used matplotlib and PyQtGraph both extensively and for any sort of fast or 'real time' plotting I'd STRONGLY recommend PyQtGraph, (in one application I plot a data stream from an inertial sensor over a serial connection of 12 32-bit floats each coming in at 1 kHz and plot without noticeable lag.)
As previous folks have mentioned, installation of PyQtGraph is trivial, in my experience it displays and performs on both windows and linux roughly equivalently (minus window manager differences), and there's an abundance of demo code in the included examples to guide completion of almost any data plotting task.
The web documentation for PyQtGraph is admittedly less than desirable, but the source code is well commented and easy to read, couple that with well documented and diverse set of demo code and in my experience it far surpasses matplotlib in both ease of use and performance (even with the much more extensive online documentation for matplotlib).
I would suggest Chaco "... a package for building interactive and custom 2-D plots and visualizations." It can be integrated in Qt apps, though you can probably get higher frame rates from PyQwt.
I've actually used it to write an "app" (that's too big a word: it's not very fancy and it all fits in ~200 LOC) that gets data from a serial port and draws it (20 lines at over 20 fps, 50 at 15 fps, at full screen in my laptop).
Chaco documentation or online help weren't as comprehensive as matplotlib's, but I guess it will have improved and at any rate it was enough for me.
As a general advice, avoid drawing everything at every frame, ie., use the .set_data methods in both matplotlib and chaco. Also, here in stackoverflow there are some questions about making matplotlib faster.
Here is a way to do it using the animation function:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
fig, ax = plt.subplots()
data = np.zeros((32,100))
X = np.arange(data.shape[-1])
# Generate line plots
lines = []
for i in range(len(data)):
# Each plot each shifter upward
line, = ax.plot(X,i+data[i], color=".75")
lines.append(line)
# Set limits
ax.set_ylim(0,len(data))
ax.set_xlim(0,data.shape[-1]-1)
# Update function
def update(*args):
# Shift data left
data[:,:-1] = data[:,1:]
# Append new values
data[:,-1] = np.arange(len(data))+np.random.uniform(0,1,len(data))
# Update data
for i in range(len(data)):
lines[i].set_ydata(data[i])
ani = animation.FuncAnimation(fig, update,interval=10)
plt.show()

Resources