"""Shared constants and small layout helpers for the viewer widgets.""" from PyQt6.QtCore import Qt from PyQt6.QtWidgets import ( QFormLayout, QFrame, QGroupBox, QLabel, QScrollArea, QSizePolicy, QVBoxLayout, QWidget, ) from sras_format import CH1_IDX, CH3_IDX, CH4_IDX # --------------------------------------------------------------------------- # Display constants # --------------------------------------------------------------------------- CH_LABELS = [ "CH1 — RF (FFT peak freq)", "CH3 — Bias A (DC mean)", "CH4 — Bias B (DC mean)", "CH1 — Velocity (SRAS)", ] # Combo index for the derived velocity mode (uses CH1_IDX data) VELOCITY_MODE_IDX = 3 # All modes that operate on CH1 waveforms CH1_DERIVED_MODES = (CH1_IDX, VELOCITY_MODE_IDX) CMAPS = ["gray", "viridis", "plasma", "inferno", "hot", "jet", "RdBu_r", "seismic"] # (mode_str, status-bar unit, colorbar label) per channel index _CHANNEL_DISPLAY = { CH1_IDX: ("RF", "Peak frequency (MHz)", "MHz"), CH3_IDX: ("DC", "DC mean (mV)", "mV"), CH4_IDX: ("DC", "DC mean (mV)", "mV"), VELOCITY_MODE_IDX: ("Velocity", "Velocity (m/s)", "m/s"), } _CSS_HINT = "font-size: 11px; color: #aaa;" _CSS_INFO = "font-size: 11px;" _CSS_MUTED = "color: #888; font-size: 11px;" _CSS_WARN = "color: #e07000; font-size: 11px;" _CSS_BUSY = "color: #4a90d9; font-size: 11px;" # Side-panel column widths (the scroll areas that hold the controls). _LEFT_PANEL_W = 288 _RIGHT_PANEL_W = 272 # Minimum width for a spin box so its value + suffix are never clipped. _SPIN_MIN_W = 96 # --------------------------------------------------------------------------- # Small layout helpers # --------------------------------------------------------------------------- def _wrap_label(text: str = "", css: str | None = None) -> QLabel: """A word-wrapped QLabel that reports its *wrapped* height to the layout. A plain word-wrapped QLabel advertises a single-line minimum height, so in a fixed-width column the layout happily shrinks it and the extra lines get clipped. Enabling height-for-width makes the box layout ask for the real height at the column's width instead. """ lbl = QLabel(text) lbl.setWordWrap(True) sp = lbl.sizePolicy() sp.setVerticalPolicy(QSizePolicy.Policy.Minimum) sp.setHeightForWidth(True) lbl.setSizePolicy(sp) if css: lbl.setStyleSheet(css) return lbl def _group(title: str) -> tuple[QGroupBox, QVBoxLayout]: """A group box with consistent, non-cramped internal margins.""" grp = QGroupBox(title) lay = QVBoxLayout(grp) lay.setContentsMargins(10, 8, 10, 10) lay.setSpacing(6) return grp, lay def _form() -> QFormLayout: """A label/field form layout for a narrow side panel.""" form = QFormLayout() form.setContentsMargins(0, 0, 0, 0) form.setHorizontalSpacing(8) form.setVerticalSpacing(6) form.setLabelAlignment(Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter) form.setFormAlignment(Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignTop) form.setFieldGrowthPolicy( QFormLayout.FieldGrowthPolicy.AllNonFixedFieldsGrow) form.setRowWrapPolicy(QFormLayout.RowWrapPolicy.DontWrapRows) return form def _scroll_panel(inner: QWidget, width: int) -> QScrollArea: """Put a side panel in a fixed-width scroll area. Without this the panels are sized by the window: a short window squeezes the controls past their minimum heights, which is what makes text overlap the widget below it. Scrolling keeps every control at its natural size. """ area = QScrollArea() area.setWidget(inner) area.setWidgetResizable(True) area.setFrameShape(QFrame.Shape.NoFrame) area.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff) area.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded) area.setFixedWidth(width) area.viewport().setAutoFillBackground(False) inner.setAutoFillBackground(False) return area