URI: 
       tplugins: separate GUIs using child classes - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit 2c0489c8094b2ba9409ae0e36bfe674ce327d883
   DIR parent 175fdbcac6fc1bd5ed54ceb709200b746877935d
  HTML Author: ThomasV <thomasv@electrum.org>
       Date:   Mon, 23 Nov 2015 14:15:25 +0100
       
       plugins: separate GUIs using child classes
       
       Diffstat:
         M gui/qt/main_window.py               |       2 --
         M lib/plugins.py                      |      11 ++++++++++-
         M plugins/audio_modem.py              |       2 +-
         M plugins/btchipwallet.py             |     194 ++++++++++++++++---------------
         M plugins/cosigner_pool.py            |       2 +-
         M plugins/email_requests.py           |       2 +-
         M plugins/exchange_rate.py            |     175 ++++++++++++++++---------------
         M plugins/greenaddress_instant.py     |       2 +-
         M plugins/keepkey.py                  |     498 ++++++++++++++++---------------
         M plugins/labels.py                   |     171 +++++++++++++++----------------
         M plugins/plot.py                     |       2 +-
         M plugins/trezor.py                   |     500 ++++++++++++++++---------------
         M plugins/trustedcoin.py              |      19 ++++++++++++-------
         M plugins/virtualkeyboard.py          |       2 +-
       
       14 files changed, 808 insertions(+), 774 deletions(-)
       ---
   DIR diff --git a/gui/qt/main_window.py b/gui/qt/main_window.py
       t@@ -107,7 +107,6 @@ expiration_values = [
        
        
        class ElectrumWindow(QMainWindow, PrintError):
       -    labelsChanged = pyqtSignal()
        
            def __init__(self, config, network, gui_object):
                QMainWindow.__init__(self)
       t@@ -157,7 +156,6 @@ class ElectrumWindow(QMainWindow, PrintError):
        
                self.connect(self, QtCore.SIGNAL('payment_request_ok'), self.payment_request_ok)
                self.connect(self, QtCore.SIGNAL('payment_request_error'), self.payment_request_error)
       -        self.labelsChanged.connect(self.update_tabs)
                self.history_list.setFocus(True)
        
                # network callbacks
   DIR diff --git a/lib/plugins.py b/lib/plugins.py
       t@@ -40,6 +40,7 @@ class Plugins(PrintError):
        
                self.plugins = {}
                self.network = None
       +        self.gui_name = gui_name
                self.descriptions = plugins.descriptions
                for item in self.descriptions:
                    name = item['name']
       t@@ -66,7 +67,15 @@ class Plugins(PrintError):
                        p = imp.load_source(full_name, path)
                    else:
                        p = __import__(full_name, fromlist=['electrum_plugins'])
       -            plugin = p.Plugin(self, config, name)
       +
       +            if self.gui_name == 'qt':
       +                klass = p.QtPlugin
       +            elif self.gui_name == 'cmdline':
       +                klass = p.CmdlinePlugin
       +            else:
       +                return
       +
       +            plugin = klass(self, config, name)
                    if self.network:
                        self.network.add_jobs(plugin.thread_jobs())
                    self.plugins[name] = plugin
   DIR diff --git a/plugins/audio_modem.py b/plugins/audio_modem.py
       t@@ -25,7 +25,7 @@ except ImportError:
            print_error('Audio MODEM is not found.')
        
        
       -class Plugin(BasePlugin):
       +class QtPlugin(BasePlugin):
        
            def __init__(self, parent, config, name):
                BasePlugin.__init__(self, parent, config, name)
   DIR diff --git a/plugins/btchipwallet.py b/plugins/btchipwallet.py
       t@@ -1,5 +1,3 @@
       -from PyQt4.Qt import QApplication, QMessageBox, QDialog, QInputDialog, QLineEdit, QVBoxLayout, QLabel, QThread, SIGNAL
       -import PyQt4.QtCore as QtCore
        from binascii import unhexlify
        from binascii import hexlify
        from struct import pack,unpack
       t@@ -7,7 +5,6 @@ from sys import stderr
        from time import sleep
        
        import electrum
       -from electrum_gui.qt.password_dialog import make_password_dialog, run_password_dialog
        from electrum.account import BIP32_Account
        from electrum.bitcoin import EncodeBase58Check, DecodeBase58Check, public_key_to_bc_address, bc_address_to_hash_160
        from electrum.i18n import _
       t@@ -32,96 +29,6 @@ try:
        except ImportError:
            BTCHIP = False
        
       -class Plugin(BasePlugin):
       -
       -    def __init__(self, parent, config, name):
       -        BasePlugin.__init__(self, parent, config, name)
       -        self._is_available = self._init()
       -        self.wallet = None
       -        self.handler = None
       -
       -    def constructor(self, s):
       -        return BTChipWallet(s)
       -
       -    def _init(self):
       -        return BTCHIP
       -
       -    def is_available(self):
       -        if not self._is_available:
       -            return False
       -        if not self.wallet:
       -            return False
       -        if self.wallet.storage.get('wallet_type') != 'btchip':
       -            return False
       -        return True
       -
       -    def set_enabled(self, enabled):
       -        self.wallet.storage.put('use_' + self.name, enabled)
       -
       -    def is_enabled(self):
       -        if not self.is_available():
       -            return False
       -        if self.wallet.has_seed():
       -            return False
       -        return True
       -
       -    def btchip_is_connected(self):
       -        try:
       -            self.wallet.get_client().getFirmwareVersion()
       -        except:
       -            return False
       -        return True
       -
       -    @hook
       -    def cmdline_load_wallet(self, wallet):
       -        self.wallet = wallet
       -        self.wallet.plugin = self
       -        if self.handler is None:
       -            self.handler = BTChipCmdLineHandler()
       -
       -    @hook
       -    def load_wallet(self, wallet, window):
       -        self.wallet = wallet
       -        self.wallet.plugin = self
       -        if self.handler is None:
       -            self.handler = BTChipQTHandler(window)
       -        if self.btchip_is_connected():
       -            if not self.wallet.check_proper_device():
       -                QMessageBox.information(window, _('Error'), _("This wallet does not match your Ledger device"), _('OK'))
       -                self.wallet.force_watching_only = True
       -        else:
       -            QMessageBox.information(window, _('Error'), _("Ledger device not detected.\nContinuing in watching-only mode."), _('OK'))
       -            self.wallet.force_watching_only = True
       -
       -    @hook
       -    def close_wallet(self):
       -        self.wallet = None
       -
       -    @hook
       -    def installwizard_load_wallet(self, wallet, window):
       -        if type(wallet) != BTChipWallet:
       -            return
       -        self.load_wallet(wallet, window)
       -
       -    @hook
       -    def installwizard_restore(self, wizard, storage):
       -        if storage.get('wallet_type') != 'btchip':
       -            return
       -        wallet = BTChipWallet(storage)
       -        try:
       -            wallet.create_main_account(None)
       -        except BaseException as e:
       -            QMessageBox.information(None, _('Error'), str(e), _('OK'))
       -            return
       -        return wallet
       -
       -    @hook
       -    def sign_tx(self, window, tx):
       -        tx.error = None
       -        try:
       -            self.wallet.sign_transaction(tx, None)
       -        except Exception as e:
       -            tx.error = str(e)
        
        class BTChipWallet(BIP32_HD_Wallet):
            wallet_type = 'btchip'
       t@@ -517,6 +424,98 @@ class BTChipWallet(BIP32_HD_Wallet):
                    return False, None, None
                return True, response, response
        
       +
       +class Plugin(BasePlugin):
       +
       +    def __init__(self, parent, config, name):
       +        BasePlugin.__init__(self, parent, config, name)
       +        self._is_available = self._init()
       +        self.wallet = None
       +        self.handler = None
       +
       +    def constructor(self, s):
       +        return BTChipWallet(s)
       +
       +    def _init(self):
       +        return BTCHIP
       +
       +    def is_available(self):
       +        if not self._is_available:
       +            return False
       +        if not self.wallet:
       +            return False
       +        if self.wallet.storage.get('wallet_type') != 'btchip':
       +            return False
       +        return True
       +
       +    def set_enabled(self, enabled):
       +        self.wallet.storage.put('use_' + self.name, enabled)
       +
       +    def is_enabled(self):
       +        if not self.is_available():
       +            return False
       +        if self.wallet.has_seed():
       +            return False
       +        return True
       +
       +    def btchip_is_connected(self):
       +        try:
       +            self.wallet.get_client().getFirmwareVersion()
       +        except:
       +            return False
       +        return True
       +
       +    @hook
       +    def close_wallet(self):
       +        self.wallet = None
       +
       +    @hook
       +    def installwizard_load_wallet(self, wallet, window):
       +        if type(wallet) != BTChipWallet:
       +            return
       +        self.load_wallet(wallet, window)
       +
       +    @hook
       +    def installwizard_restore(self, wizard, storage):
       +        if storage.get('wallet_type') != 'btchip':
       +            return
       +        wallet = BTChipWallet(storage)
       +        try:
       +            wallet.create_main_account(None)
       +        except BaseException as e:
       +            QMessageBox.information(None, _('Error'), str(e), _('OK'))
       +            return
       +        return wallet
       +
       +    @hook
       +    def sign_tx(self, window, tx):
       +        tx.error = None
       +        try:
       +            self.wallet.sign_transaction(tx, None)
       +        except Exception as e:
       +            tx.error = str(e)
       +
       +from PyQt4.Qt import QApplication, QMessageBox, QDialog, QInputDialog, QLineEdit, QVBoxLayout, QLabel, QThread, SIGNAL
       +import PyQt4.QtCore as QtCore
       +from electrum_gui.qt.password_dialog import make_password_dialog, run_password_dialog
       +
       +class QtPlugin(Plugin):
       +
       +    @hook
       +    def load_wallet(self, wallet, window):
       +        self.wallet = wallet
       +        self.wallet.plugin = self
       +        if self.handler is None:
       +            self.handler = BTChipQTHandler(window)
       +        if self.btchip_is_connected():
       +            if not self.wallet.check_proper_device():
       +                QMessageBox.information(window, _('Error'), _("This wallet does not match your Ledger device"), _('OK'))
       +                self.wallet.force_watching_only = True
       +        else:
       +            QMessageBox.information(window, _('Error'), _("Ledger device not detected.\nContinuing in watching-only mode."), _('OK'))
       +            self.wallet.force_watching_only = True
       +
       +
        class BTChipQTHandler:
        
            def __init__(self, win):
       t@@ -563,6 +562,15 @@ class BTChipQTHandler:
                    self.d.hide()
                    self.d = None
        
       +class CmdlinePlugin(Plugin):
       +    @hook
       +    def cmdline_load_wallet(self, wallet):
       +        self.wallet = wallet
       +        self.wallet.plugin = self
       +        if self.handler is None:
       +            self.handler = BTChipCmdLineHandler()
       +
       +
        class BTChipCmdLineHandler:
        
            def stop(self):
   DIR diff --git a/plugins/cosigner_pool.py b/plugins/cosigner_pool.py
       t@@ -79,7 +79,7 @@ class Listener(util.DaemonThread):
                    time.sleep(30)
        
        
       -class Plugin(BasePlugin):
       +class QtPlugin(BasePlugin):
        
            def __init__(self, parent, config, name):
                BasePlugin.__init__(self, parent, config, name)
   DIR diff --git a/plugins/email_requests.py b/plugins/email_requests.py
       t@@ -101,7 +101,7 @@ class Processor(threading.Thread):
                s.quit()
        
        
       -class Plugin(BasePlugin):
       +class QtPlugin(BasePlugin):
        
            def fullname(self):
                return 'Email'
   DIR diff --git a/plugins/exchange_rate.py b/plugins/exchange_rate.py
       t@@ -1,6 +1,3 @@
       -from PyQt4.QtGui import *
       -from PyQt4.QtCore import *
       -
        from datetime import datetime
        import inspect
        import requests
       t@@ -17,8 +14,7 @@ from electrum.plugins import BasePlugin, hook
        from electrum.i18n import _
        from electrum.util import PrintError, ThreadJob, timestamp_to_datetime
        from electrum.util import format_satoshis
       -from electrum_gui.qt.util import *
       -from electrum_gui.qt.amountedit import AmountEdit
       +
        
        # See https://en.wikipedia.org/wiki/ISO_4217
        CCY_PRECISIONS = {'BHD': 3, 'BIF': 0, 'BYR': 0, 'CLF': 4, 'CLP': 0,
       t@@ -301,25 +297,79 @@ class Plugin(BasePlugin, ThreadJob):
        
        
        
       +    def exchange_rate(self):
       +        '''Returns None, or the exchange rate as a Decimal'''
       +        rate = self.exchange.quotes.get(self.ccy)
       +        if rate:
       +            return Decimal(rate)
       +
            @hook
       -    def on_new_window(self, window):
       -        # Additional send and receive edit boxes
       -        send_e = AmountEdit(self.config_ccy)
       -        window.send_grid.addWidget(send_e, 4, 2, Qt.AlignLeft)
       -        window.amount_e.frozen.connect(
       -            lambda: send_e.setFrozen(window.amount_e.isReadOnly()))
       -        receive_e = AmountEdit(self.config_ccy)
       -        window.receive_grid.addWidget(receive_e, 2, 2, Qt.AlignLeft)
       -        window.fiat_send_e = send_e
       -        window.fiat_receive_e = receive_e
       -        self.connect_fields(window, window.amount_e, send_e, window.fee_e)
       -        self.connect_fields(window, window.receive_amount_e, receive_e, None)
       -        window.history_list.refresh_headers()
       -        window.update_status()
       -        window.connect(window.app, SIGNAL('new_fx_quotes'), lambda: self.on_fx_quotes(window))
       -        window.connect(window.app, SIGNAL('new_fx_history'), lambda: self.on_fx_history(window))
       -        window.connect(window.app, SIGNAL('close_fx_plugin'), lambda: self.restore_window(window))
       -        window.connect(window.app, SIGNAL('refresh_headers'), window.history_list.refresh_headers)
       +    def format_amount_and_units(self, btc_balance):
       +        rate = self.exchange_rate()
       +        return '' if rate is None else " (%s %s)" % (self.value_str(btc_balance, rate), self.ccy)
       +
       +    @hook
       +    def get_fiat_status_text(self, btc_balance):
       +        rate = self.exchange_rate()
       +        return _("  (No FX rate available)") if rate is None else "1 BTC~%s %s" % (self.value_str(COIN, rate), self.ccy)
       +
       +    def get_historical_rates(self):
       +        if self.show_history():
       +            self.exchange.get_historical_rates(self.ccy)
       +
       +    def requires_settings(self):
       +        return True
       +
       +    def value_str(self, satoshis, rate):
       +        if satoshis is None:  # Can happen with incomplete history
       +            return _("Unknown")
       +        if rate:
       +            value = Decimal(satoshis) / COIN * Decimal(rate)
       +            return "%s" % (self.ccy_amount_str(value, True))
       +        return _("No data")
       +
       +    @hook
       +    def historical_value_str(self, satoshis, d_t):
       +        rate = self.exchange.historical_rate(self.ccy, d_t)
       +        # Frequently there is no rate for today, until tomorrow :)
       +        # Use spot quotes in that case
       +        if rate is None and (datetime.today().date() - d_t.date()).days <= 2:
       +            rate = self.exchange.quotes.get(self.ccy)
       +            self.history_used_spot = True
       +        return self.value_str(satoshis, rate)
       +
       +    @hook
       +    def history_tab_headers(self, headers):
       +        if self.show_history():
       +            headers.extend(['%s '%self.ccy + _('Amount'), '%s '%self.ccy + _('Balance')])
       +
       +    @hook
       +    def history_tab_update_begin(self):
       +        self.history_used_spot = False
       +
       +    @hook
       +    def history_tab_update(self, tx, entry):
       +        if not self.show_history():
       +            return
       +        tx_hash, conf, value, timestamp, balance = tx
       +        if conf <= 0:
       +            date = datetime.today()
       +        else:
       +            date = timestamp_to_datetime(timestamp)
       +        for amount in [value, balance]:
       +            text = self.historical_value_str(amount, date)
       +            entry.append(text)
       +
       +
       +
       +
       +from PyQt4.QtGui import *
       +from PyQt4.QtCore import *
       +from electrum_gui.qt.util import *
       +from electrum_gui.qt.amountedit import AmountEdit
       +
       +
       +class QtPlugin(Plugin):
        
        
            def connect_fields(self, window, btc_e, fiat_e, fee_e):
       t@@ -412,68 +462,25 @@ class Plugin(BasePlugin, ThreadJob):
                    combo.blockSignals(False)
                    combo.setCurrentIndex(combo.findText(self.ccy))
        
       -    def exchange_rate(self):
       -        '''Returns None, or the exchange rate as a Decimal'''
       -        rate = self.exchange.quotes.get(self.ccy)
       -        if rate:
       -            return Decimal(rate)
       -
       -    @hook
       -    def format_amount_and_units(self, btc_balance):
       -        rate = self.exchange_rate()
       -        return '' if rate is None else " (%s %s)" % (self.value_str(btc_balance, rate), self.ccy)
       -
       -    @hook
       -    def get_fiat_status_text(self, btc_balance):
       -        rate = self.exchange_rate()
       -        return _("  (No FX rate available)") if rate is None else "1 BTC~%s %s" % (self.value_str(COIN, rate), self.ccy)
       -
       -    def get_historical_rates(self):
       -        if self.show_history():
       -            self.exchange.get_historical_rates(self.ccy)
       -
       -    def requires_settings(self):
       -        return True
       -
       -    def value_str(self, satoshis, rate):
       -        if satoshis is None:  # Can happen with incomplete history
       -            return _("Unknown")
       -        if rate:
       -            value = Decimal(satoshis) / COIN * Decimal(rate)
       -            return "%s" % (self.ccy_amount_str(value, True))
       -        return _("No data")
       -
            @hook
       -    def historical_value_str(self, satoshis, d_t):
       -        rate = self.exchange.historical_rate(self.ccy, d_t)
       -        # Frequently there is no rate for today, until tomorrow :)
       -        # Use spot quotes in that case
       -        if rate is None and (datetime.today().date() - d_t.date()).days <= 2:
       -            rate = self.exchange.quotes.get(self.ccy)
       -            self.history_used_spot = True
       -        return self.value_str(satoshis, rate)
       -
       -    @hook
       -    def history_tab_headers(self, headers):
       -        if self.show_history():
       -            headers.extend(['%s '%self.ccy + _('Amount'), '%s '%self.ccy + _('Balance')])
       -
       -    @hook
       -    def history_tab_update_begin(self):
       -        self.history_used_spot = False
       -
       -    @hook
       -    def history_tab_update(self, tx, entry):
       -        if not self.show_history():
       -            return
       -        tx_hash, conf, value, timestamp, balance = tx
       -        if conf <= 0:
       -            date = datetime.today()
       -        else:
       -            date = timestamp_to_datetime(timestamp)
       -        for amount in [value, balance]:
       -            text = self.historical_value_str(amount, date)
       -            entry.append(text)
       +    def on_new_window(self, window):
       +        # Additional send and receive edit boxes
       +        send_e = AmountEdit(self.config_ccy)
       +        window.send_grid.addWidget(send_e, 4, 2, Qt.AlignLeft)
       +        window.amount_e.frozen.connect(
       +            lambda: send_e.setFrozen(window.amount_e.isReadOnly()))
       +        receive_e = AmountEdit(self.config_ccy)
       +        window.receive_grid.addWidget(receive_e, 2, 2, Qt.AlignLeft)
       +        window.fiat_send_e = send_e
       +        window.fiat_receive_e = receive_e
       +        self.connect_fields(window, window.amount_e, send_e, window.fee_e)
       +        self.connect_fields(window, window.receive_amount_e, receive_e, None)
       +        window.history_list.refresh_headers()
       +        window.update_status()
       +        window.connect(window.app, SIGNAL('new_fx_quotes'), lambda: self.on_fx_quotes(window))
       +        window.connect(window.app, SIGNAL('new_fx_history'), lambda: self.on_fx_history(window))
       +        window.connect(window.app, SIGNAL('close_fx_plugin'), lambda: self.restore_window(window))
       +        window.connect(window.app, SIGNAL('refresh_headers'), window.history_list.refresh_headers)
        
            def settings_widget(self, window):
                return EnterButton(_('Settings'), self.settings_dialog)
   DIR diff --git a/plugins/greenaddress_instant.py b/plugins/greenaddress_instant.py
       t@@ -28,7 +28,7 @@ from electrum.i18n import _
        
        
        
       -class Plugin(BasePlugin):
       +class QtPlugin(BasePlugin):
        
            button_label = _("Verify GA instant")
        
   DIR diff --git a/plugins/keepkey.py b/plugins/keepkey.py
       t@@ -7,8 +7,6 @@ import threading
        import re
        from functools import partial
        
       -from PyQt4.Qt import QMessageBox, QDialog, QVBoxLayout, QLabel, QThread, SIGNAL, QGridLayout, QInputDialog, QPushButton
       -import PyQt4.QtCore as QtCore
        
        import electrum
        from electrum import bitcoin
       t@@ -22,14 +20,10 @@ from electrum.wallet import BIP32_HD_Wallet
        from electrum.util import print_error, print_msg
        from electrum.wallet import pw_decode, bip32_private_derivation, bip32_root
        
       -from electrum_gui.qt.util import *
       -from electrum_gui.qt.main_window import StatusBarButton, ElectrumWindow
       -from electrum_gui.qt.installwizard import InstallWizard
        
        try:
            from keepkeylib.client import types
            from keepkeylib.client import proto, BaseClient, ProtocolMixin
       -    from keepkeylib.qt.pinmatrix import PinMatrixWidget
            from keepkeylib.transport import ConnectionError
            from keepkeylib.transport_hid import HidTransport
            KEEPKEY = True
       t@@ -47,6 +41,172 @@ def give_error(message):
            raise Exception(message)
        
        
       +
       +
       +class KeepKeyWallet(BIP32_HD_Wallet):
       +    wallet_type = 'keepkey'
       +    root_derivation = "m/44'/0'"
       +
       +    def __init__(self, storage):
       +        BIP32_HD_Wallet.__init__(self, storage)
       +        self.mpk = None
       +        self.device_checked = False
       +        self.proper_device = False
       +        self.force_watching_only = False
       +
       +    def get_action(self):
       +        if not self.accounts:
       +            return 'create_accounts'
       +
       +    def can_import(self):
       +        return False
       +
       +    def can_sign_xpubkey(self, x_pubkey):
       +        xpub, sequence = BIP32_Account.parse_xpubkey(x_pubkey)
       +        return xpub in self.master_public_keys.values()
       +
       +    def can_export(self):
       +        return False
       +
       +    def can_create_accounts(self):
       +        return True
       +
       +    def can_change_password(self):
       +        return False
       +
       +    def is_watching_only(self):
       +        return self.force_watching_only
       +
       +    def get_client(self):
       +        return self.plugin.get_client()
       +
       +    def address_id(self, address):
       +        account_id, (change, address_index) = self.get_address_index(address)
       +        return "44'/0'/%s'/%d/%d" % (account_id, change, address_index)
       +
       +    def create_main_account(self, password):
       +        self.create_account('Main account', None) #name, empty password
       +
       +    def mnemonic_to_seed(self, mnemonic, passphrase):
       +        # keepkey uses bip39
       +        import pbkdf2, hashlib, hmac
       +        PBKDF2_ROUNDS = 2048
       +        mnemonic = unicodedata.normalize('NFKD', ' '.join(mnemonic.split()))
       +        passphrase = unicodedata.normalize('NFKD', passphrase)
       +        return pbkdf2.PBKDF2(mnemonic, 'mnemonic' + passphrase, iterations = PBKDF2_ROUNDS, macmodule = hmac, digestmodule = hashlib.sha512).read(64)
       +
       +    def derive_xkeys(self, root, derivation, password):
       +        x = self.master_private_keys.get(root)
       +        if x:
       +            root_xprv = pw_decode(x, password)
       +            xprv, xpub = bip32_private_derivation(root_xprv, root, derivation)
       +            return xpub, xprv
       +        else:
       +            derivation = derivation.replace(self.root_name,"44'/0'/")
       +            xpub = self.get_public_key(derivation)
       +            return xpub, None
       +
       +    def get_public_key(self, bip32_path):
       +        address_n = self.plugin.get_client().expand_path(bip32_path)
       +        node = self.plugin.get_client().get_public_node(address_n).node
       +        xpub = "0488B21E".decode('hex') + chr(node.depth) + self.i4b(node.fingerprint) + self.i4b(node.child_num) + node.chain_code + node.public_key
       +        return EncodeBase58Check(xpub)
       +
       +    def get_master_public_key(self):
       +        if not self.mpk:
       +            self.mpk = self.get_public_key("44'/0'")
       +        return self.mpk
       +
       +    def i4b(self, x):
       +        return pack('>I', x)
       +
       +    def add_keypairs(self, tx, keypairs, password):
       +        #do nothing - no priv keys available
       +        pass
       +
       +    def decrypt_message(self, pubkey, message, password):
       +        raise BaseException( _('Decrypt method is not implemented in KeepKey') )
       +        #address = public_key_to_bc_address(pubkey.decode('hex'))
       +        #address_path = self.address_id(address)
       +        #address_n = self.get_client().expand_path(address_path)
       +        #try:
       +        #    decrypted_msg = self.get_client().decrypt_message(address_n, b64decode(message))
       +        #except Exception, e:
       +        #    give_error(e)
       +        #finally:
       +        #    twd.stop()
       +        #return str(decrypted_msg)
       +
       +    def sign_message(self, address, message, password):
       +        if self.has_seed():
       +            return BIP32_HD_Wallet.sign_message(self, address, message, password)
       +        if not self.check_proper_device():
       +            give_error('Wrong device or password')
       +        try:
       +            address_path = self.address_id(address)
       +            address_n = self.plugin.get_client().expand_path(address_path)
       +        except Exception, e:
       +            give_error(e)
       +        try:
       +            msg_sig = self.plugin.get_client().sign_message('Bitcoin', address_n, message)
       +        except Exception, e:
       +            give_error(e)
       +        finally:
       +            self.plugin.handler.stop()
       +        return msg_sig.signature
       +
       +    def sign_transaction(self, tx, password):
       +        if self.has_seed():
       +            return BIP32_HD_Wallet.sign_transaction(self, tx, password)
       +        if tx.is_complete():
       +            return
       +        if not self.check_proper_device():
       +            give_error('Wrong device or password')
       +        # previous transactions used as inputs
       +        prev_tx = {}
       +        # path of the xpubs that are involved
       +        xpub_path = {}
       +        for txin in tx.inputs:
       +            tx_hash = txin['prevout_hash']
       +
       +            ptx = self.transactions.get(tx_hash)
       +            if ptx is None:
       +                ptx = self.network.synchronous_get(('blockchain.transaction.get', [tx_hash]))
       +                ptx = Transaction(ptx)
       +            prev_tx[tx_hash] = ptx
       +
       +            for x_pubkey in txin['x_pubkeys']:
       +                account_derivation = None
       +                if not is_extended_pubkey(x_pubkey):
       +                    continue
       +                xpub = x_to_xpub(x_pubkey)
       +                for k, v in self.master_public_keys.items():
       +                    if v == xpub:
       +                        account_id = re.match("x/(\d+)'", k).group(1)
       +                        account_derivation = "44'/0'/%s'"%account_id
       +                if account_derivation is not None:
       +                    xpub_path[xpub] = account_derivation
       +
       +        self.plugin.sign_transaction(tx, prev_tx, xpub_path)
       +
       +    def check_proper_device(self):
       +        self.get_client().ping('t')
       +        if not self.device_checked:
       +            address = self.addresses(False)[0]
       +            address_id = self.address_id(address)
       +            n = self.get_client().expand_path(address_id)
       +            device_address = self.get_client().get_address('Bitcoin', n)
       +            self.device_checked = True
       +
       +            if device_address != address:
       +                self.proper_device = False
       +            else:
       +                self.proper_device = True
       +
       +        return self.proper_device
       +
       +
       +
        class Plugin(BasePlugin):
        
            def __init__(self, parent, config, name):
       t@@ -118,64 +278,6 @@ class Plugin(BasePlugin):
                    self.client = None
                self.wallet = None
        
       -    @hook
       -    def cmdline_load_wallet(self, wallet):
       -        self.wallet = wallet
       -        self.wallet.plugin = self
       -        if self.handler is None:
       -            self.handler = KeepKeyCmdLineHandler()
       -
       -    @hook
       -    def load_wallet(self, wallet, window):
       -        self.print_error("load_wallet")
       -        self.wallet = wallet
       -        self.wallet.plugin = self
       -        self.keepkey_button = StatusBarButton(QIcon(":icons/keepkey.png"), _("KeepKey"), partial(self.settings_dialog, window))
       -        if type(window) is ElectrumWindow:
       -            window.statusBar().addPermanentWidget(self.keepkey_button)
       -        if self.handler is None:
       -            self.handler = KeepKeyQtHandler(window)
       -        try:
       -            self.get_client().ping('t')
       -        except BaseException as e:
       -            QMessageBox.information(window, _('Error'), _("KeepKey device not detected.\nContinuing in watching-only mode." + '\n\nReason:\n' + str(e)), _('OK'))
       -            self.wallet.force_watching_only = True
       -            return
       -        if self.wallet.addresses() and not self.wallet.check_proper_device():
       -            QMessageBox.information(window, _('Error'), _("This wallet does not match your KeepKey device"), _('OK'))
       -            self.wallet.force_watching_only = True
       -
       -    @hook
       -    def installwizard_load_wallet(self, wallet, window):
       -        if type(wallet) != KeepKeyWallet:
       -            return
       -        self.load_wallet(wallet, window)
       -
       -    @hook
       -    def installwizard_restore(self, wizard, storage):
       -        if storage.get('wallet_type') != 'keepkey':
       -            return
       -        seed = wizard.enter_seed_dialog("Enter your KeepKey seed", None, func=lambda x:True)
       -        if not seed:
       -            return
       -        wallet = KeepKeyWallet(storage)
       -        self.wallet = wallet
       -        handler = KeepKeyQtHandler(wizard)
       -        passphrase = handler.get_passphrase(_("Please enter your KeepKey passphrase.") + '\n' + _("Press OK if you do not use one."))
       -        if passphrase is None:
       -            return
       -        password = wizard.password_dialog()
       -        wallet.add_seed(seed, password)
       -        wallet.add_cosigner_seed(seed, 'x/', password, passphrase)
       -        wallet.create_main_account(password)
       -        # disable keepkey plugin
       -        self.set_enabled(False)
       -        return wallet
       -
       -    @hook
       -    def receive_menu(self, menu, addrs):
       -        if not self.wallet.is_watching_only() and self.atleast_version(1, 3) and len(addrs) == 1:
       -            menu.addAction(_("Show on TREZOR"), lambda: self.show_address(addrs[0]))
        
            def show_address(self, address):
                if not self.wallet.check_proper_device():
       t@@ -193,39 +295,6 @@ class Plugin(BasePlugin):
                    self.handler.stop()
        
        
       -    def settings_dialog(self, window):
       -        try:
       -            device_id = self.get_client().get_device_id()
       -        except BaseException as e:
       -            window.show_message(str(e))
       -            return
       -        get_label = lambda: self.get_client().features.label
       -        update_label = lambda: current_label_label.setText("Label: %s" % get_label())
       -        d = QDialog()
       -        layout = QGridLayout(d)
       -        layout.addWidget(QLabel("KeepKey Options"),0,0)
       -        layout.addWidget(QLabel("ID:"),1,0)
       -        layout.addWidget(QLabel(" %s" % device_id),1,1)
       -
       -        def modify_label():
       -            response = QInputDialog().getText(None, "Set New KeepKey Label", "New KeepKey Label:  (upon submission confirm on KeepKey)")
       -            if not response[1]:
       -                return
       -            new_label = str(response[0])
       -            self.handler.show_message("Please confirm label change on KeepKey")
       -            status = self.get_client().apply_settings(label=new_label)
       -            self.handler.stop()
       -            update_label()
       -
       -        current_label_label = QLabel()
       -        update_label()
       -        change_label_button = QPushButton("Modify")
       -        change_label_button.clicked.connect(modify_label)
       -        layout.addWidget(current_label_label,3,0)
       -        layout.addWidget(change_label_button,3,1)
       -        d.exec_()
       -
       -
            def sign_transaction(self, tx, prev_tx, xpub_path):
                self.prev_tx = prev_tx
                self.xpub_path = xpub_path
       t@@ -350,167 +419,110 @@ class Plugin(BasePlugin):
        
        
        
       -class KeepKeyWallet(BIP32_HD_Wallet):
       -    wallet_type = 'keepkey'
       -    root_derivation = "m/44'/0'"
       -
       -    def __init__(self, storage):
       -        BIP32_HD_Wallet.__init__(self, storage)
       -        self.mpk = None
       -        self.device_checked = False
       -        self.proper_device = False
       -        self.force_watching_only = False
       -
       -    def get_action(self):
       -        if not self.accounts:
       -            return 'create_accounts'
       -
       -    def can_import(self):
       -        return False
       -
       -    def can_sign_xpubkey(self, x_pubkey):
       -        xpub, sequence = BIP32_Account.parse_xpubkey(x_pubkey)
       -        return xpub in self.master_public_keys.values()
       -
       -    def can_export(self):
       -        return False
       -
       -    def can_create_accounts(self):
       -        return True
       -
       -    def can_change_password(self):
       -        return False
       -
       -    def is_watching_only(self):
       -        return self.force_watching_only
       -
       -    def get_client(self):
       -        return self.plugin.get_client()
       -
       -    def address_id(self, address):
       -        account_id, (change, address_index) = self.get_address_index(address)
       -        return "44'/0'/%s'/%d/%d" % (account_id, change, address_index)
       -
       -    def create_main_account(self, password):
       -        self.create_account('Main account', None) #name, empty password
       -
       -    def mnemonic_to_seed(self, mnemonic, passphrase):
       -        # keepkey uses bip39
       -        import pbkdf2, hashlib, hmac
       -        PBKDF2_ROUNDS = 2048
       -        mnemonic = unicodedata.normalize('NFKD', ' '.join(mnemonic.split()))
       -        passphrase = unicodedata.normalize('NFKD', passphrase)
       -        return pbkdf2.PBKDF2(mnemonic, 'mnemonic' + passphrase, iterations = PBKDF2_ROUNDS, macmodule = hmac, digestmodule = hashlib.sha512).read(64)
       +from PyQt4.Qt import QMessageBox, QDialog, QVBoxLayout, QLabel, QThread, SIGNAL, QGridLayout, QInputDialog, QPushButton
       +import PyQt4.QtCore as QtCore
       +from electrum_gui.qt.util import *
       +from electrum_gui.qt.main_window import StatusBarButton, ElectrumWindow
       +from electrum_gui.qt.installwizard import InstallWizard
       +from keepkeylib.qt.pinmatrix import PinMatrixWidget
        
       -    def derive_xkeys(self, root, derivation, password):
       -        x = self.master_private_keys.get(root)
       -        if x:
       -            root_xprv = pw_decode(x, password)
       -            xprv, xpub = bip32_private_derivation(root_xprv, root, derivation)
       -            return xpub, xprv
       -        else:
       -            derivation = derivation.replace(self.root_name,"44'/0'/")
       -            xpub = self.get_public_key(derivation)
       -            return xpub, None
        
       -    def get_public_key(self, bip32_path):
       -        address_n = self.plugin.get_client().expand_path(bip32_path)
       -        node = self.plugin.get_client().get_public_node(address_n).node
       -        xpub = "0488B21E".decode('hex') + chr(node.depth) + self.i4b(node.fingerprint) + self.i4b(node.child_num) + node.chain_code + node.public_key
       -        return EncodeBase58Check(xpub)
       +class QtPlugin(Plugin):
        
       -    def get_master_public_key(self):
       -        if not self.mpk:
       -            self.mpk = self.get_public_key("44'/0'")
       -        return self.mpk
       +    @hook
       +    def load_wallet(self, wallet, window):
       +        self.print_error("load_wallet")
       +        self.wallet = wallet
       +        self.wallet.plugin = self
       +        self.keepkey_button = StatusBarButton(QIcon(":icons/keepkey.png"), _("KeepKey"), partial(self.settings_dialog, window))
       +        if type(window) is ElectrumWindow:
       +            window.statusBar().addPermanentWidget(self.keepkey_button)
       +        if self.handler is None:
       +            self.handler = KeepKeyQtHandler(window)
       +        try:
       +            self.get_client().ping('t')
       +        except BaseException as e:
       +            QMessageBox.information(window, _('Error'), _("KeepKey device not detected.\nContinuing in watching-only mode." + '\n\nReason:\n' + str(e)), _('OK'))
       +            self.wallet.force_watching_only = True
       +            return
       +        if self.wallet.addresses() and not self.wallet.check_proper_device():
       +            QMessageBox.information(window, _('Error'), _("This wallet does not match your KeepKey device"), _('OK'))
       +            self.wallet.force_watching_only = True
        
       -    def i4b(self, x):
       -        return pack('>I', x)
       +    @hook
       +    def installwizard_load_wallet(self, wallet, window):
       +        if type(wallet) != KeepKeyWallet:
       +            return
       +        self.load_wallet(wallet, window)
        
       -    def add_keypairs(self, tx, keypairs, password):
       -        #do nothing - no priv keys available
       -        pass
       +    @hook
       +    def installwizard_restore(self, wizard, storage):
       +        if storage.get('wallet_type') != 'keepkey':
       +            return
       +        seed = wizard.enter_seed_dialog("Enter your KeepKey seed", None, func=lambda x:True)
       +        if not seed:
       +            return
       +        wallet = KeepKeyWallet(storage)
       +        self.wallet = wallet
       +        handler = KeepKeyQtHandler(wizard)
       +        passphrase = handler.get_passphrase(_("Please enter your KeepKey passphrase.") + '\n' + _("Press OK if you do not use one."))
       +        if passphrase is None:
       +            return
       +        password = wizard.password_dialog()
       +        wallet.add_seed(seed, password)
       +        wallet.add_cosigner_seed(seed, 'x/', password, passphrase)
       +        wallet.create_main_account(password)
       +        # disable keepkey plugin
       +        self.set_enabled(False)
       +        return wallet
        
       -    def decrypt_message(self, pubkey, message, password):
       -        raise BaseException( _('Decrypt method is not implemented in KeepKey') )
       -        #address = public_key_to_bc_address(pubkey.decode('hex'))
       -        #address_path = self.address_id(address)
       -        #address_n = self.get_client().expand_path(address_path)
       -        #try:
       -        #    decrypted_msg = self.get_client().decrypt_message(address_n, b64decode(message))
       -        #except Exception, e:
       -        #    give_error(e)
       -        #finally:
       -        #    twd.stop()
       -        #return str(decrypted_msg)
       +    @hook
       +    def receive_menu(self, menu, addrs):
       +        if not self.wallet.is_watching_only() and self.atleast_version(1, 3) and len(addrs) == 1:
       +            menu.addAction(_("Show on TREZOR"), lambda: self.show_address(addrs[0]))
        
       -    def sign_message(self, address, message, password):
       -        if self.has_seed():
       -            return BIP32_HD_Wallet.sign_message(self, address, message, password)
       -        if not self.check_proper_device():
       -            give_error('Wrong device or password')
       -        try:
       -            address_path = self.address_id(address)
       -            address_n = self.plugin.get_client().expand_path(address_path)
       -        except Exception, e:
       -            give_error(e)
       +    def settings_dialog(self, window):
                try:
       -            msg_sig = self.plugin.get_client().sign_message('Bitcoin', address_n, message)
       -        except Exception, e:
       -            give_error(e)
       -        finally:
       -            self.plugin.handler.stop()
       -        return msg_sig.signature
       -
       -    def sign_transaction(self, tx, password):
       -        if self.has_seed():
       -            return BIP32_HD_Wallet.sign_transaction(self, tx, password)
       -        if tx.is_complete():
       +            device_id = self.get_client().get_device_id()
       +        except BaseException as e:
       +            window.show_message(str(e))
                    return
       -        if not self.check_proper_device():
       -            give_error('Wrong device or password')
       -        # previous transactions used as inputs
       -        prev_tx = {}
       -        # path of the xpubs that are involved
       -        xpub_path = {}
       -        for txin in tx.inputs:
       -            tx_hash = txin['prevout_hash']
       +        get_label = lambda: self.get_client().features.label
       +        update_label = lambda: current_label_label.setText("Label: %s" % get_label())
       +        d = QDialog()
       +        layout = QGridLayout(d)
       +        layout.addWidget(QLabel("KeepKey Options"),0,0)
       +        layout.addWidget(QLabel("ID:"),1,0)
       +        layout.addWidget(QLabel(" %s" % device_id),1,1)
        
       -            ptx = self.transactions.get(tx_hash)
       -            if ptx is None:
       -                ptx = self.network.synchronous_get(('blockchain.transaction.get', [tx_hash]))
       -                ptx = Transaction(ptx)
       -            prev_tx[tx_hash] = ptx
       +        def modify_label():
       +            response = QInputDialog().getText(None, "Set New KeepKey Label", "New KeepKey Label:  (upon submission confirm on KeepKey)")
       +            if not response[1]:
       +                return
       +            new_label = str(response[0])
       +            self.handler.show_message("Please confirm label change on KeepKey")
       +            status = self.get_client().apply_settings(label=new_label)
       +            self.handler.stop()
       +            update_label()
        
       -            for x_pubkey in txin['x_pubkeys']:
       -                account_derivation = None
       -                if not is_extended_pubkey(x_pubkey):
       -                    continue
       -                xpub = x_to_xpub(x_pubkey)
       -                for k, v in self.master_public_keys.items():
       -                    if v == xpub:
       -                        account_id = re.match("x/(\d+)'", k).group(1)
       -                        account_derivation = "44'/0'/%s'"%account_id
       -                if account_derivation is not None:
       -                    xpub_path[xpub] = account_derivation
       +        current_label_label = QLabel()
       +        update_label()
       +        change_label_button = QPushButton("Modify")
       +        change_label_button.clicked.connect(modify_label)
       +        layout.addWidget(current_label_label,3,0)
       +        layout.addWidget(change_label_button,3,1)
       +        d.exec_()
        
       -        self.plugin.sign_transaction(tx, prev_tx, xpub_path)
        
       -    def check_proper_device(self):
       -        self.get_client().ping('t')
       -        if not self.device_checked:
       -            address = self.addresses(False)[0]
       -            address_id = self.address_id(address)
       -            n = self.get_client().expand_path(address_id)
       -            device_address = self.get_client().get_address('Bitcoin', n)
       -            self.device_checked = True
       +class CmdlinePlugin(Plugin):
        
       -            if device_address != address:
       -                self.proper_device = False
       -            else:
       -                self.proper_device = True
       +    @hook
       +    def cmdline_load_wallet(self, wallet):
       +        self.wallet = wallet
       +        self.wallet.plugin = self
       +        if self.handler is None:
       +            self.handler = KeepKeyCmdLineHandler()
        
       -        return self.proper_device
        
        
        class KeepKeyGuiMixin(object):
   DIR diff --git a/plugins/labels.py b/plugins/labels.py
       t@@ -7,15 +7,6 @@ import sys
        import traceback
        from functools import partial
        
       -try:
       -    import PyQt4
       -except Exception:
       -    sys.exit("Error: Could not import PyQt4 on Linux systems, you may try 'sudo apt-get install python-qt4'")
       -
       -from PyQt4.QtGui import *
       -from PyQt4.QtCore import *
       -import PyQt4.QtCore as QtCore
       -import PyQt4.QtGui as QtGui
        import aes
        import base64
        
       t@@ -23,8 +14,6 @@ import electrum
        from electrum.plugins import BasePlugin, hook
        from electrum.i18n import _
        
       -from electrum_gui.qt import HelpButton, EnterButton
       -from electrum_gui.qt.util import ThreadedButton, Buttons, CancelButton, OkButton
        
        class Plugin(BasePlugin):
        
       t@@ -32,34 +21,6 @@ class Plugin(BasePlugin):
                BasePlugin.__init__(self, parent, config, name)
                self.target_host = 'sync.bytesized-hosting.com:9090'
                self.wallets = {}
       -        self.obj = QObject()
       -        self.obj.connect(self.obj, SIGNAL('labels:pulled'), self.on_pulled)
       -
       -    @hook
       -    def on_new_window(self, window):
       -        wallet = window.wallet
       -        nonce = self.get_nonce(wallet)
       -        self.print_error("wallet", wallet.basename(), "nonce is", nonce)
       -        mpk = ''.join(sorted(wallet.get_master_public_keys().values()))
       -        if not mpk:
       -            return
       -
       -        password = hashlib.sha1(mpk).digest().encode('hex')[:32]
       -        iv = hashlib.sha256(password).digest()[:16]
       -        wallet_id = hashlib.sha256(mpk).digest().encode('hex')
       -        self.wallets[wallet] = (password, iv, wallet_id)
       -
       -        # If there is an auth token we can try to actually start syncing
       -        t = threading.Thread(target=self.pull_thread, args=(window, False))
       -        t.setDaemon(True)
       -        t.start()
       -
       -    @hook
       -    def on_close_window(self, window):
       -        self.wallets.pop(window.wallet)
       -
       -    def version(self):
       -        return "0.0.1"
        
            def encode(self, wallet, msg):
                password, iv, wallet_id = self.wallets[wallet]
       t@@ -85,9 +46,6 @@ class Plugin(BasePlugin):
                self.print_error("set", wallet.basename(), "nonce to", nonce)
                wallet.storage.put("wallet_nonce", nonce, force_write)
        
       -    def requires_settings(self):
       -        return True
       -
            @hook
            def set_label(self, wallet, item, label):
                if not wallet in self.wallets:
       t@@ -105,47 +63,6 @@ class Plugin(BasePlugin):
                # Caller will write the wallet
                self.set_nonce(wallet, nonce + 1, force_write=False)
        
       -    def settings_widget(self, window):
       -        return EnterButton(_('Settings'),
       -                           partial(self.settings_dialog, window))
       -
       -    def settings_dialog(self, window):
       -        print "window:", window
       -        d = QDialog(window)
       -        vbox = QVBoxLayout(d)
       -        layout = QGridLayout()
       -        vbox.addLayout(layout)
       -
       -        layout.addWidget(QLabel("Label sync options: "),2,0)
       -
       -        self.upload = ThreadedButton("Force upload",
       -                                     partial(self.push_thread, window),
       -                                     self.done_processing)
       -        layout.addWidget(self.upload, 2, 1)
       -
       -        self.download = ThreadedButton("Force download",
       -                                       partial(self.pull_thread, window, True),
       -                                       self.done_processing)
       -        layout.addWidget(self.download, 2, 2)
       -
       -        self.accept = OkButton(d, _("Done"))
       -        vbox.addLayout(Buttons(CancelButton(d), self.accept))
       -
       -        if d.exec_():
       -            return True
       -        else:
       -            return False
       -
       -    def on_pulled(self, window, nonce):
       -        wallet = window.wallet
       -        wallet.storage.put('labels', wallet.labels, False)
       -        self.set_nonce(wallet, nonce)
       -        window.labelsChanged.emit()
       -
       -    def done_processing(self):
       -        QMessageBox.information(None, _("Labels synchronised"),
       -                                _("Your labels have been synchronised."))
       -
            def do_request(self, method, url = "/labels", is_batch=False, data=None):
                url = 'https://' + self.target_host + url
                kwargs = {'headers': {}}
       t@@ -162,8 +79,7 @@ class Plugin(BasePlugin):
                    raise BaseException(response["error"])
                return response
        
       -    def push_thread(self, window):
       -        wallet = window.wallet
       +    def push_thread(self, wallet):
                wallet_id = self.wallets[wallet][2]
                bundle = {"labels": [],
                          "walletId": wallet_id,
       t@@ -179,14 +95,14 @@ class Plugin(BasePlugin):
                                             'externalId': encoded_key})
                self.do_request("POST", "/labels", True, bundle)
        
       -    def pull_thread(self, window, force):
       -        wallet = window.wallet
       +    def pull_thread(self, wallet, force):
                wallet_id = self.wallets[wallet][2]
                nonce = 1 if force else self.get_nonce(wallet) - 1
                self.print_error("asking for labels since nonce", nonce)
                try:
                    response = self.do_request("GET", ("/labels/since/%d/for/%s" % (nonce, wallet_id) ))
                    if response["labels"] is None:
       +                self.print_error('no new labels')
                        return
                    result = {}
                    for label in response["labels"]:
       t@@ -208,9 +124,86 @@ class Plugin(BasePlugin):
                            wallet.labels[key] = value
        
                    self.print_error("received %d labels" % len(response))
       -            self.obj.emit(SIGNAL('labels:pulled'), window,
       -                          response["nonce"] + 1)
       +            # do not write to disk because we're in a daemon thread
       +            wallet.storage.put('labels', wallet.labels, False)
       +            self.set_nonce(wallet, response["nonce"] + 1, False)
       +            self.on_pulled(wallet)
        
                except Exception as e:
                    traceback.print_exc(file=sys.stderr)
                    self.print_error("could not retrieve labels")
       +
       +
       +
       +
       +
       +from PyQt4.QtGui import *
       +from PyQt4.QtCore import *
       +import PyQt4.QtCore as QtCore
       +import PyQt4.QtGui as QtGui
       +from electrum_gui.qt import HelpButton, EnterButton
       +from electrum_gui.qt.util import ThreadedButton, Buttons, CancelButton, OkButton
       +
       +class QtPlugin(Plugin):
       +
       +    def __init__(self, *args):
       +        Plugin.__init__(self, *args)
       +        self.obj = QObject()
       +
       +    def requires_settings(self):
       +        return True
       +
       +    def settings_widget(self, window):
       +        return EnterButton(_('Settings'),
       +                           partial(self.settings_dialog, window))
       +
       +    def settings_dialog(self, window):
       +        d = QDialog(window)
       +        vbox = QVBoxLayout(d)
       +        layout = QGridLayout()
       +        vbox.addLayout(layout)
       +        layout.addWidget(QLabel("Label sync options: "), 2, 0)
       +        self.upload = ThreadedButton("Force upload",
       +                                     partial(self.push_thread, window.wallet),
       +                                     self.done_processing)
       +        layout.addWidget(self.upload, 2, 1)
       +        self.download = ThreadedButton("Force download",
       +                                       partial(self.pull_thread, window.wallet, True),
       +                                       self.done_processing)
       +        layout.addWidget(self.download, 2, 2)
       +        self.accept = OkButton(d, _("Done"))
       +        vbox.addLayout(Buttons(CancelButton(d), self.accept))
       +        if d.exec_():
       +            return True
       +        else:
       +            return False
       +
       +    def on_pulled(self, wallet):
       +        self.obj.emit(SIGNAL('labels_changed'), wallet)
       +
       +    def done_processing(self):
       +        QMessageBox.information(None, _("Labels synchronised"),
       +                                _("Your labels have been synchronised."))
       +
       +    @hook
       +    def on_new_window(self, window):
       +        window.connect(window.app, SIGNAL('labels_changed'), window.update_tabs)
       +        wallet = window.wallet
       +        nonce = self.get_nonce(wallet)
       +        self.print_error("wallet", wallet.basename(), "nonce is", nonce)
       +        mpk = ''.join(sorted(wallet.get_master_public_keys().values()))
       +        if not mpk:
       +            return
       +        password = hashlib.sha1(mpk).digest().encode('hex')[:32]
       +        iv = hashlib.sha256(password).digest()[:16]
       +        wallet_id = hashlib.sha256(mpk).digest().encode('hex')
       +        self.wallets[wallet] = (password, iv, wallet_id)
       +        # If there is an auth token we can try to actually start syncing
       +        t = threading.Thread(target=self.pull_thread, args=(wallet, False))
       +        t.setDaemon(True)
       +        t.start()
       +
       +    @hook
       +    def on_close_window(self, window):
       +        self.wallets.pop(window.wallet)
       +
   DIR diff --git a/plugins/plot.py b/plugins/plot.py
       t@@ -17,7 +17,7 @@ except:
            flag_matlib=False
        
        
       -class Plugin(BasePlugin):
       +class QtPlugin(BasePlugin):
        
            def is_available(self):
                if flag_matlib:
   DIR diff --git a/plugins/trezor.py b/plugins/trezor.py
       t@@ -7,8 +7,6 @@ import threading
        import re
        from functools import partial
        
       -from PyQt4.Qt import QMessageBox, QDialog, QVBoxLayout, QLabel, QThread, SIGNAL, QGridLayout, QInputDialog, QPushButton
       -import PyQt4.QtCore as QtCore
        
        import electrum
        from electrum import bitcoin
       t@@ -22,14 +20,9 @@ from electrum.wallet import BIP32_HD_Wallet
        from electrum.util import print_error, print_msg
        from electrum.wallet import pw_decode, bip32_private_derivation, bip32_root
        
       -from electrum_gui.qt.util import *
       -from electrum_gui.qt.main_window import StatusBarButton, ElectrumWindow
       -from electrum_gui.qt.installwizard import InstallWizard
       -
        try:
            from trezorlib.client import types
            from trezorlib.client import proto, BaseClient, ProtocolMixin
       -    from trezorlib.qt.pinmatrix import PinMatrixWidget
            from trezorlib.transport import ConnectionError
            from trezorlib.transport_hid import HidTransport
            TREZOR = True
       t@@ -47,6 +40,166 @@ def give_error(message):
            raise Exception(message)
        
        
       +class TrezorWallet(BIP32_HD_Wallet):
       +    wallet_type = 'trezor'
       +    root_derivation = "m/44'/0'"
       +
       +    def __init__(self, storage):
       +        BIP32_HD_Wallet.__init__(self, storage)
       +        self.mpk = None
       +        self.device_checked = False
       +        self.proper_device = False
       +        self.force_watching_only = False
       +
       +    def get_action(self):
       +        if not self.accounts:
       +            return 'create_accounts'
       +
       +    def can_import(self):
       +        return False
       +
       +    def can_sign_xpubkey(self, x_pubkey):
       +        xpub, sequence = BIP32_Account.parse_xpubkey(x_pubkey)
       +        return xpub in self.master_public_keys.values()
       +
       +    def can_export(self):
       +        return False
       +
       +    def can_create_accounts(self):
       +        return True
       +
       +    def can_change_password(self):
       +        return False
       +
       +    def is_watching_only(self):
       +        return self.force_watching_only
       +
       +    def get_client(self):
       +        return self.plugin.get_client()
       +
       +    def address_id(self, address):
       +        account_id, (change, address_index) = self.get_address_index(address)
       +        return "44'/0'/%s'/%d/%d" % (account_id, change, address_index)
       +
       +    def create_main_account(self, password):
       +        self.create_account('Main account', None) #name, empty password
       +
       +    def mnemonic_to_seed(self, mnemonic, passphrase):
       +        # trezor uses bip39
       +        import pbkdf2, hashlib, hmac
       +        PBKDF2_ROUNDS = 2048
       +        mnemonic = unicodedata.normalize('NFKD', ' '.join(mnemonic.split()))
       +        passphrase = unicodedata.normalize('NFKD', passphrase)
       +        return pbkdf2.PBKDF2(mnemonic, 'mnemonic' + passphrase, iterations = PBKDF2_ROUNDS, macmodule = hmac, digestmodule = hashlib.sha512).read(64)
       +
       +    def derive_xkeys(self, root, derivation, password):
       +        x = self.master_private_keys.get(root)
       +        if x:
       +            root_xprv = pw_decode(x, password)
       +            xprv, xpub = bip32_private_derivation(root_xprv, root, derivation)
       +            return xpub, xprv
       +        else:
       +            derivation = derivation.replace(self.root_name,"44'/0'/")
       +            xpub = self.get_public_key(derivation)
       +            return xpub, None
       +
       +    def get_public_key(self, bip32_path):
       +        address_n = self.plugin.get_client().expand_path(bip32_path)
       +        node = self.plugin.get_client().get_public_node(address_n).node
       +        xpub = "0488B21E".decode('hex') + chr(node.depth) + self.i4b(node.fingerprint) + self.i4b(node.child_num) + node.chain_code + node.public_key
       +        return EncodeBase58Check(xpub)
       +
       +    def get_master_public_key(self):
       +        if not self.mpk:
       +            self.mpk = self.get_public_key("44'/0'")
       +        return self.mpk
       +
       +    def i4b(self, x):
       +        return pack('>I', x)
       +
       +    def add_keypairs(self, tx, keypairs, password):
       +        #do nothing - no priv keys available
       +        pass
       +
       +    def decrypt_message(self, pubkey, message, password):
       +        raise BaseException( _('Decrypt method is not implemented in Trezor') )
       +        #address = public_key_to_bc_address(pubkey.decode('hex'))
       +        #address_path = self.address_id(address)
       +        #address_n = self.get_client().expand_path(address_path)
       +        #try:
       +        #    decrypted_msg = self.get_client().decrypt_message(address_n, b64decode(message))
       +        #except Exception, e:
       +        #    give_error(e)
       +        #finally:
       +        #    twd.stop()
       +        #return str(decrypted_msg)
       +
       +    def sign_message(self, address, message, password):
       +        if self.has_seed():
       +            return BIP32_HD_Wallet.sign_message(self, address, message, password)
       +        if not self.check_proper_device():
       +            give_error('Wrong device or password')
       +        try:
       +            address_path = self.address_id(address)
       +            address_n = self.plugin.get_client().expand_path(address_path)
       +        except Exception, e:
       +            give_error(e)
       +        try:
       +            msg_sig = self.plugin.get_client().sign_message('Bitcoin', address_n, message)
       +        except Exception, e:
       +            give_error(e)
       +        finally:
       +            self.plugin.handler.stop()
       +        return msg_sig.signature
       +
       +    def sign_transaction(self, tx, password):
       +        if self.has_seed():
       +            return BIP32_HD_Wallet.sign_transaction(self, tx, password)
       +        if tx.is_complete():
       +            return
       +        if not self.check_proper_device():
       +            give_error('Wrong device or password')
       +        # previous transactions used as inputs
       +        prev_tx = {}
       +        # path of the xpubs that are involved
       +        xpub_path = {}
       +        for txin in tx.inputs:
       +            tx_hash = txin['prevout_hash']
       +
       +            ptx = self.transactions.get(tx_hash)
       +            if ptx is None:
       +                ptx = self.network.synchronous_get(('blockchain.transaction.get', [tx_hash]))
       +                ptx = Transaction(ptx)
       +            prev_tx[tx_hash] = ptx
       +
       +            for x_pubkey in txin['x_pubkeys']:
       +                account_derivation = None
       +                if not is_extended_pubkey(x_pubkey):
       +                    continue
       +                xpub = x_to_xpub(x_pubkey)
       +                for k, v in self.master_public_keys.items():
       +                    if v == xpub:
       +                        account_id = re.match("x/(\d+)'", k).group(1)
       +                        account_derivation = "44'/0'/%s'"%account_id
       +                if account_derivation is not None:
       +                    xpub_path[xpub] = account_derivation
       +
       +        self.plugin.sign_transaction(tx, prev_tx, xpub_path)
       +
       +    def check_proper_device(self):
       +        self.get_client().ping('t')
       +        if not self.device_checked:
       +            address = self.addresses(False)[0]
       +            address_id = self.address_id(address)
       +            n = self.get_client().expand_path(address_id)
       +            device_address = self.get_client().get_address('Bitcoin', n)
       +            self.device_checked = True
       +            self.proper_device = (device_address == address)
       +
       +        return self.proper_device
       +
       +
       +
        class Plugin(BasePlugin):
        
            def __init__(self, parent, config, name):
       t@@ -125,107 +278,6 @@ class Plugin(BasePlugin):
                if self.handler is None:
                    self.handler = TrezorCmdLineHandler()
        
       -    @hook
       -    def load_wallet(self, wallet, window):
       -        self.print_error("load_wallet")
       -        self.wallet = wallet
       -        self.wallet.plugin = self
       -        self.trezor_button = StatusBarButton(QIcon(":icons/trezor.png"), _("Trezor"), partial(self.settings_dialog, window))
       -        if type(window) is ElectrumWindow:
       -            window.statusBar().addPermanentWidget(self.trezor_button)
       -        if self.handler is None:
       -            self.handler = TrezorQtHandler(window)
       -        try:
       -            self.get_client().ping('t')
       -        except BaseException as e:
       -            QMessageBox.information(window, _('Error'), _("Trezor device not detected.\nContinuing in watching-only mode." + '\n\nReason:\n' + str(e)), _('OK'))
       -            self.wallet.force_watching_only = True
       -            return
       -        if self.wallet.addresses() and not self.wallet.check_proper_device():
       -            QMessageBox.information(window, _('Error'), _("This wallet does not match your Trezor device"), _('OK'))
       -            self.wallet.force_watching_only = True
       -
       -    @hook
       -    def installwizard_load_wallet(self, wallet, window):
       -        if type(wallet) != TrezorWallet:
       -            return
       -        self.load_wallet(wallet, window)
       -
       -    @hook
       -    def installwizard_restore(self, wizard, storage):
       -        if storage.get('wallet_type') != 'trezor':
       -            return
       -        seed = wizard.enter_seed_dialog("Enter your Trezor seed", None, func=lambda x:True)
       -        if not seed:
       -            return
       -        wallet = TrezorWallet(storage)
       -        self.wallet = wallet
       -        handler = TrezorQtHandler(wizard)
       -        passphrase = handler.get_passphrase(_("Please enter your Trezor passphrase.") + '\n' + _("Press OK if you do not use one."))
       -        if passphrase is None:
       -            return
       -        password = wizard.password_dialog()
       -        wallet.add_seed(seed, password)
       -        wallet.add_cosigner_seed(seed, 'x/', password, passphrase)
       -        wallet.create_main_account(password)
       -        # disable trezor plugin
       -        self.set_enabled(False)
       -        return wallet
       -
       -    @hook
       -    def receive_menu(self, menu, addrs):
       -        if not self.wallet.is_watching_only() and self.atleast_version(1, 3) and len(addrs) == 1:
       -            menu.addAction(_("Show on TREZOR"), lambda: self.show_address(addrs[0]))
       -
       -    def show_address(self, address):
       -        if not self.wallet.check_proper_device():
       -            give_error('Wrong device or password')
       -        try:
       -            address_path = self.wallet.address_id(address)
       -            address_n = self.get_client().expand_path(address_path)
       -        except Exception, e:
       -            give_error(e)
       -        try:
       -            self.get_client().get_address('Bitcoin', address_n, True)
       -        except Exception, e:
       -            give_error(e)
       -        finally:
       -            self.handler.stop()
       -
       -
       -    def settings_dialog(self, window):
       -        try:
       -            device_id = self.get_client().get_device_id()
       -        except BaseException as e:
       -            window.show_message(str(e))
       -            return
       -        get_label = lambda: self.get_client().features.label
       -        update_label = lambda: current_label_label.setText("Label: %s" % get_label())
       -        d = QDialog()
       -        layout = QGridLayout(d)
       -        layout.addWidget(QLabel("Trezor Options"),0,0)
       -        layout.addWidget(QLabel("ID:"),1,0)
       -        layout.addWidget(QLabel(" %s" % device_id),1,1)
       -
       -        def modify_label():
       -            response = QInputDialog().getText(None, "Set New Trezor Label", "New Trezor Label:  (upon submission confirm on Trezor)")
       -            if not response[1]:
       -                return
       -            new_label = str(response[0])
       -            self.handler.show_message("Please confirm label change on Trezor")
       -            status = self.get_client().apply_settings(label=new_label)
       -            self.handler.stop()
       -            update_label()
       -
       -        current_label_label = QLabel()
       -        update_label()
       -        change_label_button = QPushButton("Modify")
       -        change_label_button.clicked.connect(modify_label)
       -        layout.addWidget(current_label_label,3,0)
       -        layout.addWidget(change_label_button,3,1)
       -        d.exec_()
       -
       -
            def sign_transaction(self, tx, prev_tx, xpub_path):
                self.prev_tx = prev_tx
                self.xpub_path = xpub_path
       t@@ -347,169 +399,119 @@ class Plugin(BasePlugin):
                return self.electrum_tx_to_txtype(tx)
        
        
       +from PyQt4.Qt import QMessageBox, QDialog, QVBoxLayout, QLabel, QThread, SIGNAL, QGridLayout, QInputDialog, QPushButton
       +import PyQt4.QtCore as QtCore
       +from electrum_gui.qt.util import *
       +from electrum_gui.qt.main_window import StatusBarButton, ElectrumWindow
       +from electrum_gui.qt.installwizard import InstallWizard
       +from trezorlib.qt.pinmatrix import PinMatrixWidget
        
       +class QtPlugin(Plugin):
        
       -class TrezorWallet(BIP32_HD_Wallet):
       -    wallet_type = 'trezor'
       -    root_derivation = "m/44'/0'"
       -
       -    def __init__(self, storage):
       -        BIP32_HD_Wallet.__init__(self, storage)
       -        self.mpk = None
       -        self.device_checked = False
       -        self.proper_device = False
       -        self.force_watching_only = False
       -
       -    def get_action(self):
       -        if not self.accounts:
       -            return 'create_accounts'
       -
       -    def can_import(self):
       -        return False
       -
       -    def can_sign_xpubkey(self, x_pubkey):
       -        xpub, sequence = BIP32_Account.parse_xpubkey(x_pubkey)
       -        return xpub in self.master_public_keys.values()
       -
       -    def can_export(self):
       -        return False
       -
       -    def can_create_accounts(self):
       -        return True
       -
       -    def can_change_password(self):
       -        return False
       -
       -    def is_watching_only(self):
       -        return self.force_watching_only
       -
       -    def get_client(self):
       -        return self.plugin.get_client()
       -
       -    def address_id(self, address):
       -        account_id, (change, address_index) = self.get_address_index(address)
       -        return "44'/0'/%s'/%d/%d" % (account_id, change, address_index)
       -
       -    def create_main_account(self, password):
       -        self.create_account('Main account', None) #name, empty password
       -
       -    def mnemonic_to_seed(self, mnemonic, passphrase):
       -        # trezor uses bip39
       -        import pbkdf2, hashlib, hmac
       -        PBKDF2_ROUNDS = 2048
       -        mnemonic = unicodedata.normalize('NFKD', ' '.join(mnemonic.split()))
       -        passphrase = unicodedata.normalize('NFKD', passphrase)
       -        return pbkdf2.PBKDF2(mnemonic, 'mnemonic' + passphrase, iterations = PBKDF2_ROUNDS, macmodule = hmac, digestmodule = hashlib.sha512).read(64)
       -
       -    def derive_xkeys(self, root, derivation, password):
       -        x = self.master_private_keys.get(root)
       -        if x:
       -            root_xprv = pw_decode(x, password)
       -            xprv, xpub = bip32_private_derivation(root_xprv, root, derivation)
       -            return xpub, xprv
       -        else:
       -            derivation = derivation.replace(self.root_name,"44'/0'/")
       -            xpub = self.get_public_key(derivation)
       -            return xpub, None
       -
       -    def get_public_key(self, bip32_path):
       -        address_n = self.plugin.get_client().expand_path(bip32_path)
       -        node = self.plugin.get_client().get_public_node(address_n).node
       -        xpub = "0488B21E".decode('hex') + chr(node.depth) + self.i4b(node.fingerprint) + self.i4b(node.child_num) + node.chain_code + node.public_key
       -        return EncodeBase58Check(xpub)
       -
       -    def get_master_public_key(self):
       -        if not self.mpk:
       -            self.mpk = self.get_public_key("44'/0'")
       -        return self.mpk
       +    @hook
       +    def load_wallet(self, wallet, window):
       +        self.print_error("load_wallet")
       +        self.wallet = wallet
       +        self.wallet.plugin = self
       +        self.trezor_button = StatusBarButton(QIcon(":icons/trezor.png"), _("Trezor"), partial(self.settings_dialog, window))
       +        if type(window) is ElectrumWindow:
       +            window.statusBar().addPermanentWidget(self.trezor_button)
       +        if self.handler is None:
       +            self.handler = TrezorQtHandler(window)
       +        try:
       +            self.get_client().ping('t')
       +        except BaseException as e:
       +            QMessageBox.information(window, _('Error'), _("Trezor device not detected.\nContinuing in watching-only mode." + '\n\nReason:\n' + str(e)), _('OK'))
       +            self.wallet.force_watching_only = True
       +            return
       +        if self.wallet.addresses() and not self.wallet.check_proper_device():
       +            QMessageBox.information(window, _('Error'), _("This wallet does not match your Trezor device"), _('OK'))
       +            self.wallet.force_watching_only = True
        
       -    def i4b(self, x):
       -        return pack('>I', x)
       +    @hook
       +    def installwizard_load_wallet(self, wallet, window):
       +        if type(wallet) != TrezorWallet:
       +            return
       +        self.load_wallet(wallet, window)
        
       -    def add_keypairs(self, tx, keypairs, password):
       -        #do nothing - no priv keys available
       -        pass
       +    @hook
       +    def installwizard_restore(self, wizard, storage):
       +        if storage.get('wallet_type') != 'trezor':
       +            return
       +        seed = wizard.enter_seed_dialog("Enter your Trezor seed", None, func=lambda x:True)
       +        if not seed:
       +            return
       +        wallet = TrezorWallet(storage)
       +        self.wallet = wallet
       +        handler = TrezorQtHandler(wizard)
       +        passphrase = handler.get_passphrase(_("Please enter your Trezor passphrase.") + '\n' + _("Press OK if you do not use one."))
       +        if passphrase is None:
       +            return
       +        password = wizard.password_dialog()
       +        wallet.add_seed(seed, password)
       +        wallet.add_cosigner_seed(seed, 'x/', password, passphrase)
       +        wallet.create_main_account(password)
       +        # disable trezor plugin
       +        self.set_enabled(False)
       +        return wallet
        
       -    def decrypt_message(self, pubkey, message, password):
       -        raise BaseException( _('Decrypt method is not implemented in Trezor') )
       -        #address = public_key_to_bc_address(pubkey.decode('hex'))
       -        #address_path = self.address_id(address)
       -        #address_n = self.get_client().expand_path(address_path)
       -        #try:
       -        #    decrypted_msg = self.get_client().decrypt_message(address_n, b64decode(message))
       -        #except Exception, e:
       -        #    give_error(e)
       -        #finally:
       -        #    twd.stop()
       -        #return str(decrypted_msg)
       +    @hook
       +    def receive_menu(self, menu, addrs):
       +        if not self.wallet.is_watching_only() and self.atleast_version(1, 3) and len(addrs) == 1:
       +            menu.addAction(_("Show on TREZOR"), lambda: self.show_address(addrs[0]))
        
       -    def sign_message(self, address, message, password):
       -        if self.has_seed():
       -            return BIP32_HD_Wallet.sign_message(self, address, message, password)
       -        if not self.check_proper_device():
       +    def show_address(self, address):
       +        if not self.wallet.check_proper_device():
                    give_error('Wrong device or password')
                try:
       -            address_path = self.address_id(address)
       -            address_n = self.plugin.get_client().expand_path(address_path)
       +            address_path = self.wallet.address_id(address)
       +            address_n = self.get_client().expand_path(address_path)
                except Exception, e:
                    give_error(e)
                try:
       -            msg_sig = self.plugin.get_client().sign_message('Bitcoin', address_n, message)
       +            self.get_client().get_address('Bitcoin', address_n, True)
                except Exception, e:
                    give_error(e)
                finally:
       -            self.plugin.handler.stop()
       -        return msg_sig.signature
       +            self.handler.stop()
        
       -    def sign_transaction(self, tx, password):
       -        if self.has_seed():
       -            return BIP32_HD_Wallet.sign_transaction(self, tx, password)
       -        if tx.is_complete():
       +
       +    def settings_dialog(self, window):
       +        try:
       +            device_id = self.get_client().get_device_id()
       +        except BaseException as e:
       +            window.show_message(str(e))
                    return
       -        if not self.check_proper_device():
       -            give_error('Wrong device or password')
       -        # previous transactions used as inputs
       -        prev_tx = {}
       -        # path of the xpubs that are involved
       -        xpub_path = {}
       -        for txin in tx.inputs:
       -            tx_hash = txin['prevout_hash']
       +        get_label = lambda: self.get_client().features.label
       +        update_label = lambda: current_label_label.setText("Label: %s" % get_label())
       +        d = QDialog()
       +        layout = QGridLayout(d)
       +        layout.addWidget(QLabel("Trezor Options"),0,0)
       +        layout.addWidget(QLabel("ID:"),1,0)
       +        layout.addWidget(QLabel(" %s" % device_id),1,1)
        
       -            ptx = self.transactions.get(tx_hash)
       -            if ptx is None:
       -                ptx = self.network.synchronous_get(('blockchain.transaction.get', [tx_hash]))
       -                ptx = Transaction(ptx)
       -            prev_tx[tx_hash] = ptx
       +        def modify_label():
       +            response = QInputDialog().getText(None, "Set New Trezor Label", "New Trezor Label:  (upon submission confirm on Trezor)")
       +            if not response[1]:
       +                return
       +            new_label = str(response[0])
       +            self.handler.show_message("Please confirm label change on Trezor")
       +            status = self.get_client().apply_settings(label=new_label)
       +            self.handler.stop()
       +            update_label()
       +
       +        current_label_label = QLabel()
       +        update_label()
       +        change_label_button = QPushButton("Modify")
       +        change_label_button.clicked.connect(modify_label)
       +        layout.addWidget(current_label_label,3,0)
       +        layout.addWidget(change_label_button,3,1)
       +        d.exec_()
        
       -            for x_pubkey in txin['x_pubkeys']:
       -                account_derivation = None
       -                if not is_extended_pubkey(x_pubkey):
       -                    continue
       -                xpub = x_to_xpub(x_pubkey)
       -                for k, v in self.master_public_keys.items():
       -                    if v == xpub:
       -                        account_id = re.match("x/(\d+)'", k).group(1)
       -                        account_derivation = "44'/0'/%s'"%account_id
       -                if account_derivation is not None:
       -                    xpub_path[xpub] = account_derivation
        
       -        self.plugin.sign_transaction(tx, prev_tx, xpub_path)
        
       -    def check_proper_device(self):
       -        self.get_client().ping('t')
       -        if not self.device_checked:
       -            address = self.addresses(False)[0]
       -            address_id = self.address_id(address)
       -            n = self.get_client().expand_path(address_id)
       -            device_address = self.get_client().get_address('Bitcoin', n)
       -            self.device_checked = True
        
       -            if device_address != address:
       -                self.proper_device = False
       -            else:
       -                self.proper_device = True
        
       -        return self.proper_device
        
        
        class TrezorGuiMixin(object):
   DIR diff --git a/plugins/trustedcoin.py b/plugins/trustedcoin.py
       t@@ -27,9 +27,6 @@ from urlparse import urljoin
        from urllib import quote
        from functools import partial
        
       -from PyQt4.QtGui import *
       -from PyQt4.QtCore import *
       -
        import electrum
        from electrum import bitcoin
        from electrum.bitcoin import *
       t@@ -39,10 +36,6 @@ from electrum.wallet import Multisig_Wallet, BIP32_Wallet
        from electrum.i18n import _
        from electrum.plugins import BasePlugin, run_hook, hook
        
       -from electrum_gui.qt.util import *
       -from electrum_gui.qt.qrcodewidget import QRCodeWidget
       -from electrum_gui.qt.amountedit import AmountEdit
       -from electrum_gui.qt.main_window import StatusBarButton
        
        from decimal import Decimal
        
       t@@ -460,6 +453,16 @@ class Plugin(BasePlugin):
                wallet.add_master_public_key('x3/', xpub3)
                return True
        
       +
       +from PyQt4.QtGui import *
       +from PyQt4.QtCore import *
       +from electrum_gui.qt.util import *
       +from electrum_gui.qt.qrcodewidget import QRCodeWidget
       +from electrum_gui.qt.amountedit import AmountEdit
       +from electrum_gui.qt.main_window import StatusBarButton
       +
       +class QtPlugin(Plugin):
       +
            def auth_dialog(self, window):
                d = QDialog(window)
                d.setModal(1)
       t@@ -674,3 +677,5 @@ class Plugin(BasePlugin):
                    except:
                        QMessageBox.information(window, _('Message'), _('Incorrect password'), _('OK'))
                        pw.setText('')
       +
       +
   DIR diff --git a/plugins/virtualkeyboard.py b/plugins/virtualkeyboard.py
       t@@ -3,7 +3,7 @@ from electrum.plugins import BasePlugin, hook
        from electrum.i18n import _
        import random
        
       -class Plugin(BasePlugin):
       +class QtPlugin(BasePlugin):
        
            vkb = None
            vkb_index = 0