Updates for exchange_rate plugin
Have AmountEdit return an int if is_int, otherwise a Decimal Set the tray tooltip unconditionally. More verbose logging for exchage_rate plugin. Get rate_float from Coindesk as rate can have commas. Plugin tracks windows itself, and doesn't create its own members in the window objects. Clean up the edit handling.
This commit is contained in:
parent
95d3f6da1f
commit
34ce6d1821
|
@ -63,10 +63,9 @@ class AmountEdit(MyLineEdit):
|
||||||
|
|
||||||
def get_amount(self):
|
def get_amount(self):
|
||||||
try:
|
try:
|
||||||
x = int(str(self.text()))
|
return (int if self.is_int else Decimal)(str(self.text()))
|
||||||
except:
|
except:
|
||||||
return None
|
return None
|
||||||
return x
|
|
||||||
|
|
||||||
|
|
||||||
class BTCAmountEdit(AmountEdit):
|
class BTCAmountEdit(AmountEdit):
|
||||||
|
|
|
@ -15,7 +15,6 @@ from electrum import util
|
||||||
import seed_dialog
|
import seed_dialog
|
||||||
from network_dialog import NetworkDialog
|
from network_dialog import NetworkDialog
|
||||||
from util import *
|
from util import *
|
||||||
from amountedit import AmountEdit
|
|
||||||
|
|
||||||
from electrum.plugins import always_hook, run_hook
|
from electrum.plugins import always_hook, run_hook
|
||||||
from electrum.mnemonic import prepare_seed
|
from electrum.mnemonic import prepare_seed
|
||||||
|
|
|
@ -45,7 +45,7 @@ from electrum import SimpleConfig, Wallet, WalletStorage
|
||||||
from electrum import Imported_Wallet
|
from electrum import Imported_Wallet
|
||||||
from electrum import paymentrequest
|
from electrum import paymentrequest
|
||||||
|
|
||||||
from amountedit import AmountEdit, BTCAmountEdit, MyLineEdit, BTCkBEdit
|
from amountedit import BTCAmountEdit, MyLineEdit, BTCkBEdit
|
||||||
from network_dialog import NetworkDialog
|
from network_dialog import NetworkDialog
|
||||||
from qrcodewidget import QRCodeWidget, QRDialog
|
from qrcodewidget import QRCodeWidget, QRDialog
|
||||||
from qrtextedit import ScanQRTextEdit, ShowQRTextEdit
|
from qrtextedit import ScanQRTextEdit, ShowQRTextEdit
|
||||||
|
@ -523,14 +523,12 @@ class ElectrumWindow(QMainWindow):
|
||||||
quote = r.get(0)
|
quote = r.get(0)
|
||||||
if quote:
|
if quote:
|
||||||
text += "%s"%quote
|
text += "%s"%quote
|
||||||
|
|
||||||
if self.tray:
|
|
||||||
self.tray.setToolTip("%s (%s)" % (text, self.wallet.basename()))
|
|
||||||
icon = QIcon(":icons/status_connected.png")
|
icon = QIcon(":icons/status_connected.png")
|
||||||
else:
|
else:
|
||||||
text = _("Not connected")
|
text = _("Not connected")
|
||||||
icon = QIcon(":icons/status_disconnected.png")
|
icon = QIcon(":icons/status_disconnected.png")
|
||||||
|
|
||||||
|
self.tray.setToolTip("%s (%s)" % (text, self.wallet.basename()))
|
||||||
self.balance_label.setText(text)
|
self.balance_label.setText(text)
|
||||||
self.status_button.setIcon( icon )
|
self.status_button.setIcon( icon )
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@ from threading import Thread
|
||||||
import time
|
import time
|
||||||
import traceback
|
import traceback
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
|
from functools import partial
|
||||||
|
|
||||||
from electrum.bitcoin import COIN
|
from electrum.bitcoin import COIN
|
||||||
from electrum.plugins import BasePlugin, hook
|
from electrum.plugins import BasePlugin, hook
|
||||||
|
@ -36,7 +37,9 @@ class ExchangeBase:
|
||||||
return self.__class__.__name__
|
return self.__class__.__name__
|
||||||
|
|
||||||
def update(self, ccy):
|
def update(self, ccy):
|
||||||
|
self.print_error("getting fx quotes for", ccy)
|
||||||
self.quotes = self.get_rates(ccy)
|
self.quotes = self.get_rates(ccy)
|
||||||
|
self.print_error("received fx quotes")
|
||||||
self.sig.emit(SIGNAL('fx_quotes'))
|
self.sig.emit(SIGNAL('fx_quotes'))
|
||||||
return self.quotes
|
return self.quotes
|
||||||
|
|
||||||
|
@ -46,6 +49,7 @@ class ExchangeBase:
|
||||||
def set_history(self, ccy, history):
|
def set_history(self, ccy, history):
|
||||||
'''History is a map of "%Y-%m-%d" strings to values'''
|
'''History is a map of "%Y-%m-%d" strings to values'''
|
||||||
self.history[ccy] = history
|
self.history[ccy] = history
|
||||||
|
self.print_error("received fx history for", ccy)
|
||||||
self.sig.emit(SIGNAL("fx_history"))
|
self.sig.emit(SIGNAL("fx_history"))
|
||||||
|
|
||||||
def get_historical_rates(self, ccy):
|
def get_historical_rates(self, ccy):
|
||||||
|
@ -131,7 +135,7 @@ class CoinDesk(ExchangeBase):
|
||||||
'/v1/bpi/currentprice/%s.json' % ccy)
|
'/v1/bpi/currentprice/%s.json' % ccy)
|
||||||
ccys = [d['currency'] for d in dicts]
|
ccys = [d['currency'] for d in dicts]
|
||||||
result = dict.fromkeys(ccys)
|
result = dict.fromkeys(ccys)
|
||||||
result[ccy] = Decimal(json['bpi'][ccy]['rate'])
|
result[ccy] = Decimal(json['bpi'][ccy]['rate_float'])
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def history_starts(self):
|
def history_starts(self):
|
||||||
|
@ -191,6 +195,7 @@ class Plugin(BasePlugin, ThreadJob):
|
||||||
self.history_used_spot = False
|
self.history_used_spot = False
|
||||||
self.ccy_combo = None
|
self.ccy_combo = None
|
||||||
self.hist_checkbox = None
|
self.hist_checkbox = None
|
||||||
|
self.windows = dict()
|
||||||
|
|
||||||
is_exchange = lambda obj: (inspect.isclass(obj)
|
is_exchange = lambda obj: (inspect.isclass(obj)
|
||||||
and issubclass(obj, ExchangeBase)
|
and issubclass(obj, ExchangeBase)
|
||||||
|
@ -206,10 +211,9 @@ class Plugin(BasePlugin, ThreadJob):
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
# This runs from the network thread which catches exceptions
|
# This runs from the network thread which catches exceptions
|
||||||
if self.parent.windows and self.timeout <= time.time():
|
if self.windows and self.timeout <= time.time():
|
||||||
self.timeout = time.time() + 150
|
self.timeout = time.time() + 150
|
||||||
rates = self.exchange.update(self.ccy)
|
self.exchange.update(self.ccy)
|
||||||
self.refresh_fields()
|
|
||||||
|
|
||||||
def config_ccy(self):
|
def config_ccy(self):
|
||||||
'''Use when dynamic fetching is needed'''
|
'''Use when dynamic fetching is needed'''
|
||||||
|
@ -236,24 +240,81 @@ class Plugin(BasePlugin, ThreadJob):
|
||||||
|
|
||||||
def update_status_bars(self):
|
def update_status_bars(self):
|
||||||
'''Update status bar fiat balance in all windows'''
|
'''Update status bar fiat balance in all windows'''
|
||||||
for window in self.parent.windows:
|
for window in self.windows:
|
||||||
window.update_status()
|
window.update_status()
|
||||||
|
|
||||||
def on_new_window(self, window):
|
def on_new_window(self, window):
|
||||||
window.fx_fields = {}
|
# Additional send and receive edit boxes
|
||||||
self.add_send_edit(window)
|
send_e = AmountEdit(self.config_ccy)
|
||||||
self.add_receive_edit(window)
|
window.send_grid.addWidget(send_e, 4, 3, Qt.AlignHCenter)
|
||||||
|
window.amount_e.frozen.connect(
|
||||||
|
lambda: send_e.setFrozen(window.amount_e.isReadOnly()))
|
||||||
|
receive_e = AmountEdit(self.config_ccy)
|
||||||
|
window.receive_grid.addWidget(receive_e, 2, 3, Qt.AlignHCenter)
|
||||||
|
|
||||||
|
self.windows[window] = {'edits': (send_e, receive_e),
|
||||||
|
'last_edited': {}}
|
||||||
|
self.connect_fields(window, window.amount_e, send_e, window.fee_e)
|
||||||
|
self.connect_fields(window, window.receive_amount_e, receive_e, None)
|
||||||
|
window.update_status()
|
||||||
|
|
||||||
|
def connect_fields(self, window, btc_e, fiat_e, fee_e):
|
||||||
|
last_edited = self.windows[window]['last_edited']
|
||||||
|
|
||||||
|
def edit_changed(edit):
|
||||||
|
edit.setStyleSheet(BLACK_FG)
|
||||||
|
last_edited[(fiat_e, btc_e)] = edit
|
||||||
|
amount = edit.get_amount()
|
||||||
|
rate = self.exchange_rate()
|
||||||
|
if rate is None or amount is None:
|
||||||
|
if edit is fiat_e:
|
||||||
|
btc_e.setText("")
|
||||||
|
if fee_e:
|
||||||
|
fee_e.setText("")
|
||||||
|
else:
|
||||||
|
fiat_e.setText("")
|
||||||
|
else:
|
||||||
|
if edit is fiat_e:
|
||||||
|
btc_e.setAmount(int(amount / Decimal(rate) * COIN))
|
||||||
|
if fee_e: window.update_fee()
|
||||||
|
btc_e.setStyleSheet(BLUE_FG)
|
||||||
|
else:
|
||||||
|
fiat_e.setText("%.2f" % (amount * Decimal(rate) / COIN))
|
||||||
|
fiat_e.setStyleSheet(BLUE_FG)
|
||||||
|
|
||||||
|
fiat_e.textEdited.connect(partial(edit_changed, fiat_e))
|
||||||
|
btc_e.textEdited.connect(partial(edit_changed, btc_e))
|
||||||
|
last_edited[(fiat_e, btc_e)] = btc_e
|
||||||
|
|
||||||
|
@hook
|
||||||
|
def do_clear(self, window):
|
||||||
|
self.windows[window]['edits'][0].setText('')
|
||||||
|
|
||||||
|
def on_close_window(self, window):
|
||||||
|
self.windows.pop(window)
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
# Get rid of hooks before updating status bars.
|
||||||
|
BasePlugin.close(self)
|
||||||
|
self.update_status_bars()
|
||||||
|
for window, data in self.windows.items():
|
||||||
|
for edit in data['edits']:
|
||||||
|
edit.hide()
|
||||||
window.update_status()
|
window.update_status()
|
||||||
|
|
||||||
def on_fx_history(self):
|
def on_fx_history(self):
|
||||||
'''Called when historical fx quotes are updated'''
|
'''Called when historical fx quotes are updated'''
|
||||||
for window in self.parent.windows:
|
for window in self.windows:
|
||||||
window.update_history_tab()
|
window.update_history_tab()
|
||||||
|
|
||||||
def on_fx_quotes(self):
|
def on_fx_quotes(self):
|
||||||
'''Called when fresh spot fx quotes come in'''
|
'''Called when fresh spot fx quotes come in'''
|
||||||
self.update_status_bars()
|
self.update_status_bars()
|
||||||
self.populate_ccy_combo()
|
self.populate_ccy_combo()
|
||||||
|
# Refresh edits with the new rate
|
||||||
|
for window, data in self.windows.items():
|
||||||
|
for edit in data['last_edited'].values():
|
||||||
|
edit.textEdited.emit(edit.text())
|
||||||
# History tab needs updating if it used spot
|
# History tab needs updating if it used spot
|
||||||
if self.history_used_spot:
|
if self.history_used_spot:
|
||||||
self.on_fx_history()
|
self.on_fx_history()
|
||||||
|
@ -284,14 +345,6 @@ class Plugin(BasePlugin, ThreadJob):
|
||||||
combo.blockSignals(False)
|
combo.blockSignals(False)
|
||||||
combo.setCurrentIndex(combo.findText(self.ccy))
|
combo.setCurrentIndex(combo.findText(self.ccy))
|
||||||
|
|
||||||
def close(self):
|
|
||||||
BasePlugin.close(self)
|
|
||||||
for window in self.parent.windows:
|
|
||||||
window.send_fiat_e.hide()
|
|
||||||
window.receive_fiat_e.hide()
|
|
||||||
window.update_history_tab()
|
|
||||||
window.update_status()
|
|
||||||
|
|
||||||
def exchange_rate(self):
|
def exchange_rate(self):
|
||||||
'''Returns None, or the exchange rate as a Decimal'''
|
'''Returns None, or the exchange rate as a Decimal'''
|
||||||
rate = self.exchange.quotes.get(self.ccy)
|
rate = self.exchange.quotes.get(self.ccy)
|
||||||
|
@ -405,9 +458,9 @@ class Plugin(BasePlugin, ThreadJob):
|
||||||
self.config.set_key('history_rates', 'unchecked')
|
self.config.set_key('history_rates', 'unchecked')
|
||||||
|
|
||||||
def ok_clicked():
|
def ok_clicked():
|
||||||
if self.exchange in ["CoinDesk", "itBit"]:
|
|
||||||
self.timeout = 0
|
self.timeout = 0
|
||||||
d.accept();
|
self.ccy_combo = None
|
||||||
|
d.accept()
|
||||||
|
|
||||||
combo_ex = QComboBox()
|
combo_ex = QComboBox()
|
||||||
combo_ex.addItems(sorted(self.exchanges.keys()))
|
combo_ex.addItems(sorted(self.exchanges.keys()))
|
||||||
|
@ -426,66 +479,4 @@ class Plugin(BasePlugin, ThreadJob):
|
||||||
layout.addWidget(self.hist_checkbox,2,1)
|
layout.addWidget(self.hist_checkbox,2,1)
|
||||||
layout.addWidget(ok_button,3,1)
|
layout.addWidget(ok_button,3,1)
|
||||||
|
|
||||||
result = d.exec_()
|
return d.exec_()
|
||||||
self.ccy_combo = None
|
|
||||||
return result
|
|
||||||
|
|
||||||
def refresh_fields(self):
|
|
||||||
'''Update the display at the new rate'''
|
|
||||||
for window in self.parent.windows:
|
|
||||||
for field in window.fx_fields.values():
|
|
||||||
field.textEdited.emit(field.text())
|
|
||||||
|
|
||||||
def add_send_edit(self, window):
|
|
||||||
window.send_fiat_e = AmountEdit(self.config_ccy)
|
|
||||||
self.connect_fields(window, True)
|
|
||||||
window.send_grid.addWidget(window.send_fiat_e, 4, 3, Qt.AlignHCenter)
|
|
||||||
window.amount_e.frozen.connect(lambda: window.send_fiat_e.setFrozen(window.amount_e.isReadOnly()))
|
|
||||||
|
|
||||||
def add_receive_edit(self, window):
|
|
||||||
window.receive_fiat_e = AmountEdit(self.config_ccy)
|
|
||||||
self.connect_fields(window, False)
|
|
||||||
window.receive_grid.addWidget(window.receive_fiat_e, 2, 3, Qt.AlignHCenter)
|
|
||||||
|
|
||||||
def connect_fields(self, window, send):
|
|
||||||
if send:
|
|
||||||
btc_e, fiat_e, fee_e = (window.amount_e, window.send_fiat_e,
|
|
||||||
window.fee_e)
|
|
||||||
else:
|
|
||||||
btc_e, fiat_e, fee_e = (window.receive_amount_e,
|
|
||||||
window.receive_fiat_e, None)
|
|
||||||
def fiat_changed():
|
|
||||||
fiat_e.setStyleSheet(BLACK_FG)
|
|
||||||
window.fx_fields[(fiat_e, btc_e)] = fiat_e
|
|
||||||
try:
|
|
||||||
fiat_amount = Decimal(str(fiat_e.text()))
|
|
||||||
except:
|
|
||||||
btc_e.setText("")
|
|
||||||
if fee_e: fee_e.setText("")
|
|
||||||
return
|
|
||||||
exchange_rate = self.exchange_rate()
|
|
||||||
if exchange_rate is not None:
|
|
||||||
btc_amount = fiat_amount/exchange_rate
|
|
||||||
btc_e.setAmount(int(btc_amount*Decimal(COIN)))
|
|
||||||
btc_e.setStyleSheet(BLUE_FG)
|
|
||||||
if fee_e: window.update_fee()
|
|
||||||
fiat_e.textEdited.connect(fiat_changed)
|
|
||||||
def btc_changed():
|
|
||||||
btc_e.setStyleSheet(BLACK_FG)
|
|
||||||
window.fx_fields[(fiat_e, btc_e)] = btc_e
|
|
||||||
btc_amount = btc_e.get_amount()
|
|
||||||
rate = self.exchange_rate()
|
|
||||||
if rate is None or btc_amount is None:
|
|
||||||
fiat_e.setText("")
|
|
||||||
else:
|
|
||||||
fiat_amount = rate * Decimal(btc_amount) / Decimal(COIN)
|
|
||||||
pos = fiat_e.cursorPosition()
|
|
||||||
fiat_e.setText("%.2f"%fiat_amount)
|
|
||||||
fiat_e.setCursorPosition(pos)
|
|
||||||
fiat_e.setStyleSheet(BLUE_FG)
|
|
||||||
btc_e.textEdited.connect(btc_changed)
|
|
||||||
window.fx_fields[(fiat_e, btc_e)] = btc_e
|
|
||||||
|
|
||||||
@hook
|
|
||||||
def do_clear(self, window):
|
|
||||||
window.send_fiat_e.setText('')
|
|
||||||
|
|
Loading…
Reference in New Issue