From e6d43b60fa275a2b7cc0392442e5b9fddfa86663 Mon Sep 17 00:00:00 2001 From: SomberNight Date: Wed, 1 Apr 2020 18:42:06 +0200 Subject: [PATCH] qt hww show_settings_dialog: don't scan devices in GUI thread Just makes sense in general. Also, previously, the GUI would freeze if right after startup the user clicked the hww status bar icon (especially with multiple hww connected). --- electrum/gui/qt/__init__.py | 4 ++-- electrum/gui/qt/installwizard.py | 4 +++- electrum/plugin.py | 2 ++ electrum/plugins/hw_wallet/plugin.py | 6 ++++++ electrum/plugins/hw_wallet/qt.py | 7 ++++++- electrum/plugins/keepkey/qt.py | 10 +++++++--- electrum/plugins/safe_t/qt.py | 10 +++++++--- electrum/plugins/trezor/qt.py | 10 +++++++--- 8 files changed, 40 insertions(+), 13 deletions(-) diff --git a/electrum/gui/qt/__init__.py b/electrum/gui/qt/__init__.py index e5fda06e6..1b2a6055f 100644 --- a/electrum/gui/qt/__init__.py +++ b/electrum/gui/qt/__init__.py @@ -303,7 +303,7 @@ class ElectrumGui(Logger): return window def _start_wizard_to_select_or_create_wallet(self, path) -> Optional[Abstract_Wallet]: - wizard = InstallWizard(self.config, self.app, self.plugins) + wizard = InstallWizard(self.config, self.app, self.plugins, gui_object=self) try: path, storage = wizard.select_storage(path, self.daemon.get_wallet) # storage is None if file does not exist @@ -342,7 +342,7 @@ class ElectrumGui(Logger): # Show network dialog if config does not exist if self.daemon.network: if self.config.get('auto_connect') is None: - wizard = InstallWizard(self.config, self.app, self.plugins) + wizard = InstallWizard(self.config, self.app, self.plugins, gui_object=self) wizard.init_network(self.daemon.network) wizard.terminate() diff --git a/electrum/gui/qt/installwizard.py b/electrum/gui/qt/installwizard.py index 45af0a21a..961f88916 100644 --- a/electrum/gui/qt/installwizard.py +++ b/electrum/gui/qt/installwizard.py @@ -31,6 +31,7 @@ from electrum.plugin import run_hook, Plugins if TYPE_CHECKING: from electrum.simple_config import SimpleConfig + from . import ElectrumGui MSG_ENTER_PASSWORD = _("Choose a password to encrypt your wallet keys.") + '\n'\ @@ -121,12 +122,13 @@ class InstallWizard(QDialog, MessageBoxMixin, BaseWizard): accept_signal = pyqtSignal() - def __init__(self, config: 'SimpleConfig', app: QApplication, plugins: 'Plugins'): + def __init__(self, config: 'SimpleConfig', app: QApplication, plugins: 'Plugins', *, gui_object: 'ElectrumGui'): QDialog.__init__(self, None) BaseWizard.__init__(self, config, plugins) self.setWindowTitle('Electrum - ' + _('Install Wizard')) self.app = app self.config = config + self.gui_thread = gui_object.gui_thread self.setMinimumSize(600, 400) self.accept_signal.connect(self.accept) self.title = QLabel() diff --git a/electrum/plugin.py b/electrum/plugin.py index d873a64e5..dbebf3a9f 100644 --- a/electrum/plugin.py +++ b/electrum/plugin.py @@ -556,6 +556,8 @@ class DeviceMgr(ThreadJob): keystore: 'Hardware_KeyStore', devices: List['Device'] = None) -> 'DeviceInfo': '''Ask the user to select a device to use if there is more than one, and return the DeviceInfo for the device.''' + # ideally this should not be called from the GUI thread... + # assert handler.get_gui_thread() != threading.current_thread(), 'must not be called from GUI thread' while True: infos = self.unpaired_device_infos(handler, plugin, devices) if infos: diff --git a/electrum/plugins/hw_wallet/plugin.py b/electrum/plugins/hw_wallet/plugin.py index 19e06a077..3080b9f0c 100644 --- a/electrum/plugins/hw_wallet/plugin.py +++ b/electrum/plugins/hw_wallet/plugin.py @@ -36,6 +36,7 @@ from electrum.storage import get_derivation_used_for_hw_device_encryption from electrum.keystore import Xpub, Hardware_KeyStore if TYPE_CHECKING: + import threading from electrum.wallet import Abstract_Wallet from electrum.base_wizard import BaseWizard @@ -210,6 +211,11 @@ class HardwareHandlerBase: if hasattr(self.win, 'wallet'): return self.win.wallet + def get_gui_thread(self) -> Optional['threading.Thread']: + if self.win is not None: + if hasattr(self.win, 'gui_thread'): + return self.win.gui_thread + def update_status(self, paired: bool) -> None: pass diff --git a/electrum/plugins/hw_wallet/qt.py b/electrum/plugins/hw_wallet/qt.py index 8df94ad7c..c8432f29e 100644 --- a/electrum/plugins/hw_wallet/qt.py +++ b/electrum/plugins/hw_wallet/qt.py @@ -67,6 +67,7 @@ class QtHandlerBase(HardwareHandlerBase, QObject, Logger): def __init__(self, win: Union[ElectrumWindow, InstallWizard], device: str): QObject.__init__(self) Logger.__init__(self) + assert win.gui_thread == threading.current_thread(), 'must be called from GUI thread' self.clear_signal.connect(self.clear_dialog) self.error_signal.connect(self.error_dialog) self.message_signal.connect(self.message_dialog) @@ -254,6 +255,7 @@ class QtPluginBase(object): keystore: 'Hardware_KeyStore') -> Optional[str]: '''This dialog box should be usable even if the user has forgotten their PIN or it is in bootloader mode.''' + assert window.gui_thread != threading.current_thread(), 'must not be called from GUI thread' device_id = self.device_manager().xpub_id(keystore.xpub) if not device_id: try: @@ -264,7 +266,10 @@ class QtPluginBase(object): return device_id def show_settings_dialog(self, window: ElectrumWindow, keystore: 'Hardware_KeyStore') -> None: - device_id = self.choose_device(window, keystore) + # default implementation (if no dialog): just try to connect to device + def connect(): + device_id = self.choose_device(window, keystore) + keystore.thread.add(connect) def add_show_address_on_hw_device_button_for_receive_addr(self, wallet: 'Abstract_Wallet', keystore: 'Hardware_KeyStore', diff --git a/electrum/plugins/keepkey/qt.py b/electrum/plugins/keepkey/qt.py index fce633df9..72508ee73 100644 --- a/electrum/plugins/keepkey/qt.py +++ b/electrum/plugins/keepkey/qt.py @@ -208,9 +208,13 @@ class QtPlugin(QtPluginBase): menu.addAction(_("Show on {}").format(device_name), show_address) def show_settings_dialog(self, window, keystore): - device_id = self.choose_device(window, keystore) - if device_id: - SettingsDialog(window, self, keystore, device_id).exec_() + def connect(): + device_id = self.choose_device(window, keystore) + return device_id + def show_dialog(device_id): + if device_id: + SettingsDialog(window, self, keystore, device_id).exec_() + keystore.thread.add(connect, on_success=show_dialog) def request_trezor_init_settings(self, wizard, method, device): vbox = QVBoxLayout() diff --git a/electrum/plugins/safe_t/qt.py b/electrum/plugins/safe_t/qt.py index aa44495bb..d83663d53 100644 --- a/electrum/plugins/safe_t/qt.py +++ b/electrum/plugins/safe_t/qt.py @@ -84,9 +84,13 @@ class QtPlugin(QtPluginBase): menu.addAction(_("Show on {}").format(device_name), show_address) def show_settings_dialog(self, window, keystore): - device_id = self.choose_device(window, keystore) - if device_id: - SettingsDialog(window, self, keystore, device_id).exec_() + def connect(): + device_id = self.choose_device(window, keystore) + return device_id + def show_dialog(device_id): + if device_id: + SettingsDialog(window, self, keystore, device_id).exec_() + keystore.thread.add(connect, on_success=show_dialog) def request_safe_t_init_settings(self, wizard, method, device): vbox = QVBoxLayout() diff --git a/electrum/plugins/trezor/qt.py b/electrum/plugins/trezor/qt.py index 37a45ce6b..1bae9039e 100644 --- a/electrum/plugins/trezor/qt.py +++ b/electrum/plugins/trezor/qt.py @@ -182,9 +182,13 @@ class QtPlugin(QtPluginBase): menu.addAction(_("Show on {}").format(device_name), show_address) def show_settings_dialog(self, window, keystore): - device_id = self.choose_device(window, keystore) - if device_id: - SettingsDialog(window, self, keystore, device_id).exec_() + def connect(): + device_id = self.choose_device(window, keystore) + return device_id + def show_dialog(device_id): + if device_id: + SettingsDialog(window, self, keystore, device_id).exec_() + keystore.thread.add(connect, on_success=show_dialog) def request_trezor_init_settings(self, wizard, method, device_id): vbox = QVBoxLayout()