pymmcore-plus 0.9.3__py3-none-any.whl → 0.13.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.
Files changed (68) hide show
  1. pymmcore_plus/__init__.py +7 -4
  2. pymmcore_plus/_benchmark.py +203 -0
  3. pymmcore_plus/_build.py +6 -1
  4. pymmcore_plus/_cli.py +131 -31
  5. pymmcore_plus/_logger.py +19 -10
  6. pymmcore_plus/_pymmcore.py +12 -0
  7. pymmcore_plus/_util.py +139 -32
  8. pymmcore_plus/core/__init__.py +5 -0
  9. pymmcore_plus/core/_config.py +6 -4
  10. pymmcore_plus/core/_config_group.py +4 -3
  11. pymmcore_plus/core/_constants.py +135 -10
  12. pymmcore_plus/core/_device.py +4 -4
  13. pymmcore_plus/core/_metadata.py +3 -3
  14. pymmcore_plus/core/_mmcore_plus.py +254 -170
  15. pymmcore_plus/core/_property.py +6 -6
  16. pymmcore_plus/core/_sequencing.py +370 -233
  17. pymmcore_plus/core/events/__init__.py +6 -6
  18. pymmcore_plus/core/events/_device_signal_view.py +8 -6
  19. pymmcore_plus/core/events/_norm_slot.py +2 -4
  20. pymmcore_plus/core/events/_prop_event_mixin.py +7 -4
  21. pymmcore_plus/core/events/_protocol.py +5 -2
  22. pymmcore_plus/core/events/_psygnal.py +2 -2
  23. pymmcore_plus/experimental/__init__.py +0 -0
  24. pymmcore_plus/experimental/unicore/__init__.py +14 -0
  25. pymmcore_plus/experimental/unicore/_device_manager.py +173 -0
  26. pymmcore_plus/experimental/unicore/_proxy.py +127 -0
  27. pymmcore_plus/experimental/unicore/_unicore.py +703 -0
  28. pymmcore_plus/experimental/unicore/devices/__init__.py +0 -0
  29. pymmcore_plus/experimental/unicore/devices/_device.py +269 -0
  30. pymmcore_plus/experimental/unicore/devices/_properties.py +400 -0
  31. pymmcore_plus/experimental/unicore/devices/_stage.py +221 -0
  32. pymmcore_plus/install.py +16 -11
  33. pymmcore_plus/mda/__init__.py +1 -1
  34. pymmcore_plus/mda/_engine.py +320 -148
  35. pymmcore_plus/mda/_protocol.py +6 -4
  36. pymmcore_plus/mda/_runner.py +62 -51
  37. pymmcore_plus/mda/_thread_relay.py +5 -3
  38. pymmcore_plus/mda/events/__init__.py +2 -2
  39. pymmcore_plus/mda/events/_protocol.py +10 -2
  40. pymmcore_plus/mda/events/_psygnal.py +2 -2
  41. pymmcore_plus/mda/handlers/_5d_writer_base.py +106 -15
  42. pymmcore_plus/mda/handlers/__init__.py +7 -1
  43. pymmcore_plus/mda/handlers/_img_sequence_writer.py +11 -6
  44. pymmcore_plus/mda/handlers/_ome_tiff_writer.py +8 -4
  45. pymmcore_plus/mda/handlers/_ome_zarr_writer.py +82 -9
  46. pymmcore_plus/mda/handlers/_tensorstore_handler.py +374 -0
  47. pymmcore_plus/mda/handlers/_util.py +1 -1
  48. pymmcore_plus/metadata/__init__.py +36 -0
  49. pymmcore_plus/metadata/functions.py +353 -0
  50. pymmcore_plus/metadata/schema.py +472 -0
  51. pymmcore_plus/metadata/serialize.py +120 -0
  52. pymmcore_plus/mocks.py +51 -0
  53. pymmcore_plus/model/_config_file.py +5 -6
  54. pymmcore_plus/model/_config_group.py +29 -2
  55. pymmcore_plus/model/_core_device.py +12 -1
  56. pymmcore_plus/model/_core_link.py +2 -1
  57. pymmcore_plus/model/_device.py +39 -8
  58. pymmcore_plus/model/_microscope.py +39 -3
  59. pymmcore_plus/model/_pixel_size_config.py +27 -4
  60. pymmcore_plus/model/_property.py +13 -3
  61. pymmcore_plus/seq_tester.py +1 -1
  62. {pymmcore_plus-0.9.3.dist-info → pymmcore_plus-0.13.0.dist-info}/METADATA +22 -12
  63. pymmcore_plus-0.13.0.dist-info/RECORD +71 -0
  64. {pymmcore_plus-0.9.3.dist-info → pymmcore_plus-0.13.0.dist-info}/WHEEL +1 -1
  65. pymmcore_plus/core/_state.py +0 -244
  66. pymmcore_plus-0.9.3.dist-info/RECORD +0 -55
  67. {pymmcore_plus-0.9.3.dist-info → pymmcore_plus-0.13.0.dist-info}/entry_points.txt +0 -0
  68. {pymmcore_plus-0.9.3.dist-info → pymmcore_plus-0.13.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,12 +1,17 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from dataclasses import dataclass, field
4
- from typing import TYPE_CHECKING, Container, MutableMapping, NamedTuple
4
+ from typing import TYPE_CHECKING, NamedTuple
5
5
 
6
6
  if TYPE_CHECKING:
7
+ from collections.abc import Container, MutableMapping
7
8
  from typing import Final
8
9
 
10
+ from typing_extensions import Self # py311
11
+
9
12
  from pymmcore_plus import CMMCorePlus
13
+ from pymmcore_plus.metadata.schema import ConfigGroup as ConfigGroupMeta
14
+ from pymmcore_plus.metadata.schema import ConfigPreset as ConfigPresetMeta
10
15
 
11
16
  from ._core_link import ErrCallback
12
17
 
@@ -37,6 +42,20 @@ class ConfigPreset:
37
42
  name: str
38
43
  settings: list[Setting] = field(default_factory=list)
39
44
 
45
+ @classmethod
46
+ def from_metadata(cls, meta: ConfigPresetMeta) -> Self:
47
+ return cls(
48
+ name=meta["name"],
49
+ settings=[
50
+ Setting(
51
+ device_name=d["dev"],
52
+ property_name=d["prop"],
53
+ property_value=d["val"],
54
+ )
55
+ for d in meta["settings"]
56
+ ],
57
+ )
58
+
40
59
 
41
60
  @dataclass
42
61
  class ConfigGroup:
@@ -46,7 +65,15 @@ class ConfigGroup:
46
65
  presets: MutableMapping[str, ConfigPreset] = field(default_factory=dict)
47
66
 
48
67
  @classmethod
49
- def create_from_core(cls, core: CMMCorePlus, name: str) -> ConfigGroup:
68
+ def from_metadata(cls, meta: ConfigGroupMeta) -> Self:
69
+ presets = {
70
+ preset["name"]: ConfigPreset.from_metadata(preset)
71
+ for preset in meta["presets"]
72
+ }
73
+ return cls(name=meta["name"], presets=presets)
74
+
75
+ @classmethod
76
+ def create_from_core(cls, core: CMMCorePlus, name: str) -> Self:
50
77
  obj = cls(name=name)
51
78
  obj.update_from_core(core)
52
79
  return obj
@@ -1,7 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from dataclasses import dataclass, field
4
- from typing import TYPE_CHECKING, Container
4
+ from typing import TYPE_CHECKING, Any
5
5
 
6
6
  from pymmcore_plus import CMMCorePlus, DeviceType, Keyword
7
7
 
@@ -9,6 +9,8 @@ from ._device import Device
9
9
  from ._property import Property
10
10
 
11
11
  if TYPE_CHECKING:
12
+ from collections.abc import Container
13
+
12
14
  from pymmcore_plus.model._core_link import ErrCallback
13
15
 
14
16
  CORE = Keyword.CoreDevice.value
@@ -48,6 +50,15 @@ class CoreDevice(Device):
48
50
  def __post_init__(self) -> None:
49
51
  self.CORE_GETTERS = {}
50
52
 
53
+ def __setstate__(self, state: dict[str, Any]) -> None:
54
+ super().__setstate__(state)
55
+ prop_objects = []
56
+ for prop in self.properties:
57
+ if isinstance(prop, dict):
58
+ prop = Property(**prop)
59
+ prop_objects.append(prop)
60
+ self.properties = prop_objects
61
+
51
62
  def __hash__(self) -> int:
52
63
  return super().__hash__()
53
64
 
@@ -5,8 +5,9 @@ from dataclasses import fields
5
5
  from typing import TYPE_CHECKING, Protocol
6
6
 
7
7
  if TYPE_CHECKING:
8
+ from collections.abc import Container, Iterable
8
9
  from dataclasses import Field
9
- from typing import Any, Callable, ClassVar, Container, Iterable, TypeVar
10
+ from typing import Any, Callable, ClassVar, TypeVar
10
11
 
11
12
  from typing_extensions import TypeAlias # py310
12
13
 
@@ -2,7 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  import dataclasses
4
4
  from contextlib import suppress
5
- from dataclasses import dataclass, field
5
+ from dataclasses import asdict, dataclass, field
6
6
  from typing import TYPE_CHECKING
7
7
 
8
8
  from pymmcore_plus import CMMCorePlus, DeviceType, FocusDirection, Keyword
@@ -12,9 +12,15 @@ from ._core_link import CoreObject
12
12
  from ._property import Property
13
13
 
14
14
  if TYPE_CHECKING:
15
- from typing import Any, Callable, Container, Iterable
15
+ from collections.abc import Container, Iterable
16
+ from typing import Any, Callable
16
17
 
17
- from typing_extensions import TypeAlias # py310
18
+ from typing_extensions import (
19
+ Self, # py311
20
+ TypeAlias, # py310
21
+ )
22
+
23
+ from pymmcore_plus.metadata.schema import DeviceInfo
18
24
 
19
25
  from ._core_link import ErrCallback
20
26
  from ._microscope import Microscope
@@ -84,6 +90,20 @@ class Device(CoreObject):
84
90
  # from the same library that can be loaded into this hub.
85
91
  children: tuple[str, ...] = field(default_factory=tuple)
86
92
 
93
+ @classmethod
94
+ def from_metadata(cls, metadata: DeviceInfo) -> Self:
95
+ return cls(
96
+ name=metadata["label"],
97
+ library=metadata["library"],
98
+ adapter_name=metadata["name"],
99
+ description=metadata["description"],
100
+ device_type=DeviceType[metadata["type"]],
101
+ parent_label=metadata.get("parent_label") or "",
102
+ labels=tuple(metadata.get("labels", [])),
103
+ focus_direction=FocusDirection[metadata.get("focus_direction", "Unknown")],
104
+ children=tuple(metadata.get("child_names", [])),
105
+ )
106
+
87
107
  def __post_init__(self) -> None:
88
108
  if self.name == Keyword.CoreDevice or self.device_type == DeviceType.Core:
89
109
  raise ValueError(
@@ -173,11 +193,7 @@ class Device(CoreObject):
173
193
  raise RuntimeError(f"Device {self.name} is not loaded in the core.")
174
194
 
175
195
  self.device_type = core.getDeviceType(self.name)
176
- self.CORE_GETTERS = {
177
- DeviceType.StateDevice: STATE_DEVICE_GETTERS,
178
- DeviceType.StageDevice: STAGE_DEVICE_GETTERS,
179
- DeviceType.Hub: HUB_DEVICE_GETTERS,
180
- }.get(self.device_type, DEVICE_GETTERS)
196
+ self._update_core_getters()
181
197
 
182
198
  super().update_from_core(core, exclude=exclude, on_err=on_err)
183
199
  self.properties = [
@@ -185,6 +201,21 @@ class Device(CoreObject):
185
201
  for prop_name in core.getDevicePropertyNames(self.name)
186
202
  ]
187
203
 
204
+ # pulled out for the sake of picklability
205
+ def _update_core_getters(self) -> None:
206
+ self.CORE_GETTERS = {
207
+ DeviceType.StateDevice: STATE_DEVICE_GETTERS,
208
+ DeviceType.StageDevice: STAGE_DEVICE_GETTERS,
209
+ DeviceType.Hub: HUB_DEVICE_GETTERS,
210
+ }.get(self.device_type, DEVICE_GETTERS)
211
+
212
+ def __reduce__(self) -> str | tuple[Any, ...]:
213
+ return self.__class__, (), asdict(self)
214
+
215
+ def __setstate__(self, state: dict[str, Any]) -> None:
216
+ self.__dict__.update(state)
217
+ self._update_core_getters()
218
+
188
219
  def load(self, core: CMMCorePlus, *, reload: bool = False) -> None:
189
220
  """Load device properties from the core."""
190
221
  if reload and self.name in core.getLoadedDevices():
@@ -4,7 +4,7 @@ import os
4
4
  from copy import deepcopy
5
5
  from dataclasses import dataclass, field, fields
6
6
  from pathlib import Path
7
- from typing import TYPE_CHECKING, Any, Callable, Container, Iterable
7
+ from typing import TYPE_CHECKING, Any, Callable
8
8
 
9
9
  from pymmcore_plus import DeviceType, Keyword
10
10
 
@@ -14,7 +14,10 @@ from ._device import AvailableDevice, Device, get_available_devices
14
14
  from ._pixel_size_config import PixelSizeGroup
15
15
 
16
16
  if TYPE_CHECKING:
17
+ from collections.abc import Container, Iterable
18
+
17
19
  from pymmcore_plus import CMMCorePlus
20
+ from pymmcore_plus.metadata.schema import SummaryMetaV1
18
21
 
19
22
  from ._core_link import ErrCallback
20
23
  from ._property import Property
@@ -126,7 +129,7 @@ class Microscope:
126
129
  # ------------- Config-file methods -------------
127
130
 
128
131
  @classmethod
129
- def create_from_config(cls, config_file: str) -> Microscope:
132
+ def create_from_config(cls, config_file: str | Path) -> Microscope:
130
133
  obj = cls()
131
134
  obj.load_config(config_file)
132
135
  obj.mark_clean()
@@ -154,6 +157,39 @@ class Microscope:
154
157
 
155
158
  self.mark_clean()
156
159
 
160
+ @classmethod
161
+ def from_summary_metadata(cls, summary_meta: SummaryMetaV1) -> Microscope:
162
+ """Create a Microscope model from summary metadata.
163
+
164
+ This may be used to load a model from summary metadata, such as as written
165
+ during the course of a Multi-Dimensional Acquisition. This is useful for
166
+ restoring the state of a microscope from a specific experiment, or writing
167
+ out a cfg file that can be used to restore the state of the microscope.
168
+ """
169
+ core_device = next(
170
+ (d for d in summary_meta["devices"] if d["name"] == Keyword.CoreDevice),
171
+ None,
172
+ )
173
+ if core_device is None:
174
+ raise ValueError("CoreDevice not found in metadata")
175
+ return cls(
176
+ core_device=CoreDevice.from_metadata(core_device),
177
+ devices=[
178
+ Device.from_metadata(d)
179
+ for d in summary_meta["devices"]
180
+ if d["name"] != Keyword.CoreDevice
181
+ ],
182
+ config_groups={
183
+ grp["name"]: ConfigGroup.from_metadata(grp)
184
+ for grp in summary_meta["config_groups"]
185
+ },
186
+ pixel_size_group=PixelSizeGroup.from_metadata(
187
+ summary_meta["pixel_size_configs"]
188
+ ),
189
+ config_file=summary_meta["system_info"].get("system_configuration_file")
190
+ or "",
191
+ )
192
+
157
193
  # ------------- Core-interacting methods -------------
158
194
 
159
195
  @classmethod
@@ -178,7 +214,7 @@ class Microscope:
178
214
  self.devices = [
179
215
  Device.create_from_core(core, name=name)
180
216
  for name in core.getLoadedDevices()
181
- if name != Keyword.CoreDevice
217
+ if name != Keyword.CoreDevice # type: ignore [comparison-overlap]
182
218
  ]
183
219
  if "core_device" not in exclude:
184
220
  self.core_device.update_from_core(core)
@@ -1,16 +1,21 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from dataclasses import dataclass, field, fields
4
- from typing import TYPE_CHECKING, Any, Container, Iterable, MutableMapping
4
+ from typing import TYPE_CHECKING
5
5
 
6
6
  from ._config_group import ConfigGroup, ConfigPreset, Setting
7
7
 
8
8
  if TYPE_CHECKING:
9
- from typing import Final
9
+ from collections.abc import Container, Iterable, MutableMapping
10
+ from typing import Any, Final
10
11
 
11
- from typing_extensions import TypeAlias # py310
12
+ from typing_extensions import (
13
+ Self, # py311
14
+ TypeAlias, # py310
15
+ )
12
16
 
13
17
  from pymmcore_plus import CMMCorePlus
18
+ from pymmcore_plus.metadata.schema import PixelSizeConfigPreset
14
19
 
15
20
  from ._core_link import ErrCallback
16
21
 
@@ -27,6 +32,14 @@ class PixelSizePreset(ConfigPreset):
27
32
  pixel_size_um: float = 0.0
28
33
  affine: AffineTuple = DEFAULT_AFFINE
29
34
 
35
+ @classmethod
36
+ def from_metadata(cls, meta: PixelSizeConfigPreset) -> Self: # type: ignore [override]
37
+ obj = super().from_metadata(meta)
38
+ obj.pixel_size_um = meta["pixel_size_um"]
39
+ if "pixel_size_affine" in meta:
40
+ obj.affine = meta["pixel_size_affine"]
41
+ return obj
42
+
30
43
  def __rich_repr__(self, *, defaults: bool = False) -> Iterable[tuple[str, Any]]:
31
44
  """Make AvailableDevices look a little less verbose."""
32
45
  for f in fields(self):
@@ -45,6 +58,16 @@ class PixelSizeGroup(ConfigGroup):
45
58
  name: str = PIXEL_SIZE_GROUP
46
59
  presets: MutableMapping[str, PixelSizePreset] = field(default_factory=dict) # type: ignore
47
60
 
61
+ @classmethod
62
+ def from_metadata(cls, meta: tuple[PixelSizeConfigPreset, ...]) -> Self: # type: ignore [override]
63
+ """Create a PixelSizeGroup from metadata."""
64
+ return cls(
65
+ presets={
66
+ preset_info["name"]: PixelSizePreset.from_metadata(preset_info)
67
+ for preset_info in meta
68
+ }
69
+ )
70
+
48
71
  @classmethod
49
72
  def create_from_core(
50
73
  cls, core: CMMCorePlus, name: str = PIXEL_SIZE_GROUP
@@ -55,7 +78,7 @@ class PixelSizeGroup(ConfigGroup):
55
78
  preset: PixelSizePreset(
56
79
  name=preset,
57
80
  pixel_size_um=core.getPixelSizeUmByID(preset),
58
- affine=core.getPixelSizeAffineByID(preset), # type: ignore
81
+ affine=core.getPixelSizeAffineByID(preset),
59
82
  settings=[Setting(*d) for d in core.getPixelSizeConfigData(preset)],
60
83
  )
61
84
  for preset in core.getAvailablePixelSizeConfigs()
@@ -1,14 +1,15 @@
1
1
  from __future__ import annotations
2
2
 
3
- from dataclasses import dataclass, field
4
- from typing import TYPE_CHECKING
3
+ from dataclasses import asdict, dataclass, field
4
+ from typing import TYPE_CHECKING, Any
5
5
 
6
6
  from pymmcore_plus import CMMCorePlus, PropertyType
7
7
 
8
8
  from ._core_link import CoreObject
9
9
 
10
10
  if TYPE_CHECKING:
11
- from typing import Any, Callable, Container
11
+ from collections.abc import Container
12
+ from typing import Any, Callable
12
13
 
13
14
  from typing_extensions import TypeAlias # py310
14
15
 
@@ -52,6 +53,15 @@ class Property(CoreObject):
52
53
  "exists": CMMCorePlus.hasProperty,
53
54
  }
54
55
 
56
+ def __reduce__(self) -> tuple:
57
+ # Return the class, arguments for __init__, and any state to restore
58
+ state = asdict(self)
59
+ return self.__class__, (self.device_name, self.name), state
60
+
61
+ def __setstate__(self, state: dict[str, Any]) -> None:
62
+ # Restore the state of the object
63
+ self.__dict__.update(state)
64
+
55
65
  def _core_args(self) -> tuple[str, str]:
56
66
  # the first two args to all of the funcs in CORE_GETTERS
57
67
  return self.device_name, self.name
@@ -41,7 +41,7 @@ if TYPE_CHECKING:
41
41
  import numpy as np
42
42
  from typing_extensions import Self # py311
43
43
 
44
- __all__ = ["CameraInfo", "Setting", "SettingEvent", "InfoPacket", "decode_image"]
44
+ __all__ = ["CameraInfo", "InfoPacket", "Setting", "SettingEvent", "decode_image"]
45
45
 
46
46
 
47
47
  @dataclass
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: pymmcore-plus
3
- Version: 0.9.3
3
+ Version: 0.13.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
@@ -16,24 +16,24 @@ Classifier: License :: OSI Approved :: BSD License
16
16
  Classifier: Operating System :: OS Independent
17
17
  Classifier: Programming Language :: Python
18
18
  Classifier: Programming Language :: Python :: 3
19
- Classifier: Programming Language :: Python :: 3.8
20
19
  Classifier: Programming Language :: Python :: 3.9
21
20
  Classifier: Programming Language :: Python :: 3.10
22
21
  Classifier: Programming Language :: Python :: 3.11
23
22
  Classifier: Programming Language :: Python :: 3.12
23
+ Classifier: Programming Language :: Python :: 3.13
24
24
  Classifier: Topic :: System :: Hardware
25
25
  Classifier: Topic :: System :: Hardware :: Hardware Drivers
26
26
  Classifier: Topic :: Utilities
27
- Requires-Python: >=3.8
27
+ Requires-Python: >=3.9
28
28
  Requires-Dist: numpy>=1.17.3
29
29
  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; python_version < '3.13'
33
34
  Requires-Dist: typer>=0.4.2
34
35
  Requires-Dist: typing-extensions
35
- Requires-Dist: useq-schema>=0.4.7
36
- Requires-Dist: wrapt>=1.14
36
+ Requires-Dist: useq-schema>=0.6.2
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'
@@ -43,17 +43,27 @@ Requires-Dist: mypy; extra == 'dev'
43
43
  Requires-Dist: pdbpp; extra == 'dev'
44
44
  Requires-Dist: pre-commit; extra == 'dev'
45
45
  Requires-Dist: ruff; extra == 'dev'
46
+ Requires-Dist: tensorstore-stubs; extra == 'dev'
46
47
  Provides-Extra: docs
47
48
  Requires-Dist: mkdocs-material; extra == 'docs'
49
+ Requires-Dist: mkdocs-typer==0.0.3; extra == 'docs'
48
50
  Requires-Dist: mkdocs>=1.4; extra == 'docs'
49
51
  Requires-Dist: mkdocstrings-python==1.1.2; extra == 'docs'
50
52
  Requires-Dist: mkdocstrings==0.22.0; extra == 'docs'
51
53
  Provides-Extra: io
52
54
  Requires-Dist: tifffile>=2021.6.14; extra == 'io'
53
- Requires-Dist: zarr>=2.2; extra == 'io'
55
+ Requires-Dist: zarr<3,>=2.2; extra == 'io'
56
+ Provides-Extra: pyqt5
57
+ Requires-Dist: pyqt5>=5.15.4; extra == 'pyqt5'
58
+ Provides-Extra: pyqt6
59
+ Requires-Dist: pyqt6<6.8,>=6.4.2; extra == 'pyqt6'
60
+ Provides-Extra: pyside2
61
+ Requires-Dist: pyside2>=5.15; extra == 'pyside2'
62
+ Provides-Extra: pyside6
63
+ Requires-Dist: pyside6<6.8,>=6.4.0; extra == 'pyside6'
54
64
  Provides-Extra: test
55
65
  Requires-Dist: msgpack; extra == 'test'
56
- Requires-Dist: pyside6; extra == 'test'
66
+ Requires-Dist: msgspec; (python_version < '3.13') and extra == 'test'
57
67
  Requires-Dist: pytest-cov>=4; extra == 'test'
58
68
  Requires-Dist: pytest-qt>=4; extra == 'test'
59
69
  Requires-Dist: pytest>=7.3.2; extra == 'test'
@@ -61,7 +71,8 @@ Requires-Dist: qtpy>=2; extra == 'test'
61
71
  Requires-Dist: rich; extra == 'test'
62
72
  Requires-Dist: tifffile>=2021.6.14; extra == 'test'
63
73
  Requires-Dist: typer>=0.4.2; extra == 'test'
64
- Requires-Dist: zarr>=2.2; extra == 'test'
74
+ Requires-Dist: xarray; extra == 'test'
75
+ Requires-Dist: zarr<3,>=2.2; extra == 'test'
65
76
  Description-Content-Type: text/markdown
66
77
 
67
78
  # pymmcore-plus
@@ -70,7 +81,7 @@ Description-Content-Type: text/markdown
70
81
  [![PyPI - Python Version](https://img.shields.io/pypi/pyversions/pymmcore-plus)](https://pypi.org/project/pymmcore-plus)
71
82
  [![PyPI](https://img.shields.io/pypi/v/pymmcore-plus.svg?color=green)](https://pypi.org/project/pymmcore-plus)
72
83
  [![Conda](https://img.shields.io/conda/vn/conda-forge/pymmcore-plus)](https://anaconda.org/conda-forge/pymmcore-plus)
73
- [![CI](https://github.com/pymmcore-plus/pymmcore-plus/actions/workflows/test_and_deploy.yml/badge.svg)](https://github.com/pymmcore-plus/pymmcore-plus/actions/workflows/test_and_deploy.yml)
84
+ [![CI](https://github.com/pymmcore-plus/pymmcore-plus/actions/workflows/ci.yml/badge.svg)](https://github.com/pymmcore-plus/pymmcore-plus/actions/workflows/ci.yml)
74
85
  [![docs](https://github.com/pymmcore-plus/pymmcore-plus/actions/workflows/docs.yml/badge.svg)](https://pymmcore-plus.github.io/pymmcore-plus/)
75
86
  [![codecov](https://codecov.io/gh/pymmcore-plus/pymmcore-plus/branch/main/graph/badge.svg)](https://codecov.io/gh/pymmcore-plus/pymmcore-plus)
76
87
  [![Benchmarks](https://img.shields.io/endpoint?url=https://codspeed.io/badge.json)](https://codspeed.io/pymmcore-plus/pymmcore-plus)
@@ -141,8 +152,7 @@ provides a python API to control the C++ core directly, without the need for
141
152
  Java in the loop. Each has its own advantages and disadvantages! With
142
153
  pycro-manager you immediately get the entire existing micro-manager ecosystem
143
154
  and GUI application. With pymmcore-plus you don't need to install Java, and you
144
- have direct access to the memory buffers used by the C++ core, but the GUI
145
- side of things is far less mature.
155
+ have direct access to the memory buffers used by the C++ core.
146
156
 
147
157
  ## Quickstart
148
158
 
@@ -0,0 +1,71 @@
1
+ pymmcore_plus/__init__.py,sha256=9-vK2P2jkJJ2REhCjFDBbJu0wrZM0jvDcf-d2GsjTk0,1415
2
+ pymmcore_plus/_benchmark.py,sha256=YJICxXleFQVbOluJdq4OujnIcTkkuMVzeB8GJ8nUv5I,6011
3
+ pymmcore_plus/_build.py,sha256=RPTAuwCZWGL5IDJj4JZo1DIIouUsIqS3vnbPbG2_bRE,10993
4
+ pymmcore_plus/_cli.py,sha256=FWdIvr6Rh9DVAItFaz9fWx7CbbF8ikOHWICp5h0NHTw,16393
5
+ pymmcore_plus/_logger.py,sha256=d7ldqxY0rGWORKdIzNUiFc9BW6cFBx57kHWtXyY1HE0,5416
6
+ pymmcore_plus/_pymmcore.py,sha256=QGlCEEx2pz5JsRLy3nQX3afAvV-_rm6ptWsv05U5hxI,328
7
+ pymmcore_plus/_util.py,sha256=mz5fuyzOhoMARyKYeri8FnR6eHwXsOh45WNZblewS1E,20435
8
+ pymmcore_plus/install.py,sha256=OLKkssJbQ9VSU0Rclkke0fb4Ng1YKb3Ij9rYYbQuusM,8705
9
+ pymmcore_plus/mocks.py,sha256=ejB1EEnCZs3P2qtIXDcse8Ew4I-PdahgfZgCeFAk7io,1976
10
+ pymmcore_plus/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
+ pymmcore_plus/seq_tester.py,sha256=PJFQmb-JwGhliwIgje9BOUNcaayDsA-2x278xoumfig,3768
12
+ pymmcore_plus/core/__init__.py,sha256=rYHv5JQVMVDlwYD1wodCc5L9ZbpVld1C_swGx4CRogA,1011
13
+ pymmcore_plus/core/_adapter.py,sha256=eu2BhGe_dnoQrIsh-u3poxWXsiF2Y8pfbKIGWbUgOk8,2857
14
+ pymmcore_plus/core/_config.py,sha256=yWwOnW6f37lLt83MnodNce04az-g8YDjyo7BvMiTc8s,10672
15
+ pymmcore_plus/core/_config_group.py,sha256=R-o4xuPDBPQAC3s-mFsiKwHVKWR38L9qq_aoWdPrAq8,8542
16
+ pymmcore_plus/core/_constants.py,sha256=ugXgCKVHt9E2s8outgA1cVgoB2Ea9Io4mcgxu5o_UAA,12617
17
+ pymmcore_plus/core/_device.py,sha256=Pz9Ekhss2c9IBA3B7WyMU2cCwc19Dp_dGVhMkzqUaIE,7762
18
+ pymmcore_plus/core/_metadata.py,sha256=3vb3d36XgNnUY44dpZ4Ccw0tvn4KCinZ8zrnDllmABI,2645
19
+ pymmcore_plus/core/_mmcore_plus.py,sha256=8tjNxpSRfDLmYbNllLgJ2RVbKLpeUsOMb0hw-MPR9BI,91621
20
+ pymmcore_plus/core/_property.py,sha256=QsQEzqOAedR24zEJ1Ge4kwScfT_7NOApVcgz6QxBJrI,8265
21
+ pymmcore_plus/core/_sequencing.py,sha256=Vb6hbRsb8RxSPUAlNSVWTM4Yvg7YYf9ZbewK7u_b-QM,16692
22
+ pymmcore_plus/core/events/__init__.py,sha256=Bb1Ga97GzY2z3fAeJkPs1wxbTXa1o_p6nIKof_UCvZs,1093
23
+ pymmcore_plus/core/events/_device_signal_view.py,sha256=t-NfBdg3E4rms4vDFxkkR5XtrpLxaBT7mfPwkpIsbVk,1079
24
+ pymmcore_plus/core/events/_norm_slot.py,sha256=8DCBoLHGh7cbB1OB19IJYwL6sFBFmkD8IakfBOvFbw8,2907
25
+ pymmcore_plus/core/events/_prop_event_mixin.py,sha256=FvJJnpEKrOR-_Sp3-NNCwFoUUHwmNKiHruo0Y1vybsY,4042
26
+ pymmcore_plus/core/events/_protocol.py,sha256=V4st91mw6LoogII2c05vJxD5SIQU24va86J0iqJWqXU,7528
27
+ pymmcore_plus/core/events/_psygnal.py,sha256=owaKlW2zpvocXDbAW4kHovBoVv4Fjfn-S5oUJrVWsD4,1646
28
+ pymmcore_plus/core/events/_qsignals.py,sha256=gr-GDiSVLhFhSfaoKrdTz2y3I_2IUg62bYDGuGrB3j0,3018
29
+ pymmcore_plus/experimental/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
30
+ pymmcore_plus/experimental/unicore/__init__.py,sha256=OcjUZ4tq-NtWDR5R3JFivsRePliQSIQ7Z92k_8Gfz2Q,361
31
+ pymmcore_plus/experimental/unicore/_device_manager.py,sha256=c5DAMsnK06xOy6G7YjHdUughc7xdFtzeo10woO5G_KE,6418
32
+ pymmcore_plus/experimental/unicore/_proxy.py,sha256=Sl_Jiwd4RlcKgmsrEUNZT38YPFGlQonELAg_n3sfbdo,4020
33
+ pymmcore_plus/experimental/unicore/_unicore.py,sha256=HM1rTpFFAtn5nuO9vJGsYGVkyTzeV-EY2KYdAY7EbWM,29027
34
+ pymmcore_plus/experimental/unicore/devices/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
35
+ pymmcore_plus/experimental/unicore/devices/_device.py,sha256=PfX4BSpVMPXyNCNWkZ0Xy1-72ZZdME5zm7NgtCRu8ts,9751
36
+ pymmcore_plus/experimental/unicore/devices/_properties.py,sha256=yqVyoXb3VSbHahN2mXOIgeKOS7pUQeiJIZ_Y2d55dTI,15387
37
+ pymmcore_plus/experimental/unicore/devices/_stage.py,sha256=Ab4uibYq1cjIBtwcthCxH2FudGq9UMjub-qVeRpApQY,7892
38
+ pymmcore_plus/mda/__init__.py,sha256=RXI4ubqV71lLTFVrj62UpGzXVwOOQfJSiXR0k5tRXdk,298
39
+ pymmcore_plus/mda/_engine.py,sha256=UsnyYmxj1EDPaP2lDiRuYpm2SUNsqlE0514w9hxgrVk,29258
40
+ pymmcore_plus/mda/_protocol.py,sha256=10CDJ9H57oX1z0oqK3eShXyQhufHvvu3_8wdaCYpPIg,3254
41
+ pymmcore_plus/mda/_runner.py,sha256=IeZ34e4rcjRoPayCYJo_xn33yB4kPFltt23ntHE9TVI,16679
42
+ pymmcore_plus/mda/_thread_relay.py,sha256=Ww-9gyvLEzwRhnpL1dpze71wL7IRlhH8K3Q1dmJIxgs,6193
43
+ pymmcore_plus/mda/events/__init__.py,sha256=rHTyhQZJ54dz-KtetvN22GvAY2ilR03x8v4H0qUR070,1191
44
+ pymmcore_plus/mda/events/_protocol.py,sha256=9Q7LjYOgEWQGS8gHMV97UXM9bhoVW2OeyoPyNsQbwzw,1659
45
+ pymmcore_plus/mda/events/_psygnal.py,sha256=TdN1mFGpTPXmEs9iwFKSC1svv87PDZkT2JZvl0tEGrQ,640
46
+ pymmcore_plus/mda/events/_qsignals.py,sha256=tULQg-e_NX197DxJXaWHn1zLJ-4tzc9QyOAnsobEDtA,554
47
+ pymmcore_plus/mda/handlers/_5d_writer_base.py,sha256=c9cA0n8DOBoZcy9asue5eV7jW8hFVC0XEewroFgDNHA,11925
48
+ pymmcore_plus/mda/handlers/__init__.py,sha256=LdO3jf47fb4gXmbP9I8f5xC9Mrfd_-shf86OAaN9LdA,305
49
+ pymmcore_plus/mda/handlers/_img_sequence_writer.py,sha256=fAj6CB90RXYJ2jJIRDFxZgQb-TfwuRPWGCPFopCijRI,11549
50
+ pymmcore_plus/mda/handlers/_ome_tiff_writer.py,sha256=pqqdl3KQd0tH5Gp4rHVgYqqh2Y8iwoKRXTjwq1JLy1E,6239
51
+ pymmcore_plus/mda/handlers/_ome_zarr_writer.py,sha256=cKg3kJR7TId6M2qC1nJMLlxkv5vlfA5XEAlTIr9kt_E,12275
52
+ pymmcore_plus/mda/handlers/_tensorstore_handler.py,sha256=_xaUkDEYoTO29u4iHPQK8ygcCy8CSCXYi1xu5QF8B8E,14829
53
+ pymmcore_plus/mda/handlers/_util.py,sha256=pZydpKAXtQ_gjq5x1yNK1D0hfS7NUL2nH9ivOBg4abc,1600
54
+ pymmcore_plus/metadata/__init__.py,sha256=0o_v53kwR4U_RLlCnr7GD1G6OdFlVuUByIqXiaaM5uk,699
55
+ pymmcore_plus/metadata/functions.py,sha256=EjwB-6UO8c8AUriawhbE7x6ZAR1vJAxc72iYqyes5PQ,12506
56
+ pymmcore_plus/metadata/schema.py,sha256=j7nMwjCBXaAC0zKA2OsF201dsOB_3b2ggjqIa7EiVPQ,17368
57
+ pymmcore_plus/metadata/serialize.py,sha256=hpXJm0tzILELf6OYECMg0sQhuf-h25ob6_DDl-TUUME,3805
58
+ pymmcore_plus/model/__init__.py,sha256=zKZkkSpNK4ERu-VMdi9gvRrj1aXAjNaYxlYB5PdYSg0,479
59
+ pymmcore_plus/model/_config_file.py,sha256=Cw3bF1DLQUbMXVz59ZEUjfSaXcw8Lz6StIagReIKB-k,13280
60
+ pymmcore_plus/model/_config_group.py,sha256=vL_-EWH-Nsb8xTgFqpYIFaJzBk_RDBFchBnQ61DMSvI,3407
61
+ pymmcore_plus/model/_core_device.py,sha256=viwMgrCTZn1XYIyjC8w4xj1XAmoowZmCb93isGbG8BE,2722
62
+ pymmcore_plus/model/_core_link.py,sha256=dsbT0gncfa3TAORSaWUrZR9rcI_nOLX9e5BTmyo-UYo,2737
63
+ pymmcore_plus/model/_device.py,sha256=-0s3NkonDoaMrNy_hn5EDz-c4o33ZiJSQkV_kdBteoo,16115
64
+ pymmcore_plus/model/_microscope.py,sha256=69VV6cuevinOK_LhYEkQygHGesvCZefdn9YNt3mV618,11353
65
+ pymmcore_plus/model/_pixel_size_config.py,sha256=smoOmT54nSkg52RaSQzTFG0YwyMR_SEq_lkS-JyJW9U,3514
66
+ pymmcore_plus/model/_property.py,sha256=NQzNtnEzSCR9ogwx1cfi8X-qbJ_cBSJKdSBAaoKoPQ0,3720
67
+ pymmcore_plus-0.13.0.dist-info/METADATA,sha256=z9fR4Z8rz5jq8Mr8xhuxXyECVKrzdCxhm2_4MiNZgRo,9594
68
+ pymmcore_plus-0.13.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
69
+ pymmcore_plus-0.13.0.dist-info/entry_points.txt,sha256=NtFyndrQzBpUNJyil-8e5hMGke2utAf7mkGavTLcLOY,51
70
+ pymmcore_plus-0.13.0.dist-info/licenses/LICENSE,sha256=OHJjRpOPKKRc7FEnpehNWdR5LRBdBhUtIFG-ZI0dCEA,1522
71
+ pymmcore_plus-0.13.0.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: hatchling 1.21.1
2
+ Generator: hatchling 1.27.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any