How to retain toggle-visibility by clicking on legend after emptying axis?

The following code loads a different pair of time-series into the figure whenever the slider is moved. The code below generates three pairs, so the slider has 3 possible values.

When the figure is first created right after running the code, I could toggle the visibility of a time-series by clicking on its associated label in the legend. After I move the slider, this toggle-visibility feature is gone. I suspect it is because the empty! command in refresh_plot! flushed out some default state that implements the toggle behaviour.

May someone please help me improve this code so that the exact same toggle-visibility behaviour is retained after the slider is moved? Is there an alternative to empty! that is more suitable for this example?

using GLMakie
GLMakie.closeall() # close any open screen

# Generate data: 3 records, each has two 1-D time-series.
LT = typeof(LinRange(0, 3, 50))
ts_set = Memory{LT}(undef, 3)
ts_set[1] = LinRange(0.5, 2, 30)
ts_set[2] = LinRange(0, 3, 50)
ts_set[3] = LinRange(4, 10, 200)

y1s_set = collect(sinc.(x) for x in ts_set)
y2s_set = collect(cos.(x) for x in ts_set)
title_strings = collect("Record $(r)" for r in 1:length(ts_set))

# # Figure
fig = Figure(
    backgroundcolor = RGBf(0.98, 0.98, 0.98),
    size = (1920, 1080)
)

g_slider = Slider(
    fig[1, 2],
    range = 1:length(ts_set),
    startvalue = 1,
    horizontal = false,
)

g_plots = fig[1, 1] = GridLayout()

ax = Axis(
    g_plots[1, 1],
    xlabel = "Time (sec)", ylabel = "Height (cm)", title = "Test plot",
)

# load data
function refresh_plot!(ax, r_obs, title_strings, ts_set, y1s_set, y2s_set)

    r = to_value(r_obs)

    # plot
    empty!(ax)
    ax.title[] = title_strings[r]

    ts, ys1, ys2 = ts_set[r], y1s_set[r], y2s_set[r]
    scatter!(ax, ts, ys1, label = "Sensor 1")
    scatter!(ax, ts, ys2, label = "Sensor 2")
    DataInspector()

    axislegend()

    return nothing
end

# slider
_ = lift(g_slider.value) do r_obs

    refresh_plot!(ax, r_obs, title_strings, ts_set, y1s_set, y2s_set)
    return nothing
end

display(fig)

Instead of emptying the axis and replotting, just update the same scatter plots with new data and leave the legend intact:

# Generate data: 3 records, each has two 1-D time-series.
LT = typeof(LinRange(0, 3, 50))
ts_set = Memory{LT}(undef, 3)
ts_set[1] = LinRange(0.5, 2, 30)
ts_set[2] = LinRange(0, 3, 50)
ts_set[3] = LinRange(4, 10, 200)

y1s_set = collect(sinc.(x) for x in ts_set)
y2s_set = collect(cos.(x) for x in ts_set)
title_strings = collect("Record $(r)" for r in 1:length(ts_set))

# Figure
fig = Figure(
    backgroundcolor = RGBf(0.98, 0.98, 0.98),
    size = (1920, 1080)
)

g_slider = Slider(
    fig[1, 2],
    range = 1:length(ts_set),
    startvalue = 1,
    horizontal = false,
)

g_plots = fig[1, 1] = GridLayout()

ax = Axis(
    g_plots[1, 1],
    xlabel = "Time (sec)", ylabel = "Height (cm)", title = "Test plot",
)

sc1 = scatter!(ax, Float64[], Float64[], label = "Sensor 1")
sc2 = scatter!(ax, Float64[], Float64[], label = "Sensor 2")

# load data
function refresh_plot!(ax, i, title_strings, ts_set, y1s_set, y2s_set)

    ax.title = title_strings[i]

    ts, ys1, ys2 = ts_set[i], y1s_set[i], y2s_set[i]
    update!(sc1, arg1 = ts, arg2 = ys1)
    update!(sc2, arg1 = ts, arg2 = ys2)

    autolimits!(ax)

    return nothing
end

# slider
on(g_slider.value, update = true) do i
    refresh_plot!(ax, i, title_strings, ts_set, y1s_set, y2s_set)
    return nothing
end

axislegend()
DataInspector()

display(fig)
1 Like

Thanks! I modified your code to allow the label text in the legend to be updated whenever the slider moves. However, it wouldn’t update if I keep axislegend() outside of the refresh_plot!. When I put axislegend() inside refresh_plot!, the legend label updates but overwrites the existing text. Do you have any suggestions on clearing the existing legend label text before updating?

Here is what I have:


# load data
function refresh_plot!(ax, i, title_strings, ts_set, y1s_set, y2s_set)

    ax.title = title_strings[i]

    ts, ys1, ys2 = ts_set[i], y1s_set[i], y2s_set[i]
    update!(sc1, arg1 = ts, arg2 = ys1)
    update!(
        sc2,
        arg1 = ts,
        arg2 = ys2,
        color = :olive,
        label = "Sensor 2 from $(title_strings[i])",
    )

    autolimits!(ax)
    #axislegend(ax) # legend updates, but the existing text is also visible.

    return nothing
end

# slider
on(g_slider.value, update = true) do i
    refresh_plot!(ax, i, title_strings, ts_set, y1s_set, y2s_set)
    return nothing
end

axislegend() # legend does not update when slider is moved.
DataInspector()

display(fig)