URI: 
       tmake plugins available without the GUI - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit 8f98ea4acacfadb4d6188d503f5c8d9e6be86e1f
   DIR parent 89c277de9d70b29918b5c6c3596bf4486584adb2
  HTML Author: ThomasV <thomasv@gitorious>
       Date:   Sat, 23 May 2015 10:38:19 +0200
       
       make plugins available without the GUI
       
       Diffstat:
         M electrum                            |       7 ++++---
         M gui/qt/main_window.py               |      45 ++++++++++++++++++-------------
         M lib/plugins.py                      |     109 +++++++++++++++++++++++--------
         M plugins/__init__.py                 |     107 ++++++++++++++++++++++++++++++-
         M plugins/audio_modem.py              |       7 -------
         M plugins/btchipwallet.py             |      14 +++-----------
         M plugins/cosigner_pool.py            |      11 -----------
         M plugins/exchange_rate.py            |      35 +++++++++++++------------------
         M plugins/greenaddress_instant.py     |       8 --------
         M plugins/labels.py                   |       6 ------
         M plugins/openalias.py                |       6 ------
         M plugins/plot.py                     |       7 -------
         M plugins/trezor.py                   |      14 +++-----------
         M plugins/trustedcoin.py              |      16 ++--------------
         M plugins/virtualkeyboard.py          |       6 ------
       
       15 files changed, 241 insertions(+), 157 deletions(-)
       ---
   DIR diff --git a/electrum b/electrum
       t@@ -222,6 +222,10 @@ if __name__ == '__main__':
            else:
                cmd = args[0]
        
       +    # initialize plugins.
       +    # FIXME: check gui
       +    init_plugins(config, is_bundle or is_local or is_android, cmd=='gui')
       +
            if cmd == 'gui':
                gui_name = config.get('gui', 'classic')
                if gui_name in ['lite', 'classic']:
       t@@ -233,9 +237,6 @@ if __name__ == '__main__':
                    sys.exit()
                    #sys.exit("Error: Unknown GUI: " + gui_name )
        
       -        if gui_name=='qt':
       -            init_plugins(config, is_bundle or is_local or is_android)
       -
                # network interface
                if not options.offline:
                    s = get_daemon(config, False)
   DIR diff --git a/gui/qt/main_window.py b/gui/qt/main_window.py
       t@@ -2657,7 +2657,7 @@ class ElectrumWindow(QMainWindow):
        
        
            def plugins_dialog(self):
       -        from electrum.plugins import plugins
       +        from electrum.plugins import plugins, descriptions, is_available, loader
        
                self.pluginsdialog = d = QDialog(self)
                d.setWindowTitle(_('Electrum Plugins'))
       t@@ -2680,37 +2680,44 @@ class ElectrumWindow(QMainWindow):
                grid.setColumnStretch(0,1)
                w.setLayout(grid)
        
       -        def do_toggle(cb, p, w):
       -            if p.is_enabled():
       -                if p.disable():
       -                    p.close()
       +        def do_toggle(cb, name, w):
       +            p = plugins.get(name)
       +            if p:
       +                p.disable()
       +                p.close()
       +                plugins.pop(name)
                    else:
       -                if p.enable():
       -                    p.load_wallet(self.wallet)
       -                    p.init_qt(self.gui_object)
       +                module = loader(name)
       +                plugins[name] = p = module.Plugin(self.config, name)
       +                p.enable()
       +                p.wallet = self.wallet
       +                p.load_wallet(self.wallet)
       +                p.init_qt(self.gui_object)
                    r = p.is_enabled()
                    cb.setChecked(r)
                    if w: w.setEnabled(r)
        
       -        def mk_toggle(cb, p, w):
       -            return lambda: do_toggle(cb,p,w)
       +        def mk_toggle(cb, name, w):
       +            return lambda: do_toggle(cb, name, w)
        
       -        for i, p in enumerate(plugins):
       +        for i, descr in enumerate(descriptions):
       +            name = descr['name']
       +            p = plugins.get(name)
                    try:
       -                cb = QCheckBox(p.fullname())
       -                cb.setDisabled(not p.is_available())
       -                cb.setChecked(p.is_enabled())
       +                cb = QCheckBox(descr['fullname'])
       +                cb.setEnabled(is_available(name, self.wallet))
       +                cb.setChecked(p is not None)
                        grid.addWidget(cb, i, 0)
       -                if p.requires_settings():
       +                if p and p.requires_settings():
                            w = p.settings_widget(self)
       -                    w.setEnabled( p.is_enabled() )
       +                    w.setEnabled(p.is_enabled())
                            grid.addWidget(w, i, 1)
                        else:
                            w = None
       -                cb.clicked.connect(mk_toggle(cb,p,w))
       -                grid.addWidget(HelpButton(p.description()), i, 2)
       +                cb.clicked.connect(mk_toggle(cb, name, w))
       +                grid.addWidget(HelpButton(descr['description']), i, 2)
                    except Exception:
       -                print_msg("Error: cannot display plugin", p)
       +                print_msg("Error: cannot display plugin", name)
                        traceback.print_exc(file=sys.stdout)
                grid.setRowStretch(i+1,1)
                vbox.addLayout(Buttons(CloseButton(d)))
   DIR diff --git a/lib/plugins.py b/lib/plugins.py
       t@@ -1,34 +1,89 @@
       -from util import print_error
       -import traceback, sys
       +#!/usr/bin/env python
       +#
       +# Electrum - lightweight Bitcoin client
       +# Copyright (C) 2015 Thomas Voegtlin
       +#
       +# This program is free software: you can redistribute it and/or modify
       +# it under the terms of the GNU General Public License as published by
       +# the Free Software Foundation, either version 3 of the License, or
       +# (at your option) any later version.
       +#
       +# This program is distributed in the hope that it will be useful,
       +# but WITHOUT ANY WARRANTY; without even the implied warranty of
       +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
       +# GNU General Public License for more details.
       +#
       +# You should have received a copy of the GNU General Public License
       +# along with this program. If not, see <http://www.gnu.org/licenses/>.
       +
       +import traceback
       +import sys
       +import os
       +import imp
       +import pkgutil
       +
        from util import *
        from i18n import _
       +from util import print_error
        
       -plugins = []
       -
       -
       -def init_plugins(config, local):
       -    import imp, pkgutil, __builtin__, os
       -    global plugins
       +plugins = {}
       +descriptions = []
       +loader = None
        
       -    if local:
       +def is_available(name, w):
       +    for d in descriptions:
       +        if d.get('name') == name:
       +            break
       +    else:
       +        return False
       +    deps = d.get('requires', [])
       +    for dep 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 init_plugins(config, is_local, is_gui):
       +    global plugins, descriptions, loader
       +    if is_local:
                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)
       +        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:
       -        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]
       +        electrum_plugins = __import__('electrum_plugins')
       +        loader = lambda name: __import__('electrum_plugins.' + name, fromlist=['electrum_plugins'])
        
       -    for name, p in zip(plugin_names, plugin_modules):
       +    def register_wallet_type(name):
       +        # fixme: load plugins only if really needed
       +        import wallet
                try:
       -            plugins.append( p.Plugin(config, name) )
       +            p = loader(name)
       +            plugins[name] = p.Plugin(config, name)
       +        except:
       +            return
       +        x = plugins[name].get_wallet_type()
       +        wallet.wallet_types.append(x)
       +
       +    descriptions = electrum_plugins.descriptions
       +    for item in descriptions:
       +        name = item['name']
       +        if item.get('registers_wallet_type'):
       +            register_wallet_type(name)
       +        if not config.get('use_' + name):
       +            continue
       +        try:
       +            p = loader(name)
       +            plugins[name] = p.Plugin(config, name)
                except Exception:
       -            print_msg(_("Error: cannot initialize plugin"),p)
       +            print_msg(_("Error: cannot initialize plugin"), p)
                    traceback.print_exc(file=sys.stdout)
        
       -
        hook_names = set()
        hooks = {}
        
       t@@ -81,15 +136,17 @@ class BasePlugin:
                        l.append((self, getattr(self, k)))
                        hooks[k] = l
        
       -    def fullname(self):
       -        return self.name
       +    def close(self):
       +        # remove self from hooks
       +        for k in dir(self):
       +            if k in hook_names:
       +                l = hooks.get(k, [])
       +                l.remove((self, getattr(self, k)))
       +                hooks[k] = l
        
            def print_error(self, *msg):
                print_error("[%s]"%self.name, *msg)
        
       -    def description(self):
       -        return 'undefined'
       -
            def requires_settings(self):
                return False
        
       t@@ -111,8 +168,6 @@ class BasePlugin:
        
            #def init(self): pass
        
       -    def close(self): pass
       -
            def is_enabled(self):
                return self.is_available() and self.config.get('use_'+self.name) is True
        
   DIR diff --git a/plugins/__init__.py b/plugins/__init__.py
       t@@ -1 +1,106 @@
       -# plugins
       +#!/usr/bin/env python
       +#
       +# Electrum - lightweight Bitcoin client
       +# Copyright (C) 2015 Thomas Voegtlin
       +#
       +# This program is free software: you can redistribute it and/or modify
       +# it under the terms of the GNU General Public License as published by
       +# the Free Software Foundation, either version 3 of the License, or
       +# (at your option) any later version.
       +#
       +# This program is distributed in the hope that it will be useful,
       +# but WITHOUT ANY WARRANTY; without even the implied warranty of
       +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
       +# GNU General Public License for more details.
       +#
       +# You should have received a copy of the GNU General Public License
       +# along with this program. If not, see <http://www.gnu.org/licenses/>.
       +
       +from electrum.i18n import _
       +
       +descriptions = [
       +    {
       +        'name': 'audio_modem',
       +        'fullname': _('Audio MODEM'),
       +        'description': ('Provides support for air-gapped transaction signing.\n\n'
       +                        'Requires http://github.com/romanz/amodem/'),
       +        'requires': ['amodem'],
       +        'GUI': ['qt']
       +    },
       +    {
       +        'name': 'btchipwallet',
       +        'fullname': _('BTChip Wallet'),
       +        'description': _('Provides support for BTChip hardware wallet') + '\n\n' + _('Requires github.com/btchip/btchip-python'),
       +        'requires': ['btchip'],
       +        'requires_wallet_type': ['btchip'],
       +        'registers_wallet_type': True
       +    },
       +    {
       +        'name': 'cosigner_pool',
       +        'fullname': _('Cosigner Pool'),
       +        'description': ' '.join([
       +            _("This plugin facilitates the use of multi-signatures wallets."),
       +            _("It sends and receives partially signed transactions from/to your cosigner wallet."),
       +            _("Transactions are encrypted and stored on a remote server.")
       +            ]),
       +        'GUI': ['qt'],
       +        'requires_wallet_type': ['2of2', '2of3']
       +    },
       +    {
       +        'name': 'exchange_rate',
       +        'fullname': _("Exchange rates"),
       +        'description': """exchange rates, retrieved from blockchain.info, CoinDesk, or Coinbase"""
       +    },
       +    {
       +        'name': 'greenaddress_instant',
       +        'fullname': 'GreenAddress instant',
       +        'description': _("Allows validating if your transactions have instant confirmations by GreenAddress")
       +    },
       +    {
       +        'name': 'labels',
       +        'fullname': _('LabelSync'),
       +        'description': '%s\n\n%s' % (_("The new and improved LabelSync plugin. This can sync your labels across multiple Electrum installs by using a remote database to save your data. Labels, transactions ids and addresses are encrypted before they are sent to the remote server."), _("The label sync's server software is open-source as well and can be found on github.com/maran/electrum-sync-server"))
       +    },
       +    {
       +        'name': 'openalias',
       +        'fullname': 'OpenAlias',
       +        'description': 'Allow for payments to OpenAlias addresses.\nRequires dnspython',
       +        'requires': ['dns']
       +    },
       +    {
       +        'name': 'plot',
       +        'fullname': 'Plot History',
       +        'description': '\n'.join([
       +            _("Ability to plot transaction history in graphical mode."),
       +            _("Warning: Requires matplotlib library.")
       +        ]),
       +        'requires': ['matplotlib'],
       +        'GUI': ['qt']
       +    },
       +    {
       +        'name':'trezor',
       +        'fullname': 'Trezor Wallet',
       +        'description': 'Provides support for Trezor hardware wallet\n\nRequires github.com/trezor/python-trezor',
       +        'GUI': ['qt'],
       +        'requires': ['trezorlib'],
       +        'requires_wallet_type': ['trezor'],
       +        'registers_wallet_type': True
       +    },
       +    {
       +        'name': 'trustedcoin',
       +        'fullname': _('Two Factor Authentication'),
       +        'description': ''.join([
       +            _("This plugin adds two-factor authentication to your wallet."), '<br/>',
       +            _("For more information, visit"),
       +            " <a href=\"https://api.trustedcoin.com/#/electrum-help\">https://api.trustedcoin.com/#/electrum-help</a>"
       +        ]),
       +        'GUI': ['none', 'qt'],
       +        'requires_wallet_type': ['2fa'],
       +        'registers_wallet_type': True
       +    },
       +    {
       +        'name': 'virtualkeyboard',
       +        'fullname': 'Virtual Keyboard',
       +        'description': '%s\n%s' % (_("Add an optional virtual keyboard to the password dialog."), _("Warning: do not use this if it makes you pick a weaker password.")),
       +    }
       +]
   DIR diff --git a/plugins/audio_modem.py b/plugins/audio_modem.py
       t@@ -35,13 +35,6 @@ class Plugin(BasePlugin):
                        'Linux': 'libportaudio.so'
                    }[platform.system()]
        
       -    def fullname(self):
       -        return 'Audio MODEM'
       -
       -    def description(self):
       -        return ('Provides support for air-gapped transaction signing.\n\n'
       -                'Requires http://github.com/romanz/amodem/')
       -
            def is_available(self):
                return amodem is not None
        
   DIR diff --git a/plugins/btchipwallet.py b/plugins/btchipwallet.py
       t@@ -35,18 +35,13 @@ except ImportError:
        
        class Plugin(BasePlugin):
        
       -    def fullname(self):
       -        return 'BTChip Wallet'
       -
       -    def description(self):
       -        return 'Provides support for BTChip hardware wallet\n\nRequires github.com/btchip/btchip-python'
       -
            def __init__(self, gui, name):
                BasePlugin.__init__(self, gui, name)
                self._is_available = self._init()
                self.wallet = None
       -        if self._is_available:
       -            electrum.wallet.wallet_types.append(('hardware', 'btchip', _("BTChip wallet"), BTChipWallet))
       +
       +    def get_wallet_type(self):
       +        return ('hardware', 'btchip', _("BTChip wallet"), BTChipWallet)
        
            def _init(self):
                return BTCHIP
       t@@ -70,9 +65,6 @@ class Plugin(BasePlugin):
                    return False
                return True
        
       -    def enable(self):
       -        return BasePlugin.enable(self)
       -
            def btchip_is_connected(self):
                try:
                    self.wallet.get_client().getFirmwareVersion()
   DIR diff --git a/plugins/cosigner_pool.py b/plugins/cosigner_pool.py
       t@@ -37,7 +37,6 @@ import traceback
        
        PORT = 12344
        HOST = 'ecdsa.net'
       -description = _("This plugin facilitates the use of multi-signatures wallets. It sends and receives partially signed transactions from/to your cosigner wallet. Transactions are encrypted and stored on a remote server.") 
        server = xmlrpclib.ServerProxy('http://%s:%d'%(HOST,PORT), allow_none=True)
        
        
       t@@ -85,21 +84,11 @@ class Plugin(BasePlugin):
            wallet = None
            listener = None
        
       -    def fullname(self):
       -        return 'Cosigner Pool'
       -
       -    def description(self):
       -        return description
       -
            @hook
            def init_qt(self, gui):
                self.win = gui.main_window
                self.win.connect(self.win, SIGNAL('cosigner:receive'), self.on_receive)
        
       -    def enable(self):
       -        self.set_enabled(True)
       -        return True
       -
            def is_available(self):
                if self.wallet is None:
                    return True
   DIR diff --git a/plugins/exchange_rate.py b/plugins/exchange_rate.py
       t@@ -48,8 +48,8 @@ class Exchanger(threading.Thread):
                self.query_rates = threading.Event()
                self.use_exchange = self.parent.config.get('use_exchange', "Blockchain")
                self.parent.exchanges = EXCHANGES
       -        self.parent.win.emit(SIGNAL("refresh_exchanges_combo()"))
       -        self.parent.win.emit(SIGNAL("refresh_currencies_combo()"))
       +        #self.parent.win.emit(SIGNAL("refresh_exchanges_combo()"))
       +        #self.parent.win.emit(SIGNAL("refresh_currencies_combo()"))
                self.is_running = False
        
            def get_json(self, site, get_string):
       t@@ -180,18 +180,14 @@ class Exchanger(threading.Thread):
        
        class Plugin(BasePlugin):
        
       -    def fullname(self):
       -        return "Exchange rates"
       -
       -    def description(self):
       -        return """exchange rates, retrieved from blockchain.info, CoinDesk, or Coinbase"""
       -
       -
            def __init__(self,a,b):
                BasePlugin.__init__(self,a,b)
                self.currencies = [self.fiat_unit()]
                self.exchanges = [self.config.get('use_exchange', "Blockchain")]
       -        self.exchanger = None
       +        # Do price discovery
       +        self.exchanger = Exchanger(self)
       +        self.exchanger.start()
       +        self.win = None
        
            @hook
            def init_qt(self, gui):
       t@@ -201,26 +197,25 @@ class Plugin(BasePlugin):
                self.btc_rate = Decimal("0.0")
                self.resp_hist = {}
                self.tx_list = {}
       -        if self.exchanger is None:
       -            # Do price discovery
       -            self.exchanger = Exchanger(self)
       -            self.exchanger.start()
       -            self.gui.exchanger = self.exchanger #
       -            self.add_send_edit()
       -            self.add_receive_edit()
       -            self.win.update_status()
       +        self.gui.exchanger = self.exchanger #
       +        self.add_send_edit()
       +        self.add_receive_edit()
       +        self.win.update_status()
        
            def close(self):
       +        BasePlugin.close(self)
                self.exchanger.stop()
                self.exchanger = None
       +        self.gui.exchanger = None
                self.send_fiat_e.hide()
                self.receive_fiat_e.hide()
                self.win.update_status()
        
            def set_currencies(self, currency_options):
                self.currencies = sorted(currency_options)
       -        self.win.emit(SIGNAL("refresh_currencies()"))
       -        self.win.emit(SIGNAL("refresh_currencies_combo()"))
       +        if self.win:
       +            self.win.emit(SIGNAL("refresh_currencies()"))
       +            self.win.emit(SIGNAL("refresh_currencies_combo()"))
        
            @hook
            def get_fiat_balance_text(self, btc_balance, r):
   DIR diff --git a/plugins/greenaddress_instant.py b/plugins/greenaddress_instant.py
       t@@ -31,19 +31,11 @@ from electrum.i18n import _
        from electrum.bitcoin import regenerate_key
        
        
       -description = _("Allows validating if your transactions have instant confirmations by GreenAddress")
       -
        
        class Plugin(BasePlugin):
        
            button_label = _("Verify GA instant")
        
       -    def fullname(self):
       -        return 'GreenAddress instant'
       -
       -    def description(self):
       -        return description
       -
            @hook 
            def init_qt(self, gui):
                self.win = gui.main_window
   DIR diff --git a/plugins/labels.py b/plugins/labels.py
       t@@ -28,12 +28,6 @@ class Plugin(BasePlugin):
            target_host = 'sync.bytesized-hosting.com:9090'
            encode_password = None
        
       -    def fullname(self):
       -        return _('LabelSync')
       -
       -    def description(self):
       -        return '%s\n\n%s' % (_("The new and improved LabelSync plugin. This can sync your labels across multiple Electrum installs by using a remote database to save your data. Labels, transactions ids and addresses are encrypted before they are sent to the remote server."), _("The label sync's server software is open-source as well and can be found on github.com/maran/electrum-sync-server"))
       -
            def version(self):
                return "0.0.1"
        
   DIR diff --git a/plugins/openalias.py b/plugins/openalias.py
       t@@ -54,12 +54,6 @@ except ImportError:
        
        
        class Plugin(BasePlugin):
       -    def fullname(self):
       -        return 'OpenAlias'
       -
       -    def description(self):
       -        return 'Allow for payments to OpenAlias addresses.\nRequires dnspython'
       -
            def is_available(self):
                return OA_READY
        
   DIR diff --git a/plugins/plot.py b/plugins/plot.py
       t@@ -22,13 +22,6 @@ except:
        
        class Plugin(BasePlugin):
        
       -
       -    def fullname(self):
       -        return 'Plot History'
       -
       -    def description(self):
       -        return '%s\n%s' % (_("Ability to plot transaction history in graphical mode."), _("Warning: Requires matplotlib library."))
       -
            def is_available(self):
                if flag_matlib:
                    return True
   DIR diff --git a/plugins/trezor.py b/plugins/trezor.py
       t@@ -41,19 +41,14 @@ def give_error(message):
        
        class Plugin(BasePlugin):
        
       -    def fullname(self):
       -        return 'Trezor Wallet'
       -
       -    def description(self):
       -        return 'Provides support for Trezor hardware wallet\n\nRequires github.com/trezor/python-trezor'
       -
            def __init__(self, config, name):
                BasePlugin.__init__(self, config, name)
                self._is_available = self._init()
                self._requires_settings = True
                self.wallet = None
       -        if self._is_available:
       -            electrum.wallet.wallet_types.append(('hardware', 'trezor', _("Trezor wallet"), TrezorWallet))
       +
       +    def get_wallet_type(self):
       +        return ('hardware', 'trezor', _("Trezor wallet"), TrezorWallet)
        
            def _init(self):
                return TREZOR
       t@@ -80,9 +75,6 @@ class Plugin(BasePlugin):
                    return False
                return True
        
       -    def enable(self):
       -        return BasePlugin.enable(self)
       -
            def trezor_is_connected(self):
                try:
                    self.wallet.get_client().ping('t')
   DIR diff --git a/plugins/trustedcoin.py b/plugins/trustedcoin.py
       t@@ -210,17 +210,12 @@ class Plugin(BasePlugin):
        
            def __init__(self, x, y):
                BasePlugin.__init__(self, x, y)
       -        electrum.wallet.wallet_types.append(('twofactor', '2fa', _("Wallet with two-factor authentication"), Wallet_2fa))
                self.seed_func = lambda x: bitcoin.is_new_seed(x, SEED_PREFIX)
                self.billing_info = None
                self.is_billing = False
        
       -    def fullname(self):
       -        return 'Two Factor Authentication'
       -
       -    def description(self):
       -        return _("This plugin adds two-factor authentication to your wallet.") + '<br/>'\
       -            + _("For more information, visit") + " <a href=\"https://api.trustedcoin.com/#/electrum-help\">https://api.trustedcoin.com/#/electrum-help</a>"
       +    def get_wallet_type(self):
       +        return ('twofactor', '2fa', _("Wallet with two-factor authentication"), Wallet_2fa)
        
            def is_available(self):
                if not self.wallet:
       t@@ -266,13 +261,6 @@ class Plugin(BasePlugin):
                address = public_key_to_bc_address( cK )
                return address
        
       -    def enable(self):
       -        if self.is_enabled():
       -            self.window.show_message('Error: Two-factor authentication is already activated on this wallet')
       -            return
       -        self.set_enabled(True)
       -        self.window.show_message('Two-factor authentication is enabled.')
       -
            def create_extended_seed(self, wallet, window):
                seed = wallet.make_seed()
                if not window.show_seed(seed, None):
   DIR diff --git a/plugins/virtualkeyboard.py b/plugins/virtualkeyboard.py
       t@@ -5,12 +5,6 @@ import random
        
        class Plugin(BasePlugin):
        
       -    def fullname(self):
       -        return 'Virtual Keyboard'
       -
       -    def description(self):
       -        return '%s\n%s' % (_("Add an optional virtual keyboard to the password dialog."), _("Warning: do not use this if it makes you pick a weaker password."))
       -
            @hook
            def init_qt(self, gui):
                self.gui = gui