From 187bf493a5b18299a10939901b9ed7e308435092 Mon Sep 17 00:00:00 2001 From: wyzula-jan Date: Mon, 16 Jun 2025 15:04:24 +0200 Subject: [PATCH] fix(main_window): added expiration timer for scroll label for ClientInfoMessage --- .../containers/main_window/main_window.py | 33 +++++++++++- tests/unit_tests/test_main_widnow.py | 54 +++++++++++++++++++ 2 files changed, 86 insertions(+), 1 deletion(-) diff --git a/bec_widgets/widgets/containers/main_window/main_window.py b/bec_widgets/widgets/containers/main_window/main_window.py index 0e6629a6..92861c90 100644 --- a/bec_widgets/widgets/containers/main_window/main_window.py +++ b/bec_widgets/widgets/containers/main_window/main_window.py @@ -1,7 +1,7 @@ import os from bec_lib.endpoints import MessageEndpoints -from qtpy.QtCore import QEvent, QSize, Qt +from qtpy.QtCore import QEvent, QSize, Qt, QTimer from qtpy.QtGui import QAction, QActionGroup, QIcon from qtpy.QtWidgets import QApplication, QFrame, QLabel, QMainWindow, QStyle, QVBoxLayout, QWidget @@ -80,6 +80,11 @@ class BECMainWindow(BECWidget, QMainWindow): ) status_bar.addWidget(self._client_info_label, 1) + # Timer to automatically clear client messages once they expire + self._client_info_expire_timer = QTimer(self) + self._client_info_expire_timer.setSingleShot(True) + self._client_info_expire_timer.timeout.connect(lambda: self._client_info_label.setText("")) + def _add_separator(self): """ Add a vertically centred separator to the status bar. @@ -222,9 +227,24 @@ class BECMainWindow(BECWidget, QMainWindow): @SafeSlot(dict, dict) def display_client_message(self, msg: dict, meta: dict): + """ + Display a client message in the status bar. + + Args: + msg(dict): The message to display, should contain: + meta(dict): Metadata about the message, usually empty. + """ + # self._client_info_label.setText("") message = msg.get("message", "") + expiration = msg.get("expire", 0) # 0 → never expire self._client_info_label.setText(message) + # Restart the expiration timer if necessary + if hasattr(self, "_client_info_expire_timer") and self._client_info_expire_timer.isActive(): + self._client_info_expire_timer.stop() + if expiration and expiration > 0: + self._client_info_expire_timer.start(int(expiration * 1000)) + ################################################################################ # General and Cleanup Methods ################################################################################ @@ -259,6 +279,8 @@ class BECMainWindow(BECWidget, QMainWindow): child.close() child.deleteLater() + if hasattr(self, "_client_info_expire_timer") and self._client_info_expire_timer.isActive(): + self._client_info_expire_timer.stop() # Status bar widgets cleanup self._client_info_label.cleanup() super().cleanup() @@ -266,3 +288,12 @@ class BECMainWindow(BECWidget, QMainWindow): class UILaunchWindow(BECMainWindow): RPC = True + + +if __name__ == "__main__": + import sys + + app = QApplication(sys.argv) + main_window = UILaunchWindow() + main_window.show() + sys.exit(app.exec()) diff --git a/tests/unit_tests/test_main_widnow.py b/tests/unit_tests/test_main_widnow.py index 482466f0..fed321c1 100644 --- a/tests/unit_tests/test_main_widnow.py +++ b/tests/unit_tests/test_main_widnow.py @@ -112,6 +112,60 @@ def test_scroll_label_paint_event(qtbot): assert not pixmap.isNull() +def test_display_client_message_with_expiration(qtbot, bec_main_window): + """ + A message with a finite 'expire' value should disappear once the timer + fires. + """ + test_msg = "This message should vanish fast" + expire_sec = 0.2 + + bec_main_window.display_client_message({"message": test_msg, "expire": expire_sec}, {}) + + assert bec_main_window._client_info_expire_timer.isActive() + assert bec_main_window._client_info_label.text() == test_msg + + qtbot.waitUntil(lambda: not bec_main_window._client_info_expire_timer.isActive(), timeout=1000) + + assert bec_main_window._client_info_label.text() == "" + + +def test_display_client_message_no_expiration(qtbot, bec_main_window): + """ + A message with 'expire' == 0 must persist and never start the timer. + """ + test_msg = "Persistent status message" + + bec_main_window.display_client_message({"message": test_msg, "expire": 0}, {}) + + assert not bec_main_window._client_info_expire_timer.isActive() + assert bec_main_window._client_info_label.text() == test_msg + + qtbot.wait(500) + assert bec_main_window._client_info_label.text() == test_msg + + +def test_display_client_message_overwrite_resets_timer(qtbot, bec_main_window): + """ + Sending a second message while the expiration timer is active should + overwrite the first and stop the timer if the second one is persistent. + """ + first_msg = "First (temporary)" + second_msg = "Second (persistent)" + + bec_main_window.display_client_message({"message": first_msg, "expire": 0.3}, {}) + qtbot.wait(200) + assert bec_main_window._client_info_expire_timer.isActive() + + bec_main_window.display_client_message({"message": second_msg, "expire": 0}, {}) + + assert not bec_main_window._client_info_expire_timer.isActive() + assert bec_main_window._client_info_label.text() == second_msg + + qtbot.wait(400) + assert bec_main_window._client_info_label.text() == second_msg + + ################################################################# # Tests for BECWebLinksMixin (webbrowser opening)