electrum

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

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:
Mgui/qt/main_window.py | 19+++++++++++++------
Mgui/qt/util.py | 87+++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------------
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')))