Axis jitter when using slider and axis autolimits in GLMakie

When using a Slider that changes a variable in a function, sometimes the amplitude can change quite a lot. In some cases, it makes sense to resize the axes to see what’s going on in a plot. This can easily be done in GLMakie by using autolimits!() after updating the variable. However, if the number of decimal places of the axis tick labels changes, this causes the Axis itself to resize, making the plot jitter. This is undesirable. I am working around this by setting yticklabelspace (for the y axis) large enough that this won’t happen.

I’m wondering if this is the canonical way to solve this or if there is a better way?

Example:

using GLMakie

x = 0:0.1:6π
A = Observable(1.0)
y(x, A) = A * sin.(x)

oy = Observable(y(x, to_value(A)))

fig = Figure()
display(fig)

ax = Axis(fig[1, 1], ylabel = "y", xlabel = "x",
            yticklabelspace = 20.0
            )

lines!(x, oy)

sg = SliderGrid(fig[2, 1],
    (label = "Amplitude", range = 0:0.01:10, format = "{:.1f}", startvalue=1)
)

on(sg.sliders[1].value) do val
    A[] = val
    oy[] = y(x, to_value(A))
    autolimits!(ax)
end

Without yticklabelspace
out

With yticklabelspace (now tick labels cover the axis label, but that can be fixed)
out_on

1 Like

During normal axis zooming with the mouse, this reset action is delayed because it would be annoying if the axis constantly jittered under the cursor. You can do something similar I think. Currently, the slider doesn’t make it easy to access the event when the drag stops (it will after this PR Add option to update when releasing mouse button from slide by lwhitefox · Pull Request #2228 · MakieOrg/Makie.jl · GitHub) but you could do this in pseudocode:

tight_ticklabels!(ax) # set good fixed limits to start with

timer = SomeTimer(latency = 300ms_or_so)

on(timer_fires) do
    tight_ticklabels!(ax)
end

on(slider.value) do value
    update_plot_using(value)
    start_or_reset_some_timer!(timer)
end

Thanks for the suggestion. This works alright, but getting the timer right is tricky. It would be nice if it would update only when the slider stopped. The callback function only runs once while I’m moving the slider and I’m not sure how to get the timer to fire with each value change of the slider. I’m not sure if I’m implementing this right, but this is what I’m trying:

x = 0:0.1:6π
A = Observable(1.0)
y(x, A) = A * sin.(x)

oy = Observable(y(x, to_value(A)))

fig = Figure()
display(fig)
ax = Axis(fig[1, 1], ylabel = "y", xlabel = "x")
lines!(x, oy)

tight_ticklabel_spacing!(ax)

sg = SliderGrid(fig[2, 1],
    (label = "Amplitude", range = 0:0.01:10, format = "{:.1f}", startvalue=1))

function cb(timer)
    wait(timer)
    tight_ticklabel_spacing!(ax)
end

on(sg.sliders[1].value) do val
    A[] = val
    oy[] = y(x, to_value(A))
    autolimits!(ax)
    timer = Timer(cb, 0.3, interval=0.3)
end