Refactor/parallel and dedup #1
+142
-67
@@ -28,9 +28,9 @@ from PyQt6.QtCore import QObject, Qt, QThread, pyqtSignal
|
|||||||
from PyQt6.QtGui import QAction
|
from PyQt6.QtGui import QAction
|
||||||
from PyQt6.QtWidgets import (
|
from PyQt6.QtWidgets import (
|
||||||
QApplication, QButtonGroup, QCheckBox, QComboBox, QDialog, QDialogButtonBox,
|
QApplication, QButtonGroup, QCheckBox, QComboBox, QDialog, QDialogButtonBox,
|
||||||
QDoubleSpinBox, QFileDialog, QFrame, QGroupBox, QHBoxLayout, QLabel,
|
QDoubleSpinBox, QFileDialog, QFormLayout, QFrame, QGroupBox, QHBoxLayout,
|
||||||
QMainWindow, QProgressDialog, QPushButton, QRadioButton, QSizePolicy,
|
QLabel, QMainWindow, QProgressDialog, QPushButton, QRadioButton, QScrollArea,
|
||||||
QSpinBox, QSplitter, QVBoxLayout, QWidget,
|
QSizePolicy, QSpinBox, QSplitter, QVBoxLayout, QWidget,
|
||||||
)
|
)
|
||||||
|
|
||||||
import sras_compute as compute
|
import sras_compute as compute
|
||||||
@@ -78,6 +78,79 @@ _CSS_MUTED = "color: #888; font-size: 11px;"
|
|||||||
_CSS_WARN = "color: #e07000; font-size: 11px;"
|
_CSS_WARN = "color: #e07000; font-size: 11px;"
|
||||||
_CSS_BUSY = "color: #4a90d9; 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
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# ROI (free quadrilateral in data coordinates)
|
# ROI (free quadrilateral in data coordinates)
|
||||||
@@ -674,6 +747,7 @@ class SrasViewerWindow(QMainWindow):
|
|||||||
super().__init__()
|
super().__init__()
|
||||||
self.setWindowTitle("SRAS Scan Viewer")
|
self.setWindowTitle("SRAS Scan Viewer")
|
||||||
self.resize(1560, 840)
|
self.resize(1560, 840)
|
||||||
|
self.setMinimumSize(960, 560)
|
||||||
self.setAcceptDrops(True)
|
self.setAcceptDrops(True)
|
||||||
|
|
||||||
self._sras: SrasFile | None = None
|
self._sras: SrasFile | None = None
|
||||||
@@ -791,69 +865,69 @@ class SrasViewerWindow(QMainWindow):
|
|||||||
|
|
||||||
def _build_left_panel(self) -> QWidget:
|
def _build_left_panel(self) -> QWidget:
|
||||||
panel = QWidget()
|
panel = QWidget()
|
||||||
panel.setFixedWidth(260)
|
|
||||||
panel_layout = QVBoxLayout(panel)
|
panel_layout = QVBoxLayout(panel)
|
||||||
panel_layout.setContentsMargins(0, 0, 0, 0)
|
panel_layout.setContentsMargins(0, 0, 0, 0)
|
||||||
panel_layout.setSpacing(6)
|
panel_layout.setSpacing(8)
|
||||||
|
|
||||||
# ---- File -------------------------------------------------------
|
# ---- File -------------------------------------------------------
|
||||||
grp_file = QGroupBox("File")
|
grp_file, fl = _group("File")
|
||||||
fl = QVBoxLayout(grp_file)
|
|
||||||
self.btn_open = QPushButton("Open .sras…")
|
self.btn_open = QPushButton("Open .sras…")
|
||||||
self.btn_open.clicked.connect(self._on_open)
|
self.btn_open.clicked.connect(self._on_open)
|
||||||
self.lbl_filename = QLabel("No file loaded")
|
self.lbl_filename = _wrap_label("No file loaded", _CSS_MUTED)
|
||||||
self.lbl_filename.setWordWrap(True)
|
|
||||||
self.lbl_filename.setStyleSheet(_CSS_MUTED)
|
|
||||||
fl.addWidget(self.btn_open)
|
fl.addWidget(self.btn_open)
|
||||||
fl.addWidget(self.lbl_filename)
|
fl.addWidget(self.lbl_filename)
|
||||||
panel_layout.addWidget(grp_file)
|
panel_layout.addWidget(grp_file)
|
||||||
|
|
||||||
# ---- Scan info --------------------------------------------------
|
# ---- Scan info --------------------------------------------------
|
||||||
grp_info = QGroupBox("Scan Info")
|
grp_info, il = _group("Scan Info")
|
||||||
il = QVBoxLayout(grp_info)
|
il.setSpacing(3)
|
||||||
self._info = {}
|
self._info = {}
|
||||||
for key in ("Angles", "Rows", "Frames / row", "Samples / frame",
|
for key in ("Angles", "Rows", "Frames / row", "Samples / frame",
|
||||||
"Sample rate", "X start", "Pixel Δx", "Laser freq"):
|
"Sample rate", "X start", "Pixel Δx", "Laser freq"):
|
||||||
lbl = QLabel(f"{key}: —")
|
lbl = _wrap_label(f"{key}: —", _CSS_INFO)
|
||||||
lbl.setWordWrap(True)
|
|
||||||
lbl.setStyleSheet(_CSS_INFO)
|
|
||||||
il.addWidget(lbl)
|
il.addWidget(lbl)
|
||||||
self._info[key] = lbl
|
self._info[key] = lbl
|
||||||
|
|
||||||
self.lbl_frame_warn = QLabel("") # frame-count / format notes
|
# frame-count / format notes
|
||||||
self.lbl_frame_warn.setWordWrap(True)
|
self.lbl_frame_warn = _wrap_label("", _CSS_WARN)
|
||||||
self.lbl_frame_warn.setStyleSheet(_CSS_WARN)
|
|
||||||
il.addWidget(self.lbl_frame_warn)
|
il.addWidget(self.lbl_frame_warn)
|
||||||
|
|
||||||
self.lbl_dc_precompute = QLabel("") # background DC-precompute progress
|
# background DC-precompute progress
|
||||||
self.lbl_dc_precompute.setWordWrap(True)
|
self.lbl_dc_precompute = _wrap_label("", _CSS_BUSY)
|
||||||
self.lbl_dc_precompute.setStyleSheet(_CSS_BUSY)
|
|
||||||
il.addWidget(self.lbl_dc_precompute)
|
il.addWidget(self.lbl_dc_precompute)
|
||||||
panel_layout.addWidget(grp_info)
|
panel_layout.addWidget(grp_info)
|
||||||
|
|
||||||
# ---- View settings ----------------------------------------------
|
# ---- View settings ----------------------------------------------
|
||||||
grp_view = QGroupBox("View Settings")
|
grp_view, vl = _group("View Settings")
|
||||||
vl = QVBoxLayout(grp_view)
|
|
||||||
|
view_form = _form()
|
||||||
|
|
||||||
ar = QHBoxLayout()
|
|
||||||
ar.addWidget(QLabel("Angle:"))
|
|
||||||
self.spin_angle = QSpinBox()
|
self.spin_angle = QSpinBox()
|
||||||
self.spin_angle.setRange(0, 0)
|
self.spin_angle.setRange(0, 0)
|
||||||
self.spin_angle.setEnabled(False)
|
self.spin_angle.setEnabled(False)
|
||||||
|
self.spin_angle.setMinimumWidth(64)
|
||||||
self.spin_angle.editingFinished.connect(self._on_view_changed)
|
self.spin_angle.editingFinished.connect(self._on_view_changed)
|
||||||
self.lbl_angle_deg = QLabel("—")
|
self.lbl_angle_deg = QLabel("—")
|
||||||
|
angle_field = QWidget()
|
||||||
|
ar = QHBoxLayout(angle_field)
|
||||||
|
ar.setContentsMargins(0, 0, 0, 0)
|
||||||
|
ar.setSpacing(6)
|
||||||
ar.addWidget(self.spin_angle)
|
ar.addWidget(self.spin_angle)
|
||||||
ar.addWidget(self.lbl_angle_deg)
|
ar.addWidget(self.lbl_angle_deg)
|
||||||
vl.addLayout(ar)
|
ar.addStretch()
|
||||||
|
view_form.addRow("Angle:", angle_field)
|
||||||
|
|
||||||
cr = QHBoxLayout()
|
|
||||||
cr.addWidget(QLabel("Channel:"))
|
|
||||||
self.combo_channel = QComboBox()
|
self.combo_channel = QComboBox()
|
||||||
self.combo_channel.addItems(CH_LABELS)
|
self.combo_channel.addItems(CH_LABELS)
|
||||||
self.combo_channel.setEnabled(False)
|
self.combo_channel.setEnabled(False)
|
||||||
|
self.combo_channel.setSizePolicy(QSizePolicy.Policy.Expanding,
|
||||||
|
QSizePolicy.Policy.Fixed)
|
||||||
|
self.combo_channel.setSizeAdjustPolicy(
|
||||||
|
QComboBox.SizeAdjustPolicy.AdjustToMinimumContentsLengthWithIcon)
|
||||||
|
self.combo_channel.setMinimumContentsLength(12)
|
||||||
self.combo_channel.currentIndexChanged.connect(self._on_channel_changed)
|
self.combo_channel.currentIndexChanged.connect(self._on_channel_changed)
|
||||||
cr.addWidget(self.combo_channel)
|
view_form.addRow("Channel:", self.combo_channel)
|
||||||
vl.addLayout(cr)
|
vl.addLayout(view_form)
|
||||||
|
|
||||||
sep = QFrame()
|
sep = QFrame()
|
||||||
sep.setFrameShape(QFrame.Shape.HLine)
|
sep.setFrameShape(QFrame.Shape.HLine)
|
||||||
@@ -861,10 +935,8 @@ class SrasViewerWindow(QMainWindow):
|
|||||||
vl.addWidget(sep)
|
vl.addWidget(sep)
|
||||||
|
|
||||||
# DC threshold (for RF / CH1 masking)
|
# DC threshold (for RF / CH1 masking)
|
||||||
self.grp_threshold = QGroupBox("RF Mask Threshold (CH1 only)")
|
self.grp_threshold, tl = _group("RF Mask Threshold (CH1 only)")
|
||||||
tl = QVBoxLayout(self.grp_threshold)
|
thr_form = _form()
|
||||||
thr_row = QHBoxLayout()
|
|
||||||
thr_row.addWidget(QLabel("DC threshold:"))
|
|
||||||
self.spin_threshold_mv = QDoubleSpinBox()
|
self.spin_threshold_mv = QDoubleSpinBox()
|
||||||
self.spin_threshold_mv.setRange(-500.0, 500.0)
|
self.spin_threshold_mv.setRange(-500.0, 500.0)
|
||||||
self.spin_threshold_mv.setDecimals(3)
|
self.spin_threshold_mv.setDecimals(3)
|
||||||
@@ -872,11 +944,12 @@ class SrasViewerWindow(QMainWindow):
|
|||||||
self.spin_threshold_mv.setSuffix(" mV")
|
self.spin_threshold_mv.setSuffix(" mV")
|
||||||
self.spin_threshold_mv.setValue(50.0)
|
self.spin_threshold_mv.setValue(50.0)
|
||||||
self.spin_threshold_mv.setEnabled(False)
|
self.spin_threshold_mv.setEnabled(False)
|
||||||
|
self.spin_threshold_mv.setMinimumWidth(_SPIN_MIN_W)
|
||||||
self.spin_threshold_mv.editingFinished.connect(self._on_threshold_changed)
|
self.spin_threshold_mv.editingFinished.connect(self._on_threshold_changed)
|
||||||
thr_row.addWidget(self.spin_threshold_mv)
|
thr_form.addRow("DC threshold:", self.spin_threshold_mv)
|
||||||
tl.addLayout(thr_row)
|
tl.addLayout(thr_form)
|
||||||
self.lbl_threshold_adc = QLabel(f"≈ {mv_to_adc(50.0):.1f} ADC counts")
|
self.lbl_threshold_adc = _wrap_label(
|
||||||
self.lbl_threshold_adc.setStyleSheet(_CSS_MUTED)
|
f"≈ {mv_to_adc(50.0):.1f} ADC counts", _CSS_MUTED)
|
||||||
tl.addWidget(self.lbl_threshold_adc)
|
tl.addWidget(self.lbl_threshold_adc)
|
||||||
vl.addWidget(self.grp_threshold)
|
vl.addWidget(self.grp_threshold)
|
||||||
|
|
||||||
@@ -913,8 +986,7 @@ class SrasViewerWindow(QMainWindow):
|
|||||||
panel_layout.addWidget(grp_view)
|
panel_layout.addWidget(grp_view)
|
||||||
|
|
||||||
# ---- ROI ---------------------------------------------------------
|
# ---- ROI ---------------------------------------------------------
|
||||||
grp_roi = QGroupBox("ROI (Region of Interest)")
|
grp_roi, rl = _group("ROI (Region of Interest)")
|
||||||
rl = QVBoxLayout(grp_roi)
|
|
||||||
|
|
||||||
self.btn_draw_roi = QPushButton("Draw ROI")
|
self.btn_draw_roi = QPushButton("Draw ROI")
|
||||||
self.btn_draw_roi.setCheckable(True)
|
self.btn_draw_roi.setCheckable(True)
|
||||||
@@ -943,24 +1015,26 @@ class SrasViewerWindow(QMainWindow):
|
|||||||
self.btn_export_roi.clicked.connect(self._on_export_roi_csv)
|
self.btn_export_roi.clicked.connect(self._on_export_roi_csv)
|
||||||
rl.addWidget(self.btn_export_roi)
|
rl.addWidget(self.btn_export_roi)
|
||||||
|
|
||||||
self.lbl_roi_center = QLabel("centroid: —")
|
self.lbl_roi_center = _wrap_label("centroid: —", _CSS_HINT)
|
||||||
self.lbl_roi_size = QLabel("bbox: —")
|
self.lbl_roi_size = _wrap_label("bbox: —", _CSS_HINT)
|
||||||
self.lbl_roi_npix = QLabel("pixels inside: —")
|
self.lbl_roi_npix = _wrap_label("pixels inside: —", _CSS_HINT)
|
||||||
for lbl in (self.lbl_roi_center, self.lbl_roi_size, self.lbl_roi_npix):
|
for lbl in (self.lbl_roi_center, self.lbl_roi_size, self.lbl_roi_npix):
|
||||||
lbl.setStyleSheet(_CSS_HINT)
|
|
||||||
rl.addWidget(lbl)
|
rl.addWidget(lbl)
|
||||||
|
|
||||||
panel_layout.addWidget(grp_roi)
|
panel_layout.addWidget(grp_roi)
|
||||||
panel_layout.addStretch()
|
panel_layout.addStretch()
|
||||||
return panel
|
return _scroll_panel(panel, _LEFT_PANEL_W)
|
||||||
|
|
||||||
def _build_canvases(self) -> QWidget:
|
def _build_canvases(self) -> QWidget:
|
||||||
splitter = QSplitter(Qt.Orientation.Vertical)
|
splitter = QSplitter(Qt.Orientation.Vertical)
|
||||||
|
splitter.setChildrenCollapsible(False)
|
||||||
|
|
||||||
img_widget = QWidget()
|
img_widget = QWidget()
|
||||||
img_vl = QVBoxLayout(img_widget)
|
img_vl = QVBoxLayout(img_widget)
|
||||||
img_vl.setContentsMargins(0, 0, 0, 0)
|
img_vl.setContentsMargins(0, 0, 0, 0)
|
||||||
|
img_vl.setSpacing(4)
|
||||||
self.image_canvas = ImageCanvas()
|
self.image_canvas = ImageCanvas()
|
||||||
|
self.image_canvas.setMinimumHeight(220)
|
||||||
self.image_canvas.pixel_clicked.connect(self._on_pixel_clicked)
|
self.image_canvas.pixel_clicked.connect(self._on_pixel_clicked)
|
||||||
self.image_canvas.roi_changed.connect(self._update_roi_ui)
|
self.image_canvas.roi_changed.connect(self._update_roi_ui)
|
||||||
self.image_canvas.draw_mode_changed.connect(self._on_draw_mode_changed)
|
self.image_canvas.draw_mode_changed.connect(self._on_draw_mode_changed)
|
||||||
@@ -971,24 +1045,26 @@ class SrasViewerWindow(QMainWindow):
|
|||||||
wave_widget = QWidget()
|
wave_widget = QWidget()
|
||||||
wave_vl = QVBoxLayout(wave_widget)
|
wave_vl = QVBoxLayout(wave_widget)
|
||||||
wave_vl.setContentsMargins(0, 0, 0, 0)
|
wave_vl.setContentsMargins(0, 0, 0, 0)
|
||||||
|
wave_vl.setSpacing(4)
|
||||||
self.lbl_wave_hint = QLabel(
|
self.lbl_wave_hint = QLabel(
|
||||||
"Click a pixel in the image above to inspect its waveform.")
|
"Click a pixel in the image above to inspect its waveform.")
|
||||||
self.lbl_wave_hint.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
self.lbl_wave_hint.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||||
self.lbl_wave_hint.setStyleSheet(_CSS_MUTED)
|
self.lbl_wave_hint.setStyleSheet(_CSS_MUTED)
|
||||||
self.wave_canvas = WaveformCanvas()
|
self.wave_canvas = WaveformCanvas()
|
||||||
|
self.wave_canvas.setMinimumHeight(150)
|
||||||
wave_vl.addWidget(self.lbl_wave_hint)
|
wave_vl.addWidget(self.lbl_wave_hint)
|
||||||
wave_vl.addWidget(self.wave_canvas)
|
wave_vl.addWidget(self.wave_canvas)
|
||||||
splitter.addWidget(wave_widget)
|
splitter.addWidget(wave_widget)
|
||||||
|
|
||||||
|
splitter.setStretchFactor(0, 3)
|
||||||
|
splitter.setStretchFactor(1, 1)
|
||||||
splitter.setSizes([580, 250])
|
splitter.setSizes([580, 250])
|
||||||
return splitter
|
return splitter
|
||||||
|
|
||||||
def _build_right_panel(self) -> QWidget:
|
def _build_right_panel(self) -> QWidget:
|
||||||
# Velocity settings (visible only in velocity mode)
|
# Velocity settings (visible only in velocity mode)
|
||||||
self.grp_velocity = QGroupBox("Velocity Settings (CH1 only)")
|
self.grp_velocity, vel_l = _group("Velocity Settings (CH1 only)")
|
||||||
vel_l = QVBoxLayout(self.grp_velocity)
|
vel_form = _form()
|
||||||
grat_row = QHBoxLayout()
|
|
||||||
grat_row.addWidget(QLabel("Grating size:"))
|
|
||||||
self.spin_grating_um = QDoubleSpinBox()
|
self.spin_grating_um = QDoubleSpinBox()
|
||||||
self.spin_grating_um.setRange(0.1, 1000.0)
|
self.spin_grating_um.setRange(0.1, 1000.0)
|
||||||
self.spin_grating_um.setDecimals(2)
|
self.spin_grating_um.setDecimals(2)
|
||||||
@@ -996,53 +1072,52 @@ class SrasViewerWindow(QMainWindow):
|
|||||||
self.spin_grating_um.setSuffix(" µm")
|
self.spin_grating_um.setSuffix(" µm")
|
||||||
self.spin_grating_um.setValue(25)
|
self.spin_grating_um.setValue(25)
|
||||||
self.spin_grating_um.setEnabled(False)
|
self.spin_grating_um.setEnabled(False)
|
||||||
|
self.spin_grating_um.setMinimumWidth(_SPIN_MIN_W)
|
||||||
self.spin_grating_um.editingFinished.connect(self._on_grating_changed)
|
self.spin_grating_um.editingFinished.connect(self._on_grating_changed)
|
||||||
grat_row.addWidget(self.spin_grating_um)
|
vel_form.addRow("Grating size:", self.spin_grating_um)
|
||||||
vel_l.addLayout(grat_row)
|
vel_l.addLayout(vel_form)
|
||||||
lbl_formula = QLabel("v (m/s) = freq (MHz) × grating (µm)")
|
vel_l.addWidget(_wrap_label("v (m/s) = freq (MHz) × grating (µm)",
|
||||||
lbl_formula.setStyleSheet("font-size: 10px; color: #888;")
|
"font-size: 10px; color: #888;"))
|
||||||
vel_l.addWidget(lbl_formula)
|
|
||||||
self.grp_velocity.setVisible(False)
|
self.grp_velocity.setVisible(False)
|
||||||
|
|
||||||
grp_display = QGroupBox("Display Options")
|
grp_display, dl = _group("Display Options")
|
||||||
dl = QVBoxLayout(grp_display)
|
|
||||||
|
|
||||||
cmr = QHBoxLayout()
|
cmap_form = _form()
|
||||||
cmr.addWidget(QLabel("Colormap:"))
|
|
||||||
self.combo_cmap = QComboBox()
|
self.combo_cmap = QComboBox()
|
||||||
self.combo_cmap.addItems(CMAPS)
|
self.combo_cmap.addItems(CMAPS)
|
||||||
self.combo_cmap.setCurrentText("gray")
|
self.combo_cmap.setCurrentText("gray")
|
||||||
self.combo_cmap.setEnabled(False)
|
self.combo_cmap.setEnabled(False)
|
||||||
|
self.combo_cmap.setSizePolicy(QSizePolicy.Policy.Expanding,
|
||||||
|
QSizePolicy.Policy.Fixed)
|
||||||
self.combo_cmap.currentIndexChanged.connect(self._on_cmap_changed)
|
self.combo_cmap.currentIndexChanged.connect(self._on_cmap_changed)
|
||||||
cmr.addWidget(self.combo_cmap)
|
cmap_form.addRow("Colormap:", self.combo_cmap)
|
||||||
dl.addLayout(cmr)
|
dl.addLayout(cmap_form)
|
||||||
|
|
||||||
self.chk_auto = QCheckBox("Auto-scale colormap")
|
self.chk_auto = QCheckBox("Auto-scale colormap")
|
||||||
self.chk_auto.setChecked(True)
|
self.chk_auto.setChecked(True)
|
||||||
self.chk_auto.toggled.connect(self._on_autoscale_toggled)
|
self.chk_auto.toggled.connect(self._on_autoscale_toggled)
|
||||||
dl.addWidget(self.chk_auto)
|
dl.addWidget(self.chk_auto)
|
||||||
|
|
||||||
|
range_form = _form()
|
||||||
for label, attr in (("min:", "spin_vmin"), ("max:", "spin_vmax")):
|
for label, attr in (("min:", "spin_vmin"), ("max:", "spin_vmax")):
|
||||||
row = QHBoxLayout()
|
|
||||||
row.addWidget(QLabel(label))
|
|
||||||
spin = QDoubleSpinBox()
|
spin = QDoubleSpinBox()
|
||||||
spin.setRange(-1e9, 1e9)
|
spin.setRange(-1e9, 1e9)
|
||||||
spin.setDecimals(4)
|
spin.setDecimals(4)
|
||||||
spin.setEnabled(False)
|
spin.setEnabled(False)
|
||||||
|
spin.setMinimumWidth(_SPIN_MIN_W)
|
||||||
spin.editingFinished.connect(self._on_manual_range_changed)
|
spin.editingFinished.connect(self._on_manual_range_changed)
|
||||||
setattr(self, attr, spin)
|
setattr(self, attr, spin)
|
||||||
row.addWidget(spin)
|
range_form.addRow(label, spin)
|
||||||
dl.addLayout(row)
|
dl.addLayout(range_form)
|
||||||
|
|
||||||
right_panel = QWidget()
|
right_panel = QWidget()
|
||||||
right_panel.setFixedWidth(270)
|
|
||||||
layout = QVBoxLayout(right_panel)
|
layout = QVBoxLayout(right_panel)
|
||||||
layout.setContentsMargins(0, 0, 0, 0)
|
layout.setContentsMargins(0, 0, 0, 0)
|
||||||
layout.setSpacing(6)
|
layout.setSpacing(8)
|
||||||
layout.addWidget(self.grp_velocity)
|
layout.addWidget(self.grp_velocity)
|
||||||
layout.addWidget(grp_display)
|
layout.addWidget(grp_display)
|
||||||
layout.addStretch()
|
layout.addStretch()
|
||||||
return right_panel
|
return _scroll_panel(right_panel, _RIGHT_PANEL_W)
|
||||||
|
|
||||||
def _build_menus(self):
|
def _build_menus(self):
|
||||||
menubar = self.menuBar()
|
menubar = self.menuBar()
|
||||||
|
|||||||
Reference in New Issue
Block a user