tweak signal handling

This commit is contained in:
Kamil Sopko 2016-03-25 00:59:02 +01:00
parent cac2f74be2
commit 2082446ab0
4 changed files with 70 additions and 23 deletions

View File

@ -108,6 +108,10 @@ class App(QtCore.QObject):
# Emmited when shell command is finished(one command only)
shell_command_finished = QtCore.pyqtSignal(object)
# Emitted when an unhandled exception happens
# in the worker task.
thread_exception = QtCore.pyqtSignal(object)
message = QtCore.pyqtSignal(str, str, str)
def __init__(self, user_defaults=True, post_gui=None):

View File

@ -48,12 +48,24 @@ class Worker(QtCore.QObject):
# 'worker_name' property of task allows to target
# specific worker.
if 'worker_name' in task and task['worker_name'] == self.name:
task['fcn'](*task['params'])
return
#if 'worker_name' in task and task['worker_name'] == self.name:
# task['fcn'](*task['params'])
# return
#if 'worker_name' not in task and self.name is None:
# task['fcn'](*task['params'])
# return
if ('worker_name' in task and task['worker_name'] == self.name) or \
('worker_name' not in task and self.name is None):
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
# FlatCAMApp.App.log.debug("Task ignored.")

View File

@ -258,15 +258,17 @@ class TclCommandSignaled(TclCommand):
it handles all neccessary stuff about blocking and passing exeptions
"""
# default timeout for operation is 300 sec, but it can be much more
default_timeout = 300000
# default timeout for operation is 10 sec, but it can be much more
default_timeout = 10000
output = None
def execute_call(self, args, unnamed_args):
self.output = self.execute(args, unnamed_args)
self.app.shell_command_finished.emit(self)
try:
self.output = self.execute(args, unnamed_args)
finally:
self.app.shell_command_finished.emit(self)
def execute_wrapper(self, *args):
"""
@ -279,11 +281,16 @@ class TclCommandSignaled(TclCommand):
"""
@contextmanager
def wait_signal(signal, timeout=300000):
def wait_signal(signal, timeout=10000):
"""Block loop until signal emitted, or timeout (ms) elapses."""
loop = QtCore.QEventLoop()
# Normal termination
signal.connect(loop.quit)
# Termination by exception in thread
self.app.thread_exception.connect(loop.quit)
status = {'timed_out': False}
def report_quit():
@ -292,18 +299,23 @@ class TclCommandSignaled(TclCommand):
yield
# Temporarily change how exceptions are managed.
oeh = sys.excepthook
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:
QtCore.QTimer.singleShot(timeout, report_quit)
# Block
loop.exec_()
# Restore exception management
sys.excepthook = oeh
if ex:
self.raise_tcl_error(str(ex[0]))
@ -324,12 +336,23 @@ class TclCommandSignaled(TclCommand):
# set detail for processing, it will be there until next open or close
self.app.shell.open_proccessing(self.get_current_command())
self.output = None
def handle_finished(obj):
self.app.shell_command_finished.disconnect(handle_finished)
# TODO: handle output
pass
self.app.shell_command_finished.connect(handle_finished)
with wait_signal(self.app.shell_command_finished, passed_timeout):
# every TclCommandNewObject ancestor support timeout as parameter,
# but it does not mean anything for child itself
# when operation will be really long is good to set it higher then defqault 30s
self.app.worker_task.emit({'fcn': self.execute_call, 'params': [args, unnamed_args]})
return self.output
except Exception as unknown:
self.log.error("TCL command '%s' failed." % str(self))
self.app.raise_tcl_unknown_error(unknown)

View File

@ -1,6 +1,8 @@
import sys
import unittest
from PyQt4 import QtGui
from PyQt4.QtCore import QThread
from FlatCAMApp import App
from FlatCAMObj import FlatCAMGerber, FlatCAMGeometry, FlatCAMCNCjob, FlatCAMExcellon
from ObjectUI import GerberObjectUI, GeometryObjectUI
@ -10,6 +12,8 @@ import tempfile
class TclShellTest(unittest.TestCase):
setup = False
gerber_files = 'tests/gerber_files'
copper_bottom_filename = 'detector_copper_bottom.gbr'
copper_top_filename = 'detector_copper_top.gbr'
@ -24,20 +28,23 @@ class TclShellTest(unittest.TestCase):
drill_diameter = 0.8
def setUp(self):
self.app = QtGui.QApplication(sys.argv)
# Create App, keep app defaults (do not load
# user-defined defaults).
self.fc = App(user_defaults=False)
if not self.setup:
self.setup=True
self.app = QtGui.QApplication(sys.argv)
self.fc.shell.show()
# Create App, keep app defaults (do not load
# user-defined defaults).
self.fc = App(user_defaults=False)
self.fc.shell.show()
pass
def tearDown(self):
self.app.closeAllWindows()
del self.fc
del self.app
#self.fc.tcl=None
#self.app.closeAllWindows()
#del self.fc
#del self.app
pass
def test_set_get_units(self):
@ -60,6 +67,7 @@ class TclShellTest(unittest.TestCase):
# open gerber files top, bottom and cutout
self.fc.exec_command_test('set_sys units MM')
self.fc.exec_command_test('new')