From 13678d9e1397613d09d961279c507f3c912ec5fd Mon Sep 17 00:00:00 2001 From: ThomasV Date: Tue, 3 Jan 2017 09:02:26 +0100 Subject: [PATCH] Merge exchange_rate plugin with main code * fixes #2037 (tab indexes) --- gui/kivy/main_window.py | 18 +- gui/kivy/uix/dialogs/fx_dialog.py | 35 +-- gui/kivy/uix/dialogs/settings.py | 8 +- gui/kivy/uix/screens.py | 5 +- gui/qt/history_list.py | 16 +- gui/qt/main_window.py | 166 ++++++++++++- lib/daemon.py | 6 +- .../exchange_rate => lib}/exchange_rate.py | 68 +++--- lib/plugins.py | 3 + plugins/exchange_rate/__init__.py | 5 - plugins/exchange_rate/kivy.py | 54 ----- plugins/exchange_rate/qt.py | 227 ------------------ 12 files changed, 246 insertions(+), 365 deletions(-) rename {plugins/exchange_rate => lib}/exchange_rate.py (90%) delete mode 100644 plugins/exchange_rate/__init__.py delete mode 100644 plugins/exchange_rate/kivy.py delete mode 100644 plugins/exchange_rate/qt.py diff --git a/gui/kivy/main_window.py b/gui/kivy/main_window.py index eb5dc8fd..a5c9906a 100644 --- a/gui/kivy/main_window.py +++ b/gui/kivy/main_window.py @@ -92,11 +92,12 @@ class ElectrumWindow(App): _.switch_lang(language) def on_quotes(self, d): - #Logger.info("on_quotes") - pass + Logger.info("on_quotes") + if self.history_screen: + Clock.schedule_once(lambda dt: self.history_screen.update()) def on_history(self, d): - #Logger.info("on_history") + Logger.info("on_history") if self.history_screen: Clock.schedule_once(lambda dt: self.history_screen.update()) @@ -124,7 +125,7 @@ class ElectrumWindow(App): def btc_to_fiat(self, amount_str): if not amount_str: return '' - rate = run_hook('exchange_rate') + rate = self.fx.exchange_rate() if not rate: return '' fiat_amount = self.get_amount(amount_str + ' ' + self.base_unit) * rate / pow(10, 8) @@ -133,7 +134,7 @@ class ElectrumWindow(App): def fiat_to_btc(self, fiat_amount): if not fiat_amount: return '' - rate = run_hook('exchange_rate') + rate = self.fx.exchange_rate() if not rate: return '' satoshis = int(pow(10,8) * Decimal(fiat_amount) / Decimal(rate)) @@ -198,6 +199,7 @@ class ElectrumWindow(App): self.gui_object = kwargs.get('gui_object', None) self.daemon = self.gui_object.daemon + self.fx = self.daemon.fx self.contacts = Contacts(self.electrum_config) self.invoices = InvoiceStore(self.electrum_config) @@ -386,6 +388,12 @@ class ElectrumWindow(App): self.load_wallet_by_name(self.electrum_config.get_wallet_path()) # init plugins run_hook('init_kivy', self) + + # fiat currency + self.fiat_unit = self.fx.ccy if self.fx.is_enabled() else '' + self.network.register_callback(self.on_quotes, ['on_quotes']) + self.network.register_callback(self.on_history, ['on_history']) + # default tab self.switch_to('history') # bind intent for bitcoin: URI scheme diff --git a/gui/kivy/uix/dialogs/fx_dialog.py b/gui/kivy/uix/dialogs/fx_dialog.py index e95a6390..a552fc38 100644 --- a/gui/kivy/uix/dialogs/fx_dialog.py +++ b/gui/kivy/uix/dialogs/fx_dialog.py @@ -86,46 +86,35 @@ class FxDialog(Factory.Popup): self.app = app self.config = config self.callback = callback - self.plugins = plugins - p = self.plugins.get('exchange_rate') - self.ids.enabled.active = bool(p) + self.fx = self.app.fx + self.ids.enabled.active = self.fx.is_enabled() def on_active(self, b): - if b: - p = self.plugins.get('exchange_rate') - if p is None: - p = self.plugins.enable('exchange_rate') - p.init_kivy(self.app) - else: - self.plugins.disable('exchange_rate') + self.fx.set_enabled(b) Clock.schedule_once(lambda dt: self.add_currencies()) def add_exchanges(self): - p = self.plugins.get('exchange_rate') - exchanges = sorted(p.exchanges_by_ccy.get(p.get_currency())) if p else [] - mx = p.exchange.name() if p else '' + exchanges = sorted(self.fx.exchanges_by_ccy.get(self.fx.get_currency())) if self.fx.is_enabled() else [] + mx = self.fx.exchange.name() if self.fx.is_enabled() else '' ex = self.ids.exchanges ex.values = exchanges - ex.text = (mx if mx in exchanges else exchanges[0]) if p else '' + ex.text = (mx if mx in exchanges else exchanges[0]) if self.fx.is_enabled() else '' def on_exchange(self, text): if not text: return - p = self.plugins.get('exchange_rate') - if p and text != p.exchange.name(): - p.set_exchange(text) + if self.fx.is_enabled() and text != self.fx.exchange.name(): + self.fx.set_exchange(text) def add_currencies(self): - p = self.plugins.get('exchange_rate') - currencies = sorted(p.exchanges_by_ccy.keys()) if p else [] - my_ccy = p.get_currency() if p else '' + currencies = sorted(self.fx.exchanges_by_ccy.keys()) if self.fx else [] + my_ccy = self.fx.get_currency() if self.fx.is_enabled() else '' self.ids.ccy.values = currencies self.ids.ccy.text = my_ccy def on_currency(self, ccy): if ccy: - p = self.plugins.get('exchange_rate') - if p and ccy != p.get_currency(): - p.set_currency(ccy) + if self.fx.is_enabled() and ccy != self.fx.get_currency(): + self.fx.set_currency(ccy) self.app.fiat_unit = ccy Clock.schedule_once(lambda dt: self.add_exchanges()) diff --git a/gui/kivy/uix/dialogs/settings.py b/gui/kivy/uix/dialogs/settings.py index 94c9ee05..dc458bcd 100644 --- a/gui/kivy/uix/dialogs/settings.py +++ b/gui/kivy/uix/dialogs/settings.py @@ -242,10 +242,10 @@ class SettingsDialog(Factory.Popup): self._rbf_dialog.open() def fx_status(self): - p = self.plugins.get('exchange_rate') - if p: - source = p.exchange.name() - ccy = p.get_currency() + fx = self.app.fx + if fx.is_enabled(): + source = fx.exchange.name() + ccy = fx.get_currency() return '%s [%s]' %(ccy, source) else: return 'Disabled' diff --git a/gui/kivy/uix/screens.py b/gui/kivy/uix/screens.py index 4b238cca..3f315c28 100644 --- a/gui/kivy/uix/screens.py +++ b/gui/kivy/uix/screens.py @@ -20,7 +20,6 @@ from kivy.utils import platform from electrum.util import profiler, parse_URI, format_time, InvalidPassword, NotEnoughFunds from electrum import bitcoin from electrum.util import timestamp_to_datetime -from electrum.plugins import run_hook from electrum.paymentrequest import PR_UNPAID, PR_PAID, PR_UNKNOWN, PR_EXPIRED from context_menu import ContextMenu @@ -148,9 +147,9 @@ class HistoryScreen(CScreen): ri.value_known = value is not None ri.confirmations = conf if self.app.fiat_unit and date: - rate = run_hook('history_rate', date) + rate = self.app.fx.history_rate(date) if rate: - s = run_hook('value_str', value, rate) + s = self.app.fx.value_str(value, rate) ri.quote_text = '' if s is None else s + ' ' + self.app.fiat_unit return ri diff --git a/gui/qt/history_list.py b/gui/qt/history_list.py index 1ac05f2f..824b05a8 100644 --- a/gui/qt/history_list.py +++ b/gui/qt/history_list.py @@ -30,6 +30,7 @@ from util import * from electrum.i18n import _ from electrum.util import block_explorer_URL, format_satoshis, format_time from electrum.plugins import run_hook +from electrum.util import timestamp_to_datetime TX_ICONS = [ @@ -55,8 +56,10 @@ class HistoryList(MyTreeWidget): self.setColumnHidden(1, True) def refresh_headers(self): + ccy = self.parent.fx.ccy headers = ['', '', _('Date'), _('Description') , _('Amount'), _('Balance')] - run_hook('history_tab_headers', headers) + if self.parent.fx.show_history(): + headers.extend(['%s '%ccy + _('Amount'), '%s '%ccy + _('Balance')]) self.update_headers(headers) def get_domain(self): @@ -69,7 +72,10 @@ class HistoryList(MyTreeWidget): item = self.currentItem() current_tx = item.data(0, Qt.UserRole).toString() if item else None self.clear() - run_hook('history_tab_update_begin') + + fx = self.parent.fx + fx.history_used_spot = False + for h_item in h: tx_hash, height, conf, timestamp, value, balance = h_item status, status_str = self.wallet.get_tx_status(tx_hash, height, conf, timestamp) @@ -78,7 +84,11 @@ class HistoryList(MyTreeWidget): balance_str = self.parent.format_amount(balance, whitespaces=True) label = self.wallet.get_label(tx_hash) entry = ['', tx_hash, status_str, label, v_str, balance_str] - run_hook('history_tab_update', h_item, entry) + if fx.show_history(): + date = timestamp_to_datetime(time.time() if conf <= 0 else timestamp) + for amount in [value, balance]: + text = fx.historical_value_str(amount, date) + entry.append(text) item = QTreeWidgetItem(entry) item.setIcon(0, icon) for i in range(len(entry)): diff --git a/gui/qt/main_window.py b/gui/qt/main_window.py index f893cd3c..b96bd6ef 100644 --- a/gui/qt/main_window.py +++ b/gui/qt/main_window.py @@ -54,7 +54,7 @@ from electrum import util, bitcoin, commands, coinchooser from electrum import SimpleConfig, paymentrequest from electrum.wallet import Wallet, Multisig_Wallet -from amountedit import BTCAmountEdit, MyLineEdit, BTCkBEdit +from amountedit import AmountEdit, BTCAmountEdit, MyLineEdit, BTCkBEdit from network_dialog import NetworkDialog from qrcodewidget import QRCodeWidget, QRDialog from qrtextedit import ShowQRTextEdit @@ -98,6 +98,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError): self.gui_object = gui_object self.config = config = gui_object.config self.network = gui_object.daemon.network + self.fx = gui_object.daemon.fx self.invoices = gui_object.invoices self.contacts = gui_object.contacts self.tray = gui_object.tray @@ -166,10 +167,36 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError): # set initial message self.console.showMessage(self.network.banner) + self.network.register_callback(self.on_quotes, ['on_quotes']) + self.network.register_callback(self.on_history, ['on_history']) + self.connect(self, SIGNAL('new_fx_quotes'), self.on_fx_quotes) + self.connect(self, SIGNAL('new_fx_history'), self.on_fx_history) + self.load_wallet(wallet) self.connect_slots(gui_object.timer) self.fetch_alias() + def on_history(self, b): + self.emit(SIGNAL('new_fx_history')) + + def on_fx_history(self): + self.history_list.refresh_headers() + self.history_list.update() + + def on_quotes(self, b): + self.emit(SIGNAL('new_fx_quotes')) + + def on_fx_quotes(self): + self.update_status() + # Refresh edits with the new rate + edit = self.fiat_send_e if self.fiat_send_e.is_last_edited else self.amount_e + edit.textEdited.emit(edit.text()) + edit = self.fiat_receive_e if self.fiat_receive_e.is_last_edited else self.receive_amount_e + edit.textEdited.emit(edit.text()) + # History tab needs updating if it used spot + if self.fx.history_used_spot: + self.history_list.update() + def toggle_addresses_tab(self): show_addr = not self.config.get('show_addresses_tab', False) self.config.set_key('show_addresses_tab', show_addr) @@ -528,7 +555,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError): def format_amount_and_units(self, amount): text = self.format_amount(amount) + ' '+ self.base_unit() - x = run_hook('format_amount_and_units', amount) + x = self.fx.format_amount_and_units(amount) if text and x: text += ' (%s)'%x return text @@ -546,6 +573,43 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError): return 'BTC' raise Exception('Unknown base unit') + def connect_fields(self, window, btc_e, fiat_e, fee_e): + + def edit_changed(edit): + if edit.follows: + return + edit.setStyleSheet(BLACK_FG) + fiat_e.is_last_edited = (edit == fiat_e) + amount = edit.get_amount() + rate = self.fx.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.follows = True + btc_e.setAmount(int(amount / Decimal(rate) * COIN)) + btc_e.setStyleSheet(BLUE_FG) + btc_e.follows = False + if fee_e: + window.update_fee() + else: + fiat_e.follows = True + fiat_e.setText(self.fx.ccy_amount_str( + amount * Decimal(rate) / COIN, False)) + fiat_e.setStyleSheet(BLUE_FG) + fiat_e.follows = False + + btc_e.follows = False + fiat_e.follows = False + fiat_e.textChanged.connect(partial(edit_changed, fiat_e)) + btc_e.textChanged.connect(partial(edit_changed, btc_e)) + fiat_e.is_last_edited = False + def update_status(self): if not self.wallet: return @@ -573,10 +637,10 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError): text += " [%s unconfirmed]"%(self.format_amount(u, True).strip()) if x: text += " [%s unmatured]"%(self.format_amount(x, True).strip()) - # append fiat balance and price from exchange rate plugin - rate = run_hook('get_fiat_status_text', c + u + x) - if rate: - text += rate + + # append fiat balance and price + if self.fx.is_enabled(): + text += self.fx.get_fiat_status_text(c + u + x) or '' icon = QIcon(":icons/status_connected.png") else: text = _("Not connected") @@ -641,6 +705,10 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError): grid.addWidget(self.receive_amount_e, 2, 1) self.receive_amount_e.textChanged.connect(self.update_receive_qr) + self.fiat_receive_e = AmountEdit(self.fx.get_currency) + grid.addWidget(self.fiat_receive_e, 2, 2, Qt.AlignLeft) + self.connect_fields(self, self.receive_amount_e, self.fiat_receive_e, None) + self.expires_combo = QComboBox() self.expires_combo.addItems(map(lambda x:x[0], expiration_values)) self.expires_combo.setCurrentIndex(1) @@ -893,6 +961,11 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError): grid.addWidget(amount_label, 4, 0) grid.addWidget(self.amount_e, 4, 1) + self.fiat_send_e = AmountEdit(self.fx.get_currency) + grid.addWidget(self.fiat_send_e, 4, 2, Qt.AlignLeft) + self.amount_e.frozen.connect( + lambda: self.fiat_send_e.setFrozen(self.amount_e.isReadOnly())) + self.max_button = EnterButton(_("Max"), self.spend_max) hbox = QHBoxLayout() hbox.addWidget(self.max_button) @@ -927,6 +1000,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError): # This is so that when the user blanks the fee and moves on, # we go back to auto-calculate mode and put a fee back. self.fee_e.editingFinished.connect(self.update_fee) + self.connect_fields(self, self.amount_e, self.fiat_send_e, self.fee_e) self.rbf_checkbox = QCheckBox(_('Replaceable')) msg = [_('If you check this box, your transaction will be marked as non-final,'), @@ -1380,7 +1454,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError): self.not_enough_funds = False self.payment_request = None self.payto_e.is_pr = False - for e in [self.payto_e, self.message_e, self.amount_e, self.fee_e]: + for e in [self.payto_e, self.message_e, self.amount_e, self.fiat_send_e, self.fee_e]: e.setText('') e.setFrozen(False) self.set_pay_from([]) @@ -2241,6 +2315,14 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError): title, msg = _('Import private keys'), _("Enter private keys") self._do_import(title, msg, lambda x: self.wallet.import_key(x, password)) + def update_fiat(self): + b = self.fx.is_enabled() + self.fiat_send_e.setVisible(b) + self.fiat_receive_e.setVisible(b) + self.history_list.refresh_headers() + self.history_list.update() + self.update_status() + def settings_dialog(self): self.need_restart = False d = WindowModalDialog(self, _('Preferences')) @@ -2490,10 +2572,73 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError): chooser_combo.currentIndexChanged.connect(on_chooser) tx_widgets.append((chooser_label, chooser_combo)) + # Fiat Currency + hist_checkbox = QCheckBox() + ccy_combo = QComboBox() + ex_combo = QComboBox() + + def update_currencies(): + currencies = sorted(self.fx.exchanges_by_ccy.keys()) + ccy_combo.clear() + ccy_combo.addItems([_('None')] + currencies) + if self.fx.is_enabled(): + ccy_combo.setCurrentIndex(ccy_combo.findText(self.fx.get_currency())) + + def update_history_cb(): + hist_checkbox.setChecked(self.fx.get_history_config()) + hist_checkbox.setEnabled(self.fx.is_enabled()) + + def update_exchanges(): + b = self.fx.is_enabled() + ex_combo.setEnabled(b) + if b: + h = self.fx.get_history_config() + c = self.fx.get_currency() + exchanges = self.fx.get_exchanges_by_ccy(c, h) + else: + exchanges = self.fx.exchanges.keys() + ex_combo.clear() + ex_combo.addItems(sorted(exchanges)) + ex_combo.setCurrentIndex(ex_combo.findText(self.fx.config_exchange())) + + def on_currency(hh): + b = bool(ccy_combo.currentIndex()) + ccy = str(ccy_combo.currentText()) if b else None + self.fx.set_enabled(b) + if b and ccy != self.fx.ccy: + self.fx.set_currency(ccy) + update_history_cb() + update_exchanges() + self.update_fiat() + + def on_exchange(idx): + exchange = str(ex_combo.currentText()) + if self.fx.is_enabled() and exchange != self.fx.exchange.name(): + self.fx.set_exchange(exchange) + + def on_history(checked): + self.fx.set_history_config(checked) + self.history_list.refresh_headers() + if self.fx.is_enabled() and checked: + self.fx.get_historical_rates() + + update_currencies() + update_history_cb() + update_exchanges() + ccy_combo.currentIndexChanged.connect(on_currency) + hist_checkbox.stateChanged.connect(on_history) + ex_combo.currentIndexChanged.connect(on_exchange) + + fiat_widgets = [] + fiat_widgets.append((QLabel(_('Fiat currency')), ccy_combo)) + fiat_widgets.append((QLabel(_('Show history rates')), hist_checkbox)) + fiat_widgets.append((QLabel(_('Source')), ex_combo)) + tabs_info = [ (fee_widgets, _('Fees')), (tx_widgets, _('Transactions')), (gui_widgets, _('Appearance')), + (fiat_widgets, _('Fiat')), (id_widgets, _('Identity')), ] for widgets, name in tabs_info: @@ -2517,12 +2662,19 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError): # run the dialog d.exec_() + + if self.fx: + self.fx.timeout = 0 + self.disconnect(self, SIGNAL('alias_received'), set_alias_color) run_hook('close_settings_dialog') if self.need_restart: self.show_warning(_('Please restart Electrum to activate the new GUI settings'), title=_('Success')) + + + def run_network_dialog(self): if not self.network: self.show_warning(_('You are using Electrum in offline mode; restart Electrum if you want to get connected'), title=_('Offline')) diff --git a/lib/daemon.py b/lib/daemon.py index a11b8fcb..22af8e47 100644 --- a/lib/daemon.py +++ b/lib/daemon.py @@ -39,6 +39,7 @@ from wallet import WalletStorage, Wallet from commands import known_commands, Commands from simple_config import SimpleConfig from plugins import run_hook +from exchange_rate import FxThread def get_lockfile(config): return os.path.join(config.path, 'daemon') @@ -100,14 +101,17 @@ class RequestHandler(SimpleJSONRPCRequestHandler): class Daemon(DaemonThread): def __init__(self, config, fd): - DaemonThread.__init__(self) self.config = config if config.get('offline'): self.network = None + self.fx = None else: self.network = Network(config) self.network.start() + self.fx = FxThread(config, self.network) + self.network.add_jobs([self.fx]) + self.gui = None self.wallets = {} # Setup JSONRPC server diff --git a/plugins/exchange_rate/exchange_rate.py b/lib/exchange_rate.py similarity index 90% rename from plugins/exchange_rate/exchange_rate.py rename to lib/exchange_rate.py index 9b6cb639..7a15b9c1 100644 --- a/plugins/exchange_rate/exchange_rate.py +++ b/lib/exchange_rate.py @@ -8,11 +8,10 @@ import traceback import csv from decimal import Decimal -from electrum.bitcoin import COIN -from electrum.plugins import BasePlugin, hook -from electrum.i18n import _ -from electrum.util import PrintError, ThreadJob -from electrum.util import format_satoshis +from bitcoin import COIN +from i18n import _ +from util import PrintError, ThreadJob +from util import format_satoshis # See https://en.wikipedia.org/wiki/ISO_4217 @@ -102,7 +101,7 @@ class BitcoinAverage(ExchangeBase): def historical_rates(self, ccy): history = self.get_csv('api.bitcoinaverage.com', "/history/%s/per_day_all_time_history.csv" % ccy) - return dict([(h['datetime'][:10], h['average']) + return dict([(h['DateTime'][:10], h['Average']) for h in history]) class BitcoinVenezuela(ExchangeBase): @@ -298,10 +297,11 @@ def get_exchanges_by_ccy(): -class FxPlugin(BasePlugin, ThreadJob): +class FxThread(ThreadJob): - def __init__(self, parent, config, name): - BasePlugin.__init__(self, parent, config, name) + def __init__(self, config, network): + self.config = config + self.network = network self.ccy = self.get_currency() self.history_used_spot = False self.ccy_combo = None @@ -310,19 +310,34 @@ class FxPlugin(BasePlugin, ThreadJob): self.exchanges_by_ccy = get_exchanges_by_ccy() self.set_exchange(self.config_exchange()) + def get_exchanges_by_ccy(self, ccy, h): + return self.exchanges_by_ccy.get(ccy) + def ccy_amount_str(self, amount, commas): prec = CCY_PRECISIONS.get(self.ccy, 2) fmt_str = "{:%s.%df}" % ("," if commas else "", max(0, prec)) return fmt_str.format(round(amount, prec)) - def thread_jobs(self): - return [self] - def run(self): # This runs from the plugins thread which catches exceptions - if self.timeout <= time.time(): - self.timeout = time.time() + 150 - self.exchange.update(self.ccy) + if self.is_enabled(): + if self.timeout ==0 and self.show_history(): + self.exchange.get_historical_rates(self.ccy) + if self.timeout <= time.time(): + self.timeout = time.time() + 150 + self.exchange.update(self.ccy) + + def is_enabled(self): + return bool(self.config.get('use_exchange_rate')) + + def set_enabled(self, b): + return self.config.set_key('use_exchange_rate', bool(b)) + + def get_history_config(self): + return bool(self.config.get('history_rates')) + + def set_history_config(self, b): + self.config.set_key('history_rates', bool(b)) def get_currency(self): '''Use when dynamic fetching is needed''' @@ -332,12 +347,12 @@ class FxPlugin(BasePlugin, ThreadJob): return self.config.get('use_exchange', 'BitcoinAverage') def show_history(self): - return self.ccy in self.exchange.history_ccys() + return self.is_enabled() and self.get_history_config() and self.ccy in self.exchange.history_ccys() def set_currency(self, ccy): self.ccy = ccy self.config.set_key('currency', ccy, True) - self.get_historical_rates() # Because self.ccy changes + self.timeout = 0 # Because self.ccy changes self.on_quotes() def set_exchange(self, name): @@ -351,39 +366,28 @@ class FxPlugin(BasePlugin, ThreadJob): # A new exchange means new fx quotes, initially empty. Force # a quote refresh self.timeout = 0 - self.get_historical_rates() def on_quotes(self): - pass + self.network.trigger_callback('on_quotes') def on_history(self): - pass + self.network.trigger_callback('on_history') - @hook def exchange_rate(self): '''Returns None, or the exchange rate as a Decimal''' rate = self.exchange.quotes.get(self.ccy) if rate: return Decimal(rate) - @hook def format_amount_and_units(self, btc_balance): rate = self.exchange_rate() return '' if rate is None else "%s %s" % (self.value_str(btc_balance, rate), self.ccy) - @hook def get_fiat_status_text(self, btc_balance): rate = self.exchange_rate() - return _(" (No FX rate available)") if rate is None else "1 BTC~%s %s" % (self.value_str(COIN, rate), self.ccy) + return _(" (No FX rate available)") if rate is None else " 1 BTC~%s %s" % (self.value_str(COIN, rate), self.ccy) - def get_historical_rates(self): - if self.show_history(): - self.exchange.get_historical_rates(self.ccy) - def requires_settings(self): - return True - - @hook def value_str(self, satoshis, rate): if satoshis is None: # Can happen with incomplete history return _("Unknown") @@ -392,7 +396,6 @@ class FxPlugin(BasePlugin, ThreadJob): return "%s" % (self.ccy_amount_str(value, True)) return _("No data") - @hook def history_rate(self, d_t): rate = self.exchange.historical_rate(self.ccy, d_t) # Frequently there is no rate for today, until tomorrow :) @@ -402,7 +405,6 @@ class FxPlugin(BasePlugin, ThreadJob): self.history_used_spot = True return rate - @hook def historical_value_str(self, satoshis, d_t): rate = self.history_rate(d_t) return self.value_str(satoshis, rate) diff --git a/lib/plugins.py b/lib/plugins.py index 9cbdd186..e8d54321 100644 --- a/lib/plugins.py +++ b/lib/plugins.py @@ -63,6 +63,9 @@ class Plugins(DaemonThread): def load_plugins(self): for loader, name, ispkg in pkgutil.iter_modules([self.pkgpath]): + # do not load deprecated plugins + if name in ['plot', 'exchange_rate']: + continue m = loader.find_module(name).load_module(name) d = m.__dict__ gui_good = self.gui_name in d.get('available_for', []) diff --git a/plugins/exchange_rate/__init__.py b/plugins/exchange_rate/__init__.py deleted file mode 100644 index 7e41e72d..00000000 --- a/plugins/exchange_rate/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -from electrum.i18n import _ - -fullname = _("Exchange rates") -description = _("Exchange rates and currency conversion tools.") -available_for = ['qt','kivy'] diff --git a/plugins/exchange_rate/kivy.py b/plugins/exchange_rate/kivy.py deleted file mode 100644 index 659a078e..00000000 --- a/plugins/exchange_rate/kivy.py +++ /dev/null @@ -1,54 +0,0 @@ -from __future__ import absolute_import - -from .exchange_rate import FxPlugin -from electrum.plugins import hook - - -from kivy.event import EventDispatcher - -class MyEventDispatcher(EventDispatcher): - - def __init__(self, **kwargs): - self.register_event_type('on_quotes') - self.register_event_type('on_history') - super(MyEventDispatcher, self).__init__(**kwargs) - - def on_quotes(self, *args): - pass - - def on_history(self, *args): - pass - - -class Plugin(FxPlugin): - - def __init__(self, parent, config, name): - FxPlugin.__init__(self, parent, config, name) - self.dispatcher = MyEventDispatcher() - - def on_quotes(self): - self.print_error("on_quotes", self.ccy) - self.dispatcher.dispatch('on_quotes') - - def on_history(self): - self.print_error("on_history", self.ccy) - self.dispatcher.dispatch('on_history') - - def on_close(self): - self.print_error("on close") - self.window.fiat_unit = '' - self.window.history_screen.update() - - @hook - def init_kivy(self, window): - self.print_error("init_kivy") - self.window = window - self.dispatcher.bind(on_quotes=window.on_quotes) - self.dispatcher.bind(on_history=window.on_history) - self.window.fiat_unit = self.ccy - self.dispatcher.dispatch('on_history') - - @hook - def load_wallet(self, wallet, window): - self.window = window - self.window.fiat_unit = self.ccy diff --git a/plugins/exchange_rate/qt.py b/plugins/exchange_rate/qt.py deleted file mode 100644 index 7571fe22..00000000 --- a/plugins/exchange_rate/qt.py +++ /dev/null @@ -1,227 +0,0 @@ -import time -from PyQt4.QtGui import * -from PyQt4.QtCore import * -from electrum_gui.qt.util import * -from electrum_gui.qt.amountedit import AmountEdit - - -from electrum.bitcoin import COIN -from electrum.i18n import _ -from decimal import Decimal -from functools import partial -from electrum.plugins import hook -from exchange_rate import FxPlugin -from electrum.util import timestamp_to_datetime - -class Plugin(FxPlugin, QObject): - - def __init__(self, parent, config, name): - FxPlugin.__init__(self, parent, config, name) - QObject.__init__(self) - - def connect_fields(self, window, btc_e, fiat_e, fee_e): - - def edit_changed(edit): - if edit.follows: - return - edit.setStyleSheet(BLACK_FG) - fiat_e.is_last_edited = (edit == fiat_e) - 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.follows = True - btc_e.setAmount(int(amount / Decimal(rate) * COIN)) - btc_e.setStyleSheet(BLUE_FG) - btc_e.follows = False - if fee_e: - window.update_fee() - else: - fiat_e.follows = True - fiat_e.setText(self.ccy_amount_str( - amount * Decimal(rate) / COIN, False)) - fiat_e.setStyleSheet(BLUE_FG) - fiat_e.follows = False - - btc_e.follows = False - fiat_e.follows = False - fiat_e.textChanged.connect(partial(edit_changed, fiat_e)) - btc_e.textChanged.connect(partial(edit_changed, btc_e)) - fiat_e.is_last_edited = False - - @hook - def init_qt(self, gui): - for window in gui.windows: - self.on_new_window(window) - - @hook - def do_clear(self, window): - window.fiat_send_e.setText('') - - def on_close(self): - self.emit(SIGNAL('close_fx_plugin')) - - def restore_window(self, window): - window.update_status() - window.history_list.refresh_headers() - window.fiat_send_e.hide() - window.fiat_receive_e.hide() - - def on_quotes(self): - self.emit(SIGNAL('new_fx_quotes')) - - def on_history(self): - self.emit(SIGNAL('new_fx_history')) - - def on_fx_history(self, window): - '''Called when historical fx quotes are updated''' - window.history_list.update() - - def on_fx_quotes(self, window): - '''Called when fresh spot fx quotes come in''' - window.update_status() - self.populate_ccy_combo() - # Refresh edits with the new rate - edit = window.fiat_send_e if window.fiat_send_e.is_last_edited else window.amount_e - edit.textEdited.emit(edit.text()) - edit = window.fiat_receive_e if window.fiat_receive_e.is_last_edited else window.receive_amount_e - edit.textEdited.emit(edit.text()) - # History tab needs updating if it used spot - if self.history_used_spot: - self.on_fx_history(window) - - def on_ccy_combo_change(self): - '''Called when the chosen currency changes''' - ccy = str(self.ccy_combo.currentText()) - if ccy and ccy != self.ccy: - self.set_currency(ccy) - self.hist_checkbox_update() - - def hist_checkbox_update(self): - if self.hist_checkbox: - self.hist_checkbox.setEnabled(self.ccy in self.exchange.history_ccys()) - self.hist_checkbox.setChecked(self.config_history()) - - def populate_ccy_combo(self): - # There should be at most one instance of the settings dialog - combo = self.ccy_combo - # NOTE: bool(combo) is False if it is empty. Nuts. - if combo is not None: - combo.blockSignals(True) - combo.clear() - combo.addItems(sorted(self.exchange.quotes.keys())) - combo.blockSignals(False) - combo.setCurrentIndex(combo.findText(self.ccy)) - - @hook - def on_new_window(self, window): - # Additional send and receive edit boxes - if not hasattr(window, 'fiat_send_e'): - send_e = AmountEdit(self.get_currency) - window.send_grid.addWidget(send_e, 4, 2, Qt.AlignLeft) - window.amount_e.frozen.connect( - lambda: send_e.setFrozen(window.amount_e.isReadOnly())) - receive_e = AmountEdit(self.get_currency) - window.receive_grid.addWidget(receive_e, 2, 2, Qt.AlignLeft) - window.fiat_send_e = send_e - window.fiat_receive_e = receive_e - self.connect_fields(window, window.amount_e, send_e, window.fee_e) - self.connect_fields(window, window.receive_amount_e, receive_e, None) - else: - window.fiat_send_e.show() - window.fiat_receive_e.show() - window.history_list.refresh_headers() - window.update_status() - window.connect(self, SIGNAL('new_fx_quotes'), lambda: self.on_fx_quotes(window)) - window.connect(self, SIGNAL('new_fx_history'), lambda: self.on_fx_history(window)) - window.connect(self, SIGNAL('close_fx_plugin'), lambda: self.restore_window(window)) - window.connect(self, SIGNAL('refresh_headers'), window.history_list.refresh_headers) - - def settings_widget(self, window): - return EnterButton(_('Settings'), partial(self.settings_dialog, window)) - - def settings_dialog(self, window): - d = WindowModalDialog(window, _("Exchange Rate Settings")) - layout = QGridLayout(d) - layout.addWidget(QLabel(_('Exchange rate API: ')), 0, 0) - layout.addWidget(QLabel(_('Currency: ')), 1, 0) - layout.addWidget(QLabel(_('History Rates: ')), 2, 0) - - # Currency list - self.ccy_combo = QComboBox() - self.ccy_combo.currentIndexChanged.connect(self.on_ccy_combo_change) - self.populate_ccy_combo() - - def on_change_ex(idx): - exchange = str(combo_ex.currentText()) - if exchange != self.exchange.name(): - self.set_exchange(exchange) - self.hist_checkbox_update() - - def on_change_hist(checked): - if checked: - self.config.set_key('history_rates', 'checked') - self.get_historical_rates() - else: - self.config.set_key('history_rates', 'unchecked') - self.emit(SIGNAL('refresh_headers')) - - def ok_clicked(): - self.timeout = 0 - self.ccy_combo = None - d.accept() - - combo_ex = QComboBox() - combo_ex.addItems(sorted(self.exchanges.keys())) - combo_ex.setCurrentIndex(combo_ex.findText(self.config_exchange())) - combo_ex.currentIndexChanged.connect(on_change_ex) - - self.hist_checkbox = QCheckBox() - self.hist_checkbox.stateChanged.connect(on_change_hist) - self.hist_checkbox_update() - - ok_button = QPushButton(_("OK")) - ok_button.clicked.connect(lambda: ok_clicked()) - - layout.addWidget(self.ccy_combo,1,1) - layout.addWidget(combo_ex,0,1) - layout.addWidget(self.hist_checkbox,2,1) - layout.addWidget(ok_button,3,1) - - return d.exec_() - - - def config_history(self): - return self.config.get('history_rates', 'unchecked') != 'unchecked' - - def show_history(self): - return self.config_history() and self.ccy in self.exchange.history_ccys() - - @hook - def history_tab_headers(self, headers): - if self.show_history(): - headers.extend(['%s '%self.ccy + _('Amount'), '%s '%self.ccy + _('Balance')]) - - @hook - def history_tab_update_begin(self): - self.history_used_spot = False - - @hook - def history_tab_update(self, tx, entry): - if not self.show_history(): - return - tx_hash, height, conf, timestamp, value, balance = tx - if conf <= 0: - date = timestamp_to_datetime(time.time()) - else: - date = timestamp_to_datetime(timestamp) - for amount in [value, balance]: - text = self.historical_value_str(amount, date) - entry.append(text)