Browse Source

Merge pull request #8772 from accumulator/qml_tx_inputs

qml: add transaction inputs in TxDetails and …
master
accumulator 2 years ago committed by GitHub
parent
commit
88058df409
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 37
      electrum/gui/qml/components/ConfirmTxDialog.qml
  2. 40
      electrum/gui/qml/components/CpfpBumpFeeDialog.qml
  3. 40
      electrum/gui/qml/components/RbfBumpFeeDialog.qml
  4. 40
      electrum/gui/qml/components/RbfCancelDialog.qml
  5. 35
      electrum/gui/qml/components/TxDetails.qml
  6. 17
      electrum/gui/qml/components/controls/ToggleLabel.qml
  7. 73
      electrum/gui/qml/components/controls/TxInput.qml
  8. 93
      electrum/gui/qml/components/controls/TxOutput.qml
  9. 11
      electrum/gui/qml/qetxdetails.py
  10. 41
      electrum/gui/qml/qetxfinalizer.py

37
electrum/gui/qml/components/ConfirmTxDialog.qml

@ -183,22 +183,53 @@ ElDialog {
iconStyle: InfoTextArea.IconStyle.Warn iconStyle: InfoTextArea.IconStyle.Warn
} }
Label { ToggleLabel {
text: qsTr('Outputs') id: inputs_label
Layout.columnSpan: 2 Layout.columnSpan: 2
Layout.topMargin: constants.paddingMedium
labelText: qsTr('Inputs (%1)').arg(finalizer.inputs.length)
color: Material.accentColor color: Material.accentColor
} }
Repeater { Repeater {
model: finalizer.outputs model: inputs_label.collapsed
? undefined
: finalizer.inputs
delegate: TxInput {
Layout.columnSpan: 2
Layout.fillWidth: true
idx: index
model: modelData
}
}
ToggleLabel {
id: outputs_label
Layout.columnSpan: 2
Layout.topMargin: constants.paddingMedium
labelText: qsTr('Outputs (%1)').arg(finalizer.outputs.length)
color: Material.accentColor
}
Repeater {
model: outputs_label.collapsed
? undefined
: finalizer.outputs
delegate: TxOutput { delegate: TxOutput {
Layout.columnSpan: 2 Layout.columnSpan: 2
Layout.fillWidth: true Layout.fillWidth: true
allowShare: false allowShare: false
allowClickAddress: false
idx: index
model: modelData model: modelData
} }
} }
} }
} }

40
electrum/gui/qml/components/CpfpBumpFeeDialog.qml

@ -176,23 +176,55 @@ ElDialog {
iconStyle: InfoTextArea.IconStyle.Warn iconStyle: InfoTextArea.IconStyle.Warn
} }
Label { ToggleLabel {
id: inputs_label
Layout.columnSpan: 2
Layout.topMargin: constants.paddingMedium
visible: cpfpfeebumper.valid visible: cpfpfeebumper.valid
text: qsTr('Outputs') labelText: qsTr('Inputs (%1)').arg(cpfpfeebumper.inputs.length)
color: Material.accentColor
}
Repeater {
model: inputs_label.collapsed || !inputs_label.visible
? undefined
: cpfpfeebumper.inputs
delegate: TxInput {
Layout.columnSpan: 2
Layout.fillWidth: true
idx: index
model: modelData
}
}
ToggleLabel {
id: outputs_label
Layout.columnSpan: 2 Layout.columnSpan: 2
Layout.topMargin: constants.paddingMedium
visible: cpfpfeebumper.valid
labelText: qsTr('Outputs (%1)').arg(cpfpfeebumper.outputs.length)
color: Material.accentColor color: Material.accentColor
} }
Repeater { Repeater {
model: cpfpfeebumper.valid ? cpfpfeebumper.outputs : [] model: outputs_label.collapsed || !outputs_label.visible
delegate: TxOutput { ? undefined
: cpfpfeebumper.outputs
delegate: TxOutput {
Layout.columnSpan: 2 Layout.columnSpan: 2
Layout.fillWidth: true Layout.fillWidth: true
allowShare: false allowShare: false
allowClickAddress: false
idx: index
model: modelData model: modelData
} }
} }
} }
} }

40
electrum/gui/qml/components/RbfBumpFeeDialog.qml

@ -188,23 +188,55 @@ ElDialog {
text: rbffeebumper.warning text: rbffeebumper.warning
} }
Label { ToggleLabel {
id: inputs_label
Layout.columnSpan: 2
Layout.topMargin: constants.paddingMedium
visible: rbffeebumper.valid visible: rbffeebumper.valid
text: qsTr('Outputs') labelText: qsTr('Inputs (%1)').arg(rbffeebumper.inputs.length)
color: Material.accentColor
}
Repeater {
model: inputs_label.collapsed || !inputs_label.visible
? undefined
: rbffeebumper.inputs
delegate: TxInput {
Layout.columnSpan: 2
Layout.fillWidth: true
idx: index
model: modelData
}
}
ToggleLabel {
id: outputs_label
Layout.columnSpan: 2 Layout.columnSpan: 2
Layout.topMargin: constants.paddingMedium
visible: rbffeebumper.valid
labelText: qsTr('Outputs (%1)').arg(rbffeebumper.outputs.length)
color: Material.accentColor color: Material.accentColor
} }
Repeater { Repeater {
model: rbffeebumper.valid ? rbffeebumper.outputs : [] model: outputs_label.collapsed || !outputs_label.visible
delegate: TxOutput { ? undefined
: rbffeebumper.outputs
delegate: TxOutput {
Layout.columnSpan: 2 Layout.columnSpan: 2
Layout.fillWidth: true Layout.fillWidth: true
allowShare: false allowShare: false
allowClickAddress: false
idx: index
model: modelData model: modelData
} }
} }
} }
} }

40
electrum/gui/qml/components/RbfCancelDialog.qml

@ -151,23 +151,55 @@ ElDialog {
text: txcanceller.warning text: txcanceller.warning
} }
Label { ToggleLabel {
id: inputs_label
Layout.columnSpan: 2
Layout.topMargin: constants.paddingMedium
visible: txcanceller.valid visible: txcanceller.valid
text: qsTr('Outputs') labelText: qsTr('Inputs (%1)').arg(txcanceller.inputs.length)
color: Material.accentColor
}
Repeater {
model: inputs_label.collapsed || !inputs_label.visible
? undefined
: txcanceller.inputs
delegate: TxInput {
Layout.columnSpan: 2
Layout.fillWidth: true
idx: index
model: modelData
}
}
ToggleLabel {
id: outputs_label
Layout.columnSpan: 2 Layout.columnSpan: 2
Layout.topMargin: constants.paddingMedium
visible: txcanceller.valid
labelText: qsTr('Outputs (%1)').arg(txcanceller.outputs.length)
color: Material.accentColor color: Material.accentColor
} }
Repeater { Repeater {
model: txcanceller.valid ? txcanceller.outputs : [] model: outputs_label.collapsed || !outputs_label.visible
delegate: TxOutput { ? undefined
: txcanceller.outputs
delegate: TxOutput {
Layout.columnSpan: 2 Layout.columnSpan: 2
Layout.fillWidth: true Layout.fillWidth: true
allowShare: false allowShare: false
allowClickAddress: false
idx: index
model: modelData model: modelData
} }
} }
} }
} }

35
electrum/gui/qml/components/TxDetails.qml

@ -302,19 +302,46 @@ Pane {
} }
} }
Label { ToggleLabel {
id: inputs_label
Layout.columnSpan: 2 Layout.columnSpan: 2
Layout.topMargin: constants.paddingSmall Layout.topMargin: constants.paddingMedium
text: qsTr('Outputs')
labelText: qsTr('Inputs (%1)').arg(txdetails.inputs.length)
color: Material.accentColor
}
Repeater {
model: inputs_label.collapsed
? undefined
: txdetails.inputs
delegate: TxInput {
Layout.columnSpan: 2
Layout.fillWidth: true
idx: index
model: modelData
}
}
ToggleLabel {
id: outputs_label
Layout.columnSpan: 2
Layout.topMargin: constants.paddingMedium
labelText: qsTr('Outputs (%1)').arg(txdetails.outputs.length)
color: Material.accentColor color: Material.accentColor
} }
Repeater { Repeater {
model: txdetails.outputs model: outputs_label.collapsed
? undefined
: txdetails.outputs
delegate: TxOutput { delegate: TxOutput {
Layout.columnSpan: 2 Layout.columnSpan: 2
Layout.fillWidth: true Layout.fillWidth: true
idx: index
model: modelData model: modelData
} }
} }

17
electrum/gui/qml/components/controls/ToggleLabel.qml

@ -0,0 +1,17 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Controls.Material
Label {
id: root
property bool collapsed: true
property string labelText
text: (collapsed ? '▷' : '▽') + ' ' + labelText
TapHandler {
onTapped: {
root.collapsed = !root.collapsed
}
}
}

73
electrum/gui/qml/components/controls/TxInput.qml

@ -0,0 +1,73 @@
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
import QtQuick.Controls.Material
import org.electrum 1.0
TextHighlightPane {
id: root
property variant model
property int idx: -1
ColumnLayout {
width: parent.width
RowLayout {
Layout.fillWidth: true
Label {
Layout.rightMargin: constants.paddingMedium
text: '#' + idx
font.family: FixedFont
font.bold: true
}
Label {
Layout.fillWidth: true
text: model.short_id
font.family: FixedFont
}
Label {
id: txin_value
text: model.value != undefined
? Config.formatSats(model.value)
: '<' + qsTr('unknown amount') + '>'
font.pixelSize: constants.fontSizeMedium
font.family: FixedFont
}
Label {
text: Config.baseUnit
visible: model.value != undefined
font.pixelSize: constants.fontSizeMedium
color: Material.accentColor
}
}
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: 1
antialiasing: true
color: constants.mutedForeground
}
RowLayout {
Layout.fillWidth: true
Label {
Layout.fillWidth: true
text: model.address
? model.address
: '<' + qsTr('address unknown') + '>'
font.family: FixedFont
font.pixelSize: constants.fontSizeMedium
color: model.is_mine
? model.is_change
? constants.colorAddressInternal
: constants.colorAddressExternal
: Material.foreground
elide: Text.ElideMiddle
}
}
}
}

93
electrum/gui/qml/components/controls/TxOutput.qml

@ -11,41 +11,77 @@ TextHighlightPane {
property variant model property variant model
property bool allowShare: true property bool allowShare: true
property bool allowClickAddress: true property bool allowClickAddress: true
property int idx: -1
RowLayout { RowLayout {
width: parent.width width: parent.width
Label {
text: model.address ColumnLayout {
Layout.fillWidth: true Layout.fillWidth: true
wrapMode: Text.Wrap
font.pixelSize: constants.fontSizeLarge RowLayout {
font.family: FixedFont Layout.fillWidth: true
color: model.is_mine
? model.is_change Label {
? constants.colorAddressInternal Layout.rightMargin: constants.paddingLarge
: constants.colorAddressExternal text: '#' + idx
: model.is_billing visible: idx >= 0
? constants.colorAddressBilling font.family: FixedFont
: Material.foreground font.pixelSize: constants.fontSizeMedium
TapHandler { font.bold: true
enabled: allowClickAddress && model.is_mine }
onTapped: { Label {
app.stack.push(Qt.resolvedUrl('../AddressDetails.qml'), { Layout.fillWidth: true
address: model.address font.family: FixedFont
}) text: model.short_id
}
Label {
text: Config.formatSats(model.value)
font.pixelSize: constants.fontSizeMedium
font.family: FixedFont
}
Label {
text: Config.baseUnit
font.pixelSize: constants.fontSizeMedium
color: Material.accentColor
} }
} }
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: 1
antialiasing: true
color: constants.mutedForeground
}
RowLayout {
Layout.fillWidth: true
Label {
text: model.address
Layout.fillWidth: true
wrapMode: Text.Wrap
font.pixelSize: constants.fontSizeMedium
font.family: FixedFont
color: model.is_mine
? model.is_change
? constants.colorAddressInternal
: constants.colorAddressExternal
: model.is_billing
? constants.colorAddressBilling
: Material.foreground
TapHandler {
enabled: allowClickAddress && model.is_mine
onTapped: {
app.stack.push(Qt.resolvedUrl('../AddressDetails.qml'), {
address: model.address
})
}
}
}
}
} }
Label {
text: Config.formatSats(model.value)
font.pixelSize: constants.fontSizeMedium
font.family: FixedFont
}
Label {
text: Config.baseUnit
font.pixelSize: constants.fontSizeMedium
color: Material.accentColor
}
ToolButton { ToolButton {
visible: allowShare visible: allowShare
icon.source: Qt.resolvedUrl('../../../icons/share.png') icon.source: Qt.resolvedUrl('../../../icons/share.png')
@ -58,6 +94,7 @@ TextHighlightPane {
dialog.open() dialog.open()
} }
} }
} }
} }

11
electrum/gui/qml/qetxdetails.py

@ -5,7 +5,7 @@ from PyQt6.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, TxMinedInfo from electrum.util import format_time, TxMinedInfo
from electrum.transaction import tx_from_any, Transaction, PartialTxInput, Sighash, PartialTransaction from electrum.transaction import tx_from_any, Transaction, PartialTxInput, Sighash, PartialTransaction, TxOutpoint
from electrum.network import Network from electrum.network import Network
from electrum.address_synchronizer import TX_HEIGHT_UNCONF_PARENT, TX_HEIGHT_UNCONFIRMED, TX_HEIGHT_FUTURE from electrum.address_synchronizer import TX_HEIGHT_UNCONF_PARENT, TX_HEIGHT_UNCONFIRMED, TX_HEIGHT_FUTURE
from electrum.wallet import TxSighashDanger from electrum.wallet import TxSighashDanger
@ -270,10 +270,17 @@ class QETxDetails(QObject, QtEventListener):
Network.run_from_another_thread( Network.run_from_another_thread(
self._tx.add_info_from_network(self._wallet.wallet.network, timeout=10)) # FIXME is this needed?... self._tx.add_info_from_network(self._wallet.wallet.network, timeout=10)) # FIXME is this needed?...
self._inputs = list(map(lambda x: x.to_json(), self._tx.inputs())) self._inputs = list(map(lambda x: {
'short_id': x.prevout.short_name(),
'value': x.value_sats(),
'address': x.address,
'is_mine': self._wallet.wallet.is_mine(x.address),
'is_change': self._wallet.wallet.is_change(x.address)
}, self._tx.inputs()))
self._outputs = list(map(lambda x: { self._outputs = list(map(lambda x: {
'address': x.get_ui_address_str(), 'address': x.get_ui_address_str(),
'value': QEAmount(amount_sat=x.value), 'value': QEAmount(amount_sat=x.value),
'short_id': '', # TODO
'is_mine': self._wallet.wallet.is_mine(x.get_ui_address_str()), 'is_mine': self._wallet.wallet.is_mine(x.get_ui_address_str()),
'is_change': self._wallet.wallet.is_change(x.get_ui_address_str()), 'is_change': self._wallet.wallet.is_change(x.get_ui_address_str()),
'is_billing': self._wallet.wallet.is_billing_address(x.get_ui_address_str()) 'is_billing': self._wallet.wallet.is_billing_address(x.get_ui_address_str())

41
electrum/gui/qml/qetxfinalizer.py

@ -6,7 +6,7 @@ from PyQt6.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject
from electrum.logging import get_logger from electrum.logging import get_logger
from electrum.i18n import _ from electrum.i18n import _
from electrum.transaction import PartialTxOutput, PartialTransaction, Transaction from electrum.transaction import PartialTxOutput, PartialTransaction, Transaction, TxOutpoint
from electrum.util import NotEnoughFunds, profiler from electrum.util import NotEnoughFunds, profiler
from electrum.wallet import CannotBumpFee, CannotDoubleSpendTx, CannotCPFP, BumpFeeStrategy from electrum.wallet import CannotBumpFee, CannotDoubleSpendTx, CannotCPFP, BumpFeeStrategy
from electrum.plugin import run_hook from electrum.plugin import run_hook
@ -137,6 +137,7 @@ class TxFeeSlider(FeeSlider):
self._feeRate = '' self._feeRate = ''
self._rbf = False self._rbf = False
self._tx = None self._tx = None
self._inputs = []
self._outputs = [] self._outputs = []
self._valid = False self._valid = False
self._warning = '' self._warning = ''
@ -175,6 +176,17 @@ class TxFeeSlider(FeeSlider):
self.update() self.update()
self.rbfChanged.emit() self.rbfChanged.emit()
inputsChanged = pyqtSignal()
@pyqtProperty('QVariantList', notify=inputsChanged)
def inputs(self):
return self._inputs
@inputs.setter
def inputs(self, inputs):
if self._inputs != inputs:
self._inputs = inputs
self.inputsChanged.emit()
outputsChanged = pyqtSignal() outputsChanged = pyqtSignal()
@pyqtProperty('QVariantList', notify=outputsChanged) @pyqtProperty('QVariantList', notify=outputsChanged)
def outputs(self): def outputs(self):
@ -210,14 +222,38 @@ class TxFeeSlider(FeeSlider):
self.fee = QEAmount(amount_sat=int(fee)) self.fee = QEAmount(amount_sat=int(fee))
self.feeRate = f'{feerate:.1f}' self.feeRate = f'{feerate:.1f}'
self.update_inputs_from_tx(tx)
self.update_outputs_from_tx(tx) self.update_outputs_from_tx(tx)
def update_inputs_from_tx(self, tx):
inputs = []
for inp in tx.inputs():
# addr
# addr = self.wallet.adb.get_txin_address(txin)
addr = inp.address
address_str = '<address unknown>' if addr is None else addr
txin_value = inp.value_sats() if inp.value_sats() else 0
#self.wallet.adb.get_txin_value(txin)
inputs.append({
'address': address_str,
'short_id': str(inp.short_id),
'value': QEAmount(amount_sat=txin_value),
'is_coinbase': inp.is_coinbase_input(),
'is_mine': self._wallet.wallet.is_mine(addr),
'is_change': self._wallet.wallet.is_change(addr),
'prevout_txid': inp.prevout.txid.hex()
})
self.inputs = inputs
def update_outputs_from_tx(self, tx): def update_outputs_from_tx(self, tx):
outputs = [] outputs = []
for o in tx.outputs(): for idx, o in enumerate(tx.outputs()):
outputs.append({ outputs.append({
'address': o.get_ui_address_str(), 'address': o.get_ui_address_str(),
'value': o.value, 'value': o.value,
'short_id': str(TxOutpoint(bytes.fromhex(tx.txid()), idx).short_name()),
'is_mine': self._wallet.wallet.is_mine(o.get_ui_address_str()), 'is_mine': self._wallet.wallet.is_mine(o.get_ui_address_str()),
'is_change': self._wallet.wallet.is_change(o.get_ui_address_str()), 'is_change': self._wallet.wallet.is_change(o.get_ui_address_str()),
'is_billing': self._wallet.wallet.is_billing_address(o.get_ui_address_str()) 'is_billing': self._wallet.wallet.is_billing_address(o.get_ui_address_str())
@ -829,6 +865,7 @@ class QETxCpfpFeeBumper(TxFeeSlider, TxMonMixin):
self.warning = str(e) self.warning = str(e)
return return
self.update_inputs_from_tx(self._new_tx)
self.update_outputs_from_tx(self._new_tx) self.update_outputs_from_tx(self._new_tx)
self._valid = True self._valid = True

Loading…
Cancel
Save