Skip to content

feat: add Python bridge device support (UniCore support)#86

Open
tlambert03 wants to merge 45 commits into
pymmcore-plus:mainfrom
tlambert03:unicore
Open

feat: add Python bridge device support (UniCore support)#86
tlambert03 wants to merge 45 commits into
pymmcore-plus:mainfrom
tlambert03:unicore

Conversation

@tlambert03

@tlambert03 tlambert03 commented Apr 3, 2026

Copy link
Copy Markdown
Member

@marktsuchida, here's what claude came up with. I think it looks pretty clean actually. All of the conveniences that hide the ugliness of the full C++ device API could be done in pymmcore-plus with ready-to-go ABCs that build off of a device-specific BridgeABC that has all the mandatory methods required on the C side.

the tests show the basic idea in practice

any high level thoughts about cleanup/safety/etc?

autogenerated docs at: https://github.com/tlambert03/pymmcore-nano/tree/unicore/docs/bridge-devices.md

New methods on CMMCore

  • loadPyDeviceAdapter(adapter_name: str, adapter: DeviceAdapter) -> None — register a multi-device adapter; CMMCore takes ownership.
  • loadPyDevice(label: str, py_device, type: DeviceType) -> None — convenience that wraps a single pre-instantiated Python device in a one-off adapter and loads it.

After loading, the standard CMMCore flow (getAvailableDevices, loadDevice, initializeDevice, …) works against the Python device.

New module pymmcore_nano.protocols

@runtime_checkable Protocol classes documenting what each device-type bridge expects on the Python side. All inherit from PyDevice, which requires:

  • initialize_bridge(create_property: CreatePropertyFn, notify: DeviceCallbacks) -> None
  • shutdown() -> None
  • busy() -> bool

New classes on pymmcore_nano

  • DeviceAdapter
    A collection of Python device classes that acts as an MM device adapter library.

    • DeviceAdapter() — construct an empty adapter
    • add_device_class(name: str, device_class: type, device_type: DeviceType, description: str) -> None
  • PropertyHandle
    Returned by the create_property() callable passed to initialize_bridge(). Used for dynamic constraint updates after a property is registered (valid for the device's entire lifetime).

    • set_limits(lower, upper)
    • set_allowed_values(values)
    • set_sequence_max_length(max_length)
  • DeviceCallbacks
    Notification channel handed to initialize_bridge() so a Python device can reach back into CMMCore (the equivalent of calling GetCoreCallback()->OnXyz() in C++).

    • on_property_changed(name, value)
    • on_properties_changed()
    • on_stage_position_changed(pos)
    • on_xy_stage_position_changed(x, y)
    • on_exposure_changed(exposure)
    • on_shutter_open_changed(open)
    • set_position_label(pos, label)
    • log_message(msg, debug_only=False)
    • acq_finished(status_code=0)

Behavior change worth noting

  • CMMCore.popNextImage() now uses the metadata fast-path (popNextImageMD) to avoid Python-bridge round trips for PyBridgeCamera cameras — same return type, ~5% slower for pure-C++ cameras, big win for Python cameras.
  • setSLMImage / loadSLMSequence now validate against getSLMNumberOfComponents(), accepting 3- or 4-channel RGB(A) images instead of only 3-channel.

@codecov

codecov Bot commented Apr 3, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 62.62976% with 324 lines in your changes missing coverage. Please review.
✅ Project coverage is 72.34%. Comparing base (41a3942) to head (78d77e3).

Files with missing lines Patch % Lines
src/bridge_devices.h 62.45% 209 Missing and 76 partials ⚠️
src/pymmcore_nano/protocols.py 0.00% 34 Missing ⚠️
src/_pymmcore_nano.cc 92.95% 2 Missing and 3 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main      #86      +/-   ##
==========================================
- Coverage   77.65%   72.34%   -5.32%     
==========================================
  Files           2        4       +2     
  Lines        1253     2108     +855     
  Branches      973     1266     +293     
==========================================
+ Hits          973     1525     +552     
- Misses         80      302     +222     
- Partials      200      281      +81     

☔ View full report in Codecov by Sentry.
📢 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.

…PyBridgeShutter, and PyBridgeAdapter destructors
…updates and update related protocols and tests
… conversions are explicit in SetImage methods
@tlambert03

Copy link
Copy Markdown
Member Author

well... it's grown quite a bit now. but i think the pattern is working well! and it's getting pretty fully featured, including dynamic property changes, device notification callbacks, buffer insertions, etc...

@tlambert03 tlambert03 changed the title feat: add Python bridge device support with Camera/Shutter devices feat: add Python bridge device support (UniCore support) May 14, 2026
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