From 190c19d48cdaeb93cd9aed894f233917a07bca13 Mon Sep 17 00:00:00 2001 From: Sander van Grieken Date: Fri, 22 Sep 2023 16:34:28 +0200 Subject: [PATCH] whitespace, imports, code style --- electrum/gui/qml/__init__.py | 9 ++-- electrum/gui/qml/auth.py | 10 +++-- electrum/gui/qml/qeaddresslistmodel.py | 44 ++++++++++--------- electrum/gui/qml/qebip39recovery.py | 3 +- electrum/gui/qml/qebitcoin.py | 5 +-- electrum/gui/qml/qechanneldetails.py | 2 +- electrum/gui/qml/qechannellistmodel.py | 50 ++++++++++++---------- electrum/gui/qml/qechannelopener.py | 28 ++++++------ electrum/gui/qml/qeconfig.py | 11 ++--- electrum/gui/qml/qedaemon.py | 28 ++++++------ electrum/gui/qml/qefx.py | 3 +- electrum/gui/qml/qeinvoice.py | 13 +++--- electrum/gui/qml/qeinvoicelistmodel.py | 34 ++++++++------- electrum/gui/qml/qelnpaymentdetails.py | 4 +- electrum/gui/qml/qenetwork.py | 14 +++--- electrum/gui/qml/qeqr.py | 16 +++++-- electrum/gui/qml/qerequestdetails.py | 3 -- electrum/gui/qml/qeserverlistmodel.py | 45 ++++++++++--------- electrum/gui/qml/qetransactionlistmodel.py | 25 +++++------ electrum/gui/qml/qetxdetails.py | 9 ++-- electrum/gui/qml/qetxfinalizer.py | 32 ++++++-------- electrum/gui/qml/qetypes.py | 9 ++-- electrum/gui/qml/qewallet.py | 43 ++++++++++--------- electrum/gui/qml/qewalletdb.py | 3 +- electrum/gui/qml/qewizard.py | 50 ++++++++++++---------- electrum/gui/qml/util.py | 6 ++- electrum/gui/qt/wizard/wizard.py | 4 ++ 27 files changed, 270 insertions(+), 233 deletions(-) diff --git a/electrum/gui/qml/__init__.py b/electrum/gui/qml/__init__.py index 30f7d9d1a..68c2ca40c 100644 --- a/electrum/gui/qml/__init__.py +++ b/electrum/gui/qml/__init__.py @@ -2,7 +2,6 @@ import os import signal import sys import threading -import traceback from typing import TYPE_CHECKING try: @@ -15,11 +14,10 @@ try: except Exception: sys.exit("Error: Could not import PyQt5.QtQml on Linux systems, you may try 'sudo apt-get install python3-pyqt5.qtquick'") -from PyQt5.QtCore import (Qt, QCoreApplication, QObject, QLocale, QTranslator, QTimer, pyqtSignal, - QT_VERSION_STR, PYQT_VERSION_STR) +from PyQt5.QtCore import (Qt, QCoreApplication, QLocale, QTranslator, QTimer, QT_VERSION_STR, PYQT_VERSION_STR) from PyQt5.QtGui import QGuiApplication -from electrum.i18n import _, set_language, languages +from electrum.i18n import _ from electrum.plugin import run_hook from electrum.util import profiler from electrum.logging import Logger @@ -29,7 +27,6 @@ if TYPE_CHECKING: from electrum.daemon import Daemon from electrum.simple_config import SimpleConfig from electrum.plugin import Plugins - from electrum.wallet import Abstract_Wallet from .qeapp import ElectrumQmlApplication, Exception_Hook @@ -86,7 +83,7 @@ class ElectrumGui(BaseElectrumGui, Logger): self.timer = QTimer(self.app) self.timer.setSingleShot(False) self.timer.setInterval(500) # msec - self.timer.timeout.connect(lambda: None) # periodically enter python scope + 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) diff --git a/electrum/gui/qml/auth.py b/electrum/gui/qml/auth.py index f9896efcf..137e0fd31 100644 --- a/electrum/gui/qml/auth.py +++ b/electrum/gui/qml/auth.py @@ -1,9 +1,10 @@ from functools import wraps, partial -from PyQt5.QtCore import pyqtSignal, pyqtSlot, pyqtProperty +from PyQt5.QtCore import pyqtSignal, pyqtSlot from electrum.logging import get_logger + def auth_protect(func=None, reject=None, method='pin', message=''): if func is None: return partial(auth_protect, reject=reject, method=method, message=message) @@ -20,6 +21,7 @@ def auth_protect(func=None, reject=None, method='pin', message=''): return wrapper + class AuthMixin: _auth_logger = get_logger(__name__) authRequired = pyqtSignal([str, str], arguments=['method', 'authMessage']) @@ -29,14 +31,14 @@ class AuthMixin: self._auth_logger.debug('Proceeding with authed fn()') try: self._auth_logger.debug(str(getattr(self, '__auth_fcall'))) - (func,args,kwargs,reject) = getattr(self, '__auth_fcall') + (func, args, kwargs, reject) = getattr(self, '__auth_fcall') r = func(self, *args, **kwargs) return r except Exception as e: self._auth_logger.error(f'Error executing wrapped fn(): {repr(e)}') raise e finally: - delattr(self,'__auth_fcall') + delattr(self, '__auth_fcall') @pyqtSlot() def authCancel(self): @@ -45,7 +47,7 @@ class AuthMixin: return try: - (func,args,kwargs,reject) = getattr(self, '__auth_fcall') + (func, args, kwargs, reject) = getattr(self, '__auth_fcall') if reject is not None: if hasattr(self, reject): getattr(self, reject)() diff --git a/electrum/gui/qml/qeaddresslistmodel.py b/electrum/gui/qml/qeaddresslistmodel.py index 5ecf18975..1a989ec92 100644 --- a/electrum/gui/qml/qeaddresslistmodel.py +++ b/electrum/gui/qml/qeaddresslistmodel.py @@ -1,7 +1,7 @@ import itertools from typing import TYPE_CHECKING -from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject +from PyQt5.QtCore import pyqtSlot from PyQt5.QtCore import Qt, QAbstractListModel, QModelIndex from electrum.logging import get_logger @@ -17,27 +17,30 @@ class QEAddressListModel(QAbstractListModel): _logger = get_logger(__name__) # define listmodel rolemap - _ROLE_NAMES=('type','iaddr','address','label','balance','numtx', 'held') + _ROLE_NAMES=('type', 'iaddr', 'address', 'label', 'balance', 'numtx', 'held') _ROLE_KEYS = range(Qt.UserRole, Qt.UserRole + len(_ROLE_NAMES)) _ROLE_MAP = dict(zip(_ROLE_KEYS, [bytearray(x.encode()) for x in _ROLE_NAMES])) def __init__(self, wallet: 'Abstract_Wallet', parent=None): super().__init__(parent) self.wallet = wallet - self.setDirty() + self._receive_addresses = [] + self._change_addresses = [] + + self._dirty = True self.initModel() def rowCount(self, index): - return len(self.receive_addresses) + len(self.change_addresses) + return len(self._receive_addresses) + len(self._change_addresses) def roleNames(self): return self._ROLE_MAP def data(self, index, role): - if index.row() > len(self.receive_addresses) - 1: - address = self.change_addresses[index.row() - len(self.receive_addresses)] + if index.row() > len(self._receive_addresses) - 1: + address = self._change_addresses[index.row() - len(self._receive_addresses)] else: - address = self.receive_addresses[index.row()] + address = self._receive_addresses[index.row()] role_index = role - Qt.UserRole value = address[self._ROLE_NAMES[role_index]] if isinstance(value, (bool, list, int, str, QEAmount)) or value is None: @@ -48,18 +51,19 @@ class QEAddressListModel(QAbstractListModel): def clear(self): self.beginResetModel() - self.receive_addresses = [] - self.change_addresses = [] + self._receive_addresses = [] + self._change_addresses = [] self.endResetModel() def addr_to_model(self, address): - item = {} - item['address'] = address - item['numtx'] = self.wallet.adb.get_address_history_len(address) - item['label'] = self.wallet.get_label_for_address(address) c, u, x = self.wallet.get_addr_balance(address) - item['balance'] = QEAmount(amount_sat=c + u + x) - item['held'] = self.wallet.is_frozen_address(address) + item = { + 'address': address, + 'numtx': self.wallet.adb.get_address_history_len(address), + 'label': self.wallet.get_label_for_address(address), + 'balance': QEAmount(amount_sat=c + u + x), + 'held': self.wallet.is_frozen_address(address) + } return item @pyqtSlot() @@ -86,21 +90,21 @@ class QEAddressListModel(QAbstractListModel): self.beginInsertRows(QModelIndex(), 0, n_addresses - 1) if self.wallet.wallet_type != 'imported': for i, address in enumerate(r_addresses): - insert_row('receive', self.receive_addresses, address, i) + insert_row('receive', self._receive_addresses, address, i) for i, address in enumerate(c_addresses): - insert_row('change', self.change_addresses, address, i) + insert_row('change', self._change_addresses, address, i) else: for i, address in enumerate(r_addresses): - insert_row('imported', self.receive_addresses, address, i) + insert_row('imported', self._receive_addresses, address, i) self.endInsertRows() self._dirty = False @pyqtSlot(str) def updateAddress(self, address): - for i, a in enumerate(itertools.chain(self.receive_addresses, self.change_addresses)): + for i, a in enumerate(itertools.chain(self._receive_addresses, self._change_addresses)): if a['address'] == address: - self.do_update(i,a) + self.do_update(i, a) return def do_update(self, modelindex, modelitem): diff --git a/electrum/gui/qml/qebip39recovery.py b/electrum/gui/qml/qebip39recovery.py index 26a409ee3..713bfad27 100644 --- a/electrum/gui/qml/qebip39recovery.py +++ b/electrum/gui/qml/qebip39recovery.py @@ -26,7 +26,6 @@ class QEBip39RecoveryListModel(QAbstractListModel): recoveryFailed = pyqtSignal() stateChanged = pyqtSignal() - # userinfoChanged = pyqtSignal() # define listmodel rolemap _ROLE_NAMES=('description', 'derivation_path', 'script_type') @@ -112,7 +111,7 @@ class QEBip39RecoveryListModel(QAbstractListModel): if isinstance(e, concurrent.futures.CancelledError): self.state = QEBip39RecoveryListModel.State.Cancelled return - self._logger.error(f"recovery error", exc_info=exc_info) + self._logger.error(f'recovery error', exc_info=exc_info) self.state = QEBip39RecoveryListModel.State.Failed self._thread.stop() diff --git a/electrum/gui/qml/qebitcoin.py b/electrum/gui/qml/qebitcoin.py index e3a7304da..f158a1857 100644 --- a/electrum/gui/qml/qebitcoin.py +++ b/electrum/gui/qml/qebitcoin.py @@ -5,7 +5,7 @@ from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject from electrum import mnemonic from electrum import keystore from electrum.i18n import _ -from electrum.bip32 import is_bip32_derivation, normalize_bip32_derivation, xpub_type +from electrum.bip32 import is_bip32_derivation, xpub_type from electrum.logging import get_logger from electrum.slip39 import decode_mnemonic, Slip39Error from electrum.util import get_asyncio_loop @@ -13,7 +13,6 @@ from electrum.transaction import tx_from_any from electrum.mnemonic import Mnemonic, is_any_2fa_seed_type from electrum.old_mnemonic import wordlist as old_wordlist -from .qetypes import QEAmount class QEBitcoin(QObject): _logger = get_logger(__name__) @@ -79,7 +78,7 @@ class QEBitcoin(QObject): if is_checksum: seed_type = 'bip39' seed_valid = True - elif seed_variant == 'slip39': # TODO: incomplete impl, this code only validates a single share. + elif seed_variant == 'slip39': # TODO: incomplete impl, this code only validates a single share. try: share = decode_mnemonic(seed) seed_type = 'slip39' diff --git a/electrum/gui/qml/qechanneldetails.py b/electrum/gui/qml/qechanneldetails.py index 5094f8c85..58d5857e7 100644 --- a/electrum/gui/qml/qechanneldetails.py +++ b/electrum/gui/qml/qechanneldetails.py @@ -17,7 +17,7 @@ from .util import QtEventListener, event_listener class QEChannelDetails(AuthMixin, QObject, QtEventListener): _logger = get_logger(__name__) - class State: # subset, only ones we currently need in UI + class State: # subset, only ones we currently need in UI Closed = ChannelState.CLOSED Redeemed = ChannelState.REDEEMED diff --git a/electrum/gui/qml/qechannellistmodel.py b/electrum/gui/qml/qechannellistmodel.py index d9d83b1af..ccf69ef28 100644 --- a/electrum/gui/qml/qechannellistmodel.py +++ b/electrum/gui/qml/qechannellistmodel.py @@ -29,6 +29,11 @@ class QEChannelListModel(QAbstractListModel, QtEventListener): def __init__(self, wallet, parent=None): super().__init__(parent) self.wallet = wallet + self._channels = [] + + self._fm_backups = None + self._fm_nobackups = None + self.initModel() # To avoid leaking references to "self" that prevent the @@ -52,19 +57,19 @@ class QEChannelListModel(QAbstractListModel, QtEventListener): self.unregister_callbacks() def rowCount(self, index): - return len(self.channels) + return len(self._channels) # also expose rowCount as a property countChanged = pyqtSignal() @pyqtProperty(int, notify=countChanged) def count(self): - return len(self.channels) + return len(self._channels) def roleNames(self): return self._ROLE_MAP def data(self, index, role): - tx = self.channels[index.row()] + tx = self._channels[index.row()] role_index = role - Qt.UserRole value = tx[self._ROLE_NAMES[role_index]] if isinstance(value, (bool, list, int, str, QEAmount)) or value is None: @@ -75,21 +80,22 @@ class QEChannelListModel(QAbstractListModel, QtEventListener): def clear(self): self.beginResetModel() - self.channels = [] + self._channels = [] self.endResetModel() def channel_to_model(self, lnc): lnworker = self.wallet.lnworker - item = {} - item['cid'] = lnc.channel_id.hex() - item['node_id'] = lnc.node_id.hex() - item['node_alias'] = lnworker.get_node_alias(lnc.node_id) or '' - item['short_cid'] = lnc.short_id_for_GUI() - item['state'] = lnc.get_state_for_GUI() - item['state_code'] = int(lnc.get_state()) - item['is_backup'] = lnc.is_backup() - item['is_trampoline'] = lnworker.is_trampoline_peer(lnc.node_id) - item['capacity'] = QEAmount(amount_sat=lnc.get_capacity()) + item = { + 'cid': lnc.channel_id.hex(), + 'node_id': lnc.node_id.hex(), + 'node_alias': lnworker.get_node_alias(lnc.node_id) or '', + 'short_cid': lnc.short_id_for_GUI(), + 'state': lnc.get_state_for_GUI(), + 'state_code': int(lnc.get_state()), + 'is_backup': lnc.is_backup(), + 'is_trampoline': lnworker.is_trampoline_peer(lnc.node_id), + 'capacity': QEAmount(amount_sat=lnc.get_capacity()) + } if lnc.is_backup(): item['can_send'] = QEAmount() item['can_receive'] = QEAmount() @@ -111,7 +117,7 @@ class QEChannelListModel(QAbstractListModel, QtEventListener): numOpenChannelsChanged = pyqtSignal() @pyqtProperty(int, notify=numOpenChannelsChanged) def numOpenChannels(self): - return sum([1 if x['state_code'] == ChannelState.OPEN else 0 for x in self.channels]) + return sum([1 if x['state_code'] == ChannelState.OPEN else 0 for x in self._channels]) @pyqtSlot() def initModel(self): @@ -134,20 +140,20 @@ class QEChannelListModel(QAbstractListModel, QtEventListener): self.clear() self.beginInsertRows(QModelIndex(), 0, len(channels) - 1) - self.channels = channels + self._channels = channels self.endInsertRows() self.countChanged.emit() def on_channel_updated(self, channel): - for i, c in enumerate(self.channels): + for i, c in enumerate(self._channels): if c['cid'] == channel.channel_id.hex(): - self.do_update(i,channel) + self.do_update(i, channel) break def do_update(self, modelindex, channel): self._logger.debug(f'updating our channel {channel.short_id_for_GUI()}') - modelitem = self.channels[modelindex] + modelitem = self._channels[modelindex] modelitem.update(self.channel_to_model(channel)) mi = self.createIndex(modelindex, 0) @@ -163,7 +169,7 @@ class QEChannelListModel(QAbstractListModel, QtEventListener): item = self.channel_to_model(channel) self._logger.debug(item) self.beginInsertRows(QModelIndex(), 0, 0) - self.channels.insert(0,item) + self._channels.insert(0, item) self.endInsertRows() self.countChanged.emit() return @@ -171,11 +177,11 @@ class QEChannelListModel(QAbstractListModel, QtEventListener): @pyqtSlot(str) def removeChannel(self, cid): self._logger.debug('remove channel with cid %s' % cid) - for i, channel in enumerate(self.channels): + for i, channel in enumerate(self._channels): if cid == channel['cid']: self._logger.debug(cid) self.beginRemoveRows(QModelIndex(), i, i) - self.channels.remove(channel) + self._channels.remove(channel) self.endRemoveRows() self.countChanged.emit() return diff --git a/electrum/gui/qml/qechannelopener.py b/electrum/gui/qml/qechannelopener.py index e5189ca39..f8f6f7231 100644 --- a/electrum/gui/qml/qechannelopener.py +++ b/electrum/gui/qml/qechannelopener.py @@ -27,9 +27,10 @@ class QEChannelOpener(QObject, AuthMixin): conflictingBackup = pyqtSignal([str], arguments=['message']) channelOpening = pyqtSignal([str], arguments=['peer']) channelOpenError = pyqtSignal([str], arguments=['message']) - channelOpenSuccess = pyqtSignal([str,bool,int,bool], arguments=['cid','has_onchain_backup','min_depth','tx_complete']) + channelOpenSuccess = pyqtSignal([str, bool, int, bool], + arguments=['cid', 'has_onchain_backup', 'min_depth', 'tx_complete']) - dataChanged = pyqtSignal() # generic notify signal + dataChanged = pyqtSignal() # generic notify signal def __init__(self, parent=None): super().__init__(parent) @@ -41,6 +42,10 @@ class QEChannelOpener(QObject, AuthMixin): self._opentx = None self._txdetails = None + self._finalizer = None + self._node_pubkey = None + self._connect_str_resolved = None + walletChanged = pyqtSignal() @pyqtProperty(QEWallet, notify=walletChanged) def wallet(self): @@ -124,7 +129,7 @@ class QEChannelOpener(QObject, AuthMixin): self.validChanged.emit() return - self._logger.debug('amount=%s' % str(self._amount)) + self._logger.debug(f'amount={self._amount}') if not self._amount or not (self._amount.satsInt > 0 or self._amount.isMax): self._valid = False self.validChanged.emit() @@ -136,9 +141,9 @@ class QEChannelOpener(QObject, AuthMixin): @pyqtSlot(str, result=bool) def validateConnectString(self, connect_str): try: - node_id, rest = extract_nodeid(connect_str) + extract_nodeid(connect_str) except ConnStringFormatError as e: - self._logger.debug(f"invalid connect_str. {e!r}") + self._logger.debug(f'invalid connect_str. {e!r}') return False return True @@ -199,13 +204,13 @@ class QEChannelOpener(QObject, AuthMixin): chan.constraints.funding_txn_minimum_depth, funding_tx.is_complete()) # TODO: handle incomplete TX - #if not funding_tx.is_complete(): - #self._txdetails = QETxDetails(self) - #self._txdetails.rawTx = funding_tx - #self._txdetails.wallet = self._wallet - #self.txDetailsChanged.emit() + # if not funding_tx.is_complete(): + # self._txdetails = QETxDetails(self) + # self._txdetails.rawTx = funding_tx + # self._txdetails.wallet = self._wallet + # self.txDetailsChanged.emit() - except (CancelledError,TimeoutError): + except (CancelledError, TimeoutError): error = _('Could not connect to channel peer') except Exception as e: error = str(e) @@ -216,7 +221,6 @@ class QEChannelOpener(QObject, AuthMixin): self._logger.exception("Problem opening channel: %s", error) self.channelOpenError.emit(error) - self._logger.debug('starting open thread') self.channelOpening.emit(conn_str) threading.Thread(target=open_thread, daemon=True).start() diff --git a/electrum/gui/qml/qeconfig.py b/electrum/gui/qml/qeconfig.py index 49456c3fb..2eaccac16 100644 --- a/electrum/gui/qml/qeconfig.py +++ b/electrum/gui/qml/qeconfig.py @@ -7,13 +7,14 @@ from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject, QRegularEx from electrum.bitcoin import TOTAL_COIN_SUPPLY_LIMIT_IN_BTC from electrum.i18n import set_language, languages from electrum.logging import get_logger -from electrum.util import DECIMAL_POINT_DEFAULT, base_unit_name_to_decimal_point -from electrum.invoices import PR_DEFAULT_EXPIRATION_WHEN_CREATING -from electrum.simple_config import SimpleConfig +from electrum.util import base_unit_name_to_decimal_point from .qetypes import QEAmount from .auth import AuthMixin, auth_protect +if TYPE_CHECKING: + from electrum.simple_config import SimpleConfig + class QEConfig(AuthMixin, QObject): _logger = get_logger(__name__) @@ -251,7 +252,7 @@ class QEConfig(AuthMixin, QObject): return self.config.BTC_AMOUNTS_DECIMAL_POINT def max_precision(self): - return self.decimal_point() + 0 #self.extra_precision + return self.decimal_point() + 0 # self.extra_precision @pyqtSlot(str, result=QEAmount) def unitsToSats(self, unitAmount): @@ -275,4 +276,4 @@ class QEConfig(AuthMixin, QObject): @pyqtSlot('quint64', result=float) def satsToUnits(self, satoshis): - return satoshis / pow(10,self.config.decimal_point) + return satoshis / pow(10, self.config.decimal_point) diff --git a/electrum/gui/qml/qedaemon.py b/electrum/gui/qml/qedaemon.py index b728cbf36..d6971635d 100644 --- a/electrum/gui/qml/qedaemon.py +++ b/electrum/gui/qml/qedaemon.py @@ -28,23 +28,24 @@ class QEWalletListModel(QAbstractListModel): _logger = get_logger(__name__) # define listmodel rolemap - _ROLE_NAMES= ('name','path','active') + _ROLE_NAMES= ('name', 'path', 'active') _ROLE_KEYS = range(Qt.UserRole, Qt.UserRole + len(_ROLE_NAMES)) _ROLE_MAP = dict(zip(_ROLE_KEYS, [bytearray(x.encode()) for x in _ROLE_NAMES])) def __init__(self, daemon, parent=None): QAbstractListModel.__init__(self, parent) self.daemon = daemon + self._wallets = [] self.reload() def rowCount(self, index): - return len(self.wallets) + return len(self._wallets) def roleNames(self): return self._ROLE_MAP def data(self, index, role): - (wallet_name, wallet_path) = self.wallets[index.row()] + (wallet_name, wallet_path) = self._wallets[index.row()] role_index = role - Qt.UserRole role_name = self._ROLE_NAMES[role_index] if role_name == 'name': @@ -58,7 +59,7 @@ class QEWalletListModel(QAbstractListModel): def reload(self): self._logger.debug('enumerating available wallets') self.beginResetModel() - self.wallets = [] + self._wallets = [] self.endResetModel() available = [] @@ -72,18 +73,18 @@ class QEWalletListModel(QAbstractListModel): self.add_wallet(wallet_path = path) def add_wallet(self, wallet_path): - self.beginInsertRows(QModelIndex(), len(self.wallets), len(self.wallets)) + self.beginInsertRows(QModelIndex(), len(self._wallets), len(self._wallets)) wallet_name = os.path.basename(wallet_path) wallet_path = standardize_path(wallet_path) item = (wallet_name, wallet_path) - self.wallets.append(item) + self._wallets.append(item) self.endInsertRows() def remove_wallet(self, path): i = 0 wallets = [] remove = -1 - for wallet_name, wallet_path in self.wallets: + for wallet_name, wallet_path in self._wallets: if wallet_path == path: remove = i else: @@ -92,12 +93,12 @@ class QEWalletListModel(QAbstractListModel): if remove >= 0: self.beginRemoveRows(QModelIndex(), i, i) - self.wallets = wallets + self._wallets = wallets self.endRemoveRows() @pyqtSlot(str, result=bool) def wallet_name_exists(self, name): - for wallet_name, wallet_path in self.wallets: + for wallet_name, wallet_path in self._wallets: if name == wallet_name: return True return False @@ -105,7 +106,7 @@ class QEWalletListModel(QAbstractListModel): @pyqtSlot(str) def updateWallet(self, path): i = 0 - for wallet_name, wallet_path in self.wallets: + for wallet_name, wallet_path in self._wallets: if wallet_path == path: mi = self.createIndex(i, i) self.dataChanged.emit(mi, mi, self._ROLE_KEYS) @@ -166,7 +167,7 @@ class QEDaemon(AuthMixin, QObject): @pyqtSlot(str, str) def loadWallet(self, path=None, password=None): if path is None: - self._path = self.daemon.config.get('wallet_path') # command line -w option + self._path = self.daemon.config.get('wallet_path') # command line -w option if self._path is None: self._path = self.daemon.config.GUI_LAST_WALLET else: @@ -201,7 +202,7 @@ class QEDaemon(AuthMixin, QObject): self.loadingChanged.emit() try: - local_password = password # need this in local scope + local_password = password # need this in local scope wallet = self.daemon.load_wallet(self._path, local_password) if wallet is None: @@ -238,7 +239,7 @@ class QEDaemon(AuthMixin, QObject): @pyqtSlot() @pyqtSlot(str) - def _on_backend_wallet_loaded(self, password = None): + 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 @@ -247,7 +248,6 @@ class QEDaemon(AuthMixin, QObject): self._current_wallet.password = password if password else None self.walletLoaded.emit(self._name, self._path) - @pyqtSlot(QEWallet) @pyqtSlot(QEWallet, bool) @pyqtSlot(QEWallet, bool, bool) diff --git a/electrum/gui/qml/qefx.py b/electrum/gui/qml/qefx.py index 57df12c72..22e1ecbaa 100644 --- a/electrum/gui/qml/qefx.py +++ b/electrum/gui/qml/qefx.py @@ -11,6 +11,7 @@ from electrum.simple_config import SimpleConfig from .qetypes import QEAmount from .util import QtEventListener, event_listener + class QEFX(QObject, QtEventListener): _logger = get_logger(__name__) @@ -94,7 +95,7 @@ class QEFX(QObject, QtEventListener): self.fx.set_exchange(source) self.rateSourceChanged.emit() - enabledUpdated = pyqtSignal() # curiously, enabledChanged is clashing, so name it enabledUpdated + enabledUpdated = pyqtSignal() # curiously, enabledChanged is clashing, so name it enabledUpdated @pyqtProperty(bool, notify=enabledUpdated) def enabled(self): return self.fx.is_enabled() diff --git a/electrum/gui/qml/qeinvoice.py b/electrum/gui/qml/qeinvoice.py index 0230622ec..d29e26271 100644 --- a/electrum/gui/qml/qeinvoice.py +++ b/electrum/gui/qml/qeinvoice.py @@ -275,7 +275,7 @@ class QEInvoice(QObject, QtEventListener): self._timer.start() else: self.update_userinfo() - self.determine_can_pay() # status went to PR_EXPIRED + self.determine_can_pay() # status went to PR_EXPIRED @pyqtSlot() def updateStatusString(self): @@ -293,7 +293,7 @@ class QEInvoice(QObject, QtEventListener): if self.amount.isEmpty: self.userinfo = _('Enter the amount you want to send') - if amount.isEmpty and self.status == PR_UNPAID: # unspecified amount + if amount.isEmpty and self.status == PR_UNPAID: # unspecified amount return if self.invoiceType == QEInvoice.Type.LightningInvoice: @@ -403,6 +403,7 @@ class QEInvoiceParser(QEInvoice): self._recipient = '' self._pi = None + self._lnurlData = None self.clear() @@ -476,7 +477,7 @@ class QEInvoiceParser(QEInvoice): self.setValidOnchainInvoice(invoice) self.validationSuccess.emit() else: - self.validationError.emit('unknown', f"invoice error:\n{pr.error}") + self.validationError.emit('unknown', f'invoice error:\n{pr.error}') def validateRecipient(self, recipient): if not recipient: @@ -485,7 +486,8 @@ class QEInvoiceParser(QEInvoice): self._pi = PaymentIdentifier(self._wallet.wallet, recipient) if not self._pi.is_valid() or self._pi.type not in [PaymentIdentifierType.SPK, PaymentIdentifierType.BIP21, - PaymentIdentifierType.BIP70, PaymentIdentifierType.BOLT11, PaymentIdentifierType.LNURLP]: + PaymentIdentifierType.BIP70, PaymentIdentifierType.BOLT11, + PaymentIdentifierType.LNURLP]: self.validationError.emit('unknown', _('Unknown invoice')) return @@ -552,6 +554,7 @@ class QEInvoiceParser(QEInvoice): def resolve_pi(self): assert self._pi.need_resolve() + def on_finished(pi): if pi.is_error(): pass @@ -604,7 +607,7 @@ class QEInvoiceParser(QEInvoice): # assure no shenanigans with the bolt11 invoice we get back lninvoice = Invoice.from_bech32(invoice) - if orig_amount * 1000 != lninvoice.amount_msat: # TODO msat precision can cause trouble here + if orig_amount * 1000 != lninvoice.amount_msat: # TODO msat precision can cause trouble here raise Exception('Unexpected amount in invoice, differs from lnurl-pay specified amount') self.recipient = invoice diff --git a/electrum/gui/qml/qeinvoicelistmodel.py b/electrum/gui/qml/qeinvoicelistmodel.py index ca017836c..d946cb97f 100644 --- a/electrum/gui/qml/qeinvoicelistmodel.py +++ b/electrum/gui/qml/qeinvoicelistmodel.py @@ -1,7 +1,7 @@ from abc import abstractmethod from typing import TYPE_CHECKING, List, Dict, Any -from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject, QTimer +from PyQt5.QtCore import pyqtSlot, QTimer from PyQt5.QtCore import Qt, QAbstractListModel, QModelIndex from electrum.logging import get_logger @@ -29,6 +29,7 @@ class QEAbstractInvoiceListModel(QAbstractListModel): def __init__(self, wallet: 'Abstract_Wallet', parent=None): super().__init__(parent) self.wallet = wallet + self._invoices = [] self._timer = QTimer(self) self._timer.setSingleShot(True) @@ -41,13 +42,13 @@ class QEAbstractInvoiceListModel(QAbstractListModel): raise e def rowCount(self, index): - return len(self.invoices) + return len(self._invoices) def roleNames(self): return self._ROLE_MAP def data(self, index, role): - invoice = self.invoices[index.row()] + invoice = self._invoices[index.row()] role_index = role - Qt.UserRole value = invoice[self._ROLE_NAMES[role_index]] @@ -59,7 +60,7 @@ class QEAbstractInvoiceListModel(QAbstractListModel): def clear(self): self.beginResetModel() - self.invoices = [] + self._invoices = [] self.endResetModel() @pyqtSlot() @@ -71,7 +72,7 @@ class QEAbstractInvoiceListModel(QAbstractListModel): self.clear() self.beginInsertRows(QModelIndex(), 0, len(invoices) - 1) - self.invoices = invoices + self._invoices = invoices self.endInsertRows() self.set_status_timer() @@ -79,7 +80,7 @@ class QEAbstractInvoiceListModel(QAbstractListModel): def add_invoice(self, invoice: BaseInvoice): # skip if already in list key = invoice.get_id() - for x in self.invoices: + for x in self._invoices: if x['key'] == key: return @@ -87,7 +88,7 @@ class QEAbstractInvoiceListModel(QAbstractListModel): self._logger.debug(str(item)) self.beginInsertRows(QModelIndex(), 0, 0) - self.invoices.insert(0, item) + self._invoices.insert(0, item) self.endInsertRows() self.set_status_timer() @@ -97,29 +98,29 @@ class QEAbstractInvoiceListModel(QAbstractListModel): self.add_invoice(self.get_invoice_for_key(key)) def delete_invoice(self, key: str): - for i, invoice in enumerate(self.invoices): + for i, invoice in enumerate(self._invoices): if invoice['key'] == key: self.beginRemoveRows(QModelIndex(), i, i) - self.invoices.pop(i) + self._invoices.pop(i) self.endRemoveRows() break self.set_status_timer() def get_model_invoice(self, key: str): - for invoice in self.invoices: + for invoice in self._invoices: if invoice['key'] == key: return invoice return None @pyqtSlot(str, int) def updateInvoice(self, key, status): - self._logger.debug('updating invoice for %s to %d' % (key,status)) - for i, item in enumerate(self.invoices): + self._logger.debug(f'updating invoice for {key} to {status}') + for i, item in enumerate(self._invoices): if item['key'] == key: invoice = self.get_invoice_for_key(key) item['status'] = status item['status_str'] = invoice.get_status_str(status) - index = self.index(i,0) + index = self.index(i, 0) self.dataChanged.emit(index, index, [self._ROLE_RMAP['status'], self._ROLE_RMAP['status_str']]) return @@ -137,7 +138,7 @@ class QEAbstractInvoiceListModel(QAbstractListModel): def set_status_timer(self): nearest_interval = LN_EXPIRY_NEVER - for invoice in self.invoices: + for invoice in self._invoices: if invoice['status'] != PR_EXPIRED: if invoice['expiry'] > 0 and invoice['expiry'] != LN_EXPIRY_NEVER: interval = status_update_timer_interval(invoice['timestamp'] + invoice['expiry']) @@ -150,11 +151,11 @@ class QEAbstractInvoiceListModel(QAbstractListModel): @pyqtSlot() def updateStatusStrings(self): - for i, item in enumerate(self.invoices): + for i, item in enumerate(self._invoices): invoice = self.get_invoice_for_key(item['key']) item['status'] = self.wallet.get_invoice_status(invoice) item['status_str'] = invoice.get_status_str(item['status']) - index = self.index(i,0) + index = self.index(i, 0) self.dataChanged.emit(index, index, [self._ROLE_RMAP['status'], self._ROLE_RMAP['status_str']]) self.set_status_timer() @@ -206,6 +207,7 @@ class QEInvoiceListModel(QEAbstractInvoiceListModel, QtEventListener): def get_invoice_as_dict(self, invoice: Invoice): return self.wallet.export_invoice(invoice) + class QERequestListModel(QEAbstractInvoiceListModel, QtEventListener): def __init__(self, wallet, parent=None): super().__init__(wallet, parent) diff --git a/electrum/gui/qml/qelnpaymentdetails.py b/electrum/gui/qml/qelnpaymentdetails.py index 49fef7dde..545f7877c 100644 --- a/electrum/gui/qml/qelnpaymentdetails.py +++ b/electrum/gui/qml/qelnpaymentdetails.py @@ -45,7 +45,7 @@ class QELnPaymentDetails(QObject): @key.setter def key(self, key: str): if self._key != key: - self._logger.debug('key set -> %s' % key) + self._logger.debug(f'key set -> {key}') self._key = key self.keyChanged.emit() self.update() @@ -104,7 +104,7 @@ class QELnPaymentDetails(QObject): self._label = tx['label'] self._date = format_time(tx['timestamp']) self._timestamp = tx['timestamp'] - self._status = 'settled' # TODO: other states? get_lightning_history is deciding the filter for us :( + self._status = 'settled' # TODO: other states? get_lightning_history is deciding the filter for us :( self._phash = tx['payment_hash'] self._preimage = tx['preimage'] diff --git a/electrum/gui/qml/qenetwork.py b/electrum/gui/qml/qenetwork.py index 8abf94134..1cfeb3c58 100644 --- a/electrum/gui/qml/qenetwork.py +++ b/electrum/gui/qml/qenetwork.py @@ -1,6 +1,6 @@ from typing import TYPE_CHECKING -from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject +from PyQt5.QtCore import pyqtProperty, pyqtSignal, QObject from electrum.logging import get_logger from electrum import constants @@ -173,12 +173,11 @@ class QENetwork(QObject, QtEventListener): @event_listener def on_event_unknown_channels(self, unknown): - if unknown == 0 and self._gossipUnknownChannels == 0: # TODO: backend sends a lot of unknown=0 events + if unknown == 0 and self._gossipUnknownChannels == 0: # TODO: backend sends a lot of unknown=0 events return self._logger.debug(f'unknown channels {unknown}') self._gossipUnknownChannels = unknown self.gossipUpdated.emit() - #self.lightning_gossip_num_queries = unknown def on_gossip_setting_changed(self): if not self.network: @@ -205,7 +204,8 @@ class QENetwork(QObject, QtEventListener): net_params = self.network.get_parameters() try: server = ServerAddr.from_str_with_inference(server) - if not server: raise Exception("failed to parse") + if not server: + raise Exception('failed to parse') except Exception: return net_params = net_params._replace(server=server, auto_connect=self._qeconfig.autoConnect) @@ -215,7 +215,7 @@ class QENetwork(QObject, QtEventListener): def serverWithStatus(self): server = self._server if not self.network.is_connected(): # connecting or disconnected - return f"{server} (connecting...)" + return f'{server} (connecting...)' return server @pyqtProperty(str, notify=statusChanged) @@ -244,7 +244,7 @@ class QENetwork(QObject, QtEventListener): @pyqtProperty(str, notify=dataChanged) def networkName(self): - return constants.net.__name__.replace('Bitcoin','') + return constants.net.__name__.replace('Bitcoin', '') @pyqtProperty('QVariantMap', notify=proxyChanged) def proxy(self): @@ -275,7 +275,7 @@ class QENetwork(QObject, QtEventListener): 'peers': self._gossipPeers, 'unknown_channels': self._gossipUnknownChannels, 'db_nodes': self._gossipDbNodes, - 'db_channels': self._gossipDbChannels , + 'db_channels': self._gossipDbChannels, 'db_policies': self._gossipDbPolicies } diff --git a/electrum/gui/qml/qeqr.py b/electrum/gui/qml/qeqr.py index cc3d8a4f0..1eaa90306 100644 --- a/electrum/gui/qml/qeqr.py +++ b/electrum/gui/qml/qeqr.py @@ -16,6 +16,7 @@ from electrum.qrreader import get_qr_reader from electrum.i18n import _ from electrum.util import profiler, get_asyncio_loop + class QEQRParser(QObject): _logger = get_logger(__name__) @@ -28,6 +29,7 @@ class QEQRParser(QObject): self._busy = False self._image = None + self._data = None self._text = text self.qrreader = get_qr_reader() @@ -118,10 +120,12 @@ class QEQRParser(QObject): result.append(QPoint(x+self.scan_pos_x, y+self.scan_pos_y)) return result + class QEQRImageProvider(QQuickImageProvider): def __init__(self, max_size, parent=None): super().__init__(QQuickImageProvider.Image) self._max_size = max_size + self.qimg = None _logger = get_logger(__name__) @@ -161,6 +165,7 @@ class QEQRImageProvider(QQuickImageProvider): self.qimg.fill(QColor('gray')) return self.qimg, self.qimg.size() + # helper for placing icon exactly where it should go on the QR code # pyqt5 is unwilling to accept slots on QEQRImageProvider, so we need to define # a separate class (sigh) @@ -187,6 +192,11 @@ class QEQRImageProviderHelper(QObject): qr.box_size = math.floor(pixelsize/modules) # calculate icon width in modules icon_modules = int(modules / 5) - icon_modules += (icon_modules+1)%2 # force odd - - return { 'modules': modules, 'box_size': qr.box_size, 'icon_modules': icon_modules, 'valid' : valid } + icon_modules += (icon_modules+1) % 2 # force odd + + return { + 'modules': modules, + 'box_size': qr.box_size, + 'icon_modules': icon_modules, + 'valid': valid + } diff --git a/electrum/gui/qml/qerequestdetails.py b/electrum/gui/qml/qerequestdetails.py index 855b9feb9..9d07d54e1 100644 --- a/electrum/gui/qml/qerequestdetails.py +++ b/electrum/gui/qml/qerequestdetails.py @@ -83,7 +83,6 @@ class QERequestDetails(QObject, QtEventListener): self.keyChanged.emit() self.initRequest() - statusChanged = pyqtSignal() @pyqtProperty(int, notify=statusChanged) def status(self): return self._wallet.wallet.get_invoice_status(self._req) @@ -133,7 +132,6 @@ class QERequestDetails(QObject, QtEventListener): def bip21(self): return self._req.get_bip21_URI() if self._req else '' - def initRequest(self): if self._wallet is None or self._key is None: return @@ -160,7 +158,6 @@ class QERequestDetails(QObject, QtEventListener): self._timer.setInterval(interval) # msec self._timer.start() - @pyqtSlot() def updateStatusString(self): self.statusChanged.emit() diff --git a/electrum/gui/qml/qeserverlistmodel.py b/electrum/gui/qml/qeserverlistmodel.py index aee729d7b..040c0efea 100644 --- a/electrum/gui/qml/qeserverlistmodel.py +++ b/electrum/gui/qml/qeserverlistmodel.py @@ -1,8 +1,8 @@ -from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject +from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot from PyQt5.QtCore import Qt, QAbstractListModel, QModelIndex from electrum.logging import get_logger -from electrum.util import Satoshis, format_time +from electrum.util import Satoshis from electrum.interface import ServerAddr, PREFERRED_NETWORK_PROTOCOL from electrum import blockchain @@ -22,6 +22,7 @@ class QEServerListModel(QAbstractListModel, QtEventListener): super().__init__(parent) self._chaintips = 0 + self._servers = [] self.network = network self.initModel() @@ -44,13 +45,13 @@ class QEServerListModel(QAbstractListModel, QtEventListener): self.initModel() def rowCount(self, index): - return len(self.servers) + return len(self._servers) def roleNames(self): return self._ROLE_MAP def data(self, index, role): - server = self.servers[index.row()] + server = self._servers[index.row()] role_index = role - Qt.UserRole value = server[self._ROLE_NAMES[role_index]] @@ -62,7 +63,7 @@ class QEServerListModel(QAbstractListModel, QtEventListener): def clear(self): self.beginResetModel() - self.servers = [] + self._servers = [] self.endResetModel() chaintipsChanged = pyqtSignal() @@ -97,14 +98,15 @@ class QEServerListModel(QAbstractListModel, QtEventListener): self._logger.debug(f'chain {chain_id} has name={name}, max_forkpoint=@{b.get_max_forkpoint()}, height={b.height()}') for i in interfaces: - server = {} - server['chain'] = name - server['chain_height'] = b.height() - server['is_primary'] = i == self.network.interface - server['is_connected'] = True - server['name'] = str(i.server) - server['address'] = i.server.to_friendly_name() - server['height'] = i.tip + server = { + 'chain': name, + 'chain_height': b.height(), + 'is_primary': i == self.network.interface, + 'is_connected': True, + 'name': str(i.server), + 'address': i.server.to_friendly_name(), + 'height': i.tip + } servers.append(server) @@ -120,17 +122,18 @@ class QEServerListModel(QAbstractListModel, QtEventListener): port = d.get(protocol) if port: s = ServerAddr(_host, port, protocol=protocol) - server = {} - server['chain'] = '' - server['chain_height'] = 0 - server['height'] = 0 - server['is_primary'] = False - server['is_connected'] = False - server['name'] = s.net_addr_str() + server = { + 'chain': '', + 'chain_height': 0, + 'height': 0, + 'is_primary': False, + 'is_connected': False, + 'name': s.net_addr_str() + } server['address'] = server['name'] servers.append(server) self.beginInsertRows(QModelIndex(), 0, len(servers) - 1) - self.servers = servers + self._servers = servers self.endInsertRows() diff --git a/electrum/gui/qml/qetransactionlistmodel.py b/electrum/gui/qml/qetransactionlistmodel.py index eaa773372..3ecb40e7c 100644 --- a/electrum/gui/qml/qetransactionlistmodel.py +++ b/electrum/gui/qml/qetransactionlistmodel.py @@ -1,7 +1,7 @@ from datetime import datetime, timedelta from typing import TYPE_CHECKING, Dict, Any -from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject +from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot from PyQt5.QtCore import Qt, QAbstractListModel, QModelIndex from electrum.logging import get_logger @@ -19,9 +19,9 @@ class QETransactionListModel(QAbstractListModel, QtEventListener): _logger = get_logger(__name__) # define listmodel rolemap - _ROLE_NAMES=('txid','fee_sat','height','confirmations','timestamp','monotonic_timestamp', - 'incoming','value','date','label','txpos_in_block','fee', - 'inputs','outputs','section','type','lightning','payment_hash','key','complete') + _ROLE_NAMES=('txid', 'fee_sat', 'height', 'confirmations', 'timestamp', 'monotonic_timestamp', + 'incoming', 'value', 'date', 'label', 'txpos_in_block', 'fee', + 'inputs', 'outputs', 'section', 'type', 'lightning', 'payment_hash', 'key', 'complete') _ROLE_KEYS = range(Qt.UserRole, Qt.UserRole + len(_ROLE_NAMES)) _ROLE_MAP = dict(zip(_ROLE_KEYS, [bytearray(x.encode()) for x in _ROLE_NAMES])) _ROLE_RMAP = dict(zip(_ROLE_NAMES, _ROLE_KEYS)) @@ -34,11 +34,13 @@ class QETransactionListModel(QAbstractListModel, QtEventListener): self.onchain_domain = onchain_domain self.include_lightning = include_lightning + self.tx_history = [] + self.register_callbacks() self.destroyed.connect(lambda: self.on_destroy()) self.requestRefresh.connect(lambda: self.initModel()) - self.setDirty() + self._dirty = True self.initModel() def on_destroy(self): @@ -159,19 +161,19 @@ class QETransactionListModel(QAbstractListModel, QtEventListener): txts = datetime.fromtimestamp(timestamp) today = datetime.today().replace(hour=0, minute=0, second=0, microsecond=0) - if (txts > today): + if txts > today: return 'today' - elif (txts > today - timedelta(days=1)): + elif txts > today - timedelta(days=1): return 'yesterday' - elif (txts > today - timedelta(days=7)): + elif txts > today - timedelta(days=7): return 'lastweek' - elif (txts > today - timedelta(days=31)): + elif txts > today - timedelta(days=31): return 'lastmonth' else: return 'older' def format_date_by_section(self, section, date): - #TODO: l10n + # TODO: l10n dfmt = { 'today': '%H:%M:%S', 'yesterday': '%H:%M:%S', @@ -194,7 +196,6 @@ class QETransactionListModel(QAbstractListModel, QtEventListener): ) return tx_mined_info - # initial model data @pyqtSlot() @pyqtSlot(bool) def initModel(self, force: bool = False): @@ -231,7 +232,7 @@ class QETransactionListModel(QAbstractListModel, QtEventListener): tx['section'] = self.get_section_by_timestamp(info.timestamp) tx['date'] = self.format_date_by_section(tx['section'], datetime.fromtimestamp(info.timestamp)) index = self.index(i,0) - roles = [self._ROLE_RMAP[x] for x in ['section','height','confirmations','timestamp','date']] + roles = [self._ROLE_RMAP[x] for x in ['section', 'height', 'confirmations', 'timestamp', 'date']] self.dataChanged.emit(index, index, roles) return diff --git a/electrum/gui/qml/qetxdetails.py b/electrum/gui/qml/qetxdetails.py index b70bc838c..fa63ea65c 100644 --- a/electrum/gui/qml/qetxdetails.py +++ b/electrum/gui/qml/qetxdetails.py @@ -4,7 +4,7 @@ from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject from electrum.i18n import _ from electrum.logging import get_logger -from electrum.util import format_time, AddTransactionException, TxMinedInfo +from electrum.util import format_time, TxMinedInfo from electrum.transaction import tx_from_any, Transaction from electrum.network import Network @@ -12,6 +12,7 @@ from .qewallet import QEWallet from .qetypes import QEAmount from .util import QtEventListener, event_listener + class QETxDetails(QObject, QtEventListener): _logger = get_logger(__name__) @@ -68,13 +69,13 @@ class QETxDetails(QObject, QtEventListener): @event_listener def on_event_verified(self, wallet, txid, info): if wallet == self._wallet.wallet and txid == self._txid: - self._logger.debug('verified event for our txid %s' % txid) + self._logger.debug(f'verified event for our txid {txid}') self.update() @event_listener def on_event_new_transaction(self, wallet, tx): if wallet == self._wallet.wallet and tx.txid() == self._txid: - self._logger.debug('new_transaction event for our txid %s' % self._txid) + self._logger.debug(f'new_transaction event for our txid {txid}') self.update() walletChanged = pyqtSignal() @@ -292,7 +293,7 @@ class QETxDetails(QObject, QtEventListener): group_id = item.get('group_id') if group_id: full_history = self._wallet.wallet.get_full_history() - group_item = full_history['group:'+ group_id] + group_item = full_history['group:' + group_id] self._lnamount.satsInt = int(group_item['ln_value'].value) else: self._lnamount.satsInt = int(item['amount_msat'] / 1000) diff --git a/electrum/gui/qml/qetxfinalizer.py b/electrum/gui/qml/qetxfinalizer.py index 9748c1c60..5b8383378 100644 --- a/electrum/gui/qml/qetxfinalizer.py +++ b/electrum/gui/qml/qetxfinalizer.py @@ -287,11 +287,11 @@ class QETxFinalizer(TxFeeSlider): if self._canRbf != canRbf: self._canRbf = canRbf self.canRbfChanged.emit() - self.rbf = self._canRbf # if we can RbF, we do RbF + self.rbf = self._canRbf # if we can RbF, we do RbF @profiler def make_tx(self, amount): - self._logger.debug('make_tx amount = %s' % str(amount)) + self._logger.debug(f'make_tx amount={amount}') if self.f_make_tx: tx = self.f_make_tx(amount) @@ -299,7 +299,7 @@ class QETxFinalizer(TxFeeSlider): # default impl coins = self._wallet.wallet.get_spendable_coins(None) outputs = [PartialTxOutput.from_address_and_value(self.address, amount)] - tx = self._wallet.wallet.make_unsigned_transaction(coins=coins,outputs=outputs, fee=None,rbf=self._rbf) + tx = self._wallet.wallet.make_unsigned_transaction(coins=coins, outputs=outputs, fee=None, rbf=self._rbf) self._logger.debug('fee: %d, inputs: %d, outputs: %d' % (tx.get_fee(), len(tx.inputs()), len(tx.outputs()))) @@ -312,7 +312,7 @@ class QETxFinalizer(TxFeeSlider): try: # make unsigned transaction - tx = self.make_tx(amount = '!' if self._amount.isMax else self._amount.satsInt) + tx = self.make_tx(amount='!' if self._amount.isMax else self._amount.satsInt) except NotEnoughFunds: self.warning = _("Not enough funds") self._valid = False @@ -373,10 +373,7 @@ class QETxFinalizer(TxFeeSlider): self.f_accept(self._tx) return - self._wallet.sign(self._tx, - broadcast=True, - on_success=partial(self.on_signed_tx, False) - ) + self._wallet.sign(self._tx, broadcast=True, on_success=partial(self.on_signed_tx, False)) @pyqtSlot() def sign(self): @@ -384,10 +381,7 @@ class QETxFinalizer(TxFeeSlider): self._logger.error('no valid tx') return - self._wallet.sign(self._tx, - broadcast=False, - on_success=partial(self.on_signed_tx, True) - ) + self._wallet.sign(self._tx, broadcast=False, on_success=partial(self.on_signed_tx, True)) def on_signed_tx(self, save: bool, tx: Transaction): self._logger.debug('on_signed_tx') @@ -405,12 +399,13 @@ class QETxFinalizer(TxFeeSlider): return [str(self._tx), txqr[0], txqr[1]] -# mixin for watching an existing TX based on its txid for verified event -# requires self._wallet to contain a QEWallet instance -# exposes txid qt property -# calls get_tx() once txid is set -# calls tx_verified and emits txMined signal once tx is verified class TxMonMixin(QtEventListener): + """ mixin for watching an existing TX based on its txid for verified event. + requires self._wallet to contain a QEWallet instance. + exposes txid qt property. + calls get_tx() once txid is set. + calls tx_verified and emits txMined signal once tx is verified. + """ txMined = pyqtSignal() def __init__(self, parent=None): @@ -505,7 +500,6 @@ class QETxRbfFeeBumper(TxFeeSlider, TxMonMixin): self.bumpMethodChanged.emit() self.update() - def get_tx(self): assert self._txid self._orig_tx = self._wallet.wallet.db.get_transaction(self._txid) @@ -783,7 +777,7 @@ class QETxCpfpFeeBumper(TxFeeSlider, TxMonMixin): return fee def update(self): - if not self._txid: # not initialized yet + if not self._txid: # not initialized yet return assert self._parent_tx diff --git a/electrum/gui/qml/qetypes.py b/electrum/gui/qml/qetypes.py index d41a5cd8a..d787b6e93 100644 --- a/electrum/gui/qml/qetypes.py +++ b/electrum/gui/qml/qetypes.py @@ -11,10 +11,11 @@ from electrum.i18n import _ # should also capture millisats amounts and MAX/'!' indicators # and (unformatted) string representations + class QEAmount(QObject): _logger = get_logger(__name__) - def __init__(self, *, amount_sat: int = 0, amount_msat: int = 0, is_max: bool = False, from_invoice = None, parent=None): + def __init__(self, *, amount_sat: int = 0, amount_msat: int = 0, is_max: bool = False, from_invoice=None, parent=None): super().__init__(parent) self._amount_sat = int(amount_sat) if amount_sat is not None else None self._amount_msat = int(amount_msat) if amount_msat is not None else None @@ -31,7 +32,7 @@ class QEAmount(QObject): @pyqtProperty('qint64', notify=valueChanged) def satsInt(self): - if self._amount_sat is None: # should normally be defined when accessing this property + if self._amount_sat is None: # should normally be defined when accessing this property self._logger.warning('amount_sat is undefined, returning 0') return 0 return self._amount_sat @@ -44,7 +45,7 @@ class QEAmount(QObject): @pyqtProperty('qint64', notify=valueChanged) def msatsInt(self): - if self._amount_msat is None: # should normally be defined when accessing this property + if self._amount_msat is None: # should normally be defined when accessing this property self._logger.warning('amount_msat is undefined, returning 0') return 0 return self._amount_msat @@ -86,7 +87,7 @@ class QEAmount(QObject): def copyFrom(self, amount): if not amount: - self._logger.warning('copyFrom with None argument. assuming 0') # TODO + self._logger.warning('copyFrom with None argument. assuming 0') # TODO amount = QEAmount() self.satsInt = amount.satsInt self.msatsInt = amount.msatsInt diff --git a/electrum/gui/qml/qewallet.py b/electrum/gui/qml/qewallet.py index 10ba00169..148a39303 100644 --- a/electrum/gui/qml/qewallet.py +++ b/electrum/gui/qml/qewallet.py @@ -2,18 +2,17 @@ import asyncio import queue import threading import time -from typing import TYPE_CHECKING, Optional, Tuple, Callable +from typing import TYPE_CHECKING, Callable from functools import partial -from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject, QTimer, QMetaObject, Qt +from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject, QTimer -from electrum import bitcoin from electrum.i18n import _ -from electrum.invoices import InvoiceError, PR_DEFAULT_EXPIRATION_WHEN_CREATING, PR_PAID, PR_BROADCASTING, PR_BROADCAST +from electrum.invoices import InvoiceError, PR_PAID, PR_BROADCASTING, PR_BROADCAST from electrum.logging import get_logger from electrum.network import TxBroadcastError, BestEffortRequestFailed -from electrum.transaction import PartialTxOutput, PartialTransaction, Transaction -from electrum.util import parse_max_spend, InvalidPassword, event_listener, AddTransactionException, get_asyncio_loop +from electrum.transaction import PartialTransaction, Transaction +from electrum.util import InvalidPassword, event_listener, AddTransactionException, get_asyncio_loop from electrum.plugin import run_hook from electrum.wallet import Multisig_Wallet from electrum.crypto import pw_decode_with_version_and_mac @@ -30,6 +29,7 @@ if TYPE_CHECKING: from electrum.wallet import Abstract_Wallet from .qeinvoice import QEInvoice + class QEWallet(AuthMixin, QObject, QtEventListener): __instances = [] @@ -53,12 +53,14 @@ class QEWallet(AuthMixin, QObject, QtEventListener): # shared signal for many static wallet properties dataChanged = pyqtSignal() + balanceChanged = pyqtSignal() requestStatusChanged = pyqtSignal([str,int], arguments=['key','status']) requestCreateSuccess = pyqtSignal([str], arguments=['key']) requestCreateError = pyqtSignal([str], arguments=['error']) invoiceStatusChanged = pyqtSignal([str,int], arguments=['key','status']) invoiceCreateSuccess = pyqtSignal() invoiceCreateError = pyqtSignal([str,str], arguments=['code','error']) + paymentAuthRejected = pyqtSignal() paymentSucceeded = pyqtSignal([str], arguments=['key']) paymentFailed = pyqtSignal([str,str], arguments=['key','reason']) requestNewPassword = pyqtSignal() @@ -196,7 +198,7 @@ class QEWallet(AuthMixin, QObject, QtEventListener): if wallet == self.wallet: self._logger.info(f'removed transaction {tx.txid()}') self.addressModel.setDirty() - self.historyModel.initModel(True) #setDirty() + self.historyModel.initModel(True) # setDirty()? self.balanceChanged.emit() @qt_event_listener @@ -206,7 +208,7 @@ class QEWallet(AuthMixin, QObject, QtEventListener): self.balanceChanged.emit() self.synchronizing = not wallet.is_up_to_date() if not self.synchronizing: - self.historyModel.initModel() # refresh if dirty + self.historyModel.initModel() # refresh if dirty @event_listener def on_event_channel(self, wallet, channel): @@ -224,7 +226,7 @@ class QEWallet(AuthMixin, QObject, QtEventListener): def on_event_payment_succeeded(self, wallet, key): if wallet == self.wallet: self.paymentSucceeded.emit(key) - self.historyModel.initModel(True) # TODO: be less dramatic + self.historyModel.initModel(True) # TODO: be less dramatic @event_listener def on_event_payment_failed(self, wallet, key, reason): @@ -426,13 +428,11 @@ class QEWallet(AuthMixin, QObject, QtEventListener): @pyqtProperty(bool, notify=dataChanged) def canSignWithoutCosigner(self): if isinstance(self.wallet, Multisig_Wallet): - if self.wallet.wallet_type == '2fa': # 2fa is multisig, but it handles cosigning itself + if self.wallet.wallet_type == '2fa': # 2fa is multisig, but it handles cosigning itself return True return self.wallet.m == 1 return True - balanceChanged = pyqtSignal() - @pyqtProperty(QEAmount, notify=balanceChanged) def frozenBalance(self): c, u, x = self.wallet.get_frozen_balance() @@ -499,9 +499,11 @@ class QEWallet(AuthMixin, QObject, QtEventListener): success = self.do_sign(tx, broadcast) if success: - if on_success: on_success(tx) + if on_success: + on_success(tx) else: - if on_failure: on_failure() + if on_failure: + on_failure() def do_sign(self, tx, broadcast): try: @@ -534,14 +536,16 @@ class QEWallet(AuthMixin, QObject, QtEventListener): # this assumes a 2fa wallet, but there are no other tc_sign_wrapper hooks, so that's ok def on_sign_complete(self, broadcast, cb: Callable[[Transaction], None] = None, tx: Transaction = None): self.otpSuccess.emit() - if cb: cb(tx) + if cb: + cb(tx) if broadcast: self.broadcast(tx) # this assumes a 2fa wallet, but there are no other tc_sign_wrapper hooks, so that's ok def on_sign_failed(self, cb: Callable[[], None] = None, error: str = None): self.otpFailed.emit('error', error) - if cb: cb() + if cb: + cb() def request_otp(self, on_submit): self._otp_on_submit = on_submit @@ -577,7 +581,7 @@ class QEWallet(AuthMixin, QObject, QtEventListener): threading.Thread(target=broadcast_thread, daemon=True).start() - #TODO: properly catch server side errors, e.g. bad-txns-inputs-missingorspent + # TODO: properly catch server side errors, e.g. bad-txns-inputs-missingorspent def save_tx(self, tx: 'PartialTransaction'): assert tx @@ -585,7 +589,7 @@ class QEWallet(AuthMixin, QObject, QtEventListener): try: if not self.wallet.adb.add_transaction(tx): self.saveTxError.emit(tx.txid(), 'conflict', - _("Transaction could not be saved.") + "\n" + _("It conflicts with current history.")) + _("Transaction could not be saved.") + "\n" + _("It conflicts with current history.")) return self.wallet.save_db() self.saveTxSuccess.emit(tx.txid()) @@ -595,7 +599,6 @@ class QEWallet(AuthMixin, QObject, QtEventListener): self.saveTxError.emit(tx.txid(), 'error', str(e)) return False - paymentAuthRejected = pyqtSignal() def ln_auth_rejected(self): self.paymentAuthRejected.emit() @@ -724,7 +727,7 @@ class QEWallet(AuthMixin, QObject, QtEventListener): xpub = self.wallet.get_fingerprint() decrypted = pw_decode_with_version_and_mac(encrypted, xpub) return True - except Exception as e: + except Exception: return False @pyqtSlot() diff --git a/electrum/gui/qml/qewalletdb.py b/electrum/gui/qml/qewalletdb.py index 05ff6bb9f..b46850138 100644 --- a/electrum/gui/qml/qewalletdb.py +++ b/electrum/gui/qml/qewalletdb.py @@ -85,7 +85,7 @@ class QEWalletDB(QObject): @pyqtProperty('QString', notify=passwordChanged) def password(self): - return '' # no read access + return '' # no read access @password.setter def password(self, wallet_password): @@ -142,7 +142,6 @@ class QEWalletDB(QObject): self.invalidPassword.emit() else: # storage not encrypted; but it might still have a keystore pw # FIXME hack... load both db and full wallet, just to tell if it has keystore pw. - # this also completely ignores db.requires_split(), db.get_action(), etc try: db = WalletDB(self._storage.read(), storage=self._storage, upgrade=True) except WalletRequiresSplit as e: diff --git a/electrum/gui/qml/qewizard.py b/electrum/gui/qml/qewizard.py index 293348dee..e9aa95107 100644 --- a/electrum/gui/qml/qewizard.py +++ b/electrum/gui/qml/qewizard.py @@ -13,9 +13,13 @@ if TYPE_CHECKING: class QEAbstractWizard(QObject): + """ Concrete subclasses of QEAbstractWizard must also inherit from a concrete AbstractWizard subclass. + QEAbstractWizard forms the base for all QML GUI based wizards, while AbstractWizard defines + the base for non-gui wizard flow navigation functionality. + """ _logger = get_logger(__name__) - def __init__(self, parent = None): + def __init__(self, parent=None): QObject.__init__(self, parent) @pyqtSlot(result=str) @@ -45,32 +49,33 @@ class QEAbstractWizard(QObject): class QENewWalletWizard(NewWalletWizard, QEAbstractWizard): - createError = pyqtSignal([str], arguments=["error"]) createSuccess = pyqtSignal() - def __init__(self, daemon: 'QEDaemon', plugins: 'Plugins', parent = None): + def __init__(self, daemon: 'QEDaemon', plugins: 'Plugins', parent=None): NewWalletWizard.__init__(self, daemon.daemon, plugins) QEAbstractWizard.__init__(self, parent) self._qedaemon = daemon + self._path = None + self._password = None # attach view names and accept handlers self.navmap_merge({ - 'wallet_name': { 'gui': 'WCWalletName' }, - 'wallet_type': { 'gui': 'WCWalletType' }, - 'keystore_type': { 'gui': 'WCKeystoreType' }, - 'create_seed': { 'gui': 'WCCreateSeed' }, - 'confirm_seed': { 'gui': 'WCConfirmSeed' }, - 'have_seed': { 'gui': 'WCHaveSeed' }, - 'script_and_derivation': { 'gui': 'WCScriptAndDerivation' }, - 'have_master_key': { 'gui': 'WCHaveMasterKey' }, - 'multisig': { 'gui': 'WCMultisig' }, - 'multisig_cosigner_keystore': { 'gui': 'WCCosignerKeystore' }, - 'multisig_cosigner_key': { 'gui': 'WCHaveMasterKey' }, - 'multisig_cosigner_seed': { 'gui': 'WCHaveSeed' }, - 'multisig_cosigner_script_and_derivation': { 'gui': 'WCScriptAndDerivation' }, - 'imported': { 'gui': 'WCImport' }, - 'wallet_password': { 'gui': 'WCWalletPassword' } + 'wallet_name': {'gui': 'WCWalletName'}, + 'wallet_type': {'gui': 'WCWalletType'}, + 'keystore_type': {'gui': 'WCKeystoreType'}, + 'create_seed': {'gui': 'WCCreateSeed'}, + 'confirm_seed': {'gui': 'WCConfirmSeed'}, + 'have_seed': {'gui': 'WCHaveSeed'}, + 'script_and_derivation': {'gui': 'WCScriptAndDerivation'}, + 'have_master_key': {'gui': 'WCHaveMasterKey'}, + 'multisig': {'gui': 'WCMultisig'}, + 'multisig_cosigner_keystore': {'gui': 'WCCosignerKeystore'}, + 'multisig_cosigner_key': {'gui': 'WCHaveMasterKey'}, + 'multisig_cosigner_seed': {'gui': 'WCHaveSeed'}, + 'multisig_cosigner_script_and_derivation': {'gui': 'WCScriptAndDerivation'}, + 'imported': {'gui': 'WCImport'}, + 'wallet_password': {'gui': 'WCWalletPassword'} }) pathChanged = pyqtSignal() @@ -127,15 +132,14 @@ class QENewWalletWizard(NewWalletWizard, QEAbstractWizard): class QEServerConnectWizard(ServerConnectWizard, QEAbstractWizard): - def __init__(self, daemon: 'QEDaemon', parent=None): ServerConnectWizard.__init__(self, daemon.daemon) QEAbstractWizard.__init__(self, parent) # attach view names self.navmap_merge({ - 'autoconnect': { 'gui': 'WCAutoConnect' }, - 'proxy_ask': { 'gui': 'WCProxyAsk' }, - 'proxy_config': { 'gui': 'WCProxyConfig' }, - 'server_config': { 'gui': 'WCServerConfig' }, + 'autoconnect': {'gui': 'WCAutoConnect'}, + 'proxy_ask': {'gui': 'WCProxyAsk'}, + 'proxy_config': {'gui': 'WCProxyConfig'}, + 'server_config': {'gui': 'WCServerConfig'}, }) diff --git a/electrum/gui/qml/util.py b/electrum/gui/qml/util.py index 17e75186c..7d3442eeb 100644 --- a/electrum/gui/qml/util.py +++ b/electrum/gui/qml/util.py @@ -31,6 +31,7 @@ class QtEventListener(EventListener): # 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) @@ -56,10 +57,11 @@ def status_update_timer_interval(exp): return interval + # TODO: copied from desktop client, this could be moved to a set of common code. class TaskThread(QThread, Logger): - '''Thread that runs background tasks. Callbacks are guaranteed - to happen in the context of its parent.''' + """Thread that runs background tasks. Callbacks are guaranteed + to happen in the context of its parent.""" class Task(NamedTuple): task: Callable diff --git a/electrum/gui/qt/wizard/wizard.py b/electrum/gui/qt/wizard/wizard.py index bcf0de044..8439333e2 100644 --- a/electrum/gui/qt/wizard/wizard.py +++ b/electrum/gui/qt/wizard/wizard.py @@ -19,6 +19,10 @@ if TYPE_CHECKING: class QEAbstractWizard(QDialog, MessageBoxMixin): + """ Concrete subclasses of QEAbstractWizard must also inherit from a concrete AbstractWizard subclass. + QEAbstractWizard forms the base for all QtWidgets GUI based wizards, while AbstractWizard defines + the base for non-gui wizard flow navigation functionality. + """ _logger = get_logger(__name__) requestNext = pyqtSignal()