0

我按照这个使用 Vispy 线程将标签添加到实时信号绘图并尝试使用来自https://vispy.org/gallery/scene/line_update.html#sphx-glr-gallery-scene-line-update-py的示例。我如何保存绘制的信息,我应该能够通过平移视图来查看过去的数据。我应该将绘制的数据存储在其他地方并检索它以查看旧数据吗?随着新数据添加到视图中,我试图保留所有信息。关于如何做到这一点的任何建议都会非常有帮助。

谢谢。

更新 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()
4

1 回答 1

0

总的来说,我认为这取决于您希望您的应用程序在用户体验方面如何运作,以及您希望对用户系统造成什么样的影响。你有几个选择:

  1. 保留 GPU 中的所有数据。由于 OpenGL 和 VisPy 的工作方式,这意味着您还必须在 CPU 上拥有所有数据,以便在新数据进入时更新整个阵列,然后将整个阵列上传到 GPU。有一些方法可以只上传您要发送的部分数据,但我不记得它们是否适用于此处使用的 LineVisual 或您想到的用例(新数据代替旧数据)。
  2. 保留 CPU 中的所有数据和 GPU 中的一个子集,在用户交互时自动更新 GPU 数据。这需要将各个部分连接在一起,以便您知道相机何时平移并更新 GPU 上的数据。不可怕,但有点复杂。
  3. 与 2 相同,但强制用户使用其他 UI 元素(例如 Qt 下拉菜单、按钮等)手动更新数据范围。这样您就不必为了交互而连接事物,您只需更新您需要的数据。

这也让我提出一个问题,当人们想要像你这样的应用程序时,我总是担心:你需要显示所有数据吗?显示最后 N 条记录(即使只是默认情况下)是否足够好?这使您(应用程序开发人员)变得更加容易,但以用户的灵活性为代价,他们在大多数情况下甚至可能不会注意到。

我应该在您链接到的示例中指出,如果还不清楚,这些是数据数组正在使用新数据更新的行,然后.set_data在下一次绘制时被调用以将数据上传到 GPU:

    pos[:, 1] = np.random.normal(size=N)
    color = np.roll(color, 1, axis=0)
    line.set_data(pos=pos, color=color)

颜色只是对数组的假修改以改变颜色。随机数据只是生成假数据并且只更新 Y 坐标。您显然会有需要更新的真实数据,但如果您只显示最后 N 条记录,那么在这件事中只更新 Y 位置将是一种简单且高效的方法。使用 numpy 数组可以完成很多技巧,以减少您需要制作的数据副本数量并获得最佳性能。如果您有任何问题,请告诉我。

于 2021-08-15T21:19:10.437 回答