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 pass
def raiseTclUnknownError(self, unknownException): def raise_tcl_unknown_error(self, unknownException):
""" """
raise Exception if is different type than TclErrorException raise Exception if is different type than TclErrorException
:param unknownException: :param unknownException:
@ -659,11 +659,11 @@ class App(QtCore.QObject):
""" """
if not isinstance(unknownException, self.TclErrorException): if not isinstance(unknownException, self.TclErrorException):
self.raiseTclError("Unknown error: %s" % str(unknownException)) self.raise_tcl_error("Unknown error: %s" % str(unknownException))
else: else:
raise unknownException 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 this method pass exception from python into TCL as error, so we get stacktrace and reason
:param text: text of error :param text: text of error
@ -2274,20 +2274,20 @@ class App(QtCore.QObject):
# 8 - 2*left + 2*right +2*top + 2*bottom # 8 - 2*left + 2*right +2*top + 2*bottom
if name is None: if name is None:
self.raiseTclError('Argument name is missing.') self.raise_tcl_error('Argument name is missing.')
for key in kwa: for key in kwa:
if key not in types: if key not in types:
self.raiseTclError('Unknown parameter: %s' % key) self.raise_tcl_error('Unknown parameter: %s' % key)
try: try:
kwa[key] = types[key](kwa[key]) kwa[key] = types[key](kwa[key])
except Exception, e: 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: try:
obj = self.collection.get_by_name(str(name)) obj = self.collection.get_by_name(str(name))
except: 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 # Get min and max data for each object as we just cut rectangles across X or Y
xmin, ymin, xmax, ymax = obj.bounds() xmin, ymin, xmax, ymax = obj.bounds()
@ -2337,7 +2337,7 @@ class App(QtCore.QObject):
ymax + gapsize) ymax + gapsize)
except Exception as unknown: except Exception as unknown:
self.raiseTclUnknownError(unknown) self.raise_tcl_unknown_error(unknown)
def mirror(name, *args): def mirror(name, *args):
a, kwa = h(*args) a, kwa = h(*args)
@ -2624,26 +2624,26 @@ class App(QtCore.QObject):
} }
if name is None: if name is None:
self.raiseTclError('Argument name is missing.') self.raise_tcl_error('Argument name is missing.')
for key in kwa: for key in kwa:
if key not in types: if key not in types:
self.raiseTclError('Unknown parameter: %s' % key) self.raise_tcl_error('Unknown parameter: %s' % key)
try: try:
kwa[key] = types[key](kwa[key]) kwa[key] = types[key](kwa[key])
except Exception, e: 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: try:
obj = self.collection.get_by_name(str(name)) obj = self.collection.get_by_name(str(name))
except: except:
self.raiseTclError("Could not retrieve object: %s" % name) self.raise_tcl_error("Could not retrieve object: %s" % name)
if obj is None: 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): 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: try:
# Get the tools from the list # Get the tools from the list
@ -2663,10 +2663,10 @@ class App(QtCore.QObject):
obj.app.new_object("cncjob", job_name, job_init) obj.app.new_object("cncjob", job_name, job_init)
except Exception, e: except Exception, e:
self.raiseTclError("Operation failed: %s" % str(e)) self.raise_tcl_error("Operation failed: %s" % str(e))
except Exception as unknown: except Exception as unknown:
self.raiseTclUnknownError(unknown) self.raise_tcl_unknown_error(unknown)
def millholes(name=None, *args): def millholes(name=None, *args):
@ -2684,43 +2684,43 @@ class App(QtCore.QObject):
'outname': str} 'outname': str}
if name is None: if name is None:
self.raiseTclError('Argument name is missing.') self.raise_tcl_error('Argument name is missing.')
for key in kwa: for key in kwa:
if key not in types: if key not in types:
self.raiseTclError('Unknown parameter: %s' % key) self.raise_tcl_error('Unknown parameter: %s' % key)
try: try:
kwa[key] = types[key](kwa[key]) kwa[key] = types[key](kwa[key])
except Exception, e: 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: try:
if 'tools' in kwa: if 'tools' in kwa:
kwa['tools'] = [x.strip() for x in kwa['tools'].split(",")] kwa['tools'] = [x.strip() for x in kwa['tools'].split(",")]
except Exception as e: except Exception as e:
self.raiseTclError("Bad tools: %s" % str(e)) self.raise_tcl_error("Bad tools: %s" % str(e))
try: try:
obj = self.collection.get_by_name(str(name)) obj = self.collection.get_by_name(str(name))
except: except:
self.raiseTclError("Could not retrieve object: %s" % name) self.raise_tcl_error("Could not retrieve object: %s" % name)
if obj is None: 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): 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: try:
success, msg = obj.generate_milling(**kwa) success, msg = obj.generate_milling(**kwa)
except Exception as e: except Exception as e:
self.raiseTclError("Operation failed: %s" % str(e)) self.raise_tcl_error("Operation failed: %s" % str(e))
if not success: if not success:
self.raiseTclError(msg) self.raise_tcl_error(msg)
except Exception as unknown: except Exception as unknown:
self.raiseTclUnknownError(unknown) self.raise_tcl_unknown_error(unknown)
def exteriors(name=None, *args): def exteriors(name=None, *args):
''' '''
@ -2735,26 +2735,26 @@ class App(QtCore.QObject):
types = {'outname': str} types = {'outname': str}
if name is None: if name is None:
self.raiseTclError('Argument name is missing.') self.raise_tcl_error('Argument name is missing.')
for key in kwa: for key in kwa:
if key not in types: if key not in types:
self.raiseTclError('Unknown parameter: %s' % key) self.raise_tcl_error('Unknown parameter: %s' % key)
try: try:
kwa[key] = types[key](kwa[key]) kwa[key] = types[key](kwa[key])
except Exception, e: 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: try:
obj = self.collection.get_by_name(str(name)) obj = self.collection.get_by_name(str(name))
except: except:
self.raiseTclError("Could not retrieve object: %s" % name) self.raise_tcl_error("Could not retrieve object: %s" % name)
if obj is None: 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): 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): def geo_init(geo_obj, app_obj):
geo_obj.solid_geometry = obj_exteriors geo_obj.solid_geometry = obj_exteriors
@ -2768,10 +2768,10 @@ class App(QtCore.QObject):
obj_exteriors = obj.get_exteriors() obj_exteriors = obj.get_exteriors()
self.new_object('geometry', outname, geo_init) self.new_object('geometry', outname, geo_init)
except Exception as e: except Exception as e:
self.raiseTclError("Failed: %s" % str(e)) self.raise_tcl_error("Failed: %s" % str(e))
except Exception as unknown: except Exception as unknown:
self.raiseTclUnknownError(unknown) self.raise_tcl_unknown_error(unknown)
def interiors(name=None, *args): def interiors(name=None, *args):
''' '''
@ -2787,25 +2787,25 @@ class App(QtCore.QObject):
for key in kwa: for key in kwa:
if key not in types: if key not in types:
self.raiseTclError('Unknown parameter: %s' % key) self.raise_tcl_error('Unknown parameter: %s' % key)
try: try:
kwa[key] = types[key](kwa[key]) kwa[key] = types[key](kwa[key])
except Exception, e: 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: if name is None:
self.raiseTclError('Argument name is missing.') self.raise_tcl_error('Argument name is missing.')
try: try:
obj = self.collection.get_by_name(str(name)) obj = self.collection.get_by_name(str(name))
except: except:
self.raiseTclError("Could not retrieve object: %s" % name) self.raise_tcl_error("Could not retrieve object: %s" % name)
if obj is None: 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): 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): def geo_init(geo_obj, app_obj):
geo_obj.solid_geometry = obj_interiors geo_obj.solid_geometry = obj_interiors
@ -2819,10 +2819,10 @@ class App(QtCore.QObject):
obj_interiors = obj.get_interiors() obj_interiors = obj.get_interiors()
self.new_object('geometry', outname, geo_init) self.new_object('geometry', outname, geo_init)
except Exception as e: except Exception as e:
self.raiseTclError("Failed: %s" % str(e)) self.raise_tcl_error("Failed: %s" % str(e))
except Exception as unknown: except Exception as unknown:
self.raiseTclUnknownError(unknown) self.raise_tcl_unknown_error(unknown)
def isolate(name=None, *args): def isolate(name=None, *args):
''' '''
@ -2840,29 +2840,29 @@ class App(QtCore.QObject):
for key in kwa: for key in kwa:
if key not in types: if key not in types:
self.raiseTclError('Unknown parameter: %s' % key) self.raise_tcl_error('Unknown parameter: %s' % key)
try: try:
kwa[key] = types[key](kwa[key]) kwa[key] = types[key](kwa[key])
except Exception, e: 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: try:
obj = self.collection.get_by_name(str(name)) obj = self.collection.get_by_name(str(name))
except: except:
self.raiseTclError("Could not retrieve object: %s" % name) self.raise_tcl_error("Could not retrieve object: %s" % name)
if obj is None: if obj is None:
self.raiseTclError("Object not found: %s" % name) self.raise_tcl_error("Object not found: %s" % name)
assert isinstance(obj, FlatCAMGerber), \ assert isinstance(obj, FlatCAMGerber), \
"Expected a FlatCAMGerber, got %s" % type(obj) "Expected a FlatCAMGerber, got %s" % type(obj)
if not isinstance(obj, FlatCAMGerber): 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: try:
obj.isolate(**kwa) obj.isolate(**kwa)
except Exception, e: except Exception, e:
self.raiseTclError("Operation failed: %s" % str(e)) self.raise_tcl_error("Operation failed: %s" % str(e))
return 'Ok' return 'Ok'
@ -3253,11 +3253,11 @@ class App(QtCore.QObject):
Test it like this: Test it like this:
if name is None: 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. 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. which is catched in exec_command and displayed in TCL shell console with red background.
Error in console is displayed with TCL trace. 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) 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): def on_plot_cb_click(self, *args):
if self.muted_ui: if self.muted_ui:
return return
@ -1243,7 +1247,8 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
outname=None, outname=None,
spindlespeed=None, spindlespeed=None,
multidepth=None, multidepth=None,
depthperpass=None): depthperpass=None,
use_thread=True):
""" """
Creates a CNCJob out of this Geometry object. The actual Creates a CNCJob out of this Geometry object. The actual
work is done by the target FlatCAMCNCjob object's work is done by the target FlatCAMCNCjob object's
@ -1304,18 +1309,22 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
app_obj.progress.emit(80) 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 if use_thread:
self.app.collection.promise(outname) # 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 # Create a promise with the name
self.app.worker_task.emit({'fcn': job_thread, 'params': [self.app]}) 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 def on_plot_cb_click(self, *args): # TODO: args not needed
if self.muted_ui: if self.muted_ui:

View File

@ -1,83 +1,108 @@
import sys, inspect, pkgutil
import re import re
import FlatCAMApp import FlatCAMApp
import abc
import collections import collections
from PyQt4 import QtCore
from contextlib import contextmanager
class TclCommand(object): 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) # array of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon)
aliases = [] aliases = []
# dictionary of types from Tcl command, needs to be ordered # dictionary of types from Tcl command, needs to be ordered
# OrderedDict should be like collections.OrderedDict([(key,value),(key2,value2)])
arg_names = collections.OrderedDict([ arg_names = collections.OrderedDict([
('name', str) ('name', str)
]) ])
# dictionary of types from Tcl command, needs to be ordered , this is for options like -optionname value # 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'} # array of mandatory options for current Tcl command: required = {'name','outname'}
required = ['name'] required = ['name']
# structured help for current command, args needs to be ordered # structured help for current command, args needs to be ordered
# OrderedDict should be like collections.OrderedDict([(key,value),(key2,value2)])
help = { help = {
'main': "undefined help.", 'main': "undefined help.",
'args': collections.OrderedDict([ 'args': collections.OrderedDict([
('argumentname', 'undefined help.'), ('argumentname', 'undefined help.'),
('optionname', 'undefined help.') ('optionname', 'undefined help.')
]), ]),
'examples' : [] 'examples': []
} }
def __init__(self, app): 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): def get_decorated_help(self):
""" """
Decorate help for TCL console output. 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 = [] command_string = []
for key, value in self.help['args'].items(): for arg_key, arg_type in self.help['args'].items():
command_string.append(get_decorated_argument(key, value, True)) command_string.append(get_decorated_argument(arg_key, arg_type, True))
return "> " + alias + " " + " ".join(command_string) 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 = '' option_symbol = ''
if key in self.arg_names: if help_key in self.arg_names:
type=self.arg_names[key] arg_type = self.arg_names[help_key]
type_name=str(type.__name__) type_name = str(arg_type.__name__)
in_command_name = "<" + type_name + ">" in_command_name = "<" + type_name + ">"
elif key in self.option_types: elif help_key in self.option_types:
option_symbol = '-' option_symbol = '-'
type=self.option_types[key] arg_type = self.option_types[help_key]
type_name=str(type.__name__) type_name = str(arg_type.__name__)
in_command_name = option_symbol + key + " <" + type_name + ">" in_command_name = option_symbol + help_key + " <" + type_name + ">"
else: else:
option_symbol = '' option_symbol = ''
type_name='?' type_name = '?'
in_command_name = option_symbol + key + " <" + type_name + ">" in_command_name = option_symbol + help_key + " <" + type_name + ">"
if in_command: if in_command:
if key in self.required: if help_key in self.required:
return in_command_name return in_command_name
else: else:
return '[' + in_command_name + "]" return '[' + in_command_name + "]"
else: else:
if key in self.required: if help_key in self.required:
return "\t" + option_symbol + key + " <" + type_name + ">: " + value return "\t" + option_symbol + help_key + " <" + type_name + ">: " + help_text
else: else:
return "\t[" + option_symbol + key + " <" + type_name + ">: " + value+"]" return "\t[" + option_symbol + help_key + " <" + type_name + ">: " + help_text + "]"
def get_decorated_example(example): def get_decorated_example(example_item):
return "> "+example return "> "+example_item
help_string=[self.help['main']] help_string = [self.help['main']]
for alias in self.aliases: for alias in self.aliases:
help_string.append(get_decorated_command(alias)) help_string.append(get_decorated_command(alias))
@ -89,12 +114,17 @@ class TclCommand(object):
return "\n".join(help_string) 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 Pre-processes arguments to detect '-keyword value' pairs into dictionary
and standalone parameters into list. 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 = {} options = {}
@ -121,41 +151,43 @@ class TclCommand(object):
Check arguments and options for right types Check arguments and options for right types
:param args: arguments from tcl to check :param args: arguments from tcl to check
:return: :return: named_args, unnamed_args
""" """
arguments, options = self.parse_arguments(args) arguments, options = self.parse_arguments(args)
named_args={} named_args = {}
unnamed_args=[] unnamed_args = []
# check arguments # check arguments
idx=0 idx = 0
arg_names_items=self.arg_names.items() arg_names_items = self.arg_names.items()
for argument in arguments: for argument in arguments:
if len(self.arg_names) > idx: if len(self.arg_names) > idx:
key, type = arg_names_items[idx] key, arg_type = arg_names_items[idx]
try: try:
named_args[key] = type(argument) named_args[key] = arg_type(argument)
except Exception, e: 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: else:
unnamed_args.append(argument) unnamed_args.append(argument)
idx += 1 idx += 1
# check otions # check options
for key in options: for key in options:
if key not in self.option_types: if key not in self.option_types:
self.app.raiseTclError('Unknown parameter: %s' % key) self.raise_tcl_error('Unknown parameter: %s' % key)
try: try:
named_args[key] = self.option_types[key](options[key]) named_args[key] = self.option_types[key](options[key])
except Exception, e: 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 # check required arguments
for key in self.required: for key in self.required:
if key not in named_args: 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 return named_args, unnamed_args
@ -168,12 +200,16 @@ class TclCommand(object):
:param args: arguments passed from tcl command console :param args: arguments passed from tcl command console
:return: None, output text or exception :return: None, output text or exception
""" """
try: try:
self.log.debug("TCL command '%s' executed." % str(self.__class__))
args, unnamed_args = self.check_args(args) args, unnamed_args = self.check_args(args)
return self.execute(args, unnamed_args) return self.execute(args, unnamed_args)
except Exception as unknown: 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): def execute(self, args, unnamed_args):
""" """
Direct execute of command, this method should be implemented in each descendant. 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") 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) # 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 # dictionary of types from Tcl command, needs to be ordered
arg_names = collections.OrderedDict([ 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 # 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'} # array of mandatory options for current Tcl command: required = {'name','outname'}
required = ['name'] required = ['name']
@ -28,7 +28,7 @@ class TclCommandAddPolygon(TclCommand.TclCommand):
('name', 'Name of the Geometry object to which to append the polygon.'), ('name', 'Name of the Geometry object to which to append the polygon.'),
('xi, yi', 'Coordinates of points in the polygon.') ('xi, yi', 'Coordinates of points in the polygon.')
]), ]),
'examples':[ 'examples': [
'add_polygon <name> <x0> <y0> <x1> <y1> <x2> <y2> [x3 y3 [...]]' 'add_polygon <name> <x0> <y0> <x1> <y1> <x2> <y2> [x3 y3 [...]]'
] ]
} }
@ -45,21 +45,17 @@ class TclCommandAddPolygon(TclCommand.TclCommand):
name = args['name'] name = args['name']
try: obj = self.app.collection.get_by_name(name)
obj = self.app.collection.get_by_name(name)
except:
self.app.raiseTclError("Could not retrieve object: %s" % name)
if obj is None: 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): 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: 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)] 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.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 # 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'} # array of mandatory options for current Tcl command: required = {'name','outname'}
required = ['name'] required = ['name']
@ -28,7 +28,7 @@ class TclCommandAddPolyline(TclCommand.TclCommand):
('name', 'Name of the Geometry object to which to append the polyline.'), ('name', 'Name of the Geometry object to which to append the polyline.'),
('xi, yi', 'Coordinates of points in the polyline.') ('xi, yi', 'Coordinates of points in the polyline.')
]), ]),
'examples':[ 'examples': [
'add_polyline <name> <x0> <y0> <x1> <y1> <x2> <y2> [x3 y3 [...]]' 'add_polyline <name> <x0> <y0> <x1> <y1> <x2> <y2> [x3 y3 [...]]'
] ]
} }
@ -45,21 +45,17 @@ class TclCommandAddPolyline(TclCommand.TclCommand):
name = args['name'] name = args['name']
try: obj = self.app.collection.get_by_name(name)
obj = self.app.collection.get_by_name(name)
except:
self.app.raiseTclError("Could not retrieve object: %s" % name)
if obj is None: 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): 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: 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)] 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.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) # 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 # dictionary of types from Tcl command, needs to be ordered
arg_names = collections.OrderedDict([ arg_names = collections.OrderedDict([
@ -30,7 +30,7 @@ class TclCommandExteriors(TclCommand.TclCommand):
('name', 'Name of the source Geometry object.'), ('name', 'Name of the source Geometry object.'),
('outname', 'Name of the resulting Geometry object.') ('outname', 'Name of the resulting Geometry object.')
]), ]),
'examples':[] 'examples': []
} }
def execute(self, args, unnamed_args): def execute(self, args, unnamed_args):
@ -50,19 +50,15 @@ class TclCommandExteriors(TclCommand.TclCommand):
else: else:
outname = name + "_exteriors" outname = name + "_exteriors"
try: obj = self.app.collection.get_by_name(name)
obj = self.app.collection.get_by_name(name)
except:
self.app.raiseTclError("Could not retrieve object: %s" % name)
if obj is None: 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): 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 geo_obj.solid_geometry = obj_exteriors
obj_exteriors = obj.get_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 * from ObjectCollection import *
import TclCommand import TclCommand
class TclCommandInteriors(TclCommand.TclCommand): class TclCommandInteriors(TclCommand.TclCommand):
""" """
Tcl shell command to get interiors of polygons Tcl shell command to get interiors of polygons
@ -29,7 +30,7 @@ class TclCommandInteriors(TclCommand.TclCommand):
('name', 'Name of the source Geometry object.'), ('name', 'Name of the source Geometry object.'),
('outname', 'Name of the resulting Geometry object.') ('outname', 'Name of the resulting Geometry object.')
]), ]),
'examples':[] 'examples': []
} }
def execute(self, args, unnamed_args): def execute(self, args, unnamed_args):
@ -49,19 +50,15 @@ class TclCommandInteriors(TclCommand.TclCommand):
else: else:
outname = name + "_interiors" outname = name + "_interiors"
try: obj = self.app.collection.get_by_name(name)
obj = self.app.collection.get_by_name(name)
except:
self.app.raiseTclError("Could not retrieve object: %s" % name)
if obj is None: 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): 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 geo_obj.solid_geometry = obj_exteriors
obj_exteriors = obj.get_interiors() 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 pkgutil
import inspect
import sys import sys
# allowed command modules # allowed command modules
@ -7,9 +6,10 @@ import tclCommands.TclCommandExteriors
import tclCommands.TclCommandInteriors import tclCommands.TclCommandInteriors
import tclCommands.TclCommandAddPolygon import tclCommands.TclCommandAddPolygon
import tclCommands.TclCommandAddPolyline import tclCommands.TclCommandAddPolyline
import tclCommands.TclCommandExportGcode
import tclCommands.TclCommandCncjob
__all__ = []
__all__=[]
for loader, name, is_pkg in pkgutil.walk_packages(__path__): for loader, name, is_pkg in pkgutil.walk_packages(__path__):
module = loader.find_module(name).load_module(name) 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: we need import all modules in top section:
import tclCommands.TclCommandExteriors import tclCommands.TclCommandExteriors
at this stage we can include only wanted commands with this, autoloading may be implemented in future at this stage we can include only wanted commands with this, auto loading 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. I have no enough knowledge about python's anatomy. Would be nice to include all classes which are descendant etc.
:param app: FlatCAMApp :param app: FlatCAMApp
:param commands: array of commands which should be modified :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')} 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': if key != 'tclCommands.TclCommand':
classname = key.split('.')[1] class_name = key.split('.')[1]
class_ = getattr(module, classname) class_type = getattr(mod, class_name)
commandInstance=class_(app) command_instance = class_type(app)
for alias in commandInstance.aliases: for alias in command_instance.aliases:
commands[alias]={ commands[alias] = {
'fcn': commandInstance.execute_wrapper, 'fcn': command_instance.execute_wrapper,
'help': commandInstance.get_decorated_help() 'help': command_instance.get_decorated_help()
} }