qml: add workarounds for issue assigning custom types to QObject properties

- on the python side, for pyqtProperty's with a setter, the pyqtProperty should be declared as QVariant type
- on the qml side, properties should be declared 'var', not the custom type.
This commit is contained in:
Sander van Grieken
2026-02-16 14:57:30 +01:00
parent dba6b751cc
commit 9772a6d5b6
24 changed files with 86 additions and 59 deletions
@@ -23,7 +23,7 @@ ElDialog {
property string channelBackup
function reset() {
function resetDialog() {
state = ''
errorText.text = ''
peerText.text = ''
@@ -11,7 +11,7 @@ ElDialog {
id: dialog
required property QtObject finalizer
required property Amount satoshis
required property var satoshis // type: Amount
property string address
property string message
property bool showOptions: true
@@ -31,6 +31,7 @@ Item {
property color darkerBackground: Qt.darker(Material.background, 1.20)
property color lighterBackground: Qt.lighter(Material.background, 1.10)
property color darkerDialogBackground: Qt.darker(Material.dialogColor, 1.20)
property color dialogColor: Material.dialogColor
property color notificationBackground: Qt.lighter(Material.background, 1.5)
property color colorCredit: "#ff80ff80"
@@ -10,7 +10,7 @@ import "controls"
ElDialog {
id: dialog
property Invoice invoice
property var invoice // type Invoice
property bool payImmediately: false
property string broadcastTxid
@@ -24,7 +24,7 @@ ElDialog {
property bool _canMax: invoice.invoiceType == Invoice.OnchainInvoice
property Amount _invoice_amount: invoice.amount
property var _invoice_amount: invoice.amount // type: Amount
ColumnLayout {
anchors.fill: parent
@@ -13,7 +13,7 @@ ElDialog {
title: qsTr('LNURL Payment request')
iconSource: '../../../icons/link.png'
property InvoiceParser invoiceParser
property var invoiceParser // type: InvoiceParser
padding: 0
needsSystemBarPadding: false
@@ -13,8 +13,8 @@ ElDialog {
title: qsTr('LNURL Withdraw request')
iconSource: '../../../icons/link.png'
property Wallet wallet: Daemon.currentWallet
property RequestDetails requestDetails
property var wallet: Daemon.currentWallet // type: Wallet
property var requestDetails // type: RequestDetails
padding: 0
needsSystemBarPadding: false
@@ -298,7 +298,7 @@ ElDialog {
}
onChannelOpening: (peer) => {
console.log('Channel is opening')
app.channelOpenProgressDialog.reset()
app.channelOpenProgressDialog.resetDialog()
app.channelOpenProgressDialog.peer = peer
app.channelOpenProgressDialog.open()
}
+2 -2
View File
@@ -11,8 +11,8 @@ import "controls"
ElDialog {
id: dialog
property InvoiceParser invoiceParser
property PIResolver piResolver
property var invoiceParser // type: InvoiceParser
property var piResolver // type: PIResolver
signal txFound(data: string)
signal channelBackupFound(data: string)
@@ -2,6 +2,7 @@ import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import QtQuick.Controls.Material
import QtQuick.Controls.Material.impl
import QtQml
import org.electrum 1.0
@@ -16,7 +16,7 @@ TextField {
regularExpression: msatPrecision ? Config.btcAmountRegexMsat : Config.btcAmountRegex
}
property Amount textAsSats
property var textAsSats
onTextChanged: {
textAsSats = Config.unitsToSats(amount.text)
if (fiatfield.activeFocus)
@@ -6,11 +6,11 @@ import QtQuick.Controls.Material
import org.electrum 1.0
Item {
property Amount capacity
property Amount localCapacity
property Amount remoteCapacity
property Amount canSend
property Amount canReceive
property var capacity // type: Amount
property var localCapacity // type: Amount
property var remoteCapacity // type: Amount
property var canSend // type: Amount
property var canReceive // type: Amount
property bool frozenForSending: false
property bool frozenForReceiving: false
@@ -6,7 +6,7 @@ import QtQuick.Controls.Material
import org.electrum 1.0
GridLayout {
required property Amount amount
required property var amount // type: Amount
property bool showAlt: true
property bool singleLine: true
property bool valid: true
+1 -1
View File
@@ -128,7 +128,7 @@ ApplicationWindow
background: Rectangle {
implicitHeight: 48
color: Material.dialogColor
color: constants.dialogColor
layer.enabled: true
layer.effect: ElevationEffect {
+3 -2
View File
@@ -1,4 +1,4 @@
from PyQt6.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject
from PyQt6.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject, QVariant
from electrum.logging import get_logger
from electrum.util import UserFacingException
@@ -35,12 +35,13 @@ class QEAddressDetails(AuthMixin, QObject):
self._historyModel = None
walletChanged = pyqtSignal()
@pyqtProperty(QEWallet, notify=walletChanged)
@pyqtProperty(QVariant, notify=walletChanged)
def wallet(self):
return self._wallet
@wallet.setter
def wallet(self, wallet: QEWallet):
assert isinstance(wallet, QEWallet)
if self._wallet != wallet:
self._wallet = wallet
self.walletChanged.emit()
+3 -2
View File
@@ -2,7 +2,7 @@ import threading
from enum import IntEnum
from typing import Optional, TYPE_CHECKING
from PyQt6.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject, pyqtEnum
from PyQt6.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject, pyqtEnum, QVariant
from electrum.i18n import _
from electrum.gui import messages
@@ -61,12 +61,13 @@ class QEChannelDetails(AuthMixin, QObject, QtEventListener):
self.unregister_callbacks()
walletChanged = pyqtSignal()
@pyqtProperty(QEWallet, notify=walletChanged)
@pyqtProperty(QVariant, notify=walletChanged)
def wallet(self) -> QEWallet:
return self._wallet
@wallet.setter
def wallet(self, wallet: QEWallet):
assert wallet is None or isinstance(wallet, QEWallet)
if self._wallet != wallet:
self._wallet = wallet
self.walletChanged.emit()
+7 -3
View File
@@ -1,5 +1,6 @@
from PyQt6.QtCore import Qt, QAbstractListModel, QModelIndex
from PyQt6.QtCore import pyqtProperty, pyqtSignal, pyqtSlot
from typing import TYPE_CHECKING
from PyQt6.QtCore import Qt, QAbstractListModel, QModelIndex, pyqtProperty, pyqtSignal, pyqtSlot
from electrum.lnchannel import ChannelState
from electrum.lnutil import LOCAL, REMOTE
@@ -12,6 +13,9 @@ from electrum.gui.common_qt.util import qt_event_listener, QtEventListener
from .qetypes import QEAmount
from .qemodelfilter import QEFilterProxyModel
if TYPE_CHECKING:
from electrum.wallet import Abstract_Wallet
class QEChannelListModel(QAbstractListModel, QtEventListener):
_logger = get_logger(__name__)
@@ -27,7 +31,7 @@ class QEChannelListModel(QAbstractListModel, QtEventListener):
_network_signal = pyqtSignal(str, object)
def __init__(self, wallet, parent=None):
def __init__(self, wallet: 'Abstract_Wallet', parent=None):
super().__init__(parent)
self.wallet = wallet
self._channels = []
+5 -4
View File
@@ -4,7 +4,7 @@ from asyncio.exceptions import TimeoutError
from typing import Optional
import electrum_ecc as ecc
from PyQt6.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject
from PyQt6.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject, QVariant
from electrum.i18n import _
from electrum.gui import messages
@@ -14,7 +14,6 @@ from electrum.lntransport import extract_nodeid, ConnStringFormatError
from electrum.bitcoin import DummyAddress
from electrum.lnworker import hardcoded_trampoline_nodes
from electrum.logging import get_logger
from electrum.fee_policy import FeePolicy
from electrum.transaction import PartialTransaction
from .auth import AuthMixin, auth_protect
@@ -55,12 +54,13 @@ class QEChannelOpener(QObject, AuthMixin):
self._updating_max = False
walletChanged = pyqtSignal()
@pyqtProperty(QEWallet, notify=walletChanged)
@pyqtProperty(QVariant, notify=walletChanged)
def wallet(self):
return self._wallet
@wallet.setter
def wallet(self, wallet: QEWallet):
assert wallet is None or isinstance(wallet, QEWallet)
if self._wallet != wallet:
self._wallet = wallet
self.walletChanged.emit()
@@ -79,12 +79,13 @@ class QEChannelOpener(QObject, AuthMixin):
self.validate()
amountChanged = pyqtSignal()
@pyqtProperty(QEAmount, notify=amountChanged)
@pyqtProperty(QVariant, notify=amountChanged)
def amount(self):
return self._amount
@amount.setter
def amount(self, amount: QEAmount):
assert amount is None or isinstance(amount, QEAmount)
if self._amount != amount:
self._amount.copyFrom(amount)
self.amountChanged.emit()
+5 -3
View File
@@ -4,7 +4,7 @@ from enum import IntEnum
from typing import Optional, Dict, Any, Tuple
from urllib.parse import urlparse
from PyQt6.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject, pyqtEnum, QTimer
from PyQt6.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject, pyqtEnum, QTimer, QVariant
from electrum.i18n import _
from electrum.logging import get_logger
@@ -111,12 +111,13 @@ class QEInvoice(QObject, QtEventListener):
self.determine_can_pay()
walletChanged = pyqtSignal()
@pyqtProperty(QEWallet, notify=walletChanged)
@pyqtProperty(QVariant, notify=walletChanged)
def wallet(self):
return self._wallet
@wallet.setter
def wallet(self, wallet: QEWallet):
assert wallet is None or isinstance(wallet, QEWallet)
if self._wallet != wallet:
self._wallet = wallet
self.walletChanged.emit()
@@ -153,12 +154,13 @@ class QEInvoice(QObject, QtEventListener):
self._amount.copyFrom(QEAmount(from_invoice=self._effectiveInvoice))
return self._amount
@pyqtProperty(QEAmount, notify=amountOverrideChanged)
@pyqtProperty(QVariant, notify=amountOverrideChanged)
def amountOverride(self):
return self._amountOverride
@amountOverride.setter
def amountOverride(self, new_amount: QEAmount):
assert new_amount is None or isinstance(new_amount, QEAmount)
self._logger.debug(f'set new override amount {repr(new_amount)}')
self._amountOverride.copyFrom(new_amount)
self.amountOverrideChanged.emit()
+3 -2
View File
@@ -1,4 +1,4 @@
from PyQt6.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject
from PyQt6.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject, QVariant
from electrum.logging import get_logger
from electrum.util import bfh, format_time
@@ -27,12 +27,13 @@ class QELnPaymentDetails(QObject):
self._preimage = ''
walletChanged = pyqtSignal()
@pyqtProperty(QEWallet, notify=walletChanged)
@pyqtProperty(QVariant, notify=walletChanged)
def wallet(self):
return self._wallet
@wallet.setter
def wallet(self, wallet: QEWallet):
assert wallet is None or isinstance(wallet, QEWallet)
if self._wallet != wallet:
self._wallet = wallet
self.walletChanged.emit()
+3 -2
View File
@@ -1,7 +1,7 @@
from enum import IntEnum
from typing import Optional
from PyQt6.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject, QTimer
from PyQt6.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject, QTimer, QVariant
from electrum.logging import get_logger
from electrum.i18n import _
@@ -51,12 +51,13 @@ class QEPIResolver(QObject):
self.invoiceResolved.emit(self._pi)
walletChanged = pyqtSignal()
@pyqtProperty(QEWallet, notify=walletChanged)
@pyqtProperty(QVariant, notify=walletChanged)
def wallet(self) -> Optional[QEWallet]:
return self._wallet
@wallet.setter
def wallet(self, wallet: QEWallet) -> None:
assert wallet is None or isinstance(wallet, QEWallet)
self._wallet = wallet
@pyqtProperty(bool, notify=busyChanged)
+3 -2
View File
@@ -2,7 +2,7 @@ from enum import IntEnum
from typing import Optional
from urllib.parse import urlparse
from PyQt6.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject, QTimer, pyqtEnum
from PyQt6.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject, QTimer, pyqtEnum, QVariant
from electrum.logging import get_logger
from electrum.invoices import (
@@ -74,12 +74,13 @@ class QERequestDetails(QObject, QtEventListener):
self.statusChanged.emit()
walletChanged = pyqtSignal()
@pyqtProperty(QEWallet, notify=walletChanged)
@pyqtProperty(QVariant, notify=walletChanged)
def wallet(self):
return self._wallet
@wallet.setter
def wallet(self, wallet: QEWallet):
assert wallet is None or isinstance(wallet, QEWallet)
if self._wallet != wallet:
self._wallet = wallet
self.walletChanged.emit()
+11 -6
View File
@@ -4,7 +4,7 @@ from enum import IntEnum
from typing import Union, Optional, TYPE_CHECKING, Sequence
from PyQt6.QtCore import (pyqtProperty, pyqtSignal, pyqtSlot, QObject, QTimer, pyqtEnum, QAbstractListModel, Qt,
QModelIndex)
QModelIndex, QVariant)
from PyQt6.QtGui import QColor
from electrum.i18n import _
@@ -205,12 +205,13 @@ class QESwapHelper(AuthMixin, QObject, QtEventListener):
self.unregister_callbacks()
walletChanged = pyqtSignal()
@pyqtProperty(QEWallet, notify=walletChanged)
@pyqtProperty(QVariant, notify=walletChanged)
def wallet(self):
return self._wallet
@wallet.setter
def wallet(self, wallet: QEWallet):
assert wallet is None or isinstance(wallet, QEWallet)
if self._wallet != wallet:
self._wallet = wallet
self.run_swap_manager()
@@ -294,34 +295,37 @@ class QESwapHelper(AuthMixin, QObject, QtEventListener):
self.userinfoChanged.emit()
tosendChanged = pyqtSignal()
@pyqtProperty(QEAmount, notify=tosendChanged)
@pyqtProperty(QVariant, notify=tosendChanged)
def tosend(self):
return self._tosend
@tosend.setter
def tosend(self, tosend):
assert tosend is None or isinstance(tosend, QEAmount)
if self._tosend != tosend:
self._tosend = tosend
self.tosendChanged.emit()
toreceiveChanged = pyqtSignal()
@pyqtProperty(QEAmount, notify=toreceiveChanged)
@pyqtProperty(QVariant, notify=toreceiveChanged)
def toreceive(self):
return self._toreceive
@toreceive.setter
def toreceive(self, toreceive):
assert toreceive is None or isinstance(toreceive, QEAmount)
if self._toreceive != toreceive:
self._toreceive = toreceive
self.toreceiveChanged.emit()
serverMiningfeeChanged = pyqtSignal()
@pyqtProperty(QEAmount, notify=serverMiningfeeChanged)
@pyqtProperty(QVariant, notify=serverMiningfeeChanged)
def serverMiningfee(self):
return self._server_miningfee
@serverMiningfee.setter
def serverMiningfee(self, server_miningfee):
assert server_miningfee is None or isinstance(server_miningfee, QEAmount)
if self._server_miningfee != server_miningfee:
self._server_miningfee = server_miningfee
self.serverMiningfeeChanged.emit()
@@ -338,12 +342,13 @@ class QESwapHelper(AuthMixin, QObject, QtEventListener):
self.serverfeepercChanged.emit()
miningfeeChanged = pyqtSignal()
@pyqtProperty(QEAmount, notify=miningfeeChanged)
@pyqtProperty(QVariant, notify=miningfeeChanged)
def miningfee(self):
return self._miningfee
@miningfee.setter
def miningfee(self, miningfee):
assert miningfee is None or isinstance(miningfee, QEAmount)
if self._miningfee != miningfee:
self._miningfee = miningfee
self.miningfeeChanged.emit()
+3 -2
View File
@@ -1,6 +1,6 @@
from typing import Optional
from PyQt6.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject
from PyQt6.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject, QVariant
from electrum.i18n import _
from electrum.logging import get_logger
@@ -103,12 +103,13 @@ class QETxDetails(QObject, QtEventListener):
self.update()
walletChanged = pyqtSignal()
@pyqtProperty(QEWallet, notify=walletChanged)
@pyqtProperty(QVariant, notify=walletChanged)
def wallet(self):
return self._wallet
@wallet.setter
def wallet(self, wallet: QEWallet):
assert wallet is None or isinstance(wallet, QEWallet)
if self._wallet != wallet:
self._wallet = wallet
self.walletChanged.emit()
+20 -13
View File
@@ -5,7 +5,7 @@ from decimal import Decimal
from typing import Optional, TYPE_CHECKING, Callable
from functools import partial
from PyQt6.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject, pyqtEnum
from PyQt6.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject, pyqtEnum, QVariant
from electrum.logging import get_logger
from electrum.i18n import _
@@ -68,12 +68,13 @@ class FeeSlider(QObject):
self._config = None # type: Optional[SimpleConfig]
walletChanged = pyqtSignal()
@pyqtProperty(QEWallet, notify=walletChanged)
@pyqtProperty(QVariant, notify=walletChanged)
def wallet(self):
return self._wallet
@wallet.setter
def wallet(self, wallet: QEWallet):
assert wallet is None or isinstance(wallet, QEWallet)
if self._wallet != wallet:
self._wallet = wallet
self._config = self._wallet.wallet.config
@@ -170,12 +171,13 @@ class TxFeeSlider(FeeSlider):
self._warning = ''
feeChanged = pyqtSignal()
@pyqtProperty(QEAmount, notify=feeChanged)
@pyqtProperty(QVariant, notify=feeChanged)
def fee(self):
return self._fee
@fee.setter
def fee(self, fee):
assert fee is None or isinstance(fee, QEAmount)
if self._fee != fee:
self._fee.copyFrom(fee)
self.feeChanged.emit()
@@ -419,12 +421,13 @@ class QETxFinalizer(TxFeeSlider):
self.addressChanged.emit()
amountChanged = pyqtSignal()
@pyqtProperty(QEAmount, notify=amountChanged)
@pyqtProperty(QVariant, notify=amountChanged)
def amount(self):
return self._amount
@amount.setter
def amount(self, amount):
def amount(self, amount: QEAmount):
assert amount is None or isinstance(amount, QEAmount)
if self._amount != amount:
self._logger.debug(str(amount))
self._amount.copyFrom(amount)
@@ -436,12 +439,13 @@ class QETxFinalizer(TxFeeSlider):
return self._effectiveAmount
extraFeeChanged = pyqtSignal()
@pyqtProperty(QEAmount, notify=extraFeeChanged)
@pyqtProperty(QVariant, notify=extraFeeChanged)
def extraFee(self):
return self._extraFee
@extraFee.setter
def extraFee(self, extrafee):
def extraFee(self, extrafee: QEAmount):
assert extrafee is None or isinstance(extrafee, QEAmount)
if self._extraFee != extrafee:
self._extraFee.copyFrom(extrafee)
self.extraFeeChanged.emit()
@@ -666,12 +670,13 @@ class QETxRbfFeeBumper(TxFeeSlider, TxMonMixin):
self._bump_methods_available = []
oldfeeChanged = pyqtSignal()
@pyqtProperty(QEAmount, notify=oldfeeChanged)
@pyqtProperty(QVariant, notify=oldfeeChanged)
def oldfee(self):
return self._oldfee
@oldfee.setter
def oldfee(self, oldfee):
def oldfee(self, oldfee: QEAmount):
assert oldfee is None or isinstance(oldfee, QEAmount)
if self._oldfee != oldfee:
self._oldfee.copyFrom(oldfee)
self.oldfeeChanged.emit()
@@ -806,12 +811,13 @@ class QETxCanceller(TxFeeSlider, TxMonMixin):
self._rbf = True
oldfeeChanged = pyqtSignal()
@pyqtProperty(QEAmount, notify=oldfeeChanged)
@pyqtProperty(QVariant, notify=oldfeeChanged)
def oldfee(self):
return self._oldfee
@oldfee.setter
def oldfee(self, oldfee):
def oldfee(self, oldfee: QEAmount):
assert oldfee is None or isinstance(oldfee, QEAmount)
if self._oldfee != oldfee:
self._oldfee.copyFrom(oldfee)
self.oldfeeChanged.emit()
@@ -938,12 +944,13 @@ class QETxCpfpFeeBumper(TxFeeSlider, TxMonMixin):
self._rbf = True
totalFeeChanged = pyqtSignal()
@pyqtProperty(QEAmount, notify=totalFeeChanged)
@pyqtProperty(QVariant, notify=totalFeeChanged)
def totalFee(self):
return self._total_fee
@totalFee.setter
def totalFee(self, totalfee):
def totalFee(self, totalfee: QEAmount):
assert totalfee is None or isinstance(totalfee, QEAmount)
if self._total_fee != totalfee:
self._total_fee.copyFrom(totalfee)
self.totalFeeChanged.emit()