Skip to content

fix(plot): a trajectory is animated whatever %matplotlib says - #860

Merged
petercorke merged 2 commits into
mainfrom
fix/plot-animation-render-mode
Oct 19, 2026
Merged

petercorke merged 2 commits into
mainfrom
fix/plot-animation-render-mode

Conversation

@petercorke

@petercorke petercorke commented Oct 19, 2026 •

Copy link
Copy Markdown
Owner

robot.plot(q) of a trajectory in a notebook with %matplotlib ipympl showed a blank figure, a long pause, and then the final frame. It now animates, whatever %matplotlib says, with no argument needed.

Cause

An ipympl figure updates only when the browser sends a draw message and the kernel handles it. While a cell is running (the animation loop in plot()) the kernel cannot, so only the first and the last frames were ever drawn. The render mode that was chosen for ipympl was notebook-widget for every plot. The code of the widget branch of step() has not changed since the render modes were written (2026-05-29); this is a limitation of the widget mode, not a recent regression.

Measured in JupyterLite 4.6.4 and ipympl 0.10 with real Chromium (Playwright), sampling the output every 0.5 s during a 100-frame trajectory: 1 distinct image while the cell ran with the old code, and the same with a synchronous canvas.draw(), with flush_events(), and with plt.pause(), and with ipykernel 6.29.5, 7.2.0 and 7.4.0. With the existing notebook-inline render mode: 13 of 13 samples different.

What changes

  • plot() passes animation=True to the backend when q has more than one row (only to backends with a render mode, so not Swift).
  • _resolve_render_mode(render_mode, animation) chooses notebook-inline for an animation under ipympl. A single pose is still notebook-widget, an interactive figure; the inline backend is unchanged; an explicit render_mode= is always used as given. The docstrings say so.
  • PyPlot2 had its own copy of _resolve_render_mode and _isnotebook; it now imports the ones in PyPlot, so the 2D backend gets the same choice.

Checked in the browser with the fix and no render_mode: %matplotlib ipympl with a trajectory animates (13 of 13), %matplotlib inline with a trajectory animates (13 of 13), and %matplotlib ipympl with one pose is still a live widget canvas.

tests/test_render_mode.py (14 tests) covers the choice for each backend and animation value, an explicit mode, the 2D backend sharing the function, and what plot() passes for a trajectory, a single pose, and an explicit mode. Full suite: only the 20 test_servo_half_turn failures from the stale compiled extension in my worktree; mypy clean. tests/test_notebook_animation.py (4 tests) runs a real IPython kernel and checks, from the messages it sends, that a trajectory plotted under %matplotlib ipympl and under %matplotlib inline pushes a different frame for every step while the cell runs, that one pose under ipympl is still an interactive widget, and that an explicit render_mode is honoured. It fails without the fix (assert 0 >= 6 frames for ipympl) and takes about 6 s. It does not include a browser. ipympl is added to the dev extra for it; the test skips if ipykernel or ipympl is missing.

Not tried in VS Code's notebook UI, only in JupyterLab. Written with AI assistance (Claude Code).

🤖 Generated with Claude Code

In a notebook with the ipympl backend a figure updates only when the kernel handles
a message from the browser, which it cannot do while the cell is running, so
robot.plot(q) of a trajectory showed the first and the last frame only. plot() now
asks for an animation, and the render mode for an animation under ipympl is the
inline one. A single pose is still an interactive widget.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@petercorke
petercorke enabled auto-merge (squash) October 19, 2026 21:01
@codacy-production

codacy-production Bot commented Oct 19, 2026 •

Copy link
Copy Markdown

Not up to standards ⛔

🔴 Issues 1 critical · 19 high · 1 medium

Alerts:
⚠ 21 issues (≤ 0 issues of at least minor severity)

Results:
21 new issues

Category Results
Security 19 high
1 critical
1 medium

View in Codacy

🟢 Metrics 23 complexity · -6 duplication

Metric Results
Complexity 23
Duplication -6

View in Codacy

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.

… a real IPython kernel

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@petercorke
petercorke merged commit b853cb8 into main Oct 19, 2026
23 of 24 checks passed
@github-actions github-actions Bot mentioned this pull request Oct 19, 2026
@codecov

codecov Bot commented Oct 19, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 0% with 8 lines in your changes missing coverage. Please review.
✅ Project coverage is 0.00%. Comparing base (742b6ed) to head (8306713).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
src/roboticstoolbox/backends/PyPlot/PyPlot.py 0.00% 3 Missing ⚠️
src/roboticstoolbox/robot/BaseRobot.py 0.00% 3 Missing ⚠️
src/roboticstoolbox/backends/PyPlot/PyPlot2.py 0.00% 2 Missing ⚠️
Additional details and impacted files
@@          Coverage Diff          @@
##            main    #860   +/-   ##
=====================================
  Coverage   0.00%   0.00%           
=====================================
  Files        146     146           
  Lines      16867   16846   -21     
=====================================
+ Misses     16867   16846   -21     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant