Makie mouse event

Hi,

I’m using the following to respond to mouse clicks for a chart with multiple Axis, ax1, ax2,ax3.

    on(events(ax1.scene).mousebutton, priority = 0) do event
        if event.button == Mouse.left
            println("mouse pressed")
            #println("hovered scene ",hovered_scene())
        end
        return Consume(false)
    end

The event fires reliably but it fires regardless which Axis the mouse is hovering over. Using hovered_scene() appears to be the obvious solution to determine which axis but this gives me an error “hoevered_scene is not implemented yet”

I also tried using ispressed(ax1.scene,Mouse.left) but this returns true when the button is pressed over any axis.

Am I doing something dumb here or is there another way I can determine the axis selected from within the mouse event callback?

thanks
Steve

There are currently a couple different ways to deal with makie’s event system, we haven’t figured out one perfect approach yet. For Axis I wrote some convenience methods that allow to attach/detach event handlers and the mouse events are already preprocessed into clicks, drags, etc., which is complicated to do manually.

It’s explained here in the docs Axis

The function you were looking for is is_mouseinside(ax.scene) . It checks whether the mouse is inside the boundary of the scene. Depending on how exact you want to be with your clicks you may also want to use plt, idx = pick(scene, events(ax).mouseposition[]) to check which plot you’re hovering.

Thanks Frederic, that’s perfect. I had to use Makie.is_mouseinside(ax,scene). Do you know why I had to use the Makie.?

Thanks
Steve

I would like to use the the pick function as you suggested in order to delete elements I have previously added to the Axis, like a hline or vline.

I can use the delete!(ax.scene,ax.scene.plots[someindex]) and that deletes the line but I can’t relate work out how to use the plt,idx returned by pick() to supply the parameters for delete!().

I think this is because I don’t understand the plots structure very well but I can’t find a beginners guide to how this works. Do you have any hints?

Thanks
Steve

Thanks Jules, that looks like what I need to combine mouse clicks with keyboard buttons to annotate my charts. Will these helper methods work when I have multiple Axis?
Thanks
Steve

Yes you can write the function once and then attach it to multiple axes one after the other. You don’t have to do it with an anonymous function and the do syntax, you can just pass a named function as the first argument.

Plots have a tree structure which eventually ends with a few primitive plots types. pick can only see those. You can move up to the root plot with something like

plt, idx = pick(ax.scene, events(ax).mouseposition[])
while plt.parent isa AbstractPlot
    plt = plt.parent
end

When I click on a vline, plt returned by pick is a LineSegment and I can’t see what idx represents. plt.parent is a scene so there seems no point moving up the tree but when I try to delete the plt returned by pick using
delete!(ax.scene,plt) I get
ERROR: LineSegments{Tuple{Vector{Point{2, Float32}}}} not in scene!

In the end I just want to annotate my charts by drawing hlines and vlines and then selected them with pick if I want to delete them.

If I look at the ax.scene.plots vector I can see the LineSegments are added to this vector and I can delete them using these indexes but I can’t see how to usie plt and idx returned by pick to find the correct index of the plots vector to delete.

I hate to keep asking ignorant questions but I can’t find any documentation so I’m not sure how to learn this stuff. What’s the best approach to get productive with these structures?

Thanks
Steve

idx differs between plot types but it generally refers to the arguments you pass to the plot. I.e. for a scatter plot it’s the position of the hovered marker, for lines and linesegmets the last or next position.

Deleting vlines! works fine for me like you described. Maybe you’re clicking on different lines (i.e. the ones from axis)?

fig = Figure()
ax = Axis(fig[1, 1])
vlines!(ax, 0.5, linewidth=10)

on(events(ax.scene).mousebutton) do e
    plt, idx = pick(ax.scene, events(ax.scene).mouseposition[])
    @info typeof(plt)
    if plt !== nothing
        if plt.parent == ax.scene
            @info "in axis"
            delete!(ax.scene, plt)
        else
            @info "not in axis"
        end
    end
    false
end
fig

I really appreciate the help provided to beginners who are not software developers. As an engineer, the Julia ecosystem is a life changing tool but the learning curve is very steep for many of us. Thanks for taking the time to support us on the Julia journey.

Interestingly, when I changed
plt, idx = pick(ax.scene, mouseposition(ax.scene))
to what you suggested…
plt, idx = pick(ax.scene, events(ax.scene).mouseposition[])
the pick process seemed to work much more reliably. How I’m able to add and remove annotation lines at will and should be able to save the annotations once I better understand the ax.scene.plots structure.

Regards
Steve