electrum

Electrum Bitcoin wallet
git clone https://git.parazyd.org/electrum
Log | Files | Refs | Submodules

commit 2ae3543dc4e84e5e9c1816a54be50057bdb673e9
parent f7b39f49524b8f907783e82533d1196f3c232a79
Author: Neil Booth <kyuupichan@gmail.com>
Date:   Tue, 12 Jan 2016 22:31:53 +0900

Start work on persistent install wizard

Diffstat:
Mgui/qt/installwizard.py | 157+++++++++++++++++++++++++++++++++++++++++++++----------------------------------
Mgui/qt/seed_dialog.py | 88+++++++++++++++++++++++++++++++++++++++++--------------------------------------
Mplugins/trustedcoin/qt.py | 9+++++++--
3 files changed, 143 insertions(+), 111 deletions(-)

diff --git a/gui/qt/installwizard.py b/gui/qt/installwizard.py @@ -20,6 +20,11 @@ from electrum.wizard import (WizardBase, UserCancelled, MSG_SHOW_MPK, MSG_VERIFY_SEED, MSG_GENERATING_WAIT) +def clean_text(seed_e): + text = unicode(seed_e.toPlainText()).strip() + text = ' '.join(text.split()) + return text + class CosignWidget(QWidget): size = 120 @@ -69,8 +74,64 @@ class InstallWizard(WindowModalDialog, WizardBase): self.setMinimumSize(575, 400) self.setMaximumSize(575, 400) self.connect(self, QtCore.SIGNAL('accept'), self.accept) - self.stack = QStackedLayout() - self.setLayout(self.stack) + self.title = QLabel() + self.main_widget = QWidget() + self.cancel_button = QPushButton(_("Cancel"), self) + self.next_button = QPushButton(_("Next"), self) + self.next_button.setDefault(True) + self.logo = QLabel() + self.please_wait = QLabel(_("Please wait...")) + self.please_wait.setAlignment(Qt.AlignCenter) + self.icon_filename = None + self.loop = QEventLoop() + self.cancel_button.clicked.connect(lambda: self.loop.exit(False)) + self.next_button.clicked.connect(lambda: self.loop.exit(True)) + outer_vbox = QVBoxLayout(self) + inner_vbox = QVBoxLayout() + inner_vbox = QVBoxLayout() + inner_vbox.addWidget(self.title) + inner_vbox.addWidget(self.main_widget) + inner_vbox.addStretch(1) + inner_vbox.addWidget(self.please_wait) + inner_vbox.addStretch(1) + icon_vbox = QVBoxLayout() + icon_vbox.addWidget(self.logo) + icon_vbox.addStretch(1) + hbox = QHBoxLayout() + hbox.addLayout(icon_vbox) + hbox.addLayout(inner_vbox) + hbox.setStretchFactor(inner_vbox, 1) + outer_vbox.addLayout(hbox) + outer_vbox.addLayout(Buttons(self.cancel_button, self.next_button)) + self.set_icon(':icons/electrum.png') + self.show() + self.raise_() + + def set_icon(self, filename): + prior_filename, self.icon_filename = self.icon_filename, filename + self.logo.setPixmap(QPixmap(filename).scaledToWidth(70)) + return prior_filename + + def set_main_layout(self, layout, title): + self.title.setText(title) + prior_layout = self.main_widget.layout() + if prior_layout: + QWidget().setLayout(prior_layout) + self.main_widget.setLayout(layout) + self.cancel_button.setEnabled(True) + self.next_button.setEnabled(True) + self.main_widget.setVisible(True) + self.please_wait.setVisible(False) + if not self.loop.exec_(): + raise UserCancelled + self.title.setText("") + self.cancel_button.setEnabled(False) + self.next_button.setEnabled(False) + self.main_widget.setVisible(False) + self.please_wait.setVisible(True) + # For some reason, to refresh the GUI this needs to be called twice + self.app.processEvents() + self.app.processEvents() def open_wallet(self, *args): '''Wrap the base wizard implementation with try/except blocks @@ -80,28 +141,35 @@ class InstallWizard(WindowModalDialog, WizardBase): wallet = super(InstallWizard, self).open_wallet(*args) except UserCancelled: self.print_error("wallet creation cancelled by user") + self.accept() return wallet def remove_from_recently_open(self, filename): self.config.remove_from_recently_open(filename) - # Called by plugins - def confirm(self, msg, icon=None): - '''Returns True or False''' - vbox = QVBoxLayout() - self.set_layout(vbox) - if icon: - logo = QLabel() - logo.setPixmap(icon) - vbox.addWidget(logo) - label = QLabel(msg) - label.setWordWrap(True) - vbox.addWidget(label) - vbox.addStretch(1) - vbox.addLayout(Buttons(CancelButton(self, _("Cancel")), - OkButton(self, _("Next")))) - if not self.exec_(): - raise UserCancelled + def request_seed(self, title, is_valid=None): + is_valid = is_valid or Wallet.is_any + slayout = seed_dialog.SeedLayout(None) + self.next_button.setEnabled(False) + def sanitized_seed(): + return clean_text(slayout.seed_edit()) + def set_enabled(): + self.next_button.setEnabled(is_valid(sanitized_seed())) + slayout.seed_edit().textChanged.connect(set_enabled) + self.set_main_layout(slayout.layout(), title) + return sanitized_seed() + + def show_seed(self, seed): + title = _("Your wallet generation seed is:") + slayout = seed_dialog.SeedLayout(seed) + self.set_main_layout(slayout.layout(), title) + + def verify_seed(self, seed, is_valid=None): + while True: + r = self.request_seed(MSG_VERIFY_SEED, is_valid) + if prepare_seed(r) == prepare_seed(seed): + return + self.show_error(_('Incorrect seed')) def show_and_verify_seed(self, seed, is_valid=None): """Show the user their seed. Ask them to re-enter it. Return @@ -179,55 +247,17 @@ class InstallWizard(WindowModalDialog, WizardBase): actions = [_("Create a new wallet"), _("Restore a wallet or import keys")] - main_label = QLabel(_("Electrum could not find an existing wallet.")) + title = _("Electrum could not find an existing wallet.") actions_clayout = ChoicesLayout(_("What do you want to do?"), actions) wallet_clayout = ChoicesLayout(_("Wallet kind:"), wallet_kinds) vbox = QVBoxLayout() - vbox.addWidget(main_label) vbox.addLayout(actions_clayout.layout()) vbox.addLayout(wallet_clayout.layout()) - vbox.addStretch(1) - - OK = OkButton(self, _('Next')) - vbox.addLayout(Buttons(CancelButton(self), OK)) - self.set_layout(vbox) - OK.setDefault(True) - self.raise_() - - if not self.exec_(): - raise UserCancelled - + self.set_main_layout(vbox, title) action = ['create', 'restore'][actions_clayout.selected_index()] return action, wallet_clayout.selected_index() - def verify_seed(self, seed, is_valid=None): - while True: - r = self.request_seed(MSG_VERIFY_SEED, is_valid) - if prepare_seed(r) == prepare_seed(seed): - return - self.show_error(_('Incorrect seed')) - - def get_seed_text(self, seed_e): - text = unicode(seed_e.toPlainText()).strip() - text = ' '.join(text.split()) - return text - - def request_seed(self, msg, is_valid=None): - is_valid = is_valid or Wallet.is_any - vbox, seed_e = seed_dialog.enter_seed_box(msg, self) - vbox.addStretch(1) - button = OkButton(self, _('Next')) - vbox.addLayout(Buttons(CancelButton(self), button)) - button.setEnabled(False) - def set_enabled(): - button.setEnabled(is_valid(self.get_seed_text(seed_e))) - seed_e.textChanged.connect(set_enabled) - self.set_layout(vbox) - if not self.exec_(): - raise UserCancelled - return self.get_seed_text(seed_e) - def request_many(self, n, xpub_hot=None): vbox = QVBoxLayout() scroll = QScrollArea() @@ -263,7 +293,7 @@ class InstallWizard(WindowModalDialog, WizardBase): vbox.addLayout(Buttons(CancelButton(self), button)) button.setEnabled(False) def get_texts(): - return [self.get_seed_text(entry) for entry in entries] + return [clean_text(entry) for entry in entries] def set_enabled(): texts = get_texts() is_valid = Wallet.is_xpub if xpub_hot else Wallet.is_any @@ -360,10 +390,3 @@ class InstallWizard(WindowModalDialog, WizardBase): n = int(n_edit.value()) wallet_type = '%dof%d'%(m,n) return wallet_type - - def show_seed(self, seed): - vbox = seed_dialog.show_seed_box_msg(seed, None) - vbox.addLayout(Buttons(CancelButton(self), OkButton(self, _("Next")))) - self.set_layout(vbox) - if not self.exec_(): - raise UserCancelled diff --git a/gui/qt/seed_dialog.py b/gui/qt/seed_dialog.py @@ -43,48 +43,52 @@ def icon_filename(sid): else: return ":icons/seed.png" +class SeedLayout(object): + def __init__(self, seed, sid=None): + if seed: + self.vbox = self.seed_and_warning_layout(seed, sid) + else: + self.vbox = self.seed_layout(seed, sid) -def show_seed_box_msg(seedphrase, sid=None): - msg = _("Your wallet generation seed is") + ":" - vbox = show_seed_box(msg, seedphrase, sid) - msg = ''.join([ - "<p>", - _("Please save these %d words on paper (order is important).")%len(seedphrase.split()) + " ", - _("This seed will allow you to recover your wallet in case of computer failure.") + "<br/>", - "</p>", - "<b>" + _("WARNING") + ":</b> ", - "<ul>", - "<li>" + _("Never disclose your seed.") + "</li>", - "<li>" + _("Never type it on a website.") + "</li>", - "<li>" + _("Do not send your seed to a printer.") + "</li>", - "</ul>" - ]) - label2 = QLabel(msg) - label2.setWordWrap(True) - vbox.addWidget(label2) - vbox.addStretch(1) - return vbox + def layout(self): + return self.vbox -def show_seed_box(msg, seed, sid): - vbox, seed_e = enter_seed_box(msg, None, sid=sid, text=seed) - return vbox + def seed_edit(self): + return self.seed_e -def enter_seed_box(msg, window, sid=None, text=None): - vbox = QVBoxLayout() - logo = QLabel() - logo.setPixmap(QPixmap(icon_filename(sid)).scaledToWidth(56)) - logo.setMaximumWidth(60) - label = QLabel(msg) - label.setWordWrap(True) - if not text: - seed_e = ScanQRTextEdit() - seed_e.setTabChangesFocus(True) - else: - seed_e = ShowQRTextEdit(text=text) - seed_e.setMaximumHeight(130) - vbox.addWidget(label) - grid = QGridLayout() - grid.addWidget(logo, 0, 0) - grid.addWidget(seed_e, 0, 1) - vbox.addLayout(grid) - return vbox, seed_e + def seed_and_warning_layout(self, seed, sid=None): + vbox = QVBoxLayout() + vbox.addLayout(self.seed_layout(seed, sid)) + msg = ''.join([ + "<p>", + _("Please save these %d words on paper (order is important). "), + _("This seed will allow you to recover your wallet in case " + "of computer failure.") + "<br/>", + "</p>", + "<b>" + _("WARNING") + ":</b> ", + "<ul>", + "<li>" + _("Never disclose your seed.") + "</li>", + "<li>" + _("Never type it on a website.") + "</li>", + "<li>" + _("Do not send your seed to a printer.") + "</li>", + "</ul>" + ]) % len(seed.split()) + label2 = QLabel(msg) + label2.setWordWrap(True) + vbox.addWidget(label2) + return vbox + + def seed_layout(self, seed, sid=None): + logo = QLabel() + logo.setPixmap(QPixmap(icon_filename(sid)).scaledToWidth(56)) + logo.setMaximumWidth(60) + if not seed: + seed_e = ScanQRTextEdit() + seed_e.setTabChangesFocus(True) + else: + seed_e = ShowQRTextEdit(text=seed) + seed_e.setMaximumHeight(100) + self.seed_e = seed_e + hbox = QHBoxLayout() + hbox.addWidget(logo) + hbox.addWidget(seed_e) + return hbox diff --git a/plugins/trustedcoin/qt.py b/plugins/trustedcoin/qt.py @@ -103,9 +103,14 @@ class Plugin(TrustedCoinPlugin): on_finished) def show_disclaimer(self, wallet, window): - icon = QPixmap(':icons/trustedcoin.png') - window.confirm('\n\n'.join(DISCLAIMER), icon=icon) + prior_icon = window.set_icon(':icons/trustedcoin.png') + label = QLabel('\n\n'.join(DISCLAIMER)) + label.setWordWrap(True) + vbox = QVBoxLayout() + vbox.addWidget(label) + window.set_main_layout(vbox, _("Two-Factor Authentication")) self.set_enabled(wallet, True) + window.set_icon(prior_icon) @hook def abort_send(self, window):