merge new pull requests from FlatCAM->master
implement executing of tasks inside worker thread cleanups, reimplement Isolate/New/OpenGerber as OOP style Shell commands disable edit during shell execution, show some progress add ability for breakpoints in other threads and only if available add X11 safe flag, not sure what happen on windows
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import sys
|
||||
import re
|
||||
import FlatCAMApp
|
||||
import abc
|
||||
@@ -41,6 +42,9 @@ class TclCommand(object):
|
||||
'examples': []
|
||||
}
|
||||
|
||||
# original incoming arguments into command
|
||||
original_args = None
|
||||
|
||||
def __init__(self, app):
|
||||
self.app = app
|
||||
if self.app is None:
|
||||
@@ -59,6 +63,18 @@ class TclCommand(object):
|
||||
|
||||
self.app.raise_tcl_error(text)
|
||||
|
||||
def get_current_command(self):
|
||||
"""
|
||||
get current command, we are not able to get it from TCL we have to reconstruct it
|
||||
:return: current command
|
||||
"""
|
||||
command_string = []
|
||||
command_string.append(self.aliases[0])
|
||||
if self.original_args is not None:
|
||||
for arg in self.original_args:
|
||||
command_string.append(arg)
|
||||
return " ".join(command_string)
|
||||
|
||||
def get_decorated_help(self):
|
||||
"""
|
||||
Decorate help for TCL console output.
|
||||
@@ -176,7 +192,7 @@ class TclCommand(object):
|
||||
|
||||
# check options
|
||||
for key in options:
|
||||
if key not in self.option_types:
|
||||
if key not in self.option_types and key is not 'timeout':
|
||||
self.raise_tcl_error('Unknown parameter: %s' % key)
|
||||
try:
|
||||
named_args[key] = self.option_types[key](options[key])
|
||||
@@ -201,8 +217,11 @@ class TclCommand(object):
|
||||
:return: None, output text or exception
|
||||
"""
|
||||
|
||||
#self.worker_task.emit({'fcn': self.exec_command_test, 'params': [text, False]})
|
||||
|
||||
try:
|
||||
self.log.debug("TCL command '%s' executed." % str(self.__class__))
|
||||
self.original_args=args
|
||||
args, unnamed_args = self.check_args(args)
|
||||
return self.execute(args, unnamed_args)
|
||||
except Exception as unknown:
|
||||
@@ -239,9 +258,15 @@ class TclCommandSignaled(TclCommand):
|
||||
it handles all neccessary stuff about blocking and passing exeptions
|
||||
"""
|
||||
|
||||
# default timeout for operation is 30 sec, but it can be much more
|
||||
default_timeout = 30000
|
||||
# default timeout for operation is 300 sec, but it can be much more
|
||||
default_timeout = 300000
|
||||
|
||||
output = None
|
||||
|
||||
def execute_call(self, args, unnamed_args):
|
||||
|
||||
self.output = self.execute(args, unnamed_args)
|
||||
self.app.shell_command_finished.emit(self)
|
||||
|
||||
def execute_wrapper(self, *args):
|
||||
"""
|
||||
@@ -254,7 +279,7 @@ class TclCommandSignaled(TclCommand):
|
||||
"""
|
||||
|
||||
@contextmanager
|
||||
def wait_signal(signal, timeout=30000):
|
||||
def wait_signal(signal, timeout=300000):
|
||||
"""Block loop until signal emitted, or timeout (ms) elapses."""
|
||||
loop = QtCore.QEventLoop()
|
||||
signal.connect(loop.quit)
|
||||
@@ -267,27 +292,43 @@ class TclCommandSignaled(TclCommand):
|
||||
|
||||
yield
|
||||
|
||||
oeh = sys.excepthook
|
||||
ex = []
|
||||
def exceptHook(type_, value, traceback):
|
||||
ex.append(value)
|
||||
oeh(type_, value, traceback)
|
||||
sys.excepthook = exceptHook
|
||||
|
||||
if timeout is not None:
|
||||
QtCore.QTimer.singleShot(timeout, report_quit)
|
||||
|
||||
loop.exec_()
|
||||
|
||||
sys.excepthook = oeh
|
||||
if ex:
|
||||
self.raise_tcl_error(str(ex[0]))
|
||||
|
||||
if status['timed_out']:
|
||||
self.app.raise_tcl_unknown_error('Operation timed out!')
|
||||
|
||||
try:
|
||||
self.log.debug("TCL command '%s' executed." % str(self.__class__))
|
||||
self.original_args=args
|
||||
args, unnamed_args = self.check_args(args)
|
||||
if 'timeout' in args:
|
||||
passed_timeout=args['timeout']
|
||||
del args['timeout']
|
||||
else:
|
||||
passed_timeout=self.default_timeout
|
||||
with wait_signal(self.app.new_object_available, passed_timeout):
|
||||
|
||||
# set detail for processing, it will be there until next open or close
|
||||
self.app.shell.open_proccessing(self.get_current_command())
|
||||
|
||||
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
|
||||
return self.execute(args, unnamed_args)
|
||||
self.app.worker_task.emit({'fcn': self.execute_call, 'params': [args, unnamed_args]})
|
||||
|
||||
except Exception as unknown:
|
||||
self.log.error("TCL command '%s' failed." % str(self))
|
||||
|
||||
@@ -2,7 +2,7 @@ from ObjectCollection import *
|
||||
import TclCommand
|
||||
|
||||
|
||||
class TclCommandAddPolygon(TclCommand.TclCommand):
|
||||
class TclCommandAddPolygon(TclCommand.TclCommandSignaled):
|
||||
"""
|
||||
Tcl shell command to create a polygon in the given Geometry object
|
||||
"""
|
||||
|
||||
@@ -2,7 +2,7 @@ from ObjectCollection import *
|
||||
import TclCommand
|
||||
|
||||
|
||||
class TclCommandAddPolyline(TclCommand.TclCommand):
|
||||
class TclCommandAddPolyline(TclCommand.TclCommandSignaled):
|
||||
"""
|
||||
Tcl shell command to create a polyline in the given Geometry object
|
||||
"""
|
||||
|
||||
@@ -2,7 +2,7 @@ from ObjectCollection import *
|
||||
import TclCommand
|
||||
|
||||
|
||||
class TclCommandCncjob(TclCommand.TclCommand):
|
||||
class TclCommandCncjob(TclCommand.TclCommandSignaled):
|
||||
"""
|
||||
Tcl shell command to Generates a CNC Job from a Geometry Object.
|
||||
|
||||
@@ -70,11 +70,6 @@ class TclCommandCncjob(TclCommand.TclCommand):
|
||||
if 'outname' not in args:
|
||||
args['outname'] = name + "_cnc"
|
||||
|
||||
if 'timeout' in args:
|
||||
timeout = args['timeout']
|
||||
else:
|
||||
timeout = 10000
|
||||
|
||||
obj = self.app.collection.get_by_name(name)
|
||||
if obj is None:
|
||||
self.raise_tcl_error("Object not found: %s" % name)
|
||||
|
||||
@@ -2,7 +2,7 @@ from ObjectCollection import *
|
||||
import TclCommand
|
||||
|
||||
|
||||
class TclCommandExportGcode(TclCommand.TclCommand):
|
||||
class TclCommandExportGcode(TclCommand.TclCommandSignaled):
|
||||
"""
|
||||
Tcl shell command to export gcode as tcl output for "set X [export_gcode ...]"
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ from ObjectCollection import *
|
||||
import TclCommand
|
||||
|
||||
|
||||
class TclCommandExteriors(TclCommand.TclCommand):
|
||||
class TclCommandExteriors(TclCommand.TclCommandSignaled):
|
||||
"""
|
||||
Tcl shell command to get exteriors of polygons
|
||||
"""
|
||||
@@ -57,7 +57,7 @@ class TclCommandExteriors(TclCommand.TclCommand):
|
||||
if not isinstance(obj, Geometry):
|
||||
self.raise_tcl_error('Expected Geometry, got %s %s.' % (name, type(obj)))
|
||||
|
||||
def geo_init(geo_obj):
|
||||
def geo_init(geo_obj, app_obj):
|
||||
geo_obj.solid_geometry = obj_exteriors
|
||||
|
||||
obj_exteriors = obj.get_exteriors()
|
||||
|
||||
@@ -2,7 +2,7 @@ from ObjectCollection import *
|
||||
import TclCommand
|
||||
|
||||
|
||||
class TclCommandInteriors(TclCommand.TclCommand):
|
||||
class TclCommandInteriors(TclCommand.TclCommandSignaled):
|
||||
"""
|
||||
Tcl shell command to get interiors of polygons
|
||||
"""
|
||||
@@ -57,7 +57,7 @@ class TclCommandInteriors(TclCommand.TclCommand):
|
||||
if not isinstance(obj, Geometry):
|
||||
self.raise_tcl_error('Expected Geometry, got %s %s.' % (name, type(obj)))
|
||||
|
||||
def geo_init(geo_obj):
|
||||
def geo_init(geo_obj, app_obj):
|
||||
geo_obj.solid_geometry = obj_exteriors
|
||||
|
||||
obj_exteriors = obj.get_interiors()
|
||||
|
||||
79
tclCommands/TclCommandIsolate.py
Normal file
79
tclCommands/TclCommandIsolate.py
Normal file
@@ -0,0 +1,79 @@
|
||||
from ObjectCollection import *
|
||||
import TclCommand
|
||||
|
||||
|
||||
class TclCommandIsolate(TclCommand.TclCommandSignaled):
|
||||
"""
|
||||
Tcl shell command to Creates isolation routing geometry for the given Gerber.
|
||||
|
||||
example:
|
||||
set_sys units MM
|
||||
new
|
||||
open_gerber tests/gerber_files/simple1.gbr -outname margin
|
||||
isolate margin -dia 3
|
||||
cncjob margin_iso
|
||||
"""
|
||||
|
||||
# array of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon)
|
||||
aliases = ['isolate']
|
||||
|
||||
# dictionary of types from Tcl command, needs to be ordered
|
||||
arg_names = collections.OrderedDict([
|
||||
('name', str)
|
||||
])
|
||||
|
||||
# dictionary of types from Tcl command, needs to be ordered , this is for options like -optionname value
|
||||
option_types = collections.OrderedDict([
|
||||
('dia',float),
|
||||
('passes',int),
|
||||
('overlap',float),
|
||||
('combine',int),
|
||||
('outname',str)
|
||||
])
|
||||
|
||||
# array of mandatory options for current Tcl command: required = {'name','outname'}
|
||||
required = ['name']
|
||||
|
||||
# structured help for current command, args needs to be ordered
|
||||
help = {
|
||||
'main': "Creates isolation routing geometry for the given Gerber.",
|
||||
'args': collections.OrderedDict([
|
||||
('name', 'Name of the source object.'),
|
||||
('dia', 'Tool diameter.'),
|
||||
('passes', 'Passes of tool width.'),
|
||||
('overlap', 'Fraction of tool diameter to overlap passes.'),
|
||||
('combine', 'Combine all passes into one geometry.'),
|
||||
('outname', 'Name of the resulting Geometry object.')
|
||||
]),
|
||||
'examples': []
|
||||
}
|
||||
|
||||
def execute(self, args, unnamed_args):
|
||||
"""
|
||||
execute current TCL shell command
|
||||
|
||||
:param args: array of known named arguments and options
|
||||
:param unnamed_args: array of other values which were passed into command
|
||||
without -somename and we do not have them in known arg_names
|
||||
:return: None or exception
|
||||
"""
|
||||
|
||||
name = args['name']
|
||||
|
||||
if 'outname' not in args:
|
||||
args['outname'] = name + "_iso"
|
||||
|
||||
if 'timeout' in args:
|
||||
timeout = args['timeout']
|
||||
else:
|
||||
timeout = 10000
|
||||
|
||||
obj = self.app.collection.get_by_name(name)
|
||||
if obj is None:
|
||||
self.raise_tcl_error("Object not found: %s" % name)
|
||||
|
||||
if not isinstance(obj, FlatCAMGerber):
|
||||
self.raise_tcl_error('Expected FlatCAMGerber, got %s %s.' % (name, type(obj)))
|
||||
|
||||
del args['name']
|
||||
obj.isolate(**args)
|
||||
40
tclCommands/TclCommandNew.py
Normal file
40
tclCommands/TclCommandNew.py
Normal file
@@ -0,0 +1,40 @@
|
||||
from ObjectCollection import *
|
||||
from PyQt4 import QtCore
|
||||
import TclCommand
|
||||
|
||||
|
||||
class TclCommandNew(TclCommand.TclCommand):
|
||||
"""
|
||||
Tcl shell command to starts a new project. Clears objects from memory
|
||||
"""
|
||||
|
||||
# array of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon)
|
||||
aliases = ['new']
|
||||
|
||||
# dictionary of types from Tcl command, needs to be ordered
|
||||
arg_names = collections.OrderedDict()
|
||||
|
||||
# dictionary of types from Tcl command, needs to be ordered , this is for options like -optionname value
|
||||
option_types = collections.OrderedDict()
|
||||
|
||||
# array of mandatory options for current Tcl command: required = {'name','outname'}
|
||||
required = []
|
||||
|
||||
# structured help for current command, args needs to be ordered
|
||||
help = {
|
||||
'main': "Starts a new project. Clears objects from memory.",
|
||||
'args': collections.OrderedDict(),
|
||||
'examples': []
|
||||
}
|
||||
|
||||
def execute(self, args, unnamed_args):
|
||||
"""
|
||||
execute current TCL shell command
|
||||
|
||||
:param args: array of known named arguments and options
|
||||
:param unnamed_args: array of other values which were passed into command
|
||||
without -somename and we do not have them in known arg_names
|
||||
:return: None or exception
|
||||
"""
|
||||
|
||||
self.app.on_file_new()
|
||||
95
tclCommands/TclCommandOpenGerber.py
Normal file
95
tclCommands/TclCommandOpenGerber.py
Normal file
@@ -0,0 +1,95 @@
|
||||
from ObjectCollection import *
|
||||
import TclCommand
|
||||
|
||||
|
||||
class TclCommandOpenGerber(TclCommand.TclCommandSignaled):
|
||||
"""
|
||||
Tcl shell command to opens a Gerber file
|
||||
"""
|
||||
|
||||
# array of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon)
|
||||
aliases = ['open_gerber']
|
||||
|
||||
# dictionary of types from Tcl command, needs to be ordered
|
||||
arg_names = collections.OrderedDict([
|
||||
('filename', str)
|
||||
])
|
||||
|
||||
# dictionary of types from Tcl command, needs to be ordered , this is for options like -optionname value
|
||||
option_types = collections.OrderedDict([
|
||||
('follow', str),
|
||||
('outname', str)
|
||||
])
|
||||
|
||||
# array of mandatory options for current Tcl command: required = {'name','outname'}
|
||||
required = ['filename']
|
||||
|
||||
# structured help for current command, args needs to be ordered
|
||||
help = {
|
||||
'main': "Opens a Gerber file.",
|
||||
'args': collections.OrderedDict([
|
||||
('filename', 'Path to file to open.'),
|
||||
('follow', 'N If 1, does not create polygons, just follows the gerber path.'),
|
||||
('outname', 'Name of the resulting Geometry object.')
|
||||
]),
|
||||
'examples': []
|
||||
}
|
||||
|
||||
def execute(self, args, unnamed_args):
|
||||
"""
|
||||
execute current TCL shell command
|
||||
|
||||
:param args: array of known named arguments and options
|
||||
:param unnamed_args: array of other values which were passed into command
|
||||
without -somename and we do not have them in known arg_names
|
||||
:return: None or exception
|
||||
"""
|
||||
|
||||
# How the object should be initialized
|
||||
def obj_init(gerber_obj, app_obj):
|
||||
|
||||
if not isinstance(gerber_obj, Geometry):
|
||||
self.raise_tcl_error('Expected FlatCAMGerber, got %s %s.' % (outname, type(gerber_obj)))
|
||||
|
||||
# Opening the file happens here
|
||||
self.app.progress.emit(30)
|
||||
try:
|
||||
gerber_obj.parse_file(filename, follow=follow)
|
||||
|
||||
except IOError:
|
||||
app_obj.inform.emit("[error] Failed to open file: %s " % filename)
|
||||
app_obj.progress.emit(0)
|
||||
self.raise_tcl_error('Failed to open file: %s' % filename)
|
||||
|
||||
except ParseError, e:
|
||||
app_obj.inform.emit("[error] Failed to parse file: %s, %s " % (filename, str(e)))
|
||||
app_obj.progress.emit(0)
|
||||
self.log.error(str(e))
|
||||
raise
|
||||
|
||||
# Further parsing
|
||||
app_obj.progress.emit(70)
|
||||
|
||||
filename = args['filename']
|
||||
|
||||
if 'outname' in args:
|
||||
outname = args['outname']
|
||||
else:
|
||||
outname = filename.split('/')[-1].split('\\')[-1]
|
||||
|
||||
follow = None
|
||||
if 'follow' in args:
|
||||
follow = args['follow']
|
||||
|
||||
with self.app.proc_container.new("Opening Gerber"):
|
||||
|
||||
# Object creation
|
||||
self.app.new_object("gerber", outname, obj_init)
|
||||
|
||||
# Register recent file
|
||||
self.app.file_opened.emit("gerber", filename)
|
||||
|
||||
self.app.progress.emit(100)
|
||||
|
||||
# GUI feedback
|
||||
self.app.inform.emit("Opened: " + filename)
|
||||
@@ -2,12 +2,16 @@ import pkgutil
|
||||
import sys
|
||||
|
||||
# allowed command modules
|
||||
import tclCommands.TclCommandExteriors
|
||||
import tclCommands.TclCommandInteriors
|
||||
import tclCommands.TclCommandAddPolygon
|
||||
import tclCommands.TclCommandAddPolyline
|
||||
import tclCommands.TclCommandExportGcode
|
||||
import tclCommands.TclCommandCncjob
|
||||
import tclCommands.TclCommandExportGcode
|
||||
import tclCommands.TclCommandExteriors
|
||||
import tclCommands.TclCommandInteriors
|
||||
import tclCommands.TclCommandIsolate
|
||||
import tclCommands.TclCommandNew
|
||||
import tclCommands.TclCommandOpenGerber
|
||||
|
||||
|
||||
__all__ = []
|
||||
|
||||
|
||||
Reference in New Issue
Block a user