353 lines
13 KiB
Python
353 lines
13 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 .qetypes import QEAmount
|
|
from .auth import AuthMixin, auth_protect
|
|
|
|
if TYPE_CHECKING:
|
|
from electrum.simple_config import SimpleConfig
|
|
|
|
|
|
class QEConfig(AuthMixin, QObject):
|
|
_logger = get_logger(__name__)
|
|
|
|
def __init__(self, config: 'SimpleConfig', parent=None):
|
|
super().__init__(parent)
|
|
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)
|
|
return cv.get_long_desc() if cv else ''
|
|
|
|
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
|
|
|
|
autoConnectChanged = pyqtSignal()
|
|
@pyqtProperty(bool, notify=autoConnectChanged)
|
|
def autoConnect(self):
|
|
return self.config.NETWORK_AUTO_CONNECT
|
|
|
|
@autoConnect.setter
|
|
def autoConnect(self, auto_connect):
|
|
self.config.NETWORK_AUTO_CONNECT = auto_connect
|
|
self.autoConnectChanged.emit()
|
|
|
|
# auto_connect is actually a tri-state, expose the undefined case
|
|
@pyqtProperty(bool, notify=autoConnectChanged)
|
|
def autoConnectDefined(self):
|
|
return self.config.cv.NETWORK_AUTO_CONNECT.is_set()
|
|
|
|
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("BTC") - 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()
|
|
|
|
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()
|
|
|
|
useFallbackAddressChanged = pyqtSignal()
|
|
@pyqtProperty(bool, notify=useFallbackAddressChanged)
|
|
def useFallbackAddress(self):
|
|
return self.config.WALLET_BOLT11_FALLBACK
|
|
|
|
@useFallbackAddress.setter
|
|
def useFallbackAddress(self, use_fallback):
|
|
self.config.WALLET_BOLT11_FALLBACK = use_fallback
|
|
self.useFallbackAddressChanged.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()
|
|
|
|
@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)
|