Browse Source

refactor to new event listener framework

master
Sander van Grieken 3 years ago
parent
commit
0228169852
  1. 13
      electrum/gui/qml/qechanneldetails.py
  2. 33
      electrum/gui/qml/qechannellistmodel.py
  3. 4
      electrum/gui/qml/qedaemon.py
  4. 18
      electrum/gui/qml/qefx.py
  5. 4
      electrum/gui/qml/qeinvoice.py
  6. 30
      electrum/gui/qml/qenetwork.py
  7. 87
      electrum/gui/qml/qewallet.py
  8. 31
      electrum/gui/qml/util.py

13
electrum/gui/qml/qechanneldetails.py

@ -5,14 +5,14 @@ from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject, Q_ENUMS
from electrum.i18n import _ from electrum.i18n import _
from electrum.gui import messages from electrum.gui import messages
from electrum.logging import get_logger from electrum.logging import get_logger
from electrum.util import register_callback, unregister_callback
from electrum.lnutil import LOCAL, REMOTE from electrum.lnutil import LOCAL, REMOTE
from electrum.lnchannel import ChanCloseOption from electrum.lnchannel import ChanCloseOption
from .qewallet import QEWallet from .qewallet import QEWallet
from .qetypes import QEAmount from .qetypes import QEAmount
from .util import QtEventListener, qt_event_listener
class QEChannelDetails(QObject): class QEChannelDetails(QObject, QtEventListener):
_logger = get_logger(__name__) _logger = get_logger(__name__)
_wallet = None _wallet = None
@ -25,17 +25,16 @@ class QEChannelDetails(QObject):
def __init__(self, parent=None): def __init__(self, parent=None):
super().__init__(parent) super().__init__(parent)
register_callback(self.on_network, ['channel']) self.register_callbacks()
self.destroyed.connect(lambda: self.on_destroy()) self.destroyed.connect(lambda: self.on_destroy())
def on_network(self, event, *args): @qt_event_listener
if event == 'channel': def on_event_channel(self, wallet, channel):
wallet, channel = args
if wallet == self._wallet.wallet and self._channelid == channel.channel_id.hex(): if wallet == self._wallet.wallet and self._channelid == channel.channel_id.hex():
self.channelChanged.emit() self.channelChanged.emit()
def on_destroy(self): def on_destroy(self):
unregister_callback(self.on_network) self.unregister_callbacks()
walletChanged = pyqtSignal() walletChanged = pyqtSignal()
@pyqtProperty(QEWallet, notify=walletChanged) @pyqtProperty(QEWallet, notify=walletChanged)

33
electrum/gui/qml/qechannellistmodel.py

@ -4,13 +4,14 @@ from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject
from PyQt5.QtCore import Qt, QAbstractListModel, QModelIndex from PyQt5.QtCore import Qt, QAbstractListModel, QModelIndex
from electrum.logging import get_logger from electrum.logging import get_logger
from electrum.util import Satoshis, register_callback, unregister_callback from electrum.util import Satoshis
from electrum.lnutil import LOCAL, REMOTE from electrum.lnutil import LOCAL, REMOTE
from electrum.lnchannel import ChannelState from electrum.lnchannel import ChannelState
from .qetypes import QEAmount from .qetypes import QEAmount
from .util import QtEventListener, qt_event_listener
class QEChannelListModel(QAbstractListModel): class QEChannelListModel(QAbstractListModel, QtEventListener):
_logger = get_logger(__name__) _logger = get_logger(__name__)
# define listmodel rolemap # define listmodel rolemap
@ -28,38 +29,26 @@ class QEChannelListModel(QAbstractListModel):
self.wallet = wallet self.wallet = wallet
self.init_model() self.init_model()
self._network_signal.connect(self.on_network_qt)
interests = ['channel', 'channels_updated', 'gossip_peers',
'ln_gossip_sync_progress', 'unknown_channels',
'channel_db', 'gossip_db_loaded']
# To avoid leaking references to "self" that prevent the # To avoid leaking references to "self" that prevent the
# window from being GC-ed when closed, callbacks should be # window from being GC-ed when closed, callbacks should be
# methods of this class only, and specifically not be # methods of this class only, and specifically not be
# partials, lambdas or methods of subobjects. Hence... # partials, lambdas or methods of subobjects. Hence...
register_callback(self.on_network, interests) self.register_callbacks()
self.destroyed.connect(lambda: self.on_destroy()) self.destroyed.connect(lambda: self.on_destroy())
def on_network(self, event, *args): @qt_event_listener
if event in ['channel','channels_updated']: def on_event_channel(self, wallet, channel):
# Handle in GUI thread (_network_signal -> on_network_qt)
self._network_signal.emit(event, args)
else:
self.on_network_qt(event, args)
def on_network_qt(self, event, args=None):
if event == 'channel':
wallet, channel = args
if wallet == self.wallet: if wallet == self.wallet:
self.on_channel_updated(channel) self.on_channel_updated(channel)
elif event == 'channels_updated':
wallet, = args # elif event == 'channels_updated':
@qt_event_listener
def on_event_channels_updated(self, wallet):
if wallet == self.wallet: if wallet == self.wallet:
self.init_model() # TODO: remove/add less crude than full re-init self.init_model() # TODO: remove/add less crude than full re-init
else:
self._logger.debug('unhandled event %s: %s' % (event, repr(args)))
def on_destroy(self): def on_destroy(self):
unregister_callback(self.on_network) self.unregister_callbacks()
def rowCount(self, index): def rowCount(self, index):
return len(self.channels) return len(self.channels)

4
electrum/gui/qml/qedaemon.py

@ -6,7 +6,7 @@ from PyQt5.QtCore import Qt, QAbstractListModel, QModelIndex
from electrum.util import register_callback, get_new_wallet_name, WalletFileException from electrum.util import register_callback, get_new_wallet_name, WalletFileException
from electrum.logging import get_logger from electrum.logging import get_logger
from electrum.wallet import Wallet, Abstract_Wallet, update_password_for_directory from electrum.wallet import Wallet, Abstract_Wallet
from electrum.storage import WalletStorage, StorageReadWriteError from electrum.storage import WalletStorage, StorageReadWriteError
from electrum.wallet_db import WalletDB from electrum.wallet_db import WalletDB
@ -147,7 +147,7 @@ class QEDaemon(AuthMixin, QObject):
self.walletLoaded.emit() self.walletLoaded.emit()
if self.daemon.config.get('single_password'): if self.daemon.config.get('single_password'):
self._use_single_password = update_password_for_directory(self.daemon.config, password, password) self._use_single_password = self.daemon.update_password_for_directory(old_password=password, new_password=password)
self._password = password self._password = password
self._logger.info(f'use single password: {self._use_single_password}') self._logger.info(f'use single password: {self._use_single_password}')

18
electrum/gui/qml/qefx.py

@ -6,28 +6,34 @@ from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject
from electrum.logging import get_logger from electrum.logging import get_logger
from electrum.exchange_rate import FxThread from electrum.exchange_rate import FxThread
from electrum.simple_config import SimpleConfig from electrum.simple_config import SimpleConfig
from electrum.util import register_callback
from electrum.bitcoin import COIN from electrum.bitcoin import COIN
from .qetypes import QEAmount from .qetypes import QEAmount
from .util import QtEventListener, qt_event_listener
class QEFX(QObject): class QEFX(QObject, QtEventListener):
def __init__(self, fxthread: FxThread, config: SimpleConfig, parent=None): def __init__(self, fxthread: FxThread, config: SimpleConfig, parent=None):
super().__init__(parent) super().__init__(parent)
self.fx = fxthread self.fx = fxthread
self.config = config self.config = config
register_callback(self.on_quotes, ['on_quotes']) self.register_callbacks()
register_callback(self.on_history, ['on_history']) self.destroyed.connect(lambda: self.on_destroy())
_logger = get_logger(__name__) _logger = get_logger(__name__)
quotesUpdated = pyqtSignal() quotesUpdated = pyqtSignal()
def on_quotes(self, event, *args):
def on_destroy(self):
self.unregister_callbacks()
@qt_event_listener
def on_event_on_quotes(self, *args):
self._logger.debug('new quotes') self._logger.debug('new quotes')
self.quotesUpdated.emit() self.quotesUpdated.emit()
historyUpdated = pyqtSignal() historyUpdated = pyqtSignal()
def on_history(self, event, *args): @qt_event_listener
def on_event_on_history(self, *args):
self._logger.debug('new history') self._logger.debug('new history')
self.historyUpdated.emit() self.historyUpdated.emit()

4
electrum/gui/qml/qeinvoice.py

@ -6,7 +6,7 @@ from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject, Q_ENUMS
from electrum.logging import get_logger from electrum.logging import get_logger
from electrum.i18n import _ from electrum.i18n import _
from electrum.util import (parse_URI, create_bip21_uri, InvalidBitcoinURI, InvoiceError, from electrum.util import (parse_URI, create_bip21_uri, InvalidBitcoinURI, InvoiceError,
maybe_extract_bolt11_invoice) maybe_extract_lightning_payment_identifier)
from electrum.invoices import Invoice from electrum.invoices import Invoice
from electrum.invoices import (PR_UNPAID,PR_EXPIRED,PR_UNKNOWN,PR_PAID,PR_INFLIGHT, from electrum.invoices import (PR_UNPAID,PR_EXPIRED,PR_UNKNOWN,PR_PAID,PR_INFLIGHT,
PR_FAILED,PR_ROUTING,PR_UNCONFIRMED,LN_EXPIRY_NEVER) PR_FAILED,PR_ROUTING,PR_UNCONFIRMED,LN_EXPIRY_NEVER)
@ -335,7 +335,7 @@ class QEInvoiceParser(QEInvoice):
lninvoice = None lninvoice = None
try: try:
maybe_lightning_invoice = maybe_extract_bolt11_invoice(maybe_lightning_invoice) maybe_lightning_invoice = maybe_extract_lightning_payment_identifier(maybe_lightning_invoice)
lninvoice = Invoice.from_bech32(maybe_lightning_invoice) lninvoice = Invoice.from_bech32(maybe_lightning_invoice)
except InvoiceError as e: except InvoiceError as e:
pass pass

30
electrum/gui/qml/qenetwork.py

@ -1,20 +1,16 @@
from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject
from electrum.util import register_callback
from electrum.logging import get_logger from electrum.logging import get_logger
from electrum import constants from electrum import constants
from electrum.interface import ServerAddr from electrum.interface import ServerAddr
class QENetwork(QObject): from .util import QtEventListener, qt_event_listener
class QENetwork(QObject, QtEventListener):
def __init__(self, network, parent=None): def __init__(self, network, parent=None):
super().__init__(parent) super().__init__(parent)
self.network = network self.network = network
register_callback(self.on_network_updated, ['network_updated']) self.register_callbacks()
register_callback(self.on_blockchain_updated, ['blockchain_updated'])
register_callback(self.on_default_server_changed, ['default_server_changed'])
register_callback(self.on_proxy_set, ['proxy_set'])
register_callback(self.on_status, ['status'])
register_callback(self.on_fee_histogram, ['fee_histogram'])
_logger = get_logger(__name__) _logger = get_logger(__name__)
@ -33,30 +29,36 @@ class QENetwork(QObject):
_height = 0 _height = 0
_status = "" _status = ""
def on_network_updated(self, event, *args): @qt_event_listener
def on_event_network_updated(self, *args):
self.networkUpdated.emit() self.networkUpdated.emit()
def on_blockchain_updated(self, event, *args): @qt_event_listener
def on_event_blockchain_updated(self, *args):
if self._height != self.network.get_local_height(): if self._height != self.network.get_local_height():
self._height = self.network.get_local_height() self._height = self.network.get_local_height()
self._logger.debug('new height: %d' % self._height) self._logger.debug('new height: %d' % self._height)
self.heightChanged.emit(self._height) self.heightChanged.emit(self._height)
self.blockchainUpdated.emit() self.blockchainUpdated.emit()
def on_default_server_changed(self, event, *args): @qt_event_listener
def on_event_default_server_changed(self, *args):
self.defaultServerChanged.emit() self.defaultServerChanged.emit()
def on_proxy_set(self, event, *args): @qt_event_listener
def on_event_proxy_set(self, *args):
self._logger.debug('proxy set') self._logger.debug('proxy set')
self.proxySet.emit() self.proxySet.emit()
def on_status(self, event, *args): @qt_event_listener
def on_event_status(self, *args):
self._logger.debug('status updated: %s' % self.network.connection_status) self._logger.debug('status updated: %s' % self.network.connection_status)
if self._status != self.network.connection_status: if self._status != self.network.connection_status:
self._status = self.network.connection_status self._status = self.network.connection_status
self.statusChanged.emit() self.statusChanged.emit()
def on_fee_histogram(self, event, *args): @qt_event_listener
def on_event_fee_histogram(self, *args):
self._logger.debug('fee histogram updated') self._logger.debug('fee histogram updated')
self.feeHistogramUpdated.emit() self.feeHistogramUpdated.emit()

87
electrum/gui/qml/qewallet.py

@ -7,8 +7,8 @@ import threading
from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject, QUrl, QTimer from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject, QUrl, QTimer
from electrum.i18n import _ from electrum.i18n import _
from electrum.util import (register_callback, unregister_callback, from electrum.util import (Satoshis, format_time, parse_max_spend, InvalidPassword,
Satoshis, format_time, parse_max_spend, InvalidPassword) event_listener)
from electrum.logging import get_logger from electrum.logging import get_logger
from electrum.wallet import Wallet, Abstract_Wallet from electrum.wallet import Wallet, Abstract_Wallet
from electrum.storage import StorageEncryptionVersion from electrum.storage import StorageEncryptionVersion
@ -24,8 +24,9 @@ from .qeaddresslistmodel import QEAddressListModel
from .qechannellistmodel import QEChannelListModel from .qechannellistmodel import QEChannelListModel
from .qetypes import QEAmount from .qetypes import QEAmount
from .auth import AuthMixin, auth_protect from .auth import AuthMixin, auth_protect
from .util import QtEventListener, qt_event_listener
class QEWallet(AuthMixin, QObject): class QEWallet(AuthMixin, QObject, QtEventListener):
__instances = [] __instances = []
# this factory method should be used to instantiate QEWallet # this factory method should be used to instantiate QEWallet
@ -79,7 +80,7 @@ class QEWallet(AuthMixin, QObject):
self.notification_timer.setInterval(500) # msec self.notification_timer.setInterval(500) # msec
self.notification_timer.timeout.connect(self.notify_transactions) self.notification_timer.timeout.connect(self.notify_transactions)
self._network_signal.connect(self.on_network_qt) #self._network_signal.connect(self.on_network_qt)
interests = ['wallet_updated', 'new_transaction', 'status', 'verified', interests = ['wallet_updated', 'new_transaction', 'status', 'verified',
'on_history', 'channel', 'channels_updated', 'payment_failed', 'on_history', 'channel', 'channels_updated', 'payment_failed',
'payment_succeeded', 'invoice_status', 'request_status'] 'payment_succeeded', 'invoice_status', 'request_status']
@ -87,7 +88,8 @@ class QEWallet(AuthMixin, QObject):
# window from being GC-ed when closed, callbacks should be # window from being GC-ed when closed, callbacks should be
# methods of this class only, and specifically not be # methods of this class only, and specifically not be
# partials, lambdas or methods of subobjects. Hence... # partials, lambdas or methods of subobjects. Hence...
register_callback(self.on_network, interests) #register_callback(self.on_network, interests)
self.register_callbacks()
self.destroyed.connect(lambda: self.on_destroy()) self.destroyed.connect(lambda: self.on_destroy())
@pyqtProperty(bool, notify=isUptodateChanged) @pyqtProperty(bool, notify=isUptodateChanged)
@ -108,15 +110,24 @@ class QEWallet(AuthMixin, QObject):
wallet = args[0] wallet = args[0]
if wallet == self.wallet: if wallet == self.wallet:
self._logger.debug('event %s' % event) self._logger.debug('event %s' % event)
if event == 'status':
@event_listener
def on_event_status(self, *args, **kwargs):
#if event == 'status':
self.isUptodateChanged.emit() self.isUptodateChanged.emit()
elif event == 'request_status':
wallet, key, status = args
# elif event == 'request_status':
@event_listener
def on_event_request_status(self, wallet, key, status):
#wallet, key, status = args
if wallet == self.wallet: if wallet == self.wallet:
self._logger.debug('request status %d for key %s' % (status, key)) self._logger.debug('request status %d for key %s' % (status, key))
self.requestStatusChanged.emit(key, status) self.requestStatusChanged.emit(key, status)
elif event == 'invoice_status': # elif event == 'invoice_status':
wallet, key = args @event_listener
def on_event_invoice_status(self, wallet, key):
#wallet, key = args
if wallet == self.wallet: if wallet == self.wallet:
self._logger.debug('invoice status update for key %s' % key) self._logger.debug('invoice status update for key %s' % key)
# FIXME event doesn't pass the new status, so we need to retrieve # FIXME event doesn't pass the new status, so we need to retrieve
@ -126,42 +137,66 @@ class QEWallet(AuthMixin, QObject):
self.invoiceStatusChanged.emit(key, status) self.invoiceStatusChanged.emit(key, status)
else: else:
self._logger.debug(f'No invoice found for key {key}') self._logger.debug(f'No invoice found for key {key}')
elif event == 'new_transaction':
#elif event == 'new_transaction':
@qt_event_listener
def on_event_new_transaction(self, *args):
wallet, tx = args wallet, tx = args
if wallet == self.wallet: if wallet == self.wallet:
self.add_tx_notification(tx) self.add_tx_notification(tx)
self.historyModel.init_model() # TODO: be less dramatic self.historyModel.init_model() # TODO: be less dramatic
elif event == 'verified':
wallet, txid, info = args
# elif event == 'verified':
@qt_event_listener
def on_event_verified(self, wallet, txid, info):
#wallet, txid, info = args
if wallet == self.wallet: if wallet == self.wallet:
self.historyModel.update_tx(txid, info) self.historyModel.update_tx(txid, info)
elif event == 'wallet_updated':
wallet, = args
# elif event == 'wallet_updated':
@event_listener
def on_event_wallet_updated(self, wallet):
#wallet, = args
if wallet == self.wallet: if wallet == self.wallet:
self._logger.debug('wallet %s updated' % str(wallet)) self._logger.debug('wallet %s updated' % str(wallet))
self.balanceChanged.emit() self.balanceChanged.emit()
elif event == 'channel':
wallet, channel = args # elif event == 'channel':
@event_listener
def on_event_channel(self, wallet, channel):
#wallet, channel = args
if wallet == self.wallet: if wallet == self.wallet:
self.balanceChanged.emit() self.balanceChanged.emit()
elif event == 'channels_updated':
wallet, = args # elif event == 'channels_updated':
@event_listener
def on_event_channels_updated(self, wallet):
#wallet, = args
if wallet == self.wallet: if wallet == self.wallet:
self.balanceChanged.emit() self.balanceChanged.emit()
elif event == 'payment_succeeded': # elif event == 'payment_succeeded':
wallet, key = args
@qt_event_listener
def on_event_payment_succeeded(self, wallet, key):
#wallet, key = args
if wallet == self.wallet: if wallet == self.wallet:
self.paymentSucceeded.emit(key) self.paymentSucceeded.emit(key)
self.historyModel.init_model() # TODO: be less dramatic self.historyModel.init_model() # TODO: be less dramatic
elif event == 'payment_failed':
wallet, key, reason = args # elif event == 'payment_failed':
@event_listener
def on_event_payment_failed(self, wallet, key, reason):
#wallet, key, reason = args
if wallet == self.wallet: if wallet == self.wallet:
self.paymentFailed.emit(key, reason) self.paymentFailed.emit(key, reason)
else: #else:
self._logger.debug('unhandled event: %s %s' % (event, str(args))) #self._logger.debug('unhandled event: %s %s' % (event, str(args)))
def on_destroy(self): def on_destroy(self):
unregister_callback(self.on_network) #unregister_callback(self.on_network)
self.unregister_callbacks()
def add_tx_notification(self, tx): def add_tx_notification(self, tx):
self._logger.debug('new transaction event') self._logger.debug('new transaction event')

31
electrum/gui/qml/util.py

@ -0,0 +1,31 @@
from functools import wraps
from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject
from electrum.logging import get_logger
from electrum.i18n import _
from electrum.util import EventListener, event_listener
class QtEventListener(EventListener):
qt_callback_signal = pyqtSignal(tuple)
def register_callbacks(self):
self.qt_callback_signal.connect(self.on_qt_callback_signal)
EventListener.register_callbacks(self)
def unregister_callbacks(self):
#self.qt_callback_signal.disconnect()
EventListener.unregister_callbacks(self)
def on_qt_callback_signal(self, args):
func = args[0]
return func(self, *args[1:])
# decorator for members of the QtEventListener class
def qt_event_listener(func):
func = event_listener(func)
@wraps(func)
def decorator(self, *args):
self.qt_callback_signal.emit( (func,) + args)
return decorator
Loading…
Cancel
Save