URI: 
       tAdd TaskThread, use to simplify WaitingDialog - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit c714acf73935eb0063c53c48d7ef614ee95226bd
   DIR parent d9a84875dc1103882722ceb2884cbd964cadd298
  HTML Author: Neil Booth <kyuupichan@gmail.com>
       Date:   Sat, 16 Jan 2016 16:54:51 +0900
       
       Add TaskThread, use to simplify WaitingDialog
       
       This will be useful as a client thread for hardware wallets
       
       Diffstat:
         M gui/qt/main_window.py               |      19 +++++++++++++------
         M gui/qt/util.py                      |      87 +++++++++++++++++++++-----------
       
       2 files changed, 71 insertions(+), 35 deletions(-)
       ---
   DIR diff --git a/gui/qt/main_window.py b/gui/qt/main_window.py
       t@@ -197,6 +197,10 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
                self.show()
                self.raise_()
        
       +    def on_error(self, exc_info):
       +        traceback.print_exception(*exc_info)
       +        self.show_error(str(exc_info[1]))
       +
            def on_network(self, event, *args):
                if event == 'updated':
                    self.need_update.set()
       t@@ -1265,12 +1269,15 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
                # call hook to see if plugin needs gui interaction
                run_hook('sign_tx', parent, tx)
        
       -        def sign_thread():
       -            self.wallet.sign_transaction(tx, password)
       -            return True
       +        def on_signed(result):
       +            callback(True)
       +        def on_failed(exc_info):
       +            self.on_error(exc_info)
       +            callback(False)
        
       -        WaitingDialog(parent, _('Signing transaction...'), sign_thread,
       -                      callback)
       +        task = partial(self.wallet.sign_transaction, tx, password)
       +        WaitingDialog(parent, _('Signing transaction...'), task,
       +                      on_signed, on_failed)
        
            def broadcast_transaction(self, tx, tx_desc, parent):
        
       t@@ -1309,7 +1316,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
                            self.show_error(msg, parent=parent)
        
                WaitingDialog(parent, _('Broadcasting transaction...'),
       -                      broadcast_thread, broadcast_done)
       +                      broadcast_thread, broadcast_done, self.on_error)
        
            def prepare_for_payment_request(self):
                self.tabs.setCurrentIndex(1)
   DIR diff --git a/gui/qt/util.py b/gui/qt/util.py
       t@@ -4,6 +4,8 @@ import traceback
        import sys
        import threading
        import platform
       +import Queue
       +from collections import namedtuple
        from functools import partial
        
        from electrum.i18n import _
       t@@ -211,39 +213,26 @@ class WindowModalDialog(QDialog, MessageBoxMixin):
                if title:
                    self.setWindowTitle(title)
        
       -class WaitingDialog(QThread, MessageBoxMixin):
       +
       +class WaitingDialog(WindowModalDialog):
            '''Shows a please wait dialog whilst runnning a task.  It is not
            necessary to maintain a reference to this dialog.'''
       -    def __init__(self, parent, message, task, on_finished=None):
       -        global dialogs
       -        dialogs.append(self) # Prevent GC
       -        QThread.__init__(self)
       -        self.task = task
       -        self.on_finished = on_finished
       -        self.dialog = WindowModalDialog(parent, _("Please wait"))
       -        vbox = QVBoxLayout(self.dialog)
       +    def __init__(self, parent, message, task, on_success=None, on_error=None):
       +        assert parent
       +        WindowModalDialog.__init__(self, parent, _("Please wait"))
       +        vbox = QVBoxLayout(self)
                vbox.addWidget(QLabel(message))
       -        self.dialog.show()
       -        self.dialog.connect(self, SIGNAL("finished()"), self.finished)
       -        self.start()
       +        self.accepted.connect(self.on_accepted)
       +        self.show()
       +        self.thread = TaskThread(self)
       +        self.thread.add(task, on_success, self.accept, on_error)
       +
       +    def wait(self):
       +        self.thread.wait()
       +
       +    def on_accepted(self):
       +        self.thread.stop()
        
       -    def run(self):
       -        try:
       -            self.result = self.task()
       -            self.error = None
       -        except BaseException as e:
       -            traceback.print_exc(file=sys.stdout)
       -            self.error = str(e)
       -            self.result = None
       -
       -    def finished(self):
       -        global dialogs
       -        dialogs.remove(self)
       -        self.dialog.accept()
       -        if self.error:
       -            self.show_error(self.error, parent=self.dialog.parent())
       -        if self.on_finished:
       -            self.on_finished(self.result)
        
        def line_dialog(parent, title, label, ok_label, default=None):
            dialog = WindowModalDialog(parent, title)
       t@@ -548,6 +537,46 @@ class ButtonsTextEdit(QPlainTextEdit, ButtonsWidget):
                return o
        
        
       +class TaskThread(QThread):
       +    '''Thread that runs background tasks.  Callbacks are guaranteed
       +    to happen in the context of its parent.'''
       +
       +    Task = namedtuple("Task", "task cb_success cb_done cb_error")
       +    doneSig = pyqtSignal(object, object, object)
       +
       +    def __init__(self, parent, on_error=None):
       +        super(TaskThread, self).__init__(parent)
       +        self.on_error = on_error
       +        self.tasks = Queue.Queue()
       +        self.doneSig.connect(self.on_done)
       +        self.start()
       +
       +    def add(self, task, on_success=None, on_done=None, on_error=None):
       +        on_error = on_error or self.on_error
       +        self.tasks.put(TaskThread.Task(task, on_success, on_done, on_error))
       +
       +    def run(self):
       +        while True:
       +            task = self.tasks.get()
       +            if not task:
       +                break
       +            try:
       +                result = task.task()
       +                self.doneSig.emit(result, task.cb_done, task.cb_success)
       +            except BaseException:
       +                self.doneSig.emit(sys.exc_info(), task.cb_done, task.cb_error)
       +
       +    def on_done(self, result, cb_done, cb):
       +        # This runs in the parent's thread.
       +        if cb_done:
       +            cb_done()
       +        if cb:
       +            cb(result)
       +
       +    def stop(self):
       +        self.tasks.put(None)
       +
       +
        if __name__ == "__main__":
            app = QApplication([])
            t = WaitingDialog(None, 'testing ...', lambda: [time.sleep(1)], lambda x: QMessageBox.information(None, 'done', "done", _('OK')))