How to make Makie themes work with plots inside loops?

Hi again, another unusual question about getting Makie themes to work. I need plot elements to keep getting added to a big plot inside a loop, but then the Makie “with_themes” command doesn’t work anymore. For example, I need to do something like this:

using MakiePublication
using CairoMakie
global figMyFig = Makie.Figure()
global axMyFig = Makie.Axis(figMyFig[1, 1])
#
for i in 1:5
    Makie.scatter!(axMyFig, (2.7*i),(1.7*i))
end
#
with_theme(theme_aps()) do 
    fig = figMyFig
    ax = axMyFig
    Makie.display(fig)
end

But this does NOT do the same thing as:

using MakiePublication
using CairoMakie
with_theme(theme_aps()) do 
    figMyFig2 = Makie.Figure()
    axMyFig2 = Makie.Axis(figMyFig2[1, 1])
    for i in 1:5
        Makie.scatter!(axMyFig2, (2.7*i),(1.7*i))
    end
    Makie.display(figMyFig2)
end

The latter gets the with_theme command right, but the former fails to get it working. (I’ve tried with and without the global commands, and numerous variations; nothing works.) Any ideas what’s causing this weird theme fail? Thanks!

(Note that I usually also have the Plots package loaded, which is why I put the “Makie.” in front of everything. But it doesn’t work even without Plots loaded.)

Makie themes are applied at object creation time, not retroactively.

When you call Figure(), Axis(), and scatter!(), each of those reads the currently active theme to set its attributes (fonts, colors, line widths, tick styles, etc.). Once created, those objects carry their baked-in attribute values. Wrapping an already-constructed figure in with_theme later has no effect — you’re just assigning an existing object to a new variable name, not re-creating it.

In your second example, everything works because Figure(), Axis(), and scatter!() are all called inside the with_theme block, so the theme is active during construction.

For deferred/incremental construction use set_theme! to activate the theme globally before you start building, then set_theme!() (no args) to reset afterward:

set_theme!(theme_aps())

fig = Figure()
ax = Axis(fig[1, 1])

for i in 1:5
    scatter!(ax, [2.7*i], [1.7*i])
end

display(fig)

set_theme!()  # reset to defaults

This way the theme is active for every object creation call regardless of scope, and your loop can live anywhere. The tradeoff is that it’s global mutable state, so remember to reset it when done.

Well, the use of set_theme! did indeed work!

Actually, your explanation made me realize a more “targeted” solution: just set the theme at creation time. So, this works also:

using MakiePublication
using CairoMakie
#
with_theme(theme_aps()) do 
    global figMyFig = Makie.Figure()
    global axMyFig = Makie.Axis(figMyFig[1, 1])
end
#
for i in 1:5
    Makie.scatter!(axMyFig, (2.7*i),(1.7*i))
end
#
with_theme(theme_aps()) do 
    fig = figMyFig
    ax = axMyFig
    Makie.display(fig)
end

(I need the final with_theme command to do things like add the axis legend, use different axes ranges, etc.)

Thank you again for your help!

1 Like