URI: 
       tTop level window fix for tx_dialog and h/w wallets - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit f92843bb10255fb856f687c2b9062dcf3a031926
   DIR parent 43fd49aa8f93d529cf3ab17ccaa1de0cbc6985ee
  HTML Author: Neil Booth <kyuupichan@gmail.com>
       Date:   Sat, 23 Jan 2016 16:06:32 +0900
       
       Top level window fix for tx_dialog and h/w wallets
       
       Diffstat:
         M gui/qt/main_window.py               |      43 +++++++++++++++++++++----------
         M gui/qt/transaction_dialog.py        |      10 ++++++++--
         M gui/qt/util.py                      |       9 +++++++--
       
       3 files changed, 45 insertions(+), 17 deletions(-)
       ---
   DIR diff --git a/gui/qt/main_window.py b/gui/qt/main_window.py
       t@@ -178,9 +178,24 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
                self.fetch_alias()
                self.require_fee_update = False
                self.tx_notifications = []
       +        self.tl_windows = []
                self.load_wallet(wallet)
                self.connect_slots(gui_object.timer)
        
       +    def push_top_level_window(self, window):
       +        '''Used for e.g. tx dialog box to ensure new dialogs are appropriately
       +        parented.  This used to be done by explicitly providing the parent
       +        window, but that isn't something hardware wallet prompts know.'''
       +        self.tl_windows.append(window)
       +
       +    def pop_top_level_window(self, window):
       +        self.tl_windows.remove(window)
       +
       +    def top_level_window(self):
       +        '''Do the right thing in the presence of tx dialog windows'''
       +        override = self.tl_windows[-1] if self.tl_windows else None
       +        return self.top_level_window_recurse(override)
       +
            def diagnostic_name(self):
                return "%s/%s" % (PrintError.diagnostic_name(self),
                                  self.wallet.basename() if self.wallet else "None")
       t@@ -1141,7 +1156,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
                return value of the wrapped function, or None if cancelled.
                '''
                def request_password(self, *args, **kwargs):
       -            parent = kwargs.get('parent', self.top_level_window())
       +            parent = self.top_level_window()
                    password = None
                    while self.wallet.use_encryption:
                        password = self.password_dialog(parent=parent)
       t@@ -1254,14 +1269,14 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
                            self.show_transaction(tx)
                            self.do_clear()
                        else:
       -                    self.broadcast_transaction(tx, tx_desc, self)
       -        self.sign_tx_with_password(tx, sign_done, password, self)
       +                    self.broadcast_transaction(tx, tx_desc)
       +        self.sign_tx_with_password(tx, sign_done, password)
        
            @protected
       -    def sign_tx(self, tx, callback, password, parent):
       -        self.sign_tx_with_password(tx, callback, password, parent)
       +    def sign_tx(self, tx, callback, password):
       +        self.sign_tx_with_password(tx, callback, password)
        
       -    def sign_tx_with_password(self, tx, callback, password, parent):
       +    def sign_tx_with_password(self, tx, callback, password):
                '''Sign the transaction in a separate thread.  When done, calls
                the callback with a success code of True or False.
                '''
       t@@ -1270,7 +1285,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
                    return
        
                # call hook to see if plugin needs gui interaction
       -        run_hook('sign_tx', parent, tx)
       +        run_hook('sign_tx', self, tx)
        
                def on_signed(result):
                    callback(True)
       t@@ -1279,10 +1294,10 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
                    callback(False)
        
                task = partial(self.wallet.sign_transaction, tx, password)
       -        WaitingDialog(parent, _('Signing transaction...'), task,
       +        WaitingDialog(self, _('Signing transaction...'), task,
                              on_signed, on_failed)
        
       -    def broadcast_transaction(self, tx, tx_desc, parent):
       +    def broadcast_transaction(self, tx, tx_desc):
        
                def broadcast_thread():
                    # non-GUI thread
       t@@ -1304,6 +1319,9 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
                        msg = ack_msg
                    return status, msg
        
       +        # Capture current TL window; override might be removed on return
       +        parent = self.top_level_window()
       +
                def broadcast_done(result):
                    # GUI thread
                    if result:
       t@@ -1311,14 +1329,13 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
                        if status:
                            if tx_desc is not None and tx.is_complete():
                                self.wallet.set_label(tx.hash(), tx_desc)
       -                        self.show_message(_('Payment sent.') + '\n' + msg,
       -                                          parent=parent)
       +                        parent.show_message(_('Payment sent.') + '\n' + msg)
                                self.invoices_list.update()
                                self.do_clear()
                        else:
       -                    self.show_error(msg, parent=parent)
       +                    parent.show_error(msg)
        
       -        WaitingDialog(parent, _('Broadcasting transaction...'),
       +        WaitingDialog(self, _('Broadcasting transaction...'),
                              broadcast_thread, broadcast_done, self.on_error)
        
            def prepare_for_payment_request(self):
   DIR diff --git a/gui/qt/transaction_dialog.py b/gui/qt/transaction_dialog.py
       t@@ -116,7 +116,11 @@ class TxDialog(QDialog, MessageBoxMixin):
                self.update()
        
            def do_broadcast(self):
       -        self.main_window.broadcast_transaction(self.tx, self.desc, self)
       +        self.main_window.push_top_level_window(self)
       +        try:
       +            self.main_window.broadcast_transaction(self.tx, self.desc)
       +        finally:
       +            self.main_window.pop_top_level_window(self)
                self.broadcast = True
                self.update()
        
       t@@ -140,6 +144,7 @@ class TxDialog(QDialog, MessageBoxMixin):
            def sign(self):
                def sign_done(success):
                    self.sign_button.setDisabled(False)
       +            self.main_window.pop_top_level_window(self)
                    if success:
                        self.prompt_if_unsaved = False
                        self.saved = False
       t@@ -148,7 +153,8 @@ class TxDialog(QDialog, MessageBoxMixin):
                self.sign_button.setDisabled(True)
                # Note sign_tx is wrapped and parent= is actually passed
                # to the password input dialog box
       -        self.main_window.sign_tx(self.tx, sign_done, parent=self)
       +        self.main_window.push_top_level_window(self)
       +        self.main_window.sign_tx(self.tx, sign_done)
        
            def save(self):
                name = 'signed_%s.txn' % (self.tx.hash()[0:8]) if self.tx.is_complete() else 'unsigned.txn'
   DIR diff --git a/gui/qt/util.py b/gui/qt/util.py
       t@@ -146,15 +146,18 @@ class CancelButton(QPushButton):
                self.clicked.connect(dialog.reject)
        
        class MessageBoxMixin(object):
       -    def top_level_window(self, window=None):
       +    def top_level_window_recurse(self, window=None):
                window = window or self
                classes = (WindowModalDialog, QMessageBox)
                for n, child in enumerate(window.children()):
                    # Test for visibility as old closed dialogs may not be GC-ed
                    if isinstance(child, classes) and child.isVisible():
       -                return self.top_level_window(child)
       +                return self.top_level_window_recurse(child)
                return window
        
       +    def top_level_window(self):
       +        return self.top_level_window_recurse()
       +
            def question(self, msg, parent=None, title=None, icon=None):
                Yes, No = QMessageBox.Yes, QMessageBox.No
                return self.msg_box(icon or QMessageBox.Question,
       t@@ -200,6 +203,8 @@ class WaitingDialog(WindowModalDialog):
            necessary to maintain a reference to this dialog.'''
            def __init__(self, parent, message, task, on_success=None, on_error=None):
                assert parent
       +        if isinstance(parent, MessageBoxMixin):
       +            parent = parent.top_level_window()
                WindowModalDialog.__init__(self, parent, _("Please wait"))
                vbox = QVBoxLayout(self)
                vbox.addWidget(QLabel(message))