| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150 |
- """
- Block table widget — one row per sync block, colour-coded by type.
- Row tints use alpha-transparent QColor values so they blend with whatever
- base colour the OS palette provides, keeping type indication readable on
- both light and dark themes.
- """
- from __future__ import annotations
- from PySide6.QtCore import Signal, Qt
- from PySide6.QtWidgets import (
- QTableWidget, QTableWidgetItem, QHeaderView, QAbstractItemView,
- )
- from PySide6.QtGui import QColor, QBrush
- from seq_interp.src.gui.adapters import BlockRow
- _COL_NAMES = [
- "#sync", "orig#", "Type", "Dur (µs)", "RF", "ADC", "Grad",
- "Delay", "gRF", "gADC", "gTR", "T-start (µs)",
- ]
- # Row background tints — alpha ~80/255 so they blend with the OS base colour.
- # On a white base: soft pastels. On a dark base: subtle dark tints.
- _BG: dict[str, QColor] = {
- "START": QColor(244, 143, 177, 80), # pink — start delay
- "TR": QColor(144, 202, 249, 80), # blue — TR delay
- "RF": QColor(255, 224, 130, 80), # amber — RF delay
- "adc": QColor(165, 214, 167, 80), # green — ADC block
- "rf": QColor(244, 143, 177, 80), # pink — RF block
- "grad": QColor(206, 147, 216, 80), # purple — gradient block
- "plain": QColor(0, 0, 0, 0 ), # transparent — no tint
- }
- # Structural stylesheet only — base text/background come from the OS palette.
- # Only the selection highlight and hover state are pinned to explicit colours
- # because they need to be clearly visible on any background.
- _STYLESHEET = """
- QTableWidget {
- gridline-color: palette(mid);
- font-size: 12px;
- }
- QTableWidget::item {
- padding: 2px 4px;
- }
- QTableWidget::item:selected {
- background-color: #1565c0;
- color: #ffffff;
- }
- QTableWidget::item:hover:!selected {
- background-color: rgba(144, 202, 249, 60);
- }
- QHeaderView::section {
- border: none;
- border-bottom: 1px solid palette(mid);
- border-right: 1px solid palette(mid);
- padding: 4px 6px;
- font-weight: bold;
- }
- QScrollBar:horizontal, QScrollBar:vertical {
- background: palette(alternateBase);
- }
- """
- def _bg_for(row: BlockRow) -> QColor:
- if row.is_delay:
- return _BG.get(row.delay_type, _BG["plain"])
- if row.has_adc:
- return _BG["adc"]
- if row.has_rf:
- return _BG["rf"]
- if row.has_grad:
- return _BG["grad"]
- return _BG["plain"]
- class BlockTable(QTableWidget):
- blockSelected = Signal(int) # sync_index
- def __init__(self, parent=None):
- super().__init__(0, len(_COL_NAMES), parent)
- self.setHorizontalHeaderLabels(_COL_NAMES)
- self.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeToContents)
- self.setSelectionBehavior(QAbstractItemView.SelectRows)
- self.setEditTriggers(QAbstractItemView.NoEditTriggers)
- self.setAlternatingRowColors(False)
- self.verticalHeader().setDefaultSectionSize(20)
- self.verticalHeader().setVisible(False)
- self.setStyleSheet(_STYLESHEET)
- self._rows: list[BlockRow] = []
- self._suppress = False
- self.itemSelectionChanged.connect(self._on_selection)
- # ── public ────────────────────────────────────────────────────────────────
- def load_rows(self, rows: list[BlockRow]) -> None:
- self._rows = rows
- self.setRowCount(0)
- self.setRowCount(len(rows))
- for r, row in enumerate(rows):
- bg = QBrush(_bg_for(row))
- vals = [
- str(row.sync_index),
- str(row.orig_index) if row.orig_index >= 0 else "—",
- row.block_type,
- f"{row.duration * 1e6:.3f}",
- "✓" if row.has_rf else "",
- "✓" if row.has_adc else "",
- "✓" if row.has_grad else "",
- row.delay_type if row.is_delay else "",
- str(row.gate_rf),
- str(row.gate_adc),
- str(row.gate_tr),
- f"{row.t_start * 1e6:.3f}",
- ]
- for c, val in enumerate(vals):
- item = QTableWidgetItem(val)
- item.setTextAlignment(Qt.AlignCenter)
- item.setBackground(bg)
- # Foreground intentionally not set: the OS palette Text role
- # provides the correct readable colour for the current theme.
- self.setItem(r, c, item)
- def select_by_sync_index(self, sync_index: int) -> None:
- self._suppress = True
- try:
- for r, row in enumerate(self._rows):
- if row.sync_index == sync_index:
- self.selectRow(r)
- self.scrollTo(self.model().index(r, 0))
- break
- finally:
- self._suppress = False
- def row_for_sync_index(self, sync_index: int) -> BlockRow | None:
- for row in self._rows:
- if row.sync_index == sync_index:
- return row
- return None
- # ── private ───────────────────────────────────────────────────────────────
- def _on_selection(self) -> None:
- if self._suppress:
- return
- selected = self.selectionModel().selectedRows()
- if selected:
- r = selected[0].row()
- if r < len(self._rows):
- self.blockSelected.emit(self._rows[r].sync_index)
|