URI: 
       tCreate a Plugins class - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit 49797c309438c56be8f1e02bc0036b04b9450e21
   DIR parent 2c67de8f642a182dd76c706b65c14eeeeee64d03
  HTML Author: Neil Booth <kyuupichan@gmail.com>
       Date:   Thu,  3 Sep 2015 12:02:03 +0900
       
       Create a Plugins class
       
       Encapsulates plugin logic and removes global variable ugliness.
       
       Diffstat:
         M electrum                            |      11 ++++++-----
         M gui/__init__.py                     |       2 +-
         M gui/android.py                      |      78 +++++++++++++++----------------
         M gui/gtk.py                          |      76 ++++++++++++++++----------------
         M gui/jsonrpc.py                      |       4 ++--
         M gui/qt/__init__.py                  |       3 ++-
         M gui/qt/main_window.py               |      26 ++++++++++----------------
         M gui/stdio.py                        |      20 ++++++++++----------
         M gui/text.py                         |      41 +++++++++++++++----------------
         M lib/plugins.py                      |     150 +++++++++++++++++--------------
       
       10 files changed, 208 insertions(+), 203 deletions(-)
       ---
   DIR diff --git a/electrum b/electrum
       t@@ -79,7 +79,7 @@ if is_bundle or is_local or is_android:
        from electrum import util
        from electrum import SimpleConfig, Network, Wallet, WalletStorage
        from electrum.util import print_msg, print_error, print_stderr, print_json, set_verbosity, InvalidPassword
       -from electrum.plugins import init_plugins, run_hook, always_hook
       +from electrum.plugins import Plugins, run_hook, always_hook
        from electrum.commands import get_parser, known_commands, Commands, config_variables
        
        
       t@@ -97,12 +97,12 @@ def prompt_password(prompt, confirm=True):
        
        
        
       -def init_gui(config, network):
       +def init_gui(config, network, plugins):
            gui_name = config.get('gui', 'qt')
            if gui_name in ['lite', 'classic']:
                gui_name = 'qt'
            gui = __import__('electrum_gui.' + gui_name, fromlist=['electrum_gui'])
       -    gui = gui.ElectrumGui(config, network)
       +    gui = gui.ElectrumGui(config, network, plugins)
            return gui
        
        
       t@@ -493,9 +493,10 @@ if __name__ == '__main__':
                    sys.exit(1)
        
            # initialize plugins.
       +    plugins = None
            if not is_android:
                gui_name = config.get('gui', 'qt') if cmd_name == 'gui' else 'cmdline'
       -        init_plugins(config, is_bundle or is_local or is_android, gui_name)
       +        plugins = Plugins(config, is_bundle or is_local or is_android, gui_name)
        
            # get password if needed
            if cmd_name not in ['gui', 'daemon']:
       t@@ -527,7 +528,7 @@ if __name__ == '__main__':
                network.start()
                server = NetworkServer(config, network)
                server.start()
       -        server.gui = init_gui(config, network)
       +        server.gui = init_gui(config, network, plugins)
                server.gui.main()
            elif cmd_name == 'daemon':
                subcommand = config.get('subcommand')
   DIR diff --git a/gui/__init__.py b/gui/__init__.py
       t@@ -1,5 +1,5 @@
        # To create a new GUI, please add its code to this directory.
       -# Two objects must be passed to the ElectrumGui: config and network
       +# Three objects are passed to the ElectrumGui: config, network and plugins
        # The Wallet object is instanciated by the GUI
        
        # Notifications about network events are sent to the GUI by using network.register_callback()
   DIR diff --git a/gui/android.py b/gui/android.py
       t@@ -126,10 +126,10 @@ def protocol_dialog(host, protocol, z):
        def make_layout(s, scrollable = False):
            content = """
        
       -      <LinearLayout 
       +      <LinearLayout
                android:id="@+id/zz"
                android:layout_width="match_parent"
       -        android:layout_height="wrap_content" 
       +        android:layout_height="wrap_content"
                android:background="#ff222222">
        
                <TextView
       t@@ -147,13 +147,13 @@ def make_layout(s, scrollable = False):
        
            if scrollable:
                content = """
       -      <ScrollView 
       +      <ScrollView
                android:id="@+id/scrollview"
                android:layout_width="match_parent"
                android:layout_height="match_parent" >
        
              <LinearLayout
       -        android:orientation="vertical" 
       +        android:orientation="vertical"
                android:layout_width="match_parent"
                android:layout_height="wrap_content" >
        
       t@@ -167,12 +167,12 @@ def make_layout(s, scrollable = False):
            return """<?xml version="1.0" encoding="utf-8"?>
              <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                android:id="@+id/background"
       -        android:orientation="vertical" 
       +        android:orientation="vertical"
                android:layout_width="match_parent"
       -        android:layout_height="match_parent" 
       +        android:layout_height="match_parent"
                android:background="#ff000022">
        
       -      %s 
       +      %s
              </LinearLayout>"""%content
        
        
       t@@ -181,21 +181,21 @@ def make_layout(s, scrollable = False):
        def main_layout():
            h = get_history_layout(15)
            l = make_layout("""
       -        <TextView android:id="@+id/balanceTextView" 
       +        <TextView android:id="@+id/balanceTextView"
                        android:layout_width="match_parent"
                        android:text=""
                        android:textColor="#ffffffff"
       -                android:textAppearance="?android:attr/textAppearanceLarge" 
       +                android:textAppearance="?android:attr/textAppearanceLarge"
                        android:padding="7dip"
                        android:textSize="8pt"
                        android:gravity="center_vertical|center_horizontal|left">
                </TextView>
        
       -        <TextView android:id="@+id/historyTextView" 
       +        <TextView android:id="@+id/historyTextView"
                        android:layout_width="match_parent"
       -                android:layout_height="wrap_content" 
       +                android:layout_height="wrap_content"
                        android:text="Recent transactions"
       -                android:textAppearance="?android:attr/textAppearanceLarge" 
       +                android:textAppearance="?android:attr/textAppearanceLarge"
                        android:gravity="center_vertical|center_horizontal|center">
                </TextView>
                %s """%h,True)
       t@@ -250,18 +250,18 @@ def qr_layout(addr, amount, message):
        
        payto_layout = make_layout("""
        
       -        <TextView android:id="@+id/recipientTextView" 
       +        <TextView android:id="@+id/recipientTextView"
                        android:layout_width="match_parent"
       -                android:layout_height="wrap_content" 
       +                android:layout_height="wrap_content"
                        android:text="Pay to:"
       -                android:textAppearance="?android:attr/textAppearanceLarge" 
       +                android:textAppearance="?android:attr/textAppearanceLarge"
                        android:gravity="left">
                </TextView>
        
        
                <EditText android:id="@+id/recipient"
                        android:layout_width="match_parent"
       -                android:layout_height="wrap_content" 
       +                android:layout_height="wrap_content"
                        android:tag="Tag Me" android:inputType="text">
                </EditText>
        
       t@@ -275,31 +275,31 @@ payto_layout = make_layout("""
                </LinearLayout>
        
        
       -        <TextView android:id="@+id/labelTextView" 
       +        <TextView android:id="@+id/labelTextView"
                        android:layout_width="match_parent"
       -                android:layout_height="wrap_content" 
       +                android:layout_height="wrap_content"
                        android:text="Message:"
       -                android:textAppearance="?android:attr/textAppearanceLarge" 
       +                android:textAppearance="?android:attr/textAppearanceLarge"
                        android:gravity="left">
                </TextView>
        
                <EditText android:id="@+id/message"
                        android:layout_width="match_parent"
       -                android:layout_height="wrap_content" 
       +                android:layout_height="wrap_content"
                        android:tag="Tag Me" android:inputType="text">
                </EditText>
        
       -        <TextView android:id="@+id/amountLabelTextView" 
       +        <TextView android:id="@+id/amountLabelTextView"
                        android:layout_width="match_parent"
       -                android:layout_height="wrap_content" 
       +                android:layout_height="wrap_content"
                        android:text="Amount:"
       -                android:textAppearance="?android:attr/textAppearanceLarge" 
       +                android:textAppearance="?android:attr/textAppearanceLarge"
                        android:gravity="left">
                </TextView>
        
                <EditText android:id="@+id/amount"
                        android:layout_width="match_parent"
       -                android:layout_height="wrap_content" 
       +                android:layout_height="wrap_content"
                        android:tag="Tag Me" android:inputType="numberDecimal">
                </EditText>
        
       t@@ -312,7 +312,7 @@ payto_layout = make_layout("""
        
        
        settings_layout = make_layout(""" <ListView
       -           android:id="@+id/myListView" 
       +           android:id="@+id/myListView"
                   android:layout_width="match_parent"
                   android:layout_height="wrap_content" />""")
        
       t@@ -349,23 +349,23 @@ def get_history_layout(n):
                rows += """
                <TableRow>
                  <TextView
       -            android:id="@+id/hl_%d_col1" 
       +            android:id="@+id/hl_%d_col1"
                    android:layout_column="0"
                    android:text="%s"
                    android:textColor="%s"
                    android:padding="3" />
                  <TextView
       -            android:id="@+id/hl_%d_col2" 
       +            android:id="@+id/hl_%d_col2"
                    android:layout_column="1"
                    android:text="%s"
                    android:padding="3" />
                  <TextView
       -            android:id="@+id/hl_%d_col3" 
       +            android:id="@+id/hl_%d_col3"
                    android:layout_column="2"
                    android:text="%s"
                    android:padding="3" />
                  <TextView
       -            android:id="@+id/hl_%d_col4" 
       +            android:id="@+id/hl_%d_col4"
                    android:layout_column="3"
                    android:text="%s"
                    android:padding="4" />
       t@@ -411,7 +411,7 @@ def update_layout():
                text = "Synchronizing..."
            else:
                c, u, x = wallet.get_balance()
       -        text = "Balance:"+format_satoshis(c) 
       +        text = "Balance:"+format_satoshis(c)
                if u:
                    text += '   [' + format_satoshis(u,True).strip() + ']'
                if x:
       t@@ -507,7 +507,7 @@ def main_loop():
        
                event = droid.eventWait(1000).result
                if event is None:
       -            if do_refresh: 
       +            if do_refresh:
                        update_layout()
                        do_refresh = False
                    continue
       t@@ -522,7 +522,7 @@ def main_loop():
                    if event["data"]["key"] == '4':
                        if quitting:
                            out = 'quit'
       -                else: 
       +                else:
                            quitting = True
                else: quitting = False
        
       t@@ -555,7 +555,7 @@ def main_loop():
                            out = None
        
            return out
       -                    
       +
        
        def payto_loop():
            global recipient
       t@@ -621,7 +621,7 @@ def payto_loop():
                                else:
                                    modal_dialog('Error','cannot parse QR code\n'+data)
        
       -                    
       +
                elif event["name"] in menu_commands:
                    out = event["name"]
        
       t@@ -729,7 +729,7 @@ def show_seed():
                if not password: return
            else:
                password = None
       -    
       +
            try:
                seed = wallet.get_mnemonic(password)
            except Exception:
       t@@ -892,7 +892,7 @@ def make_bitmap(data):
            finally:
                droid.dialogDismiss()
        
       -        
       +
        
        
        droid = android.Android()
       t@@ -904,7 +904,7 @@ config = None
        
        class ElectrumGui:
        
       -    def __init__(self, _config, _network):
       +    def __init__(self, _config, _network, plugins):
                global wallet, network, contacts, config
                network = _network
                config = _config
       t@@ -912,7 +912,7 @@ class ElectrumGui:
                network.register_callback('connected', update_callback)
                network.register_callback('disconnected', update_callback)
                network.register_callback('disconnecting', update_callback)
       -        
       +
                contacts = util.StoreDict(config, 'contacts')
        
                storage = WalletStorage(config.get_wallet_path())
       t@@ -1018,5 +1018,3 @@ class ElectrumGui:
                else:
                    seed = modal_input('Mnemonic', 'please enter your code')
                return str(seed)
       -
       -
   DIR diff --git a/gui/gtk.py b/gui/gtk.py
       t@@ -69,8 +69,8 @@ def show_seed_dialog(seed, parent):
        
            dialog = Gtk.MessageDialog(
                parent = parent,
       -        flags = Gtk.DialogFlags.MODAL, 
       -        buttons = Gtk.ButtonsType.OK, 
       +        flags = Gtk.DialogFlags.MODAL,
       +        buttons = Gtk.ButtonsType.OK,
                message_format = "Your wallet generation seed is:\n\n" + '"' + seed + '"'\
                    + "\n\nPlease keep it in a safe place; if you lose it, you will not be able to restore your wallet.\n\n" )
            dialog.set_title("Seed")
       t@@ -80,9 +80,9 @@ def show_seed_dialog(seed, parent):
        
        def restore_create_dialog():
        
       -    # ask if the user wants to create a new wallet, or recover from a seed. 
       +    # ask if the user wants to create a new wallet, or recover from a seed.
            # if he wants to recover, and nothing is found, do not create wallet
       -    dialog = Gtk.Dialog("electrum", parent=None, 
       +    dialog = Gtk.Dialog("electrum", parent=None,
                                flags=Gtk.DialogFlags.MODAL,
                                buttons= ("create", 0, "restore",1, "cancel",2)  )
        
       t@@ -102,7 +102,7 @@ def run_recovery_dialog():
            message = "Please enter your wallet seed or the corresponding mnemonic list of words, and the gap limit of your wallet."
            dialog = Gtk.MessageDialog(
                parent = None,
       -        flags = Gtk.DialogFlags.MODAL, 
       +        flags = Gtk.DialogFlags.MODAL,
                buttons = Gtk.ButtonsType.OK_CANCEL,
                message_format = message)
        
       t@@ -121,7 +121,7 @@ def run_recovery_dialog():
            seed_box.pack_start(seed_entry, False, False, 10)
            add_help_button(seed_box, '.')
            seed_box.show()
       -    vbox.pack_start(seed_box, False, False, 5)    
       +    vbox.pack_start(seed_box, False, False, 5)
        
            dialog.show()
            r = dialog.run()
       t@@ -142,10 +142,10 @@ def run_recovery_dialog():
        def run_settings_dialog(self):
        
            message = "Here are the settings of your wallet. For more explanations, click on the question mark buttons next to each input field."
       -        
       +
            dialog = Gtk.MessageDialog(
                parent = self.window,
       -        flags = Gtk.DialogFlags.MODAL, 
       +        flags = Gtk.DialogFlags.MODAL,
                buttons = Gtk.ButtonsType.OK_CANCEL,
                message_format = message)
        
       t@@ -171,7 +171,7 @@ def run_settings_dialog(self):
            add_help_button(fee, 'Fee per kilobyte of transaction. Recommended value:0.0001')
            fee.show()
            vbox.pack_start(fee, False,False, 5)
       -            
       +
            nz = Gtk.HBox()
            nz_entry = Gtk.Entry()
            nz_label = Gtk.Label(label='Display zeros:')
       t@@ -185,12 +185,12 @@ def run_settings_dialog(self):
            add_help_button(nz, "Number of zeros displayed after the decimal point.\nFor example, if this number is 2, then '5.' is displayed as '5.00'")
            nz.show()
            vbox.pack_start(nz, False,False, 5)
       -            
       +
            dialog.show()
            r = dialog.run()
            fee = fee_entry.get_text()
            nz = nz_entry.get_text()
       -        
       +
            dialog.destroy()
            if r==Gtk.ResponseType.CANCEL:
                return
       t@@ -238,7 +238,7 @@ def run_network_dialog( network, parent ):
            dialog.set_title("Server")
            dialog.set_image(image)
            image.show()
       -    
       +
            vbox = dialog.vbox
            host_box = Gtk.HBox()
            host_label = Gtk.Label(label='Connect to:')
       t@@ -291,11 +291,11 @@ def run_network_dialog( network, parent ):
            combobox.connect("changed", lambda x:set_protocol('tshg'[combobox.get_active()]))
            if network.is_connected():
                set_combobox(protocol)
       -        
       +
            server_list = Gtk.ListStore(str)
            for host in servers.keys():
                server_list.append([host])
       -    
       +
            treeview = Gtk.TreeView(model=server_list)
            treeview.show()
        
       t@@ -354,8 +354,8 @@ def run_network_dialog( network, parent ):
        def show_message(message, parent=None):
            dialog = Gtk.MessageDialog(
                parent = parent,
       -        flags = Gtk.DialogFlags.MODAL, 
       -        buttons = Gtk.ButtonsType.CLOSE, 
       +        flags = Gtk.DialogFlags.MODAL,
       +        buttons = Gtk.ButtonsType.CLOSE,
                message_format = message )
            dialog.show()
            dialog.run()
       t@@ -418,7 +418,7 @@ def change_password_dialog(is_encrypted, parent):
            new_password = password_entry.get_text()
            new_password2 = password2_entry.get_text()
            dialog.destroy()
       -    if result == Gtk.ResponseType.CANCEL: 
       +    if result == Gtk.ResponseType.CANCEL:
                return
        
            if new_password != new_password2:
       t@@ -480,7 +480,7 @@ class ElectrumWindow:
                self.create_about_tab()
                self.notebook.show()
                vbox.pack_start(self.notebook, True, True, 2)
       -        
       +
                self.status_bar = Gtk.Statusbar()
                vbox.pack_start(self.status_bar, False, False, 0)
        
       t@@ -578,7 +578,7 @@ class ElectrumWindow:
                                if to_address:
                                    s = r + ' <' + to_address + '>'
                                    GObject.idle_add( lambda: self.payto_entry.set_text(s) )
       -                
       +
        
                thread.start_new_thread(update_status_bar_thread, ())
                if self.wallet.seed:
       t@@ -618,7 +618,7 @@ class ElectrumWindow:
        
        
            def create_send_tab(self):
       -        
       +
                page = vbox = Gtk.VBox()
                page.show()
        
       t@@ -678,7 +678,7 @@ class ElectrumWindow:
                payto_sig_id = Gtk.Label(label='')
                payto_sig.pack_start(payto_sig_id, False, False, 0)
                vbox.pack_start(payto_sig, True, True, 5)
       -        
       +
        
                self.user_fee = False
        
       t@@ -709,7 +709,7 @@ class ElectrumWindow:
                        fee_entry.modify_text(Gtk.StateType.NORMAL, Gdk.color_parse("#cc0000"))
        
                amount_entry.connect('changed', entry_changed, False)
       -        fee_entry.connect('changed', entry_changed, True)        
       +        fee_entry.connect('changed', entry_changed, True)
        
                self.payto_entry = payto_entry
                self.payto_fee_entry = fee_entry
       t@@ -776,7 +776,7 @@ class ElectrumWindow:
        
                m1 = re.match('^(|([\w\-\.]+)@)((\w[\w\-]+\.)+[\w\-]+)$', r)
                m2 = re.match('(|([\w\-\.]+)@)((\w[\w\-]+\.)+[\w\-]+) \<([1-9A-HJ-NP-Za-km-z]{26,})\>', r)
       -        
       +
                if m1:
                    to_address = self.wallet.get_alias(r, True, self.show_message, self.question)
                    if not to_address:
       t@@ -821,8 +821,8 @@ class ElectrumWindow:
                    self.show_message( "This transaction requires a higher fee, or it will not be propagated by the network." )
                    return
        
       -            
       -        if label: 
       +
       +        if label:
                    self.wallet.labels[tx.hash()] = label
        
                status, msg = self.wallet.sendtx( tx )
       t@@ -854,7 +854,7 @@ class ElectrumWindow:
                        #        s = "self-signed"
                        #    msg = 'Alias: '+ m + '\nTarget address: '+ a[1] + '\n\nSigned by: ' + s + '\nSigning address:' + a[0]
                        #    self.show_message(msg)
       -            
       +
        
            def treeview_key_press(self, treeview, event):
                c = treeview.get_cursor()[0]
       t@@ -1134,7 +1134,7 @@ class ElectrumWindow:
                    self.network_button.set_tooltip_text("Not connected.")
                    text = "Not connected"
        
       -        self.status_bar.pop(self.context_id) 
       +        self.status_bar.pop(self.context_id)
                self.status_bar.push(self.context_id, text)
        
                if self.wallet.up_to_date and self.wallet_updated:
       t@@ -1214,7 +1214,7 @@ class ElectrumWindow:
                    + "Transaction ID:\n" + tx_hash + "\n\n" \
                    + "Status: %d confirmations\n"%conf
                if is_mine:
       -            if fee: 
       +            if fee:
                        tx_details += "Amount sent: %s\n"% format_satoshis(v-fee, False) \
                                      + "Transaction fee: %s\n"% format_satoshis(fee, False)
                    else:
       t@@ -1233,8 +1233,8 @@ class ElectrumWindow:
        
            def newaddress_dialog(self, w):
        
       -        title = "New Contact" 
       -        dialog = Gtk.Dialog(title, parent=self.window, 
       +        title = "New Contact"
       +        dialog = Gtk.Dialog(title, parent=self.window,
                                    flags=Gtk.DialogFlags.MODAL,
                                    buttons= ("cancel", 0, "ok",1)  )
                dialog.show()
       t@@ -1260,7 +1260,7 @@ class ElectrumWindow:
                address.pack_start(address_entry, True, True, 0)
                address.show()
                dialog.vbox.pack_start(address, False, True, 5)
       -        
       +
                result = dialog.run()
                address = address_entry.get_text()
                label = label_entry.get_text()
       t@@ -1273,18 +1273,18 @@ class ElectrumWindow:
                    else:
                        errorDialog = Gtk.MessageDialog(
                            parent=self.window,
       -                    flags=Gtk.DialogFlags.MODAL, 
       -                    buttons= Gtk.ButtonsType.CLOSE, 
       +                    flags=Gtk.DialogFlags.MODAL,
       +                    buttons= Gtk.ButtonsType.CLOSE,
                            message_format = "Invalid address")
                        errorDialog.show()
                        errorDialog.run()
                        errorDialog.destroy()
        
       -    
       +
        
        class ElectrumGui():
        
       -    def __init__(self, config, network):
       +    def __init__(self, config, network, plugins):
                self.network = network
                self.config = config
        
       t@@ -1321,7 +1321,7 @@ class ElectrumGui():
                        wallet.add_seed(seed, password)
                        wallet.create_master_keys(password)
                        wallet.create_main_account(password)
       -                
       +
                    else:
                        exit()
                else:
       t@@ -1351,8 +1351,8 @@ class ElectrumGui():
        
                dialog = Gtk.MessageDialog(
                    parent = None,
       -            flags = Gtk.DialogFlags.MODAL, 
       -            buttons = Gtk.ButtonsType.CANCEL, 
       +            flags = Gtk.DialogFlags.MODAL,
       +            buttons = Gtk.ButtonsType.CANCEL,
                    message_format = "Please wait..."  )
                dialog.show()
        
   DIR diff --git a/gui/jsonrpc.py b/gui/jsonrpc.py
       t@@ -36,7 +36,7 @@ class RequestHandler(SimpleJSONRPCRequestHandler):
                self.end_headers()
        
            def end_headers(self):
       -        self.send_header("Access-Control-Allow-Headers", 
       +        self.send_header("Access-Control-Allow-Headers",
                                 "Origin, X-Requested-With, Content-Type, Accept")
                self.send_header("Access-Control-Allow-Origin", "*")
                SimpleJSONRPCRequestHandler.end_headers(self)
       t@@ -45,7 +45,7 @@ class RequestHandler(SimpleJSONRPCRequestHandler):
        
        class ElectrumGui:
        
       -    def __init__(self, config, network):
       +    def __init__(self, config, network, plugins):
                self.network = network
                self.config = config
                storage = WalletStorage(self.config.get_wallet_path())
   DIR diff --git a/gui/qt/__init__.py b/gui/qt/__init__.py
       t@@ -58,10 +58,11 @@ class OpenFileEventFilter(QObject):
        
        class ElectrumGui:
        
       -    def __init__(self, config, network):
       +    def __init__(self, config, network, plugins):
                set_language(config.get('language'))
                self.network = network
                self.config = config
       +        self.plugins = plugins
                self.windows = []
                self.efilter = OpenFileEventFilter(self.windows)
                self.app = QApplication(sys.argv)
   DIR diff --git a/gui/qt/main_window.py b/gui/qt/main_window.py
       t@@ -2811,12 +2811,12 @@ class ElectrumWindow(QMainWindow):
        
        
            def plugins_dialog(self):
       -        from electrum.plugins import plugins, descriptions, is_available, loader
       -
                self.pluginsdialog = d = QDialog(self)
                d.setWindowTitle(_('Electrum Plugins'))
                d.setModal(1)
        
       +        plugins = self.gui_object.plugins
       +
                vbox = QVBoxLayout(d)
        
                # plugins
       t@@ -2828,40 +2828,34 @@ class ElectrumWindow(QMainWindow):
        
                w = QWidget()
                scroll.setWidget(w)
       -        w.setMinimumHeight(len(plugins)*35)
       +        w.setMinimumHeight(plugins.count() * 35)
        
                grid = QGridLayout()
                grid.setColumnStretch(0,1)
                w.setLayout(grid)
        
                def do_toggle(cb, name, w):
       -            p = plugins.get(name)
       +            p = plugins.toggle_enabled(self.config, name)
                    if p:
       -                p.disable()
       -                p.close()
       -                plugins.pop(name)
       -            else:
       -                module = loader(name)
       -                plugins[name] = p = module.Plugin(self.config, name)
       -                p.enable()
       +                # FIXME: this is hosed for multiple windows
                        p.wallet = self.wallet
                        p.load_wallet(self.wallet, self)
                        p.init_qt(self.gui_object)
       -            r = p.is_enabled()
       -            cb.setChecked(r)
       -            if w: w.setEnabled(r)
       +            enabled = p is not None
       +            cb.setChecked(enabled)
       +            if w: w.setEnabled(enabled)
        
                def mk_toggle(cb, name, w):
                    return lambda: do_toggle(cb, name, w)
        
       -        for i, descr in enumerate(descriptions):
       +        for i, descr in enumerate(plugins.descriptions):
                    name = descr['name']
                    p = plugins.get(name)
                    if descr.get('registers_wallet_type'):
                        continue
                    try:
                        cb = QCheckBox(descr['fullname'])
       -                cb.setEnabled(is_available(name, self.wallet))
       +                cb.setEnabled(plugins.is_available(name, self.wallet))
                        cb.setChecked(p is not None and p.is_enabled())
                        grid.addWidget(cb, i, 0)
                        if p and p.requires_settings():
   DIR diff --git a/gui/stdio.py b/gui/stdio.py
       t@@ -12,7 +12,7 @@ import sys, getpass, datetime
        
        class ElectrumGui:
        
       -    def __init__(self, config, network):
       +    def __init__(self, config, network, plugins):
                self.network = network
                self.config = config
                storage = WalletStorage(config.get_wallet_path())
       t@@ -33,7 +33,7 @@ class ElectrumGui:
                self.wallet = Wallet(storage)
                self.wallet.start_threads(network)
                self.contacts = StoreDict(self.config, 'contacts')
       -        
       +
                self.wallet.network.register_callback('updated', self.updated)
                self.wallet.network.register_callback('connected', self.connected)
                self.wallet.network.register_callback('disconnected', self.disconnected)
       t@@ -97,7 +97,7 @@ class ElectrumGui:
                delta = (80 - sum(width) - 4)/3
                format_str = "%"+"%d"%width[0]+"s"+"%"+"%d"%(width[1]+delta)+"s"+"%" \
                + "%d"%(width[2]+delta)+"s"+"%"+"%d"%(width[3]+delta)+"s"
       -        b = 0 
       +        b = 0
                messages = []
        
                for item in self.wallet.get_history():
       t@@ -123,7 +123,7 @@ class ElectrumGui:
                if self.wallet.network.is_connected():
                    if not self.wallet.up_to_date:
                        msg = _( "Synchronizing..." )
       -            else: 
       +            else:
                        c, u, x =  self.wallet.get_balance()
                        msg = _("Balance")+": %f  "%(Decimal(c) / COIN)
                        if u:
       t@@ -132,7 +132,7 @@ class ElectrumGui:
                            msg += "  [%f unmatured]"%(Decimal(x) / COIN)
                else:
                        msg = _( "Not connected" )
       -            
       +
                return(msg)
        
        
       t@@ -169,7 +169,7 @@ class ElectrumGui:
                    msg = list[i] if i < len(list) else ""
                    print(msg)
        
       -           
       +
            def main(self):
                while self.done == 0: self.main_command()
        
       t@@ -205,8 +205,8 @@ class ElectrumGui:
                except Exception as e:
                    print(str(e))
                    return
       -            
       -        if self.str_description: 
       +
       +        if self.str_description:
                    self.wallet.labels[tx.hash()] = self.str_description
        
                h = self.wallet.send_tx(tx)
       t@@ -232,7 +232,7 @@ class ElectrumGui:
        
            def password_dialog(self):
                return getpass.getpass()
       -        
       +
        
        #   XXX unused
        
       t@@ -240,6 +240,6 @@ class ElectrumGui:
                #if c == 10:
                #    out = self.run_popup('Address', ["Edit label", "Freeze", "Prioritize"])
                return
       -            
       +
            def run_contacts_tab(self, c):
                pass
   DIR diff --git a/gui/text.py b/gui/text.py
       t@@ -12,7 +12,7 @@ import tty, sys
        
        class ElectrumGui:
        
       -    def __init__(self, config, network):
       +    def __init__(self, config, network, plugins):
        
                self.config = config
                self.network = network
       t@@ -51,8 +51,8 @@ class ElectrumGui:
                self.str_amount = ""
                self.str_fee = ""
                self.history = None
       -       
       -        if self.network: 
       +
       +        if self.network:
                    self.network.register_callback('updated', self.update)
                    self.network.register_callback('connected', self.refresh)
                    self.network.register_callback('disconnected', self.refresh)
       t@@ -73,7 +73,7 @@ class ElectrumGui:
        
            def verify_seed(self):
                pass
       -    
       +
            def get_string(self, y, x):
                self.set_cursor(1)
                curses.echo()
       t@@ -85,7 +85,7 @@ class ElectrumGui:
        
            def update(self):
                self.update_history()
       -        if self.tab == 0: 
       +        if self.tab == 0:
                    self.print_history()
                self.refresh()
        
       t@@ -105,7 +105,7 @@ class ElectrumGui:
                delta = (self.maxx - sum(width) - 4)/3
                format_str = "%"+"%d"%width[0]+"s"+"%"+"%d"%(width[1]+delta)+"s"+"%"+"%d"%(width[2]+delta)+"s"+"%"+"%d"%(width[3]+delta)+"s"
        
       -        b = 0 
       +        b = 0
                self.history = []
        
                for item in self.wallet.get_history():
       t@@ -130,7 +130,7 @@ class ElectrumGui:
                elif self.network.is_connected():
                    if not self.wallet.up_to_date:
                        msg = _("Synchronizing...")
       -            else: 
       +            else:
                        c, u, x =  self.wallet.get_balance()
                        msg = _("Balance")+": %f  "%(Decimal(c) / COIN)
                        if u:
       t@@ -139,12 +139,12 @@ class ElectrumGui:
                            msg += "  [%f unmatured]"%(Decimal(x) / COIN)
                else:
                    msg = _("Not connected")
       -            
       +
                self.stdscr.addstr( self.maxy -1, 3, msg)
        
                for i in range(self.num_tabs):
                    self.stdscr.addstr( 0, 2 + 2*i + len(''.join(self.tab_names[0:i])), ' '+self.tab_names[i]+' ', curses.A_BOLD if self.tab == i else 0)
       -            
       +
                self.stdscr.addstr( self.maxy -1, self.maxx-30, ' '.join([_("Settings"), _("Network"), _("Quit")]))
        
        
       t@@ -221,7 +221,7 @@ class ElectrumGui:
            def run_history_tab(self, c):
                if c == 10:
                    out = self.run_popup('',["blah","foo"])
       -            
       +
        
            def edit_str(self, target, c, is_num=False):
                # detect backspace
       t@@ -246,11 +246,11 @@ class ElectrumGui:
                elif self.pos%6==5:
                    if c == 10: self.do_clear()
        
       -            
       +
            def run_receive_tab(self, c):
                if c == 10:
                    out = self.run_popup('Address', ["Edit label", "Freeze", "Prioritize"])
       -            
       +
            def run_contacts_tab(self, c):
                if c == 10 and self.contacts:
                    out = self.run_popup('Adress', ["Copy", "Pay to", "Edit label", "Delete"]).get('button')
       t@@ -263,7 +263,7 @@ class ElectrumGui:
                        s = self.get_string(6 + self.pos, 18)
                        if s:
                            self.wallet.labels[address] = s
       -            
       +
            def run_banner_tab(self, c):
                self.show_message(repr(c))
                pass
       t@@ -318,8 +318,8 @@ class ElectrumGui:
                except Exception as e:
                    self.show_message(str(e))
                    return
       -            
       -        if self.str_description: 
       +
       +        if self.str_description:
                    self.wallet.labels[tx.hash()] = self.str_description
        
                h = self.wallet.send_tx(tx)
       t@@ -375,7 +375,7 @@ class ElectrumGui:
                            proxy = None
        
                        self.network.set_parameters(host, port, protocol, proxy, auto_connect)
       -                
       +
        
        
            def settings_dialog(self):
       t@@ -396,11 +396,11 @@ class ElectrumGui:
                    {'label':'Password', 'type':'password', 'value':''}
                    ], buttons = 1)
                return out.get('Password')
       -        
       +
        
            def run_dialog(self, title, items, interval=2, buttons=None, y_pos=3):
                self.popup_pos = 0
       -        
       +
                self.w = curses.newwin( 5 + len(items)*interval + (2 if buttons else 0), 50, y_pos, 5)
                w = self.w
                out = {}
       t@@ -441,7 +441,7 @@ class ElectrumGui:
                    if buttons:
                        w.addstr( 5+interval*i, 10, "[  ok  ]",     curses.A_REVERSE if self.popup_pos%numpos==(numpos-2) else curses.color_pair(2))
                        w.addstr( 5+interval*i, 25, "[cancel]", curses.A_REVERSE if self.popup_pos%numpos==(numpos-1) else curses.color_pair(2))
       -                
       +
                    w.refresh()
        
                    c = self.stdscr.getch()
       t@@ -480,10 +480,9 @@ class ElectrumGui:
                            new_choice = choices[(j + 1)% len(choices)]
                            item['value'] = new_choice
                            out[item.get('label')] = item.get('value')
       -                    
       +
                        elif _type == 'button':
                            out['button'] = item.get('label')
                            break
        
                return out
       -
   DIR diff --git a/lib/plugins.py b/lib/plugins.py
       t@@ -26,70 +26,93 @@ from util import *
        from i18n import _
        from util import print_error, profiler
        
       -plugins = {}
       -descriptions = []
       -loader = None
       -
       -def is_available(name, w):
       -    for d in descriptions:
       -        if d.get('name') == name:
       -            break
       -    else:
       -        return False
       -    deps = d.get('requires', [])
       -    for dep, s in deps:
       -        try:
       -            __import__(dep)
       -        except ImportError:
       -            return False
       -    wallet_types = d.get('requires_wallet_type')
       -    if wallet_types:
       -        if w.wallet_type not in wallet_types:
       -            return False
       -    return True
       -
       -
       -def plugin_loader(config, name):
       -    global plugins
       -    if plugins.get(name) is None:
       -        print_error(_("Loading plugin by constructor:"), name)
       -        p = loader(name)
       -        plugins[name] = p.Plugin(config, name)
       -    return plugins[name]
       -
       -@profiler
       -def init_plugins(config, is_local, gui_name):
       -    global plugins, descriptions, loader
       -    if is_local:
       -        fp, pathname, description = imp.find_module('plugins')
       -        electrum_plugins = imp.load_module('electrum_plugins', fp, pathname, description)
       -        loader = lambda name: imp.load_source('electrum_plugins.' + name, os.path.join(pathname, name + '.py'))
       -    else:
       -        electrum_plugins = __import__('electrum_plugins')
       -        loader = lambda name: __import__('electrum_plugins.' + name, fromlist=['electrum_plugins'])
       -
       -    def register_wallet_type(name, x):
       -        import wallet
       -        x += (lambda: plugin_loader(config, name),)
       -        wallet.wallet_types.append(x)
       +class Plugins:
       +
       +    @profiler
       +    def __init__(self, config, is_local, gui_name):
       +        if is_local:
       +            find = imp.find_module('plugins')
       +            plugins = imp.load_module('electrum_plugins', *find)
       +            self.pathname = find[1]
       +        else:
       +            plugins = __import__('electrum_plugins')
       +            self.pathname = None
       +
       +        self.plugins = {}
       +        self.descriptions = plugins.descriptions
       +        for item in self.descriptions:
       +            name = item['name']
       +            if gui_name not in item.get('available_for', []):
       +                continue
       +            x = item.get('registers_wallet_type')
       +            if x:
       +                self.register_wallet_type(name, x)
       +            if config.get('use_' + name):
       +                self.load_plugin(config, name)
        
       -    descriptions = electrum_plugins.descriptions
       -    for item in descriptions:
       -        name = item['name']
       -        if gui_name not in item.get('available_for', []):
       -            continue
       -        x = item.get('registers_wallet_type')
       -        if x:
       -            register_wallet_type(name, x)
       -        if not config.get('use_' + name):
       -            continue
       +    def print_error(self, *msg):
       +        print_error("[%s]" % self.__class__.__name__, *msg)
       +
       +    def get(self, name):
       +        return self.plugins.get(name)
       +
       +    def count(self):
       +        return len(self.plugins)
       +
       +    def load_plugin(self, config, name):
       +        full_name = 'electrum_plugins.' + name
                try:
       -            p = loader(name)
       -            plugins[name] = p.Plugin(config, name)
       +            if self.pathname:  # local
       +                path = os.path.join(self.pathname, name + '.py')
       +                p = imp.load_source(full_name, path)
       +            else:
       +                p = __import__(full_name, fromlist=['electrum_plugins'])
       +            plugin = p.Plugin(config, name)
       +            self.plugins[name] = plugin
       +            self.print_error("loaded", name)
       +            return plugin
                except Exception:
                    print_msg(_("Error: cannot initialize plugin"), name)
                    traceback.print_exc(file=sys.stdout)
       +            return None
       +
       +    def toggle_enabled(self, config, name):
       +        p = self.get(name)
       +        config.set_key('use_' + name, p is None, True)
       +        if p:
       +            self.plugins.pop(name)
       +            p.close()
       +            self.print_error("closed", name)
       +            return None
       +        return self.load_plugin(config, name)
       +
       +    def is_available(self, name, w):
       +        for d in self.descriptions:
       +            if d.get('name') == name:
       +                break
       +        else:
       +            return False
       +        deps = d.get('requires', [])
       +        for dep, s in deps:
       +            try:
       +                __import__(dep)
       +            except ImportError:
       +                return False
       +        wallet_types = d.get('requires_wallet_type')
       +        if wallet_types:
       +            if w.wallet_type not in wallet_types:
       +                return False
       +        return True
       +
       +    def wallet_plugin_loader(self, config, name):
       +        if self.plugins.get(name) is None:
       +            self.load_plugin(config, name)
       +        return self.plugins[name]
        
       +    def register_wallet_type(self, name, x):
       +        import wallet
       +        x += (lambda: self.wallet_plugin_loader(config, name),)
       +        wallet.wallet_types.append(x)
        
        hook_names = set()
        hooks = {}
       t@@ -157,14 +180,6 @@ class BasePlugin:
            def requires_settings(self):
                return False
        
       -    def enable(self):
       -        self.set_enabled(True)
       -        return True
       -
       -    def disable(self):
       -        self.set_enabled(False)
       -        return True
       -
            @hook
            def load_wallet(self, wallet, window): pass
        
       t@@ -179,8 +194,5 @@ class BasePlugin:
            def is_available(self):
                return True
        
       -    def set_enabled(self, enabled):
       -        self.config.set_key('use_'+self.name, enabled, True)
       -
            def settings_dialog(self):
                pass