From f68f500fb665efdda08651c82999e16d010a5d4d Mon Sep 17 00:00:00 2001 From: Yekta Yazar Date: Thu, 12 Mar 2026 10:22:07 -0700 Subject: [PATCH 1/2] FIX: legend sync, stylesheet issues, and disable legend click-toggle --- trace/main.py | 28 ++++++++++++++++++++++++---- trace/services/theme_manager.py | 3 +++ trace/stylesheets/light_mode.qss | 2 +- trace/widgets/control_panel.py | 17 +++++++++++++++++ 4 files changed, 45 insertions(+), 5 deletions(-) diff --git a/trace/main.py b/trace/main.py index 94230892..e5267372 100644 --- a/trace/main.py +++ b/trace/main.py @@ -28,6 +28,7 @@ QAbstractButton, ) from pyqtgraph.exporters import ImageExporter +from pyqtgraph.graphicsItems.LegendItem import ItemSample from pydm import Display from pydm.widgets import PyDMLabel, PyDMArchiverTimePlot @@ -41,6 +42,17 @@ DISABLE_AUTO_SCROLL = -2 # Using -2 as invalid since QButtonGroups use -1 as invalid +class _LegendSample(ItemSample): + """Legend sample that does not toggle curve visibility on click. + + Trace manages curve visibility through the control panel toggles, + so legend clicks should not independently change visibility state. + """ + + def mouseClickEvent(self, event): + event.accept() + + class TraceDisplay(Display): """Main display widget for the Trace application. @@ -183,6 +195,7 @@ def build_plot_side(self, parent: QWidget) -> QWidget: cache_data=False, show_all=False, ) + self.plot._legend.sampleType = _LegendSample multi_axis_plot = self.plot.plotItem multi_axis_plot.vb.menu = None @@ -785,10 +798,17 @@ def git_version(): The output of `git describe --tags`, or an empty string on failure. """ project_directory = __file__.rsplit("/", 1)[0] - git_cmd = subprocess.run( - f"cd {project_directory} && git describe --tags", text=True, shell=True, capture_output=True - ) - return git_cmd.stdout.strip() + try: + git_cmd = subprocess.run( + f"cd {project_directory} && git describe --tags", + text=True, + shell=True, + capture_output=True, + timeout=5, + ) + return git_cmd.stdout.strip() + except subprocess.TimeoutExpired: + return "" def parse_cli_args(self, args, macros): """Parse CLI-style arguments and macros into startup configuration. diff --git a/trace/services/theme_manager.py b/trace/services/theme_manager.py index d965bbff..47af4737 100644 --- a/trace/services/theme_manager.py +++ b/trace/services/theme_manager.py @@ -9,6 +9,8 @@ from config import dark_stylesheet, light_stylesheet +ASSETS_DIR = str(light_stylesheet.parent.parent / "assets") + type ColorHex = str type IconColorDict = dict[str, ColorHex] type ButtonIconInfo = tuple[str, QPushButton, str, str] @@ -192,6 +194,7 @@ def set_theme(self, theme: Theme) -> None: self.app.setPalette(self.light_palette) stylesheet = light_stylesheet.read_text() + stylesheet = stylesheet.replace('url("assets/', f'url("{ASSETS_DIR}/') self.app.main_window.setStyleSheet(stylesheet) settings = QSettings() diff --git a/trace/stylesheets/light_mode.qss b/trace/stylesheets/light_mode.qss index 1fdd5d97..d5640c26 100644 --- a/trace/stylesheets/light_mode.qss +++ b/trace/stylesheets/light_mode.qss @@ -183,7 +183,7 @@ QCheckBox { } QCheckBox::indicator:unchecked { - background-color: #2A2A2A; + background-color: #FFFFFF; } QCheckBox::indicator { diff --git a/trace/widgets/control_panel.py b/trace/widgets/control_panel.py index 45e97427..0ba8e561 100644 --- a/trace/widgets/control_panel.py +++ b/trace/widgets/control_panel.py @@ -1091,6 +1091,23 @@ def show_invalid_icon(self, show=True): def set_active(self, state: int | Qt.CheckState): checked = Qt.CheckState(state) == Qt.Checked self.source.setVisible(checked) + self._update_legend(checked) + + def _update_legend(self, visible: bool) -> None: + """Update the legend entry for this curve to match its visibility. + Uses pyqtgraph's native removeItem/addItem since QGraphicsGridLayout + does not collapse hidden items.""" + legend = self.plot._legend + if legend is None: + return + if not visible: + legend.removeItem(self.source) + else: + # Only re-add if not already in the legend + for sample, label in legend.items: + if sample.item is self.source: + return + legend.addItem(self.source, self.source.name()) def update_live_icon(self, connected: bool) -> None: self.live_connection_status.setVisible(not connected) From 66e05bec6413c47b083a0285eaa813fdf77770a9 Mon Sep 17 00:00:00 2001 From: Yekta Yazar Date: Thu, 12 Mar 2026 14:15:41 -0700 Subject: [PATCH 2/2] update requirements.txt --- requirements.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/requirements.txt b/requirements.txt index a77faeed..2d971b8d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,7 @@ pydm +numpy pandas +scipy pytest pytest-qt qtawesome