I have a real-time plotting system that retrieves and preprocess data from a stream and plots it, with each channel (from 1 to e.g. 100) plotted on a different 'row'. The version with pyqtgraph
backend is complete and looks like:
But it's not very performant. Thus, I wanted to use vispy
, based on this example. @djhoese suggested I use the higher-level scenes instead, but I did not find a way to pass the entire array of data (channels, samples) at once as I do with the low-level gloo
, and thus limiting the OpenGL call. As performance is my main reason for implementing the vispy
backend for my real-time viewer, it didn't make sense to move away from the gloo
version.
Moreover, this version offers the possibility to set the number of rows and columns. For now, I use only one column, but one of the future improvements will be to select the number of rows/columns based on the number of channels. e.g. with a system using 128 channels, a 2-column window would be ideal.
I am slowly changing the original example code to match my need. Below, you can find the modified shaders (full-code here):
VERT_SHADER = """
#version 120
// y coordinate of the position.
attribute float a_position;
// row, col, and time index.
attribute vec3 a_index;
varying vec3 v_index;
// 2D scaling factor (zooming).
uniform vec2 u_scale;
// Size of the table.
uniform vec2 u_size;
// Number of samples per signal.
uniform float u_n;
// Color.
attribute vec3 a_color;
varying vec4 v_color;
void main() {
float nrows = u_size.x;
float ncols = u_size.y;
// Compute the x coordinate from the time index.
float x = -0.9 + 1.9*a_index.z / (u_n-1);
vec2 position = vec2(x - (1 - 1 / u_scale.x), a_position);
// Find the affine transformation for the subplots.
vec2 a = vec2(1./ncols, 1./nrows);
vec2 b = vec2(-1 + 2*(a_index.x+.5) / ncols,
-0.9 + 1.8*(a_index.y+0.5) / nrows);
// Apply the static subplot transformation + scaling.
gl_Position = vec4(a*u_scale*position+b, 0.0, 1.0);
v_color = vec4(a_color, 1.);
v_index = a_index;
}
"""
FRAG_SHADER = """
#version 120
varying vec4 v_color;
varying vec3 v_index;
void main() {
gl_FragColor = v_color;
// Discard the fragments between the signals (emulate glMultiDrawArrays).
if ((fract(v_index.x) > 0.) || (fract(v_index.y) > 0.))
discard;
}
"""
One of the changes I made was on the transformation which positions the different points on the window. Now, the plot area starts at -0.9
on the left and stops at 1
on the right; and starts at -0.9
at the top and stops at 0.9
at the bottom, freeing margins.
My question is, how can I add additional elements on the canvas? I would like to add a fix vertical line at e.g. -0.9
, I would like to add text element, I would like to add a moving vertical line, an horizontal line, ...
I'm guessing each of those elements should have there own VERT and FRAG shaders passed to the python constructor. Is this correct? How do you specify on which Canvas you want to draw, and what would be the different constructor to use for the element cited above?