Skip to content

Devices and Properties

pymmcore-plus offers two classes that provide a more object-oriented interface to common operations and queries performed on devices and their properties.

In the original CMMCore API, there are a lot of methods that accept a deviceLabel string as the first argument (and perhaps additional arguments) and query something about that device (e.g. getDeviceLibrary, getDeviceType, waitForDevice, etc...). In pymmcore-plus, the Device class acts as a "view" onto a specific device. You pass the deviceLabel once (to the constructor), and these become methods that no longer need it.

Similarly, there are many methods in the CMMCore API that require both a device label and a device property name, and query or modify that property (e.g. isPropertySequenceable, getProperty, isPropertyReadOnly, etc...). Here, the DeviceProperty class acts as a "view" onto a specific device property, with an object-oriented interface to these methods.

from pymmcore_plus import CMMCorePlus

core = CMMCorePlus()
core.loadSystemConfiguration()  # demo config

camera = core.getDeviceObject("Camera")
print(camera.type(), camera.library())  # Camera DemoCamera

exposure = camera.getPropertyObject("Exposure")
exposure.value = 42
print(exposure.value, exposure.isReadOnly(), exposure.range())  # 42.0 False (0.0, 10000.0)

To loop over the loaded devices or properties (optionally filtered by type, label, etc...), use iterDevices and iterProperties, which yield these same objects:

from pymmcore_plus import DeviceType

[dev.label for dev in core.iterDevices(DeviceType.Stage)]  # ['Z']
[prop.name for prop in core.iterProperties(device_label="Camera", has_limits=True)]

See Device, DeviceProperty and DeviceAdapter (the same idea, for a device adapter library) for the full API.

Property info

DeviceProperty.dict() returns everything about a property at once. Continuing the example above:

>>> exposure.dict()
{'valid': True,
 'value': 42.0,
 'type': 'number',
 'device_type': 'CameraDevice',
 'read_only': False,
 'sequenceable': False,
 'sequence_max_length': None,
 'pre_init': False,
 'range': (0.0, 10000.0),
 'allowed_values': ()}

(range is None for properties without limits, and allowed_values lists the choices for properties that have them, like the camera's Binning.)

Temporarily changing settings

You may want to temporarily set something on the core (for example core.setAutoShutter(False) when writing an MDA engine) and put it back afterwards. For this case you can use the convenience method CMMCorePlus.setContext:

with core.setContext(exposure=100, autoShutter=False):
    print(core.getExposure(), core.getAutoShutter())  # 100.0 False
    core.snapImage()

print(core.getExposure(), core.getAutoShutter())  # back to the original values

This works for any name that has matching get<Name> and set<Name> methods on the core (e.g. exposure, autoShutter, shutterOpen, channelGroup, etc...). The original values are restored when the with block exits, even if an exception was raised inside it.