Update plot labels and configuration calculations to use PLM instead of BTC to reflect the current currency being used.
391 lines
15 KiB
Python
391 lines
15 KiB
Python
import copy
|
|
from decimal import Decimal
|
|
from typing import TYPE_CHECKING
|
|
|
|
from PyQt6.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject, QRegularExpression
|
|
|
|
from electrum.bitcoin import TOTAL_COIN_SUPPLY_LIMIT_IN_BTC
|
|
from electrum.i18n import set_language, languages
|
|
from electrum.logging import get_logger
|
|
from electrum.util import base_unit_name_to_decimal_point
|
|
from electrum.gui import messages
|
|
|
|
from .qetypes import QEAmount
|
|
from .auth import AuthMixin, auth_protect
|
|
|
|
if TYPE_CHECKING:
|
|
from electrum.simple_config import SimpleConfig
|
|
|
|
|
|
class QEConfig(AuthMixin, QObject):
|
|
instance = None # type: Optional[QEConfig]
|
|
_logger = get_logger(__name__)
|
|
|
|
def __init__(self, config: 'SimpleConfig', parent=None):
|
|
super().__init__(parent)
|
|
if QEConfig.instance:
|
|
raise RuntimeError('There should only be one QEConfig instance')
|
|
QEConfig.instance = self
|
|
self.config = config
|
|
|
|
@pyqtSlot(str, result=str)
|
|
def shortDescFor(self, key) -> str:
|
|
cv = getattr(self.config.cv, key)
|
|
return cv.get_short_desc() if cv else ''
|
|
|
|
@pyqtSlot(str, result=str)
|
|
def longDescFor(self, key) -> str:
|
|
cv = getattr(self.config.cv, key)
|
|
if not cv:
|
|
return ""
|
|
desc = cv.get_long_desc()
|
|
return messages.to_rtf(desc)
|
|
|
|
@pyqtSlot(str, result=str)
|
|
def getTranslatedMessage(self, key) -> str:
|
|
return getattr(messages, key)
|
|
|
|
languageChanged = pyqtSignal()
|
|
@pyqtProperty(str, notify=languageChanged)
|
|
def language(self):
|
|
return self.config.LOCALIZATION_LANGUAGE
|
|
|
|
@language.setter
|
|
def language(self, language):
|
|
if language not in languages:
|
|
return
|
|
if self.config.LOCALIZATION_LANGUAGE != language:
|
|
self.config.LOCALIZATION_LANGUAGE = language
|
|
set_language(language)
|
|
self.languageChanged.emit()
|
|
|
|
languagesChanged = pyqtSignal()
|
|
@pyqtProperty('QVariantList', notify=languagesChanged)
|
|
def languagesAvailable(self):
|
|
# sort on translated languages, then re-add Default on top
|
|
langs = copy.deepcopy(languages)
|
|
default = langs.pop('')
|
|
langs_sorted = sorted(list(map(lambda x: {'value': x[0], 'text': x[1]}, langs.items())), key=lambda x: x['text'])
|
|
langs_sorted.insert(0, {'value': '', 'text': default})
|
|
return langs_sorted
|
|
|
|
termsOfUseChanged = pyqtSignal()
|
|
@pyqtProperty(bool, notify=termsOfUseChanged)
|
|
def termsOfUseAccepted(self) -> bool:
|
|
return self.config.TERMS_OF_USE_ACCEPTED >= messages.TERMS_OF_USE_LATEST_VERSION
|
|
|
|
@termsOfUseAccepted.setter
|
|
def termsOfUseAccepted(self, accepted: bool) -> None:
|
|
if accepted:
|
|
self.config.TERMS_OF_USE_ACCEPTED = messages.TERMS_OF_USE_LATEST_VERSION
|
|
else:
|
|
self.config.TERMS_OF_USE_ACCEPTED = 0
|
|
self.termsOfUseChanged.emit()
|
|
|
|
baseUnitChanged = pyqtSignal()
|
|
@pyqtProperty(str, notify=baseUnitChanged)
|
|
def baseUnit(self):
|
|
return self.config.get_base_unit()
|
|
|
|
@baseUnit.setter
|
|
def baseUnit(self, unit):
|
|
self.config.set_base_unit(unit)
|
|
self.baseUnitChanged.emit()
|
|
|
|
@pyqtProperty('QRegularExpression', notify=baseUnitChanged)
|
|
def btcAmountRegex(self):
|
|
decimal_point = base_unit_name_to_decimal_point(self.config.get_base_unit())
|
|
max_digits_before_dp = (
|
|
len(str(TOTAL_COIN_SUPPLY_LIMIT_IN_BTC))
|
|
+ (base_unit_name_to_decimal_point("PLM") - decimal_point))
|
|
exp = '[0-9]{0,%d}' % max_digits_before_dp
|
|
if decimal_point > 0:
|
|
exp += '\\.'
|
|
exp += '[0-9]{0,%d}' % decimal_point
|
|
return QRegularExpression(exp)
|
|
|
|
thousandsSeparatorChanged = pyqtSignal()
|
|
@pyqtProperty(bool, notify=thousandsSeparatorChanged)
|
|
def thousandsSeparator(self):
|
|
return self.config.BTC_AMOUNTS_ADD_THOUSANDS_SEP
|
|
|
|
@thousandsSeparator.setter
|
|
def thousandsSeparator(self, checked):
|
|
self.config.BTC_AMOUNTS_ADD_THOUSANDS_SEP = checked
|
|
self.config.amt_add_thousands_sep = checked
|
|
self.thousandsSeparatorChanged.emit()
|
|
|
|
spendUnconfirmedChanged = pyqtSignal()
|
|
@pyqtProperty(bool, notify=spendUnconfirmedChanged)
|
|
def spendUnconfirmed(self):
|
|
return not self.config.WALLET_SPEND_CONFIRMED_ONLY
|
|
|
|
@spendUnconfirmed.setter
|
|
def spendUnconfirmed(self, checked):
|
|
self.config.WALLET_SPEND_CONFIRMED_ONLY = not checked
|
|
self.spendUnconfirmedChanged.emit()
|
|
|
|
freezeReusedAddressUtxosChanged = pyqtSignal()
|
|
@pyqtProperty(bool, notify=freezeReusedAddressUtxosChanged)
|
|
def freezeReusedAddressUtxos(self):
|
|
return self.config.WALLET_FREEZE_REUSED_ADDRESS_UTXOS
|
|
|
|
@freezeReusedAddressUtxos.setter
|
|
def freezeReusedAddressUtxos(self, checked):
|
|
self.config.WALLET_FREEZE_REUSED_ADDRESS_UTXOS = checked
|
|
self.freezeReusedAddressUtxosChanged.emit()
|
|
|
|
requestExpiryChanged = pyqtSignal()
|
|
@pyqtProperty(int, notify=requestExpiryChanged)
|
|
def requestExpiry(self):
|
|
return self.config.WALLET_PAYREQ_EXPIRY_SECONDS
|
|
|
|
@requestExpiry.setter
|
|
def requestExpiry(self, expiry):
|
|
self.config.WALLET_PAYREQ_EXPIRY_SECONDS = expiry
|
|
self.requestExpiryChanged.emit()
|
|
|
|
pinCodeChanged = pyqtSignal()
|
|
@pyqtProperty(str, notify=pinCodeChanged)
|
|
def pinCode(self):
|
|
return self.config.CONFIG_PIN_CODE or ""
|
|
|
|
@pinCode.setter
|
|
def pinCode(self, pin_code):
|
|
if pin_code == '':
|
|
self.pinCodeRemoveAuth()
|
|
else:
|
|
self.config.CONFIG_PIN_CODE = pin_code
|
|
self.pinCodeChanged.emit()
|
|
|
|
@auth_protect(method='wallet_else_pin')
|
|
def pinCodeRemoveAuth(self):
|
|
self.config.CONFIG_PIN_CODE = ""
|
|
self.pinCodeChanged.emit()
|
|
|
|
useGossipChanged = pyqtSignal()
|
|
@pyqtProperty(bool, notify=useGossipChanged)
|
|
def useGossip(self):
|
|
return self.config.LIGHTNING_USE_GOSSIP
|
|
|
|
@useGossip.setter
|
|
def useGossip(self, gossip):
|
|
self.config.LIGHTNING_USE_GOSSIP = gossip
|
|
self.useGossipChanged.emit()
|
|
|
|
enableDebugLogsChanged = pyqtSignal()
|
|
@pyqtProperty(bool, notify=enableDebugLogsChanged)
|
|
def enableDebugLogs(self):
|
|
gui_setting = self.config.GUI_ENABLE_DEBUG_LOGS
|
|
return gui_setting or bool(self.config.get('verbosity'))
|
|
|
|
@pyqtProperty(bool, notify=enableDebugLogsChanged)
|
|
def canToggleDebugLogs(self):
|
|
gui_setting = self.config.GUI_ENABLE_DEBUG_LOGS
|
|
return not self.config.get('verbosity') or gui_setting
|
|
|
|
@enableDebugLogs.setter
|
|
def enableDebugLogs(self, enable):
|
|
self.config.GUI_ENABLE_DEBUG_LOGS = enable
|
|
self.enableDebugLogsChanged.emit()
|
|
|
|
alwaysAllowScreenshotsChanged = pyqtSignal()
|
|
@pyqtProperty(bool, notify=alwaysAllowScreenshotsChanged)
|
|
def alwaysAllowScreenshots(self):
|
|
return self.config.GUI_QML_ALWAYS_ALLOW_SCREENSHOTS
|
|
|
|
@alwaysAllowScreenshots.setter
|
|
def alwaysAllowScreenshots(self, enable):
|
|
self.config.GUI_QML_ALWAYS_ALLOW_SCREENSHOTS = enable
|
|
self.alwaysAllowScreenshotsChanged.emit()
|
|
|
|
setMaxBrightnessOnQrDisplayChanged = pyqtSignal()
|
|
@pyqtProperty(bool, notify=setMaxBrightnessOnQrDisplayChanged)
|
|
def setMaxBrightnessOnQrDisplay(self):
|
|
return self.config.GUI_QML_SET_MAX_BRIGHTNESS_ON_QR_DISPLAY
|
|
|
|
@setMaxBrightnessOnQrDisplay.setter
|
|
def setMaxBrightnessOnQrDisplay(self, enable):
|
|
self.config.GUI_QML_SET_MAX_BRIGHTNESS_ON_QR_DISPLAY = enable
|
|
|
|
useRecoverableChannelsChanged = pyqtSignal()
|
|
@pyqtProperty(bool, notify=useRecoverableChannelsChanged)
|
|
def useRecoverableChannels(self):
|
|
return self.config.LIGHTNING_USE_RECOVERABLE_CHANNELS
|
|
|
|
@useRecoverableChannels.setter
|
|
def useRecoverableChannels(self, useRecoverableChannels):
|
|
self.config.LIGHTNING_USE_RECOVERABLE_CHANNELS = useRecoverableChannels
|
|
self.useRecoverableChannelsChanged.emit()
|
|
|
|
trustedcoinPrepayChanged = pyqtSignal()
|
|
@pyqtProperty(int, notify=trustedcoinPrepayChanged)
|
|
def trustedcoinPrepay(self):
|
|
return self.config.PLUGIN_TRUSTEDCOIN_NUM_PREPAY
|
|
|
|
@trustedcoinPrepay.setter
|
|
def trustedcoinPrepay(self, num_prepay):
|
|
if num_prepay != self.config.PLUGIN_TRUSTEDCOIN_NUM_PREPAY:
|
|
self.config.PLUGIN_TRUSTEDCOIN_NUM_PREPAY = num_prepay
|
|
self.trustedcoinPrepayChanged.emit()
|
|
|
|
preferredRequestTypeChanged = pyqtSignal()
|
|
@pyqtProperty(str, notify=preferredRequestTypeChanged)
|
|
def preferredRequestType(self):
|
|
return self.config.GUI_QML_PREFERRED_REQUEST_TYPE
|
|
|
|
@preferredRequestType.setter
|
|
def preferredRequestType(self, preferred_request_type):
|
|
if preferred_request_type != self.config.GUI_QML_PREFERRED_REQUEST_TYPE:
|
|
self.config.GUI_QML_PREFERRED_REQUEST_TYPE = preferred_request_type
|
|
self.preferredRequestTypeChanged.emit()
|
|
|
|
userKnowsPressAndHoldChanged = pyqtSignal()
|
|
@pyqtProperty(bool, notify=userKnowsPressAndHoldChanged)
|
|
def userKnowsPressAndHold(self):
|
|
return self.config.GUI_QML_USER_KNOWS_PRESS_AND_HOLD
|
|
|
|
@userKnowsPressAndHold.setter
|
|
def userKnowsPressAndHold(self, userKnowsPressAndHold):
|
|
if userKnowsPressAndHold != self.config.GUI_QML_USER_KNOWS_PRESS_AND_HOLD:
|
|
self.config.GUI_QML_USER_KNOWS_PRESS_AND_HOLD = userKnowsPressAndHold
|
|
self.userKnowsPressAndHoldChanged.emit()
|
|
|
|
addresslistShowTypeChanged = pyqtSignal()
|
|
@pyqtProperty(int, notify=addresslistShowTypeChanged)
|
|
def addresslistShowType(self):
|
|
return self.config.GUI_QML_ADDRESS_LIST_SHOW_TYPE
|
|
|
|
@addresslistShowType.setter
|
|
def addresslistShowType(self, addresslistShowType):
|
|
if addresslistShowType != self.config.GUI_QML_ADDRESS_LIST_SHOW_TYPE:
|
|
self.config.GUI_QML_ADDRESS_LIST_SHOW_TYPE = addresslistShowType
|
|
self.addresslistShowTypeChanged.emit()
|
|
|
|
addresslistShowUsedChanged = pyqtSignal()
|
|
@pyqtProperty(bool, notify=addresslistShowUsedChanged)
|
|
def addresslistShowUsed(self):
|
|
return self.config.GUI_QML_ADDRESS_LIST_SHOW_USED
|
|
|
|
@addresslistShowUsed.setter
|
|
def addresslistShowUsed(self, addresslistShowUsed):
|
|
if addresslistShowUsed != self.config.GUI_QML_ADDRESS_LIST_SHOW_USED:
|
|
self.config.GUI_QML_ADDRESS_LIST_SHOW_USED = addresslistShowUsed
|
|
self.addresslistShowUsedChanged.emit()
|
|
|
|
outputValueRoundingChanged = pyqtSignal()
|
|
@pyqtProperty(bool, notify=outputValueRoundingChanged)
|
|
def outputValueRounding(self):
|
|
return self.config.WALLET_COIN_CHOOSER_OUTPUT_ROUNDING
|
|
|
|
@outputValueRounding.setter
|
|
def outputValueRounding(self, outputValueRounding):
|
|
if outputValueRounding != self.config.WALLET_COIN_CHOOSER_OUTPUT_ROUNDING:
|
|
self.config.WALLET_COIN_CHOOSER_OUTPUT_ROUNDING = outputValueRounding
|
|
self.outputValueRoundingChanged.emit()
|
|
|
|
lightningPaymentFeeMaxMillionthsChanged = pyqtSignal()
|
|
@pyqtProperty(int, notify=lightningPaymentFeeMaxMillionthsChanged)
|
|
def lightningPaymentFeeMaxMillionths(self):
|
|
return self.config.LIGHTNING_PAYMENT_FEE_MAX_MILLIONTHS
|
|
|
|
@lightningPaymentFeeMaxMillionths.setter
|
|
def lightningPaymentFeeMaxMillionths(self, lightningPaymentFeeMaxMillionths):
|
|
if lightningPaymentFeeMaxMillionths != self.config.LIGHTNING_PAYMENT_FEE_MAX_MILLIONTHS:
|
|
self.config.LIGHTNING_PAYMENT_FEE_MAX_MILLIONTHS = lightningPaymentFeeMaxMillionths
|
|
self.lightningPaymentFeeMaxMillionthsChanged.emit()
|
|
|
|
nostrRelaysChanged = pyqtSignal()
|
|
@pyqtProperty(str, notify=nostrRelaysChanged)
|
|
def nostrRelays(self):
|
|
return self.config.NOSTR_RELAYS
|
|
|
|
@nostrRelays.setter
|
|
def nostrRelays(self, nostr_relays):
|
|
if nostr_relays != self.config.NOSTR_RELAYS:
|
|
self.config.NOSTR_RELAYS = nostr_relays if nostr_relays else None
|
|
self.nostrRelaysChanged.emit()
|
|
|
|
swapServerNPubChanged = pyqtSignal()
|
|
@pyqtProperty(str, notify=swapServerNPubChanged)
|
|
def swapServerNPub(self):
|
|
return self.config.SWAPSERVER_NPUB
|
|
|
|
@swapServerNPub.setter
|
|
def swapServerNPub(self, swapserver_npub):
|
|
if swapserver_npub != self.config.SWAPSERVER_NPUB:
|
|
self.config.SWAPSERVER_NPUB = swapserver_npub
|
|
self.swapServerNPubChanged.emit()
|
|
|
|
lnUtxoReserveChanged = pyqtSignal()
|
|
@pyqtProperty(QEAmount, notify=lnUtxoReserveChanged)
|
|
def lnUtxoReserve(self):
|
|
self._lnutxoreserve = QEAmount(amount_sat=self.config.LN_UTXO_RESERVE)
|
|
return self._lnutxoreserve
|
|
|
|
@pyqtSlot('qint64', result=str)
|
|
@pyqtSlot(QEAmount, result=str)
|
|
def formatSatsForEditing(self, satoshis):
|
|
if isinstance(satoshis, QEAmount):
|
|
satoshis = satoshis.satsInt
|
|
return self.config.format_amount(
|
|
satoshis,
|
|
add_thousands_sep=False,
|
|
)
|
|
|
|
@pyqtSlot('qint64', result=str)
|
|
@pyqtSlot('qint64', bool, result=str)
|
|
@pyqtSlot(QEAmount, result=str)
|
|
@pyqtSlot(QEAmount, bool, result=str)
|
|
def formatSats(self, satoshis, with_unit=False):
|
|
if isinstance(satoshis, QEAmount):
|
|
satoshis = satoshis.satsInt
|
|
if with_unit:
|
|
return self.config.format_amount_and_units(satoshis)
|
|
else:
|
|
return self.config.format_amount(satoshis)
|
|
|
|
@pyqtSlot(QEAmount, result=str)
|
|
@pyqtSlot(QEAmount, bool, result=str)
|
|
def formatMilliSats(self, amount, with_unit=False):
|
|
if isinstance(amount, QEAmount):
|
|
msats = amount.msatsInt
|
|
else:
|
|
return '---'
|
|
precision = 3 # config.amt_precision_post_satoshi is not exposed in preferences
|
|
if with_unit:
|
|
return self.config.format_amount_and_units(msats/1000, precision=precision)
|
|
else:
|
|
return self.config.format_amount(msats/1000, precision=precision)
|
|
|
|
# TODO delegate all this to config.py/util.py
|
|
def decimal_point(self):
|
|
return self.config.BTC_AMOUNTS_DECIMAL_POINT
|
|
|
|
def max_precision(self):
|
|
return self.decimal_point() + 0 # self.extra_precision
|
|
|
|
@pyqtSlot(str, result=QEAmount)
|
|
def unitsToSats(self, unitAmount):
|
|
self._amount = QEAmount()
|
|
try:
|
|
x = Decimal(unitAmount)
|
|
except Exception:
|
|
return self._amount
|
|
|
|
# scale it to max allowed precision, make it an int
|
|
max_prec_amount = int(pow(10, self.max_precision()) * x)
|
|
# if the max precision is simply what unit conversion allows, just return
|
|
if self.max_precision() == self.decimal_point():
|
|
self._amount = QEAmount(amount_sat=max_prec_amount)
|
|
return self._amount
|
|
self._logger.debug('fallthrough')
|
|
# otherwise, scale it back to the expected unit
|
|
#amount = Decimal(max_prec_amount) / Decimal(pow(10, self.max_precision()-self.decimal_point()))
|
|
#return int(amount) #Decimal(amount) if not self.is_int else int(amount)
|
|
return self._amount
|
|
|
|
@pyqtSlot('quint64', result=float)
|
|
def satsToUnits(self, satoshis):
|
|
return satoshis / pow(10, self.config.decimal_point)
|