Complete implementation of blocking mechanism waiting for signal. See #196.

This commit is contained in:
Juan Pablo Caram 2016-03-24 16:06:44 -04:00
parent b0575a1c34
commit a520729444
2 changed files with 36 additions and 13 deletions

View File

@ -11,6 +11,7 @@ import Tkinter
from PyQt4 import QtCore from PyQt4 import QtCore
import time # Just used for debugging. Double check before removing. import time # Just used for debugging. Double check before removing.
from xml.dom.minidom import parseString as parse_xml_string from xml.dom.minidom import parseString as parse_xml_string
from contextlib import contextmanager
######################################## ########################################
## Imports part of FlatCAM ## ## Imports part of FlatCAM ##
@ -106,6 +107,10 @@ class App(QtCore.QObject):
message = QtCore.pyqtSignal(str, str, str) message = QtCore.pyqtSignal(str, str, str)
# Emitted when an unhandled exception happens
# in the worker task.
thread_exception = QtCore.pyqtSignal(object)
def __init__(self, user_defaults=True, post_gui=None): def __init__(self, user_defaults=True, post_gui=None):
""" """
Starts the application. Starts the application.
@ -2138,13 +2143,22 @@ class App(QtCore.QObject):
return a, kwa return a, kwa
from contextlib import contextmanager
@contextmanager @contextmanager
def wait_signal(signal, timeout=10000): def wait_signal(signal, timeout=10000):
"""Block loop until signal emitted, or timeout (ms) elapses.""" """
Block loop until signal emitted, timeout (ms) elapses
or unhandled exception happens in a thread.
:param signal: Signal to wait for.
"""
loop = QtCore.QEventLoop() loop = QtCore.QEventLoop()
# Normal termination
signal.connect(loop.quit) signal.connect(loop.quit)
# Termination by exception in thread
self.thread_exception.connect(loop.quit)
status = {'timed_out': False} status = {'timed_out': False}
def report_quit(): def report_quit():
@ -2153,17 +2167,23 @@ class App(QtCore.QObject):
yield yield
# Temporarily change how exceptions are managed.
oeh = sys.excepthook oeh = sys.excepthook
ex = [] ex = []
def exceptHook(type_, value, traceback):
ex.append(value)
oeh(type_, value, traceback)
sys.excepthook = exceptHook
def except_hook(type_, value, traceback_):
ex.append(value)
oeh(type_, value, traceback_)
sys.excepthook = except_hook
# Terminate on timeout
if timeout is not None: if timeout is not None:
QtCore.QTimer.singleShot(timeout, report_quit) QtCore.QTimer.singleShot(timeout, report_quit)
#### Block ####
loop.exec_() loop.exec_()
# Restore exception management
sys.excepthook = oeh sys.excepthook = oeh
if ex: if ex:
self.raiseTclError(str(ex[0])) self.raiseTclError(str(ex[0]))

View File

@ -14,24 +14,27 @@ class Worker(QtCore.QObject):
self.name = name self.name = name
def run(self): def run(self):
# FlatCAMApp.App.log.debug("Worker Started!")
self.app.log.debug("Worker Started!") self.app.log.debug("Worker Started!")
# Tasks are queued in the event listener. # Tasks are queued in the event listener.
self.app.worker_task.connect(self.do_worker_task) self.app.worker_task.connect(self.do_worker_task)
def do_worker_task(self, task): def do_worker_task(self, task):
# FlatCAMApp.App.log.debug("Running task: %s" % str(task))
self.app.log.debug("Running task: %s" % str(task)) self.app.log.debug("Running task: %s" % str(task))
# 'worker_name' property of task allows to target # 'worker_name' property of task allows to target
# specific worker. # specific worker.
if 'worker_name' in task and task['worker_name'] == self.name: if ('worker_name' in task and task['worker_name'] == self.name) or \
task['fcn'](*task['params']) ('worker_name' not in task and self.name is None):
return
try:
task['fcn'](*task['params'])
except Exception as e:
self.app.thread_exception.emit(e)
raise e
if 'worker_name' not in task and self.name is None:
task['fcn'](*task['params'])
return return
# FlatCAMApp.App.log.debug("Task ignored.") # FlatCAMApp.App.log.debug("Task ignored.")