Iterate an observable

I have an undirected simple graph and a vector of nodes that I want to colour red. The function plotg below allows this

using CairoMakie, Graphs, GraphMakie

function plotg(g, y)
    fig = Figure(); ax = Axis(fig[1, 1])
    graphplot!(ax, g; 
        node_color = [ ifelse(i ∈ y, :red, :black) for i ∈ vertices(g) ] )
    hidexdecorations!(ax); hideydecorations!(ax)
    return fig
end 

g = watts_strogatz(100, 4, .05; seed = 1)
red = [83, 85, 87]

plotg(g, red)

Now I wish to animate this, running through a vector of vectors of nodes to colour red

reds = [
    [85], [85, 87], [83, 85, 87], [83, 84, 85, 87], [83, 84, 85, 87, 88],
    [83, 84, 87, 88], [83, 84, 85, 87, 88], [83, 84, 85, 86, 87, 88],
    [82, 83, 84, 85, 86, 87, 88], [80, 82, 83, 84, 85, 86, 87, 88],
    [79, 80, 82, 83, 84, 85, 86, 87, 88], [79, 80, 82, 83, 84, 85, 86, 88]
]

When I try to set up an animation with Observables,

function videog(g, reds)
    red = reds[1]
    Y = Observable(red)
    fig = plotg(g, Y)
    record(fig, "asdf.mp4"; framerate = 2) do io
        for t ∈ 1:12 
            red = reds[t]
            Y[] = infec         # change values to plot
            recordframe!(io)    # record a new frame
        end
    end
end 

videog(g, reds)

I get an error message MethodError: no method matching iterate(::Observable{Vector{Int64}}).

This naive cheat gives a video but it no longer advances through the vector:

function plotg2(g, y::Vector{<:Real})
    fig = Figure(); ax = Axis(fig[1, 1])
    graphplot!(ax, g; 
        node_color = [ ifelse(i ∈ y, :red, :black) for i ∈ vertices(g) ] )
    hidexdecorations!(ax); hideydecorations!(ax)
    return fig
end 

function plotg2(g, y::Observable{Vector{Int64}})
    values = y[]
    return plotg2(g, values)
end 

function videog2(g, reds)
    red = reds[1]
    Y = Observable(red)
    fig = plotg2(g, Y)
    record(fig, "asdf.mp4"; framerate = 2) do io
        for t ∈ 1:12 
            Y[] = reds[t]       # change values to plot
            recordframe!(io)    # record a new frame
        end
    end
end 

videog2(g, reds)

Is there a way to iterate through an Observable vector in an animation or do I need to find a wholly new way of making the video?

Try to work from this minimal working example with a scatter plot and a slider (just replace the slider with an Observable index for your video):

julia> let
           pts = [(rand(2)...,) for i = 1:100]
           f = Figure()
           ax = Axis(f[1, 1])
           sl = Slider(f[2, 1], range=eachindex(reds))
           markercolor = lift(sl.value) do k
               [i in reds[k] ? :red : :green for i in eachindex(pts)]
           end
           scatter!(ax, pts, color=markercolor)
           f
       end

image

1 Like

Thank you, that helped. I now pass a vector of colours to the plot function and this works as an Observable.