From eb29b7c95c9c59b748f8667054e5b5f8c2fd0620 Mon Sep 17 00:00:00 2001 From: Sander van Grieken Date: Wed, 9 Apr 2025 13:44:26 +0200 Subject: [PATCH] qml: simplify QEConfig and QEDaemon use. force QEDaemon singleton, and refer to QEDaemon.instance where possible In cases where we would run into circular dependencies, pass the instance also refer to singleton QEConfig instead of passing instance in qeapp.py --- electrum/gui/qml/__init__.py | 2 +- electrum/gui/qml/qeapp.py | 47 ++++++++++++++--------------- electrum/gui/qml/qedaemon.py | 5 +++ electrum/gui/qml/qenetwork.py | 11 +++---- electrum/plugins/trustedcoin/qml.py | 5 +-- 5 files changed, 36 insertions(+), 34 deletions(-) diff --git a/electrum/gui/qml/__init__.py b/electrum/gui/qml/__init__.py index 15c389a5c..1957e2aa2 100644 --- a/electrum/gui/qml/__init__.py +++ b/electrum/gui/qml/__init__.py @@ -82,7 +82,7 @@ class ElectrumGui(BaseElectrumGui, Logger): self.timer.timeout.connect(lambda: None) # periodically enter python scope # hook for crash reporter - Exception_Hook.maybe_setup(config=config, slot=self.app.appController.crash) + Exception_Hook.maybe_setup(slot=self.app.appController.crash) # Initialize any QML plugins run_hook('init_qml', self.app) diff --git a/electrum/gui/qml/qeapp.py b/electrum/gui/qml/qeapp.py index 927870f1c..bb8587824 100644 --- a/electrum/gui/qml/qeapp.py +++ b/electrum/gui/qml/qeapp.py @@ -75,14 +75,13 @@ class QEAppController(BaseCrashReporter, QObject): secureWindowChanged = pyqtSignal() wantCloseChanged = pyqtSignal() - def __init__(self, qeapp: 'ElectrumQmlApplication', qedaemon: 'QEDaemon', plugins: 'Plugins'): + def __init__(self, qeapp: 'ElectrumQmlApplication', plugins: 'Plugins'): BaseCrashReporter.__init__(self, None, None, None) QObject.__init__(self) self._app = qeapp - self._qedaemon = qedaemon self._plugins = plugins - self.config = qedaemon.daemon.config + self.config = QEConfig.instance.config self._crash_user_text = '' self._app_started = False @@ -101,7 +100,7 @@ class QEAppController(BaseCrashReporter, QObject): self.notification_timer.setInterval(500) # msec self.notification_timer.timeout.connect(self.on_notification_timer) - self._qedaemon.walletLoaded.connect(self.on_wallet_loaded) + QEDaemon.instance.walletLoaded.connect(self.on_wallet_loaded) self.userNotify.connect(self.doNotify) @@ -111,12 +110,12 @@ class QEAppController(BaseCrashReporter, QObject): self._want_close = False def on_wallet_loaded(self): - qewallet = self._qedaemon.currentWallet + qewallet = QEDaemon.instance.currentWallet if not qewallet: return # register wallet in Exception_Hook - Exception_Hook.maybe_setup(config=qewallet.wallet.config, wallet=qewallet.wallet) + Exception_Hook.maybe_setup(wallet=qewallet.wallet) # attach to the wallet user notification events # connect only once @@ -344,8 +343,8 @@ class QEAppController(BaseCrashReporter, QObject): 'reportstring': self.get_report_string() } - @pyqtSlot(object, object, object, object) - def crash(self, config, e, text, tb): + @pyqtSlot(object, object, object) + def crash(self, e, text, tb): self.exc_args = (e, text, tb) # for BaseCrashReporter self.showException.emit(self.crashData()) @@ -423,8 +422,6 @@ class ElectrumQmlApplication(QGuiApplication): self.logger = get_logger(__name__) - ElectrumQmlApplication._daemon = daemon - # TODO QT6 order of declaration is important now? qmlRegisterType(QEAmount, 'org.electrum', 1, 0, 'Amount') qmlRegisterType(QENewWalletWizard, 'org.electrum', 1, 0, 'QNewWalletWizard') @@ -479,17 +476,17 @@ class ElectrumQmlApplication(QGuiApplication): self.context = self.engine.rootContext() self.plugins = plugins - self._qeconfig = QEConfig(config) - self._qenetwork = QENetwork(daemon.network, self._qeconfig) + self.config = QEConfig(config) + self.network = QENetwork(daemon.network) self.daemon = QEDaemon(daemon, self.plugins) - self.appController = QEAppController(self, self.daemon, self.plugins) - self._maxAmount = QEAmount(is_max=True) + self.appController = QEAppController(self, self.plugins) + self.maxAmount = QEAmount(is_max=True) self.context.setContextProperty('AppController', self.appController) - self.context.setContextProperty('Config', self._qeconfig) - self.context.setContextProperty('Network', self._qenetwork) + self.context.setContextProperty('Config', self.config) + self.context.setContextProperty('Network', self.network) self.context.setContextProperty('Daemon', self.daemon) self.context.setContextProperty('FixedFont', self.fixedFont) - self.context.setContextProperty('MAX', self._maxAmount) + self.context.setContextProperty('MAX', self.maxAmount) self.context.setContextProperty('QRIP', self.qr_ip_h) self.context.setContextProperty('BUILD', { 'electrum_version': version.ELECTRUM_VERSION, @@ -527,33 +524,33 @@ class ElectrumQmlApplication(QGuiApplication): class Exception_Hook(QObject, Logger): - _report_exception = pyqtSignal(object, object, object, object) + _report_exception = pyqtSignal(object, object, object) _INSTANCE = None # type: Optional[Exception_Hook] # singleton - def __init__(self, *, config: 'SimpleConfig', slot): + def __init__(self, *, slot): QObject.__init__(self) Logger.__init__(self) assert self._INSTANCE is None, "Exception_Hook is supposed to be a singleton" - self.config = config self.wallet_types_seen = set() # type: Set[str] sys.excepthook = self.handler threading.excepthook = self.handler - self._report_exception.connect(slot) + if slot: + self._report_exception.connect(slot) EarlyExceptionsQueue.set_hook_as_ready() @classmethod - def maybe_setup(cls, *, config: 'SimpleConfig', wallet: 'Abstract_Wallet' = None, slot = None) -> None: - if not config.SHOW_CRASH_REPORTER: + def maybe_setup(cls, *, wallet: 'Abstract_Wallet' = None, slot=None) -> None: + if not QEConfig.instance.config.SHOW_CRASH_REPORTER: EarlyExceptionsQueue.set_hook_as_ready() # flush already queued exceptions return if not cls._INSTANCE: - cls._INSTANCE = Exception_Hook(config=config, slot=slot) + cls._INSTANCE = Exception_Hook(slot=slot) if wallet: cls._INSTANCE.wallet_types_seen.add(wallet.wallet_type) def handler(self, *exc_info): self.logger.error('exception caught by crash reporter', exc_info=exc_info) - self._report_exception.emit(self.config, *exc_info) + self._report_exception.emit(*exc_info) diff --git a/electrum/gui/qml/qedaemon.py b/electrum/gui/qml/qedaemon.py index 7f7c57b9d..d1e236ad4 100644 --- a/electrum/gui/qml/qedaemon.py +++ b/electrum/gui/qml/qedaemon.py @@ -121,6 +121,8 @@ class QEWalletListModel(QAbstractListModel): class QEDaemon(AuthMixin, QObject): + instance = None # type: Optional[QEDaemon] + _logger = get_logger(__name__) _available_wallets = None @@ -149,6 +151,9 @@ class QEDaemon(AuthMixin, QObject): def __init__(self, daemon: 'Daemon', plugins: 'Plugins', parent=None): super().__init__(parent) + if QEDaemon.instance: + raise RuntimeError('There should only be one QEDaemon instance') + QEDaemon.instance = self self.daemon = daemon self.plugins = plugins self.qefx = QEFX(daemon.fx, daemon.config) diff --git a/electrum/gui/qml/qenetwork.py b/electrum/gui/qml/qenetwork.py index 204966d89..25dc1c24f 100644 --- a/electrum/gui/qml/qenetwork.py +++ b/electrum/gui/qml/qenetwork.py @@ -9,10 +9,10 @@ from electrum.interface import ServerAddr from electrum.fee_policy import FEERATE_DEFAULT_RELAY from .util import QtEventListener, event_listener +from .qeconfig import QEConfig from .qeserverlistmodel import QEServerListModel if TYPE_CHECKING: - from .qeconfig import QEConfig from electrum.network import Network @@ -49,18 +49,17 @@ class QENetwork(QObject, QtEventListener): _gossipDbChannels = 0 _gossipDbPolicies = 0 - def __init__(self, network: 'Network', qeconfig: 'QEConfig', parent=None): + def __init__(self, network: 'Network', parent=None): super().__init__(parent) assert network, "--offline is not yet implemented for this GUI" # TODO self.network = network - self._qeconfig = qeconfig self._serverListModel = None self._height = network.get_local_height() # init here, update event can take a while self._server_height = network.get_server_height() # init here, update event can take a while self.register_callbacks() self.destroyed.connect(lambda: self.on_destroy()) - self._qeconfig.useGossipChanged.connect(self.on_gossip_setting_changed) + QEConfig.instance.useGossipChanged.connect(self.on_gossip_setting_changed) def on_destroy(self): self.unregister_callbacks() @@ -172,7 +171,7 @@ class QENetwork(QObject, QtEventListener): def on_gossip_setting_changed(self): if not self.network: return - if self._qeconfig.useGossip: + if QEConfig.instance.useGossip: self.network.start_gossip() else: self.network.run_from_another_thread(self.network.stop_gossip()) @@ -198,7 +197,7 @@ class QENetwork(QObject, QtEventListener): raise Exception('failed to parse') except Exception: return - net_params = net_params._replace(server=server, auto_connect=self._qeconfig.autoConnect) + net_params = net_params._replace(server=server, auto_connect=QEConfig.instance.autoConnect) self.network.run_from_another_thread(self.network.set_parameters(net_params)) @pyqtProperty(str, notify=statusChanged) diff --git a/electrum/plugins/trustedcoin/qml.py b/electrum/plugins/trustedcoin/qml.py index 1d970ccb3..aa93a1d91 100644 --- a/electrum/plugins/trustedcoin/qml.py +++ b/electrum/plugins/trustedcoin/qml.py @@ -5,8 +5,9 @@ from electrum.plugin import hook from electrum.util import UserFacingException from electrum.gui.qml.qewallet import QEWallet -from .common_qt import TrustedcoinPluginQObject +from electrum.gui.qml.qedaemon import QEDaemon +from .common_qt import TrustedcoinPluginQObject from .trustedcoin import TrustedCoinPlugin, TrustedCoinException if TYPE_CHECKING: @@ -44,7 +45,7 @@ class Plugin(TrustedCoinPlugin): def init_qml(self, app: 'ElectrumQmlApplication'): self.logger.debug(f'init_qml hook called, gui={str(type(app))}') self._app = app - wizard = self._app.daemon.newWalletWizard + wizard = QEDaemon.instance.newWalletWizard # important: TrustedcoinPluginQObject needs to be parented, as keeping a ref # in the plugin is not enough to avoid gc # Note: storing the trustedcoin qt helper in the plugin is different from the desktop client,