Reputation: 11
I followed this Add labels to real-time signal plotting with Vispy thread and tried using the example from https://vispy.org/gallery/scene/line_update.html#sphx-glr-gallery-scene-line-update-py . How can I hold the plotted information, I should be able to look at the past data as well by panning the view. Should I store the plotted data else where and retrieve that to look at the old data? I am trying to hold all information as new data gets added to the view. Any suggestions on how to do this will be really helpful.
Thanks.
Update 1:
'''
import sys
import numpy as np
from vispy import app, scene
# vertex positions of data to draw
N = 100
pos = np.zeros((N, 2), dtype=np.float32)
x_lim = [0, 10.]
y_lim = [-2., 2.]
pos[:, 0] = np.linspace(x_lim[0], x_lim[1], N)
pos[:, 1] = np.random.normal(size=N)
# color array
color = np.ones((N, 4), dtype=np.float32)
color[:, 0] = np.linspace(0, 1, N)
color[:, 1] = color[::-1, 0]
canvas = scene.SceneCanvas(keys='interactive', show=True)
grid = canvas.central_widget.add_grid(spacing=0)
viewbox = grid.add_view(row=0, col=1, camera='panzoom')
# add some axes
x_axis = scene.AxisWidget(orientation='bottom')
x_axis.stretch = (1, 0.1)
grid.add_widget(x_axis, row=1, col=1)
x_axis.link_view(viewbox)
y_axis = scene.AxisWidget(orientation='left')
y_axis.stretch = (0.1, 1)
grid.add_widget(y_axis, row=0, col=0)
y_axis.link_view(viewbox)
# add a line plot inside the viewbox
line = scene.Line(pos, color, parent=viewbox.scene)
# auto-scale to see the whole line.
viewbox.camera.set_range()
import time
t0 = time.time()
x_val = []
y_val= []
def update(ev):
'''
New x and y coordinates are generated and assigned to `pos`.
`datastore` variable holds all the data (past and current
generated data). Only `pos` data is used to update the plot in
real time.
'''
global pos, color, line, x_val, y_val,datastore
pos[:, 0] = np.linspace(x_lim[0], x_lim[1], N)+time.time()-t0
pos[:, 1] = np.sin(pos[:, 0])+0.2*np.random.normal(size=(N))
x_val= np.append(x_val,pos[:,0])
y_val= np.append(y_val,pos[:,1])
datastore=np.stack((x_val, y_val),axis=1)
# print(pos.size)
color = np.roll(color, 1, axis=0)
line.set_data(pos=pos)
viewbox.camera.set_range(y=(pos[:, 1].max(), pos[:, 1].min()), x=(pos[:, 0].max(), pos[:, 0].min()))
def mouseMove(ev):
'''
`datastore` consists of all data. Based on the camera view,
data is extracted from `datastore`. this extracted data is
used to update the plot.
'''
print(viewbox.camera.get_state()['rect'])
currentBounds= viewbox.camera.get_state()['rect']
currentMin_x = currentBounds.right
currentMax_x = currentBounds.left
if currentMin_x < datastore[:,0].min():
currentMin_x = datastore[:,0].min()
if currentMax_x > datastore[:,0].max():
currentMax_x = datastore[:,0].max()
getData = datastore[(datastore[:,0]> currentMin_x) & (datastore[:,0]< currentMax_x)][:100] #Should be equal to N, for code to work
print(getData.size)
line.set_data(pos=getData, color=color) #Updates plot with the extracted data
viewbox.camera.set_range(y=(getData[:, 1].max(), getData[:, 1].min()), x=(getData[:, 0].max(), getData[:, 0].min()))
print(viewbox.camera.get_state())
t = app.Timer()
t.connect(update)
t.start(iterations=60*5)
viewbox.events.mouse_release.connect(mouseMove)
t.events.stop.connect(lambda x: app.quit())
if __name__ == '__main__' and sys.flags.interactive == 0:
app.run()
'''
Update 2
import sys
import numpy as np
from vispy import app, scene
# vertex positions of data to draw
N = 100
pos = np.zeros((N, 2), dtype=np.float32)
x_lim = [0, 10.]
y_lim = [-2., 2.]
pos[:, 0] = np.linspace(x_lim[0], x_lim[1], N)
pos[:, 1] = np.random.normal(size=N)
canvas = scene.SceneCanvas(keys='interactive', show=True)
grid = canvas.central_widget.add_grid(spacing=0)
viewbox = grid.add_view(row=0, col=1, camera='panzoom')
# add some axes
x_axis = scene.AxisWidget(orientation='bottom')
x_axis.stretch = (1, 0.1)
grid.add_widget(x_axis, row=1, col=1)
x_axis.link_view(viewbox)
y_axis = scene.AxisWidget(orientation='left')
y_axis.stretch = (0.1, 1)
grid.add_widget(y_axis, row=0, col=0)
y_axis.link_view(viewbox)
# add a line plot inside the viewbox
line = scene.Line(pos, parent=viewbox.scene)
# auto-scale to see the whole line.
viewbox.camera.set_range()
import time
t0 = time.time()
x_val = []
y_val= []
def update(ev):
global pos, line, x_val, y_val,datastore
pos[:, 0] = np.linspace(x_lim[0], x_lim[1], N)+time.time()-t0
pos[:, 1] = np.sin(pos[:, 0])+0.2*np.random.normal(size=(N))
x_val= np.append(x_val,pos[:,0])
y_val= np.append(y_val,pos[:,1])
datastore=np.stack((x_val, y_val),axis=1)
# print(pos.size)
line.set_data(pos=pos)
viewbox.camera.set_range(x=(pos[:, 0].min(),pos[:, 0].max()),y=(pos[:, 1].min(),pos[:, 1].max()))
def mouseMove(ev):
print(viewbox.camera.get_state()['rect'])
currentBounds= viewbox.camera.get_state()['rect']
currentMin_x = currentBounds.left
currentMax_x = currentBounds.right
print(currentMin_x, currentMax_x)
getData = datastore[(datastore[:,0]>currentMin_x) & (datastore[:,0]< currentMax_x)] #Should be equal to N, for code to work
print(getData)
print(getData.size)
line.set_data(pos=getData)
viewbox.camera.set_range( x=( getData[:, 0].min(),getData[:, 0].max()),y=(getData[:, 1].min(),getData[:, 1].max()))
print(viewbox.camera.get_state())
t = app.Timer()
t.connect(update)
t.start(iterations=60*15)
viewbox.events.mouse_release.connect(mouseMove)
t.events.stop.connect(lambda x: app.quit())
if __name__ == '__main__' and sys.flags.interactive == 0:
app.run()
Upvotes: 0
Views: 1110
Reputation: 3667
Overall I think this depends how you want your application to function as far as user experience and what kind of toll you want to have on the user's system. You have a couple choices:
This also leads me to ask about one concern I always have when people want applications like yours: do you need to show all the data? Would showing the last N records (even if only by default) be good enough? This makes it much easier on you, the application developer, at the cost of flexibility for the user that they probably won't even notice in most cases.
I should point out in the example you linked to, if it wasn't clear already, these are the lines where the data array are being updated with new data and then .set_data
is called to upload the data to the GPU on the next draw:
pos[:, 1] = np.random.normal(size=N)
color = np.roll(color, 1, axis=0)
line.set_data(pos=pos, color=color)
The color is just a fake modification of the array to change the color. The random data is just generating fake data and only updating the Y coordinates. You would obviously have real data that you'd need to update, but if you only show the last N records then updating only the Y positions in this matter would be an easy and performant way to do it. There are plenty of tricks that can be done with numpy arrays to reduce the amount of copies of the data you need to make and get the best performance. Let me know if you have any questions.
Upvotes: 0