Merge remote-tracking branch 'upstream/Beta' into Beta

This commit is contained in:
Robert Niemöller
2020-11-07 10:59:11 +01:00
1222 changed files with 354941 additions and 128457 deletions

View File

@@ -1,10 +1,9 @@
import sys
import re
import FlatCAMApp
import app_Main
import abc
import collections
from PyQt5 import QtCore, QtGui
from PyQt5.QtCore import Qt
from PyQt5 import QtCore
from contextlib import contextmanager
@@ -54,10 +53,12 @@ class TclCommand(object):
if self.app is None:
raise TypeError('Expected app to be FlatCAMApp instance.')
if not isinstance(self.app, FlatCAMApp.App):
if not isinstance(self.app, app_Main.App):
raise TypeError('Expected FlatCAMApp, got %s.' % type(app))
self.log = self.app.log
self.error_info = None
self.error = None
def raise_tcl_error(self, text):
"""
@@ -70,7 +71,7 @@ class TclCommand(object):
:return: none
"""
self.app.raise_tcl_error(text)
self.app.shell.raise_tcl_error(text)
def get_current_command(self):
"""
@@ -78,9 +79,7 @@ class TclCommand(object):
:return: current command
"""
command_string = []
command_string.append(self.aliases[0])
command_string = [self.aliases[0]]
if self.original_args is not None:
for arg in self.original_args:
@@ -117,7 +116,7 @@ class TclCommand(object):
if help_key in self.arg_names:
arg_type = self.arg_names[help_key]
type_name = str(arg_type.__name__)
#in_command_name = help_key + "<" + type_name + ">"
# in_command_name = help_key + "<" + type_name + ">"
in_command_name = help_key
elif help_key in self.option_types:
@@ -163,35 +162,43 @@ class TclCommand(object):
@staticmethod
def parse_arguments(args):
"""
Pre-processes arguments to detect '-keyword value' pairs into dictionary
and standalone parameters into list.
"""
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 accessibility,
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
"""
:param args: arguments from tcl to parse
:return: arguments, options
"""
options = {}
arguments = []
n = len(args)
name = None
for i in range(n):
match = re.search(r'^-([a-zA-Z].*)', args[i])
if match:
assert name is None
name = match.group(1)
continue
options = {}
arguments = []
n = len(args)
if name is None:
arguments.append(args[i])
else:
options[name] = args[i]
name = None
option_name = None
return arguments, options
for i in range(n):
match = re.search(r'^-([a-zA-Z].*)', args[i])
if match:
# assert option_name is None
if option_name is not None:
options[option_name] = None
option_name = match.group(1)
continue
if option_name is None:
arguments.append(args[i])
else:
options[option_name] = args[i]
option_name = None
if option_name is not None:
options[option_name] = None
return arguments, options
def check_args(self, args):
"""
@@ -202,7 +209,6 @@ class TclCommand(object):
"""
arguments, options = self.parse_arguments(args)
named_args = {}
unnamed_args = []
@@ -212,6 +218,7 @@ class TclCommand(object):
for argument in arguments:
if len(self.arg_names) > idx:
key, arg_type = arg_names_items[idx]
try:
named_args[key] = arg_type(argument)
except Exception as e:
@@ -227,7 +234,12 @@ class TclCommand(object):
self.raise_tcl_error('Unknown parameter: %s' % key)
try:
if key != 'timeout':
named_args[key] = self.option_types[key](options[key])
# None options are allowed; if None then the defaults are used
# - must be implemented in the Tcl commands
if options[key] is not None:
named_args[key] = self.option_types[key](options[key])
else:
named_args[key] = options[key]
else:
named_args[key] = int(options[key])
except Exception as e:
@@ -259,10 +271,10 @@ class TclCommand(object):
:return: raise exception
"""
# becouse of signaling we cannot call error to TCL from here but when task
# is finished also nonsignaled are handled here to better exception
# because of signaling we cannot call error to TCL from here but when task
# is finished also non-signaled are handled here to better exception
# handling and displayed after command is finished
raise self.app.TclErrorException(text)
raise self.app.shell.TclErrorException(text)
def execute_wrapper(self, *args):
"""
@@ -274,17 +286,16 @@ class TclCommand(object):
:return: None, output text or exception
"""
#self.worker_task.emit({'fcn': self.exec_command_test, 'params': [text, False]})
# self.worker_task.emit({'fcn': self.exec_command_test, 'params': [text, False]})
try:
self.log.debug("TCL command '%s' executed." % str(self.__class__))
self.log.debug("TCL command '%s' executed." % str(type(self).__name__))
self.original_args = args
args, unnamed_args = self.check_args(args)
return self.execute(args, unnamed_args)
except Exception as unknown:
error_info = sys.exc_info()
self.log.error("TCL command '%s' failed." % str(self))
self.app.display_tcl_error(unknown, error_info)
self.log.error("TCL command '%s' failed. Error text: %s" % (str(self), str(unknown)))
self.app.shell.display_tcl_error(unknown, error_info)
self.raise_tcl_unknown_error(unknown)
@abc.abstractmethod
@@ -376,7 +387,8 @@ class TclCommandSignaled(TclCommand):
# Terminate on timeout
if timeout is not None:
QtCore.QTimer.singleShot(timeout, report_quit)
time_val = int(timeout)
QtCore.QTimer.singleShot(time_val, report_quit)
# Block
loop.exec_()
@@ -387,12 +399,12 @@ class TclCommandSignaled(TclCommand):
raise ex[0]
if status['timed_out']:
self.app.raise_tcl_unknown_error("Operation timed outed! Consider increasing option "
"'-timeout <miliseconds>' for command or "
"'set_sys global_background_timeout <miliseconds>'.")
self.app.shell.raise_tcl_unknown_error("Operation timed outed! Consider increasing option "
"'-timeout <miliseconds>' for command or "
"'set_sys global_background_timeout <miliseconds>'.")
try:
self.log.debug("TCL command '%s' executed." % str(self.__class__))
self.log.debug("TCL command '%s' executed." % str(type(self).__name__))
self.original_args = args
args, unnamed_args = self.check_args(args)
if 'timeout' in args:
@@ -402,9 +414,9 @@ class TclCommandSignaled(TclCommand):
passed_timeout = self.app.defaults['global_background_timeout']
# set detail for processing, it will be there until next open or close
self.app.shell.open_proccessing(self.get_current_command())
self.app.shell.open_processing(self.get_current_command())
def handle_finished(obj):
def handle_finished():
self.app.shell_command_finished.disconnect(handle_finished)
if self.error is not None:
self.raise_tcl_unknown_error(self.error)
@@ -417,7 +429,6 @@ class TclCommandSignaled(TclCommand):
# when operation will be really long is good to set it higher then defqault 30s
self.app.worker_task.emit({'fcn': self.execute_call, 'params': [args, unnamed_args]})
return self.output
except Exception as unknown:
@@ -427,5 +438,5 @@ class TclCommandSignaled(TclCommand):
else:
error_info = sys.exc_info()
self.log.error("TCL command '%s' failed." % str(self))
self.app.display_tcl_error(unknown, error_info)
self.app.shell.display_tcl_error(unknown, error_info)
self.raise_tcl_unknown_error(unknown)

View File

@@ -1,4 +1,4 @@
from ObjectCollection import *
import collections
from tclCommands.TclCommand import TclCommand
@@ -13,6 +13,8 @@ class TclCommandAddCircle(TclCommand):
# List of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon)
aliases = ['add_circle']
description = '%s %s' % ("--", "Creates a circle in the given Geometry object.")
# Dictionary of types from Tcl command, needs to be ordered
arg_names = collections.OrderedDict([
('name', str),
@@ -38,7 +40,7 @@ class TclCommandAddCircle(TclCommand):
('center_y', 'Y coordinates of the center of the circle.'),
('radius', 'Radius of the circle.')
]),
'examples': []
'examples': ['add_circle geo_name 1.0 2.0 3']
}
def execute(self, args, unnamed_args):
@@ -49,17 +51,18 @@ class TclCommandAddCircle(TclCommand):
:return:
"""
obj_name = args['name']
name = args['name']
center_x = args['center_x']
center_y = args['center_y']
radius = args['radius']
try:
obj = self.app.collection.get_by_name(str(obj_name))
except:
return "Could not retrieve object: %s" % obj_name
obj = self.app.collection.get_by_name(str(name))
except Exception:
return "Could not retrieve object: %s" % name
if obj is None:
return "Object not found: %s" % obj_name
return "Object not found: %s" % name
if obj.kind != 'geometry':
self.raise_tcl_error('Expected Geometry, got %s %s.' % (name, type(obj)))
obj.add_circle([float(center_x), float(center_y)], float(radius))

View File

@@ -1,4 +1,3 @@
from ObjectCollection import *
from tclCommands.TclCommand import *
@@ -10,6 +9,8 @@ class TclCommandAddPolygon(TclCommandSignaled):
# array of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon)
aliases = ['add_polygon', 'add_poly']
description = '%s %s' % ("--", "Creates a polygon in the given Geometry object.")
# dictionary of types from Tcl command, needs to be ordered
arg_names = collections.OrderedDict([
('name', str)
@@ -49,13 +50,13 @@ class TclCommandAddPolygon(TclCommandSignaled):
if obj is None:
self.raise_tcl_error("Object not found: %s" % name)
if not isinstance(obj, Geometry):
if obj.kind != 'geometry':
self.raise_tcl_error('Expected Geometry, got %s %s.' % (name, type(obj)))
if len(unnamed_args) % 2 != 0:
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)]
nr_points = int(len(unnamed_args) / 2)
points = [[float(unnamed_args[2*i]), float(unnamed_args[2*i+1])] for i in range(nr_points)]
obj.add_polygon(points)
obj.plot()

View File

@@ -1,5 +1,4 @@
from ObjectCollection import *
from tclCommands.TclCommand import TclCommandSignaled
from tclCommands.TclCommand import *
class TclCommandAddPolyline(TclCommandSignaled):
@@ -10,6 +9,8 @@ class TclCommandAddPolyline(TclCommandSignaled):
# array of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon)
aliases = ['add_polyline']
description = '%s %s' % ("--", "Creates a polyline in the given Geometry object.")
# dictionary of types from Tcl command, needs to be ordered
arg_names = collections.OrderedDict([
('name', str)
@@ -49,13 +50,13 @@ class TclCommandAddPolyline(TclCommandSignaled):
if obj is None:
self.raise_tcl_error("Object not found: %s" % name)
if not isinstance(obj, Geometry):
if obj.kind != 'geometry':
self.raise_tcl_error('Expected Geometry, got %s %s.' % (name, type(obj)))
if len(unnamed_args) % 2 != 0:
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)]
nr_points = int(len(unnamed_args) / 2)
points = [[float(unnamed_args[2*i]), float(unnamed_args[2*i+1])] for i in range(nr_points)]
obj.add_polyline(points)
obj.plot()

View File

@@ -1,5 +1,4 @@
from ObjectCollection import *
from tclCommands.TclCommand import TclCommandSignaled
from tclCommands.TclCommand import *
class TclCommandAddRectangle(TclCommandSignaled):
@@ -10,6 +9,8 @@ class TclCommandAddRectangle(TclCommandSignaled):
# array of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon)
aliases = ['add_rectangle']
description = '%s %s' % ("--", "Creates a rectangle in the given Geometry object.")
# Dictionary of types from Tcl command, needs to be ordered.
# For positional arguments
arg_names = collections.OrderedDict([
@@ -31,13 +32,13 @@ class TclCommandAddRectangle(TclCommandSignaled):
# structured help for current command, args needs to be ordered
help = {
'main': "Add a rectange to the given Geometry object.",
'main': "Creates a rectangle in the given Geometry object.",
'args': collections.OrderedDict([
('name', 'Name of the Geometry object in which to add the rectangle.'),
('x0 y0', 'Bottom left corner coordinates.'),
('x1 y1', 'Top right corner coordinates.')
]),
'examples': []
'examples': ["add_rectangle geo_name 0 0 10 10"]
}
def execute(self, args, unnamed_args):
@@ -50,17 +51,23 @@ class TclCommandAddRectangle(TclCommandSignaled):
:return: None or exception
"""
obj_name = args['name']
name = args['name']
x0 = args['x0']
y0 = args['y0']
x1 = args['x1']
y1 = args['y1']
if unnamed_args:
self.raise_tcl_error(
"Too many arguments. Correct format: %s" % '["add_rectangle geo_name xmin ymin xmax ymax"]')
try:
obj = self.app.collection.get_by_name(str(obj_name))
except:
return "Could not retrieve object: %s" % obj_name
obj = self.app.collection.get_by_name(str(name))
except Exception:
return "Could not retrieve object: %s" % name
if obj is None:
return "Object not found: %s" % obj_name
return "Object not found: %s" % name
if obj.kind != 'geometry':
self.raise_tcl_error('Expected Geometry, got %s %s.' % (name, type(obj)))
obj.add_polygon([(x0, y0), (x1, y0), (x1, y1), (x0, y1)])

View File

@@ -1,6 +1,9 @@
from ObjectCollection import *
import collections
from tclCommands.TclCommand import TclCommandSignaled
from shapely.geometry import Point
import shapely.affinity as affinity
class TclCommandAlignDrill(TclCommandSignaled):
"""
@@ -11,6 +14,8 @@ class TclCommandAlignDrill(TclCommandSignaled):
# backward compatibility (add_poly, add_polygon)
aliases = ['aligndrill']
description = '%s %s' % ("--", "Create an Excellon object with drills for alignment.")
# Dictionary of types from Tcl command, needs to be ordered.
# For positional arguments
arg_names = collections.OrderedDict([
@@ -29,6 +34,7 @@ class TclCommandAlignDrill(TclCommandSignaled):
('axisoffset', float),
('dia', float),
('dist', float),
('outname', str),
])
# array of mandatory options for current Tcl command: required = {'name','outname'}
@@ -36,20 +42,24 @@ class TclCommandAlignDrill(TclCommandSignaled):
# structured help for current command, args needs to be ordered
help = {
'main': "Create excellon with drills for aligment.",
'main': "Create an Excellon object with drills for alignment.",
'args': collections.OrderedDict([
('name', 'Name of the object (Gerber or Excellon) to mirror.'),
('dia', 'Tool diameter'),
('box', 'Name of object which act as box (cutout for example.)'),
('holes', 'Tuple of tuples where each tuple it is a set of x, y coordinates. '
'E.g: (x0, y0), (x1, y1), ... '),
('grid', 'Aligning to grid, for those, who have aligning pins'
'inside table in grid (-5,0),(5,0),(15,0)...'),
('gridoffset', 'offset of grid from 0 position.'),
('minoffset', 'min and max distance between align hole and pcb.'),
('axisoffset', 'Offset on second axis before aligment holes'),
('axis', 'Mirror axis parallel to the X or Y axis.'),
('dist', 'Distance of the mirror axis to the X or Y axis.')
('dist', 'Distance of the mirror axis to the X or Y axis.'),
('outname', 'Name of the resulting Excellon object.'),
]),
'examples': []
'examples': ['aligndrill my_object -axis X -box my_object -dia 3.125 -grid 1 '
'-gridoffset 0 -minoffset 2 -axisoffset 2']
}
def execute(self, args, unnamed_args):
@@ -64,18 +74,21 @@ class TclCommandAlignDrill(TclCommandSignaled):
name = args['name']
if 'outname' in args:
outname = args['outname']
else:
outname = name + "_aligndrill"
# Get source object.
try:
obj = self.app.collection.get_by_name(str(name))
except:
except Exception:
return "Could not retrieve object: %s" % name
if obj is None:
return "Object not found: %s" % name
if not isinstance(obj, FlatCAMGeometry) and \
not isinstance(obj, FlatCAMGerber) and \
not isinstance(obj, FlatCAMExcellon):
if obj.kind != "geometry" and obj.kind != 'gerber' and obj.kind != 'excellon':
return "ERROR: Only Gerber, Geometry and Excellon objects can be used."
# Axis
@@ -95,8 +108,10 @@ class TclCommandAlignDrill(TclCommandSignaled):
xscale, yscale = {"X": (1.0, -1.0), "Y": (-1.0, 1.0)}[axis]
tooldia = args['dia']
# Tools
tools = {"1": {"C": args['dia']}}
# tools = {"1": {"C": args['dia']}}
def alligndrill_init_me(init_obj, app_obj):
"""
@@ -113,8 +128,8 @@ class TclCommandAlignDrill(TclCommandSignaled):
for hole in holes:
point = Point(hole)
point_mirror = affinity.scale(point, xscale, yscale, origin=(px, py))
drills.append({"point": point, "tool": "1"})
drills.append({"point": point_mirror, "tool": "1"})
drills.append(point)
drills.append(point_mirror)
else:
if 'box' not in args:
return "ERROR: -grid can be used only for -box"
@@ -154,18 +169,24 @@ class TclCommandAlignDrill(TclCommandSignaled):
for hole in localholes:
point = Point(hole)
point_mirror = affinity.scale(point, xscale, yscale, origin=(px, py))
drills.append({"point": point, "tool": "1"})
drills.append({"point": point_mirror, "tool": "1"})
drills.append(point)
drills.append(point_mirror)
init_obj.tools = {
'1': {
'tooldia': tooldia,
'drills': drills,
'solid_geometry': []
}
}
init_obj.tools = tools
init_obj.drills = drills
init_obj.create_geometry()
# Box
if 'box' in args:
try:
box = self.app.collection.get_by_name(args['box'])
except:
except Exception:
return "Could not retrieve object box: %s" % args['box']
if box is None:
@@ -176,9 +197,7 @@ class TclCommandAlignDrill(TclCommandSignaled):
px = 0.5 * (xmin + xmax)
py = 0.5 * (ymin + ymax)
obj.app.new_object("excellon",
name + "_aligndrill",
alligndrill_init_me)
obj.app.app_obj.new_object("excellon", outname, alligndrill_init_me, plot=False)
except Exception as e:
return "Operation failed: %s" % str(e)
@@ -194,8 +213,8 @@ class TclCommandAlignDrill(TclCommandSignaled):
try:
px = dist
py = dist
obj.app.new_object("excellon", name + "_alligndrill", alligndrill_init_me)
obj.app.app_obj.new_object("excellon", outname, alligndrill_init_me, plot=False)
except Exception as e:
return "Operation failed: %s" % str(e)
return 'Ok'
return 'Ok. Align Drills Excellon object created'

View File

@@ -1,6 +1,7 @@
from ObjectCollection import *
import collections
from tclCommands.TclCommand import TclCommandSignaled
from shapely.geometry import Point
class TclCommandAlignDrillGrid(TclCommandSignaled):
"""
@@ -14,10 +15,12 @@ class TclCommandAlignDrillGrid(TclCommandSignaled):
# backward compatibility (add_poly, add_polygon)
aliases = ['aligndrillgrid']
description = '%s %s' % ("--", "Create an Excellon object with drills for alignment arranged in a grid.")
# Dictionary of types from Tcl command, needs to be ordered.
# For positional arguments
arg_names = collections.OrderedDict([
('outname', str)
])
# Dictionary of types from Tcl command, needs to be ordered.
@@ -29,17 +32,17 @@ class TclCommandAlignDrillGrid(TclCommandSignaled):
('gridy', float),
('gridoffsety', float),
('columns', int),
('rows', int)
('rows', int),
('outname', str)
])
# array of mandatory options for current Tcl command: required = {'name','outname'}
required = ['outname', 'gridx', 'gridy', 'columns', 'rows']
required = ['gridx', 'gridy', 'columns', 'rows']
# structured help for current command, args needs to be ordered
help = {
'main': "Create excellon with drills for aligment grid.",
'main': "Create an Excellon object with drills for alignment arranged in a grid.",
'args': collections.OrderedDict([
('outname', 'Name of the object to create.'),
('dia', 'Tool diameter.'),
('gridx', 'Grid size in X axis.'),
('gridoffsetx', 'Move grid from origin.'),
@@ -47,8 +50,9 @@ class TclCommandAlignDrillGrid(TclCommandSignaled):
('gridoffsety', 'Move grid from origin.'),
('colums', 'Number of grid holes on X axis.'),
('rows', 'Number of grid holes on Y axis.'),
('outname', 'Name of the object to create.')
]),
'examples': []
'examples': ['aligndrillgrid -rows 2 -columns 2 -gridoffsetx 10 -gridoffsety 10 -gridx 2.54 -gridy 5.08']
}
def execute(self, args, unnamed_args):
@@ -61,6 +65,11 @@ class TclCommandAlignDrillGrid(TclCommandSignaled):
:return: None or exception
"""
if 'outname' in args:
outname = args['outname']
else:
outname = "new_aligndrill_grid"
if 'gridoffsetx' not in args:
gridoffsetx = 0
else:
@@ -71,8 +80,9 @@ class TclCommandAlignDrillGrid(TclCommandSignaled):
else:
gridoffsety = args['gridoffsety']
tooldia = args['dia']
# Tools
tools = {"1": {"C": args['dia']}}
# tools = {"1": {"C": args['dia']}}
def aligndrillgrid_init_me(init_obj, app_obj):
"""
@@ -92,14 +102,19 @@ class TclCommandAlignDrillGrid(TclCommandSignaled):
for col in range(args['columns']):
point = Point(currentx + gridoffsetx, currenty + gridoffsety)
drills.append({"point": point, "tool": "1"})
drills.append(point)
currentx = currentx + args['gridx']
currenty = currenty + args['gridy']
init_obj.tools = tools
init_obj.drills = drills
init_obj.tools = {
'1': {
'tooldia': tooldia,
'drills': drills,
'solid_geometry': []
}
}
init_obj.create_geometry()
# Create the new object
self.app.new_object("excellon", args['outname'], aligndrillgrid_init_me)
self.app.app_obj.new_object("excellon", outname, aligndrillgrid_init_me, plot=False)

View File

@@ -0,0 +1,105 @@
import collections
from tclCommands.TclCommand import TclCommand
from shapely.ops import unary_union
import gettext
import appTranslation as fcTranslate
import builtins
fcTranslate.apply_language('strings')
if '_' not in builtins.__dict__:
_ = gettext.gettext
class TclCommandBbox(TclCommand):
"""
Tcl shell command to follow a Gerber file
"""
# array of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon)
aliases = ['bounding_box', 'bbox']
description = '%s %s' % ("--", "Creates a rectangular Geometry object that surrounds the object.")
# 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([
('outname', str),
('margin', float),
('rounded', str)
])
# array of mandatory options for current Tcl command: required = {'name','outname'}
required = ['name']
# structured help for current command, args needs to be ordered
help = {
'main': "Creates a rectangular Geometry object that surrounds the object.",
'args': collections.OrderedDict([
('name', 'Object name for which to create bounding box. String'),
('margin', "Distance of the edges of the box to the nearest polygon."
"Float number."),
('rounded', "If the bounding box has to have rounded corners their radius is equal to the margin. "
"True (1) or False (0)."),
('outname', 'Name of the resulting Geometry object. String.')
]),
'examples': ['bbox name -outname name_bbox']
}
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 + "_bbox"
obj = self.app.collection.get_by_name(name)
if obj is None:
self.raise_tcl_error("%s: %s" % (_("Object not found"), name))
if obj.kind != 'gerber' and obj.kind != 'geometry':
self.raise_tcl_error('%s %s: %s.' % (
_("Expected GerberObject or GeometryObject, got"), name, type(obj)))
if 'margin' not in args:
args['margin'] = float(self.app.defaults["gerber_bboxmargin"])
margin = args['margin']
if 'rounded' in args:
try:
par = args['rounded'].capitalize()
except AttributeError:
par = args['rounded']
rounded = bool(eval(par))
else:
rounded = bool(eval(self.app.defaults["gerber_bboxrounded"]))
del args['name']
try:
def geo_init(geo_obj, app_obj):
# assert geo_obj.kind == 'geometry'
# Bounding box with rounded corners
geo = unary_union(obj.solid_geometry)
bounding_box = geo.envelope.buffer(float(margin))
if not rounded: # Remove rounded corners
bounding_box = bounding_box.envelope
geo_obj.solid_geometry = bounding_box
self.app.app_obj.new_object("geometry", args['outname'], geo_init, plot=False)
except Exception as e:
return "Operation failed: %s" % str(e)

View File

@@ -0,0 +1,83 @@
from tclCommands.TclCommand import TclCommand
import collections
import logging
import gettext
import appTranslation as fcTranslate
import builtins
fcTranslate.apply_language('strings')
if '_' not in builtins.__dict__:
_ = gettext.gettext
log = logging.getLogger('base')
class TclCommandBounds(TclCommand):
"""
Tcl shell command to return the bounds values for a supplied list of objects (identified by their names).
example:
"""
# List of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon)
aliases = ['get_bounds', 'bounds']
description = '%s %s' % ("--", "Return in the console a list of bounds values for a list of objects.")
# Dictionary of types from Tcl command, needs to be ordered
arg_names = collections.OrderedDict([
('objects', 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 = []
# structured help for current command, args needs to be ordered
help = {
'main': "Will return a list of bounds values, each set of bound values is "
"a list itself: [xmin, ymin, xmax, ymax] corresponding to each of the provided objects.",
'args': collections.OrderedDict([
('objects', 'A list of object names separated by comma without spaces.'),
]),
'examples': ['bounds a_obj.GTL,b_obj.DRL']
}
def execute(self, args, unnamed_args):
"""
:param args:
:param unnamed_args:
:return:
"""
obj_list = []
if 'objects' in args:
try:
obj_list = [str(obj_name) for obj_name in str(args['objects']).split(",") if obj_name != '']
except AttributeError as e:
log.debug("TclCommandBounds.execute --> %s" % str(e))
if not obj_list:
self.raise_tcl_error('%s: %s:' % (
_("Expected a list of objects names separated by comma. Got"), str(args['objects'])))
return 'fail'
else:
self.raise_tcl_error('%s: %s:' % (
_("Expected a list of objects names separated by comma. Got"), str(args['objects'])))
return 'fail'
result_list = []
for name in obj_list:
obj = self.app.collection.get_by_name(name)
xmin, ymin, xmax, ymax = obj.bounds()
result_list.append([xmin, ymin, xmax, ymax])
self.app.inform.emit('[success] %s ...' % _('TclCommand Bounds done.'))
return result_list

View File

@@ -1,10 +1,17 @@
# ##########################################################
# FlatCAM: 2D Post-processing for Manufacturing #
# File Author: Marius Adrian Stanciu (c) #
# Date: 8/17/2019 #
# MIT Licence #
# ##########################################################
from tclCommands.TclCommand import TclCommand
from ObjectCollection import *
import collections
class TclCommandClearShell(TclCommand):
"""
Tcl shell command to creates a circle in the given Geometry object.
Tcl shell command to clear the text in the Tcl Shell browser.
example:
@@ -13,6 +20,8 @@ class TclCommandClearShell(TclCommand):
# List of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon)
aliases = ['clear']
description = '%s %s' % ("--", "Clear the text in the Tcl Shell.")
# Dictionary of types from Tcl command, needs to be ordered
arg_names = collections.OrderedDict([
@@ -28,10 +37,10 @@ class TclCommandClearShell(TclCommand):
# structured help for current command, args needs to be ordered
help = {
'main': "Clear the text in the Tcl Shell browser.",
'main': "Clear the text in the Tcl Shell.",
'args': collections.OrderedDict([
]),
'examples': []
'examples': ['clear']
}
def execute(self, args, unnamed_args):

View File

@@ -1,6 +1,8 @@
from ObjectCollection import *
from tclCommands.TclCommand import TclCommandSignaled
import collections
from copy import deepcopy
class TclCommandCncjob(TclCommandSignaled):
"""
@@ -17,6 +19,8 @@ class TclCommandCncjob(TclCommandSignaled):
# array of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon)
aliases = ['cncjob']
description = '%s %s' % ("--", "Generates a CNC Job object from a Geometry Object.")
# dictionary of types from Tcl command, needs to be ordered
arg_names = collections.OrderedDict([
('name', str)
@@ -24,45 +28,58 @@ class TclCommandCncjob(TclCommandSignaled):
# dictionary of types from Tcl command, needs to be ordered , this is for options like -optionname value
option_types = collections.OrderedDict([
('dia', float),
('z_cut', float),
('z_move', float),
('feedrate', float),
('feedrate_z', float),
('feedrate_rapid', float),
('tooldia', float),
('spindlespeed', int),
('multidepth', bool),
('extracut', bool),
('depthperpass', float),
('extracut_length', float),
('dpp', float),
('toolchangez', float),
('toolchangexy', str),
('startz', float),
('endz', float),
('ppname_g', str),
('endxy', str),
('spindlespeed', int),
('dwelltime', float),
('pp', str),
('muted', str),
('outname', str)
])
# array of mandatory options for current Tcl command: required = {'name','outname'}
required = ['name']
required = []
# structured help for current command, args needs to be ordered
help = {
'main': "Generates a CNC Job from a Geometry Object.",
'main': "Generates a CNC Job object from a Geometry Object.",
'args': collections.OrderedDict([
('name', 'Name of the source object.'),
('tooldia', 'Tool diameter to show on screen.'),
('dia', 'Tool diameter to show on screen.'),
('z_cut', 'Z-axis cutting position.'),
('z_move', 'Z-axis moving position.'),
('feedrate', 'Moving speed when cutting.'),
('feedrate', 'Moving speed on X-Y plane when cutting.'),
('feedrate_z', 'Moving speed on Z plane when cutting.'),
('feedrate_rapid', 'Rapid moving at speed when cutting.'),
('spindlespeed', 'Speed of the spindle in rpm (example: 4000).'),
('multidepth', 'Use or not multidepth cnccut. (True or False)'),
('depthperpass', 'Height of one layer for multidepth.'),
('extracut', 'Use or not an extra cnccut over the first point in path,in the job end (example: True)'),
('toolchange', 'Enable tool changes (example: True).'),
('toolchangez', 'Z distance for toolchange (example: 30.0).'),
('toolchangexy', 'X, Y coordonates for toolchange in format (x, y) (example: (2.0, 3.1) ).'),
('extracut_length', 'The value for extra cnccut over the first point in path,in the job end; float'),
('dpp', 'If present then use multidepth cnc cut. Height of one layer for multidepth.'),
('toolchangez', 'Z distance for toolchange (example: 30.0).\n'
'If used in the command then a toolchange event will be included in gcode'),
('toolchangexy', 'The X,Y coordinates at Toolchange event in format (x, y) (example: (30.0, 15.2) or '
'without parenthesis like: 0.3,1.0). WARNING: no spaces allowed in the value.'),
('startz', 'Height before the first move.'),
('endz', 'Height where the last move will park.'),
('endxy', 'The X,Y coordinates at job end in format (x, y) (example: (2.0, 1.2) or without parenthesis'
'like: 0.3,1.0). WARNING: no spaces allowed in the value.'),
('spindlespeed', 'Speed of the spindle in rpm (example: 4000).'),
('dwelltime', 'Time to pause to allow the spindle to reach the full speed.\n'
'If it is not used in command then it will not be included'),
('outname', 'Name of the resulting Geometry object.'),
('ppname_g', 'Name of the Geometry postprocessor. No quotes, case sensitive')
('pp', 'Name of the Geometry preprocessor. No quotes, case sensitive'),
('muted', 'It will not put errors in the Shell. Can be True (1) or False (0)')
]),
'examples': []
'examples': ['cncjob geo_name -dia 0.5 -z_cut -1.7 -z_move 2 -feedrate 120 -pp default']
}
def execute(self, args, unnamed_args):
@@ -75,38 +92,180 @@ class TclCommandCncjob(TclCommandSignaled):
:return: None or exception
"""
name = args['name']
name = ''
if 'muted' in args:
try:
par = args['muted'].capitalize()
except AttributeError:
par = args['muted']
muted = bool(eval(par))
else:
muted = False
try:
name = args['name']
except KeyError:
if muted is False:
self.raise_tcl_error("Object name is missing")
else:
return "fail"
if 'outname' not in args:
args['outname'] = str(name) + "_cnc"
obj = self.app.collection.get_by_name(str(name), isCaseSensitive=False)
if obj is None:
self.raise_tcl_error("Object not found: %s" % str(name))
if muted is False:
self.raise_tcl_error("Object not found: %s" % str(name))
else:
return "fail"
if not isinstance(obj, FlatCAMGeometry):
self.raise_tcl_error('Expected FlatCAMGeometry, got %s %s.' % (str(name), type(obj)))
if obj.kind != 'geometry':
if muted is False:
self.raise_tcl_error('Expected GeometryObject, got %s %s.' % (str(name), type(obj)))
else:
return
args["z_cut"] = args["z_cut"] if "z_cut" in args else obj.options["cutz"]
args["z_move"] = args["z_move"] if "z_move" in args else obj.options["travelz"]
args["feedrate"] = args["feedrate"] if "feedrate" in args else obj.options["feedrate"]
args["feedrate_rapid"] = args["feedrate_rapid"] if "feedrate_rapid" in args else obj.options["feedrate_rapid"]
args["spindlespeed"] = args["spindlespeed"] if "spindlespeed" in args else None
args["tooldia"] = args["tooldia"] if "tooldia" in args else obj.options["cnctooldia"]
args["multidepth"] = args["multidepth"] if "multidepth" in args else obj.options["multidepth"]
args["depthperpass"] = args["depthperpass"] if "depthperpass" in args else obj.options["depthperpass"]
args["extracut"] = args["extracut"] if "extracut" in args else obj.options["extracut"]
args["endz"]= args["endz"] if "endz" in args else obj.options["endz"]
args["ppname_g"] = args["ppname_g"] if "ppname_g" in args else obj.options["ppname_g"]
args["dia"] = args["dia"] if "dia" in args and args["dia"] else self.app.defaults["geometry_cnctooldia"]
args["toolchange"] = True if "toolchange" in args and args["toolchange"] == 1 else False
args["toolchangez"] = args["toolchangez"] if "toolchangez" in args else obj.options["toolchangez"]
args["toolchangexy"] = args["toolchangexy"] if "toolchangexy" in args else obj.options["toolchangexy"]
args["z_cut"] = args["z_cut"] if "z_cut" in args and args["z_cut"] else self.app.defaults["geometry_cutz"]
args["z_move"] = args["z_move"] if "z_move" in args and args["z_move"] else \
self.app.defaults["geometry_travelz"]
args["feedrate"] = args["feedrate"] if "feedrate" in args and args["feedrate"] else \
self.app.defaults["geometry_feedrate"]
args["feedrate_z"] = args["feedrate_z"] if "feedrate_z" in args and args["feedrate_z"] else \
self.app.defaults["geometry_feedrate_z"]
args["feedrate_rapid"] = args["feedrate_rapid"] if "feedrate_rapid" in args and args["feedrate_rapid"] else \
self.app.defaults["geometry_feedrate_rapid"]
if "extracut_length" in args:
args["extracut"] = True
if args["extracut_length"] is None:
args["extracut_length"] = 0.0
else:
args["extracut_length"] = float(args["extracut_length"])
else:
args["extracut"] = self.app.defaults["geometry_extracut"]
args["extracut_length"] = self.app.defaults["geometry_extracut_length"]
if "dpp" in args:
args["multidepth"] = True
if args["dpp"] is None:
args["dpp"] = self.app.defaults["geometry_depthperpass"]
else:
args["dpp"] = float(args["dpp"])
else:
args["multidepth"] = self.app.defaults["geometry_multidepth"]
args["dpp"] = self.app.defaults["geometry_depthperpass"]
args["startz"] = args["startz"] if "startz" in args and args["startz"] else \
self.app.defaults["geometry_startz"]
args["endz"] = args["endz"] if "endz" in args and args["endz"] else self.app.defaults["geometry_endz"]
if "endxy" in args and args["endxy"]:
args["endxy"] = args["endxy"]
else:
if self.app.defaults["geometry_endxy"]:
args["endxy"] = str(self.app.defaults["geometry_endxy"])
else:
args["endxy"] = '0, 0'
if len(eval(args["endxy"])) != 2:
self.raise_tcl_error("The entered value for 'endxy' needs to have the format x,y or "
"in format (x, y) - no spaces allowed. But always two comma separated values.")
args["spindlespeed"] = args["spindlespeed"] if "spindlespeed" in args and args["spindlespeed"] != 0 else None
if 'dwelltime' in args:
args["dwell"] = True
if args['dwelltime'] is None:
args["dwelltime"] = float(obj.options["dwelltime"])
else:
args["dwelltime"] = float(args['dwelltime'])
else:
args["dwell"] = self.app.defaults["geometry_dwell"]
args["dwelltime"] = self.app.defaults["geometry_dwelltime"]
args["pp"] = args["pp"] if "pp" in args and args["pp"] else self.app.defaults["geometry_ppname_g"]
if "toolchangez" in args:
args["toolchange"] = True
if args["toolchangez"] is not None:
args["toolchangez"] = args["toolchangez"]
else:
args["toolchangez"] = self.app.defaults["geometry_toolchangez"]
else:
args["toolchange"] = self.app.defaults["geometry_toolchange"]
args["toolchangez"] = self.app.defaults["geometry_toolchangez"]
if "toolchangexy" in args and args["toolchangexy"]:
args["toolchangexy"] = args["toolchangexy"]
else:
if self.app.defaults["geometry_toolchangexy"]:
args["toolchangexy"] = str(self.app.defaults["geometry_toolchangexy"])
else:
args["toolchangexy"] = '0, 0'
if len(eval(args["toolchangexy"])) != 2:
self.raise_tcl_error("The entered value for 'toolchangexy' needs to have the format x,y or "
"in format (x, y) - no spaces allowed. But always two comma separated values.")
del args['name']
for arg in args:
if arg == "toolchange_xy" or arg == "spindlespeed" or arg == "startz":
continue
else:
if args[arg] is None:
print("None parameters: %s is None" % arg)
if muted is False:
self.raise_tcl_error('One of the command parameters that have to be not None, is None.\n'
'The parameter that is None is in the default values found in the list \n'
'generated by the TclCommand "list_sys geom". or in the arguments.')
else:
return
# HACK !!! Should be solved elsewhere!!!
# default option for multidepth is False
obj.options['multidepth'] = False
# obj.options['multidepth'] = False
obj.generatecncjob(use_thread=False, **args)
if not obj.multigeo:
obj.generatecncjob(use_thread=False, plot=False, **args)
else:
# Update the local_tools_dict values with the args value
local_tools_dict = deepcopy(obj.tools)
for tool_uid in list(local_tools_dict.keys()):
if 'data' in local_tools_dict[tool_uid]:
local_tools_dict[tool_uid]['data']['cutz'] = args["z_cut"]
local_tools_dict[tool_uid]['data']['travelz'] = args["z_move"]
local_tools_dict[tool_uid]['data']['feedrate'] = args["feedrate"]
local_tools_dict[tool_uid]['data']['feedrate_z'] = args["feedrate_z"]
local_tools_dict[tool_uid]['data']['feedrate_rapid'] = args["feedrate_rapid"]
local_tools_dict[tool_uid]['data']['multidepth'] = args["multidepth"]
local_tools_dict[tool_uid]['data']['extracut'] = args["extracut"]
if args["extracut"] is True:
local_tools_dict[tool_uid]['data']['extracut_length'] = args["extracut_length"]
else:
local_tools_dict[tool_uid]['data']['extracut_length'] = None
local_tools_dict[tool_uid]['data']['depthperpass'] = args["dpp"]
local_tools_dict[tool_uid]['data']['toolchange'] = args["toolchange"]
local_tools_dict[tool_uid]['data']['toolchangez'] = args["toolchangez"]
local_tools_dict[tool_uid]['data']['toolchangexy'] = args["toolchangexy"]
local_tools_dict[tool_uid]['data']['startz'] = args["startz"]
local_tools_dict[tool_uid]['data']['endz'] = args["endz"]
local_tools_dict[tool_uid]['data']['endxy'] = args["endxy"]
local_tools_dict[tool_uid]['data']['spindlespeed'] = args["spindlespeed"]
local_tools_dict[tool_uid]['data']['dwell'] = args["dwell"]
local_tools_dict[tool_uid]['data']['dwelltime'] = args["dwelltime"]
local_tools_dict[tool_uid]['data']['ppname_g'] = args["pp"]
obj.mtool_gen_cncjob(
outname=args['outname'],
tools_dict=local_tools_dict,
tools_in_use=[],
use_thread=False,
plot=False)
# self.raise_tcl_error('The object is a multi-geo geometry which is not supported in cncjob Tcl Command')

View File

@@ -0,0 +1,305 @@
from tclCommands.TclCommand import TclCommand
import collections
import logging
import gettext
import appTranslation as fcTranslate
import builtins
fcTranslate.apply_language('strings')
if '_' not in builtins.__dict__:
_ = gettext.gettext
log = logging.getLogger('base')
class TclCommandCopperClear(TclCommand):
"""
Clear the non-copper areas.
"""
# Array of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon)
aliases = ['ncc_clear', 'ncc']
description = '%s %s' % ("--", "Clear excess copper.")
# 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([
('tooldia', str),
('overlap', float),
('order', str),
('margin', float),
('method', str),
('connect', str),
('contour', str),
('offset', float),
('rest', str),
('all', int),
('ref', str),
('box', str),
('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': "Clear excess copper in polygons. Basically it's a negative Paint.",
'args': collections.OrderedDict([
('name', 'Name of the source Geometry object. String.'),
('tooldia', 'Diameter of the tool to be used. Can be a comma separated list of diameters.\n'
'WARNING: No space is allowed between tool diameters. E.g: correct: 0.5,1 / incorrect: 0.5, 1'),
('overlap', 'Percentage of tool diameter to overlap current pass over previous pass. Float [0, 99.9999]\n'
'E.g: for a 25% from tool diameter overlap use -overlap 25'),
('margin', 'Bounding box margin. Float number.'),
('order', 'Can have the values: "no", "fwd" and "rev". String.'
'It is useful when there are multiple tools in tooldia parameter.'
'"no" -> the order used is the one provided.'
'"fwd" -> tools are ordered from smallest to biggest.'
'"rev" -> tools are ordered from biggest to smallest.'),
('method', 'Algorithm for copper clearing. Can be: "standard", "seed" or "lines".'),
('connect', 'Draw lines to minimize tool lifts. True (1) or False (0)'),
('contour', 'Cut around the perimeter of the painting. True (1) or False (0)'),
('rest', 'Use rest-machining. True (1) or False (0)'),
('offset', 'If used, the copper clearing will finish to a distance from copper features. Float number.'),
('all', 'If used will copper clear the whole object. Either "-all" or "-box <value>" has to be used.'),
('box', 'Name of the object to be used as reference. Either "-all" or "-box <value>" has to be used. '
'String.'),
('outname', 'Name of the resulting Geometry object. String. No spaces.'),
]),
'examples': ["ncc obj_name -tooldia 0.3,1 -overlap 10 -margin 1.0 -method 'lines' -all"]
}
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']
# Get source object.
try:
obj = self.app.collection.get_by_name(str(name))
except Exception as e:
log.debug("TclCommandCopperClear.execute() --> %s" % str(e))
self.raise_tcl_error("%s: %s" % (_("Could not retrieve object"), name))
return "Could not retrieve object: %s" % name
if obj is None:
return "Object not found: %s" % name
if 'tooldia' in args:
tooldia = str(args['tooldia'])
else:
tooldia = str(self.app.defaults["tools_ncc_tools"])
if 'overlap' in args:
overlap = float(args['overlap']) / 100.0
else:
overlap = float(self.app.defaults["tools_ncc_overlap"]) / 100.0
if 'order' in args:
order = args['order']
else:
order = str(self.app.defaults["tools_ncc_order"])
if 'margin' in args:
margin = float(args['margin'])
else:
margin = float(self.app.defaults["tools_ncc_margin"])
if 'method' in args:
method = args['method']
if method == "standard":
method_data = 0
elif method == "seed":
method_data = 1
else:
method_data = 2
else:
method = str(self.app.defaults["tools_ncc_method"])
method_data = method
if 'connect' in args:
try:
par = args['connect'].capitalize()
except AttributeError:
par = args['connect']
connect = bool(eval(par))
else:
connect = bool(eval(str(self.app.defaults["tools_ncc_connect"])))
if 'contour' in args:
try:
par = args['contour'].capitalize()
except AttributeError:
par = args['contour']
contour = bool(eval(par))
else:
contour = bool(eval(str(self.app.defaults["tools_ncc_contour"])))
offset = 0.0
if 'offset' in args:
offset = float(args['offset'])
has_offset = True
else:
has_offset = False
try:
tools = [float(eval(dia)) for dia in tooldia.split(",") if dia != '']
except AttributeError:
tools = [float(tooldia)]
if 'rest' in args:
try:
par = args['rest'].capitalize()
except AttributeError:
par = args['rest']
rest = bool(eval(par))
else:
rest = bool(eval(str(self.app.defaults["tools_ncc_rest"])))
if 'outname' in args:
outname = args['outname']
else:
if rest is True:
outname = name + "_ncc"
else:
outname = name + "_ncc_rm"
# used only to have correct information's in the obj.tools[tool]['data'] dict
if "all" in args:
select = 0 # 'ITSELF
else:
select = 2 # 'REFERENCE Object'
# store here the default data for Geometry Data
default_data = {}
default_data.update({
"name": outname,
"plot": False,
"cutz": self.app.defaults["geometry_cutz"],
"vtipdia": float(self.app.defaults["geometry_vtipdia"]),
"vtipangle": float(self.app.defaults["geometry_vtipangle"]),
"travelz": self.app.defaults["geometry_travelz"],
"feedrate": self.app.defaults["geometry_feedrate"],
"feedrate_z": self.app.defaults["geometry_feedrate_z"],
"feedrate_rapid": self.app.defaults["geometry_feedrate_rapid"],
"dwell": self.app.defaults["geometry_dwell"],
"dwelltime": self.app.defaults["geometry_dwelltime"],
"multidepth": self.app.defaults["geometry_multidepth"],
"ppname_g": self.app.defaults["geometry_ppname_g"],
"depthperpass": self.app.defaults["geometry_depthperpass"],
"extracut": self.app.defaults["geometry_extracut"],
"extracut_length": self.app.defaults["geometry_extracut_length"],
"toolchange": self.app.defaults["geometry_toolchange"],
"toolchangez": self.app.defaults["geometry_toolchangez"],
"endz": self.app.defaults["geometry_endz"],
"endxy": self.app.defaults["geometry_endxy"],
"spindlespeed": self.app.defaults["geometry_spindlespeed"],
"toolchangexy": self.app.defaults["geometry_toolchangexy"],
"startz": self.app.defaults["geometry_startz"],
"area_exclusion": self.app.defaults["geometry_area_exclusion"],
"area_shape": self.app.defaults["geometry_area_shape"],
"area_strategy": self.app.defaults["geometry_area_strategy"],
"area_overz": float(self.app.defaults["geometry_area_overz"]),
"tooldia": tooldia,
"tools_ncc_operation": self.app.defaults["tools_ncc_operation"],
"tools_ncc_margin": margin,
"tools_ncc_method": method_data,
"tools_ncc_ref": select,
"tools_ncc_connect": connect,
"tools_ncc_contour": contour,
"tools_ncc_overlap": overlap,
"tools_ncc_offset_choice": self.app.defaults["tools_ncc_offset_choice"],
"tools_ncc_offset_value": self.app.defaults["tools_ncc_offset_value"],
"tools_ncc_milling_type": self.app.defaults["tools_ncc_milling_type"]
})
ncc_tools = {}
tooluid = 0
for tool in tools:
tooluid += 1
ncc_tools.update({
int(tooluid): {
'tooldia': float('%.*f' % (obj.decimals, tool)),
'offset': 'Path',
'offset_value': 0.0,
'type': 'Iso',
'tool_type': 'C1',
'data': dict(default_data),
'solid_geometry': []
}
})
ncc_tools[int(tooluid)]['data']['tooldia'] = self.app.dec_format(tool, obj.decimals)
# Non-Copper clear all polygons in the non-copper clear object
if 'all' in args:
self.app.ncclear_tool.clear_copper_tcl(ncc_obj=obj,
select_method=0, # ITSELF
ncctooldia=tooldia,
overlap=overlap,
order=order,
margin=margin,
has_offset=has_offset,
offset=offset,
method=method,
outname=outname,
connect=connect,
contour=contour,
rest=rest,
tools_storage=ncc_tools,
plot=False,
run_threaded=False)
return
# Non-Copper clear all polygons found within the box object from the the non_copper cleared object
if 'box' in args: # Reference Object
box_name = args['box']
# Get box source object.
try:
box_obj = self.app.collection.get_by_name(str(box_name))
except Exception as e:
log.debug("TclCommandCopperClear.execute() --> %s" % str(e))
self.raise_tcl_error("%s: %s" % (_("Could not retrieve object"), name))
return "Could not retrieve object: %s" % name
self.app.ncclear_tool.clear_copper_tcl(ncc_obj=obj,
sel_obj=box_obj,
select_method=2, # REFERENCE OBJECT
ncctooldia=tooldia,
overlap=overlap,
order=order,
margin=margin,
has_offset=has_offset,
offset=offset,
method=method,
outname=outname,
connect=connect,
contour=contour,
rest=rest,
tools_storage=ncc_tools,
plot=False,
run_threaded=False)
return
# if the program reached this then it's an error because neither -all or -box <value> was used.
self.raise_tcl_error('%s' % _("Expected either -box <value> or -all."))
return "Expected either -box <value> or -all. Copper clearing failed."

View File

@@ -1,8 +1,13 @@
from ObjectCollection import *
from tclCommands.TclCommand import TclCommand
from shapely.ops import cascaded_union
import collections
import logging
from shapely.ops import unary_union
from shapely.geometry import LineString
log = logging.getLogger('base')
class TclCommandCutout(TclCommand):
"""
@@ -16,6 +21,8 @@ class TclCommandCutout(TclCommand):
# names for backward compatibility (add_poly, add_polygon)
aliases = ['cutout']
description = '%s %s' % ("--", "Creates board cutout from an object (Gerber or Geometry) with a rectangular shape.")
# Dictionary of types from Tcl command, needs to be ordered
arg_names = collections.OrderedDict([
('name', str),
@@ -27,7 +34,8 @@ class TclCommandCutout(TclCommand):
('dia', float),
('margin', float),
('gapsize', float),
('gaps', str)
('gaps', str),
('outname', str)
])
# array of mandatory options for current Tcl command: required = {'name','outname'}
@@ -35,15 +43,16 @@ class TclCommandCutout(TclCommand):
# structured help for current command, args needs to be ordered
help = {
'main': 'Creates board cutout from an object (Gerber or Geometry) with a rectangular shape',
'main': 'Creates board cutout from an object (Gerber or Geometry) with a rectangular shape.',
'args': collections.OrderedDict([
('name', 'Name of the object.'),
('dia', 'Tool diameter. Default = 0.1'),
('margin', 'Margin over bounds. Default = 0.001'),
('gapsize', 'Size of gap. Default = 0.1'),
('gaps', "Type of gaps. Can be: 'tb' = top-bottom, 'lr' = left-right and '4' = one each side. Default = 4"),
('dia', 'Tool diameter.'),
('margin', 'Margin over bounds.'),
('gapsize', 'Size of gap.'),
('gaps', "Type of gaps. Can be: 'tb' = top-bottom, 'lr' = left-right and '4' = one each side."),
('outname', 'Name of the object to create.')
]),
'examples': []
'examples': ['cutout new_geo -dia 1.2 -margin 0.1 -gapsize 1 -gaps "tb" -outname cut_geo']
}
def execute(self, args, unnamed_args):
@@ -62,24 +71,29 @@ class TclCommandCutout(TclCommand):
return
if 'margin' in args:
margin_par = args['margin']
margin_par = float(args['margin'])
else:
margin_par = 0.001
margin_par = float(self.app.defaults["tools_cutout_margin"])
if 'dia' in args:
dia_par = args['dia']
dia_par = float(args['dia'])
else:
dia_par = 0.1
dia_par = float(self.app.defaults["tools_cutout_tooldia"])
if 'gaps' in args:
gaps_par = args['gaps']
else:
gaps_par = 4
gaps_par = str(self.app.defaults["tools_cutout_gaps_ff"])
if 'gapsize' in args:
gapsize_par = args['gapsize']
gapsize_par = float(args['gapsize'])
else:
gapsize_par = 0.1
gapsize_par = float(self.app.defaults["tools_cutout_gapsize"])
if 'outname' in args:
outname = args['outname']
else:
outname = name + "_cutout"
try:
obj = self.app.collection.get_by_name(str(name))
@@ -120,10 +134,10 @@ class TclCommandCutout(TclCommand):
[pts[6], pts[7], pts[8]],
[pts[9], pts[10], pts[11]]]}
cuts = cases[gaps_par]
geo_obj.solid_geometry = cascaded_union([LineString(segment) for segment in cuts])
geo_obj.solid_geometry = unary_union([LineString(segment) for segment in cuts])
try:
self.app.new_object("geometry", name + "_cutout", geo_init_me)
self.app.app_obj.new_object("geometry", outname, geo_init_me, plot=False)
self.app.inform.emit("[success] Rectangular-form Cutout operation finished.")
except Exception as e:
return "Operation failed: %s" % str(e)

View File

@@ -1,6 +1,7 @@
from ObjectCollection import *
from tclCommands.TclCommand import TclCommand
import collections
class TclCommandDelete(TclCommand):
"""
@@ -11,7 +12,9 @@ class TclCommandDelete(TclCommand):
"""
# List of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon)
aliases = ['delete']
aliases = ['delete', 'del']
description = '%s %s' % ("--", "Deletes the given object. If no name is given will delete all objects.")
# Dictionary of types from Tcl command, needs to be ordered
arg_names = collections.OrderedDict([
@@ -20,19 +23,25 @@ class TclCommandDelete(TclCommand):
# Dictionary of types from Tcl command, needs to be ordered , this is for options like -optionname value
option_types = collections.OrderedDict([
('f', str)
])
# array of mandatory options for current Tcl command: required = {'name','outname'}
required = ['name']
required = []
# structured help for current command, args needs to be ordered
help = {
'main': 'Deletes the given object.',
'main': 'Deletes the given object. If no name is given will delete all objects.',
'args': collections.OrderedDict([
('name', 'Name of the Object.'),
('f', 'Use this parameter to force deletion.\n'
'Can be used without value which will be auto assumed to be True.\n'
'Or it can have a value: True (1) or False (0).')
]),
'examples': []
'examples': ['del new_geo -f True\n'
'delete new_geo -f 1\n'
'del new_geo -f\n'
'del new_geo']
}
def execute(self, args, unnamed_args):
@@ -42,13 +51,39 @@ class TclCommandDelete(TclCommand):
:param unnamed_args:
:return:
"""
obj_name = args['name']
obj_name = None
try:
# deselect all to avoid delete selected object when run delete from shell
self.app.collection.set_all_inactive()
self.app.collection.set_active(str(obj_name))
self.app.on_delete() # Todo: This is an event handler for the GUI... bad?
except Exception as e:
return "Command failed: %s" % str(e)
obj_name = args['name']
delete_all = False
except KeyError:
delete_all = True
is_forced = False
if 'f' in args:
try:
if args['f'] is None:
is_forced = True
else:
try:
par = args['f'].capitalize()
except AttributeError:
par = args['f']
is_forced = bool(eval(par))
except KeyError:
is_forced = True
if delete_all is False:
try:
# deselect all to avoid delete selected object when run delete from shell
self.app.collection.set_all_inactive()
self.app.collection.set_active(str(obj_name))
self.app.on_delete(force_deletion=is_forced)
except Exception as e:
return "Command failed: %s" % str(e)
else:
try:
self.app.collection.set_all_active()
self.app.on_delete(force_deletion=is_forced)
except Exception as e:
return "Command failed: %s" % str(e)

View File

@@ -1,6 +1,16 @@
from ObjectCollection import *
from tclCommands.TclCommand import TclCommandSignaled
import collections
import math
import gettext
import appTranslation as fcTranslate
import builtins
fcTranslate.apply_language('strings')
if '_' not in builtins.__dict__:
_ = gettext.gettext
class TclCommandDrillcncjob(TclCommandSignaled):
"""
@@ -10,6 +20,8 @@ class TclCommandDrillcncjob(TclCommandSignaled):
# array of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon)
aliases = ['drillcncjob']
description = '%s %s' % ("--", "Generates a Drill CNC Job object from a Excellon Object.")
# dictionary of types from Tcl command, needs to be ordered
arg_names = collections.OrderedDict([
('name', str)
@@ -17,18 +29,24 @@ class TclCommandDrillcncjob(TclCommandSignaled):
# dictionary of types from Tcl command, needs to be ordered , this is for options like -optionname value
option_types = collections.OrderedDict([
('tools', str),
('drilled_dias', str),
('drillz', float),
('dpp', float),
('travelz', float),
('feedrate', float),
('feedrate_z', float),
('feedrate_rapid', float),
('spindlespeed', int),
('toolchange', bool),
('toolchangez', float),
('toolchangexy', str),
('startz', float),
('endz', float),
('ppname_e', str),
('outname', str),
('opt_type', str)
('endxy', str),
('dwelltime', float),
('pp', str),
('opt_type', str),
('diatol', float),
('muted', str),
('outname', str)
])
# array of mandatory options for current Tcl command: required = {'name','outname'}
@@ -39,24 +57,41 @@ class TclCommandDrillcncjob(TclCommandSignaled):
'main': "Generates a Drill CNC Job from a Excellon Object.",
'args': collections.OrderedDict([
('name', 'Name of the source object.'),
('tools', 'Comma separated indexes of tools (example: 1,3 or 2) or select all if not specified.'),
('drillz', 'Drill depth into material (example: -2.0).'),
('drilled_dias',
'Comma separated tool diameters of the drills to be drilled (example: 0.6,1.0 or 3.125). '
'WARNING: No space allowed'),
('drillz', 'Drill depth into material (example: -2.0). Negative value.'),
('dpp', 'Progressive drilling into material with a specified step (example: 0.7). Positive value.'),
('travelz', 'Travel distance above material (example: 2.0).'),
('feedrate', 'Drilling feed rate.'),
('feedrate_z', 'Drilling feed rate. It is the speed on the Z axis.'),
('feedrate_rapid', 'Rapid drilling feed rate.'),
('spindlespeed', 'Speed of the spindle in rpm (example: 4000).'),
('toolchange', 'Enable tool changes (example: True).'),
('toolchangez', 'Z distance for toolchange (example: 30.0).'),
('toolchangexy', 'X, Y coordonates for toolchange in format (x, y) (example: (2.0, 3.1) ).'),
('endz', 'Z distance at job end (example: 30.0).'),
('ppname_e', 'This is the Excellon postprocessor name: case_sensitive, no_quotes'),
('outname', 'Name of the resulting Geometry object.'),
('opt_type', 'Name of move optimization type. R by default from Rtree or '
'T from Travelling Salesman Algorithm')
('toolchangez', 'Z distance for toolchange (example: 30.0).\n'
'If used in the command then a toolchange event will be included in gcode'),
('toolchangexy', 'The X,Y coordinates at Toolchange event in format (x, y) (example: (30.0, 15.2) or '
'without parenthesis like: 0.3,1.0). WARNING: no spaces allowed in the value.'),
('startz', 'The Z coordinate at job start (example: 30.0).'),
('endz', 'The Z coordinate at job end (example: 30.0).'),
('endxy', 'The X,Y coordinates at job end in format (x, y) (example: (2.0, 1.2) or without parenthesis'
'like: 0.3,1.0). WARNING: no spaces allowed in the value.'),
('dwelltime', 'Time to pause to allow the spindle to reach the full speed.\n'
'If it is not used in command then it will not be included'),
('pp', 'This is the Excellon preprocessor name: case_sensitive, no_quotes'),
('opt_type', 'Name of move optimization type. B by default for Basic OR-Tools, M for Metaheuristic OR-Tools'
'T from Travelling Salesman Algorithm. B and M works only for 64bit version of FlatCAM and '
'T works only for 32bit version of FlatCAM'),
('diatol', 'Tolerance. Percentange (0.0 ... 100.0) within which dias in drilled_dias will be judged to be '
'the same as the ones in the tools from the Excellon object. E.g: if in drill_dias we have a '
'diameter with value 1.0, in the Excellon we have a tool with dia = 1.05 and we set a tolerance '
'diatol = 5.0 then the drills with the dia = (0.95 ... 1.05) '
'in Excellon will be processed. Float number.'),
('muted', 'It will not put errors in the Shell or status bar. Can be True (1) or False (0).'),
('outname', 'Name of the resulting Geometry object.')
]),
'examples': ['drillcncjob test.TXT -drillz -1.5 -travelz 14 -feedrate 222 -feedrate_rapid 456 -spindlespeed 777'
' -toolchange True -toolchangez 33 -endz 22 -ppname_e default\n'
'Usage of -feedrate_rapid matter only when the posptocessor is using it, like -marlin-.']
'examples': ['drillcncjob test.TXT -drillz -1.5 -travelz 14 -feedrate_z 222 -feedrate_rapid 456 '
'-spindlespeed 777 -toolchangez 33 -endz 22 -pp default\n'
'Usage of -feedrate_rapid matter only when the preprocessor is using it, like -marlin-.',
'drillcncjob test.DRL -drillz -1.7 -dpp 0.5 -travelz 2 -feedrate_z 800 -endxy 3,3']
}
def execute(self, args, unnamed_args):
@@ -71,15 +106,31 @@ class TclCommandDrillcncjob(TclCommandSignaled):
name = args['name']
obj = self.app.collection.get_by_name(name)
if 'outname' not in args:
args['outname'] = name + "_cnc"
obj = self.app.collection.get_by_name(name)
if obj is None:
self.raise_tcl_error("Object not found: %s" % name)
if 'muted' in args:
try:
par = args['muted'].capitalize()
except AttributeError:
par = args['muted']
muted = bool(eval(par))
else:
muted = False
if not isinstance(obj, FlatCAMExcellon):
self.raise_tcl_error('Expected FlatCAMExcellon, got %s %s.' % (name, type(obj)))
if obj is None:
if muted is False:
self.raise_tcl_error("Object not found: %s" % name)
else:
return "fail"
if obj.kind != 'excellon':
if muted is False:
self.raise_tcl_error('Expected ExcellonObject, got %s %s.' % (name, type(obj)))
else:
return "fail"
xmin = obj.options['xmin']
ymin = obj.options['ymin']
@@ -87,42 +138,213 @@ class TclCommandDrillcncjob(TclCommandSignaled):
ymax = obj.options['ymax']
def job_init(job_obj, app_obj):
# tools = args["tools"] if "tools" in args else 'all'
drillz = args["drillz"] if "drillz" in args else obj.options["drillz"]
job_obj.z_move = args["travelz"] if "travelz" in args else obj.options["travelz"]
job_obj.feedrate = args["feedrate"] if "feedrate" in args else obj.options["feedrate"]
job_obj.feedrate_rapid = args["feedrate_rapid"] \
if "feedrate_rapid" in args else obj.options["feedrate_rapid"]
try:
if 'drilled_dias' in args and args['drilled_dias'] != 'all':
diameters = [x.strip() for x in args['drilled_dias'].split(",") if x != '']
nr_diameters = len(diameters)
job_obj.spindlespeed = args["spindlespeed"] if "spindlespeed" in args else None
job_obj.pp_excellon_name = args["ppname_e"] if "ppname_e" in args \
else obj.options["ppname_e"]
req_tools = set()
for tool in obj.tools:
for req_dia in diameters:
obj_dia_form = float('%.*f' % (obj.decimals, float(obj.tools[tool]["C"])))
req_dia_form = float('%.*f' % (obj.decimals, float(req_dia)))
job_obj.coords_decimals = int(self.app.defaults["cncjob_coords_decimals"])
job_obj.fr_decimals = int(self.app.defaults["cncjob_fr_decimals"])
if 'diatol' in args:
tolerance = args['diatol'] / 100
tolerance = 0.0 if tolerance < 0.0 else tolerance
tolerance = 1.0 if tolerance > 1.0 else tolerance
if math.isclose(obj_dia_form, req_dia_form, rel_tol=tolerance):
req_tools.add(tool)
nr_diameters -= 1
else:
if obj_dia_form == req_dia_form:
req_tools.add(tool)
nr_diameters -= 1
if nr_diameters > 0:
if muted is False:
self.raise_tcl_error("One or more tool diameters of the drills to be drilled passed to the "
"TclCommand are not actual tool diameters in the Excellon object.")
else:
return "fail"
# make a string of diameters separated by comma; this is what generate_from_excellon_by_tool() is
# expecting as tools parameter
tools = ','.join(req_tools)
# no longer needed
del args['drilled_dias']
del args['diatol']
# Split and put back. We are passing the whole dictionary later.
# args['milled_dias'] = [x.strip() for x in args['tools'].split(",")]
else:
tools = 'all'
except Exception as e:
tools = 'all'
if muted is False:
self.raise_tcl_error("Bad tools: %s" % str(e))
else:
return "fail"
used_tools_info = []
used_tools_info.insert(0, [_("Tool_nr"), _("Diameter"), _("Drills_Nr"), _("Slots_Nr")])
# populate the information's list for used tools
if tools == 'all':
sort = []
for k, v in list(obj.tools.items()):
sort.append((k, v.get('tooldia')))
sorted_tools = sorted(sort, key=lambda t1: t1[1])
use_tools = [i[0] for i in sorted_tools]
for tool_no in use_tools:
tool_dia_used = obj.tools[tool_no]['tooldia']
drill_cnt = 0 # variable to store the nr of drills per tool
slot_cnt = 0 # variable to store the nr of slots per tool
# Find no of drills for the current tool
if 'drills' in obj.tools[tool_no] and obj.tools[tool_no]['drills']:
drill_cnt = len(obj.tools[tool_no]['drills'])
# Find no of slots for the current tool
if 'slots' in obj.tools[tool_no] and obj.tools[tool_no]['slots']:
slot_cnt = len(obj.tools[tool_no]['slots'])
used_tools_info.append([str(tool_no), str(tool_dia_used), str(drill_cnt), str(slot_cnt)])
drillz = args["drillz"] if "drillz" in args and args["drillz"] is not None else \
obj.options["tools_drill_cutz"]
if "toolchangez" in args:
toolchange = True
if args["toolchangez"] is not None:
toolchangez = args["toolchangez"]
else:
toolchangez = obj.options["tools_drill_toolchangez"]
else:
toolchange = self.app.defaults["tools_drill_toolchange"]
toolchangez = float(self.app.defaults["tools_drill_toolchangez"])
if "toolchangexy" in args and args["tools_drill_toolchangexy"]:
xy_toolchange = args["toolchangexy"]
else:
if self.app.defaults["tools_drill_toolchangexy"]:
xy_toolchange = str(self.app.defaults["tools_drill_toolchangexy"])
else:
xy_toolchange = '0, 0'
if len(eval(xy_toolchange)) != 2:
self.raise_tcl_error("The entered value for 'toolchangexy' needs to have the format x,y or "
"in format (x, y) - no spaces allowed. But always two comma separated values.")
endz = args["endz"] if "endz" in args and args["endz"] is not None else \
self.app.defaults["tools_drill_endz"]
if "endxy" in args and args["endxy"]:
xy_end = args["endxy"]
else:
if self.app.defaults["tools_drill_endxy"]:
xy_end = str(self.app.defaults["tools_drill_endxy"])
else:
xy_end = '0, 0'
if len(eval(xy_end)) != 2:
self.raise_tcl_error("The entered value for 'xy_end' needs to have the format x,y or "
"in format (x, y) - no spaces allowed. But always two comma separated values.")
opt_type = args["opt_type"] if "opt_type" in args and args["opt_type"] else 'B'
# ##########################################################################################
# ################# Set parameters #########################################################
# ##########################################################################################
job_obj.origin_kind = 'excellon'
job_obj.options['Tools_in_use'] = used_tools_info
job_obj.options['type'] = 'Excellon'
toolchange = True if "toolchange" in args and args["toolchange"] == 1 else False
toolchangez = args["toolchangez"] if "toolchangez" in args else obj.options["toolchangez"]
job_obj.toolchangexy = args["toolchangexy"] if "toolchangexy" in args else obj.options["toolchangexy"]
pp_excellon_name = args["pp"] if "pp" in args and args["pp"] else self.app.defaults["tools_drill_ppname_e"]
job_obj.pp_excellon_name = pp_excellon_name
job_obj.options['ppname_e'] = pp_excellon_name
if 'dpp' in args:
job_obj.multidepth = True
if args['dpp'] is not None:
job_obj.z_depthpercut = float(args['dpp'])
else:
job_obj.z_depthpercut = float(obj.options["dpp"])
else:
job_obj.multidepth = self.app.defaults["tools_drill_multidepth"]
job_obj.z_depthpercut = self.app.defaults["tools_drill_depthperpass"]
job_obj.z_move = float(args["travelz"]) if "travelz" in args and args["travelz"] else \
self.app.defaults["tools_drill_travelz"]
job_obj.feedrate = float(args["feedrate_z"]) if "feedrate_z" in args and args["feedrate_z"] else \
self.app.defaults["tools_drill_feedrate_z"]
job_obj.z_feedrate = float(args["feedrate_z"]) if "feedrate_z" in args and args["feedrate_z"] else \
self.app.defaults["tools_drill_feedrate_z"]
job_obj.feedrate_rapid = float(args["feedrate_rapid"]) \
if "feedrate_rapid" in args and args["feedrate_rapid"] else \
self.app.defaults["tools_drill_feedrate_rapid"]
job_obj.spindlespeed = float(args["spindlespeed"]) if "spindlespeed" in args else None
job_obj.spindledir = self.app.defaults['tools_drill_spindlespeed']
if 'dwelltime' in args:
job_obj.dwell = True
if args['dwelltime'] is not None:
job_obj.dwelltime = float(args['dwelltime'])
else:
job_obj.dwelltime = float(self.app.defaults["tools_drill_dwelltime"])
else:
job_obj.dwell = self.app.defaults["tools_drill_dwell"]
job_obj.dwelltime = self.app.defaults["tools_drill_dwelltime"]
job_obj.toolchange_xy_type = "excellon"
job_obj.coords_decimals = int(self.app.defaults["cncjob_coords_decimals"])
job_obj.fr_decimals = int(self.app.defaults["cncjob_fr_decimals"])
job_obj.options['xmin'] = xmin
job_obj.options['ymin'] = ymin
job_obj.options['xmax'] = xmax
job_obj.options['ymax'] = ymax
endz = args["endz"] if "endz" in args else obj.options["endz"]
job_obj.z_cut = float(drillz)
job_obj.toolchange = toolchange
job_obj.xy_toolchange = xy_toolchange
job_obj.z_toolchange = float(toolchangez)
tools = args["tools"] if "tools" in args else 'all'
opt_type = args["opt_type"] if "opt_type" in args else 'B'
if "startz" in args and args["startz"] is not None:
job_obj.startz = float(args["startz"])
else:
if self.app.defaults["tools_drill_startz"]:
job_obj.startz = self.app.defaults["tools_drill_startz"]
else:
job_obj.startz = self.app.defaults["tools_drill_travelz"]
job_obj.endz = float(endz)
job_obj.xy_end = xy_end
job_obj.excellon_optimization_type = opt_type
job_obj.spindledir = self.app.defaults["tools_drill_spindledir"]
ret_val = job_obj.generate_from_excellon_by_tool(obj, tools, use_ui=False)
job_obj.source_file = ret_val
if ret_val == 'fail':
return 'fail'
job_obj.gc_start = ret_val[1]
for t_item in job_obj.exc_cnc_tools:
job_obj.exc_cnc_tools[t_item]['data']['tools_drill_offset'] = \
float(job_obj.exc_cnc_tools[t_item]['offset_z']) + float(drillz)
job_obj.exc_cnc_tools[t_item]['data']['tools_drill_ppname_e'] = job_obj.options['ppname_e']
job_obj.generate_from_excellon_by_tool(obj, tools, drillz=drillz, toolchangez=toolchangez,
endz=endz,
toolchange=toolchange, excellon_optimization_type=opt_type)
job_obj.gcode_parse()
job_obj.create_geometry()
self.app.new_object("cncjob", args['outname'], job_init)
self.app.app_obj.new_object("cncjob", args['outname'], job_init, plot=False)

View File

@@ -0,0 +1,52 @@
from tclCommands.TclCommand import TclCommand
import collections
class TclCommandExportDXF(TclCommand):
"""
Tcl shell command to export a Geometry Object as an DXF File.
example:
export_dxf path/my_geometry filename
"""
# List of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon)
aliases = ['export_dxf', 'edxf']
description = '%s %s' % ("--", "Export a Geometry object as a DXF File.")
# Dictionary of types from Tcl command, needs to be ordered
arg_names = collections.OrderedDict([
('name', str),
('filename', 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 = ['obj_name']
# structured help for current command, args needs to be ordered
help = {
'main': "Export a Geometry object as a DXF File.",
'args': collections.OrderedDict([
('name', 'Name of the Geometry object to export.'),
('filename', 'Absolute path to file to export.\n'
'WARNING: no spaces are allowed. If unsure enclose the entire path with quotes.'),
]),
'examples': ['export_dxf my_geo path/my_file.dxf', 'export_dxf my_geo D:/my_file.dxf']
}
def execute(self, args, unnamed_args):
"""
:param args:
:param unnamed_args:
:return:
"""
if 'filename' not in args:
args['filename'] = self.app.defaults["global_last_save_folder"] + '/' + args['name']
self.app.f_handlers.export_dxf(use_thread=False, local_use=None, **args)

View File

@@ -0,0 +1,52 @@
from tclCommands.TclCommand import TclCommand
import collections
class TclCommandExportExcellon(TclCommand):
"""
Tcl shell command to export a Excellon Object as an Excellon File.
example:
export_exc path/my_excellon filename
"""
# List of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon)
aliases = ['export_exc', 'ee', 'export_excellon']
description = '%s %s' % ("--", "Export a Excellon object as a Excellon File.")
# Dictionary of types from Tcl command, needs to be ordered
arg_names = collections.OrderedDict([
('name', str),
('filename', 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 a Excellon object as a Excellon File.",
'args': collections.OrderedDict([
('name', 'Name of the Excellon object to export.'),
('filename', 'Absolute path to file to export.\n'
'WARNING: no spaces are allowed. If unsure enclose the entire path with quotes.'),
]),
'examples': ['export_excellon my_excellon path/my_file.drl', 'export_excellon My_Excellon D:/drill_file.DRL']
}
def execute(self, args, unnamed_args):
"""
:param args:
:param unnamed_args:
:return:
"""
if 'filename' not in args:
args['filename'] = self.app.defaults["global_last_save_folder"] + '/' + args['name']
self.app.f_handlers.export_excellon(use_thread=False, **args)

View File

@@ -1,5 +1,7 @@
from ObjectCollection import *
from tclCommands.TclCommand import TclCommandSignaled
from camlib import CNCjob
import collections
class TclCommandExportGcode(TclCommandSignaled):
@@ -11,7 +13,7 @@ class TclCommandExportGcode(TclCommandSignaled):
promises and send to background if there are promises.
This export may be captured and passed as preable
This export may be captured and passed as preamble
to another "export_gcode" or "write_gcode" call to join G-Code.
example:
@@ -29,11 +31,13 @@ class TclCommandExportGcode(TclCommandSignaled):
# array of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon)
aliases = ['export_gcode']
description = '%s %s' % ("--", "Return Gcode into console output.")
# dictionary of types from Tcl command, needs to be ordered
arg_names = collections.OrderedDict([
('name', str),
('preamble', str),
('postamble', str)
('postamble', str),
])
# dictionary of types from Tcl command, needs to be ordered , this is for options like -optionname value
@@ -46,11 +50,11 @@ class TclCommandExportGcode(TclCommandSignaled):
help = {
'main': "Export gcode into console output.",
'args': collections.OrderedDict([
('name', 'Name of the source Geometry object.'),
('preamble', 'Prepend GCODE.'),
('postamble', 'Append GCODE.')
('name', 'Name of the source Geometry object. Required.'),
('preamble', 'Prepend GCode to the original GCode.'),
('postamble', 'Append GCode o the original GCode.'),
]),
'examples': []
'examples': ['export_gcode geo_name -preamble "G01 X10 Y10" -postamble "G00 X20 Y20\nM04"']
}
def execute(self, args, unnamed_args):
@@ -76,4 +80,5 @@ class TclCommandExportGcode(TclCommandSignaled):
self.raise_tcl_error('!!!Promises exists, but should not here!!!')
del args['name']
return obj.get_gcode(**args)
obj.get_gcode(**args)
return

View File

@@ -0,0 +1,52 @@
from tclCommands.TclCommand import TclCommand
import collections
class TclCommandExportGerber(TclCommand):
"""
Tcl shell command to export a Gerber Object as an Gerber File.
example:
export_exc path/my_excellon filename
"""
# List of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon)
aliases = ['export_grb', 'egr', 'export_gerber']
description = '%s %s' % ("--", "Export a Gerber object as a Gerber File.")
# Dictionary of types from Tcl command, needs to be ordered
arg_names = collections.OrderedDict([
('name', str),
('filename', 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 = ['obj_name']
# structured help for current command, args needs to be ordered
help = {
'main': "Export a Gerber Object as a Gerber File.",
'args': collections.OrderedDict([
('name', 'Name of the object to export. Required.'),
('filename', 'Absolute path to file to export.\n'
'WARNING: no spaces are allowed. If unsure enclose the entire path with quotes.'),
]),
'examples': ['export_gerber my_gerber path/my_file.gbr']
}
def execute(self, args, unnamed_args):
"""
:param args:
:param unnamed_args:
:return:
"""
if 'filename' not in args:
args['filename'] = self.app.defaults["global_last_save_folder"] + '/' + args['name']
self.app.f_handlers.export_gerber(use_thread=False, **args)

View File

@@ -1,6 +1,7 @@
from ObjectCollection import *
from tclCommands.TclCommand import TclCommand
import collections
class TclCommandExportSVG(TclCommand):
"""
@@ -13,16 +14,18 @@ class TclCommandExportSVG(TclCommand):
# List of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon)
aliases = ['export_svg']
description = '%s %s' % ("--", "Export a Geometry object as a SVG File.")
# Dictionary of types from Tcl command, needs to be ordered
arg_names = collections.OrderedDict([
('name', str),
('filename', str),
('scale_factor', float)
('scale_stroke_factor', float)
])
# Dictionary of types from Tcl command, needs to be ordered , this is for options like -optionname value
option_types = collections.OrderedDict([
('scale_factor', float)
('scale_stroke_factor', float)
])
# array of mandatory options for current Tcl command: required = {'name','outname'}
@@ -30,11 +33,12 @@ class TclCommandExportSVG(TclCommand):
# structured help for current command, args needs to be ordered
help = {
'main': "Export a Geometry Object as a SVG File.",
'main': "Export a Geometry object as a SVG File.",
'args': collections.OrderedDict([
('name', 'Name of the object export.'),
('filename', 'Path to the file to export.'),
('scale_factor', 'Multiplication factor used for scaling line widths during export.')
('name', 'Name of the object export. Required.'),
('filename', 'Absolute path to file to export.\n'
'WARNING: no spaces are allowed. If unsure enclose the entire path with quotes.'),
('scale_stroke_factor', 'Multiplication factor used for scaling line widths during export.')
]),
'examples': ['export_svg my_geometry my_file.svg']
}
@@ -47,6 +51,4 @@ class TclCommandExportSVG(TclCommand):
:return:
"""
name = args['name']
self.app.export_svg(**args)
self.app.f_handlers.export_svg(**args)

View File

@@ -1,5 +1,7 @@
from ObjectCollection import *
from tclCommands.TclCommand import TclCommandSignaled
from camlib import Geometry
import collections
class TclCommandExteriors(TclCommandSignaled):
@@ -10,6 +12,9 @@ class TclCommandExteriors(TclCommandSignaled):
# array of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon)
aliases = ['exteriors', 'ext']
description = '%s %s' % ("--", "Get exteriors of polygons from a Geometry object and "
"from them create a new Geometry object.")
# dictionary of types from Tcl command, needs to be ordered
arg_names = collections.OrderedDict([
('name', str)
@@ -25,12 +30,12 @@ class TclCommandExteriors(TclCommandSignaled):
# structured help for current command, args needs to be ordered
help = {
'main': "Get exteriors of polygons.",
'main': "Get exteriors of polygons from a Geometry object and from them create a new Geometry object.",
'args': collections.OrderedDict([
('name', 'Name of the source Geometry object.'),
('name', 'Name of the source Geometry object. Required.'),
('outname', 'Name of the resulting Geometry object.')
]),
'examples': []
'examples': ['ext geo_source_name -outname "final_geo"']
}
def execute(self, args, unnamed_args):
@@ -61,4 +66,4 @@ class TclCommandExteriors(TclCommandSignaled):
geo_obj.solid_geometry = obj_exteriors
obj_exteriors = obj.get_exteriors()
self.app.new_object('geometry', outname, geo_init)
self.app.app_obj.new_object('geometry', outname, geo_init, plot=False)

View File

@@ -1,6 +1,7 @@
from ObjectCollection import *
from tclCommands.TclCommand import TclCommandSignaled
import collections
class TclCommandFollow(TclCommandSignaled):
"""
@@ -10,6 +11,8 @@ class TclCommandFollow(TclCommandSignaled):
# array of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon)
aliases = ['follow']
description = '%s %s' % ("--", "Creates a Geometry object following Gerber paths.")
# dictionary of types from Tcl command, needs to be ordered
arg_names = collections.OrderedDict([
('name', str)
@@ -25,12 +28,12 @@ class TclCommandFollow(TclCommandSignaled):
# structured help for current command, args needs to be ordered
help = {
'main': "Creates a geometry object following gerber paths.",
'main': "Creates a Geometry object following Gerber paths.",
'args': collections.OrderedDict([
('name', 'Object name to follow.'),
('name', 'Object name to follow. Required.'),
('outname', 'Name of the resulting Geometry object.')
]),
'examples': ['follow name -outname name_follow']
'examples': ['follow name -outname "name_follow"']
}
def execute(self, args, unnamed_args):
@@ -52,14 +55,14 @@ class TclCommandFollow(TclCommandSignaled):
if obj is None:
self.raise_tcl_error("Object not found: %s" % name)
if not isinstance(obj, FlatCAMGerber):
self.raise_tcl_error('Expected FlatCAMGerber, got %s %s.' % (name, type(obj)))
if obj.kind != 'gerber':
self.raise_tcl_error('Expected GerberObject, got %s %s.' % (name, type(obj)))
del args['name']
try:
obj.follow(**args)
obj.follow_geo(**args)
except Exception as e:
return "Operation failed: %s" % str(e)
# in the end toggle the visibility of the origin object so we can see the generated Geometry
self.app.collection.get_by_name(name).ui.plot_cb.toggle()
# self.app.collection.get_by_name(name).ui.plot_cb.toggle()

View File

@@ -1,9 +1,21 @@
from ObjectCollection import *
from tclCommands.TclCommand import TclCommandSignaled
import logging
import collections
from copy import deepcopy
from shapely.ops import cascaded_union
from shapely.ops import unary_union
from shapely.geometry import Polygon, LineString, LinearRing
import gettext
import appTranslation as fcTranslate
import builtins
log = logging.getLogger('base')
fcTranslate.apply_language('strings')
if '_' not in builtins.__dict__:
_ = gettext.gettext
class TclCommandGeoCutout(TclCommandSignaled):
"""
@@ -19,6 +31,8 @@ class TclCommandGeoCutout(TclCommandSignaled):
# names for backward compatibility (add_poly, add_polygon)
aliases = ['geocutout', 'geoc']
description = '%s %s' % ("--", "Creates board cutout from an object (Gerber or Geometry) of any shape.")
# Dictionary of types from Tcl command, needs to be ordered
arg_names = collections.OrderedDict([
('name', str),
@@ -30,7 +44,8 @@ class TclCommandGeoCutout(TclCommandSignaled):
('dia', float),
('margin', float),
('gapsize', float),
('gaps', str)
('gaps', str),
('outname', str)
])
# array of mandatory options for current Tcl command: required = {'name','outname'}
@@ -38,16 +53,17 @@ class TclCommandGeoCutout(TclCommandSignaled):
# structured help for current command, args needs to be ordered
help = {
'main': 'Creates board cutout from an object (Gerber or Geometry) of any shape',
'main': 'Creates board cutout from an object (Gerber or Geometry) of any shape.',
'args': collections.OrderedDict([
('name', 'Name of the object.'),
('name', 'Name of the object to be cutout. Required'),
('dia', 'Tool diameter.'),
('margin', 'Margin over bounds.'),
('gapsize', 'size of gap.'),
('gaps', "type of gaps. Can be: 'tb' = top-bottom, 'lr' = left-right, '2tb' = 2top-2bottom, "
"'2lr' = 2left-2right, '4' = 4 cuts, '8' = 8 cuts")
"'2lr' = 2left-2right, '4' = 4 cuts, '8' = 8 cuts"),
('outname', 'Name of the resulting Geometry object.'),
]),
'examples': [" #isolate margin for example from fritzing arduino shield or any svg etc\n" +
'examples': [" #isolate margin for example from Fritzing arduino shield or any svg etc\n" +
" isolate BCu_margin -dia 3 -overlap 1\n" +
"\n" +
" #create exteriors from isolated object\n" +
@@ -57,7 +73,7 @@ class TclCommandGeoCutout(TclCommandSignaled):
" delete BCu_margin_iso\n" +
"\n" +
" #finally cut holding gaps\n" +
" geocutout BCu_margin_iso_exterior -dia 3 -gapsize 0.6 -gaps 4\n"]
" geocutout BCu_margin_iso_exterior -dia 3 -gapsize 0.6 -gaps 4 -outname cutout_geo\n"]
}
flat_geometry = []
@@ -115,41 +131,46 @@ class TclCommandGeoCutout(TclCommandSignaled):
flat_geometry = flatten(geo, pathonly=True)
polygon = Polygon(pts)
toolgeo = cascaded_union(polygon)
toolgeo = unary_union(polygon)
diffs = []
for target in flat_geometry:
if type(target) == LineString or type(target) == LinearRing:
diffs.append(target.difference(toolgeo))
else:
log.warning("Not implemented.")
return cascaded_union(diffs)
return unary_union(diffs)
if 'name' in args:
name = args['name']
else:
self.app.inform.emit(
"[WARNING]The name of the object for which cutout is done is missing. Add it and retry.")
"[WARNING] %s" % _("The name of the object for which cutout is done is missing. Add it and retry."))
return
if 'margin' in args:
margin = args['margin']
margin = float(args['margin'])
else:
margin = 0.001
margin = float(self.app.defaults["tools_cutout_margin"])
if 'dia' in args:
dia = args['dia']
dia = float(args['dia'])
else:
dia = 0.1
dia = float(self.app.defaults["tools_cutout_tooldia"])
if 'gaps' in args:
gaps = args['gaps']
else:
gaps = 4
gaps = str(self.app.defaults["tools_cutout_gaps_ff"])
if 'gapsize' in args:
gapsize = args['gapsize']
gapsize = float(args['gapsize'])
else:
gapsize = 0.1
gapsize = float(self.app.defaults["tools_cutout_gapsize"])
if 'outname' in args:
outname = args['outname']
else:
outname = str(name) + "_cutout"
# Get source object.
try:
@@ -159,12 +180,13 @@ class TclCommandGeoCutout(TclCommandSignaled):
return "Could not retrieve object: %s" % name
if 0 in {dia}:
self.app.inform.emit("[WARNING]Tool Diameter is zero value. Change it to a positive real number.")
self.app.inform.emit(
"[WARNING] %s" % _("Tool Diameter is zero value. Change it to a positive real number."))
return "Tool Diameter is zero value. Change it to a positive real number."
if gaps not in ['lr', 'tb', '2lr', '2tb', '4', '8']:
self.app.inform.emit("[WARNING]Gaps value can be only one of: 'lr', 'tb', '2lr', '2tb', 4 or 8. "
"Fill in a correct value and retry. ")
self.app.inform.emit(
"[WARNING] %s" % _("Gaps value can be only one of: 'lr', 'tb', '2lr', '2tb', 4 or 8."))
return
# Get min and max data for each object as we just cut rectangles across X or Y
@@ -186,7 +208,7 @@ class TclCommandGeoCutout(TclCommandSignaled):
except ValueError:
gaps_u = gaps
if isinstance(cutout_obj, FlatCAMGeometry):
if cutout_obj.kind == 'geometry':
# rename the obj name so it can be identified as cutout
# cutout_obj.options["name"] += "_cutout"
@@ -276,15 +298,11 @@ class TclCommandGeoCutout(TclCommandSignaled):
app_obj.disable_plots(objects=[cutout_obj])
app_obj.inform.emit("[success] Any-form Cutout operation finished.")
app_obj.inform.emit("[success] %s" % _("Any-form Cutout operation finished."))
outname = cutout_obj.options["name"] + "_cutout"
self.app.new_object('geometry', outname, geo_init)
self.app.app_obj.new_object('geometry', outname, geo_init, plot=False)
# cutout_obj.plot()
# self.app.inform.emit("[success] Any-form Cutout operation finished.")
# self.app.plots_updated.emit()
elif isinstance(cutout_obj, FlatCAMGerber):
elif cutout_obj.kind == 'gerber':
def geo_init(geo_obj, app_obj):
try:
@@ -335,12 +353,11 @@ class TclCommandGeoCutout(TclCommandSignaled):
geo_obj.options['ymin'] = cutout_obj.options['ymin']
geo_obj.options['xmax'] = cutout_obj.options['xmax']
geo_obj.options['ymax'] = cutout_obj.options['ymax']
app_obj.inform.emit("[success] Any-form Cutout operation finished.")
app_obj.inform.emit("[success] %s" % _("Any-form Cutout operation finished."))
outname = cutout_obj.options["name"] + "_cutout"
self.app.new_object('geometry', outname, geo_init)
self.app.app_obj.new_object('geometry', outname, geo_init, plot=False)
cutout_obj = self.app.collection.get_by_name(outname)
else:
self.app.inform.emit("[ERROR]Cancelled. Object type is not supported.")
self.app.inform.emit("[ERROR] %s" % _("Cancelled. Object type is not supported."))
return

View File

@@ -1,6 +1,7 @@
from ObjectCollection import *
from tclCommands.TclCommand import TclCommand
import collections
class TclCommandGeoUnion(TclCommand):
"""
@@ -14,6 +15,8 @@ class TclCommandGeoUnion(TclCommand):
# List of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon)
aliases = ['geo_union']
description = '%s %s' % ("--", "Run the Union (join) geometry operation on the elements of a Geometry object.")
# Dictionary of types from Tcl command, needs to be ordered
arg_names = collections.OrderedDict([
('name', str),
@@ -31,12 +34,12 @@ class TclCommandGeoUnion(TclCommand):
help = {
'main': ('Runs a union operation (addition) on the components '
'of the geometry object. For example, if it contains '
'2 intersecting polygons, this opperation adds them into'
'2 intersecting polygons, this operation adds them into'
'a single larger polygon.'),
'args': collections.OrderedDict([
('name', 'Name of the Geometry Object.'),
('name', 'Name of the Geometry Object that contain the components to be joined. Required.'),
]),
'examples': []
'examples': ['geo_union target_geo']
}
def execute(self, args, unnamed_args):
@@ -50,8 +53,8 @@ class TclCommandGeoUnion(TclCommand):
obj_name = args['name']
try:
obj = self.collection.get_by_name(str(obj_name))
except:
obj = self.app.collection.get_by_name(str(obj_name))
except Exception:
return "Could not retrieve object: %s" % obj_name
if obj is None:
return "Object not found: %s" % obj_name

View File

@@ -1,10 +1,11 @@
from ObjectCollection import *
from tclCommands.TclCommand import TclCommand
import collections
class TclCommandGetNames(TclCommand):
"""
Tcl shell command to set an object as active in the GUI.
Tcl shell command to set an object as active in the appGUI.
example:
@@ -13,6 +14,9 @@ class TclCommandGetNames(TclCommand):
# List of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon)
aliases = ['get_names']
description = '%s %s' % ("--", "Return to TCL the list of the project objects names "
"as a string with names separated by the '\\n' char.")
# Dictionary of types from Tcl command, needs to be ordered
arg_names = collections.OrderedDict([
@@ -28,11 +32,12 @@ class TclCommandGetNames(TclCommand):
# structured help for current command, args needs to be ordered
help = {
'main': 'Lists the names of objects in the project.',
'main': 'Lists the names of objects in the project. '
'It returns a string with names separated by "\\n" character',
'args': collections.OrderedDict([
]),
'examples': []
'examples': ['get_names']
}
def execute(self, args, unnamed_args):

View File

@@ -0,0 +1,65 @@
# ##########################################################
# FlatCAM: 2D Post-processing for Manufacturing #
# File Author: Marius Adrian Stanciu (c) #
# Date: 4/28/2020 #
# MIT Licence #
# ##########################################################
from tclCommands.TclCommand import TclCommand
import collections
import logging
import gettext
import appTranslation as fcTranslate
import builtins
fcTranslate.apply_language('strings')
if '_' not in builtins.__dict__:
_ = gettext.gettext
log = logging.getLogger('base')
class TclCommandGetPath(TclCommand):
"""
Tcl shell command to get the current default path set for Tcl.
example:
"""
# List of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon)
aliases = ['get_path']
description = '%s %s' % ("--", "Get the default Tcl Shell folder path.")
# Dictionary of types from Tcl command, needs to be ordered
arg_names = collections.OrderedDict([
])
# Dictionary of types from Tcl command, needs to be ordered , this is for options like -optionname value
option_types = collections.OrderedDict([
])
# array of mandatory options for current Tcl command: required = {'name','outname'}
required = []
# structured help for current command, args needs to be ordered
help = {
'main': "Will get the folder path used as a fallback path for opening files.",
'args': collections.OrderedDict([
]),
'examples': ['get_path']
}
def execute(self, args, unnamed_args):
"""
:param args:
:param unnamed_args:
:return:
"""
self.app.shell.append_output("Current default Tcl Shell path is: ")
path = self.app.defaults["global_tcl_path"]
return path

View File

@@ -1,6 +1,14 @@
from ObjectCollection import *
# ##########################################################
# FlatCAM: 2D Post-processing for Manufacturing #
# File Author: Marius Adrian Stanciu (c) #
# Date: 8/17/2019 #
# MIT Licence #
# ##########################################################
from tclCommands.TclCommand import TclCommand
import collections
class TclCommandGetSys(TclCommand):
"""
@@ -13,6 +21,8 @@ class TclCommandGetSys(TclCommand):
# List of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon)
aliases = ['get_sys', 'getsys']
description = '%s %s' % ("--", "Returns to TCL the value for the entered system variable.")
# Dictionary of types from Tcl command, needs to be ordered
arg_names = collections.OrderedDict([
('name', str)
@@ -28,9 +38,9 @@ class TclCommandGetSys(TclCommand):
# structured help for current command, args needs to be ordered
help = {
'main': "Returns the value of the system variable.",
'main': "Returns to TCL the value for the entered system variable.",
'args': collections.OrderedDict([
('name', 'Name of the system variable.'),
('name', 'Name of the system variable. Required.'),
]),
'examples': ['get_sys excellon_zeros']
}
@@ -47,4 +57,5 @@ class TclCommandGetSys(TclCommand):
if name in self.app.defaults:
return self.app.defaults[name]
else:
return "The keyword: %s does not exist as a parameter" % str(name)

View File

@@ -0,0 +1,117 @@
# ##########################################################
# FlatCAM: 2D Post-processing for Manufacturing #
# File Author: Marius Adrian Stanciu (c) #
# Content was borrowed from FlatCAM proper #
# Date: 4/22/2020 #
# MIT Licence #
# ##########################################################
from tclCommands.TclCommand import TclCommand
import collections
import gettext
import appTranslation as fcTranslate
import builtins
fcTranslate.apply_language('strings')
if '_' not in builtins.__dict__:
_ = gettext.gettext
class TclCommandHelp(TclCommand):
"""
Tcl shell command to show Help
example:
help add_circle
"""
# List of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon)
aliases = ['help']
description = '%s %s' % ("--", "PRINTS to TCL the HELP.")
# 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([
])
# array of mandatory options for current Tcl command: required = {'name','outname'}
required = []
# structured help for current command, args needs to be ordered
help = {
'main': "Returns to TCL the value for the entered system variable.",
'args': collections.OrderedDict([
('name', 'Name of a Tcl Command for which to display the Help.'),
]),
'examples': ['help add_circle']
}
def execute(self, args, unnamed_args):
"""
:param args: Without any argument will display the list of commands. Can have as a argument
a tcl command name to display the help for that command.
:param unnamed_args:
:return:
"""
if 'name' in args:
name = args['name']
if name not in self.app.shell.tcl_commands_storage:
return "Unknown command: %s" % name
help_for_command = self.app.shell.tcl_commands_storage[name]["help"] + '\n\n'
self.app.shell.append_output(help_for_command)
else:
if not args:
cmd_enum = '%s\n' % _("Available commands:")
displayed_text = []
try:
# find the maximum length of a command name
max_len = 0
for cmd_name in self.app.shell.tcl_commands_storage:
curr_len = len(cmd_name)
if curr_len > max_len:
max_len = curr_len
h_space = "&nbsp;"
cnt = 0
for cmd_name in sorted(self.app.shell.tcl_commands_storage):
cmd_description = "<span>%s</span>" % \
self.app.shell.tcl_commands_storage[cmd_name]['description']
curr_len = len(cmd_name)
cmd_name_colored = "> <span style=\" font-weight: bold; color: red;\" >%s</span>" % \
str(cmd_name)
nr_chars = max_len - curr_len
cmd_line_txt = '%s%s&nbsp;&nbsp;%s' % (cmd_name_colored, nr_chars * h_space, cmd_description)
displayed_text.append(cmd_line_txt)
# group commands by 4 adding a line break after 4 commands
if cnt == 3:
cnt = 0
displayed_text.append(' ')
else:
cnt += 1
except Exception as err:
self.app.log.debug("tclCommands.TclCommandHelp() when run as 'help' --> %s" % str(err))
displayed_text = ['> %s' % cmd for cmd in sorted(self.app.shell.tcl_commands_storage)]
cmd_enum += '<br>'.join(displayed_text)
cmd_enum += '<br><br>%s<br>%s<br><br>' % (
_("Type help <command_name> for usage."), _("Example: help open_gerber"))
self.app.shell.append_raw(cmd_enum)

View File

@@ -1,6 +1,8 @@
from ObjectCollection import *
from tclCommands.TclCommand import TclCommandSignaled
import collections
from camlib import Geometry
class TclCommandImportSvg(TclCommandSignaled):
"""
@@ -10,6 +12,8 @@ class TclCommandImportSvg(TclCommandSignaled):
# array of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon)
aliases = ['import_svg']
description = '%s %s' % ("--", "Import a SVG file as a Geometry (or Gerber) Object.")
# dictionary of types from Tcl command, needs to be ordered
arg_names = collections.OrderedDict([
('filename', str)
@@ -26,13 +30,14 @@ class TclCommandImportSvg(TclCommandSignaled):
# structured help for current command, args needs to be ordered
help = {
'main': "Import an SVG file as a Geometry Object..",
'main': "Import a SVG file as a Geometry (or Gerber) Object.",
'args': collections.OrderedDict([
('filename', 'Path to file to open.'),
('type', 'Import as gerber or geometry(default).'),
('filename', 'Absolute path to file to open. Required.\n'
'WARNING: no spaces are allowed. If unsure enclose the entire path with quotes.'),
('type', 'Import as a Gerber or Geometry (default) object. Values can be: "geometry" or "gerber"'),
('outname', 'Name of the resulting Geometry object.')
]),
'examples': []
'examples': ['import_svg D:\\my_beautiful_svg_file.SVG']
}
def execute(self, args, unnamed_args):
@@ -61,21 +66,20 @@ class TclCommandImportSvg(TclCommandSignaled):
outname = filename.split('/')[-1].split('\\')[-1]
if 'type' in args:
obj_type = args['type']
obj_type = args['type'].lower()
else:
obj_type = 'geometry'
if obj_type != "geometry" and obj_type != "gerber":
self.raise_tcl_error("Option type can be 'geopmetry' or 'gerber' only, got '%s'." % obj_type)
self.raise_tcl_error("Option type can be 'geometry' or 'gerber' only, got '%s'." % obj_type)
with self.app.proc_container.new("Import SVG"):
with self.app.proc_container.new('%s ...' % _("Importing")):
# Object creation
self.app.new_object(obj_type, outname, obj_init)
self.app.app_obj.new_object(obj_type, outname, obj_init, plot=False)
# Register recent file
self.app.file_opened.emit("svg", filename)
# GUI feedback
self.app.inform.emit("Opened: " + filename)

View File

@@ -1,5 +1,7 @@
from ObjectCollection import *
from tclCommands.TclCommand import TclCommandSignaled
from camlib import Geometry
import collections
class TclCommandInteriors(TclCommandSignaled):
@@ -10,6 +12,9 @@ class TclCommandInteriors(TclCommandSignaled):
# array of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon)
aliases = ['interiors']
description = '%s %s' % ("--", "Create a new Geometry object with the 'interiors' geo "
"elements of the source object.")
# dictionary of types from Tcl command, needs to be ordered
arg_names = collections.OrderedDict([
('name', str)
@@ -25,12 +30,13 @@ class TclCommandInteriors(TclCommandSignaled):
# structured help for current command, args needs to be ordered
help = {
'main': "Get interiors of polygons.",
'main': "Create a new Geometry object with the 'interiors' geometric elements of "
"the specified source Geometry object.",
'args': collections.OrderedDict([
('name', 'Name of the source Geometry object.'),
('name', 'Name of the source Geometry object. Required.'),
('outname', 'Name of the resulting Geometry object.')
]),
'examples': []
'examples': ['interiors my_geo_name -outname "outputed_geo"']
}
def execute(self, args, unnamed_args):
@@ -61,4 +67,4 @@ class TclCommandInteriors(TclCommandSignaled):
geo_obj.solid_geometry = obj_interiors
obj_interiors = obj.get_interiors()
self.app.new_object('geometry', outname, geo_init)
self.app.app_obj.new_object('geometry', outname, geo_init)

View File

@@ -1,6 +1,7 @@
from ObjectCollection import *
from tclCommands.TclCommand import TclCommandSignaled
import collections
class TclCommandIsolate(TclCommandSignaled):
"""
@@ -17,6 +18,8 @@ class TclCommandIsolate(TclCommandSignaled):
# array of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon)
aliases = ['isolate']
description = '%s %s' % ("--", "Creates isolation routing Geometry for the specified Gerber object.")
# dictionary of types from Tcl command, needs to be ordered
arg_names = collections.OrderedDict([
('name', str)
@@ -27,9 +30,10 @@ class TclCommandIsolate(TclCommandSignaled):
('dia', float),
('passes', int),
('overlap', float),
('combine', int),
('combine', str),
('outname', str),
('follow', str)
('follow', str),
('iso_type', int)
])
@@ -38,17 +42,20 @@ class TclCommandIsolate(TclCommandSignaled):
# structured help for current command, args needs to be ordered
help = {
'main': "Creates isolation routing geometry for the given Gerber.",
'main': "Creates isolation routing Geometry for the specified Gerber object.",
'args': collections.OrderedDict([
('name', 'Name of the source object.'),
('name', 'Name of the Gerber source object to be isolated. Required.'),
('dia', 'Tool diameter.'),
('passes', 'Passes of tool width.'),
('overlap', 'Fraction of tool diameter to overlap passes.'),
('combine', 'Combine all passes into one geometry.'),
('overlap', 'Percentage of tool diameter to overlap current pass over previous pass. Float [0, 99.9999]\n'
'E.g: for a 25% from tool diameter overlap use -overlap 25'),
('combine', 'Combine all passes into one geometry. Can be True (1) or False (0)'),
('outname', 'Name of the resulting Geometry object.'),
('follow', 'Create a Geometry that follows the Gerber path.')
('follow', 'Create a Geometry that follows the Gerber path. Can be True (1) or False (0).'),
('iso_type', 'A value of 0 will isolate exteriors, a value of 1 will isolate interiors '
'and a value of 2 will do full isolation.')
]),
'examples': []
'examples': ['isolate my_gerber -dia 0.1 -passes 2 -overlap 10 -combine True -iso_type 2 -outname out_geo']
}
def execute(self, args, unnamed_args):
@@ -66,20 +73,30 @@ class TclCommandIsolate(TclCommandSignaled):
if 'outname' not in args:
args['outname'] = name + "_iso"
if 'timeout' in args:
timeout = args['timeout']
else:
timeout = 10000
# if 'timeout' in args:
# timeout = args['timeout']
# else:
# timeout = 10000
if 'follow' not in args:
args['follow'] = None
# evaluate this parameter so True, False, 0 and 1 works
if 'combine' in args:
try:
par = args['combine'].capitalize()
except AttributeError:
par = args['combine']
args['combine'] = bool(eval(par))
else:
args['combine'] = bool(eval(self.app.defaults["tools_iso_combine_passes"]))
obj = self.app.collection.get_by_name(name)
if obj is None:
self.raise_tcl_error("Object not found: %s" % name)
if not isinstance(obj, FlatCAMGerber):
self.raise_tcl_error('Expected FlatCAMGerber, got %s %s.' % (name, type(obj)))
if obj.kind != 'gerber':
self.raise_tcl_error('Expected GerberObject, got %s %s.' % (name, type(obj)))
del args['name']
obj.isolate(**args)
obj.isolate(plot=False, **args)

View File

@@ -1,5 +1,7 @@
from ObjectCollection import *
from tclCommands.TclCommand import TclCommand
from appObjects.FlatCAMExcellon import ExcellonObject
import collections
class TclCommandJoinExcellon(TclCommand):
@@ -13,6 +15,9 @@ class TclCommandJoinExcellon(TclCommand):
# List of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon)
aliases = ['join_excellon', 'join_excellons']
description = '%s %s' % ("--", "Merge two or more Excellon objects drills and create "
"a new Excellon object with them.")
# Dictionary of types from Tcl command, needs to be ordered
arg_names = collections.OrderedDict([
('outname', str),
@@ -20,7 +25,6 @@ class TclCommandJoinExcellon(TclCommand):
# 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'}
@@ -28,14 +32,14 @@ class TclCommandJoinExcellon(TclCommand):
# structured help for current command, args needs to be ordered
help = {
'main': "Runs a merge operation (join) on the Excellon objects.",
'main': "Runs a merge operation (join) on the Excellon objects.\n"
"The names of the Excellon objects to be merged will be entered after the outname,\n"
"separated by spaces. See the example below.\n"
"WARNING: if the name of an Excellon objects has spaces, enclose the name with quotes.",
'args': collections.OrderedDict([
('name', 'Name of the new Excellon Object.'),
('obj_name_0', 'Name of the first object'),
('obj_name_1', 'Name of the second object.'),
('obj_name_2...', 'Additional object names')
('outname', 'Name of the new Excellon Object made by joining of other Excellon objects. Required'),
]),
'examples': []
'examples': ['join_excellons merged_new_excellon exc_name_1 "exc name_2"']
}
def execute(self, args, unnamed_args):
@@ -46,7 +50,7 @@ class TclCommandJoinExcellon(TclCommand):
:return:
"""
outname = args['name']
outname = args['outname']
obj_names = unnamed_args
objs = []
@@ -58,7 +62,9 @@ class TclCommandJoinExcellon(TclCommand):
objs.append(obj)
def initialize(obj_, app):
FlatCAMExcellon.merge(objs, obj_)
ExcellonObject.merge(objs, obj_, decimals=self.app.decimals)
if objs is not None:
self.app.new_object("excellon", outname, initialize)
if objs and len(objs) >= 2:
self.app.app_obj.new_object("excellon", outname, initialize, plot=False)
else:
return "No Excellon objects to be joined or less than two Excellon objects specified for merging."

View File

@@ -1,5 +1,7 @@
from ObjectCollection import *
from tclCommands.TclCommand import TclCommand
from appObjects.FlatCAMGeometry import GeometryObject
import collections
class TclCommandJoinGeometry(TclCommand):
@@ -13,6 +15,8 @@ class TclCommandJoinGeometry(TclCommand):
# List of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon)
aliases = ['join_geometries', 'join_geometry']
description = '%s %s' % ("--", "Merge two or more Geometry objects and create a new Geometry object.")
# Dictionary of types from Tcl command, needs to be ordered
arg_names = collections.OrderedDict([
('outname', str),
@@ -28,14 +32,14 @@ class TclCommandJoinGeometry(TclCommand):
# structured help for current command, args needs to be ordered
help = {
'main': "Runs a merge operation (join) on the Excellon objects.",
'main': "Runs a merge operation (join) on the Geometry objects.\n"
"The names of the Geometry objects to be merged will be entered after the outname,\n"
"separated by spaces. See the example below.\n"
"WARNING: if the name of an Geometry objects has spaces, enclose the name with quotes.",
'args': collections.OrderedDict([
('outname', 'Name of the new Geometry Object.'),
('obj_name_0', 'Name of the first object'),
('obj_name_1', 'Name of the second object.'),
('obj_name_2...', 'Additional object names')
('outname', 'Name of the new Geometry Object made by joining of other Geometry objects. Required'),
]),
'examples': []
'examples': ['join_geometry merged_new_geo geo_name_1 "geo name_2"']
}
def execute(self, args, unnamed_args):
@@ -58,7 +62,9 @@ class TclCommandJoinGeometry(TclCommand):
objs.append(obj)
def initialize(obj_, app):
FlatCAMGeometry.merge(objs, obj_)
GeometryObject.merge(objs, obj_)
if objs is not None:
self.app.new_object("geometry", outname, initialize)
if objs and len(objs) >= 2:
self.app.app_obj.new_object("geometry", outname, initialize, plot=False)
else:
return "No Geometry objects to be joined or less than two Geometry objects specified for merging."

View File

@@ -1,3 +1,10 @@
# ##########################################################
# FlatCAM: 2D Post-processing for Manufacturing #
# File Author: Marius Adrian Stanciu (c) #
# Date: 8/17/2019 #
# MIT Licence #
# ##########################################################
from tclCommands.TclCommand import *
@@ -12,6 +19,8 @@ class TclCommandListSys(TclCommand):
# List of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon)
aliases = ['list_sys', 'listsys']
description = '%s %s' % ("--", "Outputs in Tcl Shell the list with the names of system variables.")
# Dictionary of types from Tcl command, needs to be ordered
arg_names = collections.OrderedDict([
('selection', str),
@@ -33,7 +42,7 @@ class TclCommandListSys(TclCommand):
"of the system variable.\n"
"In that case it will list only the system variables that starts with that string.\n"
"Main categories start with: gerber or excellon or geometry or cncjob or global.\n"
"Note: Use get_sys TclCommand to get the value and set_sys TclCommand to set it.\n",
"Note: Use 'get_sys system variable' to get the value and 'set_sys system variable value' to set it.\n",
'args': collections.OrderedDict([
]),
'examples': ['list_sys',
@@ -53,4 +62,6 @@ class TclCommandListSys(TclCommand):
argument = args['selection']
return str([k for k in self.app.defaults.keys() if str(k).startswith(str(argument))])
else:
return str([*self.app.defaults])
ret_val = list(self.app.defaults.keys())
return str(ret_val)
# return str([*self.app.defaults])

View File

@@ -0,0 +1,158 @@
# ##########################################################
# FlatCAM: 2D Post-processing for Manufacturing #
# File Author: Marius Adrian Stanciu (c) #
# Date: 8/17/2019 #
# MIT Licence #
# ##########################################################
from tclCommands.TclCommand import TclCommandSignaled
import math
import collections
class TclCommandMillDrills(TclCommandSignaled):
"""
Tcl shell command to Create Geometry Object for milling holes from Excellon.
example:
millholes my_drill -tools 1,2,3 -tooldia 0.1 -outname mill_holes_geo
"""
# List of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon)
aliases = ['milldrills', 'milld']
description = '%s %s' % ("--", "Create a Geometry Object for milling drill holes from Excellon.")
# 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([
('milled_dias', str),
('outname', str),
('tooldia', float),
('use_thread', str),
('diatol', float)
])
# 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': "Create a Geometry Object for milling drill holes from Excellon.",
'args': collections.OrderedDict([
('name', 'Name of the Excellon Object. Required.'),
('milled_dias', 'Comma separated tool diameters of the drills to be milled (example: 0.6, 1.0 or 3.125).\n'
'Exception: if you enter "all" then the drills for all tools will be milled.\n'
'WARNING: no spaces are allowed in the list of tools.\n'
'As a precaution you can enclose them with quotes.'),
('tooldia', 'Diameter of the milling tool (example: 0.1).'),
('outname', 'Name of Geometry object to be created holding the milled geometries.'),
('use_thread', 'If to use multithreading: True (1) or False (0).'),
('diatol', 'Tolerance. Percentange (0.0 ... 100.0) within which dias in milled_dias will be judged to be '
'the same as the ones in the tools from the Excellon object. E.g: if in milled_dias we have a '
'diameter with value 1.0, in the Excellon we have a tool with dia = 1.05 and we set a tolerance '
'diatol = 5.0 then the drills with the dia = (0.95 ... 1.05) '
'in Excellon will be processed. Float number.')
]),
'examples': ['milldrills mydrills -milled_dias "0.6,0.8" -tooldia 0.1 -diatol 10 -outname milled_holes',
'milld my_excellon.drl']
}
def execute(self, args, unnamed_args):
"""
: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']
try:
obj = self.app.collection.get_by_name(str(name))
except Exception:
obj = None
self.raise_tcl_error("Could not retrieve object: %s" % name)
if 'outname' not in args:
args['outname'] = name + "_mill_drills"
if 'use_thread' in args:
try:
par = args['use_thread'].capitalize()
except AttributeError:
par = args['use_thread']
args['use_thread'] = bool(eval(par))
else:
args['use_thread'] = False
if not obj.drills:
self.raise_tcl_error("The Excellon object has no drills: %s" % name)
try:
if 'milled_dias' in args and args['milled_dias'] != 'all':
diameters = [x.strip() for x in args['milled_dias'].split(",") if x != '']
nr_diameters = len(diameters)
req_tools = set()
for tool in obj.tools:
for req_dia in diameters:
obj_dia_form = float('%.*f' % (obj.decimals, float(obj.tools[tool]["C"])))
req_dia_form = float('%.*f' % (obj.decimals, float(req_dia)))
if 'diatol' in args:
tolerance = float(args['diatol']) / 100
tolerance = 0.0 if tolerance < 0.0 else tolerance
tolerance = 1.0 if tolerance > 1.0 else tolerance
if math.isclose(obj_dia_form, req_dia_form, rel_tol=tolerance):
req_tools.add(tool)
nr_diameters -= 1
else:
if obj_dia_form == req_dia_form:
req_tools.add(tool)
nr_diameters -= 1
if nr_diameters > 0:
self.raise_tcl_error("One or more tool diameters of the drills to be milled passed to the "
"TclCommand are not actual tool diameters in the Excellon object.")
args['tools'] = req_tools
# no longer needed
del args['milled_dias']
del args['diatol']
# Split and put back. We are passing the whole dictionary later.
# args['milled_dias'] = [x.strip() for x in args['tools'].split(",")]
else:
args['tools'] = 'all'
except Exception as e:
self.raise_tcl_error("Bad tools: %s" % str(e))
if obj.kind != 'excellon':
self.raise_tcl_error('Only Excellon objects can be mill-drilled, got %s %s.' % (name, type(obj)))
if self.app.collection.has_promises():
self.raise_tcl_error('!!!Promises exists, but should not here!!!')
try:
# 'name' is not an argument of obj.generate_milling()
del args['name']
# This runs in the background... Is blocking handled?
success, msg = obj.generate_milling_drills(plot=False, **args)
except Exception as e:
success = None
msg = None
self.raise_tcl_error("Operation failed: %s" % str(e))
if not success:
self.raise_tcl_error(msg)

View File

@@ -1,91 +0,0 @@
from ObjectCollection import *
from tclCommands.TclCommand import TclCommandSignaled
class TclCommandMillHoles(TclCommandSignaled):
"""
Tcl shell command to Create Geometry Object for milling holes from Excellon.
example:
millholes my_drill -tools 1,2,3 -tooldia 0.1 -outname mill_holes_geo
"""
# List of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon)
aliases = ['millholes', 'mill']
# 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([
('tools', str),
('outname', str),
('tooldia', float),
('use_threads', bool)
])
# 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': "Create Geometry Object for milling holes from Excellon.",
'args': collections.OrderedDict([
('name', 'Name of the Excellon Object.'),
('tools', 'Comma separated indexes of tools (example: 1,3 or 2).'),
('tooldia', 'Diameter of the milling tool (example: 0.1).'),
('outname', 'Name of object to create.'),
('use_thread', 'If to use multithreading: True or False.')
]),
'examples': ['millholes mydrills']
}
def execute(self, args, unnamed_args):
"""
: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 + "_mill"
try:
if 'tools' in args and args['tools'] != 'all':
# Split and put back. We are passing the whole dictionary later.
args['tools'] = [x.strip() for x in args['tools'].split(",")]
else:
args['tools'] = 'all'
except Exception as e:
self.raise_tcl_error("Bad tools: %s" % str(e))
try:
obj = self.app.collection.get_by_name(str(name))
except:
self.raise_tcl_error("Could not retrieve object: %s" % name)
if not isinstance(obj, FlatCAMExcellon):
self.raise_tcl_error('Only Excellon objects can be mill-drilled, got %s %s.' % (name, type(obj)))
if self.app.collection.has_promises():
self.raise_tcl_error('!!!Promises exists, but should not here!!!')
try:
# 'name' is not an argument of obj.generate_milling()
del args['name']
# This runs in the background... Is blocking handled?
success, msg = obj.generate_milling_drills(**args)
except Exception as e:
self.raise_tcl_error("Operation failed: %s" % str(e))
if not success:
self.raise_tcl_error(msg)

View File

@@ -0,0 +1,160 @@
# ##########################################################
# FlatCAM: 2D Post-processing for Manufacturing #
# File Author: Marius Adrian Stanciu (c) #
# Date: 8/17/2019 #
# MIT Licence #
# ##########################################################
from tclCommands.TclCommand import TclCommandSignaled
import collections
import math
class TclCommandMillSlots(TclCommandSignaled):
"""
Tcl shell command to Create Geometry Object for milling holes from Excellon.
example:
millholes my_drill -tools 1,2,3 -tooldia 0.1 -outname mill_holes_geo
"""
# List of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon)
aliases = ['millslots', 'mills']
description = '%s %s' % ("--", "Create a Geometry Object for milling slot holes from Excellon.")
# 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([
('milled_dias', str),
('outname', str),
('tooldia', float),
('use_thread', str),
('diatol', float)
])
# 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': "Create a Geometry Object for milling slot holes from Excellon.",
'args': collections.OrderedDict([
('name', 'Name of the Excellon Object. Required.'),
('milled_dias', 'Comma separated tool diameters of the slots to be milled (example: 0.6, 1.0 or 3.125).\n'
'Exception: if you enter "all" then the slots for all tools will be milled.\n'
'WARNING: no spaces are allowed in the list of tools.\n'
'As a precaution you can enclose them with quotes.'),
('tooldia', 'Diameter of the milling tool (example: 0.1).'),
('outname', 'Name of object to be created holding the milled geometries.'),
('use_thread', 'If to use multithreading: True (1) or False (0).'),
('diatol', 'Tolerance. Percentange (0.0 ... 100.0) within which dias in milled_dias will be judged to be '
'the same as the ones in the tools from the Excellon object. E.g: if in milled_dias we have a '
'diameter with value 1.0, in the Excellon we have a tool with dia = 1.05 and we set a tolerance '
'diatol = 5.0 then the slots with the dia = (0.95 ... 1.05) '
'in Excellon will be processed. Float number.')
]),
'examples': ['millslots myslots -milled_dias "0.6,0.8" -tooldia 0.1 -diatol 10 -outname milled_slots',
'mills my_excellon.drl']
}
def execute(self, args, unnamed_args):
"""
: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']
try:
obj = self.app.collection.get_by_name(str(name))
except Exception:
obj = None
self.raise_tcl_error("Could not retrieve object: %s" % name)
if 'outname' not in args:
args['outname'] = name + "_mill_slots"
if 'use_thread' in args:
try:
par = args['use_thread'].capitalize()
except AttributeError:
par = args['use_thread']
args['use_thread'] = bool(eval(par))
else:
args['use_thread'] = False
if not obj.slots:
self.raise_tcl_error("The Excellon object has no slots: %s" % name)
# units = self.app.defaults['units'].upper()
try:
if 'milled_dias' in args and args['milled_dias'] != 'all':
diameters = [x.strip() for x in args['milled_dias'].split(",")]
nr_diameters = len(diameters)
req_tools = set()
for tool in obj.tools:
for req_dia in diameters:
obj_dia_form = float('%.*f' % (obj.decimals, float(obj.tools[tool]["C"])))
req_dia_form = float('%.*f' % (obj.decimals, float(req_dia)))
if 'diatol' in args:
tolerance = args['diatol'] / 100
tolerance = 0.0 if tolerance < 0.0 else tolerance
tolerance = 1.0 if tolerance > 1.0 else tolerance
if math.isclose(obj_dia_form, req_dia_form, rel_tol=tolerance):
req_tools.add(tool)
nr_diameters -= 1
else:
if obj_dia_form == req_dia_form:
req_tools.add(tool)
nr_diameters -= 1
if nr_diameters > 0:
self.raise_tcl_error("One or more tool diameters of the slots to be milled passed to the "
"TclCommand are not actual tool diameters in the Excellon object.")
args['tools'] = req_tools
# no longer needed
del args['milled_dias']
del args['diatol']
# Split and put back. We are passing the whole dictionary later.
# args['milled_dias'] = [x.strip() for x in args['tools'].split(",")]
else:
args['tools'] = 'all'
except Exception as e:
self.raise_tcl_error("Bad tools: %s" % str(e))
if obj.kind != 'excellon':
self.raise_tcl_error('Only Excellon objects can have mill-slots, got %s %s.' % (name, type(obj)))
if self.app.collection.has_promises():
self.raise_tcl_error('!!!Promises exists, but should not here!!!')
try:
# 'name' is not an argument of obj.generate_milling()
del args['name']
# This runs in the background... Is blocking handled?
success, msg = obj.generate_milling_slots(plot=False, **args)
except Exception as e:
success = None
msg = None
self.raise_tcl_error("Operation failed: %s" % str(e))
if not success:
self.raise_tcl_error(msg)

View File

@@ -1,6 +1,7 @@
from ObjectCollection import *
from tclCommands.TclCommand import TclCommandSignaled
import collections
class TclCommandMirror(TclCommandSignaled):
"""
@@ -11,6 +12,8 @@ class TclCommandMirror(TclCommandSignaled):
# old names for backward compatibility (add_poly, add_polygon)
aliases = ['mirror']
description = '%s %s' % ("--", "Will mirror the geometry of a named object. Does not create a new object.")
# Dictionary of types from Tcl command, needs to be ordered.
# For positional arguments
arg_names = collections.OrderedDict([
@@ -22,22 +25,25 @@ class TclCommandMirror(TclCommandSignaled):
option_types = collections.OrderedDict([
('axis', str),
('box', str),
('dist', float)
('origin', str)
])
# array of mandatory options for current Tcl command: required = {'name','outname'}
required = ['name', 'axis']
required = ['name']
# structured help for current command, args needs to be ordered
help = {
'main': "Opens an Excellon file.",
'main': "Will mirror the geometry of a named object. Does not create a new object.",
'args': collections.OrderedDict([
('name', 'Name of the object (Gerber or Excellon) to mirror.'),
('box', 'Name of object which act as box (cutout for example.)'),
('name', 'Name of the object (Gerber, Geometry or Excellon) to be mirrored. Required.'),
('axis', 'Mirror axis parallel to the X or Y axis.'),
('dist', 'Distance of the mirror axis to the X or Y axis.')
('box', 'Name of object which act as box (cutout for example.)'),
('origin', 'Reference point . It is used only if the box is not used. Format (x,y).\n'
'Comma will separate the X and Y coordinates.\n'
'WARNING: no spaces are allowed. If uncertain enclose the two values inside parenthesis.\n'
'See the example.')
]),
'examples': []
'examples': ['mirror obj_name -box box_geo -axis X -origin 3.2,4.7']
}
def execute(self, args, unnamed_args):
@@ -55,29 +61,30 @@ class TclCommandMirror(TclCommandSignaled):
# Get source object.
try:
obj = self.app.collection.get_by_name(str(name))
except:
except Exception:
return "Could not retrieve object: %s" % name
if obj is None:
return "Object not found: %s" % name
if not isinstance(obj, FlatCAMGerber) and \
not isinstance(obj, FlatCAMExcellon) and \
not isinstance(obj, FlatCAMGeometry):
if obj.kind != 'gerber' and obj.kind != 'geometry' and obj.kind != 'excellon':
return "ERROR: Only Gerber, Excellon and Geometry objects can be mirrored."
# Axis
try:
axis = args['axis'].upper()
except KeyError:
return "ERROR: Specify -axis X or -axis Y"
if 'axis' in args:
try:
axis = args['axis'].upper()
except KeyError:
axis = 'Y'
else:
axis = 'Y'
# Box
if 'box' in args:
try:
box = self.app.collection.get_by_name(args['box'])
except:
return "Could not retrieve object box: %s" % args['box']
except Exception:
return "Could not retrieve object: %s" % args['box']
if box is None:
return "Object box not found: %s" % args['box']
@@ -89,20 +96,22 @@ class TclCommandMirror(TclCommandSignaled):
obj.mirror(axis, [px, py])
obj.plot()
return
except Exception as e:
return "Operation failed: %s" % str(e)
else:
# Origin
if 'origin' in args:
try:
dist = float(args['dist'])
origin_val = eval(args['origin'])
x = float(origin_val[0])
y = float(origin_val[1])
except KeyError:
dist = 0.0
x, y = (0, 0)
except ValueError:
return "Invalid distance: %s" % args['dist']
return "Invalid distance: %s" % str(args['origin'])
try:
obj.mirror(axis, [dist, dist])
obj.plot()
obj.mirror(axis, [x, y])
except Exception as e:
return "Operation failed: %s" % str(e)

View File

@@ -1,6 +1,7 @@
from ObjectCollection import *
from tclCommands.TclCommand import TclCommand
import collections
class TclCommandNew(TclCommand):
"""
@@ -10,6 +11,8 @@ class TclCommandNew(TclCommand):
# array of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon)
aliases = ['new']
description = '%s %s' % ("--", "Starts a new project. Clears objects from memory.")
# dictionary of types from Tcl command, needs to be ordered
arg_names = collections.OrderedDict()
@@ -23,7 +26,7 @@ class TclCommandNew(TclCommand):
help = {
'main': "Starts a new project. Clears objects from memory.",
'args': collections.OrderedDict(),
'examples': []
'examples': ['new']
}
def execute(self, args, unnamed_args):
@@ -36,4 +39,4 @@ class TclCommandNew(TclCommand):
:return: None or exception
"""
self.app.on_file_new()
self.app.on_file_new(cli=True)

View File

@@ -0,0 +1,61 @@
# ##########################################################
# FlatCAM: 2D Post-processing for Manufacturing #
# File Author: Marius Adrian Stanciu (c) #
# Date: 8/17/2019 #
# MIT Licence #
# ##########################################################
from tclCommands.TclCommand import TclCommandSignaled
import collections
class TclCommandNewExcellon(TclCommandSignaled):
"""
Tcl shell command to subtract polygon from the given Geometry object.
"""
# array of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon)
aliases = ['new_excellon']
description = '%s %s' % ("--", "Creates a new empty Excellon object.")
# Dictionary of types from Tcl command, needs to be ordered.
# For positional arguments
arg_names = collections.OrderedDict([
('name', str)
])
# Dictionary of types from Tcl command, needs to be ordered.
# For options like -optionname value
option_types = collections.OrderedDict([
])
# array of mandatory options for current Tcl command: required = {'name','outname'}
required = []
# structured help for current command, args needs to be ordered
help = {
'main': "Creates a new empty Excellon object.",
'args': collections.OrderedDict([
('name', 'New object name.'),
]),
'examples': ['new_excellon my_excellon', 'new_excellon']
}
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
"""
if 'name' in args:
name = args['name']
else:
name = 'new_exc'
self.app.app_obj.new_object('excellon', name, lambda x, y: None, plot=False)

View File

@@ -1,6 +1,7 @@
from ObjectCollection import *
from tclCommands.TclCommand import TclCommandSignaled
import collections
class TclCommandNewGeometry(TclCommandSignaled):
"""
@@ -10,6 +11,8 @@ class TclCommandNewGeometry(TclCommandSignaled):
# array of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon)
aliases = ['new_geometry']
description = '%s %s' % ("--", "Creates a new empty Geometry object.")
# Dictionary of types from Tcl command, needs to be ordered.
# For positional arguments
arg_names = collections.OrderedDict([
@@ -23,15 +26,16 @@ class TclCommandNewGeometry(TclCommandSignaled):
])
# array of mandatory options for current Tcl command: required = {'name','outname'}
required = ['name']
required = []
# structured help for current command, args needs to be ordered
help = {
'main': "Creates a new empty geometry object.",
'main': "Creates a new empty Geometry object.",
'args': collections.OrderedDict([
('name', 'New object name.'),
]),
'examples': []
'examples': ['new_geometry\n'
'new_geometry my_new_geo']
}
def execute(self, args, unnamed_args):
@@ -43,7 +47,9 @@ class TclCommandNewGeometry(TclCommandSignaled):
without -somename and we do not have them in known arg_names
:return: None or exception
"""
if 'name' in args:
name = args['name']
else:
name = 'new_geo'
name = args['name']
self.app.new_object('geometry', str(name), lambda x, y: None)
self.app.app_obj.new_object('geometry', str(name), lambda x, y: None, plot=False)

View File

@@ -0,0 +1,78 @@
# ##########################################################
# FlatCAM: 2D Post-processing for Manufacturing #
# File Author: Marius Adrian Stanciu (c) #
# Date: 8/17/2019 #
# MIT Licence #
# ##########################################################
from tclCommands.TclCommand import TclCommandSignaled
import collections
class TclCommandNewGerber(TclCommandSignaled):
"""
Tcl shell command to subtract polygon from the given Geometry object.
"""
# array of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon)
aliases = ['new_gerber']
description = '%s %s' % ("--", "Creates a new empty Gerber object.")
# Dictionary of types from Tcl command, needs to be ordered.
# For positional arguments
arg_names = collections.OrderedDict([
('name', str)
])
# Dictionary of types from Tcl command, needs to be ordered.
# For options like -optionname value
option_types = collections.OrderedDict([
])
# array of mandatory options for current Tcl command: required = {'name','outname'}
required = []
# structured help for current command, args needs to be ordered
help = {
'main': "Creates a new empty Gerber object.",
'args': collections.OrderedDict([
('name', 'New object name.'),
]),
'examples': ['new_gerber', 'new_gerber my_new_gerber_name']
}
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
"""
if 'name' in args:
name = args['name']
else:
name = 'new_grb'
def initialize(grb_obj, app_obj):
grb_obj.multitool = False
grb_obj.source_file = []
grb_obj.multigeo = False
grb_obj.follow = False
grb_obj.apertures = {}
grb_obj.solid_geometry = []
try:
grb_obj.options['xmin'] = 0
grb_obj.options['ymin'] = 0
grb_obj.options['xmax'] = 0
grb_obj.options['ymax'] = 0
except KeyError:
pass
self.app.app_obj.new_object('gerber', name, initialize, plot=False)

View File

@@ -0,0 +1,108 @@
from tclCommands.TclCommand import TclCommand
from shapely.ops import unary_union
import collections
import gettext
import appTranslation as fcTranslate
import builtins
fcTranslate.apply_language('strings')
if '_' not in builtins.__dict__:
_ = gettext.gettext
class TclCommandNregions(TclCommand):
"""
Tcl shell command to follow a Gerber file
"""
# array of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon)
aliases = ['non_copper_regions', 'ncr']
description = '%s %s' % ("--", "Creates a Geometry object with the non-copper regions.")
# 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([
('outname', str),
('margin', float),
('rounded', str)
])
# array of mandatory options for current Tcl command: required = {'name','outname'}
required = ['name']
# structured help for current command, args needs to be ordered
help = {
'main': "Creates a Geometry object with the non-copper regions.",
'args': collections.OrderedDict([
('name', 'Object name for which to create non-copper regions. String. Required.'),
('outname', 'Name of the resulting Geometry object. String.'),
('margin', "Specify the edge of the PCB by drawing a box around all objects with this minimum distance. "
"Float number."),
('rounded', "Resulting geometry will have rounded corners. True (1) or False (0).")
]),
'examples': ['ncr name -margin 0.1 -rounded True -outname name_ncr']
}
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 + "_noncopper"
obj = self.app.collection.get_by_name(name)
if obj is None:
self.raise_tcl_error("%s: %s" % (_("Object not found"), name))
if obj.kind != 'gerber' and obj.kind != 'geometry':
self.raise_tcl_error('%s %s: %s.' % (_("Expected GerberObject or GeometryObject, got"), name, type(obj)))
if 'margin' not in args:
args['margin'] = float(self.app.defaults["gerber_noncoppermargin"])
margin = float(args['margin'])
if 'rounded' in args:
try:
par = args['rounded'].capitalize()
except AttributeError:
par = args['rounded']
rounded = bool(eval(par))
else:
rounded = bool(eval(self.app.defaults["gerber_noncopperrounded"]))
del args['name']
try:
def geo_init(geo_obj, app_obj):
assert geo_obj.kind == 'geometry'
geo = unary_union(obj.solid_geometry)
bounding_box = geo.envelope.buffer(float(margin))
if not rounded:
bounding_box = bounding_box.envelope
non_copper = bounding_box.difference(geo)
geo_obj.solid_geometry = non_copper
self.app.app_obj.new_object("geometry", args['outname'], geo_init, plot=False)
except Exception as e:
return "Operation failed: %s" % str(e)
# in the end toggle the visibility of the origin object so we can see the generated Geometry
self.app.collection.get_by_name(name).ui.plot_cb.toggle()

View File

@@ -1,6 +1,7 @@
from ObjectCollection import *
from tclCommands.TclCommand import TclCommand
import collections
class TclCommandOffset(TclCommand):
"""
@@ -13,6 +14,8 @@ class TclCommandOffset(TclCommand):
# List of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon)
aliases = ['offset']
description = '%s %s' % ("--", "Will offset the geometry of a named object. Does not create a new object.")
# Dictionary of types from Tcl command, needs to be ordered
arg_names = collections.OrderedDict([
('name', str),
@@ -22,21 +25,22 @@ class TclCommandOffset(TclCommand):
# Dictionary of types from Tcl command, needs to be ordered , this is for options like -optionname value
option_types = collections.OrderedDict([
('x', float),
('y', float)
])
# array of mandatory options for current Tcl command: required = {'name','outname'}
required = ['name', 'x', 'y']
required = ['name']
# structured help for current command, args needs to be ordered
help = {
'main': "Changes the position of the object.",
'main': "Changes the position of the object on X and/or Y axis.",
'args': collections.OrderedDict([
('name', 'Name of the object to offset.'),
('x', 'Offset distance in the X axis.'),
('y', 'Offset distance in the Y axis')
('name', 'Name of the object to offset. Required.'),
('x', 'Offset distance in the X axis. If it is not used it will be assumed to be 0.0'),
('y', 'Offset distance in the Y axis. If it is not used it will be assumed to be 0.0')
]),
'examples': ['offset my_geometry 1.2 -0.3']
'examples': ['offset my_geometry -x 1.2 -y -0.3', 'offset my_geometry -x 1.0']
}
def execute(self, args, unnamed_args):
@@ -48,6 +52,12 @@ class TclCommandOffset(TclCommand):
"""
name = args['name']
x, y = args['x'], args['y']
off_x = args['x'] if 'x' in args else 0.0
off_y = args['y'] if 'y' in args else 0.0
x, y = float(off_x), float(off_y)
if (x, y) == (0.0, 0.0):
return
self.app.collection.get_by_name(name).offset((x, y))

View File

@@ -0,0 +1,98 @@
from tclCommands.TclCommand import TclCommandSignaled
import collections
class TclCommandOpenDXF(TclCommandSignaled):
"""
Tcl shell command to open an DXF file as a Geometry/Gerber Object.
"""
# array of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon)
aliases = ['open_dxf']
description = '%s %s' % ("--", "Open a DXF file as a Geometry (or Gerber) Object.")
# dictionary of types from Tcl command, needs to be ordered
arg_names = collections.OrderedDict([
('filename', str)
])
# dictionary of types from Tcl command, needs to be ordered , this is for options like -optionname value
option_types = collections.OrderedDict([
('type', str),
('outname', str)
])
# array of mandatory options for current Tcl command: required = {'name','outname'}
required = ['filename']
# structured help for current command, args needs to be ordered
help = {
'main': "Open a DXF file as a Geometry (or Gerber) Object.",
'args': collections.OrderedDict([
('filename', 'Absolute path to file to open. Required.\n'
'WARNING: no spaces are allowed. If unsure enclose the entire path with quotes.'),
('type', 'Open as a Gerber or Geometry (default) object. Values can be: "geometry" or "gerber"'),
('outname', 'Name of the resulting Geometry object.')
]),
'examples': ['open_dxf D:\\my_beautiful_svg_file.SVG']
}
def execute(self, args, unnamed_args):
"""
execute current TCL shell command
:param args: array of known named arguments and options
:param unnamed_args: array of other values which were passed into command
without -somename and we do not have them in known arg_names
:return: None or exception
"""
# How the object should be initialized
def obj_init(geo_obj, app_obj):
# if geo_obj.kind != 'geometry' and geo_obj.kind != 'gerber':
# self.raise_tcl_error('Expected Geometry or Gerber, got %s %s.' % (outname, type(geo_obj)))
if obj_type == "geometry":
geo_obj.import_dxf_as_geo(filename, units=units)
elif obj_type == "gerber":
geo_obj.import_dxf_as_gerber(filename, units=units)
else:
return "fail"
filename = args['filename']
if 'outname' in args:
outname = args['outname']
else:
outname = filename.split('/')[-1].split('\\')[-1]
if 'type' in args:
obj_type = str(args['type']).lower()
else:
obj_type = 'geometry'
if obj_type != "geometry" and obj_type != "gerber":
self.raise_tcl_error("Option type can be 'geometry' or 'gerber' only, got '%s'." % obj_type)
units = self.app.defaults['units'].upper()
with self.app.proc_container.new('%s' % _("Opening ...")):
# Object creation
ret_val = self.app.app_obj.new_object(obj_type, outname, obj_init, plot=False)
if ret_val == 'fail':
filename = self.app.defaults['global_tcl_path'] + '/' + outname
ret_val = self.app.app_obj.new_object(obj_type, outname, obj_init, plot=False)
self.app.shell.append_output(
"No path provided or path is wrong. Using the default Path... \n")
if ret_val == 'fail':
return "Failed. The OpenDXF command was used but could not open the DXF file"
# Register recent file
self.app.file_opened.emit("dxf", filename)
# GUI feedback
self.app.inform.emit("Opened: " + filename)

View File

@@ -1,6 +1,7 @@
from ObjectCollection import *
from tclCommands.TclCommand import TclCommandSignaled
import collections
class TclCommandOpenExcellon(TclCommandSignaled):
"""
@@ -10,6 +11,8 @@ class TclCommandOpenExcellon(TclCommandSignaled):
# array of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon)
aliases = ['open_excellon']
description = '%s %s' % ("--", "Opens an Excellon file, parse it and create a Excellon object from it.")
# Dictionary of types from Tcl command, needs to be ordered.
# For positional arguments
arg_names = collections.OrderedDict([
@@ -27,12 +30,15 @@ class TclCommandOpenExcellon(TclCommandSignaled):
# structured help for current command, args needs to be ordered
help = {
'main': "Opens an Excellon file.",
'main': "Opens an Excellon file, parse it and create a Excellon object from it.",
'args': collections.OrderedDict([
('filename', 'Path to file to open.'),
('filename', 'Absolute path to file to open. Required.\n'
'WARNING: no spaces are allowed. If unsure enclose the entire path with quotes.'),
('outname', 'Name of the resulting Excellon object.')
]),
'examples': []
'examples': ['open_excellon D:\\my_excellon_file.DRL',
'open_excellon "D:\\my_excellon_file with spaces in the name.DRL"',
'open_excellon path_to_file']
}
def execute(self, args, unnamed_args):
@@ -47,4 +53,10 @@ class TclCommandOpenExcellon(TclCommandSignaled):
filename = args.pop('filename')
self.app.open_excellon(filename, **args)
# if ' ' in filename:
# return "The absolute path to the project file contain spaces which is not allowed.\n" \
# "Please enclose the path within quotes."
args['plot'] = False
args['from_tcl'] = True
self.app.f_handlers.open_excellon(filename, **args)

View File

@@ -1,6 +1,7 @@
from ObjectCollection import *
from tclCommands.TclCommand import TclCommandSignaled
import collections
class TclCommandOpenGCode(TclCommandSignaled):
"""
@@ -11,6 +12,8 @@ class TclCommandOpenGCode(TclCommandSignaled):
# backward compatibility (add_poly, add_polygon)
aliases = ['open_gcode']
description = '%s %s' % ("--", "Opens an GCode file, parse it and create a GCode object from it.")
# Dictionary of types from Tcl command, needs to be ordered.
# For positional arguments
arg_names = collections.OrderedDict([
@@ -28,12 +31,14 @@ class TclCommandOpenGCode(TclCommandSignaled):
# structured help for current command, args needs to be ordered
help = {
'main': "Opens a G-Code file.",
'main': "Opens an GCode file, parse it and create a GCode object from it.",
'args': collections.OrderedDict([
('filename', 'Path to file to open.'),
('filename', 'Absolute path to file to open. Required.\n'
'WARNING: no spaces are allowed. If unsure enclose the entire path with quotes.'),
('outname', 'Name of the resulting CNCJob object.')
]),
'examples': []
'examples': ['open_gcode D:\\my_gcode_file.NC',
'open_gcode "D:\\my_gcode_file with spaces in the name.TXT"']
}
def execute(self, args, unnamed_args):
@@ -45,5 +50,12 @@ class TclCommandOpenGCode(TclCommandSignaled):
without -somename and we do not have them in known arg_names
:return: None or exception
"""
args['plot'] = False
args['from_tcl'] = True
filename = args.pop("filename")
self.app.open_gcode(args['filename'], **args)
# if ' ' in filename:
# return "The absolute path to the project file contain spaces which is not allowed.\n" \
# "Please enclose the path within quotes."
self.app.f_handlers.open_gcode(filename, **args)

View File

@@ -1,5 +1,7 @@
from ObjectCollection import *
from tclCommands.TclCommand import TclCommandSignaled
from camlib import ParseError
import collections
class TclCommandOpenGerber(TclCommandSignaled):
@@ -10,6 +12,8 @@ class TclCommandOpenGerber(TclCommandSignaled):
# array of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon)
aliases = ['open_gerber']
description = '%s %s' % ("--", "Opens an Gerber file, parse it and create a Gerber object from it.")
# dictionary of types from Tcl command, needs to be ordered
arg_names = collections.OrderedDict([
('filename', str)
@@ -27,10 +31,12 @@ class TclCommandOpenGerber(TclCommandSignaled):
help = {
'main': "Opens a Gerber file.",
'args': collections.OrderedDict([
('filename', 'Path to file to open.'),
('filename', 'Absolute path to file to open. Required.\n'
'WARNING: no spaces are allowed. If unsure enclose the entire path with quotes.'),
('outname', 'Name of the resulting Gerber object.')
]),
'examples': []
'examples': ["open_gerber gerber_object_path -outname bla",
'open_gerber "D:\\my_gerber_file with spaces in the name.GRB"']
}
def execute(self, args, unnamed_args):
@@ -43,50 +49,17 @@ class TclCommandOpenGerber(TclCommandSignaled):
:return: None or exception
"""
# How the object should be initialized
def obj_init(gerber_obj, app_obj):
if 'follow' in args:
self.raise_tcl_error("The 'follow' parameter is obsolete. To create 'follow' geometry use the 'follow' "
"parameter for the Tcl Command isolate()")
if not isinstance(gerber_obj, Geometry):
self.raise_tcl_error('Expected FlatCAMGerber, got %s %s.' % (outname, type(gerber_obj)))
# Opening the file happens here
self.app.progress.emit(30)
try:
gerber_obj.parse_file(filename)
except IOError:
app_obj.inform.emit("[ERROR_NOTCL] Failed to open file: %s " % filename)
app_obj.progress.emit(0)
self.raise_tcl_error('Failed to open file: %s' % filename)
except ParseError as e:
app_obj.inform.emit("[ERROR_NOTCL] Failed to parse file: %s, %s " % (filename, str(e)))
app_obj.progress.emit(0)
self.log.error(str(e))
return
# Further parsing
app_obj.progress.emit(70)
filename = args['filename']
filename = args.pop('filename')
if 'outname' in args:
outname = args['outname']
outname = args.pop('outname')
else:
outname = filename.split('/')[-1].split('\\')[-1]
if 'follow' in args:
self.raise_tcl_error("The 'follow' parameter is obsolete. To create 'follow' geometry use the 'follow' parameter for the Tcl Command isolate()")
with self.app.proc_container.new("Opening Gerber"):
# Object creation
self.app.new_object("gerber", outname, obj_init)
# Register recent file
self.app.file_opened.emit("gerber", filename)
self.app.progress.emit(100)
# GUI feedback
self.app.inform.emit("[success] Opened: " + filename)
args['plot'] = False
args['from_tcl'] = True
self.app.f_handlers.open_gerber(filename, outname, **args)

View File

@@ -1,6 +1,7 @@
from ObjectCollection import *
from tclCommands.TclCommand import TclCommandSignaled
import collections
class TclCommandOpenProject(TclCommandSignaled):
"""
@@ -10,6 +11,8 @@ class TclCommandOpenProject(TclCommandSignaled):
# array of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon)
aliases = ['open_project']
description = '%s %s' % ("--", "Opens an FlatCAm project file, parse it and recreate all the objects.")
# Dictionary of types from Tcl command, needs to be ordered.
# For positional arguments
arg_names = collections.OrderedDict([
@@ -27,11 +30,13 @@ class TclCommandOpenProject(TclCommandSignaled):
# structured help for current command, args needs to be ordered
help = {
'main': "Opens a FlatCAM project.",
'main': "Opens an FlatCAm project file, parse it and recreate all the objects.",
'args': collections.OrderedDict([
('filename', 'Path to file to open.'),
('filename', 'Absolute path to file to open. Required.\n'
'WARNING: no spaces are allowed. If unsure enclose the entire path with quotes.'),
]),
'examples': []
'examples': ['open_project D:\\my_project_file.FlatPrj',
'open_project "D:\\my_project_file with spaces in the name.FlatPrj"']
}
def execute(self, args, unnamed_args):
@@ -43,5 +48,9 @@ class TclCommandOpenProject(TclCommandSignaled):
without -somename and we do not have them in known arg_names
:return: None or exception
"""
filename = args['filename']
# if ' ' in filename:
# return "The absolute path to the project file contain spaces which is not allowed.\n" \
# "Please enclose the path within quotes."
self.app.open_project(args['filename'])
self.app.f_handlers.open_project(filename, cli=True, plot=False, from_tcl=True)

View File

@@ -0,0 +1,93 @@
from tclCommands.TclCommand import TclCommandSignaled
import collections
class TclCommandOpenSVG(TclCommandSignaled):
"""
Tcl shell command to import an SVG file as a Geometry Object.
"""
# array of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon)
aliases = ['open_svg']
description = '%s %s' % ("--", "Open a SVG file as a Geometry (or Gerber) Object.")
# dictionary of types from Tcl command, needs to be ordered
arg_names = collections.OrderedDict([
('filename', str)
])
# dictionary of types from Tcl command, needs to be ordered , this is for options like -optionname value
option_types = collections.OrderedDict([
('type', str),
('outname', str)
])
# array of mandatory options for current Tcl command: required = {'name','outname'}
required = ['filename']
# structured help for current command, args needs to be ordered
help = {
'main': "Open a SVG file as a Geometry (or Gerber) Object.",
'args': collections.OrderedDict([
('filename', 'Absolute path to file to open. Required.\n'
'WARNING: no spaces are allowed. If unsure enclose the entire path with quotes.'),
('type', 'Open as a Gerber or Geometry (default) object. Values can be: "geometry" or "gerber"'),
('outname', 'Name of the resulting Geometry object.')
]),
'examples': ['open_svg D:\\my_beautiful_svg_file.SVG']
}
def execute(self, args, unnamed_args):
"""
execute current TCL shell command
:param args: array of known named arguments and options
:param unnamed_args: array of other values which were passed into command
without -somename and we do not have them in known arg_names
:return: None or exception
"""
# How the object should be initialized
def obj_init(geo_obj, app_obj):
# if geo_obj.kind != 'geometry' or geo_obj.kind != 'gerber':
# self.raise_tcl_error('Expected Geometry or Gerber, got %s %s.' % (outname, type(geo_obj)))
geo_obj.import_svg(filename, obj_type, units=units)
filename = args['filename']
if 'outname' in args:
outname = args['outname']
else:
outname = filename.split('/')[-1].split('\\')[-1]
if 'type' in args:
obj_type = args['type'].lower()
else:
obj_type = 'geometry'
if obj_type != "geometry" and obj_type != "gerber":
self.raise_tcl_error("Option type can be 'geometry' or 'gerber' only, got '%s'." % obj_type)
units = self.app.defaults['units'].upper()
with self.app.proc_container.new(_("Working ...")):
# Object creation
ret_val = self.app.app_obj.new_object(obj_type, outname, obj_init, plot=False)
if ret_val == 'fail':
filename = self.app.defaults['global_tcl_path'] + '/' + outname
ret_val = self.app.app_obj.new_object(obj_type, outname, obj_init, plot=False)
self.app.shell.append_output(
"No path provided or path is wrong. Using the default Path... \n")
if ret_val == 'fail':
return "Failed. The OpenSVG command was used but could not open the SVG file"
# Register recent file
self.app.file_opened.emit("svg", filename)
# GUI feedback
self.app.inform.emit("Opened: " + filename)

View File

@@ -1,6 +1,7 @@
from ObjectCollection import *
from tclCommands.TclCommand import TclCommandSignaled
import collections
class TclCommandOptions(TclCommandSignaled):
"""
@@ -10,6 +11,9 @@ class TclCommandOptions(TclCommandSignaled):
# array of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon)
aliases = ['options']
description = '%s %s' % ("--", "Will return the options (settings) for an object as a string "
"with values separated by \\n.")
# Dictionary of types from Tcl command, needs to be ordered.
# For positional arguments
arg_names = collections.OrderedDict([
@@ -27,11 +31,11 @@ class TclCommandOptions(TclCommandSignaled):
# structured help for current command, args needs to be ordered
help = {
'main': "Shows the settings for an object.",
'main': "Will return the options (settings) for an object as a string with values separated by \\n.",
'args': collections.OrderedDict([
('name', 'Object name.'),
('name', 'Object name for which to return the options. Required.'),
]),
'examples': []
'examples': ['options obj_name']
}
def execute(self, args, unnamed_args):

View File

@@ -1,8 +1,20 @@
from ObjectCollection import *
from tclCommands.TclCommand import TclCommandSignaled
from tclCommands.TclCommand import TclCommand
import collections
import logging
import gettext
import appTranslation as fcTranslate
import builtins
fcTranslate.apply_language('strings')
if '_' not in builtins.__dict__:
_ = gettext.gettext
log = logging.getLogger('base')
class TclCommandPaint(TclCommandSignaled):
class TclCommandPaint(TclCommand):
"""
Paint the interior of polygons
"""
@@ -10,37 +22,61 @@ class TclCommandPaint(TclCommandSignaled):
# Array of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon)
aliases = ['paint']
description = '%s %s' % ("--", "Paint polygons in the specified object by covering them with toolpaths.")
# dictionary of types from Tcl command, needs to be ordered
arg_names = collections.OrderedDict([
('name', str),
('tooldia', float),
('overlap', float)
])
# dictionary of types from Tcl command, needs to be ordered , this is for options like -optionname value
option_types = collections.OrderedDict([
('tooldia', str),
('overlap', float),
('order', str),
('offset', float),
('method', str),
('connect', str),
('contour', str),
('all', str),
('single', str),
('ref', str),
('box', str),
('outname', str),
('all', bool),
('x', float),
('y', float)
])
# array of mandatory options for current Tcl command: required = {'name','outname'}
required = ['name', 'tooldia', 'overlap']
required = ['name']
# structured help for current command, args needs to be ordered
help = {
'main': "Paint polygons",
'main': "Paint polygons in the specified object by covering them with toolpaths.\n"
"Can use only one of the parameters: 'all', 'box', 'single'.",
'args': collections.OrderedDict([
('name', 'Name of the source Geometry object.'),
('tooldia', 'Diameter of the tool to be used.'),
('overlap', 'Fraction of the tool diameter to overlap cuts.'),
('outname', 'Name of the resulting Geometry object.'),
('all', 'Paint all polygons in the object.'),
('x', 'X value of coordinate for the selection of a single polygon.'),
('y', 'Y value of coordinate for the selection of a single polygon.')
('name', 'Name of the source Geometry object. String.'),
('tooldia', 'Diameter of the tools to be used. Can be a comma separated list of diameters.\n'
'WARNING: No space is allowed between tool diameters. E.g: correct: 0.5,1 / incorrect: 0.5, 1'),
('overlap', 'Percentage of tool diameter to overlap current pass over previous pass. Float [0, 99.9999]\n'
'E.g: for a 25% from tool diameter overlap use -overlap 25'),
('offset', 'Distance from the polygon border where painting starts. Float number.'),
('order', 'Can have the values: "no", "fwd" and "rev". String.\n'
'It is useful when there are multiple tools in tooldia parameter.\n'
'"no" -> the order used is the one provided.\n'
'"fwd" -> tools are ordered from smallest to biggest.\n'
'"rev" -> tools are ordered from biggest to smallest.'),
('method', 'Algorithm for painting. Can be: "standard", "seed", "lines", "laser_lines", "combo".'),
('connect', 'Draw lines to minimize tool lifts. True (1) or False (0)'),
('contour', 'Cut around the perimeter of the painting. True (1) or False (0)'),
('all', 'If used, paint all polygons in the object.'),
('box', 'name of the object to be used as paint reference. String.'),
('single', 'Value is in format x,y or (x,y). Example: 2.0,1.1\n'
'If used will paint a single polygon specified by "x" and "y" values.\n'
'WARNING: No spaces allowed in the value. Use dot decimals separator.'),
('outname', 'Name of the resulting Geometry object. String. No spaces.'),
]),
'examples': []
'examples': ["paint obj_name -tooldia 0.3 -offset 0.1 -method 'seed' -all",
"paint obj_name -tooldia 0.3 -offset 0.1 -method 'seed' -single 3.3,2.0"]
}
def execute(self, args, unnamed_args):
@@ -54,31 +90,212 @@ class TclCommandPaint(TclCommandSignaled):
"""
name = args['name']
tooldia = args['tooldia']
overlap = args['overlap']
# Get source object.
try:
obj = self.app.collection.get_by_name(str(name))
except Exception as e:
log.debug("TclCommandPaint.execute() --> %s" % str(e))
self.raise_tcl_error("%s: %s" % (_("Could not retrieve object"), name))
return "Could not retrieve object: %s" % name
if 'tooldia' in args:
tooldia = str(args['tooldia'])
else:
tooldia = str(self.app.defaults["tools_paint_tooldia"])
if 'overlap' in args:
overlap = float(args['overlap']) / 100.0
else:
overlap = float(self.app.defaults["tools_paint_overlap"]) / 100.0
if 'order' in args:
order = args['order']
else:
order = str(self.app.defaults["tools_paint_order"])
if 'offset' in args:
offset = float(args['offset'])
else:
offset = float(self.app.defaults["tools_paint_offset"])
if 'method' in args:
method = args['method']
if method == "standard":
method = _("Standard")
elif method == "seed":
method = _("Seed")
elif method == "lines":
method = _("Lines")
elif method == "laser_lines":
method = _("Laser_lines")
else:
method = _("Combo")
else:
method = str(self.app.defaults["tools_paint_method"])
if 'connect' in args:
try:
par = args['connect'].capitalize()
except AttributeError:
par = args['connect']
connect = bool(eval(par))
else:
connect = bool(eval(str(self.app.defaults["tools_paint_connect"])))
if 'contour' in args:
try:
par = args['contour'].capitalize()
except AttributeError:
par = args['contour']
contour = bool(eval(par))
else:
contour = bool(eval(str(self.app.defaults["tools_paint_contour"])))
if 'outname' in args:
outname = args['outname']
else:
outname = name + "_paint"
obj = self.app.collection.get_by_name(name)
# used only to have correct information's in the obj.tools[tool]['data'] dict
if "all" in args:
select = _("All")
elif "single" in args:
select = _("Polygon Selection")
else:
select = _("Reference Object")
try:
tools = [float(eval(dia)) for dia in tooldia.split(",") if dia != '']
except AttributeError:
tools = [float(tooldia)]
# store here the default data for Geometry Data
default_data = {}
default_data.update({
"name": outname,
"plot": False,
"cutz": self.app.defaults["geometry_cutz"],
"vtipdia": float(self.app.defaults["tools_paint_tipdia"]),
"vtipangle": float(self.app.defaults["tools_paint_tipangle"]),
"travelz": self.app.defaults["geometry_travelz"],
"feedrate": self.app.defaults["geometry_feedrate"],
"feedrate_z": self.app.defaults["geometry_feedrate_z"],
"feedrate_rapid": self.app.defaults["geometry_feedrate_rapid"],
"dwell": self.app.defaults["geometry_dwell"],
"dwelltime": self.app.defaults["geometry_dwelltime"],
"multidepth": self.app.defaults["geometry_multidepth"],
"ppname_g": self.app.defaults["geometry_ppname_g"],
"depthperpass": self.app.defaults["geometry_depthperpass"],
"extracut": self.app.defaults["geometry_extracut"],
"extracut_length": self.app.defaults["geometry_extracut_length"],
"toolchange": self.app.defaults["geometry_toolchange"],
"toolchangez": self.app.defaults["geometry_toolchangez"],
"endz": self.app.defaults["geometry_endz"],
"endxy": self.app.defaults["geometry_endxy"],
"spindlespeed": self.app.defaults["geometry_spindlespeed"],
"toolchangexy": self.app.defaults["geometry_toolchangexy"],
"startz": self.app.defaults["geometry_startz"],
"area_exclusion": self.app.defaults["geometry_area_exclusion"],
"area_shape": self.app.defaults["geometry_area_shape"],
"area_strategy": self.app.defaults["geometry_area_strategy"],
"area_overz": float(self.app.defaults["geometry_area_overz"]),
"tooldia": tooldia,
"tools_paint_offset": offset,
"tools_paint_method": method,
"tools_paint_selectmethod": select,
"tools_paint_connect": connect,
"tools_paint_contour": contour,
"tools_paint_overlap": overlap
})
paint_tools = {}
tooluid = 0
for tool in tools:
tooluid += 1
paint_tools.update({
int(tooluid): {
'tooldia': self.app.dec_format(float(tool), self.app.decimals),
'offset': 'Path',
'offset_value': 0.0,
'type': 'Iso',
'tool_type': 'C1',
'data': dict(default_data),
'solid_geometry': []
}
})
paint_tools[int(tooluid)]['data']['tooldia'] = self.app.dec_format(float(tool), self.app.decimals)
if obj is None:
self.raise_tcl_error("Object not found: %s" % name)
return "Object not found: %s" % name
if not isinstance(obj, Geometry):
self.raise_tcl_error('Expected Geometry, got %s %s.' % (name, type(obj)))
if 'all' in args and args['all']:
obj.paint_poly_all(tooldia, overlap, outname)
# Paint all polygons in the painted object
if 'all' in args:
self.app.paint_tool.paint_poly_all(obj=obj,
tooldia=tooldia,
order=order,
method=method,
outname=outname,
tools_storage=paint_tools,
plot=False,
run_threaded=False)
return
if 'x' not in args or 'y' not in args:
self.raise_tcl_error('Expected -all 1 or -x <value> and -y <value>.')
# Paint single polygon in the painted object
if 'single' in args:
if not args['single'] or args['single'] == '':
self.raise_tcl_error('%s Got: %s' %
(_("Expected a tuple value like -single 3.2,0.1."), str(args['single'])))
else:
coords_xy = [float(eval(a)) for a in args['single'].split(",") if a != '']
x = args['x']
y = args['y']
if coords_xy and len(coords_xy) != 2:
self.raise_tcl_error('%s Got: %s' %
(_("Expected a tuple value like -single 3.2,0.1."), str(coords_xy)))
x = coords_xy[0]
y = coords_xy[1]
obj.paint_poly_single_click([x, y], tooldia, overlap, outname)
ret_val = self.app.paint_tool.paint_poly(obj=obj,
inside_pt=[x, y],
tooldia=tooldia,
order=order,
method=method,
outname=outname,
tools_storage=paint_tools,
plot=False,
run_threaded=False)
if ret_val == 'fail':
return "Could not find a Polygon at the specified location."
return
# Paint all polygons found within the box object from the the painted object
if 'box' in args:
box_name = args['box']
if box_name is None:
self.raise_tcl_error('%s' % _("Expected -box <value>."))
# Get box source object.
try:
box_obj = self.app.collection.get_by_name(str(box_name))
except Exception as e:
log.debug("TclCommandPaint.execute() --> %s" % str(e))
self.raise_tcl_error("%s: %s" % (_("Could not retrieve object"), name))
return "Could not retrieve object: %s" % name
self.app.paint_tool.paint_poly_ref(obj=obj,
sel_obj=box_obj,
tooldia=tooldia,
order=order,
method=method,
outname=outname,
tools_storage=paint_tools,
plot=False,
run_threaded=False)
return
self.raise_tcl_error("%s:" % _("None of the following args: 'box', 'single', 'all' were used.\n"
"Paint failed."))
return "None of the following args: 'box', 'single', 'all' were used. Paint failed."

View File

@@ -1,8 +1,13 @@
from ObjectCollection import *
from copy import copy,deepcopy
from tclCommands.TclCommand import TclCommand
import shapely.affinity as affinity
import logging
from copy import deepcopy
import collections
log = logging.getLogger('base')
class TclCommandPanelize(TclCommand):
"""
@@ -13,7 +18,10 @@ class TclCommandPanelize(TclCommand):
"""
# List of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon)
aliases = ['panelize','pan', 'panel']
aliases = ['panelize', 'pan', 'panel']
description = '%s %s' % ("--", "Create a new object with an array of duplicates of the original geometry, "
"arranged in a grid.")
# Dictionary of types from Tcl command, needs to be ordered
arg_names = collections.OrderedDict([
@@ -28,15 +36,15 @@ class TclCommandPanelize(TclCommand):
('spacing_rows', float),
('box', str),
('outname', str),
('threaded', int)
('use_thread', str)
])
# array of mandatory options for current Tcl command: required = {'name','outname'}
required = ['name', 'rows', 'columns']
required = ['name']
# structured help for current command, args needs to be ordered
help = {
'main': 'Rectangular panelizing.',
'main': 'Create a new object with an array of duplicates of the original geometry, arranged in a grid.',
'args': collections.OrderedDict([
('name', 'Name of the object to panelize.'),
('box', 'Name of object which acts as box (cutout for example.)'
@@ -46,9 +54,16 @@ class TclCommandPanelize(TclCommand):
('columns', 'Number of columns.'),
('rows', 'Number of rows;'),
('outname', 'Name of the new geometry object.'),
('threaded', '0 = non-threaded || 1 = threaded')
('use_thread', 'False (0) = non-threaded execution or True (1) = threaded execution')
]),
'examples': []
'examples': [
'panelize obj_name',
'panel obj_name -rows 2 -columns 2 -spacing_columns 0.4 -spacing_rows 1.3 -box box_obj_name '
'-outname panelized_name',
'panel obj_name -columns 2 -box box_obj_name -outname panelized_name',
]
}
def execute(self, args, unnamed_args):
@@ -64,7 +79,7 @@ class TclCommandPanelize(TclCommand):
# Get source object.
try:
obj = self.app.collection.get_by_name(str(name))
except:
except Exception:
return "Could not retrieve object: %s" % name
if obj is None:
@@ -74,37 +89,48 @@ class TclCommandPanelize(TclCommand):
boxname = args['box']
try:
box = self.app.collection.get_by_name(boxname)
except:
except Exception:
return "Could not retrieve object: %s" % name
else:
box = obj
if 'columns' not in args or 'rows' not in args:
return "ERROR: Specify -columns and -rows"
if 'columns' in args:
columns = int(args['columns'])
else:
columns = int(0)
if 'rows' in args:
rows = int(args['rows'])
else:
rows = int(0)
if 'columns' not in args and 'rows' not in args:
return "ERROR: Specify either -columns or -rows. The one not specified it will assumed to be 0"
if 'outname' in args:
outname = args['outname']
else:
outname = name + '_panelized'
if 'threaded' in args:
threaded = args['threaded']
if 'use_thread' in args:
try:
par = args['use_thread'].capitalize()
except AttributeError:
par = args['use_thread']
threaded = bool(eval(par))
else:
threaded = 1
threaded = False
if 'spacing_columns' in args:
spacing_columns = args['spacing_columns']
spacing_columns = int(args['spacing_columns'])
else:
spacing_columns = 5
if 'spacing_rows' in args:
spacing_rows = args['spacing_rows']
spacing_rows = int(args['spacing_rows'])
else:
spacing_rows = 5
rows = args['rows']
columns = args['columns']
xmin, ymin, xmax, ymax = box.bounds()
lenghtx = xmax - xmin + spacing_columns
lenghty = ymax - ymin + spacing_rows
@@ -126,12 +152,12 @@ class TclCommandPanelize(TclCommand):
# objs.append(obj_init)
#
# def initialize_geometry(obj_init, app):
# FlatCAMGeometry.merge(objs, obj_init)
# GeometryObject.merge(objs, obj_init)
#
# def initialize_excellon(obj_init, app):
# # merge expects tools to exist in the target object
# obj_init.tools = obj.tools.copy()
# FlatCAMExcellon.merge(objs, obj_init)
# ExcellonObject.merge(objs, obj_init)
#
# objs = []
# if obj is not None:
@@ -140,20 +166,21 @@ class TclCommandPanelize(TclCommand):
# currentx = 0
# for col in range(columns):
# local_outname = outname + ".tmp." + str(col) + "." + str(row)
# if isinstance(obj, FlatCAMExcellon):
# self.app.new_object("excellon", local_outname, initialize_local_excellon, plot=False,
# if isinstance(obj, ExcellonObject):
# self.app.app_obj.new_object("excellon", local_outname, initialize_local_excellon,
# plot=False,
# autoselected=False)
# else:
# self.app.new_object("geometry", local_outname, initialize_local, plot=False,
# self.app.app_obj.new_object("geometry", local_outname, initialize_local, plot=False,
# autoselected=False)
#
# currentx += lenghtx
# currenty += lenghty
#
# if isinstance(obj, FlatCAMExcellon):
# self.app.new_object("excellon", outname, initialize_excellon)
# if isinstance(obj, ExcellonObject):
# self.app.app_obj.new_object("excellon", outname, initialize_excellon)
# else:
# self.app.new_object("geometry", outname, initialize_geometry)
# self.app.app_obj.new_object("geometry", outname, initialize_geometry)
#
# # deselect all to avoid delete selected object when run delete from shell
# self.app.collection.set_all_inactive()
@@ -171,45 +198,38 @@ class TclCommandPanelize(TclCommand):
if obj is not None:
self.app.inform.emit("Generating panel ... Please wait.")
self.app.progress.emit(0)
def job_init_excellon(obj_fin, app_obj):
currenty = 0.0
self.app.progress.emit(10)
obj_fin.tools = obj.tools.copy()
obj_fin.drills = []
obj_fin.slots = []
obj_fin.solid_geometry = []
if 'drills' not in obj_fin.tools:
obj_fin.tools['drills'] = []
if 'slots' not in obj_fin.tools:
obj_fin.tools['slots'] = []
if 'solid_geometry' not in obj_fin.tools:
obj_fin.tools['solid_geometry'] = []
for option in obj.options:
if option is not 'name':
if option != 'name':
try:
obj_fin.options[option] = obj.options[option]
except:
log.warning("Failed to copy option.", option)
except Exception as e:
app_obj.log.warning("Failed to copy option: %s" % str(option))
app_obj.log.debug("TclCommandPanelize.execute().panelize2() --> %s" % str(e))
for row in range(rows):
currentx = 0.0
for col in range(columns):
if obj.drills:
for tool_dict in obj.drills:
point_offseted = affinity.translate(tool_dict['point'], currentx, currenty)
obj_fin.drills.append(
{
"point": point_offseted,
"tool": tool_dict['tool']
}
)
if obj.slots:
for tool_dict in obj.slots:
start_offseted = affinity.translate(tool_dict['start'], currentx, currenty)
stop_offseted = affinity.translate(tool_dict['stop'], currentx, currenty)
obj_fin.slots.append(
{
"start": start_offseted,
"stop": stop_offseted,
"tool": tool_dict['tool']
}
if 'drills' in obj.tools:
for drill_pt in obj.tools['drills']:
point_offseted = affinity.translate(drill_pt, currentx, currenty)
obj_fin.tools['drills'].append(point_offseted)
if 'slots' in obj.tools:
for slot_tuple in obj.tools['slots']:
start_offseted = affinity.translate(slot_tuple[0], currentx, currenty)
stop_offseted = affinity.translate(slot_tuple[1], currentx, currenty)
obj_fin.tools['slots'].append(
(start_offseted, stop_offseted)
)
currentx += lenghtx
currenty += lenghty
@@ -224,7 +244,7 @@ class TclCommandPanelize(TclCommand):
def translate_recursion(geom):
if type(geom) == list:
geoms = list()
geoms = []
for local_geom in geom:
geoms.append(translate_recursion(local_geom))
return geoms
@@ -233,19 +253,18 @@ class TclCommandPanelize(TclCommand):
obj_fin.solid_geometry = []
if isinstance(obj, FlatCAMGeometry):
if obj.kind == 'geometry':
obj_fin.multigeo = obj.multigeo
obj_fin.tools = deepcopy(obj.tools)
if obj.multigeo is True:
for tool in obj.tools:
obj_fin.tools[tool]['solid_geometry'][:] = []
self.app.progress.emit(0)
for row in range(rows):
currentx = 0.0
for col in range(columns):
if isinstance(obj, FlatCAMGeometry):
if obj.kind == 'geometry':
if obj.multigeo is True:
for tool in obj.tools:
obj_fin.tools[tool]['solid_geometry'].append(translate_recursion(
@@ -263,28 +282,24 @@ class TclCommandPanelize(TclCommand):
currentx += lenghtx
currenty += lenghty
if isinstance(obj, FlatCAMExcellon):
self.app.progress.emit(50)
self.app.new_object("excellon", outname, job_init_excellon, plot=True, autoselected=True)
if obj.kind == 'excellon':
self.app.app_obj.new_object("excellon", outname, job_init_excellon, plot=False, autoselected=True)
else:
self.app.progress.emit(50)
self.app.new_object("geometry", outname, job_init_geometry, plot=True, autoselected=True)
self.app.app_obj.new_object("geometry", outname, job_init_geometry, plot=False, autoselected=True)
if threaded == 1:
proc = self.app.proc_container.new("Generating panel ... Please wait.")
if threaded is True:
self.app.proc_container.new(_("Working ..."))
def job_thread(app_obj):
try:
panelize_2()
self.app.inform.emit("[success] Panel created successfully.")
except Exception as e:
proc.done()
log.debug(str(e))
app_obj.inform.emit('[success]' % _("Done."))
except Exception as ee:
log.debug(str(ee))
return
proc.done()
self.app.collection.promise(outname)
self.app.worker_task.emit({'fcn': job_thread, 'params': [self.app]})
else:
panelize_2()
self.app.inform.emit("[success] Panel created successfully.")
self.app.inform.emit('[success]' % _("Done."))

View File

@@ -0,0 +1,78 @@
from tclCommands.TclCommand import TclCommandSignaled
import collections
class TclCommandPlotAll(TclCommandSignaled):
"""
Tcl shell command to update the plot on the user interface.
example:
plot
"""
# List of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon)
aliases = ['plot_all']
description = '%s %s' % ("--", "Plots all objects on appGUI.")
# Dictionary of types from Tcl command, needs to be ordered
arg_names = collections.OrderedDict([
])
# Dictionary of types from Tcl command, needs to be ordered , this is for options like -optionname value
option_types = collections.OrderedDict([
('plot_status', str),
('use_thread', str)
])
# array of mandatory options for current Tcl command: required = {'name','outname'}
required = []
# structured help for current command, args needs to be ordered
help = {
'main': "Plots all objects on appGUI.",
'args': collections.OrderedDict([
('plot_status', 'If to display or not the objects: True (1) or False (0).'),
('use_thread', 'If to use multithreading: True (1) or False (0).')
]),
'examples': ['plot_all', 'plot_all -plot_status False']
}
def execute(self, args, unnamed_args):
"""
:param args:
:param unnamed_args:
:return:
"""
if 'use_thread' in args:
try:
par = args['use_thread'].capitalize()
except AttributeError:
par = args['use_thread']
threaded = bool(eval(par))
else:
threaded = False
plot_status = True
if 'plot_status' in args:
try:
if args['plot_status'] is None:
plot_status = True
else:
try:
par = args['plot_status'].capitalize()
except AttributeError:
par = args['plot_status']
plot_status = bool(eval(par))
except KeyError:
plot_status = True
for obj in self.app.collection.get_list():
obj.options["plot"] = True if plot_status is True else False
if self.app.cmd_line_headless != 1:
self.app.plot_all(use_thread=threaded)

View File

@@ -0,0 +1,75 @@
# ##########################################################
# FlatCAM: 2D Post-processing for Manufacturing #
# File Author: Marius Adrian Stanciu (c) #
# Date: 8/17/2019 #
# MIT Licence #
# ##########################################################
from tclCommands.TclCommand import TclCommand
import collections
class TclCommandPlotObjects(TclCommand):
"""
Tcl shell command to update the plot on the user interface.
example:
plot
"""
# List of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon)
aliases = ['plot_objects']
description = '%s %s' % ("--", "Plot a specified list of objects in appGUI.")
# Dictionary of types from Tcl command, needs to be ordered
arg_names = collections.OrderedDict([
('names', str)
])
# Dictionary of types from Tcl command, needs to be ordered , this is for options like -optionname value
option_types = collections.OrderedDict([
('plot_status', str)
])
# array of mandatory options for current Tcl command: required = {'name','outname'}
required = ['names']
# structured help for current command, args needs to be ordered
help = {
'main': "Plot a specified list of objects in appGUI.",
'args': collections.OrderedDict([
('names', "A list of object names to be plotted separated by comma. Required.\n"
"WARNING: no spaces are allowed. If unsure enclose the entire list with quotes."),
('plot_status', 'If to display or not the objects: True (1) or False (0).')
]),
'examples': ["plot_objects gerber_obj.GRB,excellon_obj.DRL"]
}
def execute(self, args, unnamed_args):
"""
:param args:
:param unnamed_args:
:return:
"""
if 'plot_status' in args:
if args['plot_status'] is None:
plot_status = True
else:
plot_status = bool(eval(args['plot_status']))
else:
plot_status = True
if self.app.cmd_line_headless != 1:
names = [x.strip() for x in args['names'].split(",") if x != '']
objs = []
for name in names:
obj = self.app.collection.get_by_name(name)
obj.options["plot"] = True if plot_status is True else False
objs.append(obj)
for obj in objs:
obj.plot()

View File

@@ -1,17 +1,27 @@
from ObjectCollection import *
# ##########################################################
# FlatCAM: 2D Post-processing for Manufacturing #
# File Author: Marius Adrian Stanciu (c) #
# Date: 8/17/2019 #
# MIT Licence #
# ##########################################################
from tclCommands.TclCommand import TclCommand
import collections
class TclCommandPlot(TclCommand):
class TclCommandQuit(TclCommand):
"""
Tcl shell command to update the plot on the user interface.
Tcl shell command to quit FlatCAM from Tcl shell.
example:
plot
"""
# List of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon)
aliases = ['plot']
aliases = ['quit_flatcam']
description = '%s %s' % ("--", "Tcl shell command to quit FlatCAM from Tcl shell.")
# Dictionary of types from Tcl command, needs to be ordered
arg_names = collections.OrderedDict([
@@ -28,11 +38,11 @@ class TclCommandPlot(TclCommand):
# structured help for current command, args needs to be ordered
help = {
'main': "Updates the plot on the user interface.",
'main': "Tcl shell command to quit FlatCAM from Tcl shell.",
'args': collections.OrderedDict([
]),
'examples': []
'examples': ['quit_flatcam']
}
def execute(self, args, unnamed_args):
@@ -43,4 +53,4 @@ class TclCommandPlot(TclCommand):
:return:
"""
self.app.plot_all()
self.app.quit_application()

View File

@@ -1,6 +1,7 @@
from ObjectCollection import *
from tclCommands.TclCommand import TclCommandSignaled
import collections
class TclCommandSaveProject(TclCommandSignaled):
"""
@@ -10,6 +11,8 @@ class TclCommandSaveProject(TclCommandSignaled):
# array of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon)
aliases = ['save_project']
description = '%s %s' % ("--", "Saves the FlatCAM project to file.")
# Dictionary of types from Tcl command, needs to be ordered.
# For positional arguments
arg_names = collections.OrderedDict([
@@ -29,9 +32,12 @@ class TclCommandSaveProject(TclCommandSignaled):
help = {
'main': "Saves the FlatCAM project to file.",
'args': collections.OrderedDict([
('filename', 'Path to file.'),
('filename', 'Absolute path to file to save. Required.\n'
'WARNING: no spaces are allowed. If unsure enclose the entire path with quotes.'),
]),
'examples': []
'examples': ['save_project D:\\my_project_file.FlatPrj',
'save_project "D:\\my_project_file with spaces in the name.FlatPrj"',
'save_project path_to_where_the_file_is_stored']
}
def execute(self, args, unnamed_args):
@@ -44,4 +50,4 @@ class TclCommandSaveProject(TclCommandSignaled):
:return: None or exception
"""
self.app.save_project(args['filename'])
self.app.save_project(args['filename'], from_tcl=True)

View File

@@ -1,3 +1,10 @@
# ##########################################################
# FlatCAM: 2D Post-processing for Manufacturing #
# File Author: Marius Adrian Stanciu (c) #
# Date: 8/17/2019 #
# MIT Licence #
# ##########################################################
from tclCommands.TclCommand import *
@@ -9,6 +16,8 @@ class TclCommandSaveSys(TclCommandSignaled):
# array of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon)
aliases = ['save_sys', 'save']
description = '%s %s' % ("--", "Saves the FlatCAM system parameters to defaults file.")
# Dictionary of types from Tcl command, needs to be ordered.
# For positional arguments
arg_names = collections.OrderedDict([
@@ -26,9 +35,9 @@ class TclCommandSaveSys(TclCommandSignaled):
# structured help for current command, args needs to be ordered
help = {
'main': "Saves the FlatCAM system paramaters to defaults file.",
'main': "Saves the FlatCAM system parameters to defaults file.",
'args': collections.OrderedDict([]),
'examples': []
'examples': ['save_sys']
}
def execute(self, args, unnamed_args):
@@ -41,4 +50,4 @@ class TclCommandSaveSys(TclCommandSignaled):
:return: None or exception
"""
self.app.save_defaults(args)
self.app.preferencesUiManager.save_defaults(args)

View File

@@ -1,6 +1,18 @@
from ObjectCollection import *
from tclCommands.TclCommand import TclCommand
import collections
import logging
import gettext
import appTranslation as fcTranslate
import builtins
fcTranslate.apply_language('strings')
if '_' not in builtins.__dict__:
_ = gettext.gettext
log = logging.getLogger('base')
class TclCommandScale(TclCommand):
"""
@@ -13,6 +25,8 @@ class TclCommandScale(TclCommand):
# List of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon)
aliases = ['scale']
description = '%s %s' % ("--", "Will scale the geometry of a named object. Does not create a new object.")
# Dictionary of types from Tcl command, needs to be ordered
arg_names = collections.OrderedDict([
('name', str),
@@ -21,20 +35,34 @@ class TclCommandScale(TclCommand):
# Dictionary of types from Tcl command, needs to be ordered , this is for options like -optionname value
option_types = collections.OrderedDict([
('x', float),
('y', float),
('origin', str)
])
# array of mandatory options for current Tcl command: required = {'name','outname'}
required = ['name', 'factor']
required = ['name']
# structured help for current command, args needs to be ordered
help = {
'main': "Resizes the object by a factor.",
'main': "Resizes the object by a factor on X axis and a factor on Y axis, having as scale origin the point ",
'args': collections.OrderedDict([
('name', 'Name of the object to resize.'),
('factor', 'Fraction by which to scale.')
('name', 'Name of the object (Gerber, Geometry or Excellon) to be resized. Required.'),
('factor', 'Fraction by which to scale on both axis.'),
('x', 'Fraction by which to scale on X axis. If "factor" is used then this parameter is ignored'),
('y', 'Fraction by which to scale on Y axis. If "factor" is used then this parameter is ignored'),
('origin', 'Reference used for scale.\n'
'The reference point can be:\n'
'- "origin" which means point (0, 0)\n'
'- "min_bounds" which means the lower left point of the bounding box\n'
'- "center" which means the center point of the bounding box of the object.\n'
'- a tuple in format (x,y) with the X and Y coordinates separated by a comma. NO SPACES ALLOWED')
]),
'examples': ['scale my_geometry 4.2']
'examples': ['scale my_geometry 4.2',
'scale my_geo -x 3.1 -y 2.8',
'scale my_geo 1.2 -origin min_bounds',
'scale my_geometry -x 2 -origin 3.0,2.1']
}
def execute(self, args, unnamed_args):
@@ -46,6 +74,57 @@ class TclCommandScale(TclCommand):
"""
name = args['name']
factor = args['factor']
try:
obj_to_scale = self.app.collection.get_by_name(name)
except Exception as e:
log.debug("TclCommandCopperClear.execute() --> %s" % str(e))
self.raise_tcl_error("%s: %s" % (_("Could not retrieve object"), name))
return "Could not retrieve object: %s" % name
self.app.collection.get_by_name(name).scale(factor)
if 'origin' not in args:
xmin, ymin, xmax, ymax = obj_to_scale.bounds()
c_x = xmin + (xmax - xmin) / 2
c_y = ymin + (ymax - ymin) / 2
point = (c_x, c_y)
else:
if args['origin'] == 'origin':
point = (0, 0)
elif args['origin'] == 'min_bounds':
xmin, ymin, xmax, ymax = obj_to_scale.bounds()
point = (xmin, ymin)
elif args['origin'] == 'center':
xmin, ymin, xmax, ymax = obj_to_scale.bounds()
c_x = xmin + (xmax - xmin) / 2
c_y = ymin + (ymax - ymin) / 2
point = (c_x, c_y)
else:
try:
point = eval(args['origin'])
if not isinstance(point, tuple):
raise Exception
except Exception as e:
self.raise_tcl_error('%s\n%s' % (_("Expected -origin <origin> or "
"-origin <min_bounds> or "
"-origin <center> or "
"- origin 3.0,4.2."), str(e)))
return 'fail'
if 'factor' in args:
factor = float(args['factor'])
obj_to_scale.scale(factor, point=point)
return
if 'x' not in args and 'y' not in args:
self.raise_tcl_error('%s' % _("Expected -x <value> -y <value>."))
return 'fail'
if 'x' in args and 'y' not in args:
f_x = float(args['x'])
obj_to_scale.scale(f_x, 0, point=point)
elif 'x' not in args and 'y' in args:
f_y = float(args['y'])
obj_to_scale.scale(0, f_y, point=point)
elif 'x' in args and 'y' in args:
f_x = float(args['x'])
f_y = float(args['y'])
obj_to_scale.scale(f_x, f_y, point=point)

View File

@@ -1,10 +1,11 @@
from ObjectCollection import *
from tclCommands.TclCommand import TclCommand
import collections
class TclCommandSetActive(TclCommand):
"""
Tcl shell command to set an object as active in the GUI.
Tcl shell command to set an object as active in the appGUI.
example:
@@ -13,6 +14,8 @@ class TclCommandSetActive(TclCommand):
# List of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon)
aliases = ['set_active']
description = '%s %s' % ("--", "Sets a FlatCAM object as active (selected).")
# Dictionary of types from Tcl command, needs to be ordered
arg_names = collections.OrderedDict([
('name', str),
@@ -28,11 +31,11 @@ class TclCommandSetActive(TclCommand):
# structured help for current command, args needs to be ordered
help = {
'main': 'Sets an object as active.',
'main': 'Sets a FlatCAM object as active (selected).',
'args': collections.OrderedDict([
('name', 'Name of the Object.'),
('name', 'Name of the FlatCAM object to be set as active (selected). Required.'),
]),
'examples': []
'examples': ['set_active object_name']
}
def execute(self, args, unnamed_args):

View File

@@ -0,0 +1,103 @@
# ##########################################################
# FlatCAM: 2D Post-processing for Manufacturing #
# File Author: Marius Adrian Stanciu (c) #
# Date: 8/17/2019 #
# MIT Licence #
# ##########################################################
from tclCommands.TclCommand import TclCommand
import collections
from camlib import get_bounds
import logging
import gettext
import appTranslation as fcTranslate
import builtins
fcTranslate.apply_language('strings')
if '_' not in builtins.__dict__:
_ = gettext.gettext
log = logging.getLogger('base')
class TclCommandSetOrigin(TclCommand):
"""
Tcl shell command to set the origin to zero or to a specified location for all loaded objects in FlatCAM.
example:
"""
# List of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon)
aliases = ['set_origin', 'origin']
description = '%s %s' % ("--", "Set the origin at the specified x,y location.")
# Dictionary of types from Tcl command, needs to be ordered
arg_names = collections.OrderedDict([
('loc', str)
])
# Dictionary of types from Tcl command, needs to be ordered , this is for options like -optionname value
option_types = collections.OrderedDict([
('auto', str)
])
# array of mandatory options for current Tcl command: required = {'name','outname'}
required = []
# structured help for current command, args needs to be ordered
help = {
'main': "Will set the origin at the specified x,y location.\n"
"If it is called without arguments it will set origin at (0, 0)",
'args': collections.OrderedDict([
('loc', 'Location to offset all the selected objects. NO SPACES ALLOWED in X and Y pair.\n'
'Use like this: 2,3'),
('auto', 'If set to True it will set the origin to the minimum x, y of the object selection bounding box.'
'-auto=True is not correct but -auto 1 or -auto True is correct. True (1) or False (0).')
]),
'examples': ['set_origin 3,2', 'set_origin -auto 1', 'origin']
}
def execute(self, args, unnamed_args):
"""
:param args:
:param unnamed_args:
:return:
"""
loc = []
if 'auto' in args:
if bool(args['auto']) is True:
objs = self.app.collection.get_list()
minx, miny, __, ___ = get_bounds(objs)
loc.append(0 - minx)
loc.append(0 - miny)
else:
loc = [0, 0]
elif 'loc' in args:
try:
location = [float(eval(coord)) for coord in str(args['loc']).split(",") if coord != '']
except AttributeError as e:
log.debug("TclCommandSetOrigin.execute --> %s" % str(e))
location = (0, 0)
loc.append(location[0])
loc.append(location[1])
if len(location) != 2:
self.raise_tcl_error('%s: %s' % (
_("Expected a pair of (x, y) coordinates. Got"), str(len(location))))
return 'fail'
else:
loc = [0, 0]
self.app.on_set_zero_click(event=None, location=loc, noplot=True, use_thread=False)
msg = '[success] Tcl %s: %s' % (_('Origin set by offsetting all loaded objects with '),
'{0:.4f}, {0:.4f}'.format(loc[0], loc[1]))
self.app.inform_shell.emit(msg)

View File

@@ -0,0 +1,94 @@
# ##########################################################
# FlatCAM: 2D Post-processing for Manufacturing #
# File Author: Marius Adrian Stanciu (c) #
# Date: 4/28/2020 #
# MIT Licence #
# ##########################################################
from tclCommands.TclCommand import TclCommand
import collections
import os
import logging
import gettext
import appTranslation as fcTranslate
import builtins
fcTranslate.apply_language('strings')
if '_' not in builtins.__dict__:
_ = gettext.gettext
log = logging.getLogger('base')
class TclCommandSetPath(TclCommand):
"""
Tcl shell command to set the default path for Tcl Shell for opening files.
example:
"""
# List of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon)
aliases = ['set_path']
description = '%s %s' % ("--", "Set the folder path to the specified path.")
# Dictionary of types from Tcl command, needs to be ordered
arg_names = collections.OrderedDict([
('path', 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 = ['path']
# structured help for current command, args needs to be ordered
help = {
'main': "Will set the folder path to the specified path.\n"
"By using this command there is no need for usage of the absolute path to the files.",
'args': collections.OrderedDict([
('path', 'A folder path to where the user is supposed to have the file that he will work with.\n'
'WARNING: No spaces allowed. Use quotes around the path if it contains spaces.'),
]),
'examples': ['set_path D:\\Project_storage_path']
}
def execute(self, args, unnamed_args):
"""
:param args:
:param unnamed_args:
:return:
"""
if 'path' not in args:
return "Failed. The Tcl command set_path was used but no path argument was provided."
else:
path = str(args['path']).replace('\\', '/')
# check if path exists
path_isdir = os.path.isdir(path)
if path_isdir is False:
path_isfile = os.path.isfile(path)
if path_isfile:
msg = '[ERROR] %s: %s, %s' % (
"The provided path",
str(path),
"is a path to file and not a directory as expected.")
self.app.inform_shell.emit(msg)
return "Failed. The Tcl command set_path was used but it was not a directory."
else:
msg = '[ERROR] %s: %s, %s' % (
"The provided path", str(path), "do not exist. Check for typos.")
self.app.inform_shell.emit(msg)
return "Failed. The Tcl command set_path was used but it does not exist."
cd_command = 'cd %s' % path
self.app.shell.exec_command(cd_command, no_echo=False)
self.app.defaults["global_tcl_path"] = str(path)
msg = '[success] %s: %s' % ("Relative path set to", str(path))
self.app.inform_shell.emit(msg)

View File

@@ -1,6 +1,7 @@
from ObjectCollection import *
from tclCommands.TclCommand import TclCommand
import collections
class TclCommandSetSys(TclCommand):
"""
@@ -13,6 +14,8 @@ class TclCommandSetSys(TclCommand):
# List of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon)
aliases = ['set_sys', 'setsys']
description = '%s %s' % ("--", "Sets the value of the specified system variable.")
# Dictionary of types from Tcl command, needs to be ordered
arg_names = collections.OrderedDict([
('name', str),
@@ -29,12 +32,12 @@ class TclCommandSetSys(TclCommand):
# structured help for current command, args needs to be ordered
help = {
'main': "Sets the value of the system variable.",
'main': "Sets the value of the specified system variable.",
'args': collections.OrderedDict([
('name', 'Name of the system variable.'),
('name', 'Name of the system variable. Required.'),
('value', 'Value to set.')
]),
'examples': []
'examples': ['set_sys global_gridx 1.0']
}
def execute(self, args, unnamed_args):
@@ -80,9 +83,7 @@ class TclCommandSetSys(TclCommand):
pass
self.app.defaults[param] = value
self.app.propagate_defaults(silent=True)
self.app.defaults.propagate_defaults()
else:
self.raise_tcl_error("No such system parameter \"{}\".".format(param))

View File

@@ -1,6 +1,7 @@
from ObjectCollection import *
from tclCommands.TclCommand import TclCommand
import collections
class TclCommandSkew(TclCommand):
"""
@@ -13,30 +14,32 @@ class TclCommandSkew(TclCommand):
# List of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon)
aliases = ['skew']
description = '%s %s' % ("--", "Will deform (skew) the geometry of a named object. Does not create a new object.")
# Dictionary of types from Tcl command, needs to be ordered
arg_names = collections.OrderedDict([
('name', str),
('angle_x', float),
('angle_y', float)
])
# Dictionary of types from Tcl command, needs to be ordered , this is for options like -optionname value
option_types = collections.OrderedDict([
('x', float),
('y', float)
])
# array of mandatory options for current Tcl command: required = {'name','outname'}
required = ['name', 'angle_x', 'angle_y']
required = ['name']
# structured help for current command, args needs to be ordered
help = {
'main': "Shear/Skew an object by angles along x and y dimensions.",
'main': "Shear/Skew an object along x and y dimensions. The reference point is the left corner of "
"the bounding box of the object.",
'args': collections.OrderedDict([
('name', 'Name of the object to skew.'),
('angle_x', 'Angle in degrees by which to skew on the X axis.'),
('angle_y', 'Angle in degrees by which to skew on the Y axis.')
('name', 'Name of the object (Gerber, Geometry or Excellon) to be deformed (skewed). Required.'),
('x', 'Angle in degrees by which to skew on the X axis. If it is not used it will be assumed to be 0.0'),
('y', 'Angle in degrees by which to skew on the Y axis. If it is not used it will be assumed to be 0.0')
]),
'examples': ['skew my_geometry 10.2 3.5']
'examples': ['skew my_geometry -x 10.2 -y 3.5', 'skew my_geo -x 3.0']
}
def execute(self, args, unnamed_args):
@@ -48,7 +51,21 @@ class TclCommandSkew(TclCommand):
"""
name = args['name']
angle_x = args['angle_x']
angle_y = args['angle_y']
self.app.collection.get_by_name(name).skew(angle_x, angle_y)
if 'x' in args:
angle_x = float(args['x'])
else:
angle_x = 0.0
if 'y' in args:
angle_y = float(args['y'])
else:
angle_y = 0.0
if angle_x == 0.0 and angle_y == 0.0:
# nothing to be done
return
obj_to_skew = self.app.collection.get_by_name(name)
xmin, ymin, xmax, ymax = obj_to_skew.bounds()
obj_to_skew.skew(angle_x, angle_y, point=(xmin, ymin))

View File

@@ -1,6 +1,7 @@
from ObjectCollection import *
from tclCommands.TclCommand import TclCommandSignaled
import collections
class TclCommandSubtractPoly(TclCommandSignaled):
"""
@@ -10,6 +11,9 @@ class TclCommandSubtractPoly(TclCommandSignaled):
# array of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon)
aliases = ['subtract_poly']
description = '%s %s' % ("--", "Subtract polygon from the given Geometry object. "
"The coordinates are provided in X Y pairs.")
# Dictionary of types from Tcl command, needs to be ordered.
# For positional arguments
arg_names = collections.OrderedDict([
@@ -27,12 +31,14 @@ class TclCommandSubtractPoly(TclCommandSignaled):
# structured help for current command, args needs to be ordered
help = {
'main': "Subtract polygon from the given Geometry object.",
'main': "Subtract polygon from the given Geometry object. The coordinates are provided in X Y pairs.\n"
"If the number of coordinates is not even then the 'Incomplete coordinate' error is raised.\n"
"If last coordinates are not the same as the first ones, the polygon will be completed automatically.",
'args': collections.OrderedDict([
('name', 'Name of the Geometry object from which to subtract.'),
('name', 'Name of the Geometry object from which to subtract. Required.'),
('x0 y0 x1 y1 x2 y2 ...', 'Points defining the polygon.')
]),
'examples': []
'examples': ['subtract_poly my_geo 0 0 2 1 3 3 4 4 0 0']
}
def execute(self, args, unnamed_args):
@@ -50,11 +56,13 @@ class TclCommandSubtractPoly(TclCommandSignaled):
if len(unnamed_args) % 2 != 0:
return "Incomplete coordinate."
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(int(len(unnamed_args) / 2))
]
try:
obj = self.app.collection.get_by_name(str(obj_name))
except:
except Exception:
return "Could not retrieve object: %s" % obj_name
if obj is None:
return "Object not found: %s" % obj_name

View File

@@ -1,23 +1,31 @@
from ObjectCollection import *
from tclCommands.TclCommand import TclCommandSignaled
import collections
import gettext
import appTranslation as fcTranslate
import builtins
fcTranslate.apply_language('strings')
if '_' not in builtins.__dict__:
_ = gettext.gettext
class TclCommandSubtractRectangle(TclCommandSignaled):
"""
Tcl shell command to subtract a rectange from the given Geometry object.
Tcl shell command to subtract a rectangle from the given Geometry object.
"""
# array of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon)
aliases = ['subtract_rectangle']
description = '%s %s' % ("--", "Subtract a rectangle from the given Geometry object. "
"The coordinates are provided in X Y pairs.")
# Dictionary of types from Tcl command, needs to be ordered.
# For positional arguments
arg_names = collections.OrderedDict([
('name', str),
('x0', float),
('y0', float),
('x1', float),
('y1', float)
('name', str)
])
# Dictionary of types from Tcl command, needs to be ordered.
@@ -27,17 +35,18 @@ class TclCommandSubtractRectangle(TclCommandSignaled):
])
# array of mandatory options for current Tcl command: required = {'name','outname'}
required = ['name', 'x0', 'y0', 'x1', 'y1']
required = ['name']
# structured help for current command, args needs to be ordered
help = {
'main': "Subtract rectange from the given Geometry object.",
'main': "Subtract a rectangle from the given Geometry object. The coordinates are provided in X Y pairs.\n"
"If the number of coordinates is not even then the 'Incomplete coordinates' error is raised.",
'args': collections.OrderedDict([
('name', 'Name of the Geometry object from which to subtract.'),
('name', 'Name of the Geometry object from which to subtract. Required.'),
('x0 y0', 'Bottom left corner coordinates.'),
('x1 y1', 'Top right corner coordinates.')
]),
'examples': []
'examples': ['subtract_rectangle geo_obj 8 8 15 15']
}
def execute(self, args, unnamed_args):
@@ -49,17 +58,25 @@ class TclCommandSubtractRectangle(TclCommandSignaled):
without -somename and we do not have them in known arg_names
:return: None or exception
"""
if 'name' not in args:
self.raise_tcl_error("%s:" % _("No Geometry name in args. Provide a name and try again."))
return 'fail'
obj_name = args['name']
x0 = args['x0']
y0 = args['y0']
x1 = args['x1']
y1 = args['y1']
if len(unnamed_args) != 4:
self.raise_tcl_error("Incomplete coordinates. There are 4 required: x0 y0 x1 y1.")
return 'fail'
x0 = float(unnamed_args[0])
y0 = float(unnamed_args[1])
x1 = float(unnamed_args[2])
y1 = float(unnamed_args[3])
try:
obj = self.app.collection.get_by_name(str(obj_name))
except:
except Exception:
return "Could not retrieve object: %s" % obj_name
if obj is None:
return "Object not found: %s" % obj_name

View File

@@ -1,6 +1,7 @@
from ObjectCollection import *
from tclCommands.TclCommand import TclCommand
import collections
class TclCommandVersion(TclCommand):
"""
@@ -13,6 +14,8 @@ class TclCommandVersion(TclCommand):
# List of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon)
aliases = ['version']
description = '%s %s' % ("--", "Checks the program version.")
# Dictionary of types from Tcl command, needs to be ordered
arg_names = collections.OrderedDict([
@@ -32,7 +35,7 @@ class TclCommandVersion(TclCommand):
'args': collections.OrderedDict([
]),
'examples': []
'examples': ['version']
}
def execute(self, args, unnamed_args):
@@ -44,4 +47,3 @@ class TclCommandVersion(TclCommand):
"""
self.app.version_check()

View File

@@ -1,6 +1,7 @@
from ObjectCollection import *
from tclCommands.TclCommand import TclCommandSignaled
import collections
class TclCommandWriteGCode(TclCommandSignaled):
"""
@@ -11,6 +12,8 @@ class TclCommandWriteGCode(TclCommandSignaled):
# old names for backward compatibility (add_poly, add_polygon)
aliases = ['write_gcode']
description = '%s %s' % ("--", "Saves G-code of a CNC Job object to file.")
# Dictionary of types from Tcl command, needs to be ordered.
# For positional arguments
arg_names = collections.OrderedDict([
@@ -22,7 +25,8 @@ class TclCommandWriteGCode(TclCommandSignaled):
# For options like -optionname value
option_types = collections.OrderedDict([
('preamble', str),
('postamble', str)
('postamble', str),
('muted', str)
])
# array of mandatory options for current Tcl command: required = {'name','outname'}
@@ -32,12 +36,14 @@ class TclCommandWriteGCode(TclCommandSignaled):
help = {
'main': "Saves G-code of a CNC Job object to file.",
'args': collections.OrderedDict([
('name', 'Source CNC Job object.'),
('filename', 'Output filename.'),
('name', 'Source CNC Job object. Required.'),
('filename', 'Output filename. Required.'),
('preamble', 'Text to append at the beginning.'),
('postamble', 'Text to append at the end.')
('postamble', 'Text to append at the end.'),
('muted', 'It will not put errors in the Shell or status bar. True (1) or False (0)')
]),
'examples': []
'examples': ["write_gcode name c:\\\\gcode_repo"]
}
def execute(self, args, unnamed_args):
@@ -62,18 +68,27 @@ class TclCommandWriteGCode(TclCommandSignaled):
preamble = args['preamble'] if 'preamble' in args else ''
postamble = args['postamble'] if 'postamble' in args else ''
# TODO: This is not needed any more? All targets should be present.
if 'muted' in args:
try:
par = args['muted'].capitalize()
except AttributeError:
par = args['muted']
muted = bool(eval(par))
else:
muted = False
# This is not needed any more? All targets should be present.
# If there are promised objects, wait until all promises have been fulfilled.
# if self.collection.has_promises():
# def write_gcode_on_object(new_object):
# def write_gcode_on_object(app_obj.new_object):
# self.log.debug("write_gcode_on_object(): Disconnecting %s" % write_gcode_on_object)
# self.new_object_available.disconnect(write_gcode_on_object)
# self.app_obj.new_object_available.disconnect(write_gcode_on_object)
# write_gcode(obj_name, filename, preamble, postamble)
#
# # Try again when a new object becomes available.
# self.log.debug("write_gcode(): Collection has promises. Queued for %s." % obj_name)
# self.log.debug("write_gcode(): Queued function: %s" % write_gcode_on_object)
# self.new_object_available.connect(write_gcode_on_object)
# self.app_obj.new_object_available.connect(write_gcode_on_object)
#
# return
@@ -81,10 +96,16 @@ class TclCommandWriteGCode(TclCommandSignaled):
try:
obj = self.app.collection.get_by_name(str(obj_name))
except:
return "Could not retrieve object: %s" % obj_name
except Exception:
if muted is False:
return "Could not retrieve object: %s" % obj_name
else:
return "fail"
try:
obj.export_gcode(str(filename), str(preamble), str(postamble))
obj.export_gcode(str(filename), str(preamble), str(postamble), from_tcl=True)
except Exception as e:
return "Operation failed: %s" % str(e)
if muted is False:
return "Operation failed: %s" % str(e)
else:
return

View File

@@ -1,7 +1,6 @@
import pkgutil
import sys
# Todo: I think these imports are not needed.
# allowed command modules (please append them alphabetically ordered)
import tclCommands.TclCommandAddCircle
import tclCommands.TclCommandAddPolygon
@@ -9,43 +8,60 @@ import tclCommands.TclCommandAddPolyline
import tclCommands.TclCommandAddRectangle
import tclCommands.TclCommandAlignDrill
import tclCommands.TclCommandAlignDrillGrid
import tclCommands.TclCommandBbox
import tclCommands.TclCommandBounds
import tclCommands.TclCommandClearShell
import tclCommands.TclCommandCncjob
import tclCommands.TclCommandCopperClear
import tclCommands.TclCommandCutout
import tclCommands.TclCommandDelete
import tclCommands.TclCommandDrillcncjob
import tclCommands.TclCommandExportDXF
import tclCommands.TclCommandExportExcellon
import tclCommands.TclCommandExportGerber
import tclCommands.TclCommandExportGcode
import tclCommands.TclCommandExportSVG
import tclCommands.TclCommandExteriors
import tclCommands.TclCommandGeoCutout
import tclCommands.TclCommandGeoUnion
import tclCommands.TclCommandGetNames
import tclCommands.TclCommandGetPath
import tclCommands.TclCommandGetSys
import tclCommands.TclCommandImportSvg
import tclCommands.TclCommandHelp
import tclCommands.TclCommandInteriors
import tclCommands.TclCommandIsolate
import tclCommands.TclCommandFollow
import tclCommands.TclCommandJoinExcellon
import tclCommands.TclCommandJoinGeometry
import tclCommands.TclCommandListSys
import tclCommands.TclCommandMillHoles
import tclCommands.TclCommandMillDrills
import tclCommands.TclCommandMillSlots
import tclCommands.TclCommandMirror
import tclCommands.TclCommandNew
import tclCommands.TclCommandNregions
import tclCommands.TclCommandNewExcellon
import tclCommands.TclCommandNewGeometry
import tclCommands.TclCommandNewGerber
import tclCommands.TclCommandOffset
import tclCommands.TclCommandOpenDXF
import tclCommands.TclCommandOpenExcellon
import tclCommands.TclCommandOpenFolder
import tclCommands.TclCommandOpenGCode
import tclCommands.TclCommandOpenGerber
import tclCommands.TclCommandOpenProject
import tclCommands.TclCommandOpenFolder
import tclCommands.TclCommandOpenSVG
import tclCommands.TclCommandOptions
import tclCommands.TclCommandPaint
import tclCommands.TclCommandPanelize
import tclCommands.TclCommandPlot
import tclCommands.TclCommandPlotAll
import tclCommands.TclCommandPlotObjects
import tclCommands.TclCommandQuit
import tclCommands.TclCommandSaveProject
import tclCommands.TclCommandSaveSys
import tclCommands.TclCommandScale
import tclCommands.TclCommandSetActive
import tclCommands.TclCommandSetOrigin
import tclCommands.TclCommandSetPath
import tclCommands.TclCommandSetSys
import tclCommands.TclCommandSkew
import tclCommands.TclCommandSubtractPoly
@@ -78,7 +94,8 @@ def register_all_commands(app, commands):
:return: None
"""
tcl_modules = {k: v for k, v in list(sys.modules.items()) if k.startswith('tclCommands.TclCommand')}
tcl_modules = {k: v for k, v in list(
sys.modules.items()) if k.startswith('tclCommands.TclCommand')}
for key, mod in list(tcl_modules.items()):
if key != 'tclCommands.TclCommand':
@@ -87,7 +104,12 @@ def register_all_commands(app, commands):
command_instance = class_type(app)
for alias in command_instance.aliases:
try:
description = command_instance.description
except AttributeError:
description = ''
commands[alias] = {
'fcn': command_instance.execute_wrapper,
'help': command_instance.get_decorated_help()
'help': command_instance.get_decorated_help(),
'description': description
}