pymmcore-plus 0.14.0__tar.gz → 0.15.0__tar.gz
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-0.14.0 → pymmcore_plus-0.15.0}/PKG-INFO +1 -1
- {pymmcore_plus-0.14.0 → pymmcore_plus-0.15.0}/pyproject.toml +3 -4
- {pymmcore_plus-0.14.0 → pymmcore_plus-0.15.0}/src/pymmcore_plus/_pymmcore.py +4 -2
- {pymmcore_plus-0.14.0 → pymmcore_plus-0.15.0}/src/pymmcore_plus/core/_constants.py +21 -3
- {pymmcore_plus-0.14.0 → pymmcore_plus-0.15.0}/src/pymmcore_plus/core/_mmcore_plus.py +96 -40
- {pymmcore_plus-0.14.0 → pymmcore_plus-0.15.0}/src/pymmcore_plus/core/events/_protocol.py +49 -34
- {pymmcore_plus-0.14.0 → pymmcore_plus-0.15.0}/src/pymmcore_plus/core/events/_psygnal.py +2 -2
- {pymmcore_plus-0.14.0 → pymmcore_plus-0.15.0}/src/pymmcore_plus/experimental/unicore/__init__.py +7 -1
- {pymmcore_plus-0.14.0 → pymmcore_plus-0.15.0}/src/pymmcore_plus/experimental/unicore/_proxy.py +20 -3
- pymmcore_plus-0.15.0/src/pymmcore_plus/experimental/unicore/core/_sequence_buffer.py +318 -0
- pymmcore_plus-0.15.0/src/pymmcore_plus/experimental/unicore/core/_unicore.py +1702 -0
- pymmcore_plus-0.15.0/src/pymmcore_plus/experimental/unicore/devices/_camera.py +196 -0
- {pymmcore_plus-0.14.0 → pymmcore_plus-0.15.0}/src/pymmcore_plus/experimental/unicore/devices/_device.py +54 -28
- {pymmcore_plus-0.14.0 → pymmcore_plus-0.15.0}/src/pymmcore_plus/experimental/unicore/devices/_properties.py +8 -1
- pymmcore_plus-0.15.0/src/pymmcore_plus/experimental/unicore/devices/_slm.py +82 -0
- pymmcore_plus-0.15.0/src/pymmcore_plus/experimental/unicore/devices/_state.py +152 -0
- {pymmcore_plus-0.14.0 → pymmcore_plus-0.15.0}/src/pymmcore_plus/mda/events/_protocol.py +8 -8
- pymmcore_plus-0.15.0/src/pymmcore_plus/py.typed +0 -0
- {pymmcore_plus-0.14.0 → pymmcore_plus-0.15.0}/tests/conftest.py +11 -8
- pymmcore_plus-0.15.0/tests/test_bench.py +191 -0
- {pymmcore_plus-0.14.0 → pymmcore_plus-0.15.0}/tests/test_cli.py +4 -2
- {pymmcore_plus-0.14.0 → pymmcore_plus-0.15.0}/tests/test_events.py +5 -4
- {pymmcore_plus-0.14.0 → pymmcore_plus-0.15.0}/tests/test_sequencing.py +8 -1
- pymmcore_plus-0.15.0/tests/unicore/conftest.py +13 -0
- pymmcore_plus-0.15.0/tests/unicore/test_camera.py +266 -0
- pymmcore_plus-0.15.0/tests/unicore/test_sequence_buffer.py +577 -0
- pymmcore_plus-0.15.0/tests/unicore/test_slm.py +337 -0
- pymmcore_plus-0.15.0/tests/unicore/test_state.py +362 -0
- pymmcore_plus-0.14.0/src/pymmcore_plus/experimental/unicore/_unicore.py +0 -703
- pymmcore_plus-0.14.0/tests/test_bench.py +0 -89
- {pymmcore_plus-0.14.0 → pymmcore_plus-0.15.0}/.gitignore +0 -0
- {pymmcore_plus-0.14.0 → pymmcore_plus-0.15.0}/LICENSE +0 -0
- {pymmcore_plus-0.14.0 → pymmcore_plus-0.15.0}/README.md +0 -0
- {pymmcore_plus-0.14.0 → pymmcore_plus-0.15.0}/src/pymmcore_plus/__init__.py +0 -0
- {pymmcore_plus-0.14.0 → pymmcore_plus-0.15.0}/src/pymmcore_plus/_accumulator.py +0 -0
- {pymmcore_plus-0.14.0 → pymmcore_plus-0.15.0}/src/pymmcore_plus/_benchmark.py +0 -0
- {pymmcore_plus-0.14.0 → pymmcore_plus-0.15.0}/src/pymmcore_plus/_build.py +0 -0
- {pymmcore_plus-0.14.0 → pymmcore_plus-0.15.0}/src/pymmcore_plus/_cli.py +0 -0
- {pymmcore_plus-0.14.0 → pymmcore_plus-0.15.0}/src/pymmcore_plus/_logger.py +0 -0
- {pymmcore_plus-0.14.0 → pymmcore_plus-0.15.0}/src/pymmcore_plus/_util.py +0 -0
- {pymmcore_plus-0.14.0 → pymmcore_plus-0.15.0}/src/pymmcore_plus/core/__init__.py +0 -0
- {pymmcore_plus-0.14.0 → pymmcore_plus-0.15.0}/src/pymmcore_plus/core/_adapter.py +0 -0
- {pymmcore_plus-0.14.0 → pymmcore_plus-0.15.0}/src/pymmcore_plus/core/_config.py +0 -0
- {pymmcore_plus-0.14.0 → pymmcore_plus-0.15.0}/src/pymmcore_plus/core/_config_group.py +0 -0
- {pymmcore_plus-0.14.0 → pymmcore_plus-0.15.0}/src/pymmcore_plus/core/_device.py +0 -0
- {pymmcore_plus-0.14.0 → pymmcore_plus-0.15.0}/src/pymmcore_plus/core/_metadata.py +0 -0
- {pymmcore_plus-0.14.0 → pymmcore_plus-0.15.0}/src/pymmcore_plus/core/_property.py +0 -0
- {pymmcore_plus-0.14.0 → pymmcore_plus-0.15.0}/src/pymmcore_plus/core/_sequencing.py +0 -0
- {pymmcore_plus-0.14.0 → pymmcore_plus-0.15.0}/src/pymmcore_plus/core/events/__init__.py +0 -0
- {pymmcore_plus-0.14.0 → pymmcore_plus-0.15.0}/src/pymmcore_plus/core/events/_device_signal_view.py +0 -0
- {pymmcore_plus-0.14.0 → pymmcore_plus-0.15.0}/src/pymmcore_plus/core/events/_norm_slot.py +0 -0
- {pymmcore_plus-0.14.0 → pymmcore_plus-0.15.0}/src/pymmcore_plus/core/events/_prop_event_mixin.py +0 -0
- {pymmcore_plus-0.14.0 → pymmcore_plus-0.15.0}/src/pymmcore_plus/core/events/_qsignals.py +0 -0
- {pymmcore_plus-0.14.0 → pymmcore_plus-0.15.0}/src/pymmcore_plus/experimental/__init__.py +0 -0
- {pymmcore_plus-0.14.0 → pymmcore_plus-0.15.0}/src/pymmcore_plus/experimental/unicore/_device_manager.py +0 -0
- {pymmcore_plus-0.14.0/src/pymmcore_plus/experimental/unicore/devices → pymmcore_plus-0.15.0/src/pymmcore_plus/experimental/unicore/core}/__init__.py +0 -0
- /pymmcore_plus-0.14.0/src/pymmcore_plus/py.typed → /pymmcore_plus-0.15.0/src/pymmcore_plus/experimental/unicore/devices/__init__.py +0 -0
- {pymmcore_plus-0.14.0 → pymmcore_plus-0.15.0}/src/pymmcore_plus/experimental/unicore/devices/_stage.py +0 -0
- {pymmcore_plus-0.14.0 → pymmcore_plus-0.15.0}/src/pymmcore_plus/install.py +0 -0
- {pymmcore_plus-0.14.0 → pymmcore_plus-0.15.0}/src/pymmcore_plus/mda/__init__.py +0 -0
- {pymmcore_plus-0.14.0 → pymmcore_plus-0.15.0}/src/pymmcore_plus/mda/_engine.py +0 -0
- {pymmcore_plus-0.14.0 → pymmcore_plus-0.15.0}/src/pymmcore_plus/mda/_protocol.py +0 -0
- {pymmcore_plus-0.14.0 → pymmcore_plus-0.15.0}/src/pymmcore_plus/mda/_runner.py +0 -0
- {pymmcore_plus-0.14.0 → pymmcore_plus-0.15.0}/src/pymmcore_plus/mda/_thread_relay.py +0 -0
- {pymmcore_plus-0.14.0 → pymmcore_plus-0.15.0}/src/pymmcore_plus/mda/events/__init__.py +0 -0
- {pymmcore_plus-0.14.0 → pymmcore_plus-0.15.0}/src/pymmcore_plus/mda/events/_psygnal.py +0 -0
- {pymmcore_plus-0.14.0 → pymmcore_plus-0.15.0}/src/pymmcore_plus/mda/events/_qsignals.py +0 -0
- {pymmcore_plus-0.14.0 → pymmcore_plus-0.15.0}/src/pymmcore_plus/mda/handlers/_5d_writer_base.py +0 -0
- {pymmcore_plus-0.14.0 → pymmcore_plus-0.15.0}/src/pymmcore_plus/mda/handlers/__init__.py +0 -0
- {pymmcore_plus-0.14.0 → pymmcore_plus-0.15.0}/src/pymmcore_plus/mda/handlers/_img_sequence_writer.py +0 -0
- {pymmcore_plus-0.14.0 → pymmcore_plus-0.15.0}/src/pymmcore_plus/mda/handlers/_ome_tiff_writer.py +0 -0
- {pymmcore_plus-0.14.0 → pymmcore_plus-0.15.0}/src/pymmcore_plus/mda/handlers/_ome_zarr_writer.py +0 -0
- {pymmcore_plus-0.14.0 → pymmcore_plus-0.15.0}/src/pymmcore_plus/mda/handlers/_tensorstore_handler.py +0 -0
- {pymmcore_plus-0.14.0 → pymmcore_plus-0.15.0}/src/pymmcore_plus/mda/handlers/_util.py +0 -0
- {pymmcore_plus-0.14.0 → pymmcore_plus-0.15.0}/src/pymmcore_plus/metadata/__init__.py +0 -0
- {pymmcore_plus-0.14.0 → pymmcore_plus-0.15.0}/src/pymmcore_plus/metadata/functions.py +0 -0
- {pymmcore_plus-0.14.0 → pymmcore_plus-0.15.0}/src/pymmcore_plus/metadata/schema.py +0 -0
- {pymmcore_plus-0.14.0 → pymmcore_plus-0.15.0}/src/pymmcore_plus/metadata/serialize.py +0 -0
- {pymmcore_plus-0.14.0 → pymmcore_plus-0.15.0}/src/pymmcore_plus/mocks.py +0 -0
- {pymmcore_plus-0.14.0 → pymmcore_plus-0.15.0}/src/pymmcore_plus/model/__init__.py +0 -0
- {pymmcore_plus-0.14.0 → pymmcore_plus-0.15.0}/src/pymmcore_plus/model/_config_file.py +0 -0
- {pymmcore_plus-0.14.0 → pymmcore_plus-0.15.0}/src/pymmcore_plus/model/_config_group.py +0 -0
- {pymmcore_plus-0.14.0 → pymmcore_plus-0.15.0}/src/pymmcore_plus/model/_core_device.py +0 -0
- {pymmcore_plus-0.14.0 → pymmcore_plus-0.15.0}/src/pymmcore_plus/model/_core_link.py +0 -0
- {pymmcore_plus-0.14.0 → pymmcore_plus-0.15.0}/src/pymmcore_plus/model/_device.py +0 -0
- {pymmcore_plus-0.14.0 → pymmcore_plus-0.15.0}/src/pymmcore_plus/model/_microscope.py +0 -0
- {pymmcore_plus-0.14.0 → pymmcore_plus-0.15.0}/src/pymmcore_plus/model/_pixel_size_config.py +0 -0
- {pymmcore_plus-0.14.0 → pymmcore_plus-0.15.0}/src/pymmcore_plus/model/_property.py +0 -0
- {pymmcore_plus-0.14.0 → pymmcore_plus-0.15.0}/src/pymmcore_plus/seq_tester.py +0 -0
- {pymmcore_plus-0.14.0 → pymmcore_plus-0.15.0}/tests/__init__.py +0 -0
- {pymmcore_plus-0.14.0 → pymmcore_plus-0.15.0}/tests/io/test_image_sequence_writer.py +0 -0
- {pymmcore_plus-0.14.0 → pymmcore_plus-0.15.0}/tests/io/test_ome_tiff.py +0 -0
- {pymmcore_plus-0.14.0 → pymmcore_plus-0.15.0}/tests/io/test_zarr_writers.py +0 -0
- {pymmcore_plus-0.14.0 → pymmcore_plus-0.15.0}/tests/local_config.cfg +0 -0
- {pymmcore_plus-0.14.0 → pymmcore_plus-0.15.0}/tests/test_accumulators.py +0 -0
- {pymmcore_plus-0.14.0 → pymmcore_plus-0.15.0}/tests/test_adapter_class.py +0 -0
- {pymmcore_plus-0.14.0 → pymmcore_plus-0.15.0}/tests/test_config_group_class.py +0 -0
- {pymmcore_plus-0.14.0 → pymmcore_plus-0.15.0}/tests/test_core.py +0 -0
- {pymmcore_plus-0.14.0 → pymmcore_plus-0.15.0}/tests/test_device_class.py +0 -0
- {pymmcore_plus-0.14.0 → pymmcore_plus-0.15.0}/tests/test_mda.py +0 -0
- {pymmcore_plus-0.14.0 → pymmcore_plus-0.15.0}/tests/test_metadata.py +0 -0
- {pymmcore_plus-0.14.0 → pymmcore_plus-0.15.0}/tests/test_misc.py +0 -0
- {pymmcore_plus-0.14.0 → pymmcore_plus-0.15.0}/tests/test_model.py +0 -0
- {pymmcore_plus-0.14.0 → pymmcore_plus-0.15.0}/tests/test_pixel_config_class.py +0 -0
- {pymmcore_plus-0.14.0 → pymmcore_plus-0.15.0}/tests/test_property_class.py +0 -0
- {pymmcore_plus-0.14.0 → pymmcore_plus-0.15.0}/tests/test_slm_image.py +0 -0
- {pymmcore_plus-0.14.0 → pymmcore_plus-0.15.0}/tests/test_thread_relay.py +0 -0
- {pymmcore_plus-0.14.0 → pymmcore_plus-0.15.0}/tests/unicore/test_unicore.py +0 -0
- {pymmcore_plus-0.14.0 → pymmcore_plus-0.15.0}/tests/unicore/test_xy_stage.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pymmcore-plus
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.15.0
|
|
4
4
|
Summary: pymmcore superset providing improved APIs, event handling, and a pure python acquisition engine
|
|
5
5
|
Project-URL: Source, https://github.com/pymmcore-plus/pymmcore-plus
|
|
6
6
|
Project-URL: Tracker, https://github.com/pymmcore-plus/pymmcore-plus/issues
|
|
@@ -79,15 +79,14 @@ test = [
|
|
|
79
79
|
"pymmcore >=11.5.0.73",
|
|
80
80
|
]
|
|
81
81
|
test-codspeed = [{ include-group = "test" }, "pytest-codspeed >=3.2.0"]
|
|
82
|
-
test-qt = ["pytest-qt >=4.4", "qtpy >=2"]
|
|
82
|
+
test-qt = [{ include-group = 'test' }, "pytest-qt >=4.4", "qtpy >=2"]
|
|
83
83
|
PyQt6 = [{ include-group = 'test-qt' }, "pymmcore-plus[PyQt6]"]
|
|
84
84
|
PySide6 = [{ include-group = 'test-qt' }, "pymmcore-plus[PySide6]"]
|
|
85
85
|
PyQt5 = [{ include-group = 'test-qt' }, "pymmcore-plus[PyQt5]"]
|
|
86
86
|
PySide2 = [{ include-group = 'test-qt' }, "pymmcore-plus[PySide2]"]
|
|
87
87
|
dev = [
|
|
88
88
|
# { include-group = "docs" },
|
|
89
|
-
{ include-group = "
|
|
90
|
-
"PyQt6 >=6.4.2",
|
|
89
|
+
{ include-group = "PyQt6" },
|
|
91
90
|
"ipython>=8.18.1",
|
|
92
91
|
"pdbpp>=0.11.6 ; sys_platform != 'win32'",
|
|
93
92
|
"mypy>=1.14.1",
|
|
@@ -158,7 +157,7 @@ ignore = [
|
|
|
158
157
|
]
|
|
159
158
|
|
|
160
159
|
[tool.ruff.lint.per-file-ignores]
|
|
161
|
-
"tests
|
|
160
|
+
"tests/**/*.py" = ["D", "SLF"]
|
|
162
161
|
"examples/*.py" = ["D"]
|
|
163
162
|
"_cli.py" = ["B008"]
|
|
164
163
|
"docs/*.py" = ["A", "D"]
|
|
@@ -24,7 +24,9 @@ class VersionInfo(NamedTuple):
|
|
|
24
24
|
minor: int
|
|
25
25
|
micro: int
|
|
26
26
|
device_interface: int
|
|
27
|
-
build: int
|
|
27
|
+
build: int = 0
|
|
28
28
|
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
# pass no more than 5 parts to VersionInfo
|
|
31
|
+
numbers = re.findall(r"\d+", __version__)[:5]
|
|
32
|
+
version_info = VersionInfo(*(int(i) for i in numbers))
|
|
@@ -13,6 +13,7 @@ import pymmcore_plus._pymmcore as pymmcore
|
|
|
13
13
|
class Keyword(str, Enum):
|
|
14
14
|
Name = pymmcore.g_Keyword_Name
|
|
15
15
|
Description = pymmcore.g_Keyword_Description
|
|
16
|
+
|
|
16
17
|
CameraName = pymmcore.g_Keyword_CameraName
|
|
17
18
|
CameraID = pymmcore.g_Keyword_CameraID
|
|
18
19
|
CameraChannelName = pymmcore.g_Keyword_CameraChannelName
|
|
@@ -31,6 +32,7 @@ class Keyword(str, Enum):
|
|
|
31
32
|
Offset = pymmcore.g_Keyword_Offset
|
|
32
33
|
CCDTemperature = pymmcore.g_Keyword_CCDTemperature
|
|
33
34
|
CCDTemperatureSetPoint = pymmcore.g_Keyword_CCDTemperatureSetPoint
|
|
35
|
+
|
|
34
36
|
State = pymmcore.g_Keyword_State
|
|
35
37
|
Label = pymmcore.g_Keyword_Label
|
|
36
38
|
Position = pymmcore.g_Keyword_Position
|
|
@@ -69,12 +71,15 @@ class Keyword(str, Enum):
|
|
|
69
71
|
HubID = pymmcore.g_Keyword_HubID
|
|
70
72
|
|
|
71
73
|
# image annotations
|
|
72
|
-
|
|
73
|
-
|
|
74
|
+
Metadata_CameraLabel = pymmcore.g_Keyword_Metadata_CameraLabel
|
|
75
|
+
Metadata_Exposure = pymmcore.g_Keyword_Metadata_Exposure
|
|
76
|
+
Metadata_Height = pymmcore.g_Keyword_Metadata_Height
|
|
74
77
|
Metadata_ImageNumber = pymmcore.g_Keyword_Metadata_ImageNumber
|
|
75
78
|
Metadata_ROI_X = pymmcore.g_Keyword_Metadata_ROI_X
|
|
76
79
|
Metadata_ROI_Y = pymmcore.g_Keyword_Metadata_ROI_Y
|
|
80
|
+
Metadata_Score = pymmcore.g_Keyword_Metadata_Score
|
|
77
81
|
Metadata_TimeInCore = pymmcore.g_Keyword_Metadata_TimeInCore
|
|
82
|
+
Metadata_Width = pymmcore.g_Keyword_Metadata_Width
|
|
78
83
|
|
|
79
84
|
def __str__(self) -> str:
|
|
80
85
|
return str(self.value)
|
|
@@ -165,7 +170,9 @@ class PropertyType(IntEnum):
|
|
|
165
170
|
String = pymmcore.String
|
|
166
171
|
Float = pymmcore.Float
|
|
167
172
|
Integer = pymmcore.Integer
|
|
173
|
+
|
|
168
174
|
Boolean = auto() # not supported in pymmcore
|
|
175
|
+
Enum = auto() # not supported in pymmcore
|
|
169
176
|
|
|
170
177
|
def to_python(self) -> type | None:
|
|
171
178
|
return {0: None, 1: str, 2: float, 3: int}[self]
|
|
@@ -183,7 +190,16 @@ class PropertyType(IntEnum):
|
|
|
183
190
|
if value is None:
|
|
184
191
|
return PropertyType.Undef
|
|
185
192
|
if isinstance(value, str):
|
|
186
|
-
|
|
193
|
+
if value.lower() in ("int", "integer"):
|
|
194
|
+
return PropertyType.Integer
|
|
195
|
+
if value.lower() in ("float", "double"):
|
|
196
|
+
return PropertyType.Float
|
|
197
|
+
if value.lower() in ("bool", "boolean"):
|
|
198
|
+
return PropertyType.Boolean
|
|
199
|
+
if value.lower() in ("string", "str"):
|
|
200
|
+
return PropertyType.String
|
|
201
|
+
if value.lower() in ("enum", "enumeration"):
|
|
202
|
+
return PropertyType.Enum
|
|
187
203
|
if isinstance(value, type):
|
|
188
204
|
if value is float:
|
|
189
205
|
return PropertyType.Float
|
|
@@ -193,6 +209,8 @@ class PropertyType(IntEnum):
|
|
|
193
209
|
return PropertyType.String
|
|
194
210
|
elif value is bool:
|
|
195
211
|
return PropertyType.Boolean
|
|
212
|
+
elif issubclass(value, Enum):
|
|
213
|
+
return PropertyType.Enum
|
|
196
214
|
|
|
197
215
|
raise TypeError(
|
|
198
216
|
f"Property type must be a PropertyType enum member, "
|
|
@@ -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
|
|
@@ -42,7 +51,7 @@ from .events import CMMCoreSignaler, PCoreSignaler, _get_auto_core_callback_clas
|
|
|
42
51
|
|
|
43
52
|
if TYPE_CHECKING:
|
|
44
53
|
from collections.abc import Iterable, Iterator, Sequence
|
|
45
|
-
from typing import Literal, TypeAlias, TypedDict, Union, Unpack
|
|
54
|
+
from typing import Literal, Never, TypeAlias, TypedDict, Union, Unpack
|
|
46
55
|
|
|
47
56
|
import numpy as np
|
|
48
57
|
from pymmcore import DeviceLabel
|
|
@@ -274,7 +283,7 @@ class CMMCorePlus(pymmcore.CMMCore):
|
|
|
274
283
|
|
|
275
284
|
self._events = _get_auto_core_callback_class()()
|
|
276
285
|
self._callback_relay = MMCallbackRelay(self.events)
|
|
277
|
-
|
|
286
|
+
super().registerCallback(self._callback_relay)
|
|
278
287
|
|
|
279
288
|
self._mda_runner = MDARunner()
|
|
280
289
|
self._mda_runner.set_engine(MDAEngine(self))
|
|
@@ -287,6 +296,24 @@ class CMMCorePlus(pymmcore.CMMCore):
|
|
|
287
296
|
self._weak_clean = weakref.WeakMethod(self.unloadAllDevices)
|
|
288
297
|
atexit.register(self._weak_clean)
|
|
289
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
|
+
|
|
290
317
|
@property
|
|
291
318
|
def events(self) -> PCoreSignaler:
|
|
292
319
|
"""Signaler for core events.
|
|
@@ -300,14 +327,20 @@ class CMMCorePlus(pymmcore.CMMCore):
|
|
|
300
327
|
return self._events
|
|
301
328
|
|
|
302
329
|
def __repr__(self) -> str:
|
|
303
|
-
|
|
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>"
|
|
304
333
|
|
|
305
334
|
def __del__(self) -> None:
|
|
306
335
|
if hasattr(self, "_weak_clean"):
|
|
307
336
|
atexit.unregister(self._weak_clean)
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
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)
|
|
311
344
|
|
|
312
345
|
# Re-implemented methods from the CMMCore API
|
|
313
346
|
|
|
@@ -691,9 +724,9 @@ class CMMCorePlus(pymmcore.CMMCore):
|
|
|
691
724
|
"""
|
|
692
725
|
md = Metadata()
|
|
693
726
|
if channel is not None and slice is not None:
|
|
694
|
-
img =
|
|
727
|
+
img = self.getLastImageMD(channel, slice, md)
|
|
695
728
|
else:
|
|
696
|
-
img =
|
|
729
|
+
img = self.getLastImageMD(md)
|
|
697
730
|
return (self.fixImage(img) if fix and not pymmcore.NANO else img, md)
|
|
698
731
|
|
|
699
732
|
@overload
|
|
@@ -735,7 +768,7 @@ class CMMCorePlus(pymmcore.CMMCore):
|
|
|
735
768
|
Image and metadata
|
|
736
769
|
"""
|
|
737
770
|
md = Metadata()
|
|
738
|
-
img =
|
|
771
|
+
img = self.popNextImageMD(channel, slice, md)
|
|
739
772
|
return (self.fixImage(img) if fix and not pymmcore.NANO else img, md)
|
|
740
773
|
|
|
741
774
|
def popNextImage(self, *, fix: bool = True) -> np.ndarray:
|
|
@@ -779,7 +812,7 @@ class CMMCorePlus(pymmcore.CMMCore):
|
|
|
779
812
|
will be reshaped to (w, h, n_components) using `fixImage`.
|
|
780
813
|
"""
|
|
781
814
|
md = Metadata()
|
|
782
|
-
img =
|
|
815
|
+
img = self.getNBeforeLastImageMD(n, md)
|
|
783
816
|
return self.fixImage(img) if fix and not pymmcore.NANO else img, md
|
|
784
817
|
|
|
785
818
|
def setConfig(self, groupName: str, configName: str) -> None:
|
|
@@ -1534,7 +1567,7 @@ class CMMCorePlus(pymmcore.CMMCore):
|
|
|
1534
1567
|
def setXYPosition(self, x: float, y: float, /) -> None: ...
|
|
1535
1568
|
@overload
|
|
1536
1569
|
def setXYPosition(self, xyStageLabel: str, x: float, y: float, /) -> None: ...
|
|
1537
|
-
def setXYPosition(self, *args:
|
|
1570
|
+
def setXYPosition(self, *args: Any) -> None:
|
|
1538
1571
|
"""Sets the position of the XY stage in microns.
|
|
1539
1572
|
|
|
1540
1573
|
**Why Override?** to store the last commanded stage position internally.
|
|
@@ -1543,10 +1576,10 @@ class CMMCorePlus(pymmcore.CMMCore):
|
|
|
1543
1576
|
label: str | None = None
|
|
1544
1577
|
x, y = cast("tuple[float, float]", args)
|
|
1545
1578
|
elif len(args) == 3:
|
|
1546
|
-
label, x, y = args
|
|
1579
|
+
label, x, y = args
|
|
1547
1580
|
else:
|
|
1548
1581
|
raise ValueError("Invalid number of arguments. Expected 2 or 3.")
|
|
1549
|
-
super().setXYPosition(*args)
|
|
1582
|
+
super().setXYPosition(*args)
|
|
1550
1583
|
self._last_xy_position[label] = (x, y)
|
|
1551
1584
|
|
|
1552
1585
|
def getZPosition(self) -> float:
|
|
@@ -1592,7 +1625,7 @@ class CMMCorePlus(pymmcore.CMMCore):
|
|
|
1592
1625
|
if autoshutter := self.getAutoShutter():
|
|
1593
1626
|
self.events.propertyChanged.emit(self.getShutterDevice(), "State", True)
|
|
1594
1627
|
try:
|
|
1595
|
-
|
|
1628
|
+
self._do_snap_image()
|
|
1596
1629
|
self.events.imageSnapped.emit()
|
|
1597
1630
|
finally:
|
|
1598
1631
|
if autoshutter:
|
|
@@ -1877,15 +1910,12 @@ class CMMCorePlus(pymmcore.CMMCore):
|
|
|
1877
1910
|
**Why Override?** To emit a `startContinuousSequenceAcquisition` event.
|
|
1878
1911
|
"""
|
|
1879
1912
|
self.events.continuousSequenceAcquisitionStarting.emit()
|
|
1880
|
-
|
|
1913
|
+
self._do_start_continuous_sequence_acquisition(intervalMs)
|
|
1881
1914
|
self.events.continuousSequenceAcquisitionStarted.emit()
|
|
1882
1915
|
|
|
1883
1916
|
@overload
|
|
1884
1917
|
def startSequenceAcquisition(
|
|
1885
|
-
self,
|
|
1886
|
-
numImages: int,
|
|
1887
|
-
intervalMs: float,
|
|
1888
|
-
stopOnOverflow: bool,
|
|
1918
|
+
self, numImages: int, intervalMs: float, stopOnOverflow: bool, /
|
|
1889
1919
|
) -> None: ...
|
|
1890
1920
|
|
|
1891
1921
|
@overload
|
|
@@ -1895,9 +1925,10 @@ class CMMCorePlus(pymmcore.CMMCore):
|
|
|
1895
1925
|
numImages: int,
|
|
1896
1926
|
intervalMs: float,
|
|
1897
1927
|
stopOnOverflow: bool,
|
|
1928
|
+
/,
|
|
1898
1929
|
) -> None: ...
|
|
1899
1930
|
|
|
1900
|
-
def startSequenceAcquisition(self, *args: Any
|
|
1931
|
+
def startSequenceAcquisition(self, *args: Any) -> None:
|
|
1901
1932
|
"""Starts streaming camera sequence acquisition.
|
|
1902
1933
|
|
|
1903
1934
|
This command does not block the calling thread for the duration of the
|
|
@@ -1906,18 +1937,16 @@ class CMMCorePlus(pymmcore.CMMCore):
|
|
|
1906
1937
|
**Why Override?** To emit a `startSequenceAcquisition` event.
|
|
1907
1938
|
"""
|
|
1908
1939
|
if len(args) == 3:
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
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
|
+
)
|
|
1913
1946
|
|
|
1914
|
-
self.events.sequenceAcquisitionStarting.emit(
|
|
1915
|
-
|
|
1916
|
-
)
|
|
1917
|
-
super().startSequenceAcquisition(*args, **kwargs)
|
|
1918
|
-
self.events.sequenceAcquisitionStarted.emit(
|
|
1919
|
-
cameraLabel, numImages, intervalMs, stopOnOverflow
|
|
1920
|
-
)
|
|
1947
|
+
self.events.sequenceAcquisitionStarting.emit(*args)
|
|
1948
|
+
self._do_start_sequence_acquisition(*args)
|
|
1949
|
+
self.events.sequenceAcquisitionStarted.emit(*args)
|
|
1921
1950
|
|
|
1922
1951
|
def stopSequenceAcquisition(self, cameraLabel: str | None = None) -> None:
|
|
1923
1952
|
"""Stops streaming camera sequence acquisition.
|
|
@@ -1926,13 +1955,32 @@ class CMMCorePlus(pymmcore.CMMCore):
|
|
|
1926
1955
|
|
|
1927
1956
|
**Why Override?** To emit a `stopSequenceAcquisition` event.
|
|
1928
1957
|
"""
|
|
1929
|
-
if cameraLabel is None:
|
|
1930
|
-
super().stopSequenceAcquisition()
|
|
1931
|
-
else:
|
|
1932
|
-
super().stopSequenceAcquisition(cameraLabel)
|
|
1933
1958
|
cameraLabel = cameraLabel or super().getCameraDevice()
|
|
1959
|
+
self._do_stop_sequence_acquisition(cameraLabel)
|
|
1934
1960
|
self.events.sequenceAcquisitionStopped.emit(cameraLabel)
|
|
1935
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
|
+
|
|
1936
1984
|
def setAutoFocusOffset(self, offset: float) -> None:
|
|
1937
1985
|
"""Applies offset the one-shot focusing device.
|
|
1938
1986
|
|
|
@@ -2147,21 +2195,29 @@ class CMMCorePlus(pymmcore.CMMCore):
|
|
|
2147
2195
|
return list(xs), list(ys), list(ws), list(hs)
|
|
2148
2196
|
|
|
2149
2197
|
@overload
|
|
2150
|
-
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: ...
|
|
2151
2199
|
|
|
2152
2200
|
@overload
|
|
2153
|
-
def setROI(
|
|
2201
|
+
def setROI(
|
|
2202
|
+
self, label: str, x: int, y: int, width: int, height: int, /
|
|
2203
|
+
) -> None: ...
|
|
2154
2204
|
|
|
2155
|
-
def setROI(self, *args: Any
|
|
2205
|
+
def setROI(self, *args: Any) -> None:
|
|
2156
2206
|
"""Set the camera Region of Interest (ROI).
|
|
2157
2207
|
|
|
2158
2208
|
**Why Override?** To emit a `roiSet` event.
|
|
2159
2209
|
"""
|
|
2160
|
-
super().setROI(*args, **kwargs)
|
|
2161
2210
|
if len(args) == 4:
|
|
2162
2211
|
args = (super().getCameraDevice(), *args)
|
|
2212
|
+
self._do_set_roi(*args)
|
|
2163
2213
|
self.events.roiSet.emit(*args)
|
|
2164
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
|
+
|
|
2165
2221
|
def setChannelGroup(self, channelGroup: str) -> None:
|
|
2166
2222
|
"""Specifies the group determining the channel selection.
|
|
2167
2223
|
|
|
@@ -1,4 +1,15 @@
|
|
|
1
|
-
from
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import (
|
|
4
|
+
Any,
|
|
5
|
+
Callable,
|
|
6
|
+
ClassVar,
|
|
7
|
+
Protocol,
|
|
8
|
+
overload,
|
|
9
|
+
runtime_checkable,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
from typing_extensions import Self
|
|
2
13
|
|
|
3
14
|
|
|
4
15
|
@runtime_checkable
|
|
@@ -12,7 +23,7 @@ class PSignalInstance(Protocol):
|
|
|
12
23
|
def connect(self, slot: Callable) -> Any:
|
|
13
24
|
"""Connect slot to this signal."""
|
|
14
25
|
|
|
15
|
-
def disconnect(self, slot:
|
|
26
|
+
def disconnect(self, slot: Callable | None = None) -> Any:
|
|
16
27
|
"""Disconnect slot from this signal.
|
|
17
28
|
|
|
18
29
|
If `None`, all slots should be disconnected.
|
|
@@ -23,14 +34,18 @@ class PSignalInstance(Protocol):
|
|
|
23
34
|
|
|
24
35
|
|
|
25
36
|
@runtime_checkable
|
|
26
|
-
class
|
|
37
|
+
class PSignal(Protocol):
|
|
27
38
|
"""Descriptor that returns a signal instance."""
|
|
28
39
|
|
|
29
|
-
|
|
40
|
+
@overload
|
|
41
|
+
def __get__(self, instance: None, owner: type, /) -> Self: ...
|
|
42
|
+
@overload
|
|
43
|
+
def __get__(self, instance: Any, owner: type | None = None, /) -> Any: ...
|
|
44
|
+
def __get__(
|
|
45
|
+
self, instance: Any, owner: type | None = ..., /
|
|
46
|
+
) -> PSignalInstance | PSignal:
|
|
30
47
|
"""Returns the signal instance for this descriptor."""
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
PSignal = Union[PSignalDescriptor, PSignalInstance]
|
|
48
|
+
...
|
|
34
49
|
|
|
35
50
|
|
|
36
51
|
@runtime_checkable
|
|
@@ -91,102 +106,102 @@ class PCoreSignaler(Protocol):
|
|
|
91
106
|
"""
|
|
92
107
|
|
|
93
108
|
# native MMCore callback events
|
|
94
|
-
propertiesChanged: PSignal
|
|
109
|
+
propertiesChanged: ClassVar[PSignal]
|
|
95
110
|
"""Emits with no arguments when properties have changed."""
|
|
96
|
-
propertyChanged: PSignal
|
|
111
|
+
propertyChanged: ClassVar[PSignal]
|
|
97
112
|
"""Emits `(name: str, : propName: str, propValue: str)` when a specific property has changed.""" # noqa: E501
|
|
98
|
-
channelGroupChanged: PSignal
|
|
113
|
+
channelGroupChanged: ClassVar[PSignal]
|
|
99
114
|
"""Emits `(newChannelGroupName: str)` when a channel group has changed."""
|
|
100
|
-
configGroupChanged: PSignal
|
|
115
|
+
configGroupChanged: ClassVar[PSignal]
|
|
101
116
|
"""Emits `(groupName: str, newConfigName: str)` when a config group has changed."""
|
|
102
|
-
systemConfigurationLoaded: PSignal
|
|
117
|
+
systemConfigurationLoaded: ClassVar[PSignal]
|
|
103
118
|
"""Emits with no arguments when the system configuration has been loaded."""
|
|
104
|
-
pixelSizeChanged: PSignal
|
|
119
|
+
pixelSizeChanged: ClassVar[PSignal]
|
|
105
120
|
"""Emits `(newPixelSizeUm: float)` when the pixel size has changed."""
|
|
106
|
-
pixelSizeAffineChanged: PSignal
|
|
121
|
+
pixelSizeAffineChanged: ClassVar[PSignal]
|
|
107
122
|
"""Emits `(float, float, float, float, float, float)` when the pixel size affine has changed.""" # noqa: E501
|
|
108
|
-
stagePositionChanged: PSignal
|
|
123
|
+
stagePositionChanged: ClassVar[PSignal]
|
|
109
124
|
"""Emits `(name: str, pos: float)` when a stage position has changed."""
|
|
110
|
-
XYStagePositionChanged: PSignal
|
|
125
|
+
XYStagePositionChanged: ClassVar[PSignal]
|
|
111
126
|
"""Emits `(name: str, xpos: float, ypos: float)` when an XY stage position has changed.""" # noqa: E501
|
|
112
|
-
xYStagePositionChanged: PSignal # alias
|
|
113
|
-
exposureChanged: PSignal
|
|
127
|
+
xYStagePositionChanged: ClassVar[PSignal] # alias
|
|
128
|
+
exposureChanged: ClassVar[PSignal]
|
|
114
129
|
"""Emits `(name: str, newExposure: float)` when an exposure has changed."""
|
|
115
|
-
SLMExposureChanged: PSignal
|
|
130
|
+
SLMExposureChanged: ClassVar[PSignal]
|
|
116
131
|
"""Emits `(name: str, newExposure: float)` when the exposure of the SLM device changes.""" # noqa: E501
|
|
117
|
-
sLMExposureChanged: PSignal # alias
|
|
132
|
+
sLMExposureChanged: ClassVar[PSignal] # alias
|
|
118
133
|
|
|
119
134
|
# added for CMMCorePlus
|
|
120
|
-
configSet: PSignal
|
|
135
|
+
configSet: ClassVar[PSignal]
|
|
121
136
|
"""Emits `(str, str)` when a config has been set.
|
|
122
137
|
|
|
123
138
|
> :sparkles: This signal is unique to `pymmcore-plus`.
|
|
124
139
|
"""
|
|
125
|
-
imageSnapped: PSignal
|
|
140
|
+
imageSnapped: ClassVar[PSignal]
|
|
126
141
|
"""Emits with no arguments whenever snap is called.
|
|
127
142
|
|
|
128
143
|
> :sparkles: This signal is unique to `pymmcore-plus`.
|
|
129
144
|
"""
|
|
130
|
-
mdaEngineRegistered: PSignal
|
|
145
|
+
mdaEngineRegistered: ClassVar[PSignal]
|
|
131
146
|
"""Emits `(MDAEngine, MDAEngine)` when an MDAEngine is registered.
|
|
132
147
|
|
|
133
148
|
> :sparkles: This signal is unique to `pymmcore-plus`.
|
|
134
149
|
"""
|
|
135
150
|
|
|
136
|
-
continuousSequenceAcquisitionStarting: PSignal
|
|
151
|
+
continuousSequenceAcquisitionStarting: ClassVar[PSignal]
|
|
137
152
|
"""Emits with no arguments *before* continuous sequence acquisition is started.
|
|
138
153
|
|
|
139
154
|
> :sparkles: This signal is unique to `pymmcore-plus`.
|
|
140
155
|
"""
|
|
141
|
-
continuousSequenceAcquisitionStarted: PSignal
|
|
156
|
+
continuousSequenceAcquisitionStarted: ClassVar[PSignal]
|
|
142
157
|
"""Emits with no arguments *after* continuous sequence acquisition has started.
|
|
143
158
|
|
|
144
159
|
> :sparkles: This signal is unique to `pymmcore-plus`.
|
|
145
160
|
"""
|
|
146
|
-
sequenceAcquisitionStarting: PSignal
|
|
161
|
+
sequenceAcquisitionStarting: ClassVar[PSignal]
|
|
147
162
|
"""Emits `(str, int, float, bool)` *before* sequence acquisition is started.
|
|
148
163
|
|
|
149
164
|
(cameraLabel, numImages, intervalMs, stopOnOverflow)
|
|
150
165
|
> :sparkles: This signal is unique to `pymmcore-plus`.
|
|
151
166
|
"""
|
|
152
|
-
sequenceAcquisitionStarted: PSignal
|
|
167
|
+
sequenceAcquisitionStarted: ClassVar[PSignal]
|
|
153
168
|
"""Emits `(str, int, float, bool)` *after* sequence acquisition has started.
|
|
154
169
|
|
|
155
170
|
(cameraLabel, numImages, intervalMs, stopOnOverflow)
|
|
156
171
|
> :sparkles: This signal is unique to `pymmcore-plus`.
|
|
157
172
|
"""
|
|
158
|
-
sequenceAcquisitionStopped: PSignal
|
|
173
|
+
sequenceAcquisitionStopped: ClassVar[PSignal]
|
|
159
174
|
"""Emits `(str)` when sequence acquisition is stopped.
|
|
160
175
|
|
|
161
176
|
> :sparkles: This signal is unique to `pymmcore-plus`.
|
|
162
177
|
"""
|
|
163
|
-
autoShutterSet: PSignal
|
|
178
|
+
autoShutterSet: ClassVar[PSignal]
|
|
164
179
|
"""Emits `(bool)` when the auto shutter setting is changed.
|
|
165
180
|
|
|
166
181
|
"""
|
|
167
|
-
configGroupDeleted: PSignal
|
|
182
|
+
configGroupDeleted: ClassVar[PSignal]
|
|
168
183
|
"""Emits `(str)` when a config group is deleted.
|
|
169
184
|
|
|
170
185
|
> :sparkles: This signal is unique to `pymmcore-plus`.
|
|
171
186
|
"""
|
|
172
|
-
configDeleted: PSignal
|
|
187
|
+
configDeleted: ClassVar[PSignal]
|
|
173
188
|
"""Emits `(str, str)` when a config is deleted.
|
|
174
189
|
|
|
175
190
|
> :sparkles: This signal is unique to `pymmcore-plus`.
|
|
176
191
|
"""
|
|
177
|
-
configDefined: PSignal
|
|
192
|
+
configDefined: ClassVar[PSignal]
|
|
178
193
|
"""Emits `(str, str, str, str, str)` when a config is defined.
|
|
179
194
|
|
|
180
195
|
> :sparkles: This signal is unique to `pymmcore-plus`.
|
|
181
196
|
"""
|
|
182
|
-
roiSet: PSignal
|
|
197
|
+
roiSet: ClassVar[PSignal]
|
|
183
198
|
"""Emits `(str, int, int, int, int)` when an ROI is set.
|
|
184
199
|
|
|
185
200
|
> :sparkles: This signal is unique to `pymmcore-plus`.
|
|
186
201
|
"""
|
|
187
202
|
|
|
188
203
|
def devicePropertyChanged(
|
|
189
|
-
self, device: str, property:
|
|
204
|
+
self, device: str, property: str | None = None
|
|
190
205
|
) -> PSignalInstance:
|
|
191
206
|
"""Return object to connect/disconnect to device/property-specific changes.
|
|
192
207
|
|
|
@@ -38,9 +38,9 @@ class CMMCoreSignaler(SignalGroup, _DevicePropertyEventMixin):
|
|
|
38
38
|
|
|
39
39
|
# aliases for lower casing
|
|
40
40
|
@property
|
|
41
|
-
def xYStagePositionChanged(self) -> SignalInstance:
|
|
41
|
+
def xYStagePositionChanged(self) -> SignalInstance:
|
|
42
42
|
return self.XYStagePositionChanged
|
|
43
43
|
|
|
44
44
|
@property
|
|
45
|
-
def sLMExposureChanged(self) -> SignalInstance:
|
|
45
|
+
def sLMExposureChanged(self) -> SignalInstance:
|
|
46
46
|
return self.SLMExposureChanged
|
{pymmcore_plus-0.14.0 → pymmcore_plus-0.15.0}/src/pymmcore_plus/experimental/unicore/__init__.py
RENAMED
|
@@ -1,12 +1,18 @@
|
|
|
1
|
-
from ._unicore import UniMMCore
|
|
1
|
+
from .core._unicore import UniMMCore
|
|
2
|
+
from .devices._camera import Camera
|
|
2
3
|
from .devices._device import Device
|
|
3
4
|
from .devices._properties import PropertyInfo, pymm_property
|
|
5
|
+
from .devices._slm import SLMDevice
|
|
4
6
|
from .devices._stage import StageDevice, XYStageDevice, XYStepperStageDevice
|
|
7
|
+
from .devices._state import StateDevice
|
|
5
8
|
|
|
6
9
|
__all__ = [
|
|
10
|
+
"Camera",
|
|
7
11
|
"Device",
|
|
8
12
|
"PropertyInfo",
|
|
13
|
+
"SLMDevice",
|
|
9
14
|
"StageDevice",
|
|
15
|
+
"StateDevice",
|
|
10
16
|
"UniMMCore",
|
|
11
17
|
"XYStageDevice",
|
|
12
18
|
"XYStepperStageDevice",
|
{pymmcore_plus-0.14.0 → pymmcore_plus-0.15.0}/src/pymmcore_plus/experimental/unicore/_proxy.py
RENAMED
|
@@ -106,22 +106,39 @@ def create_proxy(
|
|
|
106
106
|
```
|
|
107
107
|
"""
|
|
108
108
|
sub_proxies = sub_proxies or {}
|
|
109
|
+
|
|
110
|
+
# Get all public attribute names from the protocol (both from dir() and type hints)
|
|
109
111
|
allowed_names = {
|
|
110
112
|
x
|
|
111
113
|
for x in chain(dir(protocol), get_type_hints(protocol))
|
|
112
|
-
if not x.startswith("_")
|
|
114
|
+
if not x.startswith("_") # Exclude private/dunder attributes
|
|
113
115
|
}
|
|
116
|
+
|
|
117
|
+
# Create an immutable module to act as our proxy object
|
|
114
118
|
proxy = _ImmutableModule(protocol.__name__)
|
|
119
|
+
|
|
120
|
+
# Iterate through each allowed attribute name
|
|
115
121
|
for attr_name in allowed_names:
|
|
122
|
+
# Get the actual attribute from the source object
|
|
116
123
|
attr = getattr(obj, attr_name)
|
|
124
|
+
|
|
125
|
+
# Check if this attribute should be sub-proxied
|
|
117
126
|
if subprotocol := sub_proxies.get(attr_name):
|
|
118
|
-
#
|
|
127
|
+
# Look for nested sub-proxies on attr_name, e.g. `attr_name.sub_attr`
|
|
128
|
+
# Filter sub_proxies for keys that start with "attr_name."
|
|
119
129
|
sub = {
|
|
120
|
-
k.split(".", 1)[1]: v
|
|
130
|
+
k.split(".", 1)[1]: v # Remove the "attr_name." prefix
|
|
121
131
|
for k, v in sub_proxies.items()
|
|
122
132
|
if k.startswith(f"{attr_name}.")
|
|
123
133
|
}
|
|
134
|
+
# Recursively create a proxy for this attribute
|
|
124
135
|
attr = create_proxy(attr, subprotocol, sub)
|
|
136
|
+
|
|
137
|
+
# Set the attribute on our proxy object
|
|
125
138
|
setattr(proxy, attr_name, attr)
|
|
139
|
+
|
|
140
|
+
# Freeze the proxy to prevent further modifications
|
|
126
141
|
proxy.__frozen__ = True
|
|
142
|
+
|
|
143
|
+
# Return the proxy cast to the expected protocol type
|
|
127
144
|
return cast("T", proxy)
|