|
|
@@ -111,7 +111,6 @@ class SeqInterpTab(QWidget):
|
|
|
root_layout.setContentsMargins(0, 0, 0, 0)
|
|
|
root_layout.setSpacing(0)
|
|
|
root_layout.addWidget(self._build_button_bar())
|
|
|
- root_layout.addWidget(self._build_params_bar())
|
|
|
root_layout.addWidget(self._build_seq_status_bar())
|
|
|
root_layout.addWidget(self._build_main_splitter(), stretch=1)
|
|
|
root_layout.addWidget(self._build_tab_status_bar())
|
|
|
@@ -219,46 +218,6 @@ class SeqInterpTab(QWidget):
|
|
|
self._button_bar = bar
|
|
|
return bar
|
|
|
|
|
|
- def _build_params_bar(self) -> QWidget:
|
|
|
- """Second toolbar row: per-channel ADC ranges + averaging."""
|
|
|
- bar = QWidget()
|
|
|
- bar.setObjectName("ParamsBar")
|
|
|
- p = theme.palette()
|
|
|
- bar.setStyleSheet(
|
|
|
- f"#ParamsBar {{ background: {p['surface']}; border-bottom: 1px solid {p['border2']}; }}"
|
|
|
- )
|
|
|
- lay = QHBoxLayout(bar)
|
|
|
- lay.setContentsMargins(6, 3, 6, 3)
|
|
|
- lay.setSpacing(6)
|
|
|
-
|
|
|
- def sep() -> QFrame:
|
|
|
- f = QFrame()
|
|
|
- f.setFrameShape(QFrame.VLine)
|
|
|
- f.setFrameShadow(QFrame.Sunken)
|
|
|
- f.setFixedWidth(2)
|
|
|
- return f
|
|
|
-
|
|
|
- # -- Averaging --------------------------------------------------------
|
|
|
- self._avg_check = QCheckBox("Усреднение")
|
|
|
- self._avg_check.setToolTip(
|
|
|
- "Включить усреднение (iadc.averaging).\nВыключено = 1."
|
|
|
- )
|
|
|
- self._avg_check.setChecked(False)
|
|
|
- self._avg_check.toggled.connect(self._on_avg_toggled)
|
|
|
- lay.addWidget(self._avg_check)
|
|
|
-
|
|
|
- self._avg_spin = QSpinBox()
|
|
|
- self._avg_spin.setRange(2, 9999)
|
|
|
- self._avg_spin.setValue(10)
|
|
|
- self._avg_spin.setToolTip("Количество усреднений")
|
|
|
- self._avg_spin.setEnabled(False)
|
|
|
- self._avg_spin.setFixedWidth(60)
|
|
|
- self._avg_spin.valueChanged.connect(self._on_avg_value_changed)
|
|
|
- lay.addWidget(self._avg_spin)
|
|
|
-
|
|
|
- lay.addStretch()
|
|
|
- self._params_bar = bar
|
|
|
- return bar
|
|
|
|
|
|
def _build_seq_status_bar(self) -> QWidget:
|
|
|
bar = QWidget()
|
|
|
@@ -331,21 +290,11 @@ class SeqInterpTab(QWidget):
|
|
|
self._controls = DelayControlsPanel()
|
|
|
lay.addWidget(self._controls)
|
|
|
|
|
|
- # Warnings are shown in PreviewPanel (right panel) — no separate widget needed here
|
|
|
+ # Warnings are shown in PreviewPanel (right panel)
|
|
|
self._warn_list = QListWidget() # kept for _refresh_warnings compatibility
|
|
|
|
|
|
- # -- Per-channel ADC range (lives here, not in the toolbar) -----------
|
|
|
- self._ch_range_grp = TrGroupBox("grp_adc_ranges")
|
|
|
- self._ch_range_grp.setTitle("АЦП диапазоны")
|
|
|
- self._ch_range_form = QFormLayout(self._ch_range_grp)
|
|
|
- self._ch_range_form.setFieldGrowthPolicy(QFormLayout.AllNonFixedFieldsGrow)
|
|
|
- self._ch_range_form.setLabelAlignment(Qt.AlignRight | Qt.AlignVCenter)
|
|
|
- self._ch_range_combos: list[QComboBox] = []
|
|
|
- lay.addWidget(self._ch_range_grp)
|
|
|
-
|
|
|
- # Initialise with hw_config defaults
|
|
|
- _default_ranges = self._read_hw_config_channel_ranges()
|
|
|
- self._rebuild_ch_range_combos(_default_ranges)
|
|
|
+ # -- Collapsible ADC section (channels + averaging) -------------------
|
|
|
+ lay.addWidget(self._build_adc_section())
|
|
|
|
|
|
lay.addStretch()
|
|
|
|
|
|
@@ -355,6 +304,88 @@ class SeqInterpTab(QWidget):
|
|
|
scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
|
|
|
return scroll
|
|
|
|
|
|
+ def _build_adc_section(self) -> QWidget:
|
|
|
+ """
|
|
|
+ Collapsible section for per-channel ADC ranges + averaging.
|
|
|
+ Click the header button to expand / collapse.
|
|
|
+ """
|
|
|
+ container = QWidget()
|
|
|
+ vlay = QVBoxLayout(container)
|
|
|
+ vlay.setContentsMargins(0, 0, 0, 0)
|
|
|
+ vlay.setSpacing(0)
|
|
|
+
|
|
|
+ # -- Header button (acts as toggle) -----------------------------------
|
|
|
+ self._adc_toggle_btn = QPushButton("▶ АЦП / Усреднение")
|
|
|
+ self._adc_toggle_btn.setCheckable(True)
|
|
|
+ self._adc_toggle_btn.setChecked(True) # expanded by default
|
|
|
+ self._adc_toggle_btn.setFlat(True)
|
|
|
+ self._adc_toggle_btn.setStyleSheet(
|
|
|
+ "QPushButton { text-align: left; padding: 4px 6px;"
|
|
|
+ " font-weight: bold; font-size: 11px; border: none;"
|
|
|
+ " background: transparent; }"
|
|
|
+ "QPushButton:hover { background: rgba(255,255,255,0.04); }"
|
|
|
+ )
|
|
|
+ self._adc_toggle_btn.toggled.connect(self._on_adc_section_toggled)
|
|
|
+ vlay.addWidget(self._adc_toggle_btn)
|
|
|
+
|
|
|
+ # -- Content widget ---------------------------------------------------
|
|
|
+ self._adc_content = QWidget()
|
|
|
+ content_lay = QVBoxLayout(self._adc_content)
|
|
|
+ content_lay.setContentsMargins(6, 2, 6, 6)
|
|
|
+ content_lay.setSpacing(6)
|
|
|
+
|
|
|
+ # Per-channel range form
|
|
|
+ self._ch_range_form = QFormLayout()
|
|
|
+ self._ch_range_form.setFieldGrowthPolicy(QFormLayout.AllNonFixedFieldsGrow)
|
|
|
+ self._ch_range_form.setLabelAlignment(Qt.AlignRight | Qt.AlignVCenter)
|
|
|
+ self._ch_range_form.setSpacing(4)
|
|
|
+ self._ch_range_combos: list[QComboBox] = []
|
|
|
+ content_lay.addLayout(self._ch_range_form)
|
|
|
+
|
|
|
+ # Separator line
|
|
|
+ sep = QFrame()
|
|
|
+ sep.setFrameShape(QFrame.HLine)
|
|
|
+ sep.setFrameShadow(QFrame.Sunken)
|
|
|
+ content_lay.addWidget(sep)
|
|
|
+
|
|
|
+ # Averaging row
|
|
|
+ avg_row = QWidget()
|
|
|
+ avg_lay = QHBoxLayout(avg_row)
|
|
|
+ avg_lay.setContentsMargins(0, 0, 0, 0)
|
|
|
+ avg_lay.setSpacing(6)
|
|
|
+
|
|
|
+ self._avg_check = QCheckBox("Усреднение")
|
|
|
+ self._avg_check.setToolTip("Включить усреднение (iadc.averaging)")
|
|
|
+ self._avg_check.setChecked(False)
|
|
|
+ self._avg_check.toggled.connect(self._on_avg_toggled)
|
|
|
+ avg_lay.addWidget(self._avg_check)
|
|
|
+
|
|
|
+ self._avg_spin = QSpinBox()
|
|
|
+ self._avg_spin.setRange(2, 9999)
|
|
|
+ self._avg_spin.setValue(10)
|
|
|
+ self._avg_spin.setToolTip("Количество усреднений")
|
|
|
+ self._avg_spin.setEnabled(False)
|
|
|
+ self._avg_spin.setFixedWidth(64)
|
|
|
+ self._avg_spin.valueChanged.connect(self._on_avg_value_changed)
|
|
|
+ avg_lay.addWidget(self._avg_spin)
|
|
|
+ avg_lay.addStretch()
|
|
|
+ content_lay.addWidget(avg_row)
|
|
|
+
|
|
|
+ vlay.addWidget(self._adc_content)
|
|
|
+
|
|
|
+ # Initialise channels from hw_config
|
|
|
+ self._rebuild_ch_range_combos(self._read_hw_config_channel_ranges())
|
|
|
+ # Set initial arrow
|
|
|
+ self._on_adc_section_toggled(True)
|
|
|
+
|
|
|
+ return container
|
|
|
+
|
|
|
+ def _on_adc_section_toggled(self, expanded: bool) -> None:
|
|
|
+ self._adc_content.setVisible(expanded)
|
|
|
+ self._adc_toggle_btn.setText(
|
|
|
+ ("▼" if expanded else "▶") + " АЦП / Усреднение"
|
|
|
+ )
|
|
|
+
|
|
|
def _build_centre_panel(self) -> QSplitter:
|
|
|
vsplit = QSplitter(Qt.Vertical)
|
|
|
self._centre_vsplit = vsplit
|
|
|
@@ -562,13 +593,37 @@ class SeqInterpTab(QWidget):
|
|
|
self._preview.set_xml_text(xml_text)
|
|
|
self._preview.set_post_json_text(post_text)
|
|
|
|
|
|
- # Plot waveforms returned by the service
|
|
|
+ # ── Plots ──────────────────────────────────────────────────────
|
|
|
waveforms = result.get("waveforms", {})
|
|
|
if waveforms:
|
|
|
self._plots.plot_from_waveforms(waveforms)
|
|
|
- self._act_enabled(run=True, export=False)
|
|
|
|
|
|
+ # ── Scheme + block table ────────────────────────────────────────
|
|
|
+ blocks_slim = result.get("blocks", [])
|
|
|
+ if blocks_slim and waveforms.get("blocks_duration"):
|
|
|
+ from src.gui.adapters import build_block_rows as _bbr
|
|
|
+ seq_like = {"blocks": blocks_slim}
|
|
|
+ sync_like = {k: waveforms[k]
|
|
|
+ for k in ("blocks_duration", "gate_adc",
|
|
|
+ "gate_rf", "gate_tr_switch")
|
|
|
+ if k in waveforms}
|
|
|
+ self._block_rows = _bbr(seq_like, sync_like)
|
|
|
+ self._table.load_rows(self._block_rows)
|
|
|
+ self._scheme.load_rows(self._block_rows)
|
|
|
+
|
|
|
+ # ── Metadata labels ─────────────────────────────────────────────
|
|
|
meta = result.get("metadata", {})
|
|
|
+ _meta_map = {
|
|
|
+ "Total blocks (orig)": meta.get("block_count", "-"),
|
|
|
+ "RF blocks": meta.get("adc_count", "-"),
|
|
|
+ "ADC blocks": meta.get("adc_count", "-"),
|
|
|
+ "RF raster (uss)": meta.get("rf_raster_us", "-"),
|
|
|
+ "Grad raster (uss)": meta.get("grad_raster_us", "-"),
|
|
|
+ }
|
|
|
+ for key, val in _meta_map.items():
|
|
|
+ if key in self._meta_labels:
|
|
|
+ self._meta_labels[key].setText(str(val))
|
|
|
+
|
|
|
name = os.path.basename(self._seq_path or "")
|
|
|
parts = [
|
|
|
f"{meta.get('block_count', '?')} blocks",
|
|
|
@@ -655,12 +710,10 @@ class SeqInterpTab(QWidget):
|
|
|
return cb
|
|
|
|
|
|
def _rebuild_ch_range_combos(self, ranges: list[int]) -> None:
|
|
|
- """Recreate per-channel combo widgets in the left-panel form layout."""
|
|
|
- # Clear existing form rows
|
|
|
+ """Recreate per-channel combo widgets in the collapsible ADC form."""
|
|
|
while self._ch_range_form.rowCount() > 0:
|
|
|
self._ch_range_form.removeRow(0)
|
|
|
self._ch_range_combos.clear()
|
|
|
-
|
|
|
for idx, code in enumerate(ranges):
|
|
|
cb = self._make_ch_combo(code)
|
|
|
cb.setToolTip(f"Динамический диапазон канала {idx}")
|