diff --git a/electrum/gui/qml/components/BalanceSummary.qml b/electrum/gui/qml/components/BalanceSummary.qml index 24c664300..ec8e8c320 100644 --- a/electrum/gui/qml/components/BalanceSummary.qml +++ b/electrum/gui/qml/components/BalanceSummary.qml @@ -45,6 +45,8 @@ Frame { } } + // instead of all these explicit connections, we should expose + // formatted balances directly as a property Connections { target: Config function onBaseUnitChanged() { setBalances() } @@ -56,5 +58,12 @@ Frame { function onWalletLoaded() { setBalances() } } + Connections { + target: Daemon.currentWallet + function onBalanceChanged() { + setBalances() + } + } + Component.onCompleted: setBalances() } diff --git a/electrum/gui/qml/components/History.qml b/electrum/gui/qml/components/History.qml index 8239a0275..e51c3a5c5 100644 --- a/electrum/gui/qml/components/History.qml +++ b/electrum/gui/qml/components/History.qml @@ -133,4 +133,10 @@ Pane { } + Connections { + target: Network + function onHeightChanged(height) { + Daemon.currentWallet.historyModel.updateBlockchainHeight(height) + } + } } diff --git a/electrum/gui/qml/components/Receive.qml b/electrum/gui/qml/components/Receive.qml index b405cebd9..92a663732 100644 --- a/electrum/gui/qml/components/Receive.qml +++ b/electrum/gui/qml/components/Receive.qml @@ -347,4 +347,11 @@ Pane { } } + Connections { + target: Daemon.currentWallet + function onRequestStatusChanged(key, status) { + Daemon.currentWallet.requestModel.updateRequest(key, status) + } + } + } diff --git a/electrum/gui/qml/components/RequestDialog.qml b/electrum/gui/qml/components/RequestDialog.qml index 8a4bbc1a0..cb9bdf844 100644 --- a/electrum/gui/qml/components/RequestDialog.qml +++ b/electrum/gui/qml/components/RequestDialog.qml @@ -13,11 +13,10 @@ Dialog { parent: Overlay.overlay modal: true + standardButtons: Dialog.Ok - width: parent.width - constants.paddingXLarge - height: parent.height - constants.paddingXLarge - x: (parent.width - width) / 2 - y: (parent.height - height) / 2 + width: parent.width + height: parent.height Overlay.modal: Rectangle { color: "#aa000000" @@ -42,6 +41,7 @@ Dialog { icon.width: 32 icon.height: 32 onClicked: dialog.close() + visible: false } } GridLayout { @@ -122,13 +122,19 @@ Dialog { text: qsTr('Address') } Label { - Layout.columnSpan: 2 Layout.fillWidth: true font.family: FixedFont font.pixelSize: constants.fontSizeLarge wrapMode: Text.WrapAnywhere text: modelItem.address } + ToolButton { + icon.source: '../../icons/copy.png' + icon.color: 'transparent' + onClicked: { + AppController.textToClipboard(modelItem.address) + } + } Label { text: qsTr('Status') @@ -162,4 +168,12 @@ Dialog { } } + Connections { + target: Daemon.currentWallet + function onRequestStatusChanged(key, code) { + if (key != modelItem.key) + return + modelItem = Daemon.currentWallet.get_request(key) + } + } } diff --git a/electrum/gui/qml/qerequestlistmodel.py b/electrum/gui/qml/qerequestlistmodel.py index f0cb65fb6..430f8a132 100644 --- a/electrum/gui/qml/qerequestlistmodel.py +++ b/electrum/gui/qml/qerequestlistmodel.py @@ -17,6 +17,7 @@ class QERequestListModel(QAbstractListModel): _ROLE_NAMES=('key','type','timestamp','message','amount','status','address') _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])) + _ROLE_RMAP = dict(zip(_ROLE_NAMES, _ROLE_KEYS)) def rowCount(self, index): return len(self.requests) @@ -87,3 +88,15 @@ class QERequestListModel(QAbstractListModel): self.endRemoveRows() break i = i + 1 + + @pyqtSlot(str, int) + def updateRequest(self, key, status): + self._logger.debug('updating request for %s to %d' % (key,status)) + i = 0 + for item in self.requests: + if item['key'] == key: + req = self.wallet.get_request(key) + item['status'] = req.get_status_str(status) + index = self.index(i,0) + self.dataChanged.emit(index, index, [self._ROLE_RMAP['status']]) + i = i + 1 diff --git a/electrum/gui/qml/qetransactionlistmodel.py b/electrum/gui/qml/qetransactionlistmodel.py index 88a44627d..224824955 100644 --- a/electrum/gui/qml/qetransactionlistmodel.py +++ b/electrum/gui/qml/qetransactionlistmodel.py @@ -1,8 +1,10 @@ +from datetime import datetime + from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject from PyQt5.QtCore import Qt, QAbstractListModel, QModelIndex from electrum.logging import get_logger -from electrum.util import Satoshis +from electrum.util import Satoshis, TxMinedInfo class QETransactionListModel(QAbstractListModel): def __init__(self, wallet, parent=None): @@ -18,6 +20,7 @@ class QETransactionListModel(QAbstractListModel): 'inputs','outputs') _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])) + _ROLE_RMAP = dict(zip(_ROLE_NAMES, _ROLE_KEYS)) def rowCount(self, index): return len(self.tx_history) @@ -55,3 +58,28 @@ class QETransactionListModel(QAbstractListModel): self.tx_history.reverse() self.endInsertRows() + def update_tx(self, txid, info): + i = 0 + for tx in self.tx_history: + if tx['txid'] == txid: + tx['height'] = info.height + tx['confirmations'] = info.conf + tx['timestamp'] = info.timestamp + tx['date'] = datetime.fromtimestamp(info.timestamp) + index = self.index(i,0) + roles = [self._ROLE_RMAP[x] for x in ['height','confirmations','timestamp','date']] + self.dataChanged.emit(index, index, roles) + return + i = i + 1 + + @pyqtSlot(int) + def updateBlockchainHeight(self, height): + self._logger.debug('updating height to %d' % height) + i = 0 + for tx in self.tx_history: + if tx['height'] > 0: + tx['confirmations'] = height - tx['height'] + 1 + index = self.index(i,0) + roles = [self._ROLE_RMAP['confirmations']] + self.dataChanged.emit(index, index, roles) + i = i + 1 diff --git a/electrum/gui/qml/qewallet.py b/electrum/gui/qml/qewallet.py index 1ec8315dc..1b25d2802 100644 --- a/electrum/gui/qml/qewallet.py +++ b/electrum/gui/qml/qewallet.py @@ -28,7 +28,7 @@ class QEWallet(QObject): dataChanged = pyqtSignal() isUptodateChanged = pyqtSignal() - requestStatus = pyqtSignal() + requestStatusChanged = pyqtSignal([str,int], arguments=['key','status']) requestCreateSuccess = pyqtSignal() requestCreateError = pyqtSignal([str,str], arguments=['code','error']) @@ -37,6 +37,7 @@ class QEWallet(QObject): def __init__(self, wallet, parent=None): super().__init__(parent) self.wallet = wallet + self._historyModel = QETransactionListModel(wallet) self._addressModel = QEAddressListModel(wallet) self._requestModel = QERequestListModel(wallet) @@ -53,10 +54,9 @@ class QEWallet(QObject): self.notification_timer.timeout.connect(self.notify_transactions) self._network_signal.connect(self.on_network_qt) - interests = ['wallet_updated', 'network_updated', 'blockchain_updated', - 'new_transaction', 'status', 'verified', 'on_history', - 'channel', 'channels_updated', 'payment_failed', - 'payment_succeeded', 'invoice_status', 'request_status'] + interests = ['wallet_updated', 'new_transaction', 'status', 'verified', + 'on_history', 'channel', 'channels_updated', 'payment_failed', + 'payment_succeeded', 'invoice_status', 'request_status'] # To avoid leaking references to "self" that prevent the # window from being GC-ed when closed, callbacks should be # methods of this class only, and specifically not be @@ -77,13 +77,24 @@ class QEWallet(QObject): if event == 'status': self.isUptodateChanged.emit() elif event == 'request_status': - self._logger.info(str(args)) - self.requestStatus.emit() + wallet, addr, c = args + if wallet == self.wallet: + self._logger.debug('request status %d for address %s' % (c, addr)) + self.requestStatusChanged.emit(addr, c) elif event == 'new_transaction': wallet, tx = args if wallet == self.wallet: self.add_tx_notification(tx) - self._historyModel.init_model() + self._historyModel.init_model() # TODO: be less dramatic + elif event == 'verified': + wallet, txid, info = args + if wallet == self.wallet: + self._historyModel.update_tx(txid, info) + elif event == 'wallet_updated': + wallet, = args + if wallet == self.wallet: + self._logger.debug('wallet %s updated' % str(wallet)) + self.balanceChanged.emit() else: self._logger.debug('unhandled event: %s %s' % (event, str(args))) @@ -115,8 +126,7 @@ class QEWallet(QObject): except queue.Empty: break - from .qeapp import ElectrumQmlApplication - config = ElectrumQmlApplication._config + config = self.wallet.config # Combine the transactions if there are at least three if len(txns) >= 3: total_amount = 0 @@ -222,10 +232,7 @@ class QEWallet(QObject): self._logger.info('no change output??? : %s' % str(tx.to_json()['outputs'])) return - from .qeapp import ElectrumQmlApplication - self.config = ElectrumQmlApplication._config - - use_rbf = bool(self.config.get('use_rbf', True)) + use_rbf = bool(self.wallet.config.get('use_rbf', True)) tx.set_rbf(use_rbf) def cb(result): @@ -235,7 +242,7 @@ class QEWallet(QObject): self._logger.info('tx not complete') return - self.network = ElectrumQmlApplication._daemon.network + self.network = self.wallet.network # TODO not always defined? try: self._logger.info('running broadcast in thread') @@ -318,3 +325,8 @@ class QEWallet(QObject): def delete_request(self, key: str): self.wallet.delete_request(key) self._requestModel.delete_request(key) + + @pyqtSlot('QString', result='QVariant') + def get_request(self, key: str): + req = self.wallet.get_request(key) + return self._requestModel.request_to_model(req)