Skip to content

Ipynb widget parity - #742

Open
larsoner wants to merge 3 commits into
pyapp-kit:mainfrom
larsoner:ipynb-widget-parity
Open

Ipynb widget parity#742
larsoner wants to merge 3 commits into
pyapp-kit:mainfrom
larsoner:ipynb-widget-parity

Conversation

@larsoner

Copy link
Copy Markdown

More notebook parity work with Fable 5 (reviewed by me):

  1. RangeSlider
  2. FloatRangeSlider
  3. ProgressBar
  4. Image
  5. RadioButtons
  6. Timers via asyncio.call_later

larsoner and others added 3 commits August 24, 2026 15:34
…d RadioButtons

Widget-parity gaps with the Qt backend, each mapped onto the natural
ipywidgets equivalent:

- RangeSlider/FloatRangeSlider -> IntRangeSlider/FloatRangeSlider
- ProgressBar -> FloatProgress (no step trait, so step is tracked on
  the backend widget; the frontend ProgressBar manages step itself)
- Image -> ipywidgets.Image, encoding the RGBA array to PNG via PIL
  (the magicgui[image] extra, same as the frontend widget requires)
- RadioButtons -> ipywidgets.RadioButtons (vertical only for now;
  horizontal raises NotImplementedError)

The single-RadioButton -> ipywidgets.RadioButtons mapping is left
untouched (pre-existing semantic mismatch, needs its own discussion).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
_mgui_start_timer/_mgui_stop_timer are implemented with
asyncio.call_later on the kernel's running event loop, matching the Qt
backend's single-app-timer semantics (including single-shot).
_mgui_process_events becomes a no-op instead of raising: ipywidgets
updates are pushed over the kernel's comm channels as traits change,
so there is nothing to flush synchronously.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@codecov

codecov Bot commented Aug 24, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 97.77778% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 89.50%. Comparing base (2f08fb3) to head (8eedd85).

Files with missing lines Patch % Lines
src/magicgui/backends/_ipynb/widgets.py 96.55% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #742      +/-   ##
==========================================
+ Coverage   89.32%   89.50%   +0.17%     
==========================================
  Files          40       40              
  Lines        4835     4879      +44     
==========================================
+ Hits         4319     4367      +48     
+ Misses        516      512       -4     

☔ 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.

larsoner added a commit to larsoner/magicgui that referenced this pull request Aug 24, 2026
The namespace from runpy.run_path is discarded, so widgets created by
an example are garbage-collected at an arbitrary later point during
event processing.  A still-visible widget whose C++ side is deleted
mid-event-loop can receive a paintEvent during deletion, which PyQt5
escalates to a process abort:

    RuntimeError: wrapped C/C++ object of type QRangeSlider has been
    deleted

as seen intermittently on the windows-latest (3.12) pyqt5 job in
test_example[demo_widgets/range_slider.py] (e.g. the scheduled main
run on 2026-08-22, and both pyapp-kit#741 and pyapp-kit#742).

Close (hide) all top-level widgets first so nothing can paint, then
deleteLater() + sendPostedEvents(None, DeferredDelete) to destroy the
native widgets (and stop their timers) while hidden -- note that
processEvents() alone does NOT dispatch DeferredDelete events -- then
gc.collect() for anything left over.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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