4

I'm currently evaluating vispy for my interactive plotting needs. While it feels a bit beta-ish I'm rather impressed with its speed. Also API design-wise it looks promising.

One feature I'd need to work is picking plot elements with the mouse. There is one example in the distribution (0.6.4) that promises to do precisely that: examples/demo/scene/picking.py. Unfortunately it doesn't work for me.

It displays a single window containing a graph with multiple lines. I can interact with the plot as whole, i.e. zoom and shift, but I cannot select individual lines.

If I monkey-debug the relevant piece of code (print statement is mine, full example is at github):

@fig.connect
def on_mouse_press(event):
    global selected, fig
    if event.handled or event.button != 1:
        return
    if selected is not None:
        selected.set_data(width=1)
    selected = None
    for v in fig.visuals_at(event.pos):
        print(v)
        if isinstance(v, vp.LinePlot):
            selected = v
            break
    if selected is not None:
        selected.set_data(width=3)
        update_cursor(event.pos)

I get <ViewBox at 0x...> no matter where I click. fig is a vispy.plot.Fig instance which is not well documented.

How can I make this work, i.e. make visuals_at see beyond the ViewBox and find the actual LinePlot instances?

Tiago Martins Peres
  • 14,289
  • 18
  • 86
  • 145
Paul Panzer
  • 51,835
  • 3
  • 54
  • 99
  • The default implementation of [viewbox_mouse_event handler](http://vispy.org/scene.html?highlight=visuals_at#vispy.scene.cameras.BaseCamera.viewbox_mouse_event) just adjusts scale factor when scrolling. – Tiago Martins Peres Mar 09 '20 at 18:28

1 Answers1

1

There is a workaround to make the view non-interactive before calling visuals_at. Afterwards the view can be set to interactive again.

This workaround can be found here in a google groups message workaround

The post is from the year 2015, so the problem seems to have been known for some time.

Code

So add to the code

plt.view.interactive = False

before calling fig.visuals_at and afterwards do

plt.view.interactive = True

Afterwards the code for on_mouse_press should look like this:

def on_mouse_press(event):
    global selected, fig
    if event.handled or event.button != 1:
        return
    if selected is not None:
        selected.set_data(width=1)
    selected = None
    plt.view.interactive = False
    for v in fig.visuals_at(event.pos):
        if isinstance(v, vp.LinePlot):
            selected = v
            break
    plt.view.interactive = True
    if selected is not None:
        selected.set_data(width=3)
        update_cursor(event.pos)

Test

Test Output

Stephan Schlecht
  • 26,556
  • 1
  • 33
  • 47