URI: 
       tgeneralize plugins to all guis - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit 404c1f84d2d0d84e30e0be00784da5585fdab6a8
   DIR parent 7d3234f591df4e275e0d9871d903ff7e18ac93cf
  HTML Author: ThomasV <thomasv@gitorious>
       Date:   Mon, 23 Sep 2013 16:14:28 +0200
       
       generalize plugins to all guis
       
       Diffstat:
         M gui/gui_classic/__init__.py         |      18 ++++++++++++++----
         D gui/gui_classic/exchange_rate.py    |      62 -------------------------------
         M gui/gui_classic/lite_window.py      |       5 ++---
         M gui/gui_classic/main_window.py      |     157 +++++++++----------------------
         M gui/gui_classic/qt_util.py          |       9 +++++++++
         M lib/plugins.py                      |      53 ++++++++++++++++++++++++++++++
         M plugins/aliases.py                  |       3 ++-
         A plugins/exchange_rate.py            |     135 +++++++++++++++++++++++++++++++
         M plugins/labels.py                   |      13 +++++++------
         M plugins/pointofsale.py              |      28 +++++++++++++++-------------
         M setup.py                            |       6 +++---
       
       11 files changed, 287 insertions(+), 202 deletions(-)
       ---
   DIR diff --git a/gui/gui_classic/__init__.py b/gui/gui_classic/__init__.py
       t@@ -19,6 +19,7 @@
        import sys, time, datetime, re, threading
        from electrum.i18n import _, set_language
        from electrum.util import print_error, print_msg, parse_url
       +from electrum.plugins import run_hook
        import os.path, json, ast, traceback
        import shutil
        
       t@@ -43,7 +44,7 @@ except:
        
        from qt_util import *
        from main_window import ElectrumWindow
       -
       +from electrum.plugins import init_plugins
        
        class Timer(QtCore.QThread):
            def run(self):
       t@@ -75,6 +76,10 @@ class ElectrumGui:
                    self.app = QApplication(sys.argv)
                self.app.installEventFilter(self.efilter)
        
       +        init_plugins(self)
       +
       +
       +
            def expand(self):
                """Hide the lite mode window and show pro-mode."""
                self.config.set_key('lite_mode', False, True)
       t@@ -128,12 +133,17 @@ class ElectrumGui:
        
                wallet.start_threads(self.network)
        
       -        s = Timer()
       -        s.start()
                    
       -        w = ElectrumWindow(self.config, self.network, self.minimize)
       +        self.main_window = w = ElectrumWindow(self.config, self.network, self.minimize)
       +
       +        # plugins that need to change the GUI do it here
       +        run_hook('init')
       +
                w.load_wallet(wallet)
        
       +        s = Timer()
       +        s.start()
       +
                self.windows.append(w)
                if url: w.set_url(url)
                w.app = self.app
   DIR diff --git a/gui/gui_classic/exchange_rate.py b/gui/gui_classic/exchange_rate.py
       t@@ -1,62 +0,0 @@
       -from PyQt4.QtCore import SIGNAL
       -import decimal
       -import httplib
       -import json
       -import threading
       -
       -class Exchanger(threading.Thread):
       -
       -    def __init__(self, parent):
       -        threading.Thread.__init__(self)
       -        self.daemon = True
       -        self.parent = parent
       -        self.quote_currencies = None
       -        self.lock = threading.Lock()
       -        # Do price discovery
       -        self.start()
       -
       -    def exchange(self, btc_amount, quote_currency):
       -        with self.lock:
       -            if self.quote_currencies is None:
       -                return None
       -            quote_currencies = self.quote_currencies.copy()
       -        if quote_currency not in quote_currencies:
       -            return None
       -        return btc_amount * quote_currencies[quote_currency]
       -
       -    def run(self):
       -        self.discovery()
       -
       -    def discovery(self):
       -        try:
       -            connection = httplib.HTTPConnection('blockchain.info')
       -            connection.request("GET", "/ticker")
       -        except:
       -            return
       -        response = connection.getresponse()
       -        if response.reason == httplib.responses[httplib.NOT_FOUND]:
       -            return
       -        try:
       -            response = json.loads(response.read())
       -        except:
       -            return
       -        quote_currencies = {}
       -        try:
       -            for r in response:
       -                quote_currencies[r] = self._lookup_rate(response, r)
       -            with self.lock:
       -                self.quote_currencies = quote_currencies
       -            self.parent.emit(SIGNAL("refresh_balance()"))
       -        except KeyError:
       -            pass
       -            
       -    def get_currencies(self):
       -        return [] if self.quote_currencies == None else sorted(self.quote_currencies.keys())
       -
       -    def _lookup_rate(self, response, quote_id):
       -        return decimal.Decimal(str(response[str(quote_id)]["15m"]))
       -
       -if __name__ == "__main__":
       -    exch = Exchanger(("BRL", "CNY", "EUR", "GBP", "RUB", "USD"))
       -    print exch.exchange(1, "EUR")
       -
   DIR diff --git a/gui/gui_classic/lite_window.py b/gui/gui_classic/lite_window.py
       t@@ -17,7 +17,6 @@ from electrum.util import get_resource_path as rsrc
        from electrum.bitcoin import is_valid
        from electrum.i18n import _
        import decimal
       -import exchange_rate
        import json
        import os.path
        import random
       t@@ -181,7 +180,7 @@ class MiniWindow(QDialog):
                self.btc_balance = None
                self.quote_currencies = ["BRL", "CNY", "EUR", "GBP", "RUB", "USD"]
                self.actuator.set_configured_currency(self.set_quote_currency)
       -        self.exchanger = exchange_rate.Exchanger(self)
       +        #self.exchanger = exchange_rate.Exchanger(self)
                # Needed because price discovery is done in a different thread
                # which needs to be sent back to this main one to update the GUI
                self.connect(self, SIGNAL("refresh_balance()"), self.refresh_balance)
       t@@ -429,7 +428,7 @@ class MiniWindow(QDialog):
                """Return a string copy of the amount fiat currency the 
                user has in bitcoins."""
                quote_currency = self.quote_currencies[0]
       -        quote_balance = self.exchanger.exchange(btc_balance, quote_currency)
       +        quote_balance = None #self.exchanger.exchange(btc_balance, quote_currency)
                if quote_balance is None:
                    quote_text = ""
                else:
   DIR diff --git a/gui/gui_classic/main_window.py b/gui/gui_classic/main_window.py
       t@@ -34,6 +34,7 @@ from PyQt4.QtCore import *
        import PyQt4.QtCore as QtCore
        
        from electrum.bitcoin import MIN_RELAY_TX_FEE, is_valid
       +from electrum.plugins import run_hook
        
        try:
            import icons_rc
       t@@ -48,7 +49,6 @@ from electrum import SimpleConfig, Wallet, WalletStorage
        
        
        from electrum import bmp, pyqrnative
       -import exchange_rate
        
        from amountedit import AmountEdit
        from network_dialog import NetworkDialog
       t@@ -141,7 +141,6 @@ class ElectrumWindow(QMainWindow):
                self.config = config
                self.network = network
                self.go_lite = go_lite
       -        self.init_plugins()
        
                self._close_electrum = False
                self.lite = None
       t@@ -195,19 +194,14 @@ class ElectrumWindow(QMainWindow):
        
                self.history_list.setFocus(True)
                
       -        self.exchanger = exchange_rate.Exchanger(self)
       -        self.connect(self, SIGNAL("refresh_balance()"), self.update_wallet)
       -
                # dark magic fix by flatfly; https://bitcointalk.org/index.php?topic=73651.msg959913#msg959913
                if platform.system() == 'Windows':
                    n = 3 if self.wallet.seed else 2
                    tabs.setCurrentIndex (n)
                    tabs.setCurrentIndex (0)
        
       -        # plugins that need to change the GUI do it here
       -        self.run_hook('init')
       -
        
       +    
        
        
            def load_wallet(self, wallet):
       t@@ -244,7 +238,7 @@ class ElectrumWindow(QMainWindow):
                self.update_buttons_on_seed()
                self.update_console()
        
       -        self.run_hook('load_wallet')
       +        run_hook('load_wallet')
        
        
            def select_wallet_file(self):
       t@@ -443,46 +437,6 @@ class ElectrumWindow(QMainWindow):
            def notify(self, message):
                self.tray.showMessage("Electrum", message, QSystemTrayIcon.Information, 20000)
        
       -    # plugins
       -    def init_plugins(self):
       -        import imp, pkgutil, __builtin__
       -        if __builtin__.use_local_modules:
       -            fp, pathname, description = imp.find_module('plugins')
       -            plugin_names = [name for a, name, b in pkgutil.iter_modules([pathname])]
       -            plugin_names = filter( lambda name: os.path.exists(os.path.join(pathname,name+'.py')), plugin_names)
       -            imp.load_module('electrum_plugins', fp, pathname, description)
       -            plugins = map(lambda name: imp.load_source('electrum_plugins.'+name, os.path.join(pathname,name+'.py')), plugin_names)
       -        else:
       -            import electrum_plugins
       -            plugin_names = [name for a, name, b in pkgutil.iter_modules(electrum_plugins.__path__)]
       -            plugins = [ __import__('electrum_plugins.'+name, fromlist=['electrum_plugins']) for name in plugin_names]
       -
       -        self.plugins = []
       -        for name, p in zip(plugin_names, plugins):
       -            try:
       -                self.plugins.append( p.Plugin(self, name) )
       -            except:
       -                print_msg(_("Error: cannot initialize plugin"),p)
       -                traceback.print_exc(file=sys.stdout)
       -
       -
       -    def run_hook(self, name, *args):
       -        for p in self.plugins:
       -            if not p.is_enabled():
       -                continue
       -
       -            f = getattr(p, name, None)
       -            if not callable(f):
       -                return
       -
       -            try:
       -                f(*args)
       -            except:
       -                print_error("Plugin error")
       -                traceback.print_exc(file=sys.stdout)
       -                
       -        return
       -
        
        
            def set_label(self, name, text = None):
       t@@ -497,7 +451,7 @@ class ElectrumWindow(QMainWindow):
                    if old_text:
                        self.wallet.labels.pop(name)
                        changed = True
       -        self.run_hook('set_label', name, text, changed)
       +        run_hook('set_label', name, text, changed)
                return changed
        
        
       t@@ -519,7 +473,7 @@ class ElectrumWindow(QMainWindow):
        
            def close(self):
                QMainWindow.close(self)
       -        self.run_hook('close_main_window')
       +        run_hook('close_main_window')
        
            def connect_slots(self, sender):
                self.connect(sender, QtCore.SIGNAL('timersignal'), self.timer_actions)
       t@@ -529,7 +483,7 @@ class ElectrumWindow(QMainWindow):
                if self.need_update.is_set():
                    self.update_wallet()
                    self.need_update.clear()
       -        self.run_hook('timer_actions')
       +        run_hook('timer_actions')
            
            def format_amount(self, x, is_diff=False, whitespaces=False):
                return format_satoshis(x, is_diff, self.num_zeros, self.decimal_point, whitespaces)
       t@@ -543,6 +497,11 @@ class ElectrumWindow(QMainWindow):
                assert self.decimal_point in [5,8]
                return "BTC" if self.decimal_point == 8 else "mBTC"
        
       +    def set_status_text(self, text):
       +        self.balance_label.setText(text)
       +        run_hook('set_status_text', text)
       +
       +
            def update_status(self):
                if self.network.interface and self.network.interface.is_connected:
                    if not self.wallet.up_to_date:
       t@@ -552,14 +511,13 @@ class ElectrumWindow(QMainWindow):
                        c, u = self.wallet.get_account_balance(self.current_account)
                        text =  _( "Balance" ) + ": %s "%( self.format_amount(c) ) + self.base_unit()
                        if u: text +=  " [%s unconfirmed]"%( self.format_amount(u,True).strip() )
       -                text += self.create_quote_text(Decimal(c+u)/100000000)
                        self.tray.setToolTip(text)
                        icon = QIcon(":icons/status_connected.png")
                else:
                    text = _("Not connected")
                    icon = QIcon(":icons/status_disconnected.png")
        
       -        self.balance_label.setText(text)
       +        self.set_status_text(text)
                self.status_button.setIcon( icon )
        
            def update_wallet(self):
       t@@ -571,14 +529,6 @@ class ElectrumWindow(QMainWindow):
                    self.update_completions()
        
        
       -    def create_quote_text(self, btc_balance):
       -        quote_currency = self.config.get("currency", "None")
       -        quote_balance = self.exchanger.exchange(btc_balance, quote_currency)
       -        if quote_balance is None:
       -            quote_text = ""
       -        else:
       -            quote_text = "  (%.2f %s)" % (quote_balance, quote_currency)
       -        return quote_text
                
            def create_history_tab(self):
                self.history_list = l = MyTreeWidget(self)
       t@@ -672,11 +622,11 @@ class ElectrumWindow(QMainWindow):
                        
                    self.current_item_changed(item)
        
       -        self.run_hook('item_changed', item, column)
       +        run_hook('item_changed', item, column)
        
        
            def current_item_changed(self, a):
       -        self.run_hook('current_item_changed', a)
       +        run_hook('current_item_changed', a)
        
        
        
       t@@ -836,7 +786,7 @@ class ElectrumWindow(QMainWindow):
                self.amount_e.textChanged.connect(lambda: entry_changed(False) )
                self.fee_e.textChanged.connect(lambda: entry_changed(True) )
        
       -        self.run_hook('create_send_tab', grid)
       +        run_hook('create_send_tab', grid)
                return w2
        
        
       t@@ -846,7 +796,7 @@ class ElectrumWindow(QMainWindow):
                    if addr in self.wallet.addressbook:
                        l.append( label + '  <' + addr + '>')
        
       -        self.run_hook('update_completions', l)
       +        run_hook('update_completions', l)
                self.completions.setStringList(l)
        
        
       t@@ -901,7 +851,7 @@ class ElectrumWindow(QMainWindow):
                    QMessageBox.warning(self, _('Error'), _("This transaction requires a higher fee, or it will not be propagated by the network."), _('OK'))
                    return
        
       -        self.run_hook('send_tx', tx)
       +        run_hook('send_tx', tx)
        
                if label: 
                    self.set_label(tx.hash(), label)
       t@@ -943,7 +893,7 @@ class ElectrumWindow(QMainWindow):
                            self.wallet.addressbook.append(address)
                        self.set_label(address, label)
        
       -        self.run_hook('set_url', url, self.show_message, self.question)
       +        run_hook('set_url', url, self.show_message, self.question)
        
                self.tabs.setCurrentIndex(1)
                label = self.wallet.labels.get(address)
       t@@ -1111,7 +1061,7 @@ class ElectrumWindow(QMainWindow):
                    t = _("Unprioritize") if addr in self.wallet.prioritized_addresses else _("Prioritize")
                    menu.addAction(t, lambda: self.toggle_priority(addr))
                    
       -        self.run_hook('receive_menu', menu)
       +        run_hook('receive_menu', menu)
                menu.exec_(self.receive_list.viewport().mapToGlobal(position))
        
        
       t@@ -1148,7 +1098,7 @@ class ElectrumWindow(QMainWindow):
                    menu.addAction(_("Edit label"), lambda: self.edit_label(False))
                    menu.addAction(_("Delete"), lambda: self.delete_contact(addr))
        
       -        self.run_hook('create_contact_menu', menu, item)
       +        run_hook('create_contact_menu', menu, item)
                menu.exec_(self.contacts_list.viewport().mapToGlobal(position))
        
        
       t@@ -1159,7 +1109,7 @@ class ElectrumWindow(QMainWindow):
                item.setData(1,0,label)
                item.setData(0,32, True) # is editable
        
       -        self.run_hook('update_receive_item', address, item)
       +        run_hook('update_receive_item', address, item)
                        
                c, u = self.wallet.get_addr_balance(address)
                balance = self.format_amount(c + u)
       t@@ -1254,7 +1204,7 @@ class ElectrumWindow(QMainWindow):
                    item.setData(0,33, address)
                    l.addTopLevelItem(item)
        
       -        self.run_hook('update_contacts_tab', l)
       +        run_hook('update_contacts_tab', l)
                l.setCurrentItem(l.topLevelItem(0))
        
        
       t@@ -1325,7 +1275,7 @@ class ElectrumWindow(QMainWindow):
                self.status_button = StatusBarButton( QIcon(":icons/status_disconnected.png"), _("Network"), self.run_network_dialog ) 
                sb.addPermanentWidget( self.status_button )
        
       -        self.run_hook('create_status_bar', (sb,))
       +        run_hook('create_status_bar', (sb,))
        
                self.setStatusBar(sb)
        
       t@@ -1616,7 +1566,7 @@ class ElectrumWindow(QMainWindow):
                vbox.addLayout(ok_cancel_buttons(d))
                d.setLayout(vbox)
        
       -        self.run_hook('password_dialog', pw, grid, 1)
       +        run_hook('password_dialog', pw, grid, 1)
                if not d.exec_(): return
                return unicode(pw.text())
        
       t@@ -1855,21 +1805,6 @@ class ElectrumWindow(QMainWindow):
                if not self.config.is_modifiable('language'):
                    for w in [lang_combo, lang_label]: w.setEnabled(False)
        
       -        currencies = self.exchanger.get_currencies()
       -        currencies.insert(0, "None")
       -
       -        cur_label=QLabel(_('Currency') + ':')
       -        grid_ui.addWidget(cur_label , 2, 0)
       -        cur_combo = QComboBox()
       -        cur_combo.addItems(currencies)
       -        try:
       -            index = currencies.index(self.config.get('currency', "None"))
       -        except:
       -            index = 0
       -        cur_combo.setCurrentIndex(index)
       -        grid_ui.addWidget(cur_combo, 2, 1)
       -        grid_ui.addWidget(HelpButton(_('Select which currency is used for quotes.')+' '), 2, 2)
       -        
                expert_cb = QCheckBox(_('Expert mode'))
                expert_cb.setChecked(self.expert_mode)
                grid_ui.addWidget(expert_cb, 3, 0)
       t@@ -1916,7 +1851,7 @@ class ElectrumWindow(QMainWindow):
                grid_wallet.setRowStretch(4,1)
        
        
       -        self.run_hook('create_settings_tab', tabs)
       +        run_hook('create_settings_tab', tabs)
        
                vbox.addLayout(ok_cancel_buttons(d))
                d.setLayout(vbox) 
       t@@ -1966,12 +1901,8 @@ class ElectrumWindow(QMainWindow):
                    self.config.set_key("language", lang_request, True)
                    need_restart = True
                    
       -        cur_request = str(currencies[cur_combo.currentIndex()])
       -        if cur_request != self.config.get('currency', "None"):
       -            self.config.set_key('currency', cur_request, True)
       -            self.update_wallet()
        
       -        self.run_hook('close_settings_dialog')
       +        run_hook('close_settings_dialog')
        
                if need_restart:
                    QMessageBox.warning(self, _('Success'), _('Please restart Electrum to activate the new GUI settings'), _('OK'))
       t@@ -1991,40 +1922,46 @@ class ElectrumWindow(QMainWindow):
        
        
            def plugins_dialog(self):
       +        from electrum.plugins import plugins
       +
                d = QDialog(self)
                d.setWindowTitle(_('Electrum Plugins'))
       -        d.setMinimumSize(450,300)
                d.setModal(1)
        
       -        # plugins
       -        tab5 = QScrollArea(d)
       -        tab5.setEnabled(True)
       -        tab5.setWidgetResizable(True)
       +        vbox = QVBoxLayout(d)
        
       -        grid_plugins = QGridLayout()
       -        grid_plugins.setColumnStretch(0,1)
       +        # plugins
       +        scroll = QScrollArea()
       +        scroll.setEnabled(True)
       +        scroll.setWidgetResizable(True)
       +        scroll.setMinimumSize(400,250)
       +        vbox.addWidget(scroll)
        
                w = QWidget()
       -        w.setLayout(grid_plugins)
       -        tab5.setWidget(w)
       +        scroll.setWidget(w)
       +        w.setMinimumHeight(len(plugins)*35)
        
       -        w.setMinimumHeight(len(self.plugins)*35)
       +        grid = QGridLayout()
       +        grid.setColumnStretch(0,1)
       +        w.setLayout(grid)
        
                def mk_toggle(cb, p):
                    return lambda: cb.setChecked(p.toggle())
       -        for i, p in enumerate(self.plugins):
       +        for i, p in enumerate(plugins):
                    try:
                        cb = QCheckBox(p.fullname())
                        cb.setDisabled(not p.is_available())
                        cb.setChecked(p.is_enabled())
                        cb.clicked.connect(mk_toggle(cb,p))
       -                grid_plugins.addWidget(cb, i, 0)
       +                grid.addWidget(cb, i, 0)
                        if p.requires_settings():
       -                    grid_plugins.addWidget(EnterButton(_('Settings'), p.settings_dialog), i, 1)
       -                grid_plugins.addWidget(HelpButton(p.description()), i, 2)
       +                    grid.addWidget(EnterButton(_('Settings'), p.settings_dialog), i, 1)
       +                grid.addWidget(HelpButton(p.description()), i, 2)
                    except:
                        print_msg(_("Error: cannot display plugin"), p)
                        traceback.print_exc(file=sys.stdout)
       -        grid_plugins.setRowStretch(i+1,1)
       +        grid.setRowStretch(i+1,1)
       +
       +        vbox.addLayout(close_button(d))
        
                d.exec_()
   DIR diff --git a/gui/gui_classic/qt_util.py b/gui/gui_classic/qt_util.py
       t@@ -57,6 +57,15 @@ class HelpButton(QPushButton):
        
        
        
       +def close_button(dialog, label=_("Close") ):
       +    hbox = QHBoxLayout()
       +    hbox.addStretch(1)
       +    b = QPushButton(label)
       +    hbox.addWidget(b)
       +    b.clicked.connect(dialog.close)
       +    b.setDefault(True)
       +    return hbox
       +
        def ok_cancel_buttons(dialog, ok_label=_("OK") ):
            hbox = QHBoxLayout()
            hbox.addStretch(1)
   DIR diff --git a/lib/plugins.py b/lib/plugins.py
       t@@ -1,3 +1,56 @@
       +from util import print_error
       +import traceback, sys
       +from electrum.util import *
       +from electrum.i18n import _
       +
       +plugins = []
       +
       +
       +def init_plugins(self):
       +    import imp, pkgutil, __builtin__, os
       +    global plugins
       +
       +    if __builtin__.use_local_modules:
       +        fp, pathname, description = imp.find_module('plugins')
       +        plugin_names = [name for a, name, b in pkgutil.iter_modules([pathname])]
       +        plugin_names = filter( lambda name: os.path.exists(os.path.join(pathname,name+'.py')), plugin_names)
       +        imp.load_module('electrum_plugins', fp, pathname, description)
       +        plugin_modules = map(lambda name: imp.load_source('electrum_plugins.'+name, os.path.join(pathname,name+'.py')), plugin_names)
       +    else:
       +        import electrum_plugins
       +        plugin_names = [name for a, name, b in pkgutil.iter_modules(electrum_plugins.__path__)]
       +        plugin_modules = [ __import__('electrum_plugins.'+name, fromlist=['electrum_plugins']) for name in plugin_names]
       +
       +    for name, p in zip(plugin_names, plugin_modules):
       +        try:
       +            plugins.append( p.Plugin(self, name) )
       +        except:
       +            print_msg(_("Error: cannot initialize plugin"),p)
       +            traceback.print_exc(file=sys.stdout)
       +
       +
       +
       +def run_hook(name, *args):
       +    
       +    global plugins
       +
       +    for p in plugins:
       +
       +        if not p.is_enabled():
       +            continue
       +
       +        f = getattr(p, name, None)
       +        if not callable(f):
       +            continue
       +
       +        try:
       +            f(*args)
       +        except:
       +            print_error("Plugin error")
       +            traceback.print_exc(file=sys.stdout)
       +            
       +    return
       +
        
        
        class BasePlugin:
   DIR diff --git a/plugins/aliases.py b/plugins/aliases.py
       t@@ -29,7 +29,8 @@ class Plugin(BasePlugin):
                self.authorities  = self.config.get('authorities', {})        # trusted addresses
                self.receipts     = self.config.get('receipts',{})            # signed URIs
        
       -
       +    def is_available(self):
       +        return False
        
            def timer_actions(self):
                if self.gui.payto_e.hasFocus():
   DIR diff --git a/plugins/exchange_rate.py b/plugins/exchange_rate.py
       t@@ -0,0 +1,135 @@
       +from PyQt4.QtGui import *
       +from PyQt4.QtCore import *
       +
       +import decimal
       +import httplib
       +import json
       +import threading
       +import re
       +from decimal import Decimal
       +from electrum.plugins import BasePlugin
       +from electrum.i18n import _
       +from electrum_gui.gui_classic.qt_util import *
       +
       +
       +class Exchanger(threading.Thread):
       +
       +    def __init__(self, parent):
       +        threading.Thread.__init__(self)
       +        self.daemon = True
       +        self.parent = parent
       +        self.quote_currencies = None
       +        self.lock = threading.Lock()
       +
       +    def exchange(self, btc_amount, quote_currency):
       +        with self.lock:
       +            if self.quote_currencies is None:
       +                return None
       +            quote_currencies = self.quote_currencies.copy()
       +        if quote_currency not in quote_currencies:
       +            return None
       +        return btc_amount * quote_currencies[quote_currency]
       +
       +    def run(self):
       +        try:
       +            connection = httplib.HTTPConnection('blockchain.info')
       +            connection.request("GET", "/ticker")
       +        except:
       +            return
       +        response = connection.getresponse()
       +        if response.reason == httplib.responses[httplib.NOT_FOUND]:
       +            return
       +        try:
       +            response = json.loads(response.read())
       +        except:
       +            return
       +        quote_currencies = {}
       +        try:
       +            for r in response:
       +                quote_currencies[r] = self._lookup_rate(response, r)
       +            with self.lock:
       +                self.quote_currencies = quote_currencies
       +            self.parent.emit(SIGNAL("refresh_balance()"))
       +        except KeyError:
       +            pass
       +
       +        print self.quote_currencies
       +            
       +    def get_currencies(self):
       +        return [] if self.quote_currencies == None else sorted(self.quote_currencies.keys())
       +
       +    def _lookup_rate(self, response, quote_id):
       +        return decimal.Decimal(str(response[str(quote_id)]["15m"]))
       +
       +
       +class Plugin(BasePlugin):
       +
       +    def fullname(self):
       +        return "Exchange rates"
       +
       +    def description(self):
       +        return """exchange rates"""
       +
       +    def init(self):
       +        self.win = self.gui.main_window
       +        self.exchanger = Exchanger(self.win)
       +        self.win.connect(self.win, SIGNAL("refresh_balance()"), self.win.update_wallet)
       +        # Do price discovery
       +        self.exchanger.start()
       +        self.gui.exchanger = self.exchanger
       +
       +    def set_status_text(self, text):
       +        m = re.match( _( "Balance" ) + ": (\d.+) " + self.win.base_unit(), str(text))
       +        if m:
       +            amount = Decimal(m.group(1))
       +            text += self.create_quote_text(amount)
       +            self.win.balance_label.setText(text)
       +
       +    def create_quote_text(self, btc_balance):
       +        quote_currency = self.config.get("currency", "None")
       +        quote_balance = self.exchanger.exchange(btc_balance, quote_currency)
       +        if quote_balance is None:
       +            quote_text = ""
       +        else:
       +            quote_text = "  (%.2f %s)" % (quote_balance, quote_currency)
       +        return quote_text
       +
       +
       +    def requires_settings(self):
       +        return True
       +
       +
       +    def settings_dialog(self):
       +        d = QDialog(self.win)
       +
       +        vbox = QVBoxLayout(d)
       +
       +        grid = QGridLayout()
       +        vbox.addLayout(grid)
       +
       +        currencies = self.exchanger.get_currencies()
       +        currencies.insert(0, "None")
       +
       +        cur_label=QLabel(_('Currency') + ':')
       +        grid.addWidget(cur_label , 2, 0)
       +        cur_combo = QComboBox()
       +        cur_combo.addItems(currencies)
       +        try:
       +            index = currencies.index(self.config.get('currency', "None"))
       +        except:
       +            index = 0
       +        cur_combo.setCurrentIndex(index)
       +        grid.addWidget(cur_combo, 2, 1)
       +        grid.addWidget(HelpButton(_('Select which currency is used for quotes.') + ' '), 2, 2)
       +
       +        vbox.addLayout(ok_cancel_buttons(d))
       +
       +        if d.exec_():
       +
       +            cur_request = str(currencies[cur_combo.currentIndex()])
       +            if cur_request != self.config.get('currency', "None"):
       +                self.config.set_key('currency', cur_request, True)
       +                self.win.update_wallet()
       +
       +
       +        
   DIR diff --git a/plugins/labels.py b/plugins/labels.py
       t@@ -47,7 +47,8 @@ class Plugin(BasePlugin):
        
            def init(self):
                self.target_host = 'labelectrum.herokuapp.com'
       -        self.wallet = self.gui.wallet
       +        self.window = self.gui.main_window
       +        self.wallet = self.window.wallet
                self.labels = self.wallet.labels
                self.transactions = self.wallet.transactions
                mpk = self.wallet.master_public_keys["m/0'/"][1]
       t@@ -103,7 +104,7 @@ class Plugin(BasePlugin):
                      self.download.setEnabled(False)
                      self.accept.setEnabled(False)
        
       -        d = QDialog(self.gui)
       +        d = QDialog(self.window)
                layout = QGridLayout(d)
                layout.addWidget(QLabel("API Key: "),0,0)
        
       t@@ -161,10 +162,10 @@ class Plugin(BasePlugin):
            def full_pull(self, force = False):
                if self.do_full_pull(force) and force:
                    QMessageBox.information(None, _("Labels synchronized"), _("Your labels have been synchronized."))
       -            self.gui.update_history_tab()
       -            self.gui.update_completions()
       -            self.gui.update_receive_tab()
       -            self.gui.update_contacts_tab()
       +            self.window.update_history_tab()
       +            self.window.update_completions()
       +            self.window.update_receive_tab()
       +            self.window.update_contacts_tab()
        
            def do_full_push(self):
                try:
   DIR diff --git a/plugins/pointofsale.py b/plugins/pointofsale.py
       t@@ -101,19 +101,21 @@ class Plugin(BasePlugin):
                return _('Show QR code window and amounts requested for each address. Add menu item to request amount.')
        
            def init(self):
       +        self.window = self.gui.main_window
       +
                self.qr_window = None
                self.merchant_name = self.config.get('merchant_name', 'Invoice')
        
       -        self.gui.expert_mode = True
       -        self.gui.receive_list.setHeaderLabels([ _('Address'), _('Label'), _('Balance'), _('Request')])
       +        self.window.expert_mode = True
       +        self.window.receive_list.setHeaderLabels([ _('Address'), _('Label'), _('Balance'), _('Request')])
                self.requested_amounts = {}
                self.toggle_QR_window(True)
        
            def load_wallet(self):
       -        self.requested_amounts = self.gui.wallet.storage.get('requested_amounts',{}) 
       +        self.requested_amounts = self.window.wallet.storage.get('requested_amounts',{}) 
        
            def close(self):
       -        self.gui.receive_list.setHeaderLabels([ _('Address'), _('Label'), _('Balance'), _('Tx')])
       +        self.window.receive_list.setHeaderLabels([ _('Address'), _('Label'), _('Balance'), _('Tx')])
                self.toggle_QR_window(False)
            
        
       t@@ -133,10 +135,10 @@ class Plugin(BasePlugin):
                    self.qr_window = QR_Window(self.gui.exchanger)
                    self.qr_window.setVisible(True)
                    self.qr_window_geometry = self.qr_window.geometry()
       -            item = self.gui.receive_list.currentItem()
       +            item = self.window.receive_list.currentItem()
                    if item:
                        address = str(item.text(1))
       -                label = self.gui.wallet.labels.get(address)
       +                label = self.window.wallet.labels.get(address)
                        amount, currency = self.requested_amounts.get(address, (None, None))
                        self.qr_window.set_content( address, label, amount, currency )
        
       t@@ -166,7 +168,7 @@ class Plugin(BasePlugin):
            def current_item_changed(self, a):
                if a is not None and self.qr_window and self.qr_window.isVisible():
                    address = str(a.text(0))
       -            label = self.gui.wallet.labels.get(address)
       +            label = self.window.wallet.labels.get(address)
                    try:
                        amount, currency = self.requested_amounts.get(address, (None, None))
                    except:
       t@@ -181,7 +183,7 @@ class Plugin(BasePlugin):
                address = str( item.text(0) )
                text = str( item.text(column) )
                try:
       -            seq = self.gui.wallet.get_address_index(address)
       +            seq = self.window.wallet.get_address_index(address)
                    index = seq[1][1]
                except:
                    print "cannot get index"
       t@@ -199,12 +201,12 @@ class Plugin(BasePlugin):
                        currency = currency.upper()
                            
                    self.requested_amounts[address] = (amount, currency)
       -            self.gui.wallet.storage.put('requested_amounts', self.requested_amounts, True)
       +            self.window.wallet.storage.put('requested_amounts', self.requested_amounts, True)
        
       -            label = self.gui.wallet.labels.get(address)
       +            label = self.window.wallet.labels.get(address)
                    if label is None:
                        label = self.merchant_name + ' - %04d'%(index+1)
       -                self.gui.wallet.labels[address] = label
       +                self.window.wallet.labels[address] = label
        
                    if self.qr_window:
                        self.qr_window.set_content( address, label, amount, currency )
       t@@ -214,13 +216,13 @@ class Plugin(BasePlugin):
                    if address in self.requested_amounts:
                        self.requested_amounts.pop(address)
                    
       -        self.gui.update_receive_item(self.gui.receive_list.currentItem())
       +        self.window.update_receive_item(self.window.receive_list.currentItem())
        
        
        
        
            def edit_amount(self):
       -        l = self.gui.receive_list
       +        l = self.window.receive_list
                item = l.currentItem()
                item.setFlags(Qt.ItemIsEditable|Qt.ItemIsSelectable | Qt.ItemIsUserCheckable | Qt.ItemIsEnabled | Qt.ItemIsDragEnabled)
                l.editItem( item, column_index )
   DIR diff --git a/setup.py b/setup.py
       t@@ -81,7 +81,6 @@ setup(name = "Electrum",
                          'electrum_gui.gui_classic.lite_window',
                          'electrum_gui.gui_classic.main_window',
                          'electrum_gui.gui_classic.installwizard',
       -                  'electrum_gui.gui_classic.exchange_rate',
                          'electrum_gui.gui_classic.icons_rc',
                          'electrum_gui.gui_classic.qrcodewidget',
                          'electrum_gui.gui_classic.history_widget',
       t@@ -93,10 +92,11 @@ setup(name = "Electrum",
                          'electrum_gui.gui_classic.transaction dialog',
                          'electrum_gui.gui_classic.version_getter',
                          'electrum_gui.gui_classic.amountedit',
       +                  #'electrum_plugins.aliases',
       +                  'electrum_plugins.exchange_rate',
       +                  'electrum_plugins.labels',
                          'electrum_plugins.pointofsale',
                          'electrum_plugins.qrscanner',
       -                  'electrum_plugins.aliases',
       -                  'electrum_plugins.labels',
                          'electrum_plugins.virtualkeyboard',
                          ],
            description = "Lightweight Bitcoin Wallet",