spacexerq vor 1 Woche
Ursprung
Commit
48c06f7b97
2 geänderte Dateien mit 120 neuen und 59 gelöschten Zeilen
  1. 112 59
      apps/gui/src/tabs/seq_interp_tab.py
  2. 8 0
      services/seq-interp/api.py

+ 112 - 59
apps/gui/src/tabs/seq_interp_tab.py

@@ -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}")

+ 8 - 0
services/seq-interp/api.py

@@ -176,6 +176,13 @@ async def _run_pipeline(file_path: str, task_id: str) -> None:
         total_s = sum(sync_data.get("blocks_duration", []))
         adc_blocks = [b for b in blocks if b.get("has_adc")]
 
+        # Serialize only the fields needed by the GUI block-row builder
+        blocks_slim = [
+            {"has_adc": bool(b.get("has_adc")),
+             "type":    list(b.get("type", []))}
+            for b in blocks
+        ]
+
         result: dict[str, Any] = {
             "task_id": task_id,
             "status": "completed",
@@ -191,6 +198,7 @@ async def _run_pipeline(file_path: str, task_id: str) -> None:
                 "rf_raster_us": params.get("rf_raster_time", 1e-6) * 1e6,
                 "grad_raster_us": params.get("grad_raster_time", 1e-5) * 1e6,
             },
+            "blocks":   blocks_slim,
             "waveforms": _extract_waveforms(seq_data, sync_data),
         }
         entry = {"status": "completed", "result": result}