Hi. I’ve been trying to learn how to use observables in Makie and have managed to make an interactive plot with an IntervalSlider.
The xaxis ticks are controlled with a lift function:
xticks = lift(ab_slider.interval) do val
collect(val), ["a", "b"]
end
Now that works, I would like to make it so that if a \leq 0 the left tick is displayed as 0 instead of a. Similarly for the upper bound, b would be replaced with the max xaxis limit.
I started with some if else statements in my function, and while it runs, the IntervalSlider and plot become really unresponsive.
xticks = lift(ab_slider.interval) do val
if val[1] > 0 && val[2] < 4
return collect(val), ["a", "b"]
else
return [0, 4], ["0", "4"]
end
end
Not only does the UI seem a bit unresponsive, but sometimes the tick values don’t update at all, and other times something seems to trigger them but not always. I suspect that I’ve not understood something about how Observables work. I found working with an observable of a tuple value for the interval less than intuitive. Thanks.
The logic isn’t fully implemented in my example as I wanted to keep it simple and what I put was enough to reproduce the problem.
Another side issue, is that if the value of a = 0 and I want to have ["0", "a", "b", "4"] as my ticks, then I get a zero step error. So I do need to implement logic to prevent this happening, but any form of logic makes the UI unresponsive and glitchy in terms of updating.
I wondered if it had something to do with the way the Observable wraps a tuple value, and I’m looking for changes in the individual values?
Can you share a runnable example that exhibits the problem? I haven’t had problems putting complicated data types in Observables before, but that might have something to do with it.
When I tried making up a full minimal example I decided to do it in a single script file instead of a jupyter notebook. Then I noticed lots of runtime errors appearing in the console that I hadn’t seen before.
The error was an inexact error and all I had to do was change the integers to floats. This error was only occurring when the conditional logic kicked in.
Thanks for your help.
Edit: I’ll just throw this in as an example for anyone else who wants to do the same
using GLMakie
GLMakie.activate!(; float=true)
set_theme!(theme_black())
##
f(x) = exp(-x/2)
xs = LinRange(0, 4, 41)
fig = Figure(size=(800, 600))
ab_slider = IntervalSlider(fig[2, 1], range = 0:0.1:4, startvalues = (0.0, 4.0))
xticks = lift(ab_slider.interval) do val
if val[1] > 0.0 && val[2] < 4.0
return collect(val), ["a", "b"]
else
return [0.0, 4.0], ["0", "4"] # Needs to be 4.0 and not 4
end
end
ax = Axis(fig[1, 1], xticks=xticks)
lines!(ax, xs, f)
fig
It’s a bit weird that only the second value in the return array needed to be a float to avoid the error.
Edit 2: It can be either 0.0 or 4.0, just not both integers 0 and 4. Sure there’s some reason behind it somewhere. Edit 3: Ah, it must make the xticks float if either value is set as float, and then it’s ok.
With lift you need to be careful that the return type of your first invocation also works for all other possible branches because that’s the type parameter that the observable uses. So if you first return an integer and later a float, the float will fail to convert to an integer.
Sometimes you have a union of types like Union{Nothing, Float64}, in that case I usually create an empty observable first with the correct type parameter like result = Observable{Union{Nothing, Float64}} and then use the map! function on that with the logic you’d otherwise put into lift