Browse Source

whitespace, imports, code style

master
Sander van Grieken 2 years ago
parent
commit
190c19d48c
  1. 7
      electrum/gui/qml/__init__.py
  2. 4
      electrum/gui/qml/auth.py
  3. 40
      electrum/gui/qml/qeaddresslistmodel.py
  4. 3
      electrum/gui/qml/qebip39recovery.py
  5. 3
      electrum/gui/qml/qebitcoin.py
  6. 48
      electrum/gui/qml/qechannellistmodel.py
  7. 14
      electrum/gui/qml/qechannelopener.py
  8. 7
      electrum/gui/qml/qeconfig.py
  9. 20
      electrum/gui/qml/qedaemon.py
  10. 1
      electrum/gui/qml/qefx.py
  11. 7
      electrum/gui/qml/qeinvoice.py
  12. 30
      electrum/gui/qml/qeinvoicelistmodel.py
  13. 2
      electrum/gui/qml/qelnpaymentdetails.py
  14. 8
      electrum/gui/qml/qenetwork.py
  15. 12
      electrum/gui/qml/qeqr.py
  16. 3
      electrum/gui/qml/qerequestdetails.py
  17. 45
      electrum/gui/qml/qeserverlistmodel.py
  18. 15
      electrum/gui/qml/qetransactionlistmodel.py
  19. 7
      electrum/gui/qml/qetxdetails.py
  20. 24
      electrum/gui/qml/qetxfinalizer.py
  21. 1
      electrum/gui/qml/qetypes.py
  22. 33
      electrum/gui/qml/qewallet.py
  23. 1
      electrum/gui/qml/qewalletdb.py
  24. 8
      electrum/gui/qml/qewizard.py
  25. 6
      electrum/gui/qml/util.py
  26. 4
      electrum/gui/qt/wizard/wizard.py

7
electrum/gui/qml/__init__.py

@ -2,7 +2,6 @@ import os
import signal import signal
import sys import sys
import threading import threading
import traceback
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
try: try:
@ -15,11 +14,10 @@ try:
except Exception: except Exception:
sys.exit("Error: Could not import PyQt5.QtQml on Linux systems, you may try 'sudo apt-get install python3-pyqt5.qtquick'") 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, from PyQt5.QtCore import (Qt, QCoreApplication, QLocale, QTranslator, QTimer, QT_VERSION_STR, PYQT_VERSION_STR)
QT_VERSION_STR, PYQT_VERSION_STR)
from PyQt5.QtGui import QGuiApplication from PyQt5.QtGui import QGuiApplication
from electrum.i18n import _, set_language, languages from electrum.i18n import _
from electrum.plugin import run_hook from electrum.plugin import run_hook
from electrum.util import profiler from electrum.util import profiler
from electrum.logging import Logger from electrum.logging import Logger
@ -29,7 +27,6 @@ if TYPE_CHECKING:
from electrum.daemon import Daemon from electrum.daemon import Daemon
from electrum.simple_config import SimpleConfig from electrum.simple_config import SimpleConfig
from electrum.plugin import Plugins from electrum.plugin import Plugins
from electrum.wallet import Abstract_Wallet
from .qeapp import ElectrumQmlApplication, Exception_Hook from .qeapp import ElectrumQmlApplication, Exception_Hook

4
electrum/gui/qml/auth.py

@ -1,9 +1,10 @@
from functools import wraps, partial from functools import wraps, partial
from PyQt5.QtCore import pyqtSignal, pyqtSlot, pyqtProperty from PyQt5.QtCore import pyqtSignal, pyqtSlot
from electrum.logging import get_logger from electrum.logging import get_logger
def auth_protect(func=None, reject=None, method='pin', message=''): def auth_protect(func=None, reject=None, method='pin', message=''):
if func is None: if func is None:
return partial(auth_protect, reject=reject, method=method, message=message) 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 return wrapper
class AuthMixin: class AuthMixin:
_auth_logger = get_logger(__name__) _auth_logger = get_logger(__name__)
authRequired = pyqtSignal([str, str], arguments=['method', 'authMessage']) authRequired = pyqtSignal([str, str], arguments=['method', 'authMessage'])

40
electrum/gui/qml/qeaddresslistmodel.py

@ -1,7 +1,7 @@
import itertools import itertools
from typing import TYPE_CHECKING 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 PyQt5.QtCore import Qt, QAbstractListModel, QModelIndex
from electrum.logging import get_logger from electrum.logging import get_logger
@ -24,20 +24,23 @@ class QEAddressListModel(QAbstractListModel):
def __init__(self, wallet: 'Abstract_Wallet', parent=None): def __init__(self, wallet: 'Abstract_Wallet', parent=None):
super().__init__(parent) super().__init__(parent)
self.wallet = wallet self.wallet = wallet
self.setDirty() self._receive_addresses = []
self._change_addresses = []
self._dirty = True
self.initModel() self.initModel()
def rowCount(self, index): 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): def roleNames(self):
return self._ROLE_MAP return self._ROLE_MAP
def data(self, index, role): def data(self, index, role):
if index.row() > len(self.receive_addresses) - 1: if index.row() > len(self._receive_addresses) - 1:
address = self.change_addresses[index.row() - len(self.receive_addresses)] address = self._change_addresses[index.row() - len(self._receive_addresses)]
else: else:
address = self.receive_addresses[index.row()] address = self._receive_addresses[index.row()]
role_index = role - Qt.UserRole role_index = role - Qt.UserRole
value = address[self._ROLE_NAMES[role_index]] value = address[self._ROLE_NAMES[role_index]]
if isinstance(value, (bool, list, int, str, QEAmount)) or value is None: if isinstance(value, (bool, list, int, str, QEAmount)) or value is None:
@ -48,18 +51,19 @@ class QEAddressListModel(QAbstractListModel):
def clear(self): def clear(self):
self.beginResetModel() self.beginResetModel()
self.receive_addresses = [] self._receive_addresses = []
self.change_addresses = [] self._change_addresses = []
self.endResetModel() self.endResetModel()
def addr_to_model(self, address): 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) c, u, x = self.wallet.get_addr_balance(address)
item['balance'] = QEAmount(amount_sat=c + u + x) item = {
item['held'] = self.wallet.is_frozen_address(address) '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 return item
@pyqtSlot() @pyqtSlot()
@ -86,19 +90,19 @@ class QEAddressListModel(QAbstractListModel):
self.beginInsertRows(QModelIndex(), 0, n_addresses - 1) self.beginInsertRows(QModelIndex(), 0, n_addresses - 1)
if self.wallet.wallet_type != 'imported': if self.wallet.wallet_type != 'imported':
for i, address in enumerate(r_addresses): 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): for i, address in enumerate(c_addresses):
insert_row('change', self.change_addresses, address, i) insert_row('change', self._change_addresses, address, i)
else: else:
for i, address in enumerate(r_addresses): 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.endInsertRows()
self._dirty = False self._dirty = False
@pyqtSlot(str) @pyqtSlot(str)
def updateAddress(self, address): 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: if a['address'] == address:
self.do_update(i, a) self.do_update(i, a)
return return

3
electrum/gui/qml/qebip39recovery.py

@ -26,7 +26,6 @@ class QEBip39RecoveryListModel(QAbstractListModel):
recoveryFailed = pyqtSignal() recoveryFailed = pyqtSignal()
stateChanged = pyqtSignal() stateChanged = pyqtSignal()
# userinfoChanged = pyqtSignal()
# define listmodel rolemap # define listmodel rolemap
_ROLE_NAMES=('description', 'derivation_path', 'script_type') _ROLE_NAMES=('description', 'derivation_path', 'script_type')
@ -112,7 +111,7 @@ class QEBip39RecoveryListModel(QAbstractListModel):
if isinstance(e, concurrent.futures.CancelledError): if isinstance(e, concurrent.futures.CancelledError):
self.state = QEBip39RecoveryListModel.State.Cancelled self.state = QEBip39RecoveryListModel.State.Cancelled
return 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.state = QEBip39RecoveryListModel.State.Failed
self._thread.stop() self._thread.stop()

3
electrum/gui/qml/qebitcoin.py

@ -5,7 +5,7 @@ from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject
from electrum import mnemonic from electrum import mnemonic
from electrum import keystore from electrum import keystore
from electrum.i18n import _ 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.logging import get_logger
from electrum.slip39 import decode_mnemonic, Slip39Error from electrum.slip39 import decode_mnemonic, Slip39Error
from electrum.util import get_asyncio_loop 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.mnemonic import Mnemonic, is_any_2fa_seed_type
from electrum.old_mnemonic import wordlist as old_wordlist from electrum.old_mnemonic import wordlist as old_wordlist
from .qetypes import QEAmount
class QEBitcoin(QObject): class QEBitcoin(QObject):
_logger = get_logger(__name__) _logger = get_logger(__name__)

48
electrum/gui/qml/qechannellistmodel.py

@ -29,6 +29,11 @@ class QEChannelListModel(QAbstractListModel, QtEventListener):
def __init__(self, wallet, parent=None): def __init__(self, wallet, parent=None):
super().__init__(parent) super().__init__(parent)
self.wallet = wallet self.wallet = wallet
self._channels = []
self._fm_backups = None
self._fm_nobackups = None
self.initModel() self.initModel()
# To avoid leaking references to "self" that prevent the # To avoid leaking references to "self" that prevent the
@ -52,19 +57,19 @@ class QEChannelListModel(QAbstractListModel, QtEventListener):
self.unregister_callbacks() self.unregister_callbacks()
def rowCount(self, index): def rowCount(self, index):
return len(self.channels) return len(self._channels)
# also expose rowCount as a property # also expose rowCount as a property
countChanged = pyqtSignal() countChanged = pyqtSignal()
@pyqtProperty(int, notify=countChanged) @pyqtProperty(int, notify=countChanged)
def count(self): def count(self):
return len(self.channels) return len(self._channels)
def roleNames(self): def roleNames(self):
return self._ROLE_MAP return self._ROLE_MAP
def data(self, index, role): def data(self, index, role):
tx = self.channels[index.row()] tx = self._channels[index.row()]
role_index = role - Qt.UserRole role_index = role - Qt.UserRole
value = tx[self._ROLE_NAMES[role_index]] value = tx[self._ROLE_NAMES[role_index]]
if isinstance(value, (bool, list, int, str, QEAmount)) or value is None: if isinstance(value, (bool, list, int, str, QEAmount)) or value is None:
@ -75,21 +80,22 @@ class QEChannelListModel(QAbstractListModel, QtEventListener):
def clear(self): def clear(self):
self.beginResetModel() self.beginResetModel()
self.channels = [] self._channels = []
self.endResetModel() self.endResetModel()
def channel_to_model(self, lnc): def channel_to_model(self, lnc):
lnworker = self.wallet.lnworker lnworker = self.wallet.lnworker
item = {} item = {
item['cid'] = lnc.channel_id.hex() 'cid': lnc.channel_id.hex(),
item['node_id'] = lnc.node_id.hex() 'node_id': lnc.node_id.hex(),
item['node_alias'] = lnworker.get_node_alias(lnc.node_id) or '' 'node_alias': lnworker.get_node_alias(lnc.node_id) or '',
item['short_cid'] = lnc.short_id_for_GUI() 'short_cid': lnc.short_id_for_GUI(),
item['state'] = lnc.get_state_for_GUI() 'state': lnc.get_state_for_GUI(),
item['state_code'] = int(lnc.get_state()) 'state_code': int(lnc.get_state()),
item['is_backup'] = lnc.is_backup() 'is_backup': lnc.is_backup(),
item['is_trampoline'] = lnworker.is_trampoline_peer(lnc.node_id) 'is_trampoline': lnworker.is_trampoline_peer(lnc.node_id),
item['capacity'] = QEAmount(amount_sat=lnc.get_capacity()) 'capacity': QEAmount(amount_sat=lnc.get_capacity())
}
if lnc.is_backup(): if lnc.is_backup():
item['can_send'] = QEAmount() item['can_send'] = QEAmount()
item['can_receive'] = QEAmount() item['can_receive'] = QEAmount()
@ -111,7 +117,7 @@ class QEChannelListModel(QAbstractListModel, QtEventListener):
numOpenChannelsChanged = pyqtSignal() numOpenChannelsChanged = pyqtSignal()
@pyqtProperty(int, notify=numOpenChannelsChanged) @pyqtProperty(int, notify=numOpenChannelsChanged)
def numOpenChannels(self): 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() @pyqtSlot()
def initModel(self): def initModel(self):
@ -134,20 +140,20 @@ class QEChannelListModel(QAbstractListModel, QtEventListener):
self.clear() self.clear()
self.beginInsertRows(QModelIndex(), 0, len(channels) - 1) self.beginInsertRows(QModelIndex(), 0, len(channels) - 1)
self.channels = channels self._channels = channels
self.endInsertRows() self.endInsertRows()
self.countChanged.emit() self.countChanged.emit()
def on_channel_updated(self, channel): 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(): if c['cid'] == channel.channel_id.hex():
self.do_update(i, channel) self.do_update(i, channel)
break break
def do_update(self, modelindex, channel): def do_update(self, modelindex, channel):
self._logger.debug(f'updating our channel {channel.short_id_for_GUI()}') 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)) modelitem.update(self.channel_to_model(channel))
mi = self.createIndex(modelindex, 0) mi = self.createIndex(modelindex, 0)
@ -163,7 +169,7 @@ class QEChannelListModel(QAbstractListModel, QtEventListener):
item = self.channel_to_model(channel) item = self.channel_to_model(channel)
self._logger.debug(item) self._logger.debug(item)
self.beginInsertRows(QModelIndex(), 0, 0) self.beginInsertRows(QModelIndex(), 0, 0)
self.channels.insert(0,item) self._channels.insert(0, item)
self.endInsertRows() self.endInsertRows()
self.countChanged.emit() self.countChanged.emit()
return return
@ -171,11 +177,11 @@ class QEChannelListModel(QAbstractListModel, QtEventListener):
@pyqtSlot(str) @pyqtSlot(str)
def removeChannel(self, cid): def removeChannel(self, cid):
self._logger.debug('remove channel with cid %s' % 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']: if cid == channel['cid']:
self._logger.debug(cid) self._logger.debug(cid)
self.beginRemoveRows(QModelIndex(), i, i) self.beginRemoveRows(QModelIndex(), i, i)
self.channels.remove(channel) self._channels.remove(channel)
self.endRemoveRows() self.endRemoveRows()
self.countChanged.emit() self.countChanged.emit()
return return

14
electrum/gui/qml/qechannelopener.py

@ -27,7 +27,8 @@ class QEChannelOpener(QObject, AuthMixin):
conflictingBackup = pyqtSignal([str], arguments=['message']) conflictingBackup = pyqtSignal([str], arguments=['message'])
channelOpening = pyqtSignal([str], arguments=['peer']) channelOpening = pyqtSignal([str], arguments=['peer'])
channelOpenError = pyqtSignal([str], arguments=['message']) 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
@ -41,6 +42,10 @@ class QEChannelOpener(QObject, AuthMixin):
self._opentx = None self._opentx = None
self._txdetails = None self._txdetails = None
self._finalizer = None
self._node_pubkey = None
self._connect_str_resolved = None
walletChanged = pyqtSignal() walletChanged = pyqtSignal()
@pyqtProperty(QEWallet, notify=walletChanged) @pyqtProperty(QEWallet, notify=walletChanged)
def wallet(self): def wallet(self):
@ -124,7 +129,7 @@ class QEChannelOpener(QObject, AuthMixin):
self.validChanged.emit() self.validChanged.emit()
return 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): if not self._amount or not (self._amount.satsInt > 0 or self._amount.isMax):
self._valid = False self._valid = False
self.validChanged.emit() self.validChanged.emit()
@ -136,9 +141,9 @@ class QEChannelOpener(QObject, AuthMixin):
@pyqtSlot(str, result=bool) @pyqtSlot(str, result=bool)
def validateConnectString(self, connect_str): def validateConnectString(self, connect_str):
try: try:
node_id, rest = extract_nodeid(connect_str) extract_nodeid(connect_str)
except ConnStringFormatError as e: 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 False
return True return True
@ -216,7 +221,6 @@ class QEChannelOpener(QObject, AuthMixin):
self._logger.exception("Problem opening channel: %s", error) self._logger.exception("Problem opening channel: %s", error)
self.channelOpenError.emit(error) self.channelOpenError.emit(error)
self._logger.debug('starting open thread') self._logger.debug('starting open thread')
self.channelOpening.emit(conn_str) self.channelOpening.emit(conn_str)
threading.Thread(target=open_thread, daemon=True).start() threading.Thread(target=open_thread, daemon=True).start()

7
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.bitcoin import TOTAL_COIN_SUPPLY_LIMIT_IN_BTC
from electrum.i18n import set_language, languages from electrum.i18n import set_language, languages
from electrum.logging import get_logger from electrum.logging import get_logger
from electrum.util import DECIMAL_POINT_DEFAULT, base_unit_name_to_decimal_point from electrum.util import base_unit_name_to_decimal_point
from electrum.invoices import PR_DEFAULT_EXPIRATION_WHEN_CREATING
from electrum.simple_config import SimpleConfig
from .qetypes import QEAmount from .qetypes import QEAmount
from .auth import AuthMixin, auth_protect from .auth import AuthMixin, auth_protect
if TYPE_CHECKING:
from electrum.simple_config import SimpleConfig
class QEConfig(AuthMixin, QObject): class QEConfig(AuthMixin, QObject):
_logger = get_logger(__name__) _logger = get_logger(__name__)

20
electrum/gui/qml/qedaemon.py

@ -35,16 +35,17 @@ class QEWalletListModel(QAbstractListModel):
def __init__(self, daemon, parent=None): def __init__(self, daemon, parent=None):
QAbstractListModel.__init__(self, parent) QAbstractListModel.__init__(self, parent)
self.daemon = daemon self.daemon = daemon
self._wallets = []
self.reload() self.reload()
def rowCount(self, index): def rowCount(self, index):
return len(self.wallets) return len(self._wallets)
def roleNames(self): def roleNames(self):
return self._ROLE_MAP return self._ROLE_MAP
def data(self, index, role): 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_index = role - Qt.UserRole
role_name = self._ROLE_NAMES[role_index] role_name = self._ROLE_NAMES[role_index]
if role_name == 'name': if role_name == 'name':
@ -58,7 +59,7 @@ class QEWalletListModel(QAbstractListModel):
def reload(self): def reload(self):
self._logger.debug('enumerating available wallets') self._logger.debug('enumerating available wallets')
self.beginResetModel() self.beginResetModel()
self.wallets = [] self._wallets = []
self.endResetModel() self.endResetModel()
available = [] available = []
@ -72,18 +73,18 @@ class QEWalletListModel(QAbstractListModel):
self.add_wallet(wallet_path = path) self.add_wallet(wallet_path = path)
def add_wallet(self, wallet_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_name = os.path.basename(wallet_path)
wallet_path = standardize_path(wallet_path) wallet_path = standardize_path(wallet_path)
item = (wallet_name, wallet_path) item = (wallet_name, wallet_path)
self.wallets.append(item) self._wallets.append(item)
self.endInsertRows() self.endInsertRows()
def remove_wallet(self, path): def remove_wallet(self, path):
i = 0 i = 0
wallets = [] wallets = []
remove = -1 remove = -1
for wallet_name, wallet_path in self.wallets: for wallet_name, wallet_path in self._wallets:
if wallet_path == path: if wallet_path == path:
remove = i remove = i
else: else:
@ -92,12 +93,12 @@ class QEWalletListModel(QAbstractListModel):
if remove >= 0: if remove >= 0:
self.beginRemoveRows(QModelIndex(), i, i) self.beginRemoveRows(QModelIndex(), i, i)
self.wallets = wallets self._wallets = wallets
self.endRemoveRows() self.endRemoveRows()
@pyqtSlot(str, result=bool) @pyqtSlot(str, result=bool)
def wallet_name_exists(self, name): 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: if name == wallet_name:
return True return True
return False return False
@ -105,7 +106,7 @@ class QEWalletListModel(QAbstractListModel):
@pyqtSlot(str) @pyqtSlot(str)
def updateWallet(self, path): def updateWallet(self, path):
i = 0 i = 0
for wallet_name, wallet_path in self.wallets: for wallet_name, wallet_path in self._wallets:
if wallet_path == path: if wallet_path == path:
mi = self.createIndex(i, i) mi = self.createIndex(i, i)
self.dataChanged.emit(mi, mi, self._ROLE_KEYS) self.dataChanged.emit(mi, mi, self._ROLE_KEYS)
@ -247,7 +248,6 @@ class QEDaemon(AuthMixin, QObject):
self._current_wallet.password = password if password else None self._current_wallet.password = password if password else None
self.walletLoaded.emit(self._name, self._path) self.walletLoaded.emit(self._name, self._path)
@pyqtSlot(QEWallet) @pyqtSlot(QEWallet)
@pyqtSlot(QEWallet, bool) @pyqtSlot(QEWallet, bool)
@pyqtSlot(QEWallet, bool, bool) @pyqtSlot(QEWallet, bool, bool)

1
electrum/gui/qml/qefx.py

@ -11,6 +11,7 @@ from electrum.simple_config import SimpleConfig
from .qetypes import QEAmount from .qetypes import QEAmount
from .util import QtEventListener, event_listener from .util import QtEventListener, event_listener
class QEFX(QObject, QtEventListener): class QEFX(QObject, QtEventListener):
_logger = get_logger(__name__) _logger = get_logger(__name__)

7
electrum/gui/qml/qeinvoice.py

@ -403,6 +403,7 @@ class QEInvoiceParser(QEInvoice):
self._recipient = '' self._recipient = ''
self._pi = None self._pi = None
self._lnurlData = None
self.clear() self.clear()
@ -476,7 +477,7 @@ class QEInvoiceParser(QEInvoice):
self.setValidOnchainInvoice(invoice) self.setValidOnchainInvoice(invoice)
self.validationSuccess.emit() self.validationSuccess.emit()
else: 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): def validateRecipient(self, recipient):
if not recipient: if not recipient:
@ -485,7 +486,8 @@ class QEInvoiceParser(QEInvoice):
self._pi = PaymentIdentifier(self._wallet.wallet, recipient) self._pi = PaymentIdentifier(self._wallet.wallet, recipient)
if not self._pi.is_valid() or self._pi.type not in [PaymentIdentifierType.SPK, PaymentIdentifierType.BIP21, 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')) self.validationError.emit('unknown', _('Unknown invoice'))
return return
@ -552,6 +554,7 @@ class QEInvoiceParser(QEInvoice):
def resolve_pi(self): def resolve_pi(self):
assert self._pi.need_resolve() assert self._pi.need_resolve()
def on_finished(pi): def on_finished(pi):
if pi.is_error(): if pi.is_error():
pass pass

30
electrum/gui/qml/qeinvoicelistmodel.py

@ -1,7 +1,7 @@
from abc import abstractmethod from abc import abstractmethod
from typing import TYPE_CHECKING, List, Dict, Any 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 PyQt5.QtCore import Qt, QAbstractListModel, QModelIndex
from electrum.logging import get_logger from electrum.logging import get_logger
@ -29,6 +29,7 @@ class QEAbstractInvoiceListModel(QAbstractListModel):
def __init__(self, wallet: 'Abstract_Wallet', parent=None): def __init__(self, wallet: 'Abstract_Wallet', parent=None):
super().__init__(parent) super().__init__(parent)
self.wallet = wallet self.wallet = wallet
self._invoices = []
self._timer = QTimer(self) self._timer = QTimer(self)
self._timer.setSingleShot(True) self._timer.setSingleShot(True)
@ -41,13 +42,13 @@ class QEAbstractInvoiceListModel(QAbstractListModel):
raise e raise e
def rowCount(self, index): def rowCount(self, index):
return len(self.invoices) return len(self._invoices)
def roleNames(self): def roleNames(self):
return self._ROLE_MAP return self._ROLE_MAP
def data(self, index, role): def data(self, index, role):
invoice = self.invoices[index.row()] invoice = self._invoices[index.row()]
role_index = role - Qt.UserRole role_index = role - Qt.UserRole
value = invoice[self._ROLE_NAMES[role_index]] value = invoice[self._ROLE_NAMES[role_index]]
@ -59,7 +60,7 @@ class QEAbstractInvoiceListModel(QAbstractListModel):
def clear(self): def clear(self):
self.beginResetModel() self.beginResetModel()
self.invoices = [] self._invoices = []
self.endResetModel() self.endResetModel()
@pyqtSlot() @pyqtSlot()
@ -71,7 +72,7 @@ class QEAbstractInvoiceListModel(QAbstractListModel):
self.clear() self.clear()
self.beginInsertRows(QModelIndex(), 0, len(invoices) - 1) self.beginInsertRows(QModelIndex(), 0, len(invoices) - 1)
self.invoices = invoices self._invoices = invoices
self.endInsertRows() self.endInsertRows()
self.set_status_timer() self.set_status_timer()
@ -79,7 +80,7 @@ class QEAbstractInvoiceListModel(QAbstractListModel):
def add_invoice(self, invoice: BaseInvoice): def add_invoice(self, invoice: BaseInvoice):
# skip if already in list # skip if already in list
key = invoice.get_id() key = invoice.get_id()
for x in self.invoices: for x in self._invoices:
if x['key'] == key: if x['key'] == key:
return return
@ -87,7 +88,7 @@ class QEAbstractInvoiceListModel(QAbstractListModel):
self._logger.debug(str(item)) self._logger.debug(str(item))
self.beginInsertRows(QModelIndex(), 0, 0) self.beginInsertRows(QModelIndex(), 0, 0)
self.invoices.insert(0, item) self._invoices.insert(0, item)
self.endInsertRows() self.endInsertRows()
self.set_status_timer() self.set_status_timer()
@ -97,24 +98,24 @@ class QEAbstractInvoiceListModel(QAbstractListModel):
self.add_invoice(self.get_invoice_for_key(key)) self.add_invoice(self.get_invoice_for_key(key))
def delete_invoice(self, key: str): def delete_invoice(self, key: str):
for i, invoice in enumerate(self.invoices): for i, invoice in enumerate(self._invoices):
if invoice['key'] == key: if invoice['key'] == key:
self.beginRemoveRows(QModelIndex(), i, i) self.beginRemoveRows(QModelIndex(), i, i)
self.invoices.pop(i) self._invoices.pop(i)
self.endRemoveRows() self.endRemoveRows()
break break
self.set_status_timer() self.set_status_timer()
def get_model_invoice(self, key: str): def get_model_invoice(self, key: str):
for invoice in self.invoices: for invoice in self._invoices:
if invoice['key'] == key: if invoice['key'] == key:
return invoice return invoice
return None return None
@pyqtSlot(str, int) @pyqtSlot(str, int)
def updateInvoice(self, key, status): def updateInvoice(self, key, status):
self._logger.debug('updating invoice for %s to %d' % (key,status)) self._logger.debug(f'updating invoice for {key} to {status}')
for i, item in enumerate(self.invoices): for i, item in enumerate(self._invoices):
if item['key'] == key: if item['key'] == key:
invoice = self.get_invoice_for_key(key) invoice = self.get_invoice_for_key(key)
item['status'] = status item['status'] = status
@ -137,7 +138,7 @@ class QEAbstractInvoiceListModel(QAbstractListModel):
def set_status_timer(self): def set_status_timer(self):
nearest_interval = LN_EXPIRY_NEVER nearest_interval = LN_EXPIRY_NEVER
for invoice in self.invoices: for invoice in self._invoices:
if invoice['status'] != PR_EXPIRED: if invoice['status'] != PR_EXPIRED:
if invoice['expiry'] > 0 and invoice['expiry'] != LN_EXPIRY_NEVER: if invoice['expiry'] > 0 and invoice['expiry'] != LN_EXPIRY_NEVER:
interval = status_update_timer_interval(invoice['timestamp'] + invoice['expiry']) interval = status_update_timer_interval(invoice['timestamp'] + invoice['expiry'])
@ -150,7 +151,7 @@ class QEAbstractInvoiceListModel(QAbstractListModel):
@pyqtSlot() @pyqtSlot()
def updateStatusStrings(self): 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']) invoice = self.get_invoice_for_key(item['key'])
item['status'] = self.wallet.get_invoice_status(invoice) item['status'] = self.wallet.get_invoice_status(invoice)
item['status_str'] = invoice.get_status_str(item['status']) item['status_str'] = invoice.get_status_str(item['status'])
@ -206,6 +207,7 @@ class QEInvoiceListModel(QEAbstractInvoiceListModel, QtEventListener):
def get_invoice_as_dict(self, invoice: Invoice): def get_invoice_as_dict(self, invoice: Invoice):
return self.wallet.export_invoice(invoice) return self.wallet.export_invoice(invoice)
class QERequestListModel(QEAbstractInvoiceListModel, QtEventListener): class QERequestListModel(QEAbstractInvoiceListModel, QtEventListener):
def __init__(self, wallet, parent=None): def __init__(self, wallet, parent=None):
super().__init__(wallet, parent) super().__init__(wallet, parent)

2
electrum/gui/qml/qelnpaymentdetails.py

@ -45,7 +45,7 @@ class QELnPaymentDetails(QObject):
@key.setter @key.setter
def key(self, key: str): def key(self, key: str):
if self._key != key: if self._key != key:
self._logger.debug('key set -> %s' % key) self._logger.debug(f'key set -> {key}')
self._key = key self._key = key
self.keyChanged.emit() self.keyChanged.emit()
self.update() self.update()

8
electrum/gui/qml/qenetwork.py

@ -1,6 +1,6 @@
from typing import TYPE_CHECKING 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.logging import get_logger
from electrum import constants from electrum import constants
@ -178,7 +178,6 @@ class QENetwork(QObject, QtEventListener):
self._logger.debug(f'unknown channels {unknown}') self._logger.debug(f'unknown channels {unknown}')
self._gossipUnknownChannels = unknown self._gossipUnknownChannels = unknown
self.gossipUpdated.emit() self.gossipUpdated.emit()
#self.lightning_gossip_num_queries = unknown
def on_gossip_setting_changed(self): def on_gossip_setting_changed(self):
if not self.network: if not self.network:
@ -205,7 +204,8 @@ class QENetwork(QObject, QtEventListener):
net_params = self.network.get_parameters() net_params = self.network.get_parameters()
try: try:
server = ServerAddr.from_str_with_inference(server) 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: except Exception:
return return
net_params = net_params._replace(server=server, auto_connect=self._qeconfig.autoConnect) net_params = net_params._replace(server=server, auto_connect=self._qeconfig.autoConnect)
@ -215,7 +215,7 @@ class QENetwork(QObject, QtEventListener):
def serverWithStatus(self): def serverWithStatus(self):
server = self._server server = self._server
if not self.network.is_connected(): # connecting or disconnected if not self.network.is_connected(): # connecting or disconnected
return f"{server} (connecting...)" return f'{server} (connecting...)'
return server return server
@pyqtProperty(str, notify=statusChanged) @pyqtProperty(str, notify=statusChanged)

12
electrum/gui/qml/qeqr.py

@ -16,6 +16,7 @@ from electrum.qrreader import get_qr_reader
from electrum.i18n import _ from electrum.i18n import _
from electrum.util import profiler, get_asyncio_loop from electrum.util import profiler, get_asyncio_loop
class QEQRParser(QObject): class QEQRParser(QObject):
_logger = get_logger(__name__) _logger = get_logger(__name__)
@ -28,6 +29,7 @@ class QEQRParser(QObject):
self._busy = False self._busy = False
self._image = None self._image = None
self._data = None
self._text = text self._text = text
self.qrreader = get_qr_reader() 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)) result.append(QPoint(x+self.scan_pos_x, y+self.scan_pos_y))
return result return result
class QEQRImageProvider(QQuickImageProvider): class QEQRImageProvider(QQuickImageProvider):
def __init__(self, max_size, parent=None): def __init__(self, max_size, parent=None):
super().__init__(QQuickImageProvider.Image) super().__init__(QQuickImageProvider.Image)
self._max_size = max_size self._max_size = max_size
self.qimg = None
_logger = get_logger(__name__) _logger = get_logger(__name__)
@ -161,6 +165,7 @@ class QEQRImageProvider(QQuickImageProvider):
self.qimg.fill(QColor('gray')) self.qimg.fill(QColor('gray'))
return self.qimg, self.qimg.size() return self.qimg, self.qimg.size()
# helper for placing icon exactly where it should go on the QR code # 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 # pyqt5 is unwilling to accept slots on QEQRImageProvider, so we need to define
# a separate class (sigh) # a separate class (sigh)
@ -189,4 +194,9 @@ class QEQRImageProviderHelper(QObject):
icon_modules = int(modules / 5) icon_modules = int(modules / 5)
icon_modules += (icon_modules+1) % 2 # force odd icon_modules += (icon_modules+1) % 2 # force odd
return { 'modules': modules, 'box_size': qr.box_size, 'icon_modules': icon_modules, 'valid' : valid } return {
'modules': modules,
'box_size': qr.box_size,
'icon_modules': icon_modules,
'valid': valid
}

3
electrum/gui/qml/qerequestdetails.py

@ -83,7 +83,6 @@ class QERequestDetails(QObject, QtEventListener):
self.keyChanged.emit() self.keyChanged.emit()
self.initRequest() self.initRequest()
statusChanged = pyqtSignal()
@pyqtProperty(int, notify=statusChanged) @pyqtProperty(int, notify=statusChanged)
def status(self): def status(self):
return self._wallet.wallet.get_invoice_status(self._req) return self._wallet.wallet.get_invoice_status(self._req)
@ -133,7 +132,6 @@ class QERequestDetails(QObject, QtEventListener):
def bip21(self): def bip21(self):
return self._req.get_bip21_URI() if self._req else '' return self._req.get_bip21_URI() if self._req else ''
def initRequest(self): def initRequest(self):
if self._wallet is None or self._key is None: if self._wallet is None or self._key is None:
return return
@ -160,7 +158,6 @@ class QERequestDetails(QObject, QtEventListener):
self._timer.setInterval(interval) # msec self._timer.setInterval(interval) # msec
self._timer.start() self._timer.start()
@pyqtSlot() @pyqtSlot()
def updateStatusString(self): def updateStatusString(self):
self.statusChanged.emit() self.statusChanged.emit()

45
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 PyQt5.QtCore import Qt, QAbstractListModel, QModelIndex
from electrum.logging import get_logger 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.interface import ServerAddr, PREFERRED_NETWORK_PROTOCOL
from electrum import blockchain from electrum import blockchain
@ -22,6 +22,7 @@ class QEServerListModel(QAbstractListModel, QtEventListener):
super().__init__(parent) super().__init__(parent)
self._chaintips = 0 self._chaintips = 0
self._servers = []
self.network = network self.network = network
self.initModel() self.initModel()
@ -44,13 +45,13 @@ class QEServerListModel(QAbstractListModel, QtEventListener):
self.initModel() self.initModel()
def rowCount(self, index): def rowCount(self, index):
return len(self.servers) return len(self._servers)
def roleNames(self): def roleNames(self):
return self._ROLE_MAP return self._ROLE_MAP
def data(self, index, role): def data(self, index, role):
server = self.servers[index.row()] server = self._servers[index.row()]
role_index = role - Qt.UserRole role_index = role - Qt.UserRole
value = server[self._ROLE_NAMES[role_index]] value = server[self._ROLE_NAMES[role_index]]
@ -62,7 +63,7 @@ class QEServerListModel(QAbstractListModel, QtEventListener):
def clear(self): def clear(self):
self.beginResetModel() self.beginResetModel()
self.servers = [] self._servers = []
self.endResetModel() self.endResetModel()
chaintipsChanged = pyqtSignal() 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()}') self._logger.debug(f'chain {chain_id} has name={name}, max_forkpoint=@{b.get_max_forkpoint()}, height={b.height()}')
for i in interfaces: for i in interfaces:
server = {} server = {
server['chain'] = name 'chain': name,
server['chain_height'] = b.height() 'chain_height': b.height(),
server['is_primary'] = i == self.network.interface 'is_primary': i == self.network.interface,
server['is_connected'] = True 'is_connected': True,
server['name'] = str(i.server) 'name': str(i.server),
server['address'] = i.server.to_friendly_name() 'address': i.server.to_friendly_name(),
server['height'] = i.tip 'height': i.tip
}
servers.append(server) servers.append(server)
@ -120,17 +122,18 @@ class QEServerListModel(QAbstractListModel, QtEventListener):
port = d.get(protocol) port = d.get(protocol)
if port: if port:
s = ServerAddr(_host, port, protocol=protocol) s = ServerAddr(_host, port, protocol=protocol)
server = {} server = {
server['chain'] = '' 'chain': '',
server['chain_height'] = 0 'chain_height': 0,
server['height'] = 0 'height': 0,
server['is_primary'] = False 'is_primary': False,
server['is_connected'] = False 'is_connected': False,
server['name'] = s.net_addr_str() 'name': s.net_addr_str()
}
server['address'] = server['name'] server['address'] = server['name']
servers.append(server) servers.append(server)
self.beginInsertRows(QModelIndex(), 0, len(servers) - 1) self.beginInsertRows(QModelIndex(), 0, len(servers) - 1)
self.servers = servers self._servers = servers
self.endInsertRows() self.endInsertRows()

15
electrum/gui/qml/qetransactionlistmodel.py

@ -1,7 +1,7 @@
from datetime import datetime, timedelta from datetime import datetime, timedelta
from typing import TYPE_CHECKING, Dict, Any 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 PyQt5.QtCore import Qt, QAbstractListModel, QModelIndex
from electrum.logging import get_logger from electrum.logging import get_logger
@ -34,11 +34,13 @@ class QETransactionListModel(QAbstractListModel, QtEventListener):
self.onchain_domain = onchain_domain self.onchain_domain = onchain_domain
self.include_lightning = include_lightning self.include_lightning = include_lightning
self.tx_history = []
self.register_callbacks() self.register_callbacks()
self.destroyed.connect(lambda: self.on_destroy()) self.destroyed.connect(lambda: self.on_destroy())
self.requestRefresh.connect(lambda: self.initModel()) self.requestRefresh.connect(lambda: self.initModel())
self.setDirty() self._dirty = True
self.initModel() self.initModel()
def on_destroy(self): def on_destroy(self):
@ -159,13 +161,13 @@ class QETransactionListModel(QAbstractListModel, QtEventListener):
txts = datetime.fromtimestamp(timestamp) txts = datetime.fromtimestamp(timestamp)
today = datetime.today().replace(hour=0, minute=0, second=0, microsecond=0) today = datetime.today().replace(hour=0, minute=0, second=0, microsecond=0)
if (txts > today): if txts > today:
return 'today' return 'today'
elif (txts > today - timedelta(days=1)): elif txts > today - timedelta(days=1):
return 'yesterday' return 'yesterday'
elif (txts > today - timedelta(days=7)): elif txts > today - timedelta(days=7):
return 'lastweek' return 'lastweek'
elif (txts > today - timedelta(days=31)): elif txts > today - timedelta(days=31):
return 'lastmonth' return 'lastmonth'
else: else:
return 'older' return 'older'
@ -194,7 +196,6 @@ class QETransactionListModel(QAbstractListModel, QtEventListener):
) )
return tx_mined_info return tx_mined_info
# initial model data
@pyqtSlot() @pyqtSlot()
@pyqtSlot(bool) @pyqtSlot(bool)
def initModel(self, force: bool = False): def initModel(self, force: bool = False):

7
electrum/gui/qml/qetxdetails.py

@ -4,7 +4,7 @@ from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject
from electrum.i18n import _ from electrum.i18n import _
from electrum.logging import get_logger 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.transaction import tx_from_any, Transaction
from electrum.network import Network from electrum.network import Network
@ -12,6 +12,7 @@ from .qewallet import QEWallet
from .qetypes import QEAmount from .qetypes import QEAmount
from .util import QtEventListener, event_listener from .util import QtEventListener, event_listener
class QETxDetails(QObject, QtEventListener): class QETxDetails(QObject, QtEventListener):
_logger = get_logger(__name__) _logger = get_logger(__name__)
@ -68,13 +69,13 @@ class QETxDetails(QObject, QtEventListener):
@event_listener @event_listener
def on_event_verified(self, wallet, txid, info): def on_event_verified(self, wallet, txid, info):
if wallet == self._wallet.wallet and txid == self._txid: 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() self.update()
@event_listener @event_listener
def on_event_new_transaction(self, wallet, tx): def on_event_new_transaction(self, wallet, tx):
if wallet == self._wallet.wallet and tx.txid() == self._txid: 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() self.update()
walletChanged = pyqtSignal() walletChanged = pyqtSignal()

24
electrum/gui/qml/qetxfinalizer.py

@ -291,7 +291,7 @@ class QETxFinalizer(TxFeeSlider):
@profiler @profiler
def make_tx(self, amount): 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: if self.f_make_tx:
tx = self.f_make_tx(amount) tx = self.f_make_tx(amount)
@ -373,10 +373,7 @@ class QETxFinalizer(TxFeeSlider):
self.f_accept(self._tx) self.f_accept(self._tx)
return return
self._wallet.sign(self._tx, self._wallet.sign(self._tx, broadcast=True, on_success=partial(self.on_signed_tx, False))
broadcast=True,
on_success=partial(self.on_signed_tx, False)
)
@pyqtSlot() @pyqtSlot()
def sign(self): def sign(self):
@ -384,10 +381,7 @@ class QETxFinalizer(TxFeeSlider):
self._logger.error('no valid tx') self._logger.error('no valid tx')
return return
self._wallet.sign(self._tx, self._wallet.sign(self._tx, broadcast=False, on_success=partial(self.on_signed_tx, True))
broadcast=False,
on_success=partial(self.on_signed_tx, True)
)
def on_signed_tx(self, save: bool, tx: Transaction): def on_signed_tx(self, save: bool, tx: Transaction):
self._logger.debug('on_signed_tx') self._logger.debug('on_signed_tx')
@ -405,12 +399,13 @@ class QETxFinalizer(TxFeeSlider):
return [str(self._tx), txqr[0], txqr[1]] 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): 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() txMined = pyqtSignal()
def __init__(self, parent=None): def __init__(self, parent=None):
@ -505,7 +500,6 @@ class QETxRbfFeeBumper(TxFeeSlider, TxMonMixin):
self.bumpMethodChanged.emit() self.bumpMethodChanged.emit()
self.update() self.update()
def get_tx(self): def get_tx(self):
assert self._txid assert self._txid
self._orig_tx = self._wallet.wallet.db.get_transaction(self._txid) self._orig_tx = self._wallet.wallet.db.get_transaction(self._txid)

1
electrum/gui/qml/qetypes.py

@ -11,6 +11,7 @@ from electrum.i18n import _
# should also capture millisats amounts and MAX/'!' indicators # should also capture millisats amounts and MAX/'!' indicators
# and (unformatted) string representations # and (unformatted) string representations
class QEAmount(QObject): class QEAmount(QObject):
_logger = get_logger(__name__) _logger = get_logger(__name__)

33
electrum/gui/qml/qewallet.py

@ -2,18 +2,17 @@ import asyncio
import queue import queue
import threading import threading
import time import time
from typing import TYPE_CHECKING, Optional, Tuple, Callable from typing import TYPE_CHECKING, Callable
from functools import partial 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.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.logging import get_logger
from electrum.network import TxBroadcastError, BestEffortRequestFailed from electrum.network import TxBroadcastError, BestEffortRequestFailed
from electrum.transaction import PartialTxOutput, PartialTransaction, Transaction from electrum.transaction import PartialTransaction, Transaction
from electrum.util import parse_max_spend, InvalidPassword, event_listener, AddTransactionException, get_asyncio_loop from electrum.util import InvalidPassword, event_listener, AddTransactionException, get_asyncio_loop
from electrum.plugin import run_hook from electrum.plugin import run_hook
from electrum.wallet import Multisig_Wallet from electrum.wallet import Multisig_Wallet
from electrum.crypto import pw_decode_with_version_and_mac from electrum.crypto import pw_decode_with_version_and_mac
@ -30,6 +29,7 @@ if TYPE_CHECKING:
from electrum.wallet import Abstract_Wallet from electrum.wallet import Abstract_Wallet
from .qeinvoice import QEInvoice from .qeinvoice import QEInvoice
class QEWallet(AuthMixin, QObject, QtEventListener): class QEWallet(AuthMixin, QObject, QtEventListener):
__instances = [] __instances = []
@ -53,12 +53,14 @@ class QEWallet(AuthMixin, QObject, QtEventListener):
# shared signal for many static wallet properties # shared signal for many static wallet properties
dataChanged = pyqtSignal() dataChanged = pyqtSignal()
balanceChanged = pyqtSignal()
requestStatusChanged = pyqtSignal([str,int], arguments=['key','status']) requestStatusChanged = pyqtSignal([str,int], arguments=['key','status'])
requestCreateSuccess = pyqtSignal([str], arguments=['key']) requestCreateSuccess = pyqtSignal([str], arguments=['key'])
requestCreateError = pyqtSignal([str], arguments=['error']) requestCreateError = pyqtSignal([str], arguments=['error'])
invoiceStatusChanged = pyqtSignal([str,int], arguments=['key','status']) invoiceStatusChanged = pyqtSignal([str,int], arguments=['key','status'])
invoiceCreateSuccess = pyqtSignal() invoiceCreateSuccess = pyqtSignal()
invoiceCreateError = pyqtSignal([str,str], arguments=['code','error']) invoiceCreateError = pyqtSignal([str,str], arguments=['code','error'])
paymentAuthRejected = pyqtSignal()
paymentSucceeded = pyqtSignal([str], arguments=['key']) paymentSucceeded = pyqtSignal([str], arguments=['key'])
paymentFailed = pyqtSignal([str,str], arguments=['key','reason']) paymentFailed = pyqtSignal([str,str], arguments=['key','reason'])
requestNewPassword = pyqtSignal() requestNewPassword = pyqtSignal()
@ -196,7 +198,7 @@ class QEWallet(AuthMixin, QObject, QtEventListener):
if wallet == self.wallet: if wallet == self.wallet:
self._logger.info(f'removed transaction {tx.txid()}') self._logger.info(f'removed transaction {tx.txid()}')
self.addressModel.setDirty() self.addressModel.setDirty()
self.historyModel.initModel(True) #setDirty() self.historyModel.initModel(True) # setDirty()?
self.balanceChanged.emit() self.balanceChanged.emit()
@qt_event_listener @qt_event_listener
@ -431,8 +433,6 @@ class QEWallet(AuthMixin, QObject, QtEventListener):
return self.wallet.m == 1 return self.wallet.m == 1
return True return True
balanceChanged = pyqtSignal()
@pyqtProperty(QEAmount, notify=balanceChanged) @pyqtProperty(QEAmount, notify=balanceChanged)
def frozenBalance(self): def frozenBalance(self):
c, u, x = self.wallet.get_frozen_balance() c, u, x = self.wallet.get_frozen_balance()
@ -499,9 +499,11 @@ class QEWallet(AuthMixin, QObject, QtEventListener):
success = self.do_sign(tx, broadcast) success = self.do_sign(tx, broadcast)
if success: if success:
if on_success: on_success(tx) if on_success:
on_success(tx)
else: else:
if on_failure: on_failure() if on_failure:
on_failure()
def do_sign(self, tx, broadcast): def do_sign(self, tx, broadcast):
try: 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 # 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): def on_sign_complete(self, broadcast, cb: Callable[[Transaction], None] = None, tx: Transaction = None):
self.otpSuccess.emit() self.otpSuccess.emit()
if cb: cb(tx) if cb:
cb(tx)
if broadcast: if broadcast:
self.broadcast(tx) self.broadcast(tx)
# this assumes a 2fa wallet, but there are no other tc_sign_wrapper hooks, so that's ok # 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): def on_sign_failed(self, cb: Callable[[], None] = None, error: str = None):
self.otpFailed.emit('error', error) self.otpFailed.emit('error', error)
if cb: cb() if cb:
cb()
def request_otp(self, on_submit): def request_otp(self, on_submit):
self._otp_on_submit = on_submit self._otp_on_submit = on_submit
@ -595,7 +599,6 @@ class QEWallet(AuthMixin, QObject, QtEventListener):
self.saveTxError.emit(tx.txid(), 'error', str(e)) self.saveTxError.emit(tx.txid(), 'error', str(e))
return False return False
paymentAuthRejected = pyqtSignal()
def ln_auth_rejected(self): def ln_auth_rejected(self):
self.paymentAuthRejected.emit() self.paymentAuthRejected.emit()
@ -724,7 +727,7 @@ class QEWallet(AuthMixin, QObject, QtEventListener):
xpub = self.wallet.get_fingerprint() xpub = self.wallet.get_fingerprint()
decrypted = pw_decode_with_version_and_mac(encrypted, xpub) decrypted = pw_decode_with_version_and_mac(encrypted, xpub)
return True return True
except Exception as e: except Exception:
return False return False
@pyqtSlot() @pyqtSlot()

1
electrum/gui/qml/qewalletdb.py

@ -142,7 +142,6 @@ class QEWalletDB(QObject):
self.invalidPassword.emit() self.invalidPassword.emit()
else: # storage not encrypted; but it might still have a keystore pw 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. # 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: try:
db = WalletDB(self._storage.read(), storage=self._storage, upgrade=True) db = WalletDB(self._storage.read(), storage=self._storage, upgrade=True)
except WalletRequiresSplit as e: except WalletRequiresSplit as e:

8
electrum/gui/qml/qewizard.py

@ -13,6 +13,10 @@ if TYPE_CHECKING:
class QEAbstractWizard(QObject): 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__) _logger = get_logger(__name__)
def __init__(self, parent=None): def __init__(self, parent=None):
@ -45,7 +49,6 @@ class QEAbstractWizard(QObject):
class QENewWalletWizard(NewWalletWizard, QEAbstractWizard): class QENewWalletWizard(NewWalletWizard, QEAbstractWizard):
createError = pyqtSignal([str], arguments=["error"]) createError = pyqtSignal([str], arguments=["error"])
createSuccess = pyqtSignal() createSuccess = pyqtSignal()
@ -53,6 +56,8 @@ class QENewWalletWizard(NewWalletWizard, QEAbstractWizard):
NewWalletWizard.__init__(self, daemon.daemon, plugins) NewWalletWizard.__init__(self, daemon.daemon, plugins)
QEAbstractWizard.__init__(self, parent) QEAbstractWizard.__init__(self, parent)
self._qedaemon = daemon self._qedaemon = daemon
self._path = None
self._password = None
# attach view names and accept handlers # attach view names and accept handlers
self.navmap_merge({ self.navmap_merge({
@ -127,7 +132,6 @@ class QENewWalletWizard(NewWalletWizard, QEAbstractWizard):
class QEServerConnectWizard(ServerConnectWizard, QEAbstractWizard): class QEServerConnectWizard(ServerConnectWizard, QEAbstractWizard):
def __init__(self, daemon: 'QEDaemon', parent=None): def __init__(self, daemon: 'QEDaemon', parent=None):
ServerConnectWizard.__init__(self, daemon.daemon) ServerConnectWizard.__init__(self, daemon.daemon)
QEAbstractWizard.__init__(self, parent) QEAbstractWizard.__init__(self, parent)

6
electrum/gui/qml/util.py

@ -31,6 +31,7 @@ class QtEventListener(EventListener):
# decorator for members of the QtEventListener class # decorator for members of the QtEventListener class
def qt_event_listener(func): def qt_event_listener(func):
func = event_listener(func) func = event_listener(func)
@wraps(func) @wraps(func)
def decorator(self, *args): def decorator(self, *args):
self.qt_callback_signal.emit( (func,) + args) self.qt_callback_signal.emit( (func,) + args)
@ -56,10 +57,11 @@ def status_update_timer_interval(exp):
return interval return interval
# TODO: copied from desktop client, this could be moved to a set of common code. # TODO: copied from desktop client, this could be moved to a set of common code.
class TaskThread(QThread, Logger): class TaskThread(QThread, Logger):
'''Thread that runs background tasks. Callbacks are guaranteed """Thread that runs background tasks. Callbacks are guaranteed
to happen in the context of its parent.''' to happen in the context of its parent."""
class Task(NamedTuple): class Task(NamedTuple):
task: Callable task: Callable

4
electrum/gui/qt/wizard/wizard.py

@ -19,6 +19,10 @@ if TYPE_CHECKING:
class QEAbstractWizard(QDialog, MessageBoxMixin): 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__) _logger = get_logger(__name__)
requestNext = pyqtSignal() requestNext = pyqtSignal()

Loading…
Cancel
Save