From a16ed6f040ec74c30996d9968ea54caceaead4b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mi=C5=82osz=20Malczak?= Date: Tue, 1 Apr 2025 00:59:28 +0200 Subject: [PATCH] Add explanation mark on tab title when there is new interactive argument #7 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Miłosz Malczak --- artiq/dashboard/interactive_args.py | 57 ++++++++++++++++++++++++++++- artiq/frontend/artiq_dashboard.py | 3 +- 2 files changed, 57 insertions(+), 3 deletions(-) diff --git a/artiq/dashboard/interactive_args.py b/artiq/dashboard/interactive_args.py index 719c5daff5..0b7214c665 100644 --- a/artiq/dashboard/interactive_args.py +++ b/artiq/dashboard/interactive_args.py @@ -73,6 +73,7 @@ def cancel(self): class _InteractiveArgsView(QtWidgets.QStackedWidget): supplied = QtCore.pyqtSignal(int, dict) cancelled = QtCore.pyqtSignal(int) + alert = QtCore.pyqtSignal() def __init__(self): QtWidgets.QStackedWidget.__init__(self) @@ -114,6 +115,7 @@ def rowsInserted(self, parent, first, last): assert first == last self.setCurrentIndex(0) self._insert_widget(first) + self.alert.emit() def rowsRemoved(self, parent, first, last): assert first == last @@ -122,21 +124,37 @@ def rowsRemoved(self, parent, first, last): widget.deleteLater() if self.tabs.count() == 0: self.setCurrentIndex(1) + self.alert.emit() class InteractiveArgsDock(QtWidgets.QDockWidget): - def __init__(self, interactive_args_sub, interactive_args_rpc): + def __init__(self, interactive_args_sub, interactive_args_rpc, + main_window): QtWidgets.QDockWidget.__init__(self, "Interactive Args") self.setObjectName("Interactive Args") self.setFeatures( - self.DockWidgetFeature.DockWidgetMovable | self.DockWidgetFeature.DockWidgetFloatable) + self.DockWidgetFeature.DockWidgetMovable | + self.DockWidgetFeature.DockWidgetFloatable) self.interactive_args_rpc = interactive_args_rpc + self.main_window = main_window self.request_view = _InteractiveArgsView() self.request_view.supplied.connect(self.supply) self.request_view.cancelled.connect(self.cancel) + self.request_view.alert.connect(self._set_alert) self.setWidget(self.request_view) interactive_args_sub.add_setmodel_callback(self.request_view.setModel) + def _set_alert(self): + tabbar = self._find_tab_bar() + if tabbar is None: + return + tabbar_idx = self._tab_index(tabbar) + if tabbar_idx != tabbar.currentIndex(): + self.setWindowTitle("(!)Interactive Args") + + def _clear_alert(self): + self.setWindowTitle("Interactive Args") + def supply(self, rid, values): asyncio.ensure_future(self._supply_task(rid, values)) @@ -156,3 +174,38 @@ async def _cancel_task(self, rid): except Exception: logger.error("failed to cancel interactive args request for experiment: %d", rid, exc_info=True) + + def showEvent(self, event): + super().showEvent(event) + QtCore.QTimer.singleShot(0, self._connect_to_tab_bar) + + def _tab_index(self, tabbar): + # Return the index of this dock widget's tab in the given QTabBar. + for i in range(tabbar.count()): + if tabbar.tabText(i) == self.windowTitle(): + return i + return None + + def _on_tab_changed(self, index): + tabbar = self._find_tab_bar() + if tabbar is None: + return + my_index = self._tab_index(tabbar) + if my_index is not None and my_index == index: + self._clear_alert() + + def _connect_to_tab_bar(self): + # Locate the QTabBar in the main window and connect its currentChanged + # signal so that when this tab is selected the alert is cleared. + tabbar = self._find_tab_bar() + if tabbar is not None: + tabbar.currentChanged.connect(self._on_tab_changed) + + def _find_tab_bar(self): + # Search for a QTabBar among main_window’s children. + tabbars = self.main_window.findChildren(QtWidgets.QTabBar) + for tabbar in tabbars: + for i in range(tabbar.count()): + if tabbar.tabText(i) == self.windowTitle(): + return tabbar + return None diff --git a/artiq/frontend/artiq_dashboard.py b/artiq/frontend/artiq_dashboard.py index 495c66c804..b5813d4fa6 100755 --- a/artiq/frontend/artiq_dashboard.py +++ b/artiq/frontend/artiq_dashboard.py @@ -245,7 +245,8 @@ def report_disconnect(): d_interactive_args = interactive_args.InteractiveArgsDock( sub_clients["interactive_args"], - rpc_clients["interactive_arg_db"] + rpc_clients["interactive_arg_db"], + main_window ) d_schedule = schedule.ScheduleDock(