Better support for USB devices
Benefits of this rewrite include: - support of disconnecting / reconnecting a device without having to close the wallet, even in a different USB socket - support of multiple keepkey / trezor devices, both during wallet creation and general use - wallet is watching-only dynamically according to whether the associated device is currently plugged in or not
This commit is contained in:
@@ -3,7 +3,6 @@ import threading
|
||||
|
||||
from PyQt4.Qt import QGridLayout, QInputDialog, QPushButton
|
||||
from PyQt4.Qt import QVBoxLayout, QLabel, SIGNAL
|
||||
from trezor import TrezorPlugin
|
||||
from electrum_gui.qt.main_window import StatusBarButton
|
||||
from electrum_gui.qt.password_dialog import PasswordDialog
|
||||
from electrum_gui.qt.util import *
|
||||
@@ -19,23 +18,30 @@ class QtHandler(PrintError):
|
||||
Trezor protocol; derived classes can customize it.'''
|
||||
|
||||
def __init__(self, win, pin_matrix_widget_class, device):
|
||||
win.connect(win, SIGNAL('message_done'), self.dialog_stop)
|
||||
win.connect(win, SIGNAL('clear_dialog'), self.clear_dialog)
|
||||
win.connect(win, SIGNAL('error_dialog'), self.error_dialog)
|
||||
win.connect(win, SIGNAL('message_dialog'), self.message_dialog)
|
||||
win.connect(win, SIGNAL('pin_dialog'), self.pin_dialog)
|
||||
win.connect(win, SIGNAL('passphrase_dialog'), self.passphrase_dialog)
|
||||
self.window_stack = [win]
|
||||
self.win = win
|
||||
self.windows = [win]
|
||||
self.pin_matrix_widget_class = pin_matrix_widget_class
|
||||
self.device = device
|
||||
self.done = threading.Event()
|
||||
self.dialog = None
|
||||
self.done = threading.Event()
|
||||
|
||||
def stop(self):
|
||||
self.win.emit(SIGNAL('message_done'))
|
||||
def watching_only_changed(self):
|
||||
self.win.emit(SIGNAL('watching_only_changed'))
|
||||
|
||||
def show_message(self, msg, cancel_callback=None):
|
||||
self.win.emit(SIGNAL('message_dialog'), msg, cancel_callback)
|
||||
|
||||
def show_error(self, msg):
|
||||
self.win.emit(SIGNAL('error_dialog'), msg)
|
||||
|
||||
def finished(self):
|
||||
self.win.emit(SIGNAL('clear_dialog'))
|
||||
|
||||
def get_pin(self, msg):
|
||||
self.done.clear()
|
||||
self.win.emit(SIGNAL('pin_dialog'), msg)
|
||||
@@ -50,22 +56,19 @@ class QtHandler(PrintError):
|
||||
|
||||
def pin_dialog(self, msg):
|
||||
# Needed e.g. when renaming label and haven't entered PIN
|
||||
self.dialog_stop()
|
||||
d = WindowModalDialog(self.windows[-1], _("Enter PIN"))
|
||||
dialog = WindowModalDialog(self.window_stack[-1], _("Enter PIN"))
|
||||
matrix = self.pin_matrix_widget_class()
|
||||
vbox = QVBoxLayout()
|
||||
vbox.addWidget(QLabel(msg))
|
||||
vbox.addWidget(matrix)
|
||||
vbox.addLayout(Buttons(CancelButton(d), OkButton(d)))
|
||||
d.setLayout(vbox)
|
||||
if not d.exec_():
|
||||
self.response = None # FIXME: this is lost?
|
||||
vbox.addLayout(Buttons(CancelButton(dialog), OkButton(dialog)))
|
||||
dialog.setLayout(vbox)
|
||||
dialog.exec_()
|
||||
self.response = str(matrix.get_value())
|
||||
self.done.set()
|
||||
|
||||
def passphrase_dialog(self, msg):
|
||||
self.dialog_stop()
|
||||
d = PasswordDialog(self.windows[-1], None, msg,
|
||||
d = PasswordDialog(self.window_stack[-1], None, msg,
|
||||
PasswordDialog.PW_PASSHPRASE)
|
||||
confirmed, p, passphrase = d.run()
|
||||
if confirmed:
|
||||
@@ -75,9 +78,9 @@ class QtHandler(PrintError):
|
||||
|
||||
def message_dialog(self, msg, cancel_callback):
|
||||
# Called more than once during signing, to confirm output and fee
|
||||
self.dialog_stop()
|
||||
self.clear_dialog()
|
||||
title = _('Please check your %s device') % self.device
|
||||
dialog = self.dialog = WindowModalDialog(self.windows[-1], title)
|
||||
self.dialog = dialog = WindowModalDialog(self.window_stack[-1], title)
|
||||
l = QLabel(msg)
|
||||
vbox = QVBoxLayout(dialog)
|
||||
if cancel_callback:
|
||||
@@ -86,19 +89,25 @@ class QtHandler(PrintError):
|
||||
vbox.addWidget(l)
|
||||
dialog.show()
|
||||
|
||||
def dialog_stop(self):
|
||||
def error_dialog(self, msg):
|
||||
self.win.show_error(msg, parent=self.window_stack[-1])
|
||||
|
||||
def clear_dialog(self):
|
||||
if self.dialog:
|
||||
self.dialog.hide()
|
||||
self.dialog.accept()
|
||||
self.dialog = None
|
||||
|
||||
def pop_window(self):
|
||||
self.windows.pop()
|
||||
|
||||
def push_window(self, window):
|
||||
self.windows.append(window)
|
||||
def exec_dialog(self, dialog):
|
||||
self.window_stack.append(dialog)
|
||||
try:
|
||||
dialog.exec_()
|
||||
finally:
|
||||
assert dialog == self.window_stack.pop()
|
||||
|
||||
|
||||
class QtPlugin(TrezorPlugin):
|
||||
def qt_plugin_class(base_plugin_class):
|
||||
|
||||
class QtPlugin(base_plugin_class):
|
||||
# Derived classes must provide the following class-static variables:
|
||||
# icon_file
|
||||
# pin_matrix_widget_class
|
||||
@@ -110,33 +119,28 @@ class QtPlugin(TrezorPlugin):
|
||||
def load_wallet(self, wallet, window):
|
||||
if type(wallet) != self.wallet_class:
|
||||
return
|
||||
try:
|
||||
client = self.get_client(wallet)
|
||||
client.handler = self.create_handler(window)
|
||||
client.check_proper_device(wallet)
|
||||
self.button = StatusBarButton(QIcon(self.icon_file), self.device,
|
||||
partial(self.settings_dialog, window))
|
||||
window.statusBar().addPermanentWidget(self.button)
|
||||
except Exception as e:
|
||||
window.show_error(str(e))
|
||||
window.tzb = StatusBarButton(QIcon(self.icon_file), self.device,
|
||||
partial(self.settings_dialog, window))
|
||||
window.statusBar().addPermanentWidget(window.tzb)
|
||||
wallet.handler = self.create_handler(window)
|
||||
# Trigger a pairing
|
||||
self.client(wallet)
|
||||
|
||||
def on_create_wallet(self, wallet, wizard):
|
||||
client = self.get_client(wallet)
|
||||
client.handler = self.create_handler(wizard)
|
||||
assert type(wallet) == self.wallet_class
|
||||
wallet.handler = self.create_handler(wizard)
|
||||
self.select_device(wallet, wizard)
|
||||
wallet.create_main_account(None)
|
||||
|
||||
@hook
|
||||
def receive_menu(self, menu, addrs, wallet):
|
||||
if type(wallet) != self.wallet_class:
|
||||
return
|
||||
if (not wallet.is_watching_only() and
|
||||
self.atleast_version(1, 3) and len(addrs) == 1):
|
||||
if type(wallet) == self.wallet_class and len(addrs) == 1:
|
||||
menu.addAction(_("Show on %s") % self.device,
|
||||
lambda: self.show_address(wallet, addrs[0]))
|
||||
|
||||
def settings_dialog(self, window):
|
||||
|
||||
handler = self.get_client(window.wallet).handler
|
||||
handler = window.wallet.handler
|
||||
client = self.client(window.wallet)
|
||||
|
||||
def rename():
|
||||
title = _("Set Device Label")
|
||||
@@ -145,10 +149,7 @@ class QtPlugin(TrezorPlugin):
|
||||
if not response[1]:
|
||||
return
|
||||
new_label = str(response[0])
|
||||
try:
|
||||
client.change_label(new_label)
|
||||
finally:
|
||||
handler.stop()
|
||||
client.change_label(new_label)
|
||||
device_label.setText(new_label)
|
||||
|
||||
def update_pin_info():
|
||||
@@ -159,13 +160,9 @@ class QtPlugin(TrezorPlugin):
|
||||
clear_pin_button.setVisible(features.pin_protection)
|
||||
|
||||
def set_pin(remove):
|
||||
try:
|
||||
client.set_pin(remove=remove)
|
||||
finally:
|
||||
handler.stop()
|
||||
client.set_pin(remove=remove)
|
||||
update_pin_info()
|
||||
|
||||
client = self.get_client()
|
||||
features = client.features
|
||||
noyes = [_("No"), _("Yes")]
|
||||
bl_hash = features.bootloader_hash.encode('hex').upper()
|
||||
@@ -200,7 +197,7 @@ class QtPlugin(TrezorPlugin):
|
||||
widget = item if isinstance(item, QWidget) else QLabel(item)
|
||||
layout.addWidget(widget, row_num, col_num)
|
||||
|
||||
dialog = WindowModalDialog(None, _("%s Settings") % self.device)
|
||||
dialog = WindowModalDialog(window, _("%s Settings") % self.device)
|
||||
vbox = QVBoxLayout()
|
||||
tabs = QTabWidget()
|
||||
tabs.addTab(info_tab, _("Information"))
|
||||
@@ -210,8 +207,6 @@ class QtPlugin(TrezorPlugin):
|
||||
vbox.addLayout(Buttons(CloseButton(dialog)))
|
||||
|
||||
dialog.setLayout(vbox)
|
||||
handler.push_window(dialog)
|
||||
try:
|
||||
dialog.exec_()
|
||||
finally:
|
||||
handler.pop_window()
|
||||
handler.exec_dialog(dialog)
|
||||
|
||||
return QtPlugin
|
||||
|
||||
Reference in New Issue
Block a user