From 6bc851b03c1d3f945b74b681681fd683de18127b Mon Sep 17 00:00:00 2001 From: Nolan Kovacs Date: Sat, 4 Apr 2026 14:20:37 -0400 Subject: [PATCH] a --- dist/screener_gui.py | 80 ++++++++++++++++++++++++-------------------- updater.py | 2 +- 2 files changed, 44 insertions(+), 38 deletions(-) diff --git a/dist/screener_gui.py b/dist/screener_gui.py index bef1f12..916043e 100644 --- a/dist/screener_gui.py +++ b/dist/screener_gui.py @@ -1494,30 +1494,29 @@ class _MetricRow(QWidget): self.val_lbl.setStyleSheet(f"color:{c}; font-size:12px; font-family:'Consolas';") -def _make_scroll_tab(fields): - """Return (QWidget tab, dict of key->_MetricRow).""" +def _make_grid_tab(fields, cols=2): + """Return (QWidget tab, dict of key->MetricCell) using 2-col metric card grid.""" tab = QWidget() scroll = QScrollArea() scroll.setWidgetResizable(True) scroll.setFrameShape(QFrame.NoFrame) inner = QWidget() - lay = QVBoxLayout(inner) - lay.setContentsMargins(0, 4, 0, 4) - lay.setSpacing(0) - rows = {} - for label, key in fields: - row_w = _MetricRow(label, key) - rows[key] = row_w - lay.addWidget(row_w) - sep = QFrame(); sep.setFixedHeight(1) - sep.setStyleSheet(f"background:{_C['border']};") - lay.addWidget(sep) - lay.addStretch() + lay = QGridLayout(inner) + lay.setContentsMargins(10, 10, 10, 10) + lay.setSpacing(6) + cells = {} + for i, (label, key) in enumerate(fields): + tip = _METRIC_TOOLTIPS.get(label, "") + cell = MetricCell(label, key, tip) + cells[key] = cell + lay.addWidget(cell, i // cols, i % cols) + # stretch the last row so cards stay top-aligned + lay.setRowStretch((len(fields) - 1) // cols + 1, 1) scroll.setWidget(inner) outer = QVBoxLayout(tab) outer.setContentsMargins(0, 0, 0, 0) outer.addWidget(scroll) - return tab, rows + return tab, cells class DetailPanel(QWidget): @@ -1560,43 +1559,41 @@ class DetailPanel(QWidget): self._tabs.addTab(self._score_tab, "Scores") # Valuation - t, rows = _make_scroll_tab(_VAL_FIELDS) - self._all_rows.update(rows) + t, cells = _make_grid_tab(_VAL_FIELDS) + self._all_rows.update(cells) self._tabs.addTab(t, "Valuation") # Growth - t, rows = _make_scroll_tab(_GROWTH_FIELDS) - self._all_rows.update(rows) + t, cells = _make_grid_tab(_GROWTH_FIELDS) + self._all_rows.update(cells) self._tabs.addTab(t, "Growth") # Momentum - t, rows = _make_scroll_tab(_MOM_FIELDS) - self._all_rows.update(rows) + t, cells = _make_grid_tab(_MOM_FIELDS) + self._all_rows.update(cells) self._tabs.addTab(t, "Momentum") # Quality & Profit - t, rows = _make_scroll_tab(_QUAL_FIELDS) - self._all_rows.update(rows) + t, cells = _make_grid_tab(_QUAL_FIELDS) + self._all_rows.update(cells) self._tabs.addTab(t, "Quality & Profit") - # Analyst tab (metrics + commentary) + # Analyst tab (metric grid + commentary) analyst_tab = QWidget() al = QVBoxLayout(analyst_tab) al.setContentsMargins(0, 0, 0, 0) al.setSpacing(0) - # Metrics in a small scrollable area - anlst_inner = QWidget() - anlst_lay = QVBoxLayout(anlst_inner) - anlst_lay.setContentsMargins(0, 4, 0, 4) - anlst_lay.setSpacing(0) - for label, key in _ANLST_FIELDS: - row_w = _MetricRow(label, key) - self._all_rows[key] = row_w - anlst_lay.addWidget(row_w) - sep2 = QFrame(); sep2.setFixedHeight(1) - sep2.setStyleSheet(f"background:{_C['border']};") - anlst_lay.addWidget(sep2) - al.addWidget(anlst_inner) + # Metrics as a 2-col grid + anlst_grid_w = QWidget() + anlst_grid_lay = QGridLayout(anlst_grid_w) + anlst_grid_lay.setContentsMargins(10, 10, 10, 6) + anlst_grid_lay.setSpacing(6) + for i, (label, key) in enumerate(_ANLST_FIELDS): + tip = _METRIC_TOOLTIPS.get(label, "") + cell = MetricCell(label, key, tip) + self._all_rows[key] = cell + anlst_grid_lay.addWidget(cell, i // 2, i % 2) + al.addWidget(anlst_grid_w) hdiv = QFrame(); hdiv.setFixedHeight(1) hdiv.setStyleSheet(f"background:{_C['border']};") @@ -2044,6 +2041,7 @@ class ScreenerPage(QWidget): self._run_btn.setEnabled(True) self._stop_btn.setEnabled(False) self._progress.hide() + QMessageBox.critical(self, "Screen Error", f"The screening run failed:\n\n{text}") @Slot() def _on_stopped(self): @@ -2054,6 +2052,14 @@ class ScreenerPage(QWidget): def _apply_filters(self): if self._df is None: return + try: + self._apply_filters_inner() + except Exception as exc: + import traceback + QMessageBox.critical(self, "Display Error", + f"Failed to display results:\n\n{traceback.format_exc()}") + + def _apply_filters_inner(self): df = self._df rule = _SECTOR_FILTER_MAP.get(self._sector_cb.currentText()) if rule: diff --git a/updater.py b/updater.py index 844fd33..de74f5c 100644 --- a/updater.py +++ b/updater.py @@ -19,7 +19,7 @@ import requests # --------------------------------------------------------------------------- # Current app version — kept in sync by push_update.py before each build. # --------------------------------------------------------------------------- -APP_VERSION = "1.4.3" +APP_VERSION = "1.4.4" def _exe_dir() -> str: