sky tab: add Msg column with 10s activity indicator per satellite row
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+44
-2
@@ -18,9 +18,11 @@ from PyQt5.QtGui import QColor, QBrush
|
||||
from data_model import ReceiverManager, GNSS_NAMES, GNSS_COLORS
|
||||
from sky_widget import SkyPlotWidget
|
||||
|
||||
_COL_HEADERS = ['GNSS', 'SV', 'Elev °', 'Az °', 'C/N₀', 'Fix', 'Receiver']
|
||||
_COL_HEADERS = ['GNSS', 'SV', 'Elev °', 'Az °', 'C/N₀', 'Fix', 'Receiver', 'Msg']
|
||||
_COL_MSG = len(_COL_HEADERS) - 1
|
||||
_TRAIL_MAXLEN = 86400 # 24 hours at 1 Hz
|
||||
_TRAIL_MAX_HOURS = 24
|
||||
_MSG_HIGHLIGHT_MS = 10_000
|
||||
|
||||
|
||||
class SkyTab(QWidget):
|
||||
@@ -33,6 +35,7 @@ class SkyTab(QWidget):
|
||||
self._info_cards: dict = {} # rid -> QFrame
|
||||
self._info_labels: dict = {} # rid -> (lbl_pdop, lbl_visible, lbl_used)
|
||||
self._trail: dict = {} # (rid, gnss_id, sv_id) -> deque[(elev, azim)]
|
||||
self._hi_timers: dict = {} # (rid, gnss_id, sv_id) -> QTimer
|
||||
|
||||
outer = QVBoxLayout(self)
|
||||
outer.setContentsMargins(4, 4, 4, 4)
|
||||
@@ -87,7 +90,9 @@ class SkyTab(QWidget):
|
||||
self._table.setHorizontalHeaderLabels(_COL_HEADERS)
|
||||
hdr = self._table.horizontalHeader()
|
||||
hdr.setSectionResizeMode(QHeaderView.ResizeToContents)
|
||||
hdr.setStretchLastSection(True)
|
||||
hdr.setSectionResizeMode(_COL_MSG, QHeaderView.Fixed)
|
||||
hdr.setStretchLastSection(False)
|
||||
self._table.setColumnWidth(_COL_MSG, 36)
|
||||
self._table.setEditTriggers(QTableWidget.NoEditTriggers)
|
||||
self._table.setSelectionBehavior(QTableWidget.SelectRows)
|
||||
self._table.setAlternatingRowColors(False)
|
||||
@@ -193,12 +198,15 @@ class SkyTab(QWidget):
|
||||
# ── Data update ───────────────────────────────────────────────────────────
|
||||
|
||||
def _on_sat_update(self, rid: str, sats: list):
|
||||
selected = self._selected_rids()
|
||||
for sat in sats:
|
||||
key = (rid, sat.gnss_id, sat.sv_id)
|
||||
self._store[key] = sat
|
||||
if key not in self._trail:
|
||||
self._trail[key] = deque(maxlen=_TRAIL_MAXLEN)
|
||||
self._trail[key].append((sat.elevation, sat.azimuth))
|
||||
if rid in selected:
|
||||
self._highlight_row(key)
|
||||
|
||||
self._sky.update_data(self._visible_sats(), self._active_trails())
|
||||
self._update_info()
|
||||
@@ -219,6 +227,36 @@ class SkyTab(QWidget):
|
||||
def _rebuild_table_from_store(self):
|
||||
self._rebuild_table(self._visible_sats())
|
||||
|
||||
# ── Row highlight on message receive ─────────────────────────────────────
|
||||
|
||||
def _highlight_row(self, key):
|
||||
try:
|
||||
row = self._table_keys.index(key)
|
||||
except ValueError:
|
||||
return # row not in table yet (full rebuild pending)
|
||||
item = self._table.item(row, _COL_MSG)
|
||||
if item:
|
||||
item.setText("✓")
|
||||
t = self._hi_timers.get(key)
|
||||
if t:
|
||||
t.start(_MSG_HIGHLIGHT_MS)
|
||||
else:
|
||||
t = QTimer(self)
|
||||
t.setSingleShot(True)
|
||||
t.timeout.connect(lambda k=key, r=row: self._clear_highlight(k, r))
|
||||
t.start(_MSG_HIGHLIGHT_MS)
|
||||
self._hi_timers[key] = t
|
||||
|
||||
def _clear_highlight(self, key, row):
|
||||
self._hi_timers.pop(key, None)
|
||||
if row >= self._table.rowCount() or row >= len(self._table_keys):
|
||||
return
|
||||
if self._table_keys[row] != key:
|
||||
return # row was reassigned during a full rebuild
|
||||
item = self._table.item(row, _COL_MSG)
|
||||
if item:
|
||||
item.setText("")
|
||||
|
||||
# ── Table ─────────────────────────────────────────────────────────────────
|
||||
|
||||
def _rebuild_table(self, sats: list):
|
||||
@@ -260,6 +298,10 @@ class SkyTab(QWidget):
|
||||
self._table.setItem(row, 4, cell(f"{sat.cno:.1f}"))
|
||||
self._table.setItem(row, 5, cell("✓" if sat.used_in_fix else ""))
|
||||
self._table.setItem(row, 6, cell(rid))
|
||||
msg_item = QTableWidgetItem("")
|
||||
msg_item.setTextAlignment(Qt.AlignCenter)
|
||||
msg_item.setBackground(brush)
|
||||
self._table.setItem(row, _COL_MSG, msg_item)
|
||||
|
||||
self._table.setSortingEnabled(True)
|
||||
vp.setUpdatesEnabled(True)
|
||||
|
||||
Reference in New Issue
Block a user