commit c714acf73935eb0063c53c48d7ef614ee95226bd
parent d9a84875dc1103882722ceb2884cbd964cadd298
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:
2 files changed, 71 insertions(+), 35 deletions(-)
diff --git a/gui/qt/main_window.py b/gui/qt/main_window.py
@@ -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()
@@ -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):
@@ -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)
diff --git a/gui/qt/util.py b/gui/qt/util.py
@@ -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 _
@@ -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)
@@ -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')))