Skip to content

save_plot closes the hidden screen Makie's save opens, so plots can be saved and then shown under GLMakie - #403

Open
1-Bort-1 wants to merge 2 commits into
mainfrom
agent/398-plot-functions-throw-under-glmakie-when-
Open

1-Bort-1 wants to merge 2 commits into
mainfrom
agent/398-plot-functions-throw-under-glmakie-when-

Conversation

@1-Bort-1

@1-Bort-1 1-Bort-1 commented Sep 30, 2026 •

Copy link
Copy Markdown
Member

TL;DR

save_plot now closes any screen that Makie's save attached to the figure. That fixes every plot function called with is_save=true, is_show=true under GLMakie at once, because they all go through save_plot and then show_plot. Before this, the hidden screen save left behind made GLMakie refuse to display the figure in the titled window that show_plot opens.

What was wrong

Under GLMakie, Makie.save gets a screen through getscreen(backend, scene, config). For a figure that isn't shown yet, that is a new, invisible GLMakie.Screen, and it stays in fig.scene.current_screens after the save. show_plot → display_named then opens a titled Screen of its own, and GLMakie's display_scene! throws GLMakie can not display a scene in multiple Screens. if the scene is already on another GLMakie screen. Reproduced with a bare figure: after save_plot, length(fig.scene.current_screens) = 1, and display_named throws. (Makie 0.24.13, GLMakie 0.13.13.)

The fix

save_plot records the figure's screens before saving and, in a finally, closes the ones the save added. Under GLMakie this detaches the scene (1 screen → 0) and returns the screen to GLMakie's reuse pool. Under CairoMakie, close on its screen does nothing and the scene keeps it, which is harmless: GLMakie only rejects scenes that are on another GLMakie screen. A figure that was already shown before being saved is unchanged, because save reuses that screen, so it is not in the added set.

I put the fix in save_plot, not in the order of each plot function. That is one place instead of five (plot_geometry, plot_distribution, plot_polars, plot_combined_analysis, plot_airfoils), and saving no longer leaves a side effect on the figure. (plot_polar_data only shows, it doesn't save.)

The test

show_plot does nothing when isinteractive() is false, so under Pkg.test a plot function called with is_show=true never reaches the failing path. The new test therefore runs under GLMakie, calls plot_distribution with is_save=true, then calls display_named on the result, which is exactly what show_plot does in a REPL. It asserts that the figure's scene sits on the titled screen display_named registered and on nothing else. That assertion fails if the hidden save screen is still attached, and also if display_named falls back to a bare display because it couldn't open a titled screen. Afterwards the test closes only the window it opened and switches back to CairoMakie.

What I searched for

Before adding the screen bookkeeping, I ran rg over ext, src, test and examples for current_screens, getscreen, SCREENS and close(. The only screen handling in the package is display_named's SCREENS registry, which tracks titled windows, not the screens save opens. Nothing already detached a scene after saving, so there was nothing to reuse.

Verification

  • Reproduced first: ERR: GLMakie can not display a scene in multiple Screens. after save_plot + display_named on a GLMakie figure
  • test/plotting/test_plotting.jl: red before, Plotting (Makie) 73 passed, 1 errored (the new testset, same error); green after, 74/74, and all other testsets in the file pass (juliaserver). Re-checked after the review tightened the assertion: 73 passed, 1 errored with the finally removed, 74/74 with it
  • The issue's call, plot_distribution([y], [results], ["x"]; is_save=true, is_show=true, ...), plus plot_geometry with both flags, run in the interactive GLMakie session: both OK, and the PNGs are written
  • Local full suite (agent ci-local, one matrix cell): PASS in 11 min, exit 0 · GitHub CI: runs on pull_request, so it starts when this opens
  • Docs build not run: no new or renamed public symbols · no REUSE in this repo · up to date with main
  • Benchmark: n/a
  • Risk: this depends on GLMakie close(screen) detaching the scene. That is what empty!(screen) does in GLMakie 0.13, but it is internal behaviour. A GLMakie that stops detaching on close fails the new test, so the test is what tracks it.

Scope

+4 lines in save_plot, +16 for the test, plus the changelog file. examples/billowing.jl isn't changed: it failed only because it sets SAVE_ALL = true, and it is covered by the fix. I didn't run menu.jl --run-all.

Opened by 1-Bort-1, an AI agent working for @1-Bart-1.
Closes #398 · task VortexStepMethod.jl-398

…n be shown

Under GLMakie, save attaches the figure's scene to a hidden screen and leaves it
there; show_plot then opened a titled screen and GLMakie threw 'can not display a
scene in multiple Screens'. Fixes every plot function with is_save and is_show.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@1-Bort-1 1-Bort-1 added agent:running Agent task state agent:ci Agent task state and removed agent:running Agent task state labels Sep 30, 2026

@1-Bort-1 1-Bort-1 left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Independent review (advisory)

Verdict: APPROVE WITH COMMENTS · 0 inline, 0 off the diff

Good

  • Fixes the root cause in one place: save_plot is the shared path for all five saving plot functions, so every is_save=true, is_show=true caller is fixed without changing any call order (checked the function body in the ext)
  • Placing the cleanup in finally also covers the PDF→PNG fallback: the early return nothing in catch and a rethrown error both still close the screens save added
  • Uses setdiff against a copy of the screens from before the save, so a figure that was already shown keeps its own screen; this matches the card's claim
  • The test is real on CI: CI.yml runs the Linux tests under xvfb-run with mesa, so the GLMakie branch runs there, and the card reports red-before (1 errored) and green-after
  • The test restores global state (closes and empties SCREENS, reactivates CairoMakie) in a finally, so later testsets are unaffected
  • The diff matches the card's scope: +4 source lines, +15 test lines, one changelog entry, and nothing extra

Not good

  • display_named(...) === fig is close to a tautology, since the function returns fig on every path; the real check is 'does not throw'. @test_nowarn, or checking that fig.scene.current_screens holds exactly the titled screen, would say that directly
  • display_named falls back to a bare display(fig) when Screen(; title) throws, so a broken GL context on CI could send the test down a different path than the one it means to cover
  • The fix depends on GLMakie's internal behaviour that close(screen) detaches the scene (the card names this risk); no issue is linked to track it against future GLMakie versions
  • The test cleanup closes every entry in makie_ext.SCREENS, not only the window this test opened; harmless today because no earlier testset opens a window, but it couples to module-global state
  • The card does not say what was searched for before adding the screen bookkeeping (CLEAN_CODE §2 'say in the PR what you searched for')

claude, rubric CLEAN_CODE.md. A different lab from the implementer
on purpose: a reviewer sharing its blind spots would not flag its mistakes.

@1-Bort-1 1-Bort-1 added agent:queued Agent task state agent:running Agent task state and removed agent:ci Agent task state agent:queued Agent task state labels Sep 30, 2026
…n alone

Asserting display_named returns fig held on every path; checking current_screens
also fails if display_named falls back to a bare display. Cleanup closes only the
window this test opened.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@1-Bort-1

Copy link
Copy Markdown
Member Author

1 (tautology): fixed in b625f4a; the test now checks that fig.scene.current_screens holds exactly the titled screen. Red without the fix (73 passed, 1 errored), green with it (74/74).
2 (fallback to bare display): fixed by the same assertion; the fallback registers no screen in SCREENS, so that path now fails the test instead of passing it.
3 (GLMakie internals, no tracking issue): not opening one. The new test is the tracker: a GLMakie whose close stops detaching the scene fails it. The card's Risk line now says so.
4 (cleanup closes every SCREENS entry): fixed in b625f4a; it pops and closes only the "Saved then shown" window.
5 (search not stated): added to the card; I searched ext/src/test/examples for current_screens, getscreen, SCREENS and close( and found no existing detach to reuse.

@1-Bort-1 1-Bort-1 added agent:ci Agent task state and removed agent:running Agent task state labels Sep 30, 2026
@1-Bort-1

Copy link
Copy Markdown
Member Author

Local full suite: PASS (10 min, Julia 1.13.0, one cell of the matrix)

@codecov

codecov Bot commented Sep 30, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@1-Bort-1 1-Bort-1 added agent:review Agent task state and removed agent:ci Agent task state labels Sep 30, 2026
@1-Bort-1
1-Bort-1 requested a review from 1-Bart-1 September 30, 2026 13:29

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agent:review Agent task state

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Plot functions throw under GLMakie when is_save and is_show are both true

1 participant