Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 31 additions & 2 deletions src/pymmcore_widgets/device_properties/_property_widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,15 @@ def __init__(self, parent: QWidget | None = None) -> None:
self.setKeyboardTracking(False) # Don't emit while typing
self.setRange(DEFAULT_FLOAT_MIN, DEFAULT_FLOAT_MAX)
self.setDecimals(4)
self._max_decimals: int | None = None
_block_wheel(self)

def setMaxDecimals(self, n: int | None) -> None:
"""Cap auto-expansion of decimals. None means no cap."""
self._max_decimals = n
if n is not None and self.decimals() > n:
self.setDecimals(n)

def textFromValue(self, v: float) -> str:
# Format with precision, strip trailing zeros
text = f"{v:.{self.decimals()}f}".rstrip("0").rstrip(".")
Expand All @@ -117,11 +124,12 @@ def setValue(self, val: float | str) -> None:
if isinstance(val, str):
val = float(val or 0)
# Auto-adjust decimals to show value properly
val_str = f"{val:.10f}".rstrip("0").rstrip(".")
cap = self._max_decimals if self._max_decimals is not None else 10
val_str = f"{val:.{cap}f}".rstrip("0").rstrip(".")
if "." in val_str:
dec = len(val_str.split(".")[1])
if dec > self.decimals():
self.setDecimals(min(dec, 10))
self.setDecimals(dec)
super().setValue(val)


Expand Down Expand Up @@ -151,9 +159,11 @@ def __init__(
self,
is_float: bool = False,
parent: QWidget | None = None,
auto_expand: bool = False,
) -> None:
super().__init__(parent)
self._is_float = is_float
self._auto_expand = auto_expand
self._scale = 1.0 # Calculated in setRange for floats
self._value: int | float = 0 # Store exact value to avoid precision loss

Expand Down Expand Up @@ -186,6 +196,10 @@ def __init__(

self._updating = False

def spinBox(self) -> QSpinBox | QDoubleSpinBox:
"""Return the spinbox widget."""
return self._spinbox

def setRange(self, minimum: float, maximum: float) -> None:
"""Set the range for both slider and spinbox."""
if self._is_float:
Expand Down Expand Up @@ -236,6 +250,8 @@ def _on_spinbox_changed(self, val: float) -> None:
# Don't update if parent signals are blocked (being set programmatically)
if self.signalsBlocked():
return
if self._auto_expand:
self._maybe_expand_slider(val)
with _update_blocker(self):
self._value = val # Update stored value
if self._is_float:
Expand All @@ -244,6 +260,19 @@ def _on_spinbox_changed(self, val: float) -> None:
self._slider.setValue(val)
self.valueChanged.emit(val)

def _maybe_expand_slider(self, val: float) -> None:
"""Expand slider range if the spinbox value exceeds it."""
if self._is_float:
smax = self._slider.maximum() / self._scale
smin = self._slider.minimum() / self._scale
else:
smax = self._slider.maximum()
smin = self._slider.minimum()
if val > smax:
self.setRange(smin, val * 1.2)
elif val < smin:
self.setRange(val * 1.2, smax)


class ChoiceComboBox(QComboBox):
"""Combobox for properties with allowed values."""
Expand Down
2 changes: 1 addition & 1 deletion src/pymmcore_widgets/mda/_core_mda.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
)
from pymmcore_widgets.useq_widgets._positions import AF_PER_POS_TOOLTIP
from pymmcore_widgets.useq_widgets._time import TimePlanWidget
from pymmcore_widgets.useq_widgets._z import Mode
from pymmcore_widgets.useq_widgets._zplan_widget import Mode

from ._core_channels import CoreConnectedChannelTable
from ._core_grid import CoreConnectedGridPlanWidget
Expand Down
36 changes: 8 additions & 28 deletions src/pymmcore_widgets/mda/_core_z.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@

from pymmcore_plus import CMMCorePlus

from pymmcore_widgets.useq_widgets._z import ROW_TOP_BOTTOM, Mode, ZPlanWidget

from ._xy_bounds import MarkVisit
from pymmcore_widgets.useq_widgets._zplan_widget import Mode, ZPlanWidget

if TYPE_CHECKING:
from qtpy.QtWidgets import QWidget
Expand All @@ -29,41 +27,23 @@ class CoreConnectedZPlanWidget(ZPlanWidget):
def __init__(
self, mmcore: CMMCorePlus | None = None, parent: QWidget | None = None
) -> None:
self.bottom_btn = MarkVisit(
"mdi:arrow-collapse-down", mark_text="Mark Bottom", icon_size=16
)
self.top_btn = MarkVisit(
"mdi:arrow-collapse-up", mark_text="Mark Top", icon_size=16
)

super().__init__(parent)
self._mmc = mmcore or CMMCorePlus.instance()

self.bottom_btn.mark.clicked.connect(self._mark_bottom)
self.top_btn.mark.clicked.connect(self._mark_top)
self.bottom_btn.visit.clicked.connect(self._visit_bottom)
self.top_btn.visit.clicked.connect(self._visit_top)

row = ROW_TOP_BOTTOM + 1 # --------------- Bottom / Top parameters
self._grid_layout.addWidget(self.bottom_btn, row, 1)
self._grid_layout.addWidget(self.top_btn, row, 4)
self._btn_mark_top.clicked.connect(self._mark_top)
self._btn_mark_bot.clicked.connect(self._mark_bottom)

def setMode(
self,
mode: Mode | Literal["top_bottom", "range_around", "above_below"],
) -> None:
super().setMode(mode)
self.bottom_btn.setVisible(self._mode == Mode.TOP_BOTTOM)
self.top_btn.setVisible(self._mode == Mode.TOP_BOTTOM)

def _mark_bottom(self) -> None:
self.bottom.setValue(self._mmc.getZPosition())
is_tb = self._mode == Mode.TOP_BOTTOM
self._btn_mark_top.setVisible(is_tb)
self._btn_mark_bot.setVisible(is_tb)

def _mark_top(self) -> None:
self.top.setValue(self._mmc.getZPosition())

def _visit_bottom(self) -> None:
self._mmc.setZPosition(self.bottom.value())

def _visit_top(self) -> None:
self._mmc.setZPosition(self.top.value())
def _mark_bottom(self) -> None:
self.bottom.setValue(self._mmc.getZPosition())
2 changes: 1 addition & 1 deletion src/pymmcore_widgets/useq_widgets/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
from ._positions import PositionTable
from ._time import TimePlanWidget
from ._well_plate_widget import WellPlateWidget
from ._z import ZPlanWidget
from ._zplan_widget import ZPlanWidget
from .points_plans import PointsPlanWidget

__all__ = [
Expand Down
2 changes: 1 addition & 1 deletion src/pymmcore_widgets/useq_widgets/_mda_sequence.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
from pymmcore_widgets.useq_widgets._grid import GridPlanWidget
from pymmcore_widgets.useq_widgets._positions import AF_PER_POS_TOOLTIP, PositionTable
from pymmcore_widgets.useq_widgets._time import TimePlanWidget
from pymmcore_widgets.useq_widgets._z import Mode, ZPlanWidget
from pymmcore_widgets.useq_widgets._zplan_widget import Mode, ZPlanWidget

if TYPE_CHECKING:
from collections.abc import Sequence
Expand Down
Loading
Loading