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:
Kamil Sopko 2016-03-19 15:13:07 +01:00
parent 0f463a1fc2
commit 980638630d
10 changed files with 427 additions and 162 deletions

View File

@ -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.

View File

@ -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:

View File

@ -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)

View File

@ -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()

View File

@ -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()

View 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)

View 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)

View File

@ -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)

View File

@ -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)

View File

@ -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()
}