diff --git a/electrum/gui/qt/__init__.py b/electrum/gui/qt/__init__.py index 2316e5c22..3ba70996f 100644 --- a/electrum/gui/qt/__init__.py +++ b/electrum/gui/qt/__init__.py @@ -88,6 +88,10 @@ class QNetworkUpdatedSignalObject(QObject): class ElectrumGui(Logger): + network_dialog: Optional['NetworkDialog'] + lightning_dialog: Optional['LightningDialog'] + watchtower_dialog: Optional['WatchtowerDialog'] + @profiler def __init__(self, config: 'SimpleConfig', daemon: 'Daemon', plugins: 'Plugins'): set_language(config.get('language', get_default_language())) @@ -111,6 +115,7 @@ class ElectrumGui(Logger): self.app = QElectrumApplication(sys.argv) self.app.installEventFilter(self.efilter) self.app.setWindowIcon(read_QIcon("electrum.png")) + self._cleaned_up = False # timer self.timer = QTimer(self.app) self.timer.setSingleShot(False) @@ -199,15 +204,40 @@ class ElectrumGui(Logger): for w in self.windows: w.hide() - def close(self): - for window in self.windows: + def _cleanup_before_exit(self): + if self._cleaned_up: + return + self._cleaned_up = True + self.app.new_window_signal.disconnect() + self.efilter = None + # If there are still some open windows, try to clean them up. + for window in list(self.windows): window.close() + window.clean_up() if self.network_dialog: self.network_dialog.close() + self.network_dialog.clean_up() + self.network_dialog = None + self.network_updated_signal_obj = None if self.lightning_dialog: self.lightning_dialog.close() + self.lightning_dialog = None if self.watchtower_dialog: self.watchtower_dialog.close() + self.watchtower_dialog = None + # Shut down the timer cleanly + self.timer.stop() + self.timer = None + # clipboard persistence. see http://www.mail-archive.com/pyqt@riverbankcomputing.com/msg17328.html + event = QtCore.QEvent(QtCore.QEvent.Clipboard) + self.app.sendEvent(self.app.clipboard(), event) + if self.tray: + self.tray.hide() + self.tray.deleteLater() + self.tray = None + + def close(self): + self._cleanup_before_exit() self.app.quit() def new_window(self, path, uri=None): @@ -232,8 +262,10 @@ class ElectrumGui(Logger): self.network_dialog.show() self.network_dialog.raise_() return - self.network_dialog = NetworkDialog(self.daemon.network, self.config, - self.network_updated_signal_obj) + self.network_dialog = NetworkDialog( + network=self.daemon.network, + config=self.config, + network_updated_signal_obj=self.network_updated_signal_obj) self.network_dialog.show() def _create_window_for_wallet(self, wallet): @@ -382,18 +414,7 @@ class ElectrumGui(Logger): self.app.lastWindowClosed.connect(quit_after_last_window) def clean_up(): - # If there are still some open windows, try to clean them up. - for window in list(self.windows): - window.clean_up() - # Shut down the timer cleanly - self.timer.stop() - # clipboard persistence. see http://www.mail-archive.com/pyqt@riverbankcomputing.com/msg17328.html - event = QtCore.QEvent(QtCore.QEvent.Clipboard) - self.app.sendEvent(self.app.clipboard(), event) - if self.tray: - self.tray.hide() - self.tray.deleteLater() - self.tray = None + self._cleanup_before_exit() self.app.aboutToQuit.connect(clean_up) # main loop diff --git a/electrum/gui/qt/network_dialog.py b/electrum/gui/qt/network_dialog.py index 5085e246b..3f2387f04 100644 --- a/electrum/gui/qt/network_dialog.py +++ b/electrum/gui/qt/network_dialog.py @@ -27,6 +27,7 @@ import socket import time from enum import IntEnum from typing import Tuple, TYPE_CHECKING +import threading from PyQt5.QtCore import Qt, pyqtSignal, QThread from PyQt5.QtWidgets import (QTreeWidget, QTreeWidgetItem, QMenu, QGridLayout, QComboBox, @@ -53,7 +54,7 @@ protocol_names = ['TCP', 'SSL'] protocol_letters = 'ts' class NetworkDialog(QDialog): - def __init__(self, network, config, network_updated_signal_obj): + def __init__(self, *, network: Network, config: 'SimpleConfig', network_updated_signal_obj): QDialog.__init__(self) self.setWindowTitle(_('Network')) self.setMinimumSize(500, 500) @@ -68,19 +69,20 @@ class NetworkDialog(QDialog): self._cleaned_up = False def on_network(self, event, *args): - self.network_updated_signal_obj.network_updated_signal.emit(event, args) + signal_obj = self.network_updated_signal_obj + if signal_obj: + signal_obj.network_updated_signal.emit(event, args) def on_update(self): self.nlayout.update() - def closeEvent(self, event): - if not self._cleaned_up: - self._cleaned_up = True - self.clean_up() - event.accept() - def clean_up(self): + if self._cleaned_up: + return + self._cleaned_up = True self.nlayout.clean_up() + self.network_updated_signal_obj.network_updated_signal.disconnect() + self.network_updated_signal_obj = None class NodesListWidget(QTreeWidget): @@ -320,8 +322,7 @@ class NetworkChoiceLayout(object): def clean_up(self): if self.td: self.td.found_proxy.disconnect() - self.td.exit() - self.td.wait() + self.td.stop() self.td = None def check_disable_proxy(self, b): @@ -463,6 +464,7 @@ class TorDetector(QThread): def __init__(self): QThread.__init__(self) + self._stop_event = threading.Event() def run(self): # Probable ports for Tor to listen at @@ -475,7 +477,14 @@ class TorDetector(QThread): break else: self.found_proxy.emit(None) - time.sleep(10) + stopping = self._stop_event.wait(10) + if stopping: + return + + def stop(self): + self._stop_event.set() + self.exit() + self.wait() @staticmethod def is_tor_port(net_addr: Tuple[str, int]) -> bool: