pymmcore-plus 0.13.4__py3-none-any.whl → 0.13.5__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
pymmcore_plus/_cli.py CHANGED
@@ -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)
@@ -5,8 +5,10 @@ try:
5
5
  from pymmcore_nano import __version__
6
6
 
7
7
  BACKEND = "pymmcore-nano"
8
+ NANO = True
8
9
  except ImportError:
9
10
  from pymmcore import * # noqa F403
10
11
  from pymmcore import __version__ # noqa F401
11
12
 
12
13
  BACKEND = "pymmcore"
14
+ NANO = False
@@ -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
- for k, v in args[0].items():
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
- self._last_config: str | None = None # last loaded config file
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._last_config = str(fpath.resolve())
403
- super().loadSystemConfiguration(self._last_config)
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._last_config
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
- md = Metadata(md)
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.BACKEND == "pymmcore" else img
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.BACKEND == "pymmcore" else img, md
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.BACKEND == "pymmcore" else img
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.
@@ -6,7 +6,7 @@ from ._protocol import PCoreSignaler
6
6
  from ._psygnal import CMMCoreSignaler
7
7
 
8
8
  if TYPE_CHECKING:
9
- from ._qsignals import QCoreSignaler # noqa: TC004
9
+ from ._qsignals import QCoreSignaler
10
10
 
11
11
 
12
12
  __all__ = [
@@ -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}."
@@ -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.
@@ -238,18 +245,15 @@ class MDAEngine(PMDAEngine):
238
245
  if event.keep_shutter_open:
239
246
  ...
240
247
 
241
- if event.x_pos is not None or event.y_pos is not None:
242
- self._set_event_position(event)
248
+ self._set_event_xy_position(event)
249
+
243
250
  if event.z_pos is not None:
244
251
  self._set_event_z(event)
245
252
  if event.slm_image is not None:
246
253
  self._set_event_slm_image(event)
247
- if event.channel is not None:
248
- try:
249
- # possible speedup by setting manually.
250
- self._mmc.setConfig(event.channel.group, event.channel.config)
251
- except Exception as e:
252
- logger.warning("Failed to set channel. %s", e)
254
+
255
+ self._set_event_channel(event)
256
+
253
257
  if event.exposure is not None:
254
258
  try:
255
259
  self._mmc.setExposure(event.exposure)
@@ -413,11 +417,7 @@ class MDAEngine(PMDAEngine):
413
417
  # the call below, we won't be able to query `core.getCurrentConfig()`
414
418
  # not sure that's necessary; and this is here for tests to pass for now,
415
419
  # but this could be removed.
416
- if event.channel is not None:
417
- try:
418
- core.setConfig(event.channel.group, event.channel.config)
419
- except Exception as e:
420
- logger.warning("Failed to set channel. %s", e)
420
+ self._set_event_channel(event)
421
421
 
422
422
  if event.slm_image:
423
423
  self._set_event_slm_image(event)
@@ -430,8 +430,8 @@ class MDAEngine(PMDAEngine):
430
430
  # start sequences or set non-sequenced values
431
431
  if event.x_sequence:
432
432
  core.startXYStageSequence(core.getXYStageDevice())
433
- elif event.x_pos is not None or event.y_pos is not None:
434
- self._set_event_position(event)
433
+ else:
434
+ self._set_event_xy_position(event)
435
435
 
436
436
  if event.z_sequence:
437
437
  core.startStageSequence(core.getFocusDevice())
@@ -594,15 +594,51 @@ class MDAEngine(PMDAEngine):
594
594
 
595
595
  return _perform_full_focus(self._mmc.getZPosition())
596
596
 
597
- def _set_event_position(self, event: MDAEvent) -> None:
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
+
598
603
  # skip if no XY stage device is found
599
604
  if not self._mmc.getXYStageDevice():
600
605
  logger.warning("No XY stage device found. Cannot set XY position.")
601
606
  return
602
607
 
603
- x = event.x_pos if event.x_pos is not None else self._mmc.getXPosition()
604
- y = event.y_pos if event.y_pos is not None else self._mmc.getYPosition()
605
- self._mmc.setXYPosition(x, y)
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)
606
642
 
607
643
  def _set_event_z(self, event: MDAEvent) -> None:
608
644
  # skip if no Z stage device is found
@@ -8,7 +8,7 @@ from ._protocol import PMDASignaler
8
8
  from ._psygnal import MDASignaler
9
9
 
10
10
  if TYPE_CHECKING:
11
- from ._qsignals import QMDASignaler # noqa: TC004
11
+ from ._qsignals import QMDASignaler
12
12
 
13
13
 
14
14
  __all__ = [
@@ -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), (typ, val) = val
92
- return cls(dev, prop, typ, val)
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), (typ, val), count = val
104
- return cls(dev, prop, typ, val, count)
103
+ (dev, prop), (type_, val), count = val
104
+ return cls(dev, prop, type_, val, count)
105
105
 
106
106
 
107
107
  @dataclass
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pymmcore-plus
3
- Version: 0.13.4
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,7 +30,7 @@ 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
+ Requires-Dist: tensorstore<=0.1.71
34
34
  Requires-Dist: typer>=0.4.2
35
35
  Requires-Dist: typing-extensions
36
36
  Requires-Dist: useq-schema>=0.7.0
@@ -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 an impressive
143
- library written by Henry Pinkard designed to make it easier to work with and
144
- control the Java Micro-manager application using python. As such, it requires
145
- Java to be installed and running in the background (either via the micro-manager
146
- GUI application directly, or via a headless process). The python half
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. Each has its own advantages and disadvantages! With
153
- pycro-manager you immediately get the entire existing micro-manager ecosystem
154
- and GUI application. With pymmcore-plus you don't need to install Java, and you
155
- have direct access to the memory buffers used by the C++ core.
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 ](https://pymmcore-plus.github.io/pymmcore-plus/install/) for more details._
196
+ _See [installation documentation](https://pymmcore-plus.github.io/pymmcore-plus/install/) for more details._
195
197
 
196
198
  ### Usage
197
199
 
@@ -1,25 +1,25 @@
1
1
  pymmcore_plus/__init__.py,sha256=9-vK2P2jkJJ2REhCjFDBbJu0wrZM0jvDcf-d2GsjTk0,1415
2
2
  pymmcore_plus/_benchmark.py,sha256=YJICxXleFQVbOluJdq4OujnIcTkkuMVzeB8GJ8nUv5I,6011
3
3
  pymmcore_plus/_build.py,sha256=RPTAuwCZWGL5IDJj4JZo1DIIouUsIqS3vnbPbG2_bRE,10993
4
- pymmcore_plus/_cli.py,sha256=FWdIvr6Rh9DVAItFaz9fWx7CbbF8ikOHWICp5h0NHTw,16393
4
+ pymmcore_plus/_cli.py,sha256=vysJhdBua23NFSILU0CSMs84hjKVbQl4ttUnk5ouMsk,16395
5
5
  pymmcore_plus/_logger.py,sha256=d7ldqxY0rGWORKdIzNUiFc9BW6cFBx57kHWtXyY1HE0,5416
6
- pymmcore_plus/_pymmcore.py,sha256=QGlCEEx2pz5JsRLy3nQX3afAvV-_rm6ptWsv05U5hxI,328
6
+ pymmcore_plus/_pymmcore.py,sha256=HzV-vW7QT0BhkIh7aWwQKwAtmt27iNnHGzXXzg3_6n0,361
7
7
  pymmcore_plus/_util.py,sha256=mz5fuyzOhoMARyKYeri8FnR6eHwXsOh45WNZblewS1E,20435
8
8
  pymmcore_plus/install.py,sha256=OLKkssJbQ9VSU0Rclkke0fb4Ng1YKb3Ij9rYYbQuusM,8705
9
9
  pymmcore_plus/mocks.py,sha256=jNUfmffD1OArtIvEmqWsy7GCrtTpssVF03flH8cEYx8,1867
10
10
  pymmcore_plus/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
- pymmcore_plus/seq_tester.py,sha256=PJFQmb-JwGhliwIgje9BOUNcaayDsA-2x278xoumfig,3768
11
+ pymmcore_plus/seq_tester.py,sha256=ielLx2ZUJrOXVCojk64UXTeKDoARxt8QkQjt5AE5Gng,3776
12
12
  pymmcore_plus/core/__init__.py,sha256=rYHv5JQVMVDlwYD1wodCc5L9ZbpVld1C_swGx4CRogA,1011
13
13
  pymmcore_plus/core/_adapter.py,sha256=eu2BhGe_dnoQrIsh-u3poxWXsiF2Y8pfbKIGWbUgOk8,2857
14
14
  pymmcore_plus/core/_config.py,sha256=yWwOnW6f37lLt83MnodNce04az-g8YDjyo7BvMiTc8s,10672
15
15
  pymmcore_plus/core/_config_group.py,sha256=R-o4xuPDBPQAC3s-mFsiKwHVKWR38L9qq_aoWdPrAq8,8542
16
16
  pymmcore_plus/core/_constants.py,sha256=6foxGbek3tgnUHYUtQ7NCqwIIqqGYW1W56HjrhZqsA0,12829
17
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=I5IqUmR-IgdftHO5dkvzA99kAxRZoj-8_tkTaJcfLNQ,91819
18
+ pymmcore_plus/core/_metadata.py,sha256=L8x1gX_zXPz02BUqc7eqJM_Bey2G0RyX30SOBs2aBNc,2755
19
+ pymmcore_plus/core/_mmcore_plus.py,sha256=XEdoYyMPfk4zJxXqSzVaq7J5_qtngNYP7ebf_blaOQY,92887
20
20
  pymmcore_plus/core/_property.py,sha256=QsQEzqOAedR24zEJ1Ge4kwScfT_7NOApVcgz6QxBJrI,8265
21
21
  pymmcore_plus/core/_sequencing.py,sha256=Vb6hbRsb8RxSPUAlNSVWTM4Yvg7YYf9ZbewK7u_b-QM,16692
22
- pymmcore_plus/core/events/__init__.py,sha256=Bb1Ga97GzY2z3fAeJkPs1wxbTXa1o_p6nIKof_UCvZs,1093
22
+ pymmcore_plus/core/events/__init__.py,sha256=F8r10LEBLrAV8qfkXScSkpqfExdT2XoOx92OqSturpc,1078
23
23
  pymmcore_plus/core/events/_device_signal_view.py,sha256=t-NfBdg3E4rms4vDFxkkR5XtrpLxaBT7mfPwkpIsbVk,1079
24
24
  pymmcore_plus/core/events/_norm_slot.py,sha256=8DCBoLHGh7cbB1OB19IJYwL6sFBFmkD8IakfBOvFbw8,2907
25
25
  pymmcore_plus/core/events/_prop_event_mixin.py,sha256=FvJJnpEKrOR-_Sp3-NNCwFoUUHwmNKiHruo0Y1vybsY,4042
@@ -30,17 +30,17 @@ pymmcore_plus/experimental/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJ
30
30
  pymmcore_plus/experimental/unicore/__init__.py,sha256=OcjUZ4tq-NtWDR5R3JFivsRePliQSIQ7Z92k_8Gfz2Q,361
31
31
  pymmcore_plus/experimental/unicore/_device_manager.py,sha256=c5DAMsnK06xOy6G7YjHdUughc7xdFtzeo10woO5G_KE,6418
32
32
  pymmcore_plus/experimental/unicore/_proxy.py,sha256=Sl_Jiwd4RlcKgmsrEUNZT38YPFGlQonELAg_n3sfbdo,4020
33
- pymmcore_plus/experimental/unicore/_unicore.py,sha256=HM1rTpFFAtn5nuO9vJGsYGVkyTzeV-EY2KYdAY7EbWM,29027
33
+ pymmcore_plus/experimental/unicore/_unicore.py,sha256=3-HkZbbrQxrUgvEx3A6XEStDH9C3c6NdG_Kb8y1KubY,29029
34
34
  pymmcore_plus/experimental/unicore/devices/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
35
35
  pymmcore_plus/experimental/unicore/devices/_device.py,sha256=PfX4BSpVMPXyNCNWkZ0Xy1-72ZZdME5zm7NgtCRu8ts,9751
36
- pymmcore_plus/experimental/unicore/devices/_properties.py,sha256=yqVyoXb3VSbHahN2mXOIgeKOS7pUQeiJIZ_Y2d55dTI,15387
36
+ pymmcore_plus/experimental/unicore/devices/_properties.py,sha256=KuHlSNNpmfj_Q1m1uyfycpE5C63jLg5Mm_D57In2oPo,15389
37
37
  pymmcore_plus/experimental/unicore/devices/_stage.py,sha256=Ab4uibYq1cjIBtwcthCxH2FudGq9UMjub-qVeRpApQY,7892
38
38
  pymmcore_plus/mda/__init__.py,sha256=7VH-MqOcuK1JNSOG9HhES6Ac-Z-LuT8a0f2xPbGEt7w,344
39
- pymmcore_plus/mda/_engine.py,sha256=QxUXlDNITCVILfZ2GGoIDe8iSOaMXn6mswhvnsmUzm8,29914
39
+ pymmcore_plus/mda/_engine.py,sha256=U83PnmbaWChxUCEskKqHVy-Xa8KAZAMuvwPSVTAwsKg,31205
40
40
  pymmcore_plus/mda/_protocol.py,sha256=10CDJ9H57oX1z0oqK3eShXyQhufHvvu3_8wdaCYpPIg,3254
41
41
  pymmcore_plus/mda/_runner.py,sha256=NSOhpll6_WxDLO19FTs19dASJcHcOoVOCy7q_QzX_Ms,18523
42
42
  pymmcore_plus/mda/_thread_relay.py,sha256=Ww-9gyvLEzwRhnpL1dpze71wL7IRlhH8K3Q1dmJIxgs,6193
43
- pymmcore_plus/mda/events/__init__.py,sha256=rHTyhQZJ54dz-KtetvN22GvAY2ilR03x8v4H0qUR070,1191
43
+ pymmcore_plus/mda/events/__init__.py,sha256=v7YsVXzd3cTavFs2v3_PEhjqP4CtuRE0nWUoJA3CvYU,1176
44
44
  pymmcore_plus/mda/events/_protocol.py,sha256=9Q7LjYOgEWQGS8gHMV97UXM9bhoVW2OeyoPyNsQbwzw,1659
45
45
  pymmcore_plus/mda/events/_psygnal.py,sha256=TdN1mFGpTPXmEs9iwFKSC1svv87PDZkT2JZvl0tEGrQ,640
46
46
  pymmcore_plus/mda/events/_qsignals.py,sha256=tULQg-e_NX197DxJXaWHn1zLJ-4tzc9QyOAnsobEDtA,554
@@ -49,7 +49,7 @@ pymmcore_plus/mda/handlers/__init__.py,sha256=TbgpRdcs3BRdCf6uXJlwo_IIbxM6xXaLoc
49
49
  pymmcore_plus/mda/handlers/_img_sequence_writer.py,sha256=XUJovvdWViTkn2VZr4XcovNIuBNZF4J4cCHIdwAs1WE,11639
50
50
  pymmcore_plus/mda/handlers/_ome_tiff_writer.py,sha256=pqqdl3KQd0tH5Gp4rHVgYqqh2Y8iwoKRXTjwq1JLy1E,6239
51
51
  pymmcore_plus/mda/handlers/_ome_zarr_writer.py,sha256=cKg3kJR7TId6M2qC1nJMLlxkv5vlfA5XEAlTIr9kt_E,12275
52
- pymmcore_plus/mda/handlers/_tensorstore_handler.py,sha256=_Hqfgc2I8n97KPT7quU0p4tqSlomtLbTO-e78bIB6hA,15280
52
+ pymmcore_plus/mda/handlers/_tensorstore_handler.py,sha256=rgLyuTJjV1m5j-O5tLm-BggIblJpdrHa_FB17_S7rug,15282
53
53
  pymmcore_plus/mda/handlers/_util.py,sha256=pZydpKAXtQ_gjq5x1yNK1D0hfS7NUL2nH9ivOBg4abc,1600
54
54
  pymmcore_plus/metadata/__init__.py,sha256=0o_v53kwR4U_RLlCnr7GD1G6OdFlVuUByIqXiaaM5uk,699
55
55
  pymmcore_plus/metadata/functions.py,sha256=EjwB-6UO8c8AUriawhbE7x6ZAR1vJAxc72iYqyes5PQ,12506
@@ -64,8 +64,8 @@ pymmcore_plus/model/_device.py,sha256=-0s3NkonDoaMrNy_hn5EDz-c4o33ZiJSQkV_kdBteo
64
64
  pymmcore_plus/model/_microscope.py,sha256=69VV6cuevinOK_LhYEkQygHGesvCZefdn9YNt3mV618,11353
65
65
  pymmcore_plus/model/_pixel_size_config.py,sha256=smoOmT54nSkg52RaSQzTFG0YwyMR_SEq_lkS-JyJW9U,3514
66
66
  pymmcore_plus/model/_property.py,sha256=NQzNtnEzSCR9ogwx1cfi8X-qbJ_cBSJKdSBAaoKoPQ0,3720
67
- pymmcore_plus-0.13.4.dist-info/METADATA,sha256=tmhgzAJdqyf1tO_aUNpfIslpMGlqcp49oZ2-kSX7qNs,9594
68
- pymmcore_plus-0.13.4.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
69
- pymmcore_plus-0.13.4.dist-info/entry_points.txt,sha256=NtFyndrQzBpUNJyil-8e5hMGke2utAf7mkGavTLcLOY,51
70
- pymmcore_plus-0.13.4.dist-info/licenses/LICENSE,sha256=OHJjRpOPKKRc7FEnpehNWdR5LRBdBhUtIFG-ZI0dCEA,1522
71
- pymmcore_plus-0.13.4.dist-info/RECORD,,
67
+ pymmcore_plus-0.13.5.dist-info/METADATA,sha256=HKYfO-wVXV-thoxMJNDaIejT08X5m81K915GzyCEH4c,9710
68
+ pymmcore_plus-0.13.5.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
69
+ pymmcore_plus-0.13.5.dist-info/entry_points.txt,sha256=NtFyndrQzBpUNJyil-8e5hMGke2utAf7mkGavTLcLOY,51
70
+ pymmcore_plus-0.13.5.dist-info/licenses/LICENSE,sha256=OHJjRpOPKKRc7FEnpehNWdR5LRBdBhUtIFG-ZI0dCEA,1522
71
+ pymmcore_plus-0.13.5.dist-info/RECORD,,