Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions electrum/gui/qml/qeaddresslistmodel.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,9 +123,9 @@ def __init__(self, wallet: 'Abstract_Wallet', parent=None):
self._filterModel = None

self.register_callbacks()
self.destroyed.connect(lambda: self.on_destroy())
self.destroyed.connect(self.on_destroy)

QEConfig.instance.freezeReusedAddressUtxosChanged.connect(lambda: self.setDirty())
QEConfig.instance.freezeReusedAddressUtxosChanged.connect(self.setDirty)

self._dirty = True
self.initModel()
Expand Down
2 changes: 1 addition & 1 deletion electrum/gui/qml/qechanneldetails.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ def __init__(self, parent=None):
self._is_closing = False

self.register_callbacks()
self.destroyed.connect(lambda: self.on_destroy())
self.destroyed.connect(self.on_destroy)

@event_listener
def on_event_channel(self, wallet: 'Abstract_Wallet', channel: 'AbstractChannel'):
Expand Down
2 changes: 1 addition & 1 deletion electrum/gui/qml/qechannellistmodel.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ def __init__(self, wallet: 'Abstract_Wallet', parent=None):
# methods of this class only, and specifically not be
# partials, lambdas or methods of subobjects. Hence...
self.register_callbacks()
self.destroyed.connect(lambda: self.on_destroy())
self.destroyed.connect(self.on_destroy)

@qt_event_listener
def on_event_channel(self, wallet, channel):
Expand Down
38 changes: 30 additions & 8 deletions electrum/gui/qml/qedaemon.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,19 @@ class QEDaemon(AuthMixin, QObject):
walletDeleteError = pyqtSignal([str, str], arguments=['code', 'message'])
walletRenameError = pyqtSignal([str], arguments=['message'])

__qewallet_instances = []

# this factory method should be used to instantiate QEWallet
# so we have only one QEWallet for each electrum.wallet
@classmethod
def getQEWalletInstanceFor(cls, wallet):
for i in cls.__qewallet_instances:
if i.wallet == wallet:
return i
i = QEWallet(wallet, QEDaemon.instance)
cls.__qewallet_instances.append(i)
return i

def __init__(self, daemon: 'Daemon', plugins: 'Plugins', parent=None):
super().__init__(parent)
if QEDaemon.instance:
Expand Down Expand Up @@ -205,7 +218,7 @@ def loadWallet(self, path=None, password=None):

wallet_already_open = self.daemon.get_wallet(self._path)
if wallet_already_open is not None:
password = QEWallet.getInstanceFor(wallet_already_open).password
password = QEDaemon.getQEWalletInstanceFor(wallet_already_open).password

def load_wallet_task():
success = False
Expand Down Expand Up @@ -267,7 +280,7 @@ def _on_backend_wallet_loaded(self, password=None):
self._logger.debug('_on_backend_wallet_loaded')
wallet = self.daemon.get_wallet(self._path)
assert wallet is not None
self._current_wallet = QEWallet.getInstanceFor(wallet)
self._current_wallet = QEDaemon.getQEWalletInstanceFor(wallet)
self.availableWallets.updateWallet(self._path)
wallet.unlock(password or None) # not conditional on wallet.requires_unlock in qml, as
# the auth wrapper doesn't pass the entered password, but instead we rely on the password in memory
Expand Down Expand Up @@ -301,9 +314,7 @@ def checkThenDeleteWallet(self, wallet, confirm_requests=False, confirm_balance=
def delete_wallet(self, wallet):
path = standardize_path(wallet.wallet.storage.get_path())
self._logger.debug('deleting wallet with path %s' % path)
self._current_wallet = None
# TODO walletLoaded signal is confusing
self.walletLoaded.emit(None, None)
self.unloadWallet(wallet)

if not self.daemon.delete_wallet(path):
self.walletDeleteError.emit('error', _('Problem deleting wallet'))
Expand Down Expand Up @@ -349,13 +360,24 @@ def renameWallet(self, new_name: str):
new_path = standardize_path(os.path.join(wallet_dir, new_name))
if old_path == new_path:
return
self._current_wallet = None
self.daemon.stop_wallet(old_path)
self.unloadWallet(wallet)
try:
self.daemon.rename_wallet_file(old_path, new_path)
except Exception as e:
self.walletRenameError.emit(_('Error renaming wallet:\n') + str(e))
self.walletLoaded.emit(None, None)

# @pyqtSlot()
# TODO: make slot once the GUI/backend properly handles not ending up in a no wallet loaded scenario
def unloadWallet(self, wallet: QEWallet):
if wallet:
wallet_path = standardize_path(wallet.wallet.storage.get_path())
self.daemon.stop_wallet(wallet_path)
QEDaemon.__qewallet_instances.remove(wallet)
if wallet == self._current_wallet:
self._current_wallet = None
# TODO walletLoaded signal is confusing
self.walletLoaded.emit(None, None)
wallet.deleteLater()

@pyqtProperty(bool, notify=loadingChanged)
def loading(self):
Expand Down
2 changes: 1 addition & 1 deletion electrum/gui/qml/qefx.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ def __init__(self, fxthread: FxThread, config: SimpleConfig, parent=None):
self.fx = fxthread
self.config = config
self.register_callbacks()
self.destroyed.connect(lambda: self.on_destroy())
self.destroyed.connect(self.on_destroy)

def on_destroy(self):
self.unregister_callbacks()
Expand Down
2 changes: 1 addition & 1 deletion electrum/gui/qml/qeinvoice.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ def __init__(self, parent=None):
self._updating_max = False

self.register_callbacks()
self.destroyed.connect(lambda: self.on_destroy())
self.destroyed.connect(self.on_destroy)

def on_destroy(self):
self.unregister_callbacks()
Expand Down
4 changes: 2 additions & 2 deletions electrum/gui/qml/qeinvoicelistmodel.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ class QEInvoiceListModel(QEAbstractInvoiceListModel, QtEventListener):
def __init__(self, wallet, parent=None):
super().__init__(wallet, parent)
self.register_callbacks()
self.destroyed.connect(lambda: self.on_destroy())
self.destroyed.connect(self.on_destroy)

_logger = get_logger(__name__)

Expand Down Expand Up @@ -217,7 +217,7 @@ class QERequestListModel(QEAbstractInvoiceListModel, QtEventListener):
def __init__(self, wallet, parent=None):
super().__init__(wallet, parent)
self.register_callbacks()
self.destroyed.connect(lambda: self.on_destroy())
self.destroyed.connect(self.on_destroy)

_logger = get_logger(__name__)

Expand Down
2 changes: 1 addition & 1 deletion electrum/gui/qml/qenetwork.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ def __init__(self, network: 'Network', parent=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.destroyed.connect(self.on_destroy)

QEConfig.instance.useGossipChanged.connect(self.on_gossip_setting_changed)

Expand Down
10 changes: 6 additions & 4 deletions electrum/gui/qml/qeqrscanner.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import os

from PyQt6.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject, Qt
from PyQt6.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject, Qt, QMetaObject
from PyQt6.QtGui import QGuiApplication

from electrum.gui.qml.qetypes import QEBytes
Expand All @@ -25,13 +25,11 @@ class QEQRScanner(QObject):

foundText = pyqtSignal(str)
foundBinary = pyqtSignal(QEBytes)

finished = pyqtSignal()

def __init__(self, parent=None):
super().__init__(parent)
self._hint = _("Scan a QR code.")
self.finished.connect(self._unbind, Qt.ConnectionType.QueuedConnection)

self.destroyed.connect(lambda: self.on_destroy())

Expand Down Expand Up @@ -78,6 +76,11 @@ def on_qr_activity_result(self, requestCode, resultCode, intent):
send_exception_to_crash_reporter(e)
finally:
self.finished.emit()
self.unbind()

def unbind(self):
# submit unbind request queued
QMetaObject.invokeMethod(self, '_unbind', Qt.ConnectionType.QueuedConnection)

@pyqtSlot()
def _unbind(self):
Expand All @@ -88,4 +91,3 @@ def _scan_qr_non_android(self):
data = QGuiApplication.clipboard().text()
self.foundText.emit(data)
self.finished.emit()
return
2 changes: 1 addition & 1 deletion electrum/gui/qml/qeserverlistmodel.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ def __init__(self, network, parent=None):
self.network = network
self.initModel()
self.register_callbacks()
self.destroyed.connect(lambda: self.unregister_callbacks())
self.destroyed.connect(self.unregister_callbacks)

@qt_event_listener
def on_event_network_updated(self):
Expand Down
14 changes: 8 additions & 6 deletions electrum/gui/qml/qeswaphelper.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from typing import Union, Optional, TYPE_CHECKING, Sequence

from PyQt6.QtCore import (pyqtProperty, pyqtSignal, pyqtSlot, QObject, QTimer, pyqtEnum, QAbstractListModel, Qt,
QModelIndex, QVariant)
QModelIndex, QVariant, QMetaObject)
from PyQt6.QtGui import QColor

from electrum.i18n import _
Expand Down Expand Up @@ -154,7 +154,6 @@ class State(IntEnum):
error = pyqtSignal([str], arguments=['message'])
undefinedNPub = pyqtSignal()
offersUpdated = pyqtSignal()
requestTxUpdate = pyqtSignal()

def __init__(self, parent=None):
super().__init__(parent)
Expand Down Expand Up @@ -192,7 +191,6 @@ def __init__(self, parent=None):
self._fwd_swap_updatetx_timer = QTimer(self)
self._fwd_swap_updatetx_timer.setSingleShot(True)
self._fwd_swap_updatetx_timer.timeout.connect(self.fwd_swap_updatetx)
self.requestTxUpdate.connect(self.tx_update_pushback_timer)

self.offersUpdated.connect(self.on_offers_updated)
self.transport_task: Optional[asyncio.Task] = None
Expand Down Expand Up @@ -606,10 +604,14 @@ def swap_slider_moved(self):
else:
# update tx only if slider isn't moved for a while
self.valid = False
# trigger tx_update_pushback_timer through signal, as this might be called from other thread
self.requestTxUpdate.emit()
self.requestTxUpdate()

def tx_update_pushback_timer(self):
def requestTxUpdate(self):
# trigger _tx_update_pushback_timer from qt thread, as this might be called from other thread
QMetaObject.invokeMethod(self, '_tx_update_pushback_timer', Qt.ConnectionType.QueuedConnection)

@pyqtSlot()
def _tx_update_pushback_timer(self):
self._fwd_swap_updatetx_timer.start(250)

def check_valid(self, send_amount, receive_amount):
Expand Down
11 changes: 6 additions & 5 deletions electrum/gui/qml/qetransactionlistmodel.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from datetime import datetime, timedelta
from typing import TYPE_CHECKING, Dict, Any

from PyQt6.QtCore import pyqtProperty, pyqtSignal, pyqtSlot
from PyQt6.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QMetaObject
from PyQt6.QtCore import Qt, QAbstractListModel, QModelIndex

from electrum.logging import get_logger
Expand All @@ -27,8 +27,6 @@ class QETransactionListModel(QAbstractListModel, QtEventListener):
_ROLE_MAP = dict(zip(_ROLE_KEYS, [bytearray(x.encode()) for x in _ROLE_NAMES]))
_ROLE_RMAP = dict(zip(_ROLE_NAMES, _ROLE_KEYS))

requestRefresh = pyqtSignal()

def __init__(self, wallet: 'Abstract_Wallet', parent=None, *, onchain_domain=None, include_lightning=True):
super().__init__(parent)
self.wallet = wallet
Expand All @@ -38,8 +36,7 @@ def __init__(self, wallet: 'Abstract_Wallet', parent=None, *, onchain_domain=Non
self.tx_history = []

self.register_callbacks()
self.destroyed.connect(lambda: self.on_destroy())
self.requestRefresh.connect(lambda: self.initModel())
self.destroyed.connect(self.on_destroy)

self._dirty = True
self.initModel()
Expand Down Expand Up @@ -87,6 +84,10 @@ def on_event_labels_received(self, wallet, labels):
if wallet == self.wallet:
self.initModel(True) # TODO: be less dramatic

def requestRefresh(self):
# ensure execute on qt thread
QMetaObject.invokeMethod(self, 'initModel', Qt.ConnectionType.QueuedConnection)

def rowCount(self, index):
return len(self.tx_history)

Expand Down
Loading