Makie Observables

Hi,
I’m trying to use an interval slider to update xlims on a series plot but the xlims! function is not being triggered when the observables are being changed.

EndDate = Observable(500.0)
DateRange_is = IntervalSlider(fig[4,1],range = (0:100:600), startvalues=(100,500))
LabelText = lift(DateRange_is.interval) do int
    string(int[1]," , ",int[2])
    println("slider changed to ",int[1]," - ",int[2])
    StartDate[] = int[1]
    EndDate[] = int[2]
end
xlims!(Price_ax,StartDate[],EndDate[])

I get no errors but the xlims of the plot dont get updated unless I re-execute
xlims!(Price_ax,StartDate[],EndDate[]) I thought this line would automatically update the plot because I used observables as parameters.

The values StartDate and EndDate are getting updated correctly when the slider is moved but concerningly I noticed in the object browser that there are no listeners for the observables and that
If I change the xlims command to xlims!(Price_ax,StartDate,EndDate) I just get a type error…
ERROR: MethodError: Cannot convert an object of type Observable{Float64} to an object of type Float32

Can anyone tell me if my use case is appropriate and perhaps give me pointers to examples that use widgets and observables to trigger commands like xlims.

Thanks
Steve
Julia 1.6.2
Julia ext 1.4.3
vscode 1.61
Windows 10

Hi Steve,

To listen to observables use on for one observable or orany for multiple

onany(StartDate, EndDate) do start_date, end_date
    xlims!(Price_ax, start_date, end_date)
end

This is now listening for when the observable changes, and then passing the values into xlims with either of StartDate or EndDate changes, and you should see there are listeners for both observables.

Also if you use backticks around code in discourse posts its much easier to read! See

3 Likes

You can just call xlims! in your existing lift of DateRange_is.interval.

What you did in xlims!(Price_ax,StartDate[], EndDate[]) was extracting the plain Int values from your two observables and feed them into the function, that doesn’t do any Observable magic. Functions have to be explicitly written to handle observables, and xlims! is not such a function, it doesn’t do any connections on its own. The functions that do that are almost exclusively plotting functions such as lines, scatter, etc.

2 Likes

Thanks Max, that gives me a much better understanding of how to setup the listeners. Sorry about the format, fixed now.

1 Like

Thanks Jules, that makes sense. Is there some information on what methods support Observables or do I need to learn how to read the source files?
Thanks
Steve

Not really, but the plotting functions all do, input arguments and attributes. The rest usually doesn’t, unless specified in the docstring. Usually functions that do an action, like changing the x limits, will not take an observable. But you can set up your own observables that call such functions.

Here is my proposal to adopt the y-axis according to the maximum value of a bar-plot:

# ----------------------------------------------------------------------------------------------------------------------------- #
# --- Reset button that resets all sliders inside a SliderGrid:                                                                 #
# ----------------------------------------------------------------------------------------------------------------------------- #
using GLMakie

fig = Figure()
# --- figure set-up: two panes side by side, right one with two sub-panes: 
# --- left pane:
ax = Axis(fig[1, 1]) 
ylims!(ax, 0, 30)

# --- right pane contains two sub-panes: [1,1]: top, [2,1]: bottom
# --- SliderGrid: right pane, top sub-pane:
sg = SliderGrid(  fig[1, 2][1, 1], 
    (; label = "Voltage",     range = 0:0.1:10, format = "{:.1f}V", startvalue = 5.3),
    (; label = "Current",     range = 0:0.1:20, format = "{:.1f}A", startvalue = 10.2),
    (; label = "Resistance",  range = 0:0.1:50, format = "{:.1f}Ω", startvalue = 15.9),
    width = 350,
    tellheight = false,
    )

# --- Button: right pane, bottom sub-pane:
bt = Button(fig[1, 2][2, 1]; label = "reset", tellheight = false, 
        strokecolor = RGBf(0.94, 0.14, 0.24), strokewidth = 4)

# --- access elements inside slidergrid, each element is of type "Observable{Any}(Float64)"
# --- variable sliderobservables is a vector of "Observable{Any}(Float64)"
sliderobservables = [s.value for s in sg.sliders]


# --- build a vector of type "Observable" containing the value of each slider inside slider grid:
bars = lift(sliderobservables...) do slvalues...
    [slvalues...]
end
# --- build a scaler of type "Observable" containing the max y-value inside the bar-plot:
y_range_upper = lift(sliderobservables...) do slvalues...
    maximum([slvalues...])
end

# --- reset all sliders inside SliderGrid "sg"
on(bt.clicks) do n # n = number of clicks
    for i_slider in sg.sliders
        set_close_to!(i_slider, i_slider.startvalue[])
        ylims!(ax, 0, 30)
    end
end

# --- listen to observable "y_range_upper"
on(y_range_upper) do _yaxis_upper
    ylims!(ax, 0, max(30, round(Int, 1.2 * _yaxis_upper)))
    # println("y_range_upper: ", _yaxis_upper)
end
barplot!(ax, bars, color = [:yellow, :orange, :red])

display(fig)
2 Likes