qml: also show coins in Addresses page, and add a few filter options. Additionally, long press
now activates multi-select mode, and add action to (un)freeze selection.
This commit is contained in:
@@ -2,6 +2,7 @@ import QtQuick 2.6
|
||||
import QtQuick.Layouts 1.0
|
||||
import QtQuick.Controls 2.0
|
||||
import QtQuick.Controls.Material 2.0
|
||||
import QtQml.Models 2.2
|
||||
|
||||
import org.electrum 1.0
|
||||
|
||||
@@ -14,34 +15,246 @@ Pane {
|
||||
padding: 0
|
||||
|
||||
ColumnLayout {
|
||||
id: layout
|
||||
anchors.fill: parent
|
||||
spacing: 0
|
||||
|
||||
ElListView {
|
||||
id: listview
|
||||
|
||||
ColumnLayout {
|
||||
id: layout
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
|
||||
clip: true
|
||||
model: Daemon.currentWallet.addressModel
|
||||
currentIndex: -1
|
||||
Pane {
|
||||
id: filtersPane
|
||||
Layout.fillWidth: true
|
||||
GridLayout {
|
||||
columns: 3
|
||||
width: parent.width
|
||||
|
||||
section.property: 'type'
|
||||
section.criteria: ViewSection.FullString
|
||||
section.delegate: sectionDelegate
|
||||
CheckBox {
|
||||
id: showUsed
|
||||
text: qsTr('Show Used')
|
||||
enabled: listview.filterModel.showAddressesCoins != 2
|
||||
onCheckedChanged: listview.filterModel.showUsed = checked
|
||||
Component.onCompleted: checked = listview.filterModel.showUsed
|
||||
}
|
||||
|
||||
delegate: AddressDelegate {
|
||||
onClicked: {
|
||||
var page = app.stack.push(Qt.resolvedUrl('AddressDetails.qml'), {'address': model.address})
|
||||
page.addressDetailsChanged.connect(function() {
|
||||
// update listmodel when details change
|
||||
listview.model.updateAddress(model.address)
|
||||
})
|
||||
RowLayout {
|
||||
Layout.columnSpan: 2
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignRight
|
||||
Label {
|
||||
text: qsTr('Show')
|
||||
}
|
||||
ElComboBox {
|
||||
id: showCoinsAddresses
|
||||
textRole: 'text'
|
||||
valueRole: 'value'
|
||||
model: ListModel {
|
||||
id: showCoinsAddressesModel
|
||||
Component.onCompleted: {
|
||||
// we need to fill the model like this, as ListElement can't evaluate script
|
||||
showCoinsAddressesModel.append({'text': qsTr('Addresses'), 'value': 1})
|
||||
showCoinsAddressesModel.append({'text': qsTr('Coins'), 'value': 2})
|
||||
showCoinsAddressesModel.append({'text': qsTr('Both'), 'value': 3})
|
||||
showCoinsAddresses.currentIndex = 0
|
||||
for (let i=0; i < showCoinsAddressesModel.count; i++) {
|
||||
if (showCoinsAddressesModel.get(i).value == listview.filterModel.showAddressesCoins) {
|
||||
showCoinsAddresses.currentIndex = i
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
onCurrentValueChanged: {
|
||||
if (activeFocus && currentValue) {
|
||||
listview.filterModel.showAddressesCoins = currentValue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
RowLayout {
|
||||
Layout.columnSpan: 3
|
||||
Layout.fillWidth: true
|
||||
TextField {
|
||||
id: searchEdit
|
||||
Layout.fillWidth: true
|
||||
placeholderText: qsTr('text search')
|
||||
onTextChanged: listview.filterModel.filterText = text
|
||||
}
|
||||
Image {
|
||||
source: Qt.resolvedUrl('../../icons/zoom.png')
|
||||
sourceSize.width: constants.iconSizeMedium
|
||||
sourceSize.height: constants.iconSizeMedium
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ScrollIndicator.vertical: ScrollIndicator { }
|
||||
Frame {
|
||||
id: channelsFrame
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
|
||||
verticalPadding: 0
|
||||
horizontalPadding: 0
|
||||
background: PaneInsetBackground {}
|
||||
|
||||
ElListView {
|
||||
id: listview
|
||||
|
||||
anchors.fill: parent
|
||||
clip: true
|
||||
|
||||
property QtObject backingModel: Daemon.currentWallet.addressCoinModel
|
||||
property QtObject filterModel: Daemon.currentWallet.addressCoinModel.filterModel
|
||||
property bool selectMode: false
|
||||
property bool freeze: true
|
||||
model: visualModel
|
||||
currentIndex: -1
|
||||
|
||||
section.property: 'type'
|
||||
section.criteria: ViewSection.FullString
|
||||
section.delegate: sectionDelegate
|
||||
|
||||
function getSelectedItems() {
|
||||
var items = []
|
||||
for (let i = 0; i < selectedGroup.count; i++) {
|
||||
let modelitem = selectedGroup.get(i).model
|
||||
if (modelitem.outpoint)
|
||||
items.push(modelitem.outpoint)
|
||||
else
|
||||
items.push(modelitem.address)
|
||||
}
|
||||
return items
|
||||
}
|
||||
|
||||
DelegateModel {
|
||||
id: visualModel
|
||||
model: listview.filterModel
|
||||
groups: [
|
||||
DelegateModelGroup {
|
||||
id: selectedGroup;
|
||||
name: 'selected'
|
||||
onCountChanged: {
|
||||
if (count == 0)
|
||||
listview.selectMode = false
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
delegate: Loader {
|
||||
id: loader
|
||||
width: parent.width
|
||||
|
||||
sourceComponent: model.outpoint ? _coinDelegate : _addressDelegate
|
||||
|
||||
function toggle() {
|
||||
loader.DelegateModel.inSelected = !loader.DelegateModel.inSelected
|
||||
}
|
||||
|
||||
Component {
|
||||
id: _addressDelegate
|
||||
AddressDelegate {
|
||||
id: addressDelegate
|
||||
width: parent.width
|
||||
property bool selected: loader.DelegateModel.inSelected
|
||||
highlighted: selected
|
||||
onClicked: {
|
||||
if (!listview.selectMode) {
|
||||
var page = app.stack.push(Qt.resolvedUrl('AddressDetails.qml'), {
|
||||
address: model.address
|
||||
})
|
||||
page.addressDetailsChanged.connect(function() {
|
||||
// update listmodel when details change
|
||||
listview.backingModel.updateAddress(model.address)
|
||||
})
|
||||
} else {
|
||||
loader.toggle()
|
||||
}
|
||||
}
|
||||
onPressAndHold: {
|
||||
loader.toggle()
|
||||
if (!listview.selectMode && selectedGroup.count > 0)
|
||||
listview.selectMode = true
|
||||
}
|
||||
}
|
||||
}
|
||||
Component {
|
||||
id: _coinDelegate
|
||||
Pane {
|
||||
height: coinDelegate.height
|
||||
padding: 0
|
||||
background: Rectangle {
|
||||
color: Qt.darker(constants.darkerBackground, 1.10)
|
||||
}
|
||||
|
||||
CoinDelegate {
|
||||
id: coinDelegate
|
||||
width: parent.width
|
||||
property bool selected: loader.DelegateModel.inSelected
|
||||
highlighted: selected
|
||||
onClicked: {
|
||||
if (!listview.selectMode) {
|
||||
var page = app.stack.push(Qt.resolvedUrl('TxDetails.qml'), {
|
||||
txid: model.txid
|
||||
})
|
||||
} else {
|
||||
loader.toggle()
|
||||
}
|
||||
}
|
||||
onPressAndHold: {
|
||||
loader.toggle()
|
||||
if (!listview.selectMode && selectedGroup.count > 0)
|
||||
listview.selectMode = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
add: Transition {
|
||||
NumberAnimation { properties: "opacity"; from: 0.0; to: 1.0; duration: 300
|
||||
easing.type: Easing.OutQuad
|
||||
}
|
||||
}
|
||||
|
||||
onSelectModeChanged: {
|
||||
if (selectMode) {
|
||||
listview.freeze = !selectedGroup.get(0).model.held
|
||||
}
|
||||
}
|
||||
|
||||
ScrollIndicator.vertical: ScrollIndicator { }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ButtonContainer {
|
||||
Layout.fillWidth: true
|
||||
FlatButton {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredWidth: 1
|
||||
text: listview.freeze ? qsTr('Freeze') : qsTr('Unfreeze')
|
||||
icon.source: '../../icons/seal.png'
|
||||
visible: listview.selectMode
|
||||
onClicked: {
|
||||
var items = listview.getSelectedItems()
|
||||
listview.backingModel.setFrozenForItems(listview.freeze, items)
|
||||
selectedGroup.remove(0, selectedGroup.count)
|
||||
}
|
||||
}
|
||||
FlatButton {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredWidth: 1
|
||||
text: qsTr('Pay from...')
|
||||
icon.source: '../../icons/tab_send.png'
|
||||
visible: listview.selectMode
|
||||
enabled: false // TODO
|
||||
onClicked: {
|
||||
//
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -52,7 +265,6 @@ Pane {
|
||||
id: root
|
||||
width: ListView.view.width
|
||||
height: childrenRect.height
|
||||
|
||||
required property string section
|
||||
property string section_label: section == 'receive'
|
||||
? qsTr('receive addresses')
|
||||
@@ -74,6 +286,6 @@ Pane {
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
Daemon.currentWallet.addressModel.initModel()
|
||||
Daemon.currentWallet.addressCoinModel.initModel()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,9 +27,9 @@ ItemDelegate {
|
||||
Label {
|
||||
id: indexLabel
|
||||
font.bold: true
|
||||
text: model.iaddr < 10
|
||||
? '#' + ('0'+model.iaddr).slice(-2)
|
||||
: '#' + model.iaddr
|
||||
text: model.addridx < 10
|
||||
? '#' + ('0'+model.addridx).slice(-2)
|
||||
: '#' + model.addridx
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
Label {
|
||||
|
||||
97
electrum/gui/qml/components/controls/CoinDelegate.qml
Normal file
97
electrum/gui/qml/components/controls/CoinDelegate.qml
Normal file
@@ -0,0 +1,97 @@
|
||||
import QtQuick 2.6
|
||||
import QtQuick.Controls 2.0
|
||||
import QtQuick.Layouts 1.0
|
||||
import QtQuick.Controls.Material 2.0
|
||||
|
||||
import org.electrum 1.0
|
||||
|
||||
ItemDelegate {
|
||||
id: delegate
|
||||
width: ListView.view.width
|
||||
height: delegateLayout.height
|
||||
highlighted: ListView.isCurrentItem
|
||||
|
||||
font.pixelSize: constants.fontSizeMedium // set default font size for child controls
|
||||
|
||||
ColumnLayout {
|
||||
id: delegateLayout
|
||||
width: parent.width
|
||||
spacing: 0
|
||||
|
||||
GridLayout {
|
||||
columns: 3
|
||||
Layout.topMargin: constants.paddingSmall
|
||||
Layout.leftMargin: constants.paddingLarge + 2*constants.paddingLarge
|
||||
Layout.rightMargin: constants.paddingLarge
|
||||
|
||||
Rectangle {
|
||||
id: useIndicator
|
||||
Layout.rowSpan: 2
|
||||
Layout.preferredWidth: constants.iconSizeSmall
|
||||
Layout.preferredHeight: constants.iconSizeSmall
|
||||
Layout.alignment: Qt.AlignTop
|
||||
color: model.held
|
||||
? constants.colorAddressFrozen
|
||||
: constants.colorAddressUsedWithBalance
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Label {
|
||||
font.family: FixedFont
|
||||
text: model.outpoint
|
||||
elide: Text.ElideMiddle
|
||||
Layout.preferredWidth: implicitWidth + constants.paddingMedium
|
||||
}
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
visible: model.short_id
|
||||
font.family: FixedFont
|
||||
font.pixelSize: constants.fontSizeSmall
|
||||
text: '[' + model.short_id + ']'
|
||||
}
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignLeft | Qt.AlignTop
|
||||
visible: !model.short_id
|
||||
Image {
|
||||
source: Qt.resolvedUrl('../../../icons/unconfirmed.png')
|
||||
sourceSize.width: constants.iconSizeSmall
|
||||
sourceSize.height: constants.iconSizeSmall
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Label {
|
||||
font.family: FixedFont
|
||||
text: Config.formatSats(model.amount, false)
|
||||
visible: model.amount.satsInt != 0
|
||||
}
|
||||
Label {
|
||||
color: Material.accentColor
|
||||
text: Config.baseUnit
|
||||
visible: model.amount.satsInt != 0
|
||||
}
|
||||
}
|
||||
|
||||
Label {
|
||||
id: labelLabel
|
||||
Layout.fillWidth: true
|
||||
Layout.columnSpan: 2
|
||||
visible: model.label
|
||||
font.pixelSize: constants.fontSizeMedium
|
||||
text: model.label
|
||||
elide: Text.ElideRight
|
||||
maximumLineCount: 2
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.preferredWidth: 1
|
||||
Layout.preferredHeight: constants.paddingSmall
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
import itertools
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import TYPE_CHECKING, List
|
||||
|
||||
from PyQt5.QtCore import pyqtSlot
|
||||
from PyQt5.QtCore import pyqtSlot, QSortFilterProxyModel, pyqtSignal, pyqtProperty
|
||||
from PyQt5.QtCore import Qt, QAbstractListModel, QModelIndex
|
||||
|
||||
from electrum.logging import get_logger
|
||||
@@ -11,38 +10,126 @@ from .qetypes import QEAmount
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from electrum.wallet import Abstract_Wallet
|
||||
from electrum.transaction import PartialTxInput
|
||||
|
||||
|
||||
class QEAddressListModel(QAbstractListModel):
|
||||
class QEAddressCoinFilterProxyModel(QSortFilterProxyModel):
|
||||
_logger = get_logger(__name__)
|
||||
|
||||
def __init__(self, parent_model, parent=None):
|
||||
super().__init__(parent)
|
||||
self._filter_text = None
|
||||
self._show_coins = True
|
||||
self._show_addresses = True
|
||||
self._show_used = False
|
||||
self._parent_model = parent_model
|
||||
self.setSourceModel(parent_model)
|
||||
|
||||
countChanged = pyqtSignal()
|
||||
@pyqtProperty(int, notify=countChanged)
|
||||
def count(self):
|
||||
return self.rowCount(QModelIndex())
|
||||
|
||||
def filterAcceptsRow(self, s_row, s_parent):
|
||||
parent_model = self.sourceModel()
|
||||
addridx = parent_model.data(parent_model.index(s_row, 0, s_parent), parent_model._ROLE_RMAP['addridx'])
|
||||
if addridx is None: # coin
|
||||
if not self._show_coins:
|
||||
return False
|
||||
else:
|
||||
if not self._show_addresses:
|
||||
return False
|
||||
balance = parent_model.data(parent_model.index(s_row, 0, s_parent), parent_model._ROLE_RMAP['balance'])
|
||||
numtx = parent_model.data(parent_model.index(s_row, 0, s_parent), parent_model._ROLE_RMAP['numtx'])
|
||||
if balance.isEmpty and numtx and not self._show_used:
|
||||
return False
|
||||
if self._filter_text:
|
||||
label = parent_model.data(parent_model.index(s_row, 0, s_parent), parent_model._ROLE_RMAP['label'])
|
||||
address = parent_model.data(parent_model.index(s_row, 0, s_parent), parent_model._ROLE_RMAP['address'])
|
||||
for item in [label, address]:
|
||||
if self._filter_text in str(item):
|
||||
return True
|
||||
return False
|
||||
return True
|
||||
|
||||
showAddressesCoinsChanged = pyqtSignal()
|
||||
@pyqtProperty(int, notify=showAddressesCoinsChanged)
|
||||
def showAddressesCoins(self) -> int:
|
||||
result = 0
|
||||
if self._show_addresses:
|
||||
result += 1
|
||||
if self._show_coins:
|
||||
result += 2
|
||||
return result
|
||||
|
||||
@showAddressesCoins.setter
|
||||
def showAddressesCoins(self, show_addresses_coins: int):
|
||||
show_addresses = show_addresses_coins in [1, 3]
|
||||
show_coins = show_addresses_coins in [2, 3]
|
||||
|
||||
if self._show_addresses != show_addresses or self._show_coins != show_coins:
|
||||
self._show_addresses = show_addresses
|
||||
self._show_coins = show_coins
|
||||
self.invalidateFilter()
|
||||
self.showAddressesCoinsChanged.emit()
|
||||
|
||||
showUsedChanged = pyqtSignal()
|
||||
@pyqtProperty(bool, notify=showUsedChanged)
|
||||
def showUsed(self) -> bool:
|
||||
return self._show_used
|
||||
|
||||
@showUsed.setter
|
||||
def showUsed(self, show_used: bool):
|
||||
if self._show_used != show_used:
|
||||
self._show_used = show_used
|
||||
self.invalidateFilter()
|
||||
self.showUsedChanged.emit()
|
||||
|
||||
filterTextChanged = pyqtSignal()
|
||||
@pyqtProperty(str, notify=filterTextChanged)
|
||||
def filterText(self) -> str:
|
||||
return self._filter_text
|
||||
|
||||
@filterText.setter
|
||||
def filterText(self, filter_text: str):
|
||||
if self._filter_text != filter_text:
|
||||
self._filter_text = filter_text
|
||||
self.invalidateFilter()
|
||||
self.filterTextChanged.emit()
|
||||
|
||||
|
||||
class QEAddressCoinListModel(QAbstractListModel):
|
||||
_logger = get_logger(__name__)
|
||||
|
||||
# define listmodel rolemap
|
||||
_ROLE_NAMES=('type', 'iaddr', 'address', 'label', 'balance', 'numtx', 'held')
|
||||
_ROLE_NAMES=('type', 'addridx', 'address', 'label', 'balance', 'numtx', 'held', 'height', 'amount', 'outpoint',
|
||||
'short_outpoint', 'short_id', 'txid')
|
||||
_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))
|
||||
|
||||
def __init__(self, wallet: 'Abstract_Wallet', parent=None):
|
||||
super().__init__(parent)
|
||||
self.wallet = wallet
|
||||
self._receive_addresses = []
|
||||
self._change_addresses = []
|
||||
self._items = []
|
||||
self._filterModel = None
|
||||
|
||||
self._dirty = True
|
||||
self.initModel()
|
||||
|
||||
def rowCount(self, index):
|
||||
return len(self._receive_addresses) + len(self._change_addresses)
|
||||
return len(self._items)
|
||||
|
||||
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)]
|
||||
else:
|
||||
address = self._receive_addresses[index.row()]
|
||||
address = self._items[index.row()]
|
||||
role_index = role - Qt.UserRole
|
||||
value = address[self._ROLE_NAMES[role_index]]
|
||||
try:
|
||||
value = address[self._ROLE_NAMES[role_index]]
|
||||
except KeyError:
|
||||
return None
|
||||
if isinstance(value, (bool, list, int, str, QEAmount)) or value is None:
|
||||
return value
|
||||
if isinstance(value, Satoshis):
|
||||
@@ -51,13 +138,14 @@ class QEAddressListModel(QAbstractListModel):
|
||||
|
||||
def clear(self):
|
||||
self.beginResetModel()
|
||||
self._receive_addresses = []
|
||||
self._change_addresses = []
|
||||
self._items = []
|
||||
self.endResetModel()
|
||||
|
||||
def addr_to_model(self, address):
|
||||
def addr_to_model(self, addrtype: str, addridx: int, address: str):
|
||||
c, u, x = self.wallet.get_addr_balance(address)
|
||||
item = {
|
||||
'type': addrtype,
|
||||
'addridx': addridx,
|
||||
'address': address,
|
||||
'numtx': self.wallet.adb.get_address_history_len(address),
|
||||
'label': self.wallet.get_label_for_address(address),
|
||||
@@ -66,6 +154,27 @@ class QEAddressListModel(QAbstractListModel):
|
||||
}
|
||||
return item
|
||||
|
||||
def coin_to_model(self, addrtype: str, coin: 'PartialTxInput'):
|
||||
txid = coin.prevout.txid.hex()
|
||||
short_id = ''
|
||||
# check below duplicated from TxInput as we cannot get short_id unambiguously
|
||||
if coin.block_txpos is not None and coin.block_txpos >= 0:
|
||||
short_id = str(coin.short_id)
|
||||
item = {
|
||||
'type': addrtype,
|
||||
'amount': QEAmount(amount_sat=coin.value_sats()),
|
||||
'address': coin.address,
|
||||
'height': coin.block_height,
|
||||
'outpoint': coin.prevout.to_str(),
|
||||
'short_outpoint': coin.prevout.short_name(),
|
||||
'short_id': short_id,
|
||||
'txid': txid,
|
||||
'label': self.wallet.get_label_for_txid(txid) or '',
|
||||
'held': self.wallet.is_frozen_coin(coin),
|
||||
'coin': coin
|
||||
}
|
||||
return item
|
||||
|
||||
@pyqtSlot()
|
||||
def setDirty(self):
|
||||
self._dirty = True
|
||||
@@ -80,36 +189,70 @@ class QEAddressListModel(QAbstractListModel):
|
||||
c_addresses = self.wallet.get_change_addresses() if self.wallet.wallet_type != 'imported' else []
|
||||
n_addresses = len(r_addresses) + len(c_addresses)
|
||||
|
||||
def insert_row(atype, alist, address, iaddr):
|
||||
item = self.addr_to_model(address)
|
||||
item['type'] = atype
|
||||
item['iaddr'] = iaddr
|
||||
alist.append(item)
|
||||
def insert_address(atype, address, addridx):
|
||||
item = self.addr_to_model(atype, addridx, address)
|
||||
self._items.append(item)
|
||||
|
||||
utxos = self.wallet.get_utxos([address])
|
||||
utxos.sort(key=lambda x: x.block_height)
|
||||
for i, coin in enumerate(utxos):
|
||||
self._items.append(self.coin_to_model(atype, coin))
|
||||
|
||||
self.clear()
|
||||
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_address('receive', address, i)
|
||||
for i, address in enumerate(c_addresses):
|
||||
insert_row('change', self._change_addresses, address, i)
|
||||
insert_address('change', address, i)
|
||||
else:
|
||||
for i, address in enumerate(r_addresses):
|
||||
insert_row('imported', self._receive_addresses, address, i)
|
||||
insert_address('imported', 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(self._items):
|
||||
if a['address'] == address:
|
||||
self.do_update(i, a)
|
||||
return
|
||||
|
||||
def updateCoin(self, outpoint):
|
||||
for i, a in enumerate(self._items):
|
||||
if a.get('outpoint') == outpoint:
|
||||
self.do_update(i, a)
|
||||
return
|
||||
|
||||
def do_update(self, modelindex, modelitem):
|
||||
mi = self.createIndex(modelindex, 0)
|
||||
self._logger.debug(repr(modelitem))
|
||||
modelitem.update(self.addr_to_model(modelitem['address']))
|
||||
if modelitem.get('outpoint'):
|
||||
modelitem.update(self.coin_to_model(modelitem['type'], modelitem['coin']))
|
||||
else:
|
||||
modelitem.update(self.addr_to_model(modelitem['type'], modelitem['addridx'], modelitem['address']))
|
||||
self._logger.debug(repr(modelitem))
|
||||
self.dataChanged.emit(mi, mi, self._ROLE_KEYS)
|
||||
|
||||
filterModelChanged = pyqtSignal()
|
||||
@pyqtProperty(QEAddressCoinFilterProxyModel, notify=filterModelChanged)
|
||||
def filterModel(self):
|
||||
if self._filterModel is None:
|
||||
self._filterModel = QEAddressCoinFilterProxyModel(self)
|
||||
return self._filterModel
|
||||
|
||||
@pyqtSlot(bool, list)
|
||||
def setFrozenForItems(self, freeze: bool, items: List[str]):
|
||||
self._logger.debug(f'set frozen to {freeze} for {items!r}')
|
||||
coins = list(filter(lambda x: ':' in x, items))
|
||||
if len(coins):
|
||||
self.wallet.set_frozen_state_of_coins(coins, freeze)
|
||||
for coin in coins:
|
||||
self.updateCoin(coin)
|
||||
addresses = list(filter(lambda x: ':' not in x, items))
|
||||
if len(addresses):
|
||||
self.wallet.set_frozen_state_of_addresses(addresses, freeze)
|
||||
for address in addresses:
|
||||
self.updateAddress(address)
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
from PyQt5.QtCore import pyqtSignal, pyqtProperty, QSortFilterProxyModel, QModelIndex
|
||||
from PyQt5.QtCore import pyqtSignal, pyqtProperty, QSortFilterProxyModel, QModelIndex, pyqtSlot
|
||||
|
||||
from electrum.logging import get_logger
|
||||
|
||||
|
||||
class QEFilterProxyModel(QSortFilterProxyModel):
|
||||
_logger = get_logger(__name__)
|
||||
|
||||
@@ -18,8 +19,10 @@ class QEFilterProxyModel(QSortFilterProxyModel):
|
||||
def isCustomFilter(self):
|
||||
return self._filter_value is not None
|
||||
|
||||
@pyqtSlot(str)
|
||||
def setFilterValue(self, filter_value):
|
||||
self._filter_value = filter_value
|
||||
self.invalidate()
|
||||
|
||||
def filterAcceptsRow(self, s_row, s_parent):
|
||||
if not self.isCustomFilter:
|
||||
|
||||
@@ -19,7 +19,7 @@ from electrum.wallet import Multisig_Wallet
|
||||
from electrum.crypto import pw_decode_with_version_and_mac
|
||||
|
||||
from .auth import AuthMixin, auth_protect
|
||||
from .qeaddresslistmodel import QEAddressListModel
|
||||
from .qeaddresslistmodel import QEAddressCoinListModel
|
||||
from .qechannellistmodel import QEChannelListModel
|
||||
from .qeinvoicelistmodel import QEInvoiceListModel, QERequestListModel
|
||||
from .qetransactionlistmodel import QETransactionListModel
|
||||
@@ -91,7 +91,7 @@ class QEWallet(AuthMixin, QObject, QtEventListener):
|
||||
self._synchronizing_progress = ''
|
||||
|
||||
self._historyModel = None
|
||||
self._addressModel = None
|
||||
self._addressCoinModel = None
|
||||
self._requestModel = None
|
||||
self._invoiceModel = None
|
||||
self._channelModel = None
|
||||
@@ -184,7 +184,7 @@ class QEWallet(AuthMixin, QObject, QtEventListener):
|
||||
if wallet == self.wallet:
|
||||
self._logger.info(f'new transaction {tx.txid()}')
|
||||
self.add_tx_notification(tx)
|
||||
self.addressModel.setDirty()
|
||||
self.addressCoinModel.setDirty()
|
||||
self.historyModel.setDirty() # assuming wallet.is_up_to_date triggers after
|
||||
self.balanceChanged.emit()
|
||||
|
||||
@@ -198,7 +198,7 @@ class QEWallet(AuthMixin, QObject, QtEventListener):
|
||||
def on_event_removed_transaction(self, wallet, tx):
|
||||
if wallet == self.wallet:
|
||||
self._logger.info(f'removed transaction {tx.txid()}')
|
||||
self.addressModel.setDirty()
|
||||
self.addressCoinModel.setDirty()
|
||||
self.historyModel.initModel(True) # setDirty()?
|
||||
self.balanceChanged.emit()
|
||||
|
||||
@@ -295,12 +295,12 @@ class QEWallet(AuthMixin, QObject, QtEventListener):
|
||||
self._historyModel = QETransactionListModel(self.wallet)
|
||||
return self._historyModel
|
||||
|
||||
addressModelChanged = pyqtSignal()
|
||||
@pyqtProperty(QEAddressListModel, notify=addressModelChanged)
|
||||
def addressModel(self):
|
||||
if self._addressModel is None:
|
||||
self._addressModel = QEAddressListModel(self.wallet)
|
||||
return self._addressModel
|
||||
addressCoinModelChanged = pyqtSignal()
|
||||
@pyqtProperty(QEAddressCoinListModel, notify=addressCoinModelChanged)
|
||||
def addressCoinModel(self):
|
||||
if self._addressCoinModel is None:
|
||||
self._addressCoinModel = QEAddressCoinListModel(self.wallet)
|
||||
return self._addressCoinModel
|
||||
|
||||
requestModelChanged = pyqtSignal()
|
||||
@pyqtProperty(QERequestListModel, notify=requestModelChanged)
|
||||
@@ -658,7 +658,7 @@ class QEWallet(AuthMixin, QObject, QtEventListener):
|
||||
|
||||
assert key is not None
|
||||
self._logger.debug(f'created request with key {key} addr {addr}')
|
||||
self.addressModel.setDirty()
|
||||
self.addressCoinModel.setDirty()
|
||||
self.requestModel.add_invoice(self.wallet.get_request(key))
|
||||
self.requestCreateSuccess.emit(key)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user