pymmcore-plus 0.13.3__tar.gz → 0.13.5__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.13.3 → pymmcore_plus-0.13.5}/PKG-INFO +18 -16
- {pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/README.md +15 -13
- {pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/pyproject.toml +4 -3
- {pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/src/pymmcore_plus/_cli.py +1 -1
- {pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/src/pymmcore_plus/_pymmcore.py +2 -0
- {pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/src/pymmcore_plus/core/_metadata.py +8 -5
- {pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/src/pymmcore_plus/core/_mmcore_plus.py +36 -17
- {pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/src/pymmcore_plus/core/events/__init__.py +1 -1
- {pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/src/pymmcore_plus/experimental/unicore/_unicore.py +1 -1
- {pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/src/pymmcore_plus/experimental/unicore/devices/_properties.py +1 -1
- {pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/src/pymmcore_plus/mda/_engine.py +63 -20
- {pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/src/pymmcore_plus/mda/events/__init__.py +1 -1
- {pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/src/pymmcore_plus/mda/handlers/_tensorstore_handler.py +1 -1
- {pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/src/pymmcore_plus/seq_tester.py +4 -4
- {pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/tests/test_cli.py +1 -1
- {pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/tests/test_core.py +15 -4
- {pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/tests/test_mda.py +6 -0
- {pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/tests/test_sequencing.py +2 -1
- {pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/.gitignore +0 -0
- {pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/LICENSE +0 -0
- {pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/src/pymmcore_plus/__init__.py +0 -0
- {pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/src/pymmcore_plus/_benchmark.py +0 -0
- {pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/src/pymmcore_plus/_build.py +0 -0
- {pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/src/pymmcore_plus/_logger.py +0 -0
- {pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/src/pymmcore_plus/_util.py +0 -0
- {pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/src/pymmcore_plus/core/__init__.py +0 -0
- {pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/src/pymmcore_plus/core/_adapter.py +0 -0
- {pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/src/pymmcore_plus/core/_config.py +0 -0
- {pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/src/pymmcore_plus/core/_config_group.py +0 -0
- {pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/src/pymmcore_plus/core/_constants.py +0 -0
- {pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/src/pymmcore_plus/core/_device.py +0 -0
- {pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/src/pymmcore_plus/core/_property.py +0 -0
- {pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/src/pymmcore_plus/core/_sequencing.py +0 -0
- {pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/src/pymmcore_plus/core/events/_device_signal_view.py +0 -0
- {pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/src/pymmcore_plus/core/events/_norm_slot.py +0 -0
- {pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/src/pymmcore_plus/core/events/_prop_event_mixin.py +0 -0
- {pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/src/pymmcore_plus/core/events/_protocol.py +0 -0
- {pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/src/pymmcore_plus/core/events/_psygnal.py +0 -0
- {pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/src/pymmcore_plus/core/events/_qsignals.py +0 -0
- {pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/src/pymmcore_plus/experimental/__init__.py +0 -0
- {pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/src/pymmcore_plus/experimental/unicore/__init__.py +0 -0
- {pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/src/pymmcore_plus/experimental/unicore/_device_manager.py +0 -0
- {pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/src/pymmcore_plus/experimental/unicore/_proxy.py +0 -0
- {pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/src/pymmcore_plus/experimental/unicore/devices/__init__.py +0 -0
- {pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/src/pymmcore_plus/experimental/unicore/devices/_device.py +0 -0
- {pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/src/pymmcore_plus/experimental/unicore/devices/_stage.py +0 -0
- {pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/src/pymmcore_plus/install.py +0 -0
- {pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/src/pymmcore_plus/mda/__init__.py +0 -0
- {pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/src/pymmcore_plus/mda/_protocol.py +0 -0
- {pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/src/pymmcore_plus/mda/_runner.py +0 -0
- {pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/src/pymmcore_plus/mda/_thread_relay.py +0 -0
- {pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/src/pymmcore_plus/mda/events/_protocol.py +0 -0
- {pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/src/pymmcore_plus/mda/events/_psygnal.py +0 -0
- {pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/src/pymmcore_plus/mda/events/_qsignals.py +0 -0
- {pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/src/pymmcore_plus/mda/handlers/_5d_writer_base.py +0 -0
- {pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/src/pymmcore_plus/mda/handlers/__init__.py +0 -0
- {pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/src/pymmcore_plus/mda/handlers/_img_sequence_writer.py +0 -0
- {pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/src/pymmcore_plus/mda/handlers/_ome_tiff_writer.py +0 -0
- {pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/src/pymmcore_plus/mda/handlers/_ome_zarr_writer.py +0 -0
- {pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/src/pymmcore_plus/mda/handlers/_util.py +0 -0
- {pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/src/pymmcore_plus/metadata/__init__.py +0 -0
- {pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/src/pymmcore_plus/metadata/functions.py +0 -0
- {pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/src/pymmcore_plus/metadata/schema.py +0 -0
- {pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/src/pymmcore_plus/metadata/serialize.py +0 -0
- {pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/src/pymmcore_plus/mocks.py +0 -0
- {pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/src/pymmcore_plus/model/__init__.py +0 -0
- {pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/src/pymmcore_plus/model/_config_file.py +0 -0
- {pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/src/pymmcore_plus/model/_config_group.py +0 -0
- {pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/src/pymmcore_plus/model/_core_device.py +0 -0
- {pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/src/pymmcore_plus/model/_core_link.py +0 -0
- {pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/src/pymmcore_plus/model/_device.py +0 -0
- {pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/src/pymmcore_plus/model/_microscope.py +0 -0
- {pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/src/pymmcore_plus/model/_pixel_size_config.py +0 -0
- {pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/src/pymmcore_plus/model/_property.py +0 -0
- {pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/src/pymmcore_plus/py.typed +0 -0
- {pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/tests/__init__.py +0 -0
- {pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/tests/conftest.py +0 -0
- {pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/tests/io/test_image_sequence_writer.py +0 -0
- {pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/tests/io/test_ome_tiff.py +0 -0
- {pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/tests/io/test_zarr_writers.py +0 -0
- {pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/tests/local_config.cfg +0 -0
- {pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/tests/test_adapter_class.py +0 -0
- {pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/tests/test_bench.py +0 -0
- {pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/tests/test_config_group_class.py +0 -0
- {pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/tests/test_device_class.py +0 -0
- {pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/tests/test_events.py +0 -0
- {pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/tests/test_metadata.py +0 -0
- {pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/tests/test_misc.py +0 -0
- {pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/tests/test_model.py +0 -0
- {pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/tests/test_pixel_config_class.py +0 -0
- {pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/tests/test_property_class.py +0 -0
- {pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/tests/test_slm_image.py +0 -0
- {pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/tests/test_thread_relay.py +0 -0
- {pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/tests/unicore/test_unicore.py +0 -0
- {pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/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.13.
|
|
3
|
+
Version: 0.13.5
|
|
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
|
|
@@ -30,10 +30,10 @@ Requires-Dist: platformdirs>=3.0.0
|
|
|
30
30
|
Requires-Dist: psygnal>=0.7
|
|
31
31
|
Requires-Dist: pymmcore>=10.7.0.71.0
|
|
32
32
|
Requires-Dist: rich>=10.2.0
|
|
33
|
-
Requires-Dist: tensorstore
|
|
33
|
+
Requires-Dist: tensorstore<=0.1.71
|
|
34
34
|
Requires-Dist: typer>=0.4.2
|
|
35
35
|
Requires-Dist: typing-extensions
|
|
36
|
-
Requires-Dist: useq-schema>=0.
|
|
36
|
+
Requires-Dist: useq-schema>=0.7.0
|
|
37
37
|
Provides-Extra: cli
|
|
38
38
|
Requires-Dist: rich>=10.2.0; extra == 'cli'
|
|
39
39
|
Requires-Dist: typer>=0.4.2; extra == 'cli'
|
|
@@ -99,7 +99,7 @@ environments**.
|
|
|
99
99
|
[CMMCorePlus
|
|
100
100
|
documentation](https://pymmcore-plus.github.io/pymmcore-plus/api/cmmcoreplus/)
|
|
101
101
|
for details.
|
|
102
|
-
- `pymmcore-plus` includes an [acquisition engine](https://pymmcore-plus.github.io/pymmcore-plus/guides/mda_engine/)
|
|
102
|
+
- `pymmcore-plus` includes an [acquisition engine](https://pymmcore-plus.github.io/pymmcore-plus/guides/mda_engine/)
|
|
103
103
|
that drives micro-manager for conventional multi-dimensional experiments. It accepts an
|
|
104
104
|
[MDASequence](https://pymmcore-plus.github.io/useq-schema/schema/sequence/)
|
|
105
105
|
from [useq-schema](https://pymmcore-plus.github.io/useq-schema/) for
|
|
@@ -112,7 +112,7 @@ environments**.
|
|
|
112
112
|
|
|
113
113
|
## Documentation
|
|
114
114
|
|
|
115
|
-
https://pymmcore-plus.github.io/pymmcore-plus
|
|
115
|
+
<https://pymmcore-plus.github.io/pymmcore-plus/>
|
|
116
116
|
|
|
117
117
|
## Why not just use `pymmcore` directly?
|
|
118
118
|
|
|
@@ -139,20 +139,22 @@ python users are accustomed to. This library:
|
|
|
139
139
|
|
|
140
140
|
## How does this relate to `Pycro-Manager`?
|
|
141
141
|
|
|
142
|
-
[Pycro-Manager](https://github.com/micro-manager/pycro-manager) is
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
communicates with the Java half using ZeroMQ messaging.
|
|
142
|
+
[Pycro-Manager](https://github.com/micro-manager/pycro-manager) is designed to
|
|
143
|
+
make it easier to work with and control the Java Micro-manager application
|
|
144
|
+
(MMStudio) using python. As such, it requires Java to be installed and for
|
|
145
|
+
MMStudio to be running a server in another process. The python half communicates
|
|
146
|
+
with the Java half using ZeroMQ messaging.
|
|
148
147
|
|
|
149
148
|
**In brief**: while `Pycro-Manager` provides a python API to control the Java
|
|
150
149
|
Micro-manager application (which in turn controls the C++ core), `pymmcore-plus`
|
|
151
150
|
provides a python API to control the C++ core directly, without the need for
|
|
152
|
-
Java in the loop.
|
|
153
|
-
pycro-manager you
|
|
154
|
-
and GUI application. With pymmcore-plus
|
|
155
|
-
have direct access to the memory buffers
|
|
151
|
+
Java in the loop. Each has its own advantages and disadvantages! With
|
|
152
|
+
pycro-manager you retain the entire existing micro-manager ecosystem
|
|
153
|
+
and GUI application. With pymmcore-plus, the entire thing is python: you
|
|
154
|
+
don't need to install Java, and you have direct access to the memory buffers
|
|
155
|
+
used by the C++ core. Work on recreating the gui application in python
|
|
156
|
+
being done in [`pymmcore-widgets`](https://github.com/pymmcore-plus/pymmcore-widgets)
|
|
157
|
+
and [`pymmcore-gui`](https://github.com/pymmcore-plus/pymmcore-gui).
|
|
156
158
|
|
|
157
159
|
## Quickstart
|
|
158
160
|
|
|
@@ -191,7 +193,7 @@ mmcore install
|
|
|
191
193
|
|
|
192
194
|
(you can also download these manually from [micro-manager.org](https://micro-manager.org/Micro-Manager_Nightly_Builds))
|
|
193
195
|
|
|
194
|
-
_See [installation documentation
|
|
196
|
+
_See [installation documentation](https://pymmcore-plus.github.io/pymmcore-plus/install/) for more details._
|
|
195
197
|
|
|
196
198
|
### Usage
|
|
197
199
|
|
|
@@ -22,7 +22,7 @@ environments**.
|
|
|
22
22
|
[CMMCorePlus
|
|
23
23
|
documentation](https://pymmcore-plus.github.io/pymmcore-plus/api/cmmcoreplus/)
|
|
24
24
|
for details.
|
|
25
|
-
- `pymmcore-plus` includes an [acquisition engine](https://pymmcore-plus.github.io/pymmcore-plus/guides/mda_engine/)
|
|
25
|
+
- `pymmcore-plus` includes an [acquisition engine](https://pymmcore-plus.github.io/pymmcore-plus/guides/mda_engine/)
|
|
26
26
|
that drives micro-manager for conventional multi-dimensional experiments. It accepts an
|
|
27
27
|
[MDASequence](https://pymmcore-plus.github.io/useq-schema/schema/sequence/)
|
|
28
28
|
from [useq-schema](https://pymmcore-plus.github.io/useq-schema/) for
|
|
@@ -35,7 +35,7 @@ environments**.
|
|
|
35
35
|
|
|
36
36
|
## Documentation
|
|
37
37
|
|
|
38
|
-
https://pymmcore-plus.github.io/pymmcore-plus
|
|
38
|
+
<https://pymmcore-plus.github.io/pymmcore-plus/>
|
|
39
39
|
|
|
40
40
|
## Why not just use `pymmcore` directly?
|
|
41
41
|
|
|
@@ -62,20 +62,22 @@ python users are accustomed to. This library:
|
|
|
62
62
|
|
|
63
63
|
## How does this relate to `Pycro-Manager`?
|
|
64
64
|
|
|
65
|
-
[Pycro-Manager](https://github.com/micro-manager/pycro-manager) is
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
communicates with the Java half using ZeroMQ messaging.
|
|
65
|
+
[Pycro-Manager](https://github.com/micro-manager/pycro-manager) is designed to
|
|
66
|
+
make it easier to work with and control the Java Micro-manager application
|
|
67
|
+
(MMStudio) using python. As such, it requires Java to be installed and for
|
|
68
|
+
MMStudio to be running a server in another process. The python half communicates
|
|
69
|
+
with the Java half using ZeroMQ messaging.
|
|
71
70
|
|
|
72
71
|
**In brief**: while `Pycro-Manager` provides a python API to control the Java
|
|
73
72
|
Micro-manager application (which in turn controls the C++ core), `pymmcore-plus`
|
|
74
73
|
provides a python API to control the C++ core directly, without the need for
|
|
75
|
-
Java in the loop.
|
|
76
|
-
pycro-manager you
|
|
77
|
-
and GUI application. With pymmcore-plus
|
|
78
|
-
have direct access to the memory buffers
|
|
74
|
+
Java in the loop. Each has its own advantages and disadvantages! With
|
|
75
|
+
pycro-manager you retain the entire existing micro-manager ecosystem
|
|
76
|
+
and GUI application. With pymmcore-plus, the entire thing is python: you
|
|
77
|
+
don't need to install Java, and you have direct access to the memory buffers
|
|
78
|
+
used by the C++ core. Work on recreating the gui application in python
|
|
79
|
+
being done in [`pymmcore-widgets`](https://github.com/pymmcore-plus/pymmcore-widgets)
|
|
80
|
+
and [`pymmcore-gui`](https://github.com/pymmcore-plus/pymmcore-gui).
|
|
79
81
|
|
|
80
82
|
## Quickstart
|
|
81
83
|
|
|
@@ -114,7 +116,7 @@ mmcore install
|
|
|
114
116
|
|
|
115
117
|
(you can also download these manually from [micro-manager.org](https://micro-manager.org/Micro-Manager_Nightly_Builds))
|
|
116
118
|
|
|
117
|
-
_See [installation documentation
|
|
119
|
+
_See [installation documentation](https://pymmcore-plus.github.io/pymmcore-plus/install/) for more details._
|
|
118
120
|
|
|
119
121
|
### Usage
|
|
120
122
|
|
|
@@ -39,9 +39,10 @@ dependencies = [
|
|
|
39
39
|
"numpy >=1.17.3",
|
|
40
40
|
"psygnal >=0.7",
|
|
41
41
|
"pymmcore >=10.7.0.71.0",
|
|
42
|
-
"typing-extensions",
|
|
43
|
-
"useq-schema >=0.
|
|
44
|
-
|
|
42
|
+
"typing-extensions", # not actually required at runtime
|
|
43
|
+
"useq-schema >=0.7.0",
|
|
44
|
+
# until https://github.com/google/tensorstore/issues/217 is resolved
|
|
45
|
+
"tensorstore <= 0.1.71",
|
|
45
46
|
# cli requirements included by default for now
|
|
46
47
|
"typer >=0.4.2",
|
|
47
48
|
"rich >=10.2.0",
|
|
@@ -289,7 +289,7 @@ def run(
|
|
|
289
289
|
mda.setdefault("channels", []).append(_c)
|
|
290
290
|
if channel_group is not None:
|
|
291
291
|
for c in mda.get("channels", []):
|
|
292
|
-
cast(dict, c)["group"] = channel_group
|
|
292
|
+
cast("dict", c)["group"] = channel_group
|
|
293
293
|
|
|
294
294
|
# this will raise if anything has gone wrong.
|
|
295
295
|
_mda = MDASequence(**mda)
|
|
@@ -20,8 +20,7 @@ class Metadata(pymmcore.Metadata):
|
|
|
20
20
|
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
|
21
21
|
super().__init__()
|
|
22
22
|
if args and isinstance(args[0], Mapping):
|
|
23
|
-
|
|
24
|
-
self[k] = v
|
|
23
|
+
kwargs = {**args[0], **kwargs}
|
|
25
24
|
for k, v in kwargs.items():
|
|
26
25
|
self[k] = v
|
|
27
26
|
|
|
@@ -76,15 +75,19 @@ class Metadata(pymmcore.Metadata):
|
|
|
76
75
|
return json.dumps(dict(self))
|
|
77
76
|
|
|
78
77
|
def keys(self) -> KeysView[str]:
|
|
79
|
-
return cast(KeysView, metadata_keys(self))
|
|
78
|
+
return cast("KeysView", metadata_keys(self))
|
|
80
79
|
|
|
81
80
|
def items(self) -> ItemsView[str, str]:
|
|
82
|
-
return cast(ItemsView, metadata_items(self))
|
|
81
|
+
return cast("ItemsView", metadata_items(self))
|
|
83
82
|
|
|
84
83
|
def values(self) -> ValuesView[str]:
|
|
85
|
-
return cast(ValuesView, metadata_values(self))
|
|
84
|
+
return cast("ValuesView", metadata_values(self))
|
|
86
85
|
|
|
87
86
|
|
|
88
87
|
metadata_keys = new_class("metadata_keys", (KeysView,), {})
|
|
89
88
|
metadata_items = new_class("metadata_items", (ItemsView,), {})
|
|
90
89
|
metadata_values = new_class("metadata_values", (ValuesView,), {})
|
|
90
|
+
|
|
91
|
+
# Register the new classes with the `collections.abc` module
|
|
92
|
+
# so that isistance() works as expected.
|
|
93
|
+
Mapping.register(Metadata)
|
|
@@ -13,7 +13,7 @@ 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 TYPE_CHECKING, Any, Callable, NamedTuple, TypeVar, overload
|
|
16
|
+
from typing import TYPE_CHECKING, Any, Callable, NamedTuple, TypeVar, cast, overload
|
|
17
17
|
|
|
18
18
|
from psygnal import SignalInstance
|
|
19
19
|
|
|
@@ -221,7 +221,13 @@ class CMMCorePlus(pymmcore.CMMCore):
|
|
|
221
221
|
self.setPrimaryLogFile(str(logfile))
|
|
222
222
|
logger.debug("Initialized core %s", self)
|
|
223
223
|
|
|
224
|
-
|
|
224
|
+
# some internal state, remembering the last arguments passed to various
|
|
225
|
+
# functions. These are subject to change: do not depend on externally
|
|
226
|
+
self._last_sys_config: str | None = None # last loaded config file
|
|
227
|
+
self._last_config: tuple[str, str] = ("", "")
|
|
228
|
+
# last position set by setXYPosition, None means currentXYStageDevice
|
|
229
|
+
self._last_xy_position: dict[str | None, tuple[float, float]] = {}
|
|
230
|
+
|
|
225
231
|
self._mm_path = mm_path or find_micromanager()
|
|
226
232
|
if not adapter_paths and self._mm_path:
|
|
227
233
|
adapter_paths = [self._mm_path]
|
|
@@ -399,15 +405,15 @@ class CMMCorePlus(pymmcore.CMMCore):
|
|
|
399
405
|
fpath = Path(self._mm_path) / fileName
|
|
400
406
|
if not fpath.exists():
|
|
401
407
|
raise FileNotFoundError(f"Path does not exist: {fpath}")
|
|
402
|
-
self.
|
|
403
|
-
super().loadSystemConfiguration(self.
|
|
408
|
+
self._last_sys_config = str(fpath.resolve())
|
|
409
|
+
super().loadSystemConfiguration(self._last_sys_config)
|
|
404
410
|
|
|
405
411
|
def systemConfigurationFile(self) -> str | None:
|
|
406
412
|
"""Return the path to the last loaded system configuration file, or `None`.
|
|
407
413
|
|
|
408
414
|
:sparkles: *This method is new in `CMMCorePlus`.*
|
|
409
415
|
"""
|
|
410
|
-
return self.
|
|
416
|
+
return self._last_sys_config
|
|
411
417
|
|
|
412
418
|
def unloadAllDevices(self) -> None:
|
|
413
419
|
"""Unload all devices from the core and reset all configuration data.
|
|
@@ -642,10 +648,7 @@ class CMMCorePlus(pymmcore.CMMCore):
|
|
|
642
648
|
img = super().getLastImageMD(channel, slice, md)
|
|
643
649
|
else:
|
|
644
650
|
img = super().getLastImageMD(md)
|
|
645
|
-
return (
|
|
646
|
-
self.fixImage(img) if fix and pymmcore.BACKEND == "pymmcore" else img,
|
|
647
|
-
md,
|
|
648
|
-
)
|
|
651
|
+
return (self.fixImage(img) if fix and not pymmcore.NANO else img, md)
|
|
649
652
|
|
|
650
653
|
@overload
|
|
651
654
|
def popNextImageAndMD(
|
|
@@ -687,11 +690,7 @@ class CMMCorePlus(pymmcore.CMMCore):
|
|
|
687
690
|
"""
|
|
688
691
|
md = Metadata()
|
|
689
692
|
img = super().popNextImageMD(channel, slice, md)
|
|
690
|
-
|
|
691
|
-
return (
|
|
692
|
-
self.fixImage(img) if fix and pymmcore.BACKEND == "pymmcore" else img,
|
|
693
|
-
md,
|
|
694
|
-
)
|
|
693
|
+
return (self.fixImage(img) if fix and not pymmcore.NANO else img, md)
|
|
695
694
|
|
|
696
695
|
def popNextImage(self, *, fix: bool = True) -> np.ndarray:
|
|
697
696
|
"""Gets and removes the next image from the circular buffer.
|
|
@@ -707,7 +706,7 @@ class CMMCorePlus(pymmcore.CMMCore):
|
|
|
707
706
|
will be reshaped to (w, h, n_components) using `fixImage`.
|
|
708
707
|
"""
|
|
709
708
|
img: np.ndarray = super().popNextImage()
|
|
710
|
-
return self.fixImage(img) if fix and pymmcore.
|
|
709
|
+
return self.fixImage(img) if fix and not pymmcore.NANO else img
|
|
711
710
|
|
|
712
711
|
def getNBeforeLastImageAndMD(
|
|
713
712
|
self, n: int, *, fix: bool = True
|
|
@@ -735,7 +734,7 @@ class CMMCorePlus(pymmcore.CMMCore):
|
|
|
735
734
|
"""
|
|
736
735
|
md = Metadata()
|
|
737
736
|
img = super().getNBeforeLastImageMD(n, md)
|
|
738
|
-
return self.fixImage(img) if fix and pymmcore.
|
|
737
|
+
return self.fixImage(img) if fix and not pymmcore.NANO else img, md
|
|
739
738
|
|
|
740
739
|
def setConfig(self, groupName: str, configName: str) -> None:
|
|
741
740
|
"""Applies a configuration to a group.
|
|
@@ -747,6 +746,7 @@ class CMMCorePlus(pymmcore.CMMCore):
|
|
|
747
746
|
"""
|
|
748
747
|
super().setConfig(groupName, configName)
|
|
749
748
|
self.events.configSet.emit(groupName, configName)
|
|
749
|
+
self._last_config = (groupName, configName)
|
|
750
750
|
|
|
751
751
|
# NEW methods
|
|
752
752
|
|
|
@@ -1348,6 +1348,25 @@ class CMMCorePlus(pymmcore.CMMCore):
|
|
|
1348
1348
|
self.waitForDevice(self.getXYStageDevice())
|
|
1349
1349
|
self.waitForDevice(self.getFocusDevice())
|
|
1350
1350
|
|
|
1351
|
+
@overload
|
|
1352
|
+
def setXYPosition(self, x: float, y: float, /) -> None: ...
|
|
1353
|
+
@overload
|
|
1354
|
+
def setXYPosition(self, xyStageLabel: str, x: float, y: float, /) -> None: ...
|
|
1355
|
+
def setXYPosition(self, *args: str | float) -> None:
|
|
1356
|
+
"""Sets the position of the XY stage in microns.
|
|
1357
|
+
|
|
1358
|
+
**Why Override?** to store the last commanded stage position internally.
|
|
1359
|
+
"""
|
|
1360
|
+
if len(args) == 2:
|
|
1361
|
+
label: str | None = None
|
|
1362
|
+
x, y = cast("tuple[float, float]", args)
|
|
1363
|
+
elif len(args) == 3:
|
|
1364
|
+
label, x, y = args # type: ignore
|
|
1365
|
+
else:
|
|
1366
|
+
raise ValueError("Invalid number of arguments. Expected 2 or 3.")
|
|
1367
|
+
super().setXYPosition(*args) # type: ignore
|
|
1368
|
+
self._last_xy_position[label] = (x, y)
|
|
1369
|
+
|
|
1351
1370
|
def getZPosition(self) -> float:
|
|
1352
1371
|
"""Obtains the current position of the Z axis of the Z stage in microns.
|
|
1353
1372
|
|
|
@@ -1668,7 +1687,7 @@ class CMMCorePlus(pymmcore.CMMCore):
|
|
|
1668
1687
|
if numChannel is not None
|
|
1669
1688
|
else super().getImage()
|
|
1670
1689
|
)
|
|
1671
|
-
return self.fixImage(img) if fix and pymmcore.
|
|
1690
|
+
return self.fixImage(img) if fix and not pymmcore.NANO else img
|
|
1672
1691
|
|
|
1673
1692
|
def startContinuousSequenceAcquisition(self, intervalMs: float = 0) -> None:
|
|
1674
1693
|
"""Start a ContinuousSequenceAcquisition.
|
{pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/src/pymmcore_plus/experimental/unicore/_unicore.py
RENAMED
|
@@ -654,7 +654,7 @@ def _ensure_label(
|
|
|
654
654
|
if len(args) < min_args:
|
|
655
655
|
# we didn't get the label
|
|
656
656
|
return getter(), args
|
|
657
|
-
return cast(str, args[0]), args[1:]
|
|
657
|
+
return cast("str", args[0]), args[1:]
|
|
658
658
|
|
|
659
659
|
|
|
660
660
|
class PropertyStateCache(MutableMapping[tuple[str, str], Any]):
|
|
@@ -173,7 +173,7 @@ class PropertyController(Generic[TDev, TProp]):
|
|
|
173
173
|
f"of property {self.property.name!r}: {self.property.limits}."
|
|
174
174
|
) from e
|
|
175
175
|
min_, max_ = self.property.limits
|
|
176
|
-
if not min_ <= cast(float, value) <= max_:
|
|
176
|
+
if not min_ <= cast("float", value) <= max_:
|
|
177
177
|
raise ValueError(
|
|
178
178
|
f"Value {value!r} is not within the allowed range of property "
|
|
179
179
|
f"{self.property.name!r}: {self.property.limits}."
|
|
@@ -7,7 +7,7 @@ from typing import TYPE_CHECKING, Literal, NamedTuple, cast
|
|
|
7
7
|
|
|
8
8
|
import numpy as np
|
|
9
9
|
import useq
|
|
10
|
-
from useq import HardwareAutofocus, MDAEvent, MDASequence
|
|
10
|
+
from useq import AcquireImage, HardwareAutofocus, MDAEvent, MDASequence
|
|
11
11
|
|
|
12
12
|
from pymmcore_plus._logger import logger
|
|
13
13
|
from pymmcore_plus._util import retry
|
|
@@ -72,6 +72,10 @@ class MDAEngine(PMDAEngine):
|
|
|
72
72
|
def __init__(self, mmc: CMMCorePlus, use_hardware_sequencing: bool = True) -> None:
|
|
73
73
|
self._mmc = mmc
|
|
74
74
|
self.use_hardware_sequencing: bool = use_hardware_sequencing
|
|
75
|
+
# if True, always set XY position, even if the commanded position is the same
|
|
76
|
+
# as the last commanded position (this does *not* query the stage for the
|
|
77
|
+
# current position).
|
|
78
|
+
self.force_set_xy_position: bool = True
|
|
75
79
|
|
|
76
80
|
# whether to include position metadata when fetching on-frame metadata
|
|
77
81
|
# omitted by default when performing triggered acquisition because it's slow.
|
|
@@ -92,6 +96,9 @@ class MDAEngine(PMDAEngine):
|
|
|
92
96
|
# Note: getAutoShutter() is True when no config is loaded at all
|
|
93
97
|
self._autoshutter_was_set: bool = self._mmc.getAutoShutter()
|
|
94
98
|
|
|
99
|
+
self._last_config: tuple[str, str] = ("", "")
|
|
100
|
+
self._last_xy_pos: tuple[float | None, float | None] = (None, None)
|
|
101
|
+
|
|
95
102
|
# -----
|
|
96
103
|
# The following values are stored during setup_sequence simply to speed up
|
|
97
104
|
# retrieval of metadata during each frame.
|
|
@@ -195,6 +202,13 @@ class MDAEngine(PMDAEngine):
|
|
|
195
202
|
)
|
|
196
203
|
return
|
|
197
204
|
|
|
205
|
+
# don't try to execute any other action types. Mostly, this is just
|
|
206
|
+
# CustomAction, which is a user-defined action that the engine doesn't know how
|
|
207
|
+
# to handle. But may include other actions in the future, and this ensures
|
|
208
|
+
# backwards compatibility.
|
|
209
|
+
if not isinstance(action, (AcquireImage, type(None))):
|
|
210
|
+
return
|
|
211
|
+
|
|
198
212
|
# if the autofocus was engaged at the start of the sequence AND autofocus action
|
|
199
213
|
# did not fail, re-engage it. NOTE: we need to do that AFTER the runner calls
|
|
200
214
|
# `setup_event`, so we can't do it inside the exec_event autofocus action above.
|
|
@@ -231,18 +245,15 @@ class MDAEngine(PMDAEngine):
|
|
|
231
245
|
if event.keep_shutter_open:
|
|
232
246
|
...
|
|
233
247
|
|
|
234
|
-
|
|
235
|
-
|
|
248
|
+
self._set_event_xy_position(event)
|
|
249
|
+
|
|
236
250
|
if event.z_pos is not None:
|
|
237
251
|
self._set_event_z(event)
|
|
238
252
|
if event.slm_image is not None:
|
|
239
253
|
self._set_event_slm_image(event)
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
self._mmc.setConfig(event.channel.group, event.channel.config)
|
|
244
|
-
except Exception as e:
|
|
245
|
-
logger.warning("Failed to set channel. %s", e)
|
|
254
|
+
|
|
255
|
+
self._set_event_channel(event)
|
|
256
|
+
|
|
246
257
|
if event.exposure is not None:
|
|
247
258
|
try:
|
|
248
259
|
self._mmc.setExposure(event.exposure)
|
|
@@ -406,11 +417,7 @@ class MDAEngine(PMDAEngine):
|
|
|
406
417
|
# the call below, we won't be able to query `core.getCurrentConfig()`
|
|
407
418
|
# not sure that's necessary; and this is here for tests to pass for now,
|
|
408
419
|
# but this could be removed.
|
|
409
|
-
|
|
410
|
-
try:
|
|
411
|
-
core.setConfig(event.channel.group, event.channel.config)
|
|
412
|
-
except Exception as e:
|
|
413
|
-
logger.warning("Failed to set channel. %s", e)
|
|
420
|
+
self._set_event_channel(event)
|
|
414
421
|
|
|
415
422
|
if event.slm_image:
|
|
416
423
|
self._set_event_slm_image(event)
|
|
@@ -423,8 +430,8 @@ class MDAEngine(PMDAEngine):
|
|
|
423
430
|
# start sequences or set non-sequenced values
|
|
424
431
|
if event.x_sequence:
|
|
425
432
|
core.startXYStageSequence(core.getXYStageDevice())
|
|
426
|
-
|
|
427
|
-
self.
|
|
433
|
+
else:
|
|
434
|
+
self._set_event_xy_position(event)
|
|
428
435
|
|
|
429
436
|
if event.z_sequence:
|
|
430
437
|
core.startStageSequence(core.getFocusDevice())
|
|
@@ -587,15 +594,51 @@ class MDAEngine(PMDAEngine):
|
|
|
587
594
|
|
|
588
595
|
return _perform_full_focus(self._mmc.getZPosition())
|
|
589
596
|
|
|
590
|
-
def
|
|
597
|
+
def _set_event_xy_position(self, event: MDAEvent) -> None:
|
|
598
|
+
event_x, event_y = event.x_pos, event.y_pos
|
|
599
|
+
# If neither coordinate is provided, do nothing.
|
|
600
|
+
if event_x is None and event_y is None:
|
|
601
|
+
return
|
|
602
|
+
|
|
591
603
|
# skip if no XY stage device is found
|
|
592
604
|
if not self._mmc.getXYStageDevice():
|
|
593
605
|
logger.warning("No XY stage device found. Cannot set XY position.")
|
|
594
606
|
return
|
|
595
607
|
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
608
|
+
# Retrieve the last commanded XY position.
|
|
609
|
+
last_x, last_y = self._mmc._last_xy_position.get(None) or (None, None) # noqa: SLF001
|
|
610
|
+
if (
|
|
611
|
+
not self.force_set_xy_position
|
|
612
|
+
and (event_x is None or event_x == last_x)
|
|
613
|
+
and (event_y is None or event_y == last_y)
|
|
614
|
+
):
|
|
615
|
+
return
|
|
616
|
+
|
|
617
|
+
if event_x is None or event_y is None:
|
|
618
|
+
cur_x, cur_y = self._mmc.getXYPosition()
|
|
619
|
+
event_x = cur_x if event_x is None else event_x
|
|
620
|
+
event_y = cur_y if event_y is None else event_y
|
|
621
|
+
|
|
622
|
+
try:
|
|
623
|
+
self._mmc.setXYPosition(event_x, event_y)
|
|
624
|
+
except Exception as e:
|
|
625
|
+
logger.warning("Failed to set XY position. %s", e)
|
|
626
|
+
|
|
627
|
+
def _set_event_channel(self, event: MDAEvent) -> None:
|
|
628
|
+
if (ch := event.channel) is None:
|
|
629
|
+
return
|
|
630
|
+
|
|
631
|
+
# comparison with _last_config is a fast/rough check ... which may miss subtle
|
|
632
|
+
# differences if device properties have been individually set in the meantime.
|
|
633
|
+
# could also compare to the system state, with:
|
|
634
|
+
# data = self._mmc.getConfigData(ch.group, ch.config)
|
|
635
|
+
# if self._mmc.getSystemStateCache().isConfigurationIncluded(data):
|
|
636
|
+
# ...
|
|
637
|
+
if (ch.group, ch.config) != self._mmc._last_config: # noqa: SLF001
|
|
638
|
+
try:
|
|
639
|
+
self._mmc.setConfig(ch.group, ch.config)
|
|
640
|
+
except Exception as e:
|
|
641
|
+
logger.warning("Failed to set channel. %s", e)
|
|
599
642
|
|
|
600
643
|
def _set_event_z(self, event: MDAEvent) -> None:
|
|
601
644
|
# skip if no Z stage device is found
|
{pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/src/pymmcore_plus/mda/handlers/_tensorstore_handler.py
RENAMED
|
@@ -302,7 +302,7 @@ class TensorStoreHandler:
|
|
|
302
302
|
|
|
303
303
|
# HACK
|
|
304
304
|
if self.ts_driver == "zarr":
|
|
305
|
-
meta = cast(dict, spec.setdefault("metadata", {}))
|
|
305
|
+
meta = cast("dict", spec.setdefault("metadata", {}))
|
|
306
306
|
if "dimension_separator" not in meta:
|
|
307
307
|
meta["dimension_separator"] = "/"
|
|
308
308
|
return spec
|
|
@@ -88,8 +88,8 @@ class Setting:
|
|
|
88
88
|
|
|
89
89
|
@classmethod
|
|
90
90
|
def _from_list(cls, val: list) -> Self:
|
|
91
|
-
(dev, prop), (
|
|
92
|
-
return cls(dev, prop,
|
|
91
|
+
(dev, prop), (type_, val) = val
|
|
92
|
+
return cls(dev, prop, type_, val)
|
|
93
93
|
|
|
94
94
|
|
|
95
95
|
@dataclass
|
|
@@ -100,8 +100,8 @@ class SettingEvent(Setting):
|
|
|
100
100
|
|
|
101
101
|
@classmethod
|
|
102
102
|
def _from_list(cls, val: list) -> Self:
|
|
103
|
-
(dev, prop), (
|
|
104
|
-
return cls(dev, prop,
|
|
103
|
+
(dev, prop), (type_, val), count = val
|
|
104
|
+
return cls(dev, prop, type_, val, count)
|
|
105
105
|
|
|
106
106
|
|
|
107
107
|
@dataclass
|
|
@@ -208,7 +208,7 @@ def test_run_mda(tmp_path: Path, with_file: bool, args: dict[str, dict | str]) -
|
|
|
208
208
|
# otherwise it updates the existing
|
|
209
209
|
else:
|
|
210
210
|
_data = seq.model_dump() if hasattr(seq, "model_dump") else seq.dict()
|
|
211
|
-
sub_field = cast(dict, _data[field_name])
|
|
211
|
+
sub_field = cast("dict", _data[field_name])
|
|
212
212
|
sub_field.update(**val)
|
|
213
213
|
newval = getattr(MDASequence(**{field_name: sub_field}), field_name)
|
|
214
214
|
seq = seq.replace(**{field_name: newval})
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import os
|
|
2
2
|
import re
|
|
3
|
+
from collections.abc import Mapping
|
|
3
4
|
from pathlib import Path
|
|
4
5
|
from typing import TYPE_CHECKING, Any, Callable
|
|
5
6
|
from unittest.mock import MagicMock, call, patch
|
|
@@ -339,18 +340,28 @@ def test_new_metadata():
|
|
|
339
340
|
assert isinstance(md, pymmcore.Metadata)
|
|
340
341
|
|
|
341
342
|
|
|
342
|
-
def
|
|
343
|
+
def test_get_image_and_meta(core: CMMCorePlus) -> None:
|
|
343
344
|
core.startContinuousSequenceAcquisition(10)
|
|
344
345
|
core.stopSequenceAcquisition()
|
|
345
346
|
|
|
346
347
|
image, md = core.getNBeforeLastImageAndMD(0)
|
|
347
|
-
assert isinstance(image, np.ndarray)
|
|
348
|
+
assert isinstance(image, np.ndarray)
|
|
349
|
+
assert isinstance(md, Metadata)
|
|
350
|
+
assert "TimeReceivedByCore" in md
|
|
348
351
|
|
|
349
352
|
image, md = core.getLastImageAndMD()
|
|
350
|
-
assert isinstance(image, np.ndarray)
|
|
353
|
+
assert isinstance(image, np.ndarray)
|
|
354
|
+
assert isinstance(md, Metadata)
|
|
355
|
+
assert "TimeReceivedByCore" in md
|
|
351
356
|
|
|
352
357
|
image, md = core.popNextImageAndMD()
|
|
353
|
-
assert isinstance(image, np.ndarray)
|
|
358
|
+
assert isinstance(image, np.ndarray)
|
|
359
|
+
assert isinstance(md, Metadata)
|
|
360
|
+
assert "TimeReceivedByCore" in md
|
|
361
|
+
|
|
362
|
+
assert Metadata(md) == md
|
|
363
|
+
assert isinstance(md, Mapping)
|
|
364
|
+
assert issubclass(Metadata, Mapping)
|
|
354
365
|
|
|
355
366
|
|
|
356
367
|
def test_configuration(core: CMMCorePlus) -> None:
|
|
@@ -490,3 +490,9 @@ def test_get_handlers(core: CMMCorePlus) -> None:
|
|
|
490
490
|
# but they should have been available during start and finish events
|
|
491
491
|
assert on_start_names == ["TensorStoreHandler"]
|
|
492
492
|
assert on_finish_names == ["TensorStoreHandler"]
|
|
493
|
+
|
|
494
|
+
|
|
495
|
+
def test_custom_action(core: CMMCorePlus) -> None:
|
|
496
|
+
"""Make sure we can handle custom actions gracefully"""
|
|
497
|
+
|
|
498
|
+
core.mda.run([MDAEvent(action=useq.CustomAction())])
|
|
@@ -42,6 +42,7 @@ def test_sequenced_mda(core: CMMCorePlus) -> None:
|
|
|
42
42
|
engine = MDAEngine(mmc=core_mock)
|
|
43
43
|
|
|
44
44
|
engine.use_hardware_sequencing = False
|
|
45
|
+
engine.force_set_xy_position = False
|
|
45
46
|
assert len(list(engine.event_iterator(mda))) == NLOOPS * 2 * 2
|
|
46
47
|
|
|
47
48
|
engine.use_hardware_sequencing = True
|
|
@@ -66,7 +67,7 @@ def test_sequenced_mda(core: CMMCorePlus) -> None:
|
|
|
66
67
|
expected_exposure = [call(5), call(10)] * 2
|
|
67
68
|
assert core_mock.setExposure.call_args_list == expected_exposure
|
|
68
69
|
|
|
69
|
-
expected_pos = [call(0, 0), call(
|
|
70
|
+
expected_pos = [call(0, 0), call(1, 1)]
|
|
70
71
|
assert core_mock.setXYPosition.call_args_list == expected_pos
|
|
71
72
|
|
|
72
73
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/src/pymmcore_plus/core/events/_device_signal_view.py
RENAMED
|
File without changes
|
|
File without changes
|
{pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/src/pymmcore_plus/core/events/_prop_event_mixin.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/src/pymmcore_plus/experimental/unicore/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/src/pymmcore_plus/experimental/unicore/_proxy.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/src/pymmcore_plus/mda/handlers/_5d_writer_base.py
RENAMED
|
File without changes
|
|
File without changes
|
{pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/src/pymmcore_plus/mda/handlers/_img_sequence_writer.py
RENAMED
|
File without changes
|
{pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/src/pymmcore_plus/mda/handlers/_ome_tiff_writer.py
RENAMED
|
File without changes
|
{pymmcore_plus-0.13.3 → pymmcore_plus-0.13.5}/src/pymmcore_plus/mda/handlers/_ome_zarr_writer.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|