tweak signal handling
This commit is contained in:
@@ -108,6 +108,10 @@ class App(QtCore.QObject):
|
|||||||
# Emmited when shell command is finished(one command only)
|
# Emmited when shell command is finished(one command only)
|
||||||
shell_command_finished = QtCore.pyqtSignal(object)
|
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)
|
message = QtCore.pyqtSignal(str, str, str)
|
||||||
|
|
||||||
def __init__(self, user_defaults=True, post_gui=None):
|
def __init__(self, user_defaults=True, post_gui=None):
|
||||||
|
|||||||
@@ -48,12 +48,24 @@ class Worker(QtCore.QObject):
|
|||||||
|
|
||||||
# '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:
|
||||||
task['fcn'](*task['params'])
|
# task['fcn'](*task['params'])
|
||||||
return
|
# 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
|
return
|
||||||
|
|
||||||
# FlatCAMApp.App.log.debug("Task ignored.")
|
# FlatCAMApp.App.log.debug("Task ignored.")
|
||||||
|
|||||||
@@ -258,15 +258,17 @@ class TclCommandSignaled(TclCommand):
|
|||||||
it handles all neccessary stuff about blocking and passing exeptions
|
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 for operation is 10 sec, but it can be much more
|
||||||
default_timeout = 300000
|
default_timeout = 10000
|
||||||
|
|
||||||
output = None
|
output = None
|
||||||
|
|
||||||
def execute_call(self, args, unnamed_args):
|
def execute_call(self, args, unnamed_args):
|
||||||
|
|
||||||
self.output = self.execute(args, unnamed_args)
|
try:
|
||||||
self.app.shell_command_finished.emit(self)
|
self.output = self.execute(args, unnamed_args)
|
||||||
|
finally:
|
||||||
|
self.app.shell_command_finished.emit(self)
|
||||||
|
|
||||||
def execute_wrapper(self, *args):
|
def execute_wrapper(self, *args):
|
||||||
"""
|
"""
|
||||||
@@ -279,11 +281,16 @@ class TclCommandSignaled(TclCommand):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
@contextmanager
|
@contextmanager
|
||||||
def wait_signal(signal, timeout=300000):
|
def wait_signal(signal, timeout=10000):
|
||||||
"""Block loop until signal emitted, or timeout (ms) elapses."""
|
"""Block loop until signal emitted, or timeout (ms) elapses."""
|
||||||
loop = QtCore.QEventLoop()
|
loop = QtCore.QEventLoop()
|
||||||
|
|
||||||
|
# Normal termination
|
||||||
signal.connect(loop.quit)
|
signal.connect(loop.quit)
|
||||||
|
|
||||||
|
# Termination by exception in thread
|
||||||
|
self.app.thread_exception.connect(loop.quit)
|
||||||
|
|
||||||
status = {'timed_out': False}
|
status = {'timed_out': False}
|
||||||
|
|
||||||
def report_quit():
|
def report_quit():
|
||||||
@@ -292,18 +299,23 @@ class TclCommandSignaled(TclCommand):
|
|||||||
|
|
||||||
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.raise_tcl_error(str(ex[0]))
|
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
|
# set detail for processing, it will be there until next open or close
|
||||||
self.app.shell.open_proccessing(self.get_current_command())
|
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):
|
with wait_signal(self.app.shell_command_finished, passed_timeout):
|
||||||
# every TclCommandNewObject ancestor support timeout as parameter,
|
# every TclCommandNewObject ancestor support timeout as parameter,
|
||||||
# but it does not mean anything for child itself
|
# but it does not mean anything for child itself
|
||||||
# when operation will be really long is good to set it higher then defqault 30s
|
# 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]})
|
self.app.worker_task.emit({'fcn': self.execute_call, 'params': [args, unnamed_args]})
|
||||||
|
|
||||||
|
return self.output
|
||||||
|
|
||||||
except Exception as unknown:
|
except Exception as unknown:
|
||||||
self.log.error("TCL command '%s' failed." % str(self))
|
self.log.error("TCL command '%s' failed." % str(self))
|
||||||
self.app.raise_tcl_unknown_error(unknown)
|
self.app.raise_tcl_unknown_error(unknown)
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
import sys
|
import sys
|
||||||
import unittest
|
import unittest
|
||||||
from PyQt4 import QtGui
|
from PyQt4 import QtGui
|
||||||
|
from PyQt4.QtCore import QThread
|
||||||
|
|
||||||
from FlatCAMApp import App
|
from FlatCAMApp import App
|
||||||
from FlatCAMObj import FlatCAMGerber, FlatCAMGeometry, FlatCAMCNCjob, FlatCAMExcellon
|
from FlatCAMObj import FlatCAMGerber, FlatCAMGeometry, FlatCAMCNCjob, FlatCAMExcellon
|
||||||
from ObjectUI import GerberObjectUI, GeometryObjectUI
|
from ObjectUI import GerberObjectUI, GeometryObjectUI
|
||||||
@@ -10,6 +12,8 @@ import tempfile
|
|||||||
|
|
||||||
class TclShellTest(unittest.TestCase):
|
class TclShellTest(unittest.TestCase):
|
||||||
|
|
||||||
|
setup = False
|
||||||
|
|
||||||
gerber_files = 'tests/gerber_files'
|
gerber_files = 'tests/gerber_files'
|
||||||
copper_bottom_filename = 'detector_copper_bottom.gbr'
|
copper_bottom_filename = 'detector_copper_bottom.gbr'
|
||||||
copper_top_filename = 'detector_copper_top.gbr'
|
copper_top_filename = 'detector_copper_top.gbr'
|
||||||
@@ -24,20 +28,23 @@ class TclShellTest(unittest.TestCase):
|
|||||||
drill_diameter = 0.8
|
drill_diameter = 0.8
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.app = QtGui.QApplication(sys.argv)
|
|
||||||
|
|
||||||
# Create App, keep app defaults (do not load
|
if not self.setup:
|
||||||
# user-defined defaults).
|
self.setup=True
|
||||||
self.fc = App(user_defaults=False)
|
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
|
pass
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
self.app.closeAllWindows()
|
#self.fc.tcl=None
|
||||||
|
#self.app.closeAllWindows()
|
||||||
del self.fc
|
#del self.fc
|
||||||
del self.app
|
#del self.app
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def test_set_get_units(self):
|
def test_set_get_units(self):
|
||||||
@@ -60,6 +67,7 @@ class TclShellTest(unittest.TestCase):
|
|||||||
|
|
||||||
# open gerber files top, bottom and cutout
|
# open gerber files top, bottom and cutout
|
||||||
|
|
||||||
|
|
||||||
self.fc.exec_command_test('set_sys units MM')
|
self.fc.exec_command_test('set_sys units MM')
|
||||||
self.fc.exec_command_test('new')
|
self.fc.exec_command_test('new')
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user