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