cleanups
implement TclCommand.TclCommandSignaled as proof of concept (not usefull) bypass using threads within obj.generatecncjob(use_thread = False, **args) reimplement some more shell commands to OOP style
This commit is contained in:
parent
0f463a1fc2
commit
980638630d
102
FlatCAMApp.py
102
FlatCAMApp.py
@ -651,7 +651,7 @@ class App(QtCore.QObject):
|
||||
|
||||
pass
|
||||
|
||||
def raiseTclUnknownError(self, unknownException):
|
||||
def raise_tcl_unknown_error(self, unknownException):
|
||||
"""
|
||||
raise Exception if is different type than TclErrorException
|
||||
:param unknownException:
|
||||
@ -659,11 +659,11 @@ class App(QtCore.QObject):
|
||||
"""
|
||||
|
||||
if not isinstance(unknownException, self.TclErrorException):
|
||||
self.raiseTclError("Unknown error: %s" % str(unknownException))
|
||||
self.raise_tcl_error("Unknown error: %s" % str(unknownException))
|
||||
else:
|
||||
raise unknownException
|
||||
|
||||
def raiseTclError(self, text):
|
||||
def raise_tcl_error(self, text):
|
||||
"""
|
||||
this method pass exception from python into TCL as error, so we get stacktrace and reason
|
||||
:param text: text of error
|
||||
@ -2274,20 +2274,20 @@ class App(QtCore.QObject):
|
||||
# 8 - 2*left + 2*right +2*top + 2*bottom
|
||||
|
||||
if name is None:
|
||||
self.raiseTclError('Argument name is missing.')
|
||||
self.raise_tcl_error('Argument name is missing.')
|
||||
|
||||
for key in kwa:
|
||||
if key not in types:
|
||||
self.raiseTclError('Unknown parameter: %s' % key)
|
||||
self.raise_tcl_error('Unknown parameter: %s' % key)
|
||||
try:
|
||||
kwa[key] = types[key](kwa[key])
|
||||
except Exception, e:
|
||||
self.raiseTclError("Cannot cast argument '%s' to type %s." % (key, str(types[key])))
|
||||
self.raise_tcl_error("Cannot cast argument '%s' to type %s." % (key, str(types[key])))
|
||||
|
||||
try:
|
||||
obj = self.collection.get_by_name(str(name))
|
||||
except:
|
||||
self.raiseTclError("Could not retrieve object: %s" % name)
|
||||
self.raise_tcl_error("Could not retrieve object: %s" % name)
|
||||
|
||||
# Get min and max data for each object as we just cut rectangles across X or Y
|
||||
xmin, ymin, xmax, ymax = obj.bounds()
|
||||
@ -2337,7 +2337,7 @@ class App(QtCore.QObject):
|
||||
ymax + gapsize)
|
||||
|
||||
except Exception as unknown:
|
||||
self.raiseTclUnknownError(unknown)
|
||||
self.raise_tcl_unknown_error(unknown)
|
||||
|
||||
def mirror(name, *args):
|
||||
a, kwa = h(*args)
|
||||
@ -2624,26 +2624,26 @@ class App(QtCore.QObject):
|
||||
}
|
||||
|
||||
if name is None:
|
||||
self.raiseTclError('Argument name is missing.')
|
||||
self.raise_tcl_error('Argument name is missing.')
|
||||
|
||||
for key in kwa:
|
||||
if key not in types:
|
||||
self.raiseTclError('Unknown parameter: %s' % key)
|
||||
self.raise_tcl_error('Unknown parameter: %s' % key)
|
||||
try:
|
||||
kwa[key] = types[key](kwa[key])
|
||||
except Exception, e:
|
||||
self.raiseTclError("Cannot cast argument '%s' to type %s." % (key, str(types[key])))
|
||||
self.raise_tcl_error("Cannot cast argument '%s' to type %s." % (key, str(types[key])))
|
||||
|
||||
try:
|
||||
obj = self.collection.get_by_name(str(name))
|
||||
except:
|
||||
self.raiseTclError("Could not retrieve object: %s" % name)
|
||||
self.raise_tcl_error("Could not retrieve object: %s" % name)
|
||||
|
||||
if obj is None:
|
||||
self.raiseTclError('Object not found: %s' % name)
|
||||
self.raise_tcl_error('Object not found: %s' % name)
|
||||
|
||||
if not isinstance(obj, FlatCAMExcellon):
|
||||
self.raiseTclError('Only Excellon objects can be drilled, got %s %s.' % (name, type(obj)))
|
||||
self.raise_tcl_error('Only Excellon objects can be drilled, got %s %s.' % (name, type(obj)))
|
||||
|
||||
try:
|
||||
# Get the tools from the list
|
||||
@ -2663,10 +2663,10 @@ class App(QtCore.QObject):
|
||||
obj.app.new_object("cncjob", job_name, job_init)
|
||||
|
||||
except Exception, e:
|
||||
self.raiseTclError("Operation failed: %s" % str(e))
|
||||
self.raise_tcl_error("Operation failed: %s" % str(e))
|
||||
|
||||
except Exception as unknown:
|
||||
self.raiseTclUnknownError(unknown)
|
||||
self.raise_tcl_unknown_error(unknown)
|
||||
|
||||
|
||||
def millholes(name=None, *args):
|
||||
@ -2684,43 +2684,43 @@ class App(QtCore.QObject):
|
||||
'outname': str}
|
||||
|
||||
if name is None:
|
||||
self.raiseTclError('Argument name is missing.')
|
||||
self.raise_tcl_error('Argument name is missing.')
|
||||
|
||||
for key in kwa:
|
||||
if key not in types:
|
||||
self.raiseTclError('Unknown parameter: %s' % key)
|
||||
self.raise_tcl_error('Unknown parameter: %s' % key)
|
||||
try:
|
||||
kwa[key] = types[key](kwa[key])
|
||||
except Exception, e:
|
||||
self.raiseTclError("Cannot cast argument '%s' to type %s." % (key, types[key]))
|
||||
self.raise_tcl_error("Cannot cast argument '%s' to type %s." % (key, types[key]))
|
||||
|
||||
try:
|
||||
if 'tools' in kwa:
|
||||
kwa['tools'] = [x.strip() for x in kwa['tools'].split(",")]
|
||||
except Exception as e:
|
||||
self.raiseTclError("Bad tools: %s" % str(e))
|
||||
self.raise_tcl_error("Bad tools: %s" % str(e))
|
||||
|
||||
try:
|
||||
obj = self.collection.get_by_name(str(name))
|
||||
except:
|
||||
self.raiseTclError("Could not retrieve object: %s" % name)
|
||||
self.raise_tcl_error("Could not retrieve object: %s" % name)
|
||||
|
||||
if obj is None:
|
||||
self.raiseTclError("Object not found: %s" % name)
|
||||
self.raise_tcl_error("Object not found: %s" % name)
|
||||
|
||||
if not isinstance(obj, FlatCAMExcellon):
|
||||
self.raiseTclError('Only Excellon objects can be mill drilled, got %s %s.' % (name, type(obj)))
|
||||
self.raise_tcl_error('Only Excellon objects can be mill drilled, got %s %s.' % (name, type(obj)))
|
||||
|
||||
try:
|
||||
success, msg = obj.generate_milling(**kwa)
|
||||
except Exception as e:
|
||||
self.raiseTclError("Operation failed: %s" % str(e))
|
||||
self.raise_tcl_error("Operation failed: %s" % str(e))
|
||||
|
||||
if not success:
|
||||
self.raiseTclError(msg)
|
||||
self.raise_tcl_error(msg)
|
||||
|
||||
except Exception as unknown:
|
||||
self.raiseTclUnknownError(unknown)
|
||||
self.raise_tcl_unknown_error(unknown)
|
||||
|
||||
def exteriors(name=None, *args):
|
||||
'''
|
||||
@ -2735,26 +2735,26 @@ class App(QtCore.QObject):
|
||||
types = {'outname': str}
|
||||
|
||||
if name is None:
|
||||
self.raiseTclError('Argument name is missing.')
|
||||
self.raise_tcl_error('Argument name is missing.')
|
||||
|
||||
for key in kwa:
|
||||
if key not in types:
|
||||
self.raiseTclError('Unknown parameter: %s' % key)
|
||||
self.raise_tcl_error('Unknown parameter: %s' % key)
|
||||
try:
|
||||
kwa[key] = types[key](kwa[key])
|
||||
except Exception, e:
|
||||
self.raiseTclError("Cannot cast argument '%s' to type %s." % (key, types[key]))
|
||||
self.raise_tcl_error("Cannot cast argument '%s' to type %s." % (key, types[key]))
|
||||
|
||||
try:
|
||||
obj = self.collection.get_by_name(str(name))
|
||||
except:
|
||||
self.raiseTclError("Could not retrieve object: %s" % name)
|
||||
self.raise_tcl_error("Could not retrieve object: %s" % name)
|
||||
|
||||
if obj is None:
|
||||
self.raiseTclError("Object not found: %s" % name)
|
||||
self.raise_tcl_error("Object not found: %s" % name)
|
||||
|
||||
if not isinstance(obj, Geometry):
|
||||
self.raiseTclError('Expected Geometry, got %s %s.' % (name, type(obj)))
|
||||
self.raise_tcl_error('Expected Geometry, got %s %s.' % (name, type(obj)))
|
||||
|
||||
def geo_init(geo_obj, app_obj):
|
||||
geo_obj.solid_geometry = obj_exteriors
|
||||
@ -2768,10 +2768,10 @@ class App(QtCore.QObject):
|
||||
obj_exteriors = obj.get_exteriors()
|
||||
self.new_object('geometry', outname, geo_init)
|
||||
except Exception as e:
|
||||
self.raiseTclError("Failed: %s" % str(e))
|
||||
self.raise_tcl_error("Failed: %s" % str(e))
|
||||
|
||||
except Exception as unknown:
|
||||
self.raiseTclUnknownError(unknown)
|
||||
self.raise_tcl_unknown_error(unknown)
|
||||
|
||||
def interiors(name=None, *args):
|
||||
'''
|
||||
@ -2787,25 +2787,25 @@ class App(QtCore.QObject):
|
||||
|
||||
for key in kwa:
|
||||
if key not in types:
|
||||
self.raiseTclError('Unknown parameter: %s' % key)
|
||||
self.raise_tcl_error('Unknown parameter: %s' % key)
|
||||
try:
|
||||
kwa[key] = types[key](kwa[key])
|
||||
except Exception, e:
|
||||
self.raiseTclError("Cannot cast argument '%s' to type %s." % (key, types[key]))
|
||||
self.raise_tcl_error("Cannot cast argument '%s' to type %s." % (key, types[key]))
|
||||
|
||||
if name is None:
|
||||
self.raiseTclError('Argument name is missing.')
|
||||
self.raise_tcl_error('Argument name is missing.')
|
||||
|
||||
try:
|
||||
obj = self.collection.get_by_name(str(name))
|
||||
except:
|
||||
self.raiseTclError("Could not retrieve object: %s" % name)
|
||||
self.raise_tcl_error("Could not retrieve object: %s" % name)
|
||||
|
||||
if obj is None:
|
||||
self.raiseTclError("Object not found: %s" % name)
|
||||
self.raise_tcl_error("Object not found: %s" % name)
|
||||
|
||||
if not isinstance(obj, Geometry):
|
||||
self.raiseTclError('Expected Geometry, got %s %s.' % (name, type(obj)))
|
||||
self.raise_tcl_error('Expected Geometry, got %s %s.' % (name, type(obj)))
|
||||
|
||||
def geo_init(geo_obj, app_obj):
|
||||
geo_obj.solid_geometry = obj_interiors
|
||||
@ -2819,10 +2819,10 @@ class App(QtCore.QObject):
|
||||
obj_interiors = obj.get_interiors()
|
||||
self.new_object('geometry', outname, geo_init)
|
||||
except Exception as e:
|
||||
self.raiseTclError("Failed: %s" % str(e))
|
||||
self.raise_tcl_error("Failed: %s" % str(e))
|
||||
|
||||
except Exception as unknown:
|
||||
self.raiseTclUnknownError(unknown)
|
||||
self.raise_tcl_unknown_error(unknown)
|
||||
|
||||
def isolate(name=None, *args):
|
||||
'''
|
||||
@ -2840,29 +2840,29 @@ class App(QtCore.QObject):
|
||||
|
||||
for key in kwa:
|
||||
if key not in types:
|
||||
self.raiseTclError('Unknown parameter: %s' % key)
|
||||
self.raise_tcl_error('Unknown parameter: %s' % key)
|
||||
try:
|
||||
kwa[key] = types[key](kwa[key])
|
||||
except Exception, e:
|
||||
self.raiseTclError("Cannot cast argument '%s' to type %s." % (key, types[key]))
|
||||
self.raise_tcl_error("Cannot cast argument '%s' to type %s." % (key, types[key]))
|
||||
try:
|
||||
obj = self.collection.get_by_name(str(name))
|
||||
except:
|
||||
self.raiseTclError("Could not retrieve object: %s" % name)
|
||||
self.raise_tcl_error("Could not retrieve object: %s" % name)
|
||||
|
||||
if obj is None:
|
||||
self.raiseTclError("Object not found: %s" % name)
|
||||
self.raise_tcl_error("Object not found: %s" % name)
|
||||
|
||||
assert isinstance(obj, FlatCAMGerber), \
|
||||
"Expected a FlatCAMGerber, got %s" % type(obj)
|
||||
|
||||
if not isinstance(obj, FlatCAMGerber):
|
||||
self.raiseTclError('Expected FlatCAMGerber, got %s %s.' % (name, type(obj)))
|
||||
self.raise_tcl_error('Expected FlatCAMGerber, got %s %s.' % (name, type(obj)))
|
||||
|
||||
try:
|
||||
obj.isolate(**kwa)
|
||||
except Exception, e:
|
||||
self.raiseTclError("Operation failed: %s" % str(e))
|
||||
self.raise_tcl_error("Operation failed: %s" % str(e))
|
||||
|
||||
return 'Ok'
|
||||
|
||||
@ -3253,11 +3253,11 @@ class App(QtCore.QObject):
|
||||
Test it like this:
|
||||
if name is None:
|
||||
|
||||
self.raiseTclError('Argument name is missing.')
|
||||
self.raise_tcl_error('Argument name is missing.')
|
||||
|
||||
When error ocurre, always use raiseTclError, never return "sometext" on error,
|
||||
When error ocurre, always use raise_tcl_error, never return "sometext" on error,
|
||||
otherwise we will miss it and processing will silently continue.
|
||||
Method raiseTclError pass error into TCL interpreter, then raise python exception,
|
||||
Method raise_tcl_error pass error into TCL interpreter, then raise python exception,
|
||||
which is catched in exec_command and displayed in TCL shell console with red background.
|
||||
Error in console is displayed with TCL trace.
|
||||
|
||||
|
@ -1040,6 +1040,10 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob):
|
||||
|
||||
self.app.inform.emit("Saved to: " + filename)
|
||||
|
||||
def get_gcode(self, preamble='', postamble=''):
|
||||
#we need this to beable get_gcode separatelly for shell command export_code
|
||||
return preamble + '\n' + self.gcode + "\n" + postamble
|
||||
|
||||
def on_plot_cb_click(self, *args):
|
||||
if self.muted_ui:
|
||||
return
|
||||
@ -1243,7 +1247,8 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
|
||||
outname=None,
|
||||
spindlespeed=None,
|
||||
multidepth=None,
|
||||
depthperpass=None):
|
||||
depthperpass=None,
|
||||
use_thread=True):
|
||||
"""
|
||||
Creates a CNCJob out of this Geometry object. The actual
|
||||
work is done by the target FlatCAMCNCjob object's
|
||||
@ -1304,18 +1309,22 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
|
||||
|
||||
app_obj.progress.emit(80)
|
||||
|
||||
# To be run in separate thread
|
||||
def job_thread(app_obj):
|
||||
with self.app.proc_container.new("Generating CNC Job."):
|
||||
app_obj.new_object("cncjob", outname, job_init)
|
||||
app_obj.inform.emit("CNCjob created: %s" % outname)
|
||||
app_obj.progress.emit(100)
|
||||
|
||||
# Create a promise with the name
|
||||
self.app.collection.promise(outname)
|
||||
if use_thread:
|
||||
# To be run in separate thread
|
||||
def job_thread(app_obj):
|
||||
with self.app.proc_container.new("Generating CNC Job."):
|
||||
app_obj.new_object("cncjob", outname, job_init)
|
||||
app_obj.inform.emit("CNCjob created: %s" % outname)
|
||||
app_obj.progress.emit(100)
|
||||
|
||||
# Send to worker
|
||||
self.app.worker_task.emit({'fcn': job_thread, 'params': [self.app]})
|
||||
# Create a promise with the name
|
||||
self.app.collection.promise(outname)
|
||||
|
||||
# Send to worker
|
||||
self.app.worker_task.emit({'fcn': job_thread, 'params': [self.app]})
|
||||
else:
|
||||
self.app.new_object("cncjob", outname, job_init)
|
||||
|
||||
def on_plot_cb_click(self, *args): # TODO: args not needed
|
||||
if self.muted_ui:
|
||||
|
@ -1,83 +1,108 @@
|
||||
import sys, inspect, pkgutil
|
||||
import re
|
||||
import FlatCAMApp
|
||||
import abc
|
||||
import collections
|
||||
from PyQt4 import QtCore
|
||||
from contextlib import contextmanager
|
||||
|
||||
|
||||
class TclCommand(object):
|
||||
|
||||
app=None
|
||||
# FlatCAMApp
|
||||
app = None
|
||||
|
||||
# logger
|
||||
log = None
|
||||
|
||||
# array of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon)
|
||||
aliases = []
|
||||
|
||||
# dictionary of types from Tcl command, needs to be ordered
|
||||
# OrderedDict should be like collections.OrderedDict([(key,value),(key2,value2)])
|
||||
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([])
|
||||
# OrderedDict should be like collections.OrderedDict([(key,value),(key2,value2)])
|
||||
option_types = collections.OrderedDict()
|
||||
|
||||
# array of mandatory options for current Tcl command: required = {'name','outname'}
|
||||
required = ['name']
|
||||
|
||||
# structured help for current command, args needs to be ordered
|
||||
# OrderedDict should be like collections.OrderedDict([(key,value),(key2,value2)])
|
||||
help = {
|
||||
'main': "undefined help.",
|
||||
'args': collections.OrderedDict([
|
||||
('argumentname', 'undefined help.'),
|
||||
('optionname', 'undefined help.')
|
||||
]),
|
||||
'examples' : []
|
||||
'examples': []
|
||||
}
|
||||
|
||||
def __init__(self, app):
|
||||
self.app=app
|
||||
self.app = app
|
||||
if self.app is None:
|
||||
raise TypeError('Expected app to be FlatCAMApp instance.')
|
||||
if not isinstance(self.app, FlatCAMApp.App):
|
||||
raise TypeError('Expected FlatCAMApp, got %s.' % type(app))
|
||||
self.log = self.app.log
|
||||
|
||||
def raise_tcl_error(self, text):
|
||||
"""
|
||||
this method pass exception from python into TCL as error, so we get stacktrace and reason
|
||||
this is only redirect to self.app.raise_tcl_error
|
||||
:param text: text of error
|
||||
:return: none
|
||||
"""
|
||||
|
||||
self.app.raise_tcl_error(text)
|
||||
|
||||
def get_decorated_help(self):
|
||||
"""
|
||||
Decorate help for TCL console output.
|
||||
|
||||
:return: decorated help from structue
|
||||
:return: decorated help from structure
|
||||
"""
|
||||
|
||||
def get_decorated_command(alias):
|
||||
def get_decorated_command(alias_name):
|
||||
command_string = []
|
||||
for key, value in self.help['args'].items():
|
||||
command_string.append(get_decorated_argument(key, value, True))
|
||||
return "> " + alias + " " + " ".join(command_string)
|
||||
for arg_key, arg_type in self.help['args'].items():
|
||||
command_string.append(get_decorated_argument(arg_key, arg_type, True))
|
||||
return "> " + alias_name + " " + " ".join(command_string)
|
||||
|
||||
def get_decorated_argument(key, value, in_command=False):
|
||||
def get_decorated_argument(help_key, help_text, in_command=False):
|
||||
option_symbol = ''
|
||||
if key in self.arg_names:
|
||||
type=self.arg_names[key]
|
||||
type_name=str(type.__name__)
|
||||
if help_key in self.arg_names:
|
||||
arg_type = self.arg_names[help_key]
|
||||
type_name = str(arg_type.__name__)
|
||||
in_command_name = "<" + type_name + ">"
|
||||
elif key in self.option_types:
|
||||
elif help_key in self.option_types:
|
||||
option_symbol = '-'
|
||||
type=self.option_types[key]
|
||||
type_name=str(type.__name__)
|
||||
in_command_name = option_symbol + key + " <" + type_name + ">"
|
||||
arg_type = self.option_types[help_key]
|
||||
type_name = str(arg_type.__name__)
|
||||
in_command_name = option_symbol + help_key + " <" + type_name + ">"
|
||||
else:
|
||||
option_symbol = ''
|
||||
type_name='?'
|
||||
in_command_name = option_symbol + key + " <" + type_name + ">"
|
||||
type_name = '?'
|
||||
in_command_name = option_symbol + help_key + " <" + type_name + ">"
|
||||
|
||||
if in_command:
|
||||
if key in self.required:
|
||||
if help_key in self.required:
|
||||
return in_command_name
|
||||
else:
|
||||
return '[' + in_command_name + "]"
|
||||
else:
|
||||
if key in self.required:
|
||||
return "\t" + option_symbol + key + " <" + type_name + ">: " + value
|
||||
if help_key in self.required:
|
||||
return "\t" + option_symbol + help_key + " <" + type_name + ">: " + help_text
|
||||
else:
|
||||
return "\t[" + option_symbol + key + " <" + type_name + ">: " + value+"]"
|
||||
return "\t[" + option_symbol + help_key + " <" + type_name + ">: " + help_text + "]"
|
||||
|
||||
def get_decorated_example(example):
|
||||
return "> "+example
|
||||
def get_decorated_example(example_item):
|
||||
return "> "+example_item
|
||||
|
||||
help_string=[self.help['main']]
|
||||
help_string = [self.help['main']]
|
||||
for alias in self.aliases:
|
||||
help_string.append(get_decorated_command(alias))
|
||||
|
||||
@ -89,12 +114,17 @@ class TclCommand(object):
|
||||
|
||||
return "\n".join(help_string)
|
||||
|
||||
def parse_arguments(self, args):
|
||||
@staticmethod
|
||||
def parse_arguments(args):
|
||||
"""
|
||||
Pre-processes arguments to detect '-keyword value' pairs into dictionary
|
||||
and standalone parameters into list.
|
||||
|
||||
This is copy from FlatCAMApp.setup_shell().h() just for accesibility, original should be removed after all commands will be converted
|
||||
This is copy from FlatCAMApp.setup_shell().h() just for accessibility,
|
||||
original should be removed after all commands will be converted
|
||||
|
||||
:param args: arguments from tcl to parse
|
||||
:return: arguments, options
|
||||
"""
|
||||
|
||||
options = {}
|
||||
@ -121,41 +151,43 @@ class TclCommand(object):
|
||||
Check arguments and options for right types
|
||||
|
||||
:param args: arguments from tcl to check
|
||||
:return:
|
||||
:return: named_args, unnamed_args
|
||||
"""
|
||||
|
||||
arguments, options = self.parse_arguments(args)
|
||||
|
||||
named_args={}
|
||||
unnamed_args=[]
|
||||
named_args = {}
|
||||
unnamed_args = []
|
||||
|
||||
# check arguments
|
||||
idx=0
|
||||
arg_names_items=self.arg_names.items()
|
||||
idx = 0
|
||||
arg_names_items = self.arg_names.items()
|
||||
for argument in arguments:
|
||||
if len(self.arg_names) > idx:
|
||||
key, type = arg_names_items[idx]
|
||||
key, arg_type = arg_names_items[idx]
|
||||
try:
|
||||
named_args[key] = type(argument)
|
||||
named_args[key] = arg_type(argument)
|
||||
except Exception, e:
|
||||
self.app.raiseTclError("Cannot cast named argument '%s' to type %s." % (key, type))
|
||||
self.raise_tcl_error("Cannot cast named argument '%s' to type %s with exception '%s'."
|
||||
% (key, arg_type, str(e)))
|
||||
else:
|
||||
unnamed_args.append(argument)
|
||||
idx += 1
|
||||
|
||||
# check otions
|
||||
# check options
|
||||
for key in options:
|
||||
if key not in self.option_types:
|
||||
self.app.raiseTclError('Unknown parameter: %s' % key)
|
||||
self.raise_tcl_error('Unknown parameter: %s' % key)
|
||||
try:
|
||||
named_args[key] = self.option_types[key](options[key])
|
||||
except Exception, e:
|
||||
self.app.raiseTclError("Cannot cast argument '-%s' to type %s." % (key, self.option_types[key]))
|
||||
self.raise_tcl_error("Cannot cast argument '-%s' to type '%s' with exception '%s'."
|
||||
% (key, self.option_types[key], str(e)))
|
||||
|
||||
# check required arguments
|
||||
for key in self.required:
|
||||
if key not in named_args:
|
||||
self.app.raiseTclError("Missing required argument '%s'." % (key))
|
||||
self.raise_tcl_error("Missing required argument '%s'." % key)
|
||||
|
||||
return named_args, unnamed_args
|
||||
|
||||
@ -168,12 +200,16 @@ class TclCommand(object):
|
||||
:param args: arguments passed from tcl command console
|
||||
:return: None, output text or exception
|
||||
"""
|
||||
|
||||
try:
|
||||
self.log.debug("TCL command '%s' executed." % str(self.__class__))
|
||||
args, unnamed_args = self.check_args(args)
|
||||
return self.execute(args, unnamed_args)
|
||||
except Exception as unknown:
|
||||
self.app.raiseTclUnknownError(unknown)
|
||||
self.log.error("TCL command '%s' failed." % str(self))
|
||||
self.app.raise_tcl_unknown_error(unknown)
|
||||
|
||||
@abc.abstractmethod
|
||||
def execute(self, args, unnamed_args):
|
||||
"""
|
||||
Direct execute of command, this method should be implemented in each descendant.
|
||||
@ -186,3 +222,73 @@ class TclCommand(object):
|
||||
"""
|
||||
|
||||
raise NotImplementedError("Please Implement this method")
|
||||
|
||||
|
||||
class TclCommandSignaled(TclCommand):
|
||||
"""
|
||||
!!! I left it here only for demonstration !!!
|
||||
Go to TclCommandCncjob and into class definition put
|
||||
class TclCommandCncjob(TclCommand.TclCommandSignaled):
|
||||
also change
|
||||
obj.generatecncjob(use_thread = False, **args)
|
||||
to
|
||||
obj.generatecncjob(use_thread = True, **args)
|
||||
|
||||
|
||||
This class is child of TclCommand and is used for commands which create new objects
|
||||
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
|
||||
|
||||
|
||||
def execute_wrapper(self, *args):
|
||||
"""
|
||||
Command which is called by tcl console when current commands aliases are hit.
|
||||
Main catch(except) is implemented here.
|
||||
This method should be reimplemented only when initial checking sequence differs
|
||||
|
||||
:param args: arguments passed from tcl command console
|
||||
:return: None, output text or exception
|
||||
"""
|
||||
|
||||
@contextmanager
|
||||
def wait_signal(signal, timeout=30000):
|
||||
"""Block loop until signal emitted, or timeout (ms) elapses."""
|
||||
loop = QtCore.QEventLoop()
|
||||
signal.connect(loop.quit)
|
||||
|
||||
status = {'timed_out': False}
|
||||
|
||||
def report_quit():
|
||||
status['timed_out'] = True
|
||||
loop.quit()
|
||||
|
||||
yield
|
||||
|
||||
if timeout is not None:
|
||||
QtCore.QTimer.singleShot(timeout, report_quit)
|
||||
|
||||
loop.exec_()
|
||||
|
||||
if status['timed_out']:
|
||||
self.app.raise_tcl_unknown_error('Operation timed out!')
|
||||
|
||||
try:
|
||||
self.log.debug("TCL command '%s' executed." % str(self.__class__))
|
||||
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):
|
||||
# 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)
|
||||
|
||||
except Exception as unknown:
|
||||
self.log.error("TCL command '%s' failed." % str(self))
|
||||
self.app.raise_tcl_unknown_error(unknown)
|
@ -8,7 +8,7 @@ class TclCommandAddPolygon(TclCommand.TclCommand):
|
||||
"""
|
||||
|
||||
# array of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon)
|
||||
aliases = ['add_polygon','add_poly']
|
||||
aliases = ['add_polygon', 'add_poly']
|
||||
|
||||
# dictionary of types from Tcl command, needs to be ordered
|
||||
arg_names = collections.OrderedDict([
|
||||
@ -16,7 +16,7 @@ class TclCommandAddPolygon(TclCommand.TclCommand):
|
||||
])
|
||||
|
||||
# dictionary of types from Tcl command, needs to be ordered , this is for options like -optionname value
|
||||
option_types = collections.OrderedDict([])
|
||||
option_types = collections.OrderedDict()
|
||||
|
||||
# array of mandatory options for current Tcl command: required = {'name','outname'}
|
||||
required = ['name']
|
||||
@ -28,7 +28,7 @@ class TclCommandAddPolygon(TclCommand.TclCommand):
|
||||
('name', 'Name of the Geometry object to which to append the polygon.'),
|
||||
('xi, yi', 'Coordinates of points in the polygon.')
|
||||
]),
|
||||
'examples':[
|
||||
'examples': [
|
||||
'add_polygon <name> <x0> <y0> <x1> <y1> <x2> <y2> [x3 y3 [...]]'
|
||||
]
|
||||
}
|
||||
@ -45,21 +45,17 @@ class TclCommandAddPolygon(TclCommand.TclCommand):
|
||||
|
||||
name = args['name']
|
||||
|
||||
try:
|
||||
obj = self.app.collection.get_by_name(name)
|
||||
except:
|
||||
self.app.raiseTclError("Could not retrieve object: %s" % name)
|
||||
|
||||
obj = self.app.collection.get_by_name(name)
|
||||
if obj is None:
|
||||
self.app.raiseTclError("Object not found: %s" % name)
|
||||
self.raise_tcl_error("Object not found: %s" % name)
|
||||
|
||||
if not isinstance(obj, Geometry):
|
||||
self.app.raiseTclError('Expected Geometry, got %s %s.' % (name, type(obj)))
|
||||
self.raise_tcl_error('Expected Geometry, got %s %s.' % (name, type(obj)))
|
||||
|
||||
if len(unnamed_args) % 2 != 0:
|
||||
self.app.raiseTclError("Incomplete coordinates.")
|
||||
self.raise_tcl_error("Incomplete coordinates.")
|
||||
|
||||
points = [[float(unnamed_args[2*i]), float(unnamed_args[2*i+1])] for i in range(len(unnamed_args)/2)]
|
||||
|
||||
obj.add_polygon(points)
|
||||
obj.plot()
|
||||
obj.plot()
|
||||
|
@ -16,7 +16,7 @@ class TclCommandAddPolyline(TclCommand.TclCommand):
|
||||
])
|
||||
|
||||
# dictionary of types from Tcl command, needs to be ordered , this is for options like -optionname value
|
||||
option_types = collections.OrderedDict([])
|
||||
option_types = collections.OrderedDict()
|
||||
|
||||
# array of mandatory options for current Tcl command: required = {'name','outname'}
|
||||
required = ['name']
|
||||
@ -28,7 +28,7 @@ class TclCommandAddPolyline(TclCommand.TclCommand):
|
||||
('name', 'Name of the Geometry object to which to append the polyline.'),
|
||||
('xi, yi', 'Coordinates of points in the polyline.')
|
||||
]),
|
||||
'examples':[
|
||||
'examples': [
|
||||
'add_polyline <name> <x0> <y0> <x1> <y1> <x2> <y2> [x3 y3 [...]]'
|
||||
]
|
||||
}
|
||||
@ -45,21 +45,17 @@ class TclCommandAddPolyline(TclCommand.TclCommand):
|
||||
|
||||
name = args['name']
|
||||
|
||||
try:
|
||||
obj = self.app.collection.get_by_name(name)
|
||||
except:
|
||||
self.app.raiseTclError("Could not retrieve object: %s" % name)
|
||||
|
||||
obj = self.app.collection.get_by_name(name)
|
||||
if obj is None:
|
||||
self.app.raiseTclError("Object not found: %s" % name)
|
||||
self.raise_tcl_error("Object not found: %s" % name)
|
||||
|
||||
if not isinstance(obj, Geometry):
|
||||
self.app.raiseTclError('Expected Geometry, got %s %s.' % (name, type(obj)))
|
||||
self.raise_tcl_error('Expected Geometry, got %s %s.' % (name, type(obj)))
|
||||
|
||||
if len(unnamed_args) % 2 != 0:
|
||||
self.app.raiseTclError("Incomplete coordinates.")
|
||||
self.raise_tcl_error("Incomplete coordinates.")
|
||||
|
||||
points = [[float(unnamed_args[2*i]), float(unnamed_args[2*i+1])] for i in range(len(unnamed_args)/2)]
|
||||
|
||||
obj.add_polyline(points)
|
||||
obj.plot()
|
||||
obj.plot()
|
||||
|
86
tclCommands/TclCommandCncjob.py
Normal file
86
tclCommands/TclCommandCncjob.py
Normal file
@ -0,0 +1,86 @@
|
||||
from ObjectCollection import *
|
||||
import TclCommand
|
||||
|
||||
|
||||
class TclCommandCncjob(TclCommand.TclCommand):
|
||||
"""
|
||||
Tcl shell command to Generates a CNC Job from a Geometry Object.
|
||||
|
||||
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 = ['cncjob']
|
||||
|
||||
# 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([
|
||||
('z_cut',float),
|
||||
('z_move',float),
|
||||
('feedrate',float),
|
||||
('tooldia',float),
|
||||
('spindlespeed',int),
|
||||
('multidepth',bool),
|
||||
('depthperpass',float),
|
||||
('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': "Generates a CNC Job from a Geometry Object.",
|
||||
'args': collections.OrderedDict([
|
||||
('name', 'Name of the source object.'),
|
||||
('z_cut', 'Z-axis cutting position.'),
|
||||
('z_move', 'Z-axis moving position.'),
|
||||
('feedrate', 'Moving speed when cutting.'),
|
||||
('tooldia', 'Tool diameter to show on screen.'),
|
||||
('spindlespeed', 'Speed of the spindle in rpm (example: 4000).'),
|
||||
('multidepth', 'Use or not multidepth cnccut.'),
|
||||
('depthperpass', 'Height of one layer for multidepth.'),
|
||||
('outname', 'Name of the resulting Geometry object.'),
|
||||
('timeout', 'Max wait for job timeout before error.')
|
||||
]),
|
||||
'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 + "_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)
|
||||
|
||||
if not isinstance(obj, FlatCAMGeometry):
|
||||
self.raise_tcl_error('Expected FlatCAMGeometry, got %s %s.' % (name, type(obj)))
|
||||
|
||||
del args['name']
|
||||
obj.generatecncjob(use_thread = False, **args)
|
79
tclCommands/TclCommandExportGcode.py
Normal file
79
tclCommands/TclCommandExportGcode.py
Normal file
@ -0,0 +1,79 @@
|
||||
from ObjectCollection import *
|
||||
import TclCommand
|
||||
|
||||
|
||||
class TclCommandExportGcode(TclCommand.TclCommand):
|
||||
"""
|
||||
Tcl shell command to export gcode as tcl output for "set X [export_gcode ...]"
|
||||
|
||||
Requires name to be available. It might still be in the
|
||||
making at the time this function is called, so check for
|
||||
promises and send to background if there are promises.
|
||||
|
||||
|
||||
this export may be catched by tcl and past as preable to another export_gcode or write_gcode
|
||||
this can be used to join GCODES
|
||||
|
||||
example:
|
||||
set_sys units MM
|
||||
new
|
||||
open_gerber tests/gerber_files/simple1.gbr -outname margin
|
||||
isolate margin -dia 3
|
||||
cncjob margin_iso
|
||||
cncjob margin_iso
|
||||
set EXPORT [export_gcode margin_iso_cnc]
|
||||
write_gcode margin_iso_cnc_1 /tmp/file.gcode ${EXPORT}
|
||||
|
||||
"""
|
||||
|
||||
# array of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon)
|
||||
aliases = ['export_gcode']
|
||||
|
||||
# dictionary of types from Tcl command, needs to be ordered
|
||||
arg_names = collections.OrderedDict([
|
||||
('name', str),
|
||||
('preamble', str),
|
||||
('postamble', str)
|
||||
])
|
||||
|
||||
# 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 = ['name']
|
||||
|
||||
# structured help for current command, args needs to be ordered
|
||||
help = {
|
||||
'main': "Export gcode into console output.",
|
||||
'args': collections.OrderedDict([
|
||||
('name', 'Name of the source Geometry object.'),
|
||||
('preamble', 'Prepend GCODE.'),
|
||||
('postamble', 'Append GCODE.')
|
||||
]),
|
||||
'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']
|
||||
|
||||
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, CNCjob):
|
||||
self.raise_tcl_error('Expected CNCjob, got %s %s.' % (name, type(obj)))
|
||||
|
||||
if self.app.collection.has_promises():
|
||||
self.raise_tcl_error('!!!Promises exists, but should not here!!!')
|
||||
|
||||
del args['name']
|
||||
return obj.get_gcode(**args)
|
@ -8,7 +8,7 @@ class TclCommandExteriors(TclCommand.TclCommand):
|
||||
"""
|
||||
|
||||
# array of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon)
|
||||
aliases = ['exteriors','ext']
|
||||
aliases = ['exteriors', 'ext']
|
||||
|
||||
# dictionary of types from Tcl command, needs to be ordered
|
||||
arg_names = collections.OrderedDict([
|
||||
@ -30,7 +30,7 @@ class TclCommandExteriors(TclCommand.TclCommand):
|
||||
('name', 'Name of the source Geometry object.'),
|
||||
('outname', 'Name of the resulting Geometry object.')
|
||||
]),
|
||||
'examples':[]
|
||||
'examples': []
|
||||
}
|
||||
|
||||
def execute(self, args, unnamed_args):
|
||||
@ -50,19 +50,15 @@ class TclCommandExteriors(TclCommand.TclCommand):
|
||||
else:
|
||||
outname = name + "_exteriors"
|
||||
|
||||
try:
|
||||
obj = self.app.collection.get_by_name(name)
|
||||
except:
|
||||
self.app.raiseTclError("Could not retrieve object: %s" % name)
|
||||
|
||||
obj = self.app.collection.get_by_name(name)
|
||||
if obj is None:
|
||||
self.app.raiseTclError("Object not found: %s" % name)
|
||||
self.raise_tcl_error("Object not found: %s" % name)
|
||||
|
||||
if not isinstance(obj, Geometry):
|
||||
self.app.raiseTclError('Expected Geometry, got %s %s.' % (name, type(obj)))
|
||||
self.raise_tcl_error('Expected Geometry, got %s %s.' % (name, type(obj)))
|
||||
|
||||
def geo_init(geo_obj, app_obj):
|
||||
def geo_init(geo_obj):
|
||||
geo_obj.solid_geometry = obj_exteriors
|
||||
|
||||
obj_exteriors = obj.get_exteriors()
|
||||
self.app.new_object('geometry', outname, geo_init)
|
||||
self.app.new_object('geometry', outname, geo_init)
|
||||
|
@ -1,6 +1,7 @@
|
||||
from ObjectCollection import *
|
||||
import TclCommand
|
||||
|
||||
|
||||
class TclCommandInteriors(TclCommand.TclCommand):
|
||||
"""
|
||||
Tcl shell command to get interiors of polygons
|
||||
@ -29,7 +30,7 @@ class TclCommandInteriors(TclCommand.TclCommand):
|
||||
('name', 'Name of the source Geometry object.'),
|
||||
('outname', 'Name of the resulting Geometry object.')
|
||||
]),
|
||||
'examples':[]
|
||||
'examples': []
|
||||
}
|
||||
|
||||
def execute(self, args, unnamed_args):
|
||||
@ -49,19 +50,15 @@ class TclCommandInteriors(TclCommand.TclCommand):
|
||||
else:
|
||||
outname = name + "_interiors"
|
||||
|
||||
try:
|
||||
obj = self.app.collection.get_by_name(name)
|
||||
except:
|
||||
self.app.raiseTclError("Could not retrieve object: %s" % name)
|
||||
|
||||
obj = self.app.collection.get_by_name(name)
|
||||
if obj is None:
|
||||
self.app.raiseTclError("Object not found: %s" % name)
|
||||
self.raise_tcl_error("Object not found: %s" % name)
|
||||
|
||||
if not isinstance(obj, Geometry):
|
||||
self.app.raiseTclError('Expected Geometry, got %s %s.' % (name, type(obj)))
|
||||
self.raise_tcl_error('Expected Geometry, got %s %s.' % (name, type(obj)))
|
||||
|
||||
def geo_init(geo_obj, app_obj):
|
||||
def geo_init(geo_obj):
|
||||
geo_obj.solid_geometry = obj_exteriors
|
||||
|
||||
obj_exteriors = obj.get_interiors()
|
||||
self.app.new_object('geometry', outname, geo_init)
|
||||
self.app.new_object('geometry', outname, geo_init)
|
||||
|
@ -1,5 +1,4 @@
|
||||
import pkgutil
|
||||
import inspect
|
||||
import sys
|
||||
|
||||
# allowed command modules
|
||||
@ -7,9 +6,10 @@ import tclCommands.TclCommandExteriors
|
||||
import tclCommands.TclCommandInteriors
|
||||
import tclCommands.TclCommandAddPolygon
|
||||
import tclCommands.TclCommandAddPolyline
|
||||
import tclCommands.TclCommandExportGcode
|
||||
import tclCommands.TclCommandCncjob
|
||||
|
||||
|
||||
__all__=[]
|
||||
__all__ = []
|
||||
|
||||
for loader, name, is_pkg in pkgutil.walk_packages(__path__):
|
||||
module = loader.find_module(name).load_module(name)
|
||||
@ -25,8 +25,8 @@ def register_all_commands(app, commands):
|
||||
|
||||
we need import all modules in top section:
|
||||
import tclCommands.TclCommandExteriors
|
||||
at this stage we can include only wanted commands with this, autoloading may be implemented in future
|
||||
I have no enought knowledge about python's anatomy. Would be nice to include all classes which are descendant etc.
|
||||
at this stage we can include only wanted commands with this, auto loading may be implemented in future
|
||||
I have no enough knowledge about python's anatomy. Would be nice to include all classes which are descendant etc.
|
||||
|
||||
:param app: FlatCAMApp
|
||||
:param commands: array of commands which should be modified
|
||||
@ -35,14 +35,14 @@ def register_all_commands(app, commands):
|
||||
|
||||
tcl_modules = {k: v for k, v in sys.modules.items() if k.startswith('tclCommands.TclCommand')}
|
||||
|
||||
for key, module in tcl_modules.items():
|
||||
for key, mod in tcl_modules.items():
|
||||
if key != 'tclCommands.TclCommand':
|
||||
classname = key.split('.')[1]
|
||||
class_ = getattr(module, classname)
|
||||
commandInstance=class_(app)
|
||||
class_name = key.split('.')[1]
|
||||
class_type = getattr(mod, class_name)
|
||||
command_instance = class_type(app)
|
||||
|
||||
for alias in commandInstance.aliases:
|
||||
commands[alias]={
|
||||
'fcn': commandInstance.execute_wrapper,
|
||||
'help': commandInstance.get_decorated_help()
|
||||
}
|
||||
for alias in command_instance.aliases:
|
||||
commands[alias] = {
|
||||
'fcn': command_instance.execute_wrapper,
|
||||
'help': command_instance.get_decorated_help()
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user