diff --git a/frappy/client/__init__.py b/frappy/client/__init__.py index 6b6fc15..4b34c32 100644 --- a/frappy/client/__init__.py +++ b/frappy/client/__init__.py @@ -493,6 +493,8 @@ class SecopClient(ProxyClient): self.txq.get(False) except Exception: pass + if self.io: + self.io.shutdown() if self._txthread: self.txq.put(None) # shutdown marker self._txthread.join() diff --git a/frappy/gui/connection.py b/frappy/gui/connection.py index d6c95ff..ec95c1f 100644 --- a/frappy/gui/connection.py +++ b/frappy/gui/connection.py @@ -107,3 +107,6 @@ class QSECNode(QObject): def unhandledMessage(self, action, specifier, data): self.unhandledMsg.emit('%s %s %r' % (action, specifier, data)) + + def terminate_connection(self): + self.conn.disconnect() diff --git a/frappy/gui/mainwindow.py b/frappy/gui/mainwindow.py index 98e0c33..7778eff 100644 --- a/frappy/gui/mainwindow.py +++ b/frappy/gui/mainwindow.py @@ -229,6 +229,7 @@ class MainWindow(QMainWindow): try: node = self.tab.widget(index).getSecNode() # disconnect node from all events + node.terminate_connection() self._nodes.pop(node.nodename) self.log.debug("Closing tab with node %s" % node.nodename) except AttributeError: @@ -247,6 +248,5 @@ class MainWindow(QMainWindow): def _onQuit(self): for node in self._nodes.values(): # this is only qt signals deconnecting! - # TODO: terminate node.conn explicitly? - node.disconnect() + node.terminate_connection() self.logwin.onClose() diff --git a/frappy/gui/tabwidget.py b/frappy/gui/tabwidget.py index dc8aba0..9e5e006 100644 --- a/frappy/gui/tabwidget.py +++ b/frappy/gui/tabwidget.py @@ -473,7 +473,7 @@ class TearOffTabWidget(QTabWidget): return widget def close_current(self): - self.removeTab(self.currentIndex()) + self.tabCloseRequested.emit(self.currentIndex()) class DetachedWindow(QMainWindow): diff --git a/frappy/lib/asynconn.py b/frappy/lib/asynconn.py index a110cf8..995e084 100644 --- a/frappy/lib/asynconn.py +++ b/frappy/lib/asynconn.py @@ -86,6 +86,9 @@ class AsynConn: if cls.scheme: cls.SCHEME_MAP[cls.scheme] = cls + def shutdown(self): + """prepare connection for disconnect, can be empty""" + def disconnect(self): raise NotImplementedError @@ -177,6 +180,10 @@ class AsynTcp(AsynConn): # indicate that retrying might make sense raise CommunicationFailedError(str(e)) from None + def shutdown(self): + if self.connection: + self.connection.shutdown(socket.SHUT_RDWR) + def disconnect(self): if self.connection: closeSocket(self.connection)