pymmcore-plus 0.13.7__py3-none-any.whl → 0.15.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pymmcore_plus/__init__.py +2 -0
- pymmcore_plus/_accumulator.py +258 -0
- pymmcore_plus/_pymmcore.py +4 -2
- pymmcore_plus/core/__init__.py +34 -1
- pymmcore_plus/core/_constants.py +21 -3
- pymmcore_plus/core/_device.py +739 -19
- pymmcore_plus/core/_mmcore_plus.py +260 -47
- pymmcore_plus/core/events/_protocol.py +49 -34
- pymmcore_plus/core/events/_psygnal.py +2 -2
- pymmcore_plus/experimental/unicore/__init__.py +7 -1
- pymmcore_plus/experimental/unicore/_proxy.py +20 -3
- pymmcore_plus/experimental/unicore/core/__init__.py +0 -0
- pymmcore_plus/experimental/unicore/core/_sequence_buffer.py +318 -0
- pymmcore_plus/experimental/unicore/core/_unicore.py +1702 -0
- pymmcore_plus/experimental/unicore/devices/_camera.py +196 -0
- pymmcore_plus/experimental/unicore/devices/_device.py +54 -28
- pymmcore_plus/experimental/unicore/devices/_properties.py +8 -1
- pymmcore_plus/experimental/unicore/devices/_slm.py +82 -0
- pymmcore_plus/experimental/unicore/devices/_state.py +152 -0
- pymmcore_plus/mda/events/_protocol.py +8 -8
- pymmcore_plus/mda/handlers/_tensorstore_handler.py +3 -1
- {pymmcore_plus-0.13.7.dist-info → pymmcore_plus-0.15.0.dist-info}/METADATA +14 -37
- {pymmcore_plus-0.13.7.dist-info → pymmcore_plus-0.15.0.dist-info}/RECORD +26 -20
- pymmcore_plus/experimental/unicore/_unicore.py +0 -703
- {pymmcore_plus-0.13.7.dist-info → pymmcore_plus-0.15.0.dist-info}/WHEEL +0 -0
- {pymmcore_plus-0.13.7.dist-info → pymmcore_plus-0.15.0.dist-info}/entry_points.txt +0 -0
- {pymmcore_plus-0.13.7.dist-info → pymmcore_plus-0.15.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -13,9 +13,18 @@ from pathlib import Path
|
|
|
13
13
|
from re import Pattern
|
|
14
14
|
from textwrap import dedent
|
|
15
15
|
from threading import Thread
|
|
16
|
-
from typing import
|
|
16
|
+
from typing import (
|
|
17
|
+
TYPE_CHECKING,
|
|
18
|
+
Any,
|
|
19
|
+
Callable,
|
|
20
|
+
NamedTuple,
|
|
21
|
+
TypeVar,
|
|
22
|
+
cast,
|
|
23
|
+
overload,
|
|
24
|
+
)
|
|
17
25
|
|
|
18
26
|
from psygnal import SignalInstance
|
|
27
|
+
from typing_extensions import deprecated
|
|
19
28
|
|
|
20
29
|
import pymmcore_plus._pymmcore as pymmcore
|
|
21
30
|
from pymmcore_plus._logger import current_logfile, logger
|
|
@@ -23,6 +32,7 @@ from pymmcore_plus._util import find_micromanager, print_tabular_data
|
|
|
23
32
|
from pymmcore_plus.mda import MDAEngine, MDARunner, PMDAEngine
|
|
24
33
|
from pymmcore_plus.metadata.functions import summary_metadata
|
|
25
34
|
|
|
35
|
+
from . import _device
|
|
26
36
|
from ._adapter import DeviceAdapter
|
|
27
37
|
from ._config import Configuration
|
|
28
38
|
from ._config_group import ConfigGroup
|
|
@@ -31,26 +41,38 @@ from ._constants import (
|
|
|
31
41
|
DeviceInitializationState,
|
|
32
42
|
DeviceType,
|
|
33
43
|
FocusDirection,
|
|
44
|
+
Keyword,
|
|
34
45
|
PixelType,
|
|
35
46
|
PropertyType,
|
|
36
47
|
)
|
|
37
|
-
from ._device import Device
|
|
38
48
|
from ._metadata import Metadata
|
|
39
49
|
from ._property import DeviceProperty
|
|
40
50
|
from .events import CMMCoreSignaler, PCoreSignaler, _get_auto_core_callback_class
|
|
41
51
|
|
|
42
52
|
if TYPE_CHECKING:
|
|
43
53
|
from collections.abc import Iterable, Iterator, Sequence
|
|
44
|
-
from typing import Literal, TypedDict, Unpack
|
|
54
|
+
from typing import Literal, Never, TypeAlias, TypedDict, Union, Unpack
|
|
45
55
|
|
|
46
56
|
import numpy as np
|
|
57
|
+
from pymmcore import DeviceLabel
|
|
47
58
|
from useq import MDAEvent
|
|
48
59
|
|
|
49
60
|
from pymmcore_plus.mda._runner import SingleOutput
|
|
50
61
|
from pymmcore_plus.metadata.schema import SummaryMetaV1
|
|
51
62
|
|
|
52
63
|
_T = TypeVar("_T")
|
|
64
|
+
_DT = TypeVar("_DT", bound=_device.Device)
|
|
53
65
|
ListOrTuple = list[_T] | tuple[_T, ...]
|
|
66
|
+
DeviceTypesWithCurrent: TypeAlias = Union[
|
|
67
|
+
Literal[DeviceType.CameraDevice]
|
|
68
|
+
| Literal[DeviceType.ShutterDevice]
|
|
69
|
+
| Literal[DeviceType.StageDevice]
|
|
70
|
+
| Literal[DeviceType.XYStageDevice]
|
|
71
|
+
| Literal[DeviceType.AutoFocusDevice]
|
|
72
|
+
| Literal[DeviceType.SLMDevice]
|
|
73
|
+
| Literal[DeviceType.GalvoDevice]
|
|
74
|
+
| Literal[DeviceType.ImageProcessorDevice]
|
|
75
|
+
]
|
|
54
76
|
|
|
55
77
|
class PropertySchema(TypedDict, total=False):
|
|
56
78
|
"""JSON schema `dict` describing a device property."""
|
|
@@ -261,7 +283,7 @@ class CMMCorePlus(pymmcore.CMMCore):
|
|
|
261
283
|
|
|
262
284
|
self._events = _get_auto_core_callback_class()()
|
|
263
285
|
self._callback_relay = MMCallbackRelay(self.events)
|
|
264
|
-
|
|
286
|
+
super().registerCallback(self._callback_relay)
|
|
265
287
|
|
|
266
288
|
self._mda_runner = MDARunner()
|
|
267
289
|
self._mda_runner.set_engine(MDAEngine(self))
|
|
@@ -274,6 +296,24 @@ class CMMCorePlus(pymmcore.CMMCore):
|
|
|
274
296
|
self._weak_clean = weakref.WeakMethod(self.unloadAllDevices)
|
|
275
297
|
atexit.register(self._weak_clean)
|
|
276
298
|
|
|
299
|
+
@deprecated(
|
|
300
|
+
"registerCallback is disallowed in pymmcore-plus. Use .events instead."
|
|
301
|
+
)
|
|
302
|
+
def registerCallback(self, *_: Never) -> Never: # type: ignore[override]
|
|
303
|
+
"""*registerCallback is disallowed in pymmcore-plus!*
|
|
304
|
+
|
|
305
|
+
If you want to connect callbacks to events, use the
|
|
306
|
+
[`CMMCorePlus.events`][pymmcore_plus.CMMCorePlus.events] property instead.
|
|
307
|
+
""" # noqa
|
|
308
|
+
raise RuntimeError(
|
|
309
|
+
dedent("""
|
|
310
|
+
This method is disallowed in pymmcore-plus.
|
|
311
|
+
|
|
312
|
+
If you want to connect callbacks to events, use the
|
|
313
|
+
`CMMCorePlus.events` property instead.
|
|
314
|
+
""")
|
|
315
|
+
)
|
|
316
|
+
|
|
277
317
|
@property
|
|
278
318
|
def events(self) -> PCoreSignaler:
|
|
279
319
|
"""Signaler for core events.
|
|
@@ -287,14 +327,20 @@ class CMMCorePlus(pymmcore.CMMCore):
|
|
|
287
327
|
return self._events
|
|
288
328
|
|
|
289
329
|
def __repr__(self) -> str:
|
|
290
|
-
|
|
330
|
+
"""Return a string representation of the core object."""
|
|
331
|
+
ndevices = len(self.getLoadedDevices()) - 1
|
|
332
|
+
return f"<{type(self).__name__} at {hex(id(self))} with {ndevices} devices>"
|
|
291
333
|
|
|
292
334
|
def __del__(self) -> None:
|
|
293
335
|
if hasattr(self, "_weak_clean"):
|
|
294
336
|
atexit.unregister(self._weak_clean)
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
337
|
+
try:
|
|
338
|
+
super().registerCallback(None) # type: ignore
|
|
339
|
+
self.reset()
|
|
340
|
+
# clean up logging
|
|
341
|
+
self.setPrimaryLogFile("")
|
|
342
|
+
except Exception as e:
|
|
343
|
+
logger.exception("Error during CMMCorePlus.__del__(): %s", e)
|
|
298
344
|
|
|
299
345
|
# Re-implemented methods from the CMMCore API
|
|
300
346
|
|
|
@@ -334,7 +380,12 @@ class CMMCorePlus(pymmcore.CMMCore):
|
|
|
334
380
|
and the property Value has actually changed.
|
|
335
381
|
"""
|
|
336
382
|
with self._property_change_emission_ensured(stateDeviceLabel, STATE_PROPS):
|
|
337
|
-
|
|
383
|
+
try:
|
|
384
|
+
super().setStateLabel(stateDeviceLabel, stateLabel)
|
|
385
|
+
except RuntimeError as e: # pragma: no cover
|
|
386
|
+
state_labels = self.getStateLabels(stateDeviceLabel)
|
|
387
|
+
msg = f"{e}. Available Labels: {state_labels}"
|
|
388
|
+
raise RuntimeError(msg) from None
|
|
338
389
|
|
|
339
390
|
def setDeviceAdapterSearchPaths(self, paths: Sequence[str]) -> None:
|
|
340
391
|
"""Set the device adapter search paths.
|
|
@@ -377,6 +428,9 @@ class CMMCorePlus(pymmcore.CMMCore):
|
|
|
377
428
|
recognized by the specific plugin library. See
|
|
378
429
|
[`pymmcore.CMMCore.getAvailableDevices`][] for a list of valid device names.
|
|
379
430
|
"""
|
|
431
|
+
if str(label).lower() == Keyword.CoreDevice.value.lower(): # pragma: no cover
|
|
432
|
+
raise ValueError(f"Label {label!r} is reserved.")
|
|
433
|
+
|
|
380
434
|
try:
|
|
381
435
|
super().loadDevice(label, moduleName, deviceName)
|
|
382
436
|
except (RuntimeError, ValueError) as e:
|
|
@@ -670,9 +724,9 @@ class CMMCorePlus(pymmcore.CMMCore):
|
|
|
670
724
|
"""
|
|
671
725
|
md = Metadata()
|
|
672
726
|
if channel is not None and slice is not None:
|
|
673
|
-
img =
|
|
727
|
+
img = self.getLastImageMD(channel, slice, md)
|
|
674
728
|
else:
|
|
675
|
-
img =
|
|
729
|
+
img = self.getLastImageMD(md)
|
|
676
730
|
return (self.fixImage(img) if fix and not pymmcore.NANO else img, md)
|
|
677
731
|
|
|
678
732
|
@overload
|
|
@@ -714,7 +768,7 @@ class CMMCorePlus(pymmcore.CMMCore):
|
|
|
714
768
|
Image and metadata
|
|
715
769
|
"""
|
|
716
770
|
md = Metadata()
|
|
717
|
-
img =
|
|
771
|
+
img = self.popNextImageMD(channel, slice, md)
|
|
718
772
|
return (self.fixImage(img) if fix and not pymmcore.NANO else img, md)
|
|
719
773
|
|
|
720
774
|
def popNextImage(self, *, fix: bool = True) -> np.ndarray:
|
|
@@ -758,7 +812,7 @@ class CMMCorePlus(pymmcore.CMMCore):
|
|
|
758
812
|
will be reshaped to (w, h, n_components) using `fixImage`.
|
|
759
813
|
"""
|
|
760
814
|
md = Metadata()
|
|
761
|
-
img =
|
|
815
|
+
img = self.getNBeforeLastImageMD(n, md)
|
|
762
816
|
return self.fixImage(img) if fix and not pymmcore.NANO else img, md
|
|
763
817
|
|
|
764
818
|
def setConfig(self, groupName: str, configName: str) -> None:
|
|
@@ -850,7 +904,7 @@ class CMMCorePlus(pymmcore.CMMCore):
|
|
|
850
904
|
device_adapter: str | re.Pattern | None = ...,
|
|
851
905
|
*,
|
|
852
906
|
as_object: Literal[True] = ...,
|
|
853
|
-
) -> Iterator[Device]: ...
|
|
907
|
+
) -> Iterator[_device.Device]: ...
|
|
854
908
|
|
|
855
909
|
def iterDevices(
|
|
856
910
|
self,
|
|
@@ -859,7 +913,7 @@ class CMMCorePlus(pymmcore.CMMCore):
|
|
|
859
913
|
device_adapter: str | re.Pattern | None = None,
|
|
860
914
|
*,
|
|
861
915
|
as_object: bool = True,
|
|
862
|
-
) -> Iterator[Device] | Iterator[str]:
|
|
916
|
+
) -> Iterator[_device.Device] | Iterator[str]:
|
|
863
917
|
"""Iterate over currently loaded devices.
|
|
864
918
|
|
|
865
919
|
:sparkles: *This method is new in `CMMCorePlus`.*
|
|
@@ -912,7 +966,7 @@ class CMMCorePlus(pymmcore.CMMCore):
|
|
|
912
966
|
devices = [d for d in devices if ptrn.search(self.getDeviceLibrary(d))]
|
|
913
967
|
|
|
914
968
|
for dev in devices:
|
|
915
|
-
yield Device(dev, mmcore=self) if as_object else dev
|
|
969
|
+
yield _device.Device.create(dev, mmcore=self) if as_object else dev
|
|
916
970
|
|
|
917
971
|
@overload
|
|
918
972
|
def iterProperties(
|
|
@@ -1093,7 +1147,77 @@ class CMMCorePlus(pymmcore.CMMCore):
|
|
|
1093
1147
|
"""
|
|
1094
1148
|
return DeviceAdapter(library_name, mmcore=self)
|
|
1095
1149
|
|
|
1096
|
-
|
|
1150
|
+
@overload
|
|
1151
|
+
def getDeviceObject(
|
|
1152
|
+
self, device_label: str, device_type: Literal[DeviceType.Camera]
|
|
1153
|
+
) -> _device.CameraDevice: ...
|
|
1154
|
+
@overload
|
|
1155
|
+
def getDeviceObject(
|
|
1156
|
+
self, device_label: str, device_type: Literal[DeviceType.Stage]
|
|
1157
|
+
) -> _device.StageDevice: ...
|
|
1158
|
+
@overload
|
|
1159
|
+
def getDeviceObject(
|
|
1160
|
+
self, device_label: str, device_type: Literal[DeviceType.State]
|
|
1161
|
+
) -> _device.StateDevice: ...
|
|
1162
|
+
@overload
|
|
1163
|
+
def getDeviceObject(
|
|
1164
|
+
self, device_label: str, device_type: Literal[DeviceType.Shutter]
|
|
1165
|
+
) -> _device.ShutterDevice: ...
|
|
1166
|
+
@overload
|
|
1167
|
+
def getDeviceObject(
|
|
1168
|
+
self, device_label: str, device_type: Literal[DeviceType.XYStage]
|
|
1169
|
+
) -> _device.XYStageDevice: ...
|
|
1170
|
+
@overload
|
|
1171
|
+
def getDeviceObject(
|
|
1172
|
+
self, device_label: str, device_type: Literal[DeviceType.Serial]
|
|
1173
|
+
) -> _device.SerialDevice: ...
|
|
1174
|
+
@overload
|
|
1175
|
+
def getDeviceObject(
|
|
1176
|
+
self, device_label: str, device_type: Literal[DeviceType.Generic]
|
|
1177
|
+
) -> _device.GenericDevice: ...
|
|
1178
|
+
@overload
|
|
1179
|
+
def getDeviceObject(
|
|
1180
|
+
self, device_label: str, device_type: Literal[DeviceType.AutoFocus]
|
|
1181
|
+
) -> _device.AutoFocusDevice: ...
|
|
1182
|
+
@overload
|
|
1183
|
+
def getDeviceObject(
|
|
1184
|
+
self, device_label: str, device_type: Literal[DeviceType.ImageProcessor]
|
|
1185
|
+
) -> _device.ImageProcessorDevice: ...
|
|
1186
|
+
@overload
|
|
1187
|
+
def getDeviceObject(
|
|
1188
|
+
self, device_label: str, device_type: Literal[DeviceType.SignalIO]
|
|
1189
|
+
) -> _device.SignalIODevice: ...
|
|
1190
|
+
@overload
|
|
1191
|
+
def getDeviceObject(
|
|
1192
|
+
self, device_label: str, device_type: Literal[DeviceType.Magnifier]
|
|
1193
|
+
) -> _device.MagnifierDevice: ...
|
|
1194
|
+
@overload
|
|
1195
|
+
def getDeviceObject(
|
|
1196
|
+
self, device_label: str, device_type: Literal[DeviceType.SLM]
|
|
1197
|
+
) -> _device.SLMDevice: ...
|
|
1198
|
+
@overload
|
|
1199
|
+
def getDeviceObject(
|
|
1200
|
+
self, device_label: str, device_type: Literal[DeviceType.Hub]
|
|
1201
|
+
) -> _device.HubDevice: ...
|
|
1202
|
+
@overload
|
|
1203
|
+
def getDeviceObject(
|
|
1204
|
+
self, device_label: str, device_type: Literal[DeviceType.Galvo]
|
|
1205
|
+
) -> _device.GalvoDevice: ...
|
|
1206
|
+
@overload
|
|
1207
|
+
def getDeviceObject(
|
|
1208
|
+
self,
|
|
1209
|
+
device_label: Literal[Keyword.CoreDevice],
|
|
1210
|
+
device_type: Literal[DeviceType.Core],
|
|
1211
|
+
) -> _device.CoreDevice: ...
|
|
1212
|
+
@overload
|
|
1213
|
+
def getDeviceObject(
|
|
1214
|
+
self, device_label: str, device_type: DeviceType = ...
|
|
1215
|
+
) -> _device.Device: ...
|
|
1216
|
+
@overload
|
|
1217
|
+
def getDeviceObject(self, device_label: str, device_type: type[_DT]) -> _DT: ...
|
|
1218
|
+
def getDeviceObject(
|
|
1219
|
+
self, device_label: str, device_type: type[_DT] | DeviceType = DeviceType.Any
|
|
1220
|
+
) -> _DT | _device.Device:
|
|
1097
1221
|
"""Return a `Device` object bound to device_label on this core.
|
|
1098
1222
|
|
|
1099
1223
|
:sparkles: *This method is new in `CMMCorePlus`.*
|
|
@@ -1136,7 +1260,18 @@ class CMMCorePlus(pymmcore.CMMCore):
|
|
|
1136
1260
|
}
|
|
1137
1261
|
}
|
|
1138
1262
|
"""
|
|
1139
|
-
|
|
1263
|
+
dev = _device.Device.create(device_label, mmcore=self)
|
|
1264
|
+
if (isinstance(device_type, type) and not isinstance(dev, device_type)) or (
|
|
1265
|
+
isinstance(device_type, DeviceType)
|
|
1266
|
+
and device_type not in {DeviceType.Any, DeviceType.Unknown}
|
|
1267
|
+
and dev.type() != device_type
|
|
1268
|
+
):
|
|
1269
|
+
raise TypeError(
|
|
1270
|
+
f"{device_type!r} requested but device with label "
|
|
1271
|
+
f"{device_label!r} is a {dev.type()}."
|
|
1272
|
+
)
|
|
1273
|
+
|
|
1274
|
+
return dev
|
|
1140
1275
|
|
|
1141
1276
|
def getConfigGroupObject(
|
|
1142
1277
|
self, group_name: str, allow_missing: bool = False
|
|
@@ -1189,6 +1324,61 @@ class CMMCorePlus(pymmcore.CMMCore):
|
|
|
1189
1324
|
for group in self.getAvailableConfigGroups():
|
|
1190
1325
|
yield ConfigGroup(group, mmcore=self)
|
|
1191
1326
|
|
|
1327
|
+
def getCurrentDeviceOfType(
|
|
1328
|
+
self, device_type: DeviceTypesWithCurrent
|
|
1329
|
+
) -> DeviceLabel | Literal[""]:
|
|
1330
|
+
"""Return the current device of type `device_type`.
|
|
1331
|
+
|
|
1332
|
+
Only the following device types have a "current" device:
|
|
1333
|
+
- CameraDevice
|
|
1334
|
+
- ShutterDevice
|
|
1335
|
+
- StageDevice
|
|
1336
|
+
- XYStageDevice
|
|
1337
|
+
- AutoFocusDevice
|
|
1338
|
+
- SLMDevice
|
|
1339
|
+
- GalvoDevice
|
|
1340
|
+
- ImageProcessorDevice
|
|
1341
|
+
|
|
1342
|
+
Calling this method with any other device type will raise a `ValueError`.
|
|
1343
|
+
|
|
1344
|
+
:sparkles: *This method is new in `CMMCorePlus`.*
|
|
1345
|
+
|
|
1346
|
+
Parameters
|
|
1347
|
+
----------
|
|
1348
|
+
device_type : DeviceType
|
|
1349
|
+
The type of device to get the current device for.
|
|
1350
|
+
See [`DeviceType`][pymmcore_plus.DeviceType] for a list of device types.
|
|
1351
|
+
|
|
1352
|
+
Returns
|
|
1353
|
+
-------
|
|
1354
|
+
str
|
|
1355
|
+
The label of the current device of type `device_type`.
|
|
1356
|
+
If no device of that type is currently set, an empty string is returned.
|
|
1357
|
+
|
|
1358
|
+
Raises
|
|
1359
|
+
------
|
|
1360
|
+
ValueError
|
|
1361
|
+
If the core does not have the concept of a "current" device of the provided
|
|
1362
|
+
`device_type`.
|
|
1363
|
+
"""
|
|
1364
|
+
if device_type == DeviceType.CameraDevice:
|
|
1365
|
+
return self.getCameraDevice()
|
|
1366
|
+
if device_type == DeviceType.ShutterDevice:
|
|
1367
|
+
return self.getShutterDevice()
|
|
1368
|
+
if device_type == DeviceType.StageDevice:
|
|
1369
|
+
return self.getFocusDevice()
|
|
1370
|
+
if device_type == DeviceType.XYStageDevice:
|
|
1371
|
+
return self.getXYStageDevice()
|
|
1372
|
+
if device_type == DeviceType.AutoFocusDevice:
|
|
1373
|
+
return self.getAutoFocusDevice()
|
|
1374
|
+
if device_type == DeviceType.SLMDevice:
|
|
1375
|
+
return self.getSLMDevice()
|
|
1376
|
+
if device_type == DeviceType.GalvoDevice:
|
|
1377
|
+
return self.getGalvoDevice()
|
|
1378
|
+
if device_type == DeviceType.ImageProcessorDevice:
|
|
1379
|
+
return self.getImageProcessorDevice()
|
|
1380
|
+
raise ValueError(f"'Current' {device_type.name} is undefined. ")
|
|
1381
|
+
|
|
1192
1382
|
def getDeviceSchema(self, device_label: str) -> DeviceSchema:
|
|
1193
1383
|
"""Return JSON-schema describing device `device_label` and its properties.
|
|
1194
1384
|
|
|
@@ -1377,7 +1567,7 @@ class CMMCorePlus(pymmcore.CMMCore):
|
|
|
1377
1567
|
def setXYPosition(self, x: float, y: float, /) -> None: ...
|
|
1378
1568
|
@overload
|
|
1379
1569
|
def setXYPosition(self, xyStageLabel: str, x: float, y: float, /) -> None: ...
|
|
1380
|
-
def setXYPosition(self, *args:
|
|
1570
|
+
def setXYPosition(self, *args: Any) -> None:
|
|
1381
1571
|
"""Sets the position of the XY stage in microns.
|
|
1382
1572
|
|
|
1383
1573
|
**Why Override?** to store the last commanded stage position internally.
|
|
@@ -1386,10 +1576,10 @@ class CMMCorePlus(pymmcore.CMMCore):
|
|
|
1386
1576
|
label: str | None = None
|
|
1387
1577
|
x, y = cast("tuple[float, float]", args)
|
|
1388
1578
|
elif len(args) == 3:
|
|
1389
|
-
label, x, y = args
|
|
1579
|
+
label, x, y = args
|
|
1390
1580
|
else:
|
|
1391
1581
|
raise ValueError("Invalid number of arguments. Expected 2 or 3.")
|
|
1392
|
-
super().setXYPosition(*args)
|
|
1582
|
+
super().setXYPosition(*args)
|
|
1393
1583
|
self._last_xy_position[label] = (x, y)
|
|
1394
1584
|
|
|
1395
1585
|
def getZPosition(self) -> float:
|
|
@@ -1435,7 +1625,7 @@ class CMMCorePlus(pymmcore.CMMCore):
|
|
|
1435
1625
|
if autoshutter := self.getAutoShutter():
|
|
1436
1626
|
self.events.propertyChanged.emit(self.getShutterDevice(), "State", True)
|
|
1437
1627
|
try:
|
|
1438
|
-
|
|
1628
|
+
self._do_snap_image()
|
|
1439
1629
|
self.events.imageSnapped.emit()
|
|
1440
1630
|
finally:
|
|
1441
1631
|
if autoshutter:
|
|
@@ -1720,15 +1910,12 @@ class CMMCorePlus(pymmcore.CMMCore):
|
|
|
1720
1910
|
**Why Override?** To emit a `startContinuousSequenceAcquisition` event.
|
|
1721
1911
|
"""
|
|
1722
1912
|
self.events.continuousSequenceAcquisitionStarting.emit()
|
|
1723
|
-
|
|
1913
|
+
self._do_start_continuous_sequence_acquisition(intervalMs)
|
|
1724
1914
|
self.events.continuousSequenceAcquisitionStarted.emit()
|
|
1725
1915
|
|
|
1726
1916
|
@overload
|
|
1727
1917
|
def startSequenceAcquisition(
|
|
1728
|
-
self,
|
|
1729
|
-
numImages: int,
|
|
1730
|
-
intervalMs: float,
|
|
1731
|
-
stopOnOverflow: bool,
|
|
1918
|
+
self, numImages: int, intervalMs: float, stopOnOverflow: bool, /
|
|
1732
1919
|
) -> None: ...
|
|
1733
1920
|
|
|
1734
1921
|
@overload
|
|
@@ -1738,9 +1925,10 @@ class CMMCorePlus(pymmcore.CMMCore):
|
|
|
1738
1925
|
numImages: int,
|
|
1739
1926
|
intervalMs: float,
|
|
1740
1927
|
stopOnOverflow: bool,
|
|
1928
|
+
/,
|
|
1741
1929
|
) -> None: ...
|
|
1742
1930
|
|
|
1743
|
-
def startSequenceAcquisition(self, *args: Any
|
|
1931
|
+
def startSequenceAcquisition(self, *args: Any) -> None:
|
|
1744
1932
|
"""Starts streaming camera sequence acquisition.
|
|
1745
1933
|
|
|
1746
1934
|
This command does not block the calling thread for the duration of the
|
|
@@ -1749,18 +1937,16 @@ class CMMCorePlus(pymmcore.CMMCore):
|
|
|
1749
1937
|
**Why Override?** To emit a `startSequenceAcquisition` event.
|
|
1750
1938
|
"""
|
|
1751
1939
|
if len(args) == 3:
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1940
|
+
args = (self.getCameraDevice(), *args)
|
|
1941
|
+
elif len(args) != 4:
|
|
1942
|
+
raise ValueError(
|
|
1943
|
+
"startSequenceAcquisition requires either 3 or 4 arguments, "
|
|
1944
|
+
f"got {len(args)}."
|
|
1945
|
+
)
|
|
1756
1946
|
|
|
1757
|
-
self.events.sequenceAcquisitionStarting.emit(
|
|
1758
|
-
|
|
1759
|
-
)
|
|
1760
|
-
super().startSequenceAcquisition(*args, **kwargs)
|
|
1761
|
-
self.events.sequenceAcquisitionStarted.emit(
|
|
1762
|
-
cameraLabel, numImages, intervalMs, stopOnOverflow
|
|
1763
|
-
)
|
|
1947
|
+
self.events.sequenceAcquisitionStarting.emit(*args)
|
|
1948
|
+
self._do_start_sequence_acquisition(*args)
|
|
1949
|
+
self.events.sequenceAcquisitionStarted.emit(*args)
|
|
1764
1950
|
|
|
1765
1951
|
def stopSequenceAcquisition(self, cameraLabel: str | None = None) -> None:
|
|
1766
1952
|
"""Stops streaming camera sequence acquisition.
|
|
@@ -1769,13 +1955,32 @@ class CMMCorePlus(pymmcore.CMMCore):
|
|
|
1769
1955
|
|
|
1770
1956
|
**Why Override?** To emit a `stopSequenceAcquisition` event.
|
|
1771
1957
|
"""
|
|
1772
|
-
if cameraLabel is None:
|
|
1773
|
-
super().stopSequenceAcquisition()
|
|
1774
|
-
else:
|
|
1775
|
-
super().stopSequenceAcquisition(cameraLabel)
|
|
1776
1958
|
cameraLabel = cameraLabel or super().getCameraDevice()
|
|
1959
|
+
self._do_stop_sequence_acquisition(cameraLabel)
|
|
1777
1960
|
self.events.sequenceAcquisitionStopped.emit(cameraLabel)
|
|
1778
1961
|
|
|
1962
|
+
# here for ease of overriding in Unicore ---------------------
|
|
1963
|
+
|
|
1964
|
+
def _do_snap_image(self) -> None:
|
|
1965
|
+
super().snapImage()
|
|
1966
|
+
|
|
1967
|
+
def _do_start_sequence_acquisition(
|
|
1968
|
+
self, cameraLabel: str, numImages: int, intervalMs: float, stopOnOverflow: bool
|
|
1969
|
+
) -> None:
|
|
1970
|
+
super().startSequenceAcquisition(
|
|
1971
|
+
cameraLabel, numImages, intervalMs, stopOnOverflow
|
|
1972
|
+
)
|
|
1973
|
+
|
|
1974
|
+
def _do_start_continuous_sequence_acquisition(self, intervalMs: float) -> None:
|
|
1975
|
+
"""Starts the actual continuous sequence acquisition process."""
|
|
1976
|
+
super().startContinuousSequenceAcquisition(intervalMs)
|
|
1977
|
+
|
|
1978
|
+
def _do_stop_sequence_acquisition(self, cameraLabel: str) -> None:
|
|
1979
|
+
"""Stops the actual sequence acquisition process."""
|
|
1980
|
+
super().stopSequenceAcquisition(cameraLabel)
|
|
1981
|
+
|
|
1982
|
+
# end of Unicore helpers ---------------------
|
|
1983
|
+
|
|
1779
1984
|
def setAutoFocusOffset(self, offset: float) -> None:
|
|
1780
1985
|
"""Applies offset the one-shot focusing device.
|
|
1781
1986
|
|
|
@@ -1990,21 +2195,29 @@ class CMMCorePlus(pymmcore.CMMCore):
|
|
|
1990
2195
|
return list(xs), list(ys), list(ws), list(hs)
|
|
1991
2196
|
|
|
1992
2197
|
@overload
|
|
1993
|
-
def setROI(self, x: int, y: int, width: int, height: int) -> None: ...
|
|
2198
|
+
def setROI(self, x: int, y: int, width: int, height: int, /) -> None: ...
|
|
1994
2199
|
|
|
1995
2200
|
@overload
|
|
1996
|
-
def setROI(
|
|
2201
|
+
def setROI(
|
|
2202
|
+
self, label: str, x: int, y: int, width: int, height: int, /
|
|
2203
|
+
) -> None: ...
|
|
1997
2204
|
|
|
1998
|
-
def setROI(self, *args: Any
|
|
2205
|
+
def setROI(self, *args: Any) -> None:
|
|
1999
2206
|
"""Set the camera Region of Interest (ROI).
|
|
2000
2207
|
|
|
2001
2208
|
**Why Override?** To emit a `roiSet` event.
|
|
2002
2209
|
"""
|
|
2003
|
-
super().setROI(*args, **kwargs)
|
|
2004
2210
|
if len(args) == 4:
|
|
2005
2211
|
args = (super().getCameraDevice(), *args)
|
|
2212
|
+
self._do_set_roi(*args)
|
|
2006
2213
|
self.events.roiSet.emit(*args)
|
|
2007
2214
|
|
|
2215
|
+
# here for ease of overriding in Unicore
|
|
2216
|
+
|
|
2217
|
+
def _do_set_roi(self, label: str, x: int, y: int, width: int, height: int) -> None:
|
|
2218
|
+
"""Internal method to set the ROI for a specific camera device."""
|
|
2219
|
+
super().setROI(label, x, y, width, height)
|
|
2220
|
+
|
|
2008
2221
|
def setChannelGroup(self, channelGroup: str) -> None:
|
|
2009
2222
|
"""Specifies the group determining the channel selection.
|
|
2010
2223
|
|