diff --git a/electrum/gui/qml/components/Preferences.qml b/electrum/gui/qml/components/Preferences.qml index 461612e52..a9de2ff7e 100644 --- a/electrum/gui/qml/components/Preferences.qml +++ b/electrum/gui/qml/components/Preferences.qml @@ -35,12 +35,20 @@ Pane { ComboBox { id: baseUnit model: ['BTC','mBTC','bits','sat'] + onCurrentValueChanged: { + if (activeFocus) + Config.baseUnit = currentValue + } } CheckBox { id: thousands Layout.columnSpan: 2 text: qsTr('Add thousands separators to bitcoin amounts') + onCheckedChanged: { + if (activeFocus) + Config.thousandsSeparator = checked + } } CheckBox { @@ -50,45 +58,58 @@ Pane { enabled: false } + Label { + text: qsTr('Fiat Currency') + } + + ComboBox { + id: currencies + model: Daemon.fx.currencies + onCurrentValueChanged: { + if (activeFocus) + Daemon.fx.fiatCurrency = currentValue + } + } + CheckBox { - id: writeLogs + id: historyRates + text: qsTr('History rates') + enabled: currencies.currentValue != '' Layout.columnSpan: 2 - text: qsTr('Write logs to file') - enabled: false + onCheckStateChanged: { + if (activeFocus) + Daemon.fx.historyRates = checked + } } Label { - text: qsTr('Fiat Currency') + text: qsTr('Source') + enabled: currencies.currentValue != '' } ComboBox { - id: currencies - model: Daemon.currencies + id: rateSources + enabled: currencies.currentValue != '' + model: Daemon.fx.rateSources + onModelChanged: { + currentIndex = rateSources.indexOfValue(Daemon.fx.rateSource) + } + onCurrentValueChanged: { + if (activeFocus) + Daemon.fx.rateSource = currentValue + } } } } - RowLayout { - Layout.fillWidth: true - Layout.alignment: Qt.AlignHCenter - Button { - text: qsTr('Save') - onClicked: save() - } - } - } - - function save() { - Config.baseUnit = baseUnit.currentValue - Config.thousandsSeparator = thousands.checked - Config.fiatCurrency = currencies.currentValue ? currencies.currentValue : '' - app.stack.pop() } Component.onCompleted: { baseUnit.currentIndex = ['BTC','mBTC','bits','sat'].indexOf(Config.baseUnit) thousands.checked = Config.thousandsSeparator - currencies.currentIndex = currencies.indexOfValue(Config.fiatCurrency) + currencies.currentIndex = currencies.indexOfValue(Daemon.fx.fiatCurrency) + historyRates.checked = Daemon.fx.historyRates + rateSources.currentIndex = rateSources.indexOfValue(Daemon.fx.rateSource) } } diff --git a/electrum/gui/qml/components/Receive.qml b/electrum/gui/qml/components/Receive.qml index 65cfb676f..3a5aa2ff1 100644 --- a/electrum/gui/qml/components/Receive.qml +++ b/electrum/gui/qml/components/Receive.qml @@ -81,19 +81,19 @@ Pane { TextField { id: amountFiat - visible: Config.fiatCurrency != '' + visible: Daemon.fx.fiatCurrency != '' font.family: FixedFont Layout.fillWidth: true inputMethodHints: Qt.ImhDigitsOnly } Label { - visible: Config.fiatCurrency != '' - text: Config.fiatCurrency + visible: Daemon.fx.fiatCurrency != '' + text: Daemon.fx.fiatCurrency color: Material.accentColor } - Item { visible: Config.fiatCurrency == ''; width: 1; height: 1; Layout.columnSpan: 2 } + Item { visible: Daemon.fx.fiatCurrency == ''; width: 1; height: 1; Layout.columnSpan: 2 } RowLayout { Layout.columnSpan: 4 @@ -356,22 +356,22 @@ Pane { if (amountFiat.activeFocus) return var a = Config.unitsToSats(amount.text) - amountFiat.text = Daemon.fiatValue(a) + amountFiat.text = Daemon.fx.fiatValue(a) } } Connections { target: amountFiat function onTextChanged() { if (amountFiat.activeFocus) { - amount.text = Daemon.satoshiValue(amountFiat.text) + amount.text = Daemon.fx.satoshiValue(amountFiat.text) } } } Connections { - target: Network - function onFiatUpdated() { + target: Daemon.fx + function onQuotesUpdated() { var a = Config.unitsToSats(amount.text) - amountFiat.text = Daemon.fiatValue(a) + amountFiat.text = Daemon.fx.fiatValue(a) } } diff --git a/electrum/gui/qml/components/Send.qml b/electrum/gui/qml/components/Send.qml index 0da854df6..4083cd41a 100644 --- a/electrum/gui/qml/components/Send.qml +++ b/electrum/gui/qml/components/Send.qml @@ -52,19 +52,19 @@ Pane { TextField { id: amountFiat - visible: Config.fiatCurrency != '' + visible: Daemon.fx.fiatCurrency != '' font.family: FixedFont placeholderText: qsTr('Amount') inputMethodHints: Qt.ImhPreferNumbers } Label { - visible: Config.fiatCurrency != '' - text: Config.fiatCurrency + visible: Daemon.fx.fiatCurrency != '' + text: Daemon.fx.fiatCurrency color: Material.accentColor } - Item { visible: Config.fiatCurrency == ''; height: 1; Layout.columnSpan: 2; Layout.fillWidth: true } + Item { visible: Daemon.fx.fiatCurrency == ''; height: 1; Layout.columnSpan: 2; Layout.fillWidth: true } Item { width: 1; height: 1 } // workaround colspan on baseunit messing up row above @@ -115,22 +115,22 @@ Pane { if (amountFiat.activeFocus) return var a = Config.unitsToSats(amount.text) - amountFiat.text = Daemon.fiatValue(a) + amountFiat.text = Daemon.fx.fiatValue(a) } } Connections { target: amountFiat function onTextChanged() { if (amountFiat.activeFocus) { - amount.text = Daemon.satoshiValue(amountFiat.text) + amount.text = Daemon.fx.satoshiValue(amountFiat.text) } } } Connections { - target: Network - function onFiatUpdated() { + target: Daemon.fx + function onQuotesUpdated() { var a = Config.unitsToSats(amount.text) - amountFiat.text = Daemon.fiatValue(a) + amountFiat.text = Daemon.fx.fiatValue(a) } } diff --git a/electrum/gui/qml/qeapp.py b/electrum/gui/qml/qeapp.py index 056f5f874..0f209bfc7 100644 --- a/electrum/gui/qml/qeapp.py +++ b/electrum/gui/qml/qeapp.py @@ -16,6 +16,7 @@ from .qewallet import QEWallet from .qeqr import QEQRParser, QEQRImageProvider from .qewalletdb import QEWalletDB from .qebitcoin import QEBitcoin +from .qefx import QEFX class QEAppController(QObject): userNotify = pyqtSignal(str) @@ -83,7 +84,6 @@ class ElectrumQmlApplication(QGuiApplication): self.logger = get_logger(__name__) - #ElectrumQmlApplication._config = config ElectrumQmlApplication._daemon = daemon qmlRegisterType(QEWalletListModel, 'org.electrum', 1, 0, 'WalletListModel') @@ -91,6 +91,7 @@ class ElectrumQmlApplication(QGuiApplication): qmlRegisterType(QEWalletDB, 'org.electrum', 1, 0, 'WalletDB') qmlRegisterType(QEBitcoin, 'org.electrum', 1, 0, 'Bitcoin') qmlRegisterType(QEQRParser, 'org.electrum', 1, 0, 'QRParser') + qmlRegisterType(QEFX, 'org.electrum', 1, 0, 'FX') self.engine = QQmlApplicationEngine(parent=self) self.engine.addImportPath('./qml') @@ -121,8 +122,6 @@ class ElectrumQmlApplication(QGuiApplication): 'protocol_version': version.PROTOCOL_VERSION }) - self._qeconfig.fiatCurrencyChanged.connect(self._qedaemon.setFiatCurrency) - qInstallMessageHandler(self.message_handler) # get notified whether root QML document loads or not diff --git a/electrum/gui/qml/qeconfig.py b/electrum/gui/qml/qeconfig.py index ec9c27904..a8c973a05 100644 --- a/electrum/gui/qml/qeconfig.py +++ b/electrum/gui/qml/qeconfig.py @@ -68,16 +68,6 @@ class QEConfig(QObject): self.config.amt_add_thousands_sep = checked self.thousandsSeparatorChanged.emit() - fiatCurrencyChanged = pyqtSignal() - @pyqtProperty(str, notify=fiatCurrencyChanged) - def fiatCurrency(self): - return self.config.get('currency') - - @fiatCurrency.setter - def fiatCurrency(self, currency): - self.config.set_key('currency', currency) - self.fiatCurrencyChanged.emit() - @pyqtSlot('qint64', result=str) @pyqtSlot('qint64', bool, result=str) def formatSats(self, satoshis, with_unit=False): diff --git a/electrum/gui/qml/qedaemon.py b/electrum/gui/qml/qedaemon.py index c2688c44f..c8c66191f 100644 --- a/electrum/gui/qml/qedaemon.py +++ b/electrum/gui/qml/qedaemon.py @@ -8,9 +8,9 @@ from electrum.util import register_callback, get_new_wallet_name, WalletFileExce from electrum.logging import get_logger from electrum.wallet import Wallet, Abstract_Wallet from electrum.storage import WalletStorage, StorageReadWriteError -from electrum.bitcoin import COIN from .qewallet import QEWallet +from .qefx import QEFX # wallet list model. supports both wallet basenames (wallet file basenames) # and whole Wallet instances (loaded wallets) @@ -86,6 +86,7 @@ class QEDaemon(QObject): def __init__(self, daemon, parent=None): super().__init__(parent) self.daemon = daemon + self.qefx = QEFX(daemon.fx, daemon.config) _logger = get_logger(__name__) _loaded_wallets = QEWalletListModel() @@ -98,7 +99,7 @@ class QEDaemon(QObject): activeWalletsChanged = pyqtSignal() availableWalletsChanged = pyqtSignal() walletOpenError = pyqtSignal([str], arguments=["error"]) - currenciesChanged = pyqtSignal() + fxChanged = pyqtSignal() @pyqtSlot() @pyqtSlot(str) @@ -135,31 +136,6 @@ class QEDaemon(QObject): self._logger.error(str(e)) self.walletOpenError.emit(str(e)) - @pyqtSlot(str, result=str) - def fiatValue(self, satoshis): - rate = self.daemon.fx.exchange_rate() - try: - sd = Decimal(satoshis) - if sd == 0: - return '' - except: - return '' - return self.daemon.fx.value_str(satoshis,rate) - - # TODO: move conversion to FxThread - @pyqtSlot(str, result=str) - def satoshiValue(self, fiat): - rate = self.daemon.fx.exchange_rate() - try: - fd = Decimal(fiat) - except: - return '' - v = fd / Decimal(rate) * COIN - return '' if v.is_nan() else self.daemon.config.format_amount(v) - - @pyqtSlot() - def setFiatCurrency(self): - self.daemon.fx.set_currency(self.daemon.config.get('currency')) @pyqtProperty('QString') def path(self): @@ -180,6 +156,7 @@ class QEDaemon(QObject): return self._available_wallets - @pyqtProperty('QVariantList', notify=currenciesChanged) - def currencies(self): - return [''] + self.daemon.fx.get_currencies(False) + @pyqtProperty(QEFX, notify=fxChanged) + def fx(self): + return self.qefx + diff --git a/electrum/gui/qml/qefx.py b/electrum/gui/qml/qefx.py new file mode 100644 index 000000000..08b98a69d --- /dev/null +++ b/electrum/gui/qml/qefx.py @@ -0,0 +1,107 @@ +from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject + +from decimal import Decimal + +from electrum.logging import get_logger +from electrum.exchange_rate import FxThread +from electrum.simple_config import SimpleConfig +from electrum.util import register_callback +from electrum.bitcoin import COIN + +class QEFX(QObject): + def __init__(self, fxthread: FxThread, config: SimpleConfig, parent=None): + super().__init__(parent) + self.fx = fxthread + self.config = config + register_callback(self.on_quotes, ['on_quotes']) + register_callback(self.on_history, ['on_history']) + + _logger = get_logger(__name__) + + quotesUpdated = pyqtSignal() + def on_quotes(self, event, *args): + self._logger.debug('new quotes') + self.quotesUpdated.emit() + + historyUpdated = pyqtSignal() + def on_history(self, event, *args): + self._logger.debug('new history') + self.historyUpdated.emit() + + currenciesChanged = pyqtSignal() + @pyqtProperty('QVariantList', notify=currenciesChanged) + def currencies(self): + return [''] + self.fx.get_currencies(self.historyRates) + + rateSourcesChanged = pyqtSignal() + @pyqtProperty('QVariantList', notify=rateSourcesChanged) + def rateSources(self): + return self.fx.get_exchanges_by_ccy(self.fiatCurrency, self.historyRates) + + fiatCurrencyChanged = pyqtSignal() + @pyqtProperty(str, notify=fiatCurrencyChanged) + def fiatCurrency(self): + return self.fx.get_currency() + + @fiatCurrency.setter + def fiatCurrency(self, currency): + if currency != self.fiatCurrency: + self.fx.set_currency(currency) + self.enabled = currency != '' + self.fiatCurrencyChanged.emit() + self.rateSourcesChanged.emit() + + historyRatesChanged = pyqtSignal() + @pyqtProperty(bool, notify=historyRatesChanged) + def historyRates(self): + return self.fx.get_history_config() + + @historyRates.setter + def historyRates(self, checked): + if checked != self.historyRates: + self.fx.set_history_config(checked) + self.historyRatesChanged.emit() + self.rateSourcesChanged.emit() + + rateSourceChanged = pyqtSignal() + @pyqtProperty(str, notify=rateSourceChanged) + def rateSource(self): + return self.fx.config_exchange() + + @rateSource.setter + def rateSource(self, source): + if source != self.rateSource: + self.fx.set_exchange(source) + self.rateSourceChanged.emit() + + enabledChanged = pyqtSignal() + @pyqtProperty(bool, notify=enabledChanged) + def enabled(self): + return self.fx.is_enabled() + + @enabled.setter + def enabled(self, enable): + if enable != self.enabled: + self.fx.set_enabled(enable) + self.enabledChanged.emit() + + @pyqtSlot(str, result=str) + def fiatValue(self, satoshis): + rate = self.fx.exchange_rate() + try: + sd = Decimal(satoshis) + if sd == 0: + return '' + except: + return '' + return self.fx.value_str(satoshis,rate) + + @pyqtSlot(str, result=str) + def satoshiValue(self, fiat): + rate = self.fx.exchange_rate() + try: + fd = Decimal(fiat) + except: + return '' + v = fd / Decimal(rate) * COIN + return '' if v.is_nan() else self.config.format_amount(v) diff --git a/electrum/gui/qml/qenetwork.py b/electrum/gui/qml/qenetwork.py index 18eeb95f1..3522185e9 100644 --- a/electrum/gui/qml/qenetwork.py +++ b/electrum/gui/qml/qenetwork.py @@ -15,7 +15,6 @@ class QENetwork(QObject): register_callback(self.on_proxy_set, ['proxy_set']) register_callback(self.on_status, ['status']) register_callback(self.on_fee_histogram, ['fee_histogram']) - register_callback(self.on_fiat, ['on_quotes','on_history']) _logger = get_logger(__name__) @@ -27,7 +26,6 @@ class QENetwork(QObject): proxyChanged = pyqtSignal() statusChanged = pyqtSignal() feeHistogramUpdated = pyqtSignal() - fiatUpdated = pyqtSignal() # shared signal for static properties dataChanged = pyqtSignal() @@ -62,10 +60,6 @@ class QENetwork(QObject): self._logger.debug('fee histogram updated') self.feeHistogramUpdated.emit() - def on_fiat(self, event, *args): - self._logger.debug('new fiat quotes') - self.fiatUpdated.emit() - @pyqtProperty(int,notify=heightChanged) def height(self): return self._height