fix: energy spin movestate colors never rendered
The movestate property sat on the spinbox's inner QLineEdit, which both themes deliberately paint transparent (QAbstractSpinBox QLineEdit rule) - the state machine worked but no color ever showed, and the tests only asserted the property, not the paint. The property now lives on the spinbox itself with QAbstractSpinBox[movestate=...] added to both themes' rules, verified by a pixel-sampling regression test that grabs the rendered widget through the real stylesheet. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -744,12 +744,15 @@ def _sunrise_stylesheet(overrides: dict[str, str] | None = None) -> str:
|
||||
}
|
||||
|
||||
/* Motor batch-move states (before the invalid rules on purpose: equal
|
||||
specificity, so out-of-range red must come last to win). */
|
||||
QLineEdit[movestate="pending"] {
|
||||
specificity, so out-of-range red must come last to win). Spinboxes
|
||||
(Set Energy) carry the property on the box itself — their inner
|
||||
QLineEdit is transparent by design (rule below), so it can't show
|
||||
the state. */
|
||||
QLineEdit[movestate="pending"], QAbstractSpinBox[movestate="pending"] {
|
||||
background-color: $input_pending_bg;
|
||||
}
|
||||
|
||||
QLineEdit[movestate="moving"] {
|
||||
QLineEdit[movestate="moving"], QAbstractSpinBox[movestate="moving"] {
|
||||
background-color: $input_moving_bg;
|
||||
}
|
||||
|
||||
@@ -1429,11 +1432,11 @@ def _sunset_stylesheet() -> str:
|
||||
background-color: $dark_disabled;
|
||||
}
|
||||
|
||||
QLineEdit[movestate="pending"] {
|
||||
QLineEdit[movestate="pending"], QAbstractSpinBox[movestate="pending"] {
|
||||
background-color: $dark_input_pending_bg;
|
||||
}
|
||||
|
||||
QLineEdit[movestate="moving"] {
|
||||
QLineEdit[movestate="moving"], QAbstractSpinBox[movestate="moving"] {
|
||||
background-color: $dark_input_moving_bg;
|
||||
}
|
||||
|
||||
|
||||
@@ -26,10 +26,10 @@ class SpinMoveState(QObject):
|
||||
"""MotorMoveGroup's state machine for a single QDoubleSpinBox whose apply
|
||||
gate is a button (the Set Energy rows): neutral tracks the readback, a
|
||||
user edit stages pending (orange) and enables the button, the click turns
|
||||
moving (green), and arrival within tol returns to neutral. Colors ride the
|
||||
same movestate QSS via the spinbox's internal QLineEdit, so no new
|
||||
styling. The caller keeps its own clicked connection for the actual send;
|
||||
this object only tracks state."""
|
||||
moving (green), and arrival within tol returns to neutral. The movestate
|
||||
property sits on the spinbox itself — its inner QLineEdit is transparent
|
||||
by theme design, so coloring it shows nothing. The caller keeps its own
|
||||
clicked connection for the actual send; this object only tracks state."""
|
||||
|
||||
def __init__(self, spin: QDoubleSpinBox, button: QPushButton, tol: float, parent=None):
|
||||
super().__init__(parent)
|
||||
@@ -76,11 +76,10 @@ class SpinMoveState(QObject):
|
||||
self._syncing = False
|
||||
|
||||
def _set_state(self, state: str):
|
||||
box = self._spin.lineEdit()
|
||||
if box.property("movestate") != state:
|
||||
box.setProperty("movestate", state)
|
||||
box.style().unpolish(box)
|
||||
box.style().polish(box)
|
||||
if self._spin.property("movestate") != state:
|
||||
self._spin.setProperty("movestate", state)
|
||||
self._spin.style().unpolish(self._spin)
|
||||
self._spin.style().polish(self._spin)
|
||||
self._state = state
|
||||
self._button.setEnabled(state == "pending")
|
||||
|
||||
|
||||
@@ -359,7 +359,7 @@ def test_energy_spin_motor_move_semantics(settings_panel, daq_status_factory):
|
||||
# Same MotorMoveGroup wiring as the Beamline setup row (full state walk
|
||||
# tested there); here: stage -> pending, button sends -> moving, readback
|
||||
# arrival -> neutral.
|
||||
box = settings_panel.energy_spin.lineEdit()
|
||||
box = settings_panel.energy_spin # movestate sits on the spin itself
|
||||
assert not settings_panel.change_energy_button.isEnabled()
|
||||
|
||||
settings_panel.update_daq_status(daq_status_factory()) # readback 12.0 keV
|
||||
|
||||
@@ -55,7 +55,7 @@ def test_energy_spin_motor_move_semantics(qtbot, daq_status_factory):
|
||||
# sends, and the color walks neutral -> pending -> moving -> neutral.
|
||||
panel = MonochromatorPanel()
|
||||
qtbot.addWidget(panel)
|
||||
box = panel.energy_spin.lineEdit()
|
||||
box = panel.energy_spin # movestate sits on the spin, not its inner edit
|
||||
sent = []
|
||||
panel.change_energy.connect(sent.append)
|
||||
assert not panel.change_energy_button.isEnabled() # nothing staged yet
|
||||
|
||||
@@ -92,3 +92,45 @@ def test_update_limits_reranges_box(chi, qtbot):
|
||||
assert box.range_validator.bottom() == -1.0
|
||||
assert box.range_validator.top() == 50.0
|
||||
assert "50" in box.toolTip()
|
||||
|
||||
|
||||
def test_spin_move_state_colors_actually_render(qtbot):
|
||||
# Regression: movestate used to sit on the spin's inner QLineEdit, which
|
||||
# the theme paints transparent — the property asserts passed while no
|
||||
# color ever showed. Sample real pixels through the theme stylesheet.
|
||||
from PySide6.QtGui import QColor
|
||||
from PySide6.QtWidgets import QDoubleSpinBox, QPushButton
|
||||
|
||||
from aare.gui.styles import (
|
||||
INPUT_MOVING_BG,
|
||||
INPUT_PENDING_BG,
|
||||
THEME_SUNRISE,
|
||||
build_app_stylesheet,
|
||||
)
|
||||
from aare.gui.widgets.motor_move_group import SpinMoveState
|
||||
|
||||
spin = QDoubleSpinBox()
|
||||
spin.setRange(4.0, 20.0)
|
||||
spin.setStyleSheet(build_app_stylesheet(THEME_SUNRISE))
|
||||
button = QPushButton()
|
||||
qtbot.addWidget(spin)
|
||||
qtbot.addWidget(button)
|
||||
state = SpinMoveState(spin, button, tol=0.01)
|
||||
spin.resize(140, 24)
|
||||
spin.show()
|
||||
|
||||
def value_area_color():
|
||||
img = spin.grab().toImage()
|
||||
return img.pixelColor(20, img.height() // 2)
|
||||
|
||||
state.update_actual(12.0)
|
||||
neutral = value_area_color()
|
||||
|
||||
spin.setValue(12.4) # user staging
|
||||
assert value_area_color() == QColor(INPUT_PENDING_BG)
|
||||
|
||||
button.click()
|
||||
assert value_area_color() == QColor(INPUT_MOVING_BG)
|
||||
|
||||
state.update_actual(12.398) # arrived within tol
|
||||
assert value_area_color() == neutral
|
||||
|
||||
Reference in New Issue
Block a user