From 62fdcb28566bd373c9eacff7e74e18bf91e02d0c Mon Sep 17 00:00:00 2001 From: Hinderling Date: Wed, 13 May 2026 18:19:48 +0200 Subject: [PATCH 1/2] fix: don't write back to core when initializing StateDeviceWidget The initial setCurrentText fired currentIndexChanged -> _on_combo_changed -> mmc.setState() for the already-current state. On Nikon TI scopes that re-applies the objective turret preset on widget init: Z drops, PFS disables, the turret "rotates" to its current position, Z raises. Hardware moves before the user touches anything. Block signals around the initial sync, matching the pattern already used in _refresh_choices and _on_sys_cfg_loaded. --- src/pymmcore_widgets/_deprecated/_device_widget.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/pymmcore_widgets/_deprecated/_device_widget.py b/src/pymmcore_widgets/_deprecated/_device_widget.py index aa8e6afb1..a674fc857 100644 --- a/src/pymmcore_widgets/_deprecated/_device_widget.py +++ b/src/pymmcore_widgets/_deprecated/_device_widget.py @@ -145,7 +145,8 @@ def __init__( self._combo.currentIndexChanged.connect(self._on_combo_changed) self._changing = False self._refresh_choices() - self._combo.setCurrentText(self._mmc.getStateLabel(self._device_label)) + with signals_blocked(self._combo): + self._combo.setCurrentText(self._mmc.getStateLabel(self._device_label)) self.setLayout(QHBoxLayout()) self.layout().setContentsMargins(0, 0, 0, 0) From 0b75db1f502b74fc0c35aaa36a84a1465e25b18d Mon Sep 17 00:00:00 2001 From: Hinderling Date: Thu, 14 May 2026 11:28:24 +0200 Subject: [PATCH 2/2] fix: don't write back to core when initializing StageWidget _set_as_default() calls _set_as_default_btn.setChecked(True) without signals_blocked, which fires the toggled signal -> _on_radiobutton_toggled -> mmc.setProperty(CORE, XY_STAGE/Focus, self._device). On construction, if the device is already the default stage in core, the widget writes the same value back to core, firing a propertyChanged ripple. No hardware motion, but the same init-side- effect anti-pattern as the StateDeviceWidget fix. Block signals around the setChecked calls, matching the pattern used elsewhere in the same widget (_on_radiobutton_toggled, _on_prop_changed). --- src/pymmcore_widgets/control/_stage_widget.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/pymmcore_widgets/control/_stage_widget.py b/src/pymmcore_widgets/control/_stage_widget.py index a3178ec2f..7e2a45ebe 100644 --- a/src/pymmcore_widgets/control/_stage_widget.py +++ b/src/pymmcore_widgets/control/_stage_widget.py @@ -375,12 +375,13 @@ def _on_system_cfg(self) -> None: self._set_as_default() def _set_as_default(self) -> None: - if self._dtype is DeviceType.XYStage: - if self._mmc.getXYStageDevice() == self._device: - self._set_as_default_btn.setChecked(True) - elif self._dtype is DeviceType.Stage: - if self._mmc.getFocusDevice() == self._device: - self._set_as_default_btn.setChecked(True) + with signals_blocked(self._set_as_default_btn): + if self._dtype is DeviceType.XYStage: + if self._mmc.getXYStageDevice() == self._device: + self._set_as_default_btn.setChecked(True) + elif self._dtype is DeviceType.Stage: + if self._mmc.getFocusDevice() == self._device: + self._set_as_default_btn.setChecked(True) @Slot(bool) def _on_radiobutton_toggled(self, state: bool) -> None: