From 42c5e9712228d8f6ec53ab828e01d83c6282abf7 Mon Sep 17 00:00:00 2001 From: Alexander Zaft Date: Tue, 18 Apr 2023 09:29:46 +0200 Subject: [PATCH] Rebuild NodeWidget when the description changes + add descriptionChanged signal * track detached of TabWidgetStorage correctly * build new NodeWidget when the nodes description changes. the old one is replaced Change-Id: I61e60c61b7c2c975819730cb98562657a66f16af Reviewed-on: https://forge.frm2.tum.de/review/c/secop/frappy/+/30910 Tested-by: Jenkins Automated Tests Reviewed-by: Enrico Faulhaber Reviewed-by: Alexander Zaft --- frappy/gui/connection.py | 8 +++++++- frappy/gui/mainwindow.py | 11 +++++++++++ frappy/gui/moduleoverview.py | 11 +++++++++++ frappy/gui/tabwidget.py | 27 +++++++++++++++++++++++++++ 4 files changed, 56 insertions(+), 1 deletion(-) diff --git a/frappy/gui/connection.py b/frappy/gui/connection.py index 4741234..d6e1740 100644 --- a/frappy/gui/connection.py +++ b/frappy/gui/connection.py @@ -30,6 +30,7 @@ class QSECNode(QObject): newData = pyqtSignal(str, str, object) # module, parameter, data stateChange = pyqtSignal(str, bool, str) # node name, online, connection state unhandledMsg = pyqtSignal(str) # message + descriptionChanged = pyqtSignal(str, object) # contactpoint, self logEntry = pyqtSignal(str) def __init__(self, uri, parent_logger, parent=None): @@ -48,7 +49,7 @@ class QSECNode(QObject): self.protocolVersion = conn.secop_version self.log.debug('SECoP Version: %s', conn.secop_version) conn.register_callback(None, self.updateItem, self.nodeStateChange, - self.unhandledMessage) + self.unhandledMessage, self.descriptiveDataChange) # provide methods from old baseclient for making other gui code work def reconnect(self): @@ -106,5 +107,10 @@ class QSECNode(QObject): def unhandledMessage(self, action, specifier, data): self.unhandledMsg.emit(f'{action} {specifier} {data!r}') + def descriptiveDataChange(self, _module, conn): + self.modules = conn.modules + self.properties = conn.properties + self.descriptionChanged.emit(self.contactPoint, self) + def terminate_connection(self): self.conn.disconnect() diff --git a/frappy/gui/mainwindow.py b/frappy/gui/mainwindow.py index 650578f..22432de 100644 --- a/frappy/gui/mainwindow.py +++ b/frappy/gui/mainwindow.py @@ -229,6 +229,7 @@ class MainWindow(QMainWindow): self.tab.addTab(nodeWidget, node.equipmentId) self._nodeWidgets[host] = nodeWidget self.tab.setCurrentWidget(nodeWidget) + node.descriptionChanged.connect(self._descriptiveDataChange) # add to recent nodes settings = QSettings() @@ -275,6 +276,16 @@ class MainWindow(QMainWindow): self.log.debug("Closing tab with node %s", node.nodename) self.tab.removeTab(index) + def _descriptiveDataChange(self, host, node): + old_widget = self._nodeWidgets[host] + new_widget = NodeWidget(node) + curr_idx = self.tab.currentIndex() + index = self.tab.indexOf(old_widget) + self._nodeWidgets[host] = new_widget + self.tab.replace_widget(old_widget, new_widget, title=node.equipmentId) + if curr_idx == index: + self.tab.setCurrentIndex(curr_idx) + def _rebuildAdvanced(self, advanced): if advanced: pass diff --git a/frappy/gui/moduleoverview.py b/frappy/gui/moduleoverview.py index f80bb71..ca1bc9b 100644 --- a/frappy/gui/moduleoverview.py +++ b/frappy/gui/moduleoverview.py @@ -170,7 +170,18 @@ class ModuleOverview(QTreeWidget): module.disconnected() def setToReconnected(self): + """set status after connection is reestablished. + + If the node-description has changed during the reconnection, the nodewidget + this overview is a part of gets replaced. However, the reconnect-event + gets through before the descriptionChanged-event. So if a module is no + longer present we return early in order to avoid KeyErrors on node.modules + For the case of additional modules or changed module contents we do not care. + """ + nodemods = self._node.modules.keys() for mname, module in self._modules.items(): + if mname not in nodemods: + return # description changed and we will be replaced, return early cache = self._node.queryCache(mname) if not 'status' in cache: module.setClearIcon() diff --git a/frappy/gui/tabwidget.py b/frappy/gui/tabwidget.py index 9e5e006..2f992a0 100644 --- a/frappy/gui/tabwidget.py +++ b/frappy/gui/tabwidget.py @@ -296,6 +296,7 @@ class TearOffTabWidget(QTabWidget): for i in self.tabIdx.values(): if i.widget == w: detachWindow.tabIdx = self.tabIdx[i.index].index + self.tabIdx[i.index].detached = detachWindow break detachWindow.closed.connect(self.attachTab) @@ -383,6 +384,9 @@ class TearOffTabWidget(QTabWidget): if newIndex != -1: self.setCurrentIndex(newIndex) + idx = self.find_widget(tearOffWidget) + self.tabIdx[idx].detached = None + def tabChangedTab(self, index): # for i in range(self.count()): # for p in self.widget(i).panels: @@ -463,6 +467,29 @@ class TearOffTabWidget(QTabWidget): # # QByteArray)) # detachWindow.show() + def find_widget(self, widget): + for idx, tab in self.tabIdx.items(): + if tab.widget == widget: + return idx + return None + + def replace_widget(self, old_widget, new_widget, title=None): + """If old_widget is a child of either a tab or a detached window, it will + be replaced by new_widget""" + idx = self.find_widget(old_widget) + if not idx: + return + wstore = self.tabIdx[idx] + if title: + wstore.title = title + if wstore.detached: + wstore.detached.setWidget(new_widget) + else: + tabi = self._tabWidgetIndex(old_widget) + self.removeTab(tabi) + self.insertTab(tabi, new_widget, wstore.title) + wstore.widget = new_widget + def topLevelWidget(self, w): widget = w while True: