From c33175c31279e68d2375e97aae1e1f7acb3ebf7b Mon Sep 17 00:00:00 2001 From: Neil Booth Date: Sat, 4 Jul 2015 18:13:26 +0900 Subject: [PATCH 1/2] Improved password wrapper. Doesn't require any particular argument order of the wrapped function. Caller can tell if the user cancelled the request or not. Optionally handles parent window hints. --- gui/qt/main_window.py | 55 +++++++++++++++++++++++-------------------- 1 file changed, 29 insertions(+), 26 deletions(-) diff --git a/gui/qt/main_window.py b/gui/qt/main_window.py index b75049f9f..0dd1980b9 100644 --- a/gui/qt/main_window.py +++ b/gui/qt/main_window.py @@ -1043,7 +1043,31 @@ class ElectrumWindow(QMainWindow): self.completions.setStringList(l) def protected(func): - return lambda s, *args: s.do_protect(func, args) + '''Password request wrapper. The password is passed to the function + as the 'password' named argument. Return value is a 2-element + tuple: (Cancelled, Result) where Cancelled is True if the user + cancels the password request, otherwise False. Result is the + return value of the wrapped function, or None if cancelled. + ''' + def request_password(self, *args, **kwargs): + parent = kwargs.get('parent', self) + if self.wallet.use_encryption: + while True: + password = self.password_dialog(parent=parent) + if not password: + return True, None + try: + self.wallet.check_password(password) + break + except Exception as e: + QMessageBox.warning(parent, _('Error'), str(e), _('OK')) + continue + else: + password = None + + kwargs['password'] = password + return False, func(self, *args, **kwargs) + return request_password def read_send_tab(self): if self.payment_request and self.payment_request.has_expired(): @@ -1858,29 +1882,6 @@ class ElectrumWindow(QMainWindow): d = QRDialog(data, self, title) d.exec_() - - def do_protect(self, func, args): - if self.wallet.use_encryption: - while True: - password = self.password_dialog() - if not password: - return - try: - self.wallet.check_password(password) - break - except Exception as e: - QMessageBox.warning(self, _('Error'), str(e), _('OK')) - continue - else: - password = None - - if args != (False,): - args = (self,) + args + (password,) - else: - args = (self, password) - apply(func, args) - - def show_public_keys(self, address): if not address: return try: @@ -2060,8 +2061,10 @@ class ElectrumWindow(QMainWindow): def show_warning(self, msg): QMessageBox.warning(self, _('Warning'), msg, _('OK')) - def password_dialog(self, msg=None): - d = QDialog(self) + def password_dialog(self, msg=None, parent=None): + if parent == None: + parent = self + d = QDialog(parent) d.setModal(1) d.setWindowTitle(_("Enter Password")) pw = QLineEdit() From d41dfa394b757ca04889782a14d203b8253daf77 Mon Sep 17 00:00:00 2001 From: Neil Booth Date: Sat, 4 Jul 2015 18:25:44 +0900 Subject: [PATCH 2/2] Improved dialog centring Password requests from the tx dialog box are now centred on the tx dialog. Similarly for error messages if misentering the password. Also, "Signing transaction..." and "Broadcasting transaction..." are centred on the appropriate tx dialog. Finally restore the old "Sign" button enabling / disabling, as we can now tell if the user cancelled the password request. --- gui/qt/main_window.py | 16 ++++++++++------ gui/qt/transaction_dialog.py | 9 +++++++-- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/gui/qt/main_window.py b/gui/qt/main_window.py index 0dd1980b9..5cc56c3c9 100644 --- a/gui/qt/main_window.py +++ b/gui/qt/main_window.py @@ -1165,10 +1165,12 @@ class ElectrumWindow(QMainWindow): @protected - def sign_tx(self, tx, callback, password): + def sign_tx(self, tx, callback, password, parent=None): '''Sign the transaction in a separate thread. When done, calls the callback with a success code of True or False. ''' + if parent == None: + parent = self self.send_button.setDisabled(True) # call hook to see if plugin needs gui interaction @@ -1186,11 +1188,11 @@ class ElectrumWindow(QMainWindow): callback(success[0]) # keep a reference to WaitingDialog or the gui might crash - self.waiting_dialog = WaitingDialog(self, 'Signing transaction...', sign_thread, on_sign_successful, on_dialog_close) + self.waiting_dialog = WaitingDialog(parent, 'Signing transaction...', sign_thread, on_sign_successful, on_dialog_close) self.waiting_dialog.start() - def broadcast_transaction(self, tx, tx_desc): + def broadcast_transaction(self, tx, tx_desc, parent=None): def broadcast_thread(): # non-GUI thread @@ -1217,14 +1219,16 @@ class ElectrumWindow(QMainWindow): if status: if tx_desc is not None and tx.is_complete(): self.wallet.set_label(tx.hash(), tx_desc) - QMessageBox.information(self, '', _('Payment sent.') + '\n' + msg, _('OK')) + QMessageBox.information(parent, '', _('Payment sent.') + '\n' + msg, _('OK')) self.update_invoices_list() self.do_clear() else: - QMessageBox.warning(self, _('Error'), msg, _('OK')) + QMessageBox.warning(parent, _('Error'), msg, _('OK')) self.send_button.setDisabled(False) - self.waiting_dialog = WaitingDialog(self, 'Broadcasting transaction...', broadcast_thread, broadcast_done) + if parent == None: + parent = self + self.waiting_dialog = WaitingDialog(parent, 'Broadcasting transaction...', broadcast_thread, broadcast_done) self.waiting_dialog.start() diff --git a/gui/qt/transaction_dialog.py b/gui/qt/transaction_dialog.py index a58362cdb..d6bd6c668 100644 --- a/gui/qt/transaction_dialog.py +++ b/gui/qt/transaction_dialog.py @@ -116,7 +116,7 @@ class TxDialog(QWidget): self.update() def do_broadcast(self): - self.parent.broadcast_transaction(self.tx, self.desc) + self.parent.broadcast_transaction(self.tx, self.desc, parent=self) self.broadcast = True self.update() @@ -142,10 +142,15 @@ class TxDialog(QWidget): def sign(self): def sign_done(success): + self.sign_button.setDisabled(False) self.prompt_if_unsaved = True self.saved = False self.update() - self.parent.sign_tx(self.tx, sign_done) + self.sign_button.setDisabled(True) + cancelled, ret = self.parent.sign_tx(self.tx, sign_done, parent=self) + if cancelled: + self.sign_button.setDisabled(False) + def save(self): name = 'signed_%s.txn' % (self.tx.hash()[0:8]) if self.tx.is_complete() else 'unsigned.txn'