From 6e482f437a6b15e865deab60ad033a1a4a8907f2 Mon Sep 17 00:00:00 2001 From: Sander van Grieken Date: Wed, 7 Apr 2021 16:48:40 +0200 Subject: [PATCH] qml: fixes and cleanup for qedaemon, qenetwork. expose many wallet properties in qewallet --- electrum/gui/qml/qedaemon.py | 25 ++++------ electrum/gui/qml/qenetwork.py | 39 +++++++++------ electrum/gui/qml/qewallet.py | 93 ++++++++++++++++++++++++++++++++--- 3 files changed, 120 insertions(+), 37 deletions(-) diff --git a/electrum/gui/qml/qedaemon.py b/electrum/gui/qml/qedaemon.py index 58f4ce928..24a168202 100644 --- a/electrum/gui/qml/qedaemon.py +++ b/electrum/gui/qml/qedaemon.py @@ -28,15 +28,6 @@ class QEWalletListModel(QAbstractListModel): def roleNames(self): return self._ROLE_MAP - def data(self, index, role): - role_index = role - (Qt.UserRole + 1) - value = tx[self._ROLE_NAMES[role_index]] - if isinstance(value, bool) or isinstance(value, list) or isinstance(value, int) or value is None: - return value - if isinstance(value, Satoshis): - return value.value - return str(value) - def data(self, index, role): (wallet_name, wallet_path, wallet) = self.wallets[index.row()] role_index = role - (Qt.UserRole + 1) @@ -44,7 +35,7 @@ class QEWalletListModel(QAbstractListModel): if role_name == 'name': return wallet_name if role_name == 'path': - return wallet_name + return wallet_path if role_name == 'active': return wallet != None @@ -72,8 +63,13 @@ class QEDaemon(QObject): walletLoaded = pyqtSignal() walletRequiresPassword = pyqtSignal() + _current_wallet = None + @pyqtSlot() + @pyqtSlot(str) + @pyqtSlot(str, str) def load_wallet(self, path=None, password=None): + self._logger.debug('load wallet ' + str(path)) if path == None: path = self.daemon.config.get('gui_last_wallet') wallet = self.daemon.load_wallet(path, password) @@ -91,7 +87,9 @@ class QEDaemon(QObject): @pyqtProperty('QString',notify=walletLoaded) def walletName(self): - return self._current_wallet.wallet.basename() + if self._current_wallet != None: + return self._current_wallet.wallet.basename() + return '' @pyqtProperty(QEWalletListModel) def activeWallets(self): @@ -108,8 +106,5 @@ class QEDaemon(QObject): available.append(i.path) for path in sorted(available): wallet = self.daemon.get_wallet(path) - if wallet != None: - availableListModel.add_wallet(wallet_path = wallet.storage.path, wallet = wallet) - else: - availableListModel.add_wallet(wallet_path = path) + availableListModel.add_wallet(wallet_path = path, wallet = wallet) return availableListModel diff --git a/electrum/gui/qml/qenetwork.py b/electrum/gui/qml/qenetwork.py index 38a3b6b03..a857fdf1a 100644 --- a/electrum/gui/qml/qenetwork.py +++ b/electrum/gui/qml/qenetwork.py @@ -2,6 +2,7 @@ from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject from electrum.util import register_callback from electrum.logging import get_logger +from electrum import constants class QENetwork(QObject): def __init__(self, network, parent=None): @@ -15,11 +16,13 @@ class QENetwork(QObject): _logger = get_logger(__name__) - network_updated = pyqtSignal() - blockchain_updated = pyqtSignal() - default_server_changed = pyqtSignal() - proxy_set = pyqtSignal() - status_updated = pyqtSignal() + networkUpdated = pyqtSignal() + blockchainUpdated = pyqtSignal() + defaultServerChanged = pyqtSignal() + proxySet = pyqtSignal() + statusUpdated = pyqtSignal() + + dataChanged = pyqtSignal() # dummy to silence warnings _num_updates = 0 _server = "" @@ -28,40 +31,48 @@ class QENetwork(QObject): def on_network_updated(self, event, *args): self._num_updates = self._num_updates + 1 - self.network_updated.emit() + self.networkUpdated.emit() def on_blockchain_updated(self, event, *args): self._logger.info('chainupdate: ' + str(event) + str(args)) self._height = self.network.get_local_height() - self.blockchain_updated.emit() + self.blockchainUpdated.emit() def on_default_server_changed(self, event, *args): netparams = self.network.get_parameters() self._server = str(netparams.server) - self.default_server_changed.emit() + self.defaultServerChanged.emit() def on_proxy_set(self, event, *args): self._logger.info('proxy set') - self.proxy_set.emit() + self.proxySet.emit() def on_status(self, event, *args): self._logger.info('status updated') self._status = self.network.connection_status - self.status_updated.emit() + self.statusUpdated.emit() - @pyqtProperty(int,notify=network_updated) + @pyqtProperty(int,notify=networkUpdated) def updates(self): return self._num_updates - @pyqtProperty(int,notify=blockchain_updated) + @pyqtProperty(int,notify=blockchainUpdated) def height(self): return self._height - @pyqtProperty('QString',notify=default_server_changed) + @pyqtProperty('QString',notify=defaultServerChanged) def server(self): return self._server - @pyqtProperty('QString',notify=status_updated) + @pyqtProperty('QString',notify=statusUpdated) def status(self): return self._status + @pyqtProperty(bool, notify=dataChanged) + def isTestNet(self): + return constants.net.TESTNET + + @pyqtProperty('QString', notify=dataChanged) + def networkName(self): + return constants.net.__name__.replace('Bitcoin','') + diff --git a/electrum/gui/qml/qewallet.py b/electrum/gui/qml/qewallet.py index 1aabfcc40..1ded4dc7b 100644 --- a/electrum/gui/qml/qewallet.py +++ b/electrum/gui/qml/qewallet.py @@ -4,6 +4,9 @@ from PyQt5.QtCore import Qt, QAbstractListModel, QModelIndex, QByteArray from electrum.util import register_callback, Satoshis from electrum.logging import get_logger from electrum.wallet import Wallet, Abstract_Wallet +from electrum import bitcoin +from electrum.transaction import Transaction, tx_from_any, PartialTransaction, PartialTxOutput +from electrum.invoices import PR_PAID, PR_UNPAID, PR_UNKNOWN, PR_EXPIRED, PR_UNCONFIRMED, PR_TYPE_ONCHAIN, PR_TYPE_LN class QETransactionsListModel(QAbstractListModel): def __init__(self, parent=None): @@ -13,11 +16,10 @@ class QETransactionsListModel(QAbstractListModel): _logger = get_logger(__name__) # define listmodel rolemap - ROLES=('txid','fee_sat','height','confirmations','timestamp','monotonic_timestamp','incoming','bc_value', + _ROLE_NAMES=('txid','fee_sat','height','confirmations','timestamp','monotonic_timestamp','incoming','bc_value', 'bc_balance','date','label','txpos_in_block','fee','inputs','outputs') - keys = range(Qt.UserRole + 1, Qt.UserRole + 1 + len(ROLES)) - ROLENAMES = [bytearray(x.encode()) for x in ROLES] - _ROLE_MAP = dict(zip(keys, ROLENAMES)) + _ROLE_KEYS = range(Qt.UserRole + 1, Qt.UserRole + 1 + len(_ROLE_NAMES)) + _ROLE_MAP = dict(zip(_ROLE_KEYS, [bytearray(x.encode()) for x in _ROLE_NAMES])) def rowCount(self, index): return len(self.tx_history) @@ -28,14 +30,21 @@ class QETransactionsListModel(QAbstractListModel): def data(self, index, role): tx = self.tx_history[index.row()] role_index = role - (Qt.UserRole + 1) - value = tx[self.ROLES[role_index]] + value = tx[self._ROLE_NAMES[role_index]] if isinstance(value, bool) or isinstance(value, list) or isinstance(value, int) or value is None: return value if isinstance(value, Satoshis): return value.value return str(value) + def clear(self): + self.beginResetModel() + self.tx_history = [] + self.endResetModel() + + # initial model data def set_history(self, history): + self.clear() self.beginInsertRows(QModelIndex(), 0, len(history) - 1) self.tx_history = history self.tx_history.reverse() @@ -45,23 +54,91 @@ class QEWallet(QObject): def __init__(self, wallet, parent=None): super().__init__(parent) self.wallet = wallet + self._historyModel = QETransactionsListModel() self.get_history() + register_callback(self.on_request_status, ['request_status']) _logger = get_logger(__name__) - _historyModel = QETransactionsListModel() + dataChanged = pyqtSignal() # dummy to silence warnings - @pyqtProperty(QETransactionsListModel) + requestStatus = pyqtSignal() + def on_request_status(self, event, *args): + self._logger.debug(str(event)) +# (wallet, addr, status) = args +# self._historyModel.add_tx() + self.requestStatus.emit() + + historyModelChanged = pyqtSignal() + @pyqtProperty(QETransactionsListModel, notify=historyModelChanged) def historyModel(self): return self._historyModel + @pyqtProperty('QString', notify=dataChanged) + def txinType(self): + return self.wallet.get_txin_type(self.wallet.dummy_address()) + + @pyqtProperty(bool, notify=dataChanged) + def isWatchOnly(self): + return self.wallet.is_watching_only() + + @pyqtProperty(bool, notify=dataChanged) + def isDeterministic(self): + return self.wallet.is_deterministic() + + @pyqtProperty(bool, notify=dataChanged) + def isEncrypted(self): + return self.wallet.storage.is_encrypted() + + @pyqtProperty(bool, notify=dataChanged) + def isHardware(self): + return self.wallet.storage.is_encrypted_with_hw_device() + + @pyqtProperty('QString', notify=dataChanged) + def derivationPath(self): + return self.wallet.get_address_path_str(self.wallet.dummy_address()) + + balanceChanged = pyqtSignal() + + @pyqtProperty(int, notify=balanceChanged) + def frozenBalance(self): + return self.wallet.get_frozen_balance() + + @pyqtProperty(int, notify=balanceChanged) + def unconfirmedBalance(self): + return self.wallet.get_balance()[1] + + @pyqtProperty(int, notify=balanceChanged) + def confirmedBalance(self): + c, u, x = self.wallet.get_balance() + self._logger.info('balance: ' + str(c) + ' ' + str(u) + ' ' + str(x) + ' ') + + return c+x + + # lightning feature? + isUptodateChanged = pyqtSignal() + @pyqtProperty(bool, notify=isUptodateChanged) + def isUptodate(self): + return self.wallet.is_up_to_date() + def get_history(self): history = self.wallet.get_detailed_history(show_addresses = True) txs = history['transactions'] - self._logger.info(txs) # use primitives for tx in txs: for output in tx['outputs']: output['value'] = output['value'].value self._historyModel.set_history(txs) + self.historyModelChanged.emit() + + @pyqtSlot('QString', int, int, bool) + def send_onchain(self, address, amount, fee=None, rbf=False): + self._logger.info('send_onchain: ' + address + ' ' + str(amount)) + coins = self.wallet.get_spendable_coins(None) + if not bitcoin.is_address(address): + self._logger.warning('Invalid Bitcoin Address: ' + address) + return False + outputs = [PartialTxOutput.from_address_and_value(address, amount)] + tx = self.wallet.make_unsigned_transaction(coins=coins,outputs=outputs) + return True