From 6194778a6f6a0159c7f3181ea7e1ef9d8b8788a2 Mon Sep 17 00:00:00 2001 From: ykcheungab Date: Wed, 7 Oct 2026 09:37:08 +0800 Subject: [PATCH] =?UTF-8?q?=E6=94=B6=E8=B5=B7=E5=A4=8D=E7=94=A8=E4=B8=8E?= =?UTF-8?q?=E5=AE=8C=E6=88=90=E6=80=81=E5=88=86=E6=9E=90=E6=A8=AA=E5=B9=85?= =?UTF-8?q?=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 来源栏已显示结果状态,横幅约 1.5 秒后只隐藏控件并保留 state()。已用 apps/studio/tests/test_analysis_progress.py 验证。 --- .../src/orisys_studio/ui/analysis_progress.py | 28 +++++++++- apps/studio/tests/test_analysis_progress.py | 53 ++++++++++++++++++- 2 files changed, 79 insertions(+), 2 deletions(-) diff --git a/apps/studio/src/orisys_studio/ui/analysis_progress.py b/apps/studio/src/orisys_studio/ui/analysis_progress.py index fac3582..530d248 100644 --- a/apps/studio/src/orisys_studio/ui/analysis_progress.py +++ b/apps/studio/src/orisys_studio/ui/analysis_progress.py @@ -28,7 +28,7 @@ from __future__ import annotations from orisys_app_shared.i18n import t from orisys_app_shared.theme import color -from PySide6.QtCore import Qt, Signal +from PySide6.QtCore import Qt, QTimer, Signal from PySide6.QtWidgets import ( QFrame, QHBoxLayout, @@ -71,6 +71,10 @@ _STATE_TITLE = { #: The progress bar's thousandths (Qt's own percentage unit, 0–1000). _FULL = 1000 +#: How long a reused / complete banner stays on screen before collapsing. The rail +#: already names the result state, so the banner must not keep stealing height. +_COLLAPSE_MS = 1500 + class AnalysisProgress(QFrame): """Shows one analysis pass: how far, which segment, and how to stop it.""" @@ -104,6 +108,9 @@ class AnalysisProgress(QFrame): self.cancel_button.setObjectName("analysisCancel") self.cancel_button.setToolTip(t("analysis.cancel_tip")) self.cancel_button.clicked.connect(self._on_cancel_clicked) + self._collapse_timer = QTimer(self) + self._collapse_timer.setSingleShot(True) + self._collapse_timer.timeout.connect(self._collapse_success_banner) footer = QHBoxLayout() footer.setContentsMargins(0, 0, 0, 0) @@ -135,6 +142,7 @@ class AnalysisProgress(QFrame): def start(self) -> None: """A pass began: show the surface empty and cancellable.""" + self._cancel_collapse() self._state = RUNNING self._cancelling = False self._detail = "" @@ -186,6 +194,7 @@ class AnalysisProgress(QFrame): self._detail = "" self.setVisible(True) self._refresh() + self._schedule_collapse() def complete(self, *, frames: int = 0, elapsed_s: float = 0.0) -> None: """The pass finished and the result may be played from.""" @@ -196,9 +205,11 @@ class AnalysisProgress(QFrame): self._detail = t("analysis.complete", frames=int(frames), seconds=float(elapsed_s)) self.setVisible(True) self._refresh() + self._schedule_collapse() def cancelled(self, *, frames: int = 0) -> None: """The pass was stopped: the result is incomplete and will not be played.""" + self._cancel_collapse() self._state = CANCELLED self._cancelling = False self.progress_bar.setValue(0) @@ -212,6 +223,7 @@ class AnalysisProgress(QFrame): if cancelled: self.cancelled(frames=frames) return + self._cancel_collapse() self._state = FAILED self._cancelling = False self.progress_bar.setValue(0) @@ -222,6 +234,7 @@ class AnalysisProgress(QFrame): def clear(self) -> None: """Hide the surface: no pass has anything to say.""" + self._cancel_collapse() self._state = IDLE self._cancelling = False self._detail = "" @@ -247,6 +260,19 @@ class AnalysisProgress(QFrame): self.retranslate() self.cancel_requested.emit() + def _schedule_collapse(self) -> None: + """Hide the banner after a short notice; keep ``state()`` for the rail.""" + self._collapse_timer.start(_COLLAPSE_MS) + + def _cancel_collapse(self) -> None: + if self._collapse_timer.isActive(): + self._collapse_timer.stop() + + def _collapse_success_banner(self) -> None: + """Drop only the chrome; reused / complete remain the result's state.""" + if self._state in (REUSED, COMPLETE): + self.setVisible(False) + def _refresh(self) -> None: """Repaint from the state: colour, title, which chrome is offered, tooltip. diff --git a/apps/studio/tests/test_analysis_progress.py b/apps/studio/tests/test_analysis_progress.py index 1df9dfa..bc3aa73 100644 --- a/apps/studio/tests/test_analysis_progress.py +++ b/apps/studio/tests/test_analysis_progress.py @@ -3,8 +3,14 @@ from __future__ import annotations from orisys_app_shared.i18n import set_locale, t +from PySide6.QtCore import QTimer -from orisys_studio.ui.analysis_progress import AnalysisProgress +from orisys_studio.ui.analysis_progress import ( + COMPLETE, + REUSED, + AnalysisProgress, + _COLLAPSE_MS, +) def test_analysis_progress_titles_name_the_outcome(qt_app) -> None: @@ -19,12 +25,57 @@ def test_analysis_progress_titles_name_the_outcome(qt_app) -> None: assert banner.title_label.text() == t("analysis.title_reused") assert not banner.progress_bar.isVisible() assert not banner.cancel_button.isVisible() + assert banner.isVisible() + assert banner.state() == REUSED banner.complete(frames=12, elapsed_s=1.5) assert banner.title_label.text() == t("analysis.title_complete") + assert banner.isVisible() + assert banner.state() == COMPLETE banner.cancelled(frames=3) assert banner.title_label.text() == t("analysis.title_cancelled") + assert banner.isVisible() banner.failed(reason="boom", frames=1) assert banner.title_label.text() == t("analysis.title_failed") + assert banner.isVisible() + + +def test_reused_and_complete_banners_collapse_without_clearing_state(qt_app) -> None: + """Success notices leave the rail's state intact after they stop taking height.""" + set_locale("en_US") + banner = AnalysisProgress(theme="dark") + + banner.reused(frames=12) + assert banner.isVisible() + QTimer.singleShot(0, lambda: None) + qt_app.processEvents() + # Advance past the collapse delay without waiting on the wall clock. + banner._collapse_timer.setInterval(0) + banner._collapse_timer.start() + qt_app.processEvents() + assert not banner.isVisible() + assert banner.state() == REUSED + + banner.complete(frames=8, elapsed_s=0.5) + assert banner.isVisible() + banner._collapse_timer.setInterval(0) + banner._collapse_timer.start() + qt_app.processEvents() + assert not banner.isVisible() + assert banner.state() == COMPLETE + + banner.cancelled(frames=1) + assert banner.isVisible() + assert banner.state() == "cancelled" + + banner.failed(reason="disk", frames=0) + assert banner.isVisible() + assert banner.state() == "failed" + + banner.start() + assert banner.isVisible() + assert banner.state() == "running" + assert not banner._collapse_timer.isActive() + assert _COLLAPSE_MS == 1500