How to create a series of interactive plots using GLMakie?

I need to make an interactive plot that takes mouse clicks on an image plot as input. There will be a series of plots with an image on top and a ‘Done’ button at the bottom. The program should plot each image in its own window (one after another in a loop), and then a person clicks on the image and only the last mouse click should be plotted on the image. Once the scatter point is correct, click ‘Done’ and the window closes, and then the next image gets plotted until it reaches the end. I have found a way to plot one image and how to close the window, but I’m not sure how to exit out of the code and return the final scatter point location in each image.

Any input or comments on what I have so far would be appreciated. GLMakie wizards, I need your help!!

Here’s what I have so far:


using GLMakie, Images
GLMakie.activate!()

image_name = "IMG.tif"
img = load(image_name)

function makefig(img,image_name)
    target = Observable(Point2f[])
    fig = Figure(resolution = (1600, 900))
    image(fig[1,1],img,
        axis = (title = image_name, aspect = DataAspect()))
    xlims!(1,size(img,1)),  ylims!(1,size(img,2))
    fig[2,1] = buttongrid = GridLayout(tellwidth = false)
    buttons = buttongrid[1, 1] = [
    Button(fig, label = "Done", 
        height = 60, width = 250, fontsize = 30)]
    fig[2,1] = GridLayout(height = 150)
    display(fig)
    return fig, buttons, target
end

function return_mouse_position(img, image_name)
    fig, buttons, target = makefig(img,image_name)
    ax = content(fig[1,1])
    Makie.deactivate_interaction!(ax, :rectanglezoom)
    open_window = fig.scene.events.window_open
    on(events(ax).mousebutton) do event
        if event.button == Mouse.left && event.action == Mouse.press
            mp = mouseposition(ax)
            pushfirst!(target[],mp),
            notify(target)
            scatter!(ax, target; 
            marker = :circle, strokewidth = 2, 
            strokecolor = :red, color = :black, markersize = [12])
        end
        on(buttons[1].clicks) do click
            open_window[] = false
        end
        off(events(ax).mousebutton)
    end
    return mp 
end

mouse_location = return_mouse_position(img,image_name)


[I’m running Julia @v1.8 on Windows 10 and using GLMakie@v0.8.2]

Here’s how I would do this kind of logic, I wouldn’t use a new window every time but replace the image content in the existing window. One simple method of making the scatter dot appear and disappear is by setting it to NaN values to hide it. Then on each button click I append the current point to some vector which gets printed out at the end.

using GLMakie

n = 3
images = [rand(RGBf, 10, 10) for _ in 1:n]
i_active = Observable(1)
imgdata = @lift images[$i_active]

f = Figure(fontsize = 30)
ax = Axis(f[1, 1], aspect = DataAspect())
b = Button(f[2, 1], tellwidth = false, label = @lift("Confirm image $($i_active)"))
image!(ax, imgdata)

Makie.deregister_interaction!(ax, :rectanglezoom)

current_point = Observable(Point2f(NaN, NaN))

Makie.register_interaction!(ax, :scatterclick) do e::MouseEvent, axis
    if e.type === MouseEventTypes.leftclick
        current_point[] = e.data
    end
end

scatter!(ax, current_point, color = :red, markersize = 40)

points = Point2f[]

on(b.clicks) do c
    if !(all(isfinite, current_point[]))
        @warn "Click on the image first to mark a point"
        return
    end
    push!(points, current_point[])

    if i_active[] == n
        f.scene.events.window_open[] = false
        @info points
        return
    end

    i_active[] += 1
    current_point[] = Point2f(NaN, NaN)
end

display(f)
2 Likes