From f77403b0f2f4f543dcdf927b521c89931a2e244e Mon Sep 17 00:00:00 2001 From: Juan Pablo Caram Date: Sat, 8 Oct 2016 11:49:22 -0400 Subject: [PATCH] Bringing up to date with VisPyCanvas. Mostly migration of Tcl commands to new architecture. --- FlatCAMApp.py | 2946 ++++++++++---------- FlatCAMGUI.py | 6 +- tclCommands/TclCommand.py | 28 +- tclCommands/TclCommandAddCircle.py | 65 + tclCommands/TclCommandAddRectangle.py | 66 + tclCommands/TclCommandAlignDrill.py | 201 ++ tclCommands/TclCommandAlignDrillGrid.py | 105 + tclCommands/TclCommandCncjob.py | 18 +- tclCommands/TclCommandCutout.py | 99 + tclCommands/TclCommandDelete.py | 54 + tclCommands/TclCommandDrillcncjob.py | 14 +- tclCommands/TclCommandExportSVG.py | 53 + tclCommands/TclCommandGeoCutout.py | 118 + tclCommands/TclCommandGeoUnion.py | 59 + tclCommands/TclCommandGetNames.py | 46 + tclCommands/TclCommandIsolate.py | 10 +- tclCommands/TclCommandJoinExcellon.py | 64 + tclCommands/TclCommandJoinGeometry.py | 64 + tclCommands/TclCommandMillHoles.py | 77 + tclCommands/TclCommandMirror.py | 108 + tclCommands/TclCommandNew.py | 3 +- tclCommands/TclCommandNewGeometry.py | 49 + tclCommands/TclCommandOffset.py | 53 + tclCommands/TclCommandOpenExcellon.py | 48 + tclCommands/TclCommandOpenGCode.py | 49 + tclCommands/TclCommandOpenGerber.py | 4 +- tclCommands/TclCommandOpenProject.py | 47 + tclCommands/TclCommandOptions.py | 50 + tclCommands/TclCommandPanelize.py | 145 + tclCommands/TclCommandPlot.py | 46 + tclCommands/TclCommandSaveProject.py | 47 + tclCommands/TclCommandScale.py | 51 + tclCommands/TclCommandSetActive.py | 51 + tclCommands/TclCommandSubtractPoly.py | 62 + tclCommands/TclCommandSubtractRectangle.py | 66 + tclCommands/TclCommandWriteGCode.py | 87 + tclCommands/__init__.py | 31 +- 37 files changed, 3619 insertions(+), 1471 deletions(-) create mode 100644 tclCommands/TclCommandAddCircle.py create mode 100644 tclCommands/TclCommandAddRectangle.py create mode 100644 tclCommands/TclCommandAlignDrill.py create mode 100644 tclCommands/TclCommandAlignDrillGrid.py create mode 100644 tclCommands/TclCommandCutout.py create mode 100644 tclCommands/TclCommandDelete.py create mode 100644 tclCommands/TclCommandExportSVG.py create mode 100644 tclCommands/TclCommandGeoCutout.py create mode 100644 tclCommands/TclCommandGeoUnion.py create mode 100644 tclCommands/TclCommandGetNames.py create mode 100644 tclCommands/TclCommandJoinExcellon.py create mode 100644 tclCommands/TclCommandJoinGeometry.py create mode 100644 tclCommands/TclCommandMillHoles.py create mode 100644 tclCommands/TclCommandMirror.py create mode 100644 tclCommands/TclCommandNewGeometry.py create mode 100644 tclCommands/TclCommandOffset.py create mode 100644 tclCommands/TclCommandOpenExcellon.py create mode 100644 tclCommands/TclCommandOpenGCode.py create mode 100644 tclCommands/TclCommandOpenProject.py create mode 100644 tclCommands/TclCommandOptions.py create mode 100644 tclCommands/TclCommandPanelize.py create mode 100644 tclCommands/TclCommandPlot.py create mode 100644 tclCommands/TclCommandSaveProject.py create mode 100644 tclCommands/TclCommandScale.py create mode 100644 tclCommands/TclCommandSetActive.py create mode 100644 tclCommands/TclCommandSubtractPoly.py create mode 100644 tclCommands/TclCommandSubtractRectangle.py create mode 100644 tclCommands/TclCommandWriteGCode.py diff --git a/FlatCAMApp.py b/FlatCAMApp.py index 388a49f9..98cf5dfe 100644 --- a/FlatCAMApp.py +++ b/FlatCAMApp.py @@ -515,6 +515,7 @@ class App(QtCore.QObject): self.ui.menufilesaveprojectas.triggered.connect(self.on_file_saveprojectas) self.ui.menufilesaveprojectcopy.triggered.connect(lambda: self.on_file_saveprojectas(make_copy=True)) self.ui.menufilesavedefaults.triggered.connect(self.on_file_savedefaults) + self.ui.exit_action.triggered.connect(self.on_file_exit) self.ui.menueditnew.triggered.connect(lambda: self.new_object('geometry', 'New Geometry', lambda x, y: None)) self.ui.menueditedit.triggered.connect(self.edit_geometry) self.ui.menueditok.triggered.connect(self.editor2geometry) @@ -1148,6 +1149,9 @@ class App(QtCore.QObject): self.save_defaults() + def on_file_exit(self): + QtGui.qApp.quit() + def save_defaults(self, silent=False): """ Saves application default options @@ -1457,7 +1461,6 @@ class App(QtCore.QObject): # self.options2form() - def on_delete(self): """ Delete the currently selected FlatCAMObjs. @@ -1470,7 +1473,6 @@ class App(QtCore.QObject): while (self.collection.get_active()): self.delete_first_selected() - def delete_first_selected(self): # Keep this for later @@ -1998,6 +2000,8 @@ class App(QtCore.QObject): Opens a Gerber file, parses it and creates a new object for it in the program. Thread-safe. + :param outname: Name of the resulting object. None causes the + name to be that of the file. :param filename: Gerber file filename :type filename: str :param follow: If true, the parser will not create polygons, just lines @@ -2068,6 +2072,8 @@ class App(QtCore.QObject): Opens an Excellon file, parses it and creates a new object for it in the program. Thread-safe. + :param outname: Name of the resulting object. None causes the + name to be that of the file. :param filename: Excellon file filename :type filename: str :return: None @@ -2129,6 +2135,8 @@ class App(QtCore.QObject): Opens a G-gcode file, parses it and creates a new object for it in the program. Thread-safe. + :param outname: Name of the resulting object. None causes the + name to be that of the file. :param filename: G-code file filename :type filename: str :return: None @@ -2344,9 +2352,10 @@ class App(QtCore.QObject): return commands[p]["help"] - def options(name): - ops = self.collection.get_by_name(str(name)).options - return '\n'.join(["%s: %s" % (o, ops[o]) for o in ops]) + # --- Migrated to new architecture --- + # def options(name): + # ops = self.collection.get_by_name(str(name)).options + # return '\n'.join(["%s: %s" % (o, ops[o]) for o in ops]) def h(*args): """ @@ -2438,1096 +2447,1141 @@ class App(QtCore.QObject): # if status['timed_out']: # raise Exception('Timed out!') - def mytest(*args): - to = int(args[0]) - - try: - for rec in self.recent: - if rec['kind'] == 'gerber': - self.open_gerber(str(rec['filename'])) - break - - basename = self.collection.get_names()[0] - isolate(basename, '-passes', '10', '-combine', '1') - iso = self.collection.get_by_name(basename + "_iso") - - with wait_signal(self.new_object_available, to): - iso.generatecncjob() - # iso.generatecncjob() - # wait_signal2(self.new_object_available, to) - - return str(self.collection.get_names()) - - except Exception as e: - return str(e) - - def mytest2(*args): - to = int(args[0]) - - for rec in self.recent: - if rec['kind'] == 'gerber': - self.open_gerber(str(rec['filename'])) - break - - basename = self.collection.get_names()[0] - isolate(basename, '-passes', '10', '-combine', '1') - iso = self.collection.get_by_name(basename + "_iso") - - with wait_signal(self.new_object_available, to): - 1/0 # Force exception - iso.generatecncjob() - - return str(self.collection.get_names()) - - def mytest3(*args): - to = int(args[0]) - - def sometask(*args): - time.sleep(2) - self.inform.emit("mytest3") - - with wait_signal(self.inform, to): - self.worker_task.emit({'fcn': sometask, 'params': []}) - - return "mytest3 done" - - def mytest4(*args): - to = int(args[0]) - - def sometask(*args): - time.sleep(2) - 1/0 # Force exception - self.inform.emit("mytest4") - - with wait_signal(self.inform, to): - self.worker_task.emit({'fcn': sometask, 'params': []}) - - return "mytest3 done" - - def export_svg(name, filename, *args): - a, kwa = h(*args) - types = {'scale_factor': float} - - for key in kwa: - if key not in types: - return 'Unknown parameter: %s' % key - kwa[key] = types[key](kwa[key]) - - self.export_svg(str(name), str(filename), **kwa) - - def import_svg(filename, *args): - a, kwa = h(*args) - types = {'outname': str} - - for key in kwa: - if key not in types: - return 'Unknown parameter: %s' % key - kwa[key] = types[key](kwa[key]) - - self.import_svg(str(filename), **kwa) - - def open_gerber(filename, *args): - a, kwa = h(*args) - types = {'follow': bool, - 'outname': str} - - for key in kwa: - if key not in types: - return 'Unknown parameter: %s' % key - kwa[key] = types[key](kwa[key]) - - self.open_gerber(str(filename), **kwa) - - def open_excellon(filename, *args): - a, kwa = h(*args) - types = {'outname': str} - - for key in kwa: - if key not in types: - return 'Unknown parameter: %s' % key - kwa[key] = types[key](kwa[key]) - - self.open_excellon(str(filename), **kwa) - - def open_gcode(filename, *args): - a, kwa = h(*args) - types = {'outname': str} - - for key in kwa: - if key not in types: - return 'Unknown parameter: %s' % key - kwa[key] = types[key](kwa[key]) - - self.open_gcode(str(filename), **kwa) - - def cutout(name, *args): - a, kwa = h(*args) - types = {'dia': float, - 'margin': float, - 'gapsize': float, - 'gaps': str} - - for key in kwa: - if key not in types: - return 'Unknown parameter: %s' % key - kwa[key] = types[key](kwa[key]) - - try: - obj = self.collection.get_by_name(str(name)) - except: - return "Could not retrieve object: %s" % name - - def geo_init_me(geo_obj, app_obj): - margin = kwa['margin'] + kwa['dia'] / 2 - gap_size = kwa['dia'] + kwa['gapsize'] - minx, miny, maxx, maxy = obj.bounds() - minx -= margin - maxx += margin - miny -= margin - maxy += margin - midx = 0.5 * (minx + maxx) - midy = 0.5 * (miny + maxy) - hgap = 0.5 * gap_size - pts = [[midx - hgap, maxy], - [minx, maxy], - [minx, midy + hgap], - [minx, midy - hgap], - [minx, miny], - [midx - hgap, miny], - [midx + hgap, miny], - [maxx, miny], - [maxx, midy - hgap], - [maxx, midy + hgap], - [maxx, maxy], - [midx + hgap, maxy]] - cases = {"tb": [[pts[0], pts[1], pts[4], pts[5]], - [pts[6], pts[7], pts[10], pts[11]]], - "lr": [[pts[9], pts[10], pts[1], pts[2]], - [pts[3], pts[4], pts[7], pts[8]]], - "4": [[pts[0], pts[1], pts[2]], - [pts[3], pts[4], pts[5]], - [pts[6], pts[7], pts[8]], - [pts[9], pts[10], pts[11]]]} - cuts = cases[kwa['gaps']] - geo_obj.solid_geometry = cascaded_union([LineString(segment) for segment in cuts]) - - try: - obj.app.new_object("geometry", name + "_cutout", geo_init_me) - except Exception, e: - return "Operation failed: %s" % str(e) - - return 'Ok' - - def geocutout(name=None, *args): - """ - TCL shell command - see help section - - Subtract gaps from geometry, this will not create new object - - :param name: name of object - :param args: array of arguments - :return: "Ok" if completed without errors - """ - - try: - a, kwa = h(*args) - types = {'dia': float, - 'gapsize': float, - 'gaps': str} - - # How gaps wil be rendered: - # lr - left + right - # tb - top + bottom - # 4 - left + right +top + bottom - # 2lr - 2*left + 2*right - # 2tb - 2*top + 2*bottom - # 8 - 2*left + 2*right +2*top + 2*bottom - - if name is None: - self.raise_tcl_error('Argument name is missing.') - - for key in kwa: - if key not in types: - self.raise_tcl_error('Unknown parameter: %s' % key) - try: - kwa[key] = types[key](kwa[key]) - except Exception, e: - self.raise_tcl_error("Cannot cast argument '%s' to type %s." % (key, str(types[key]))) - - try: - obj = self.collection.get_by_name(str(name)) - except: - self.raise_tcl_error("Could not retrieve object: %s" % name) - - # Get min and max data for each object as we just cut rectangles across X or Y - xmin, ymin, xmax, ymax = obj.bounds() - px = 0.5 * (xmin + xmax) - py = 0.5 * (ymin + ymax) - lenghtx = (xmax - xmin) - lenghty = (ymax - ymin) - gapsize = kwa['gapsize'] + kwa['dia'] / 2 - - if kwa['gaps'] == '8' or kwa['gaps'] == '2lr': - - subtract_rectangle(name, - xmin - gapsize, - py - gapsize + lenghty / 4, - xmax + gapsize, - py + gapsize + lenghty / 4) - subtract_rectangle(name, - xmin-gapsize, - py - gapsize - lenghty / 4, - xmax + gapsize, - py + gapsize - lenghty / 4) - - if kwa['gaps'] == '8' or kwa['gaps']=='2tb': - subtract_rectangle(name, - px - gapsize + lenghtx / 4, - ymin-gapsize, - px + gapsize + lenghtx / 4, - ymax + gapsize) - subtract_rectangle(name, - px - gapsize - lenghtx / 4, - ymin - gapsize, - px + gapsize - lenghtx / 4, - ymax + gapsize) - - if kwa['gaps'] == '4' or kwa['gaps']=='lr': - subtract_rectangle(name, - xmin - gapsize, - py - gapsize, - xmax + gapsize, - py + gapsize) - - if kwa['gaps'] == '4' or kwa['gaps']=='tb': - subtract_rectangle(name, - px - gapsize, - ymin - gapsize, - px + gapsize, - ymax + gapsize) - - except Exception as unknown: - self.raise_tcl_unknown_error(unknown) - - def mirror(name, *args): - a, kwa = h(*args) - types = {'box': str, - 'axis': str, - 'dist': float} - - for key in kwa: - if key not in types: - return 'Unknown parameter: %s' % key - kwa[key] = types[key](kwa[key]) - - # Get source object. - try: - obj = self.collection.get_by_name(str(name)) - except: - 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): - return "ERROR: Only Gerber, Excellon and Geometry objects can be mirrored." - - # Axis - try: - axis = kwa['axis'].upper() - except KeyError: - return "ERROR: Specify -axis X or -axis Y" - - # Box - if 'box' in kwa: - try: - box = self.collection.get_by_name(kwa['box']) - except: - return "Could not retrieve object box: %s" % kwa['box'] - - if box is None: - return "Object box not found: %s" % kwa['box'] - - try: - xmin, ymin, xmax, ymax = box.bounds() - px = 0.5 * (xmin + xmax) - py = 0.5 * (ymin + ymax) - - obj.mirror(axis, [px, py]) - obj.plot() - - except Exception, e: - return "Operation failed: %s" % str(e) - - else: - try: - dist = float(kwa['dist']) - except KeyError: - dist = 0.0 - except ValueError: - return "Invalid distance: %s" % kwa['dist'] - - try: - obj.mirror(axis, [dist, dist]) - obj.plot() - except Exception, e: - return "Operation failed: %s" % str(e) - - return 'Ok' - - def aligndrillgrid(outname, *args): - a, kwa = h(*args) - types = {'gridx': float, - 'gridy': float, - 'gridoffsetx': float, - 'gridoffsety': float, - 'columns':int, - 'rows':int, - 'dia': float - } - for key in kwa: - if key not in types: - return 'Unknown parameter: %s' % key - kwa[key] = types[key](kwa[key]) - - if 'columns' not in kwa or 'rows' not in kwa: - return "ERROR: Specify -columns and -rows" - - if 'gridx' not in kwa or 'gridy' not in kwa: - return "ERROR: Specify -gridx and -gridy" - - if 'dia' not in kwa: - return "ERROR: Specify -dia" - - if 'gridoffsetx' not in kwa: - gridoffsetx=0 - else: - gridoffsetx=kwa['gridoffsetx'] - - if 'gridoffsety' not in kwa: - gridoffsety=0 - else: - gridoffsety=kwa['gridoffsety'] - - # Tools - tools = {"1": {"C": kwa['dia']}} - - def aligndrillgrid_init_me(init_obj, app_obj): - drills = [] - currenty=0 - - for row in range(kwa['rows']): - currentx=0 - - for col in range(kwa['columns']): - point = Point(currentx + gridoffsetx, currenty + gridoffsety) - drills.append({"point": point, "tool": "1"}) - currentx = currentx + kwa['gridx'] - - currenty = currenty + kwa['gridy'] - - init_obj.tools = tools - init_obj.drills = drills - init_obj.create_geometry() - - self.new_object("excellon", outname , aligndrillgrid_init_me) - - def aligndrill(name, *args): - a, kwa = h(*args) - types = {'box': str, - 'axis': str, - 'holes': str, - 'grid': float, - 'minoffset': float, - 'gridoffset': float, - 'axisoffset': float, - 'dia': float, - 'dist': float} - - for key in kwa: - if key not in types: - return 'Unknown parameter: %s' % key - kwa[key] = types[key](kwa[key]) - - # Get source object. - try: - obj = self.collection.get_by_name(str(name)) - except: - 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): - return "ERROR: Only Gerber, Geometry and Excellon objects can be used." - - # Axis - try: - axis = kwa['axis'].upper() - except KeyError: - return "ERROR: Specify -axis X or -axis Y" - - if not ('holes' in kwa or ('grid' in kwa and 'gridoffset' in kwa)): - return "ERROR: Specify -holes or -grid with -gridoffset " - - if 'holes' in kwa: - try: - holes = eval("[" + kwa['holes'] + "]") - except KeyError: - return "ERROR: Wrong -holes format (X1,Y1),(X2,Y2)" - - xscale, yscale = {"X": (1.0, -1.0), "Y": (-1.0, 1.0)}[axis] - - # Tools - tools = {"1": {"C": kwa['dia']}} - - def alligndrill_init_me(init_obj, app_obj): - - drills = [] - if 'holes' in kwa: - 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"}) - else: - if not 'box' in kwa: - return "ERROR: -grid can be used only for -box" - - if 'axisoffset' in kwa: - axisoffset=kwa['axisoffset'] - else: - axisoffset=0 - - # This will align hole to given aligngridoffset and minimal offset from pcb, based on selected axis - if axis == "X": - firstpoint = kwa['gridoffset'] - - while (xmin - kwa['minoffset']) < firstpoint: - firstpoint = firstpoint - kwa['grid'] - - lastpoint = kwa['gridoffset'] - - while (xmax + kwa['minoffset']) > lastpoint: - lastpoint = lastpoint + kwa['grid'] - - localHoles = (firstpoint, axisoffset), (lastpoint, axisoffset) - - else: - firstpoint = kwa['gridoffset'] - - while (ymin - kwa['minoffset']) < firstpoint: - firstpoint = firstpoint - kwa['grid'] - - lastpoint = kwa['gridoffset'] - - while (ymax + kwa['minoffset']) > lastpoint: - lastpoint=lastpoint+kwa['grid'] - - localHoles = (axisoffset, firstpoint), (axisoffset, lastpoint) - - 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"}) - - init_obj.tools = tools - init_obj.drills = drills - init_obj.create_geometry() - - # Box - if 'box' in kwa: - try: - box = self.collection.get_by_name(kwa['box']) - except: - return "Could not retrieve object box: %s" % kwa['box'] - - if box is None: - return "Object box not found: %s" % kwa['box'] - - try: - xmin, ymin, xmax, ymax = box.bounds() - px = 0.5 * (xmin + xmax) - py = 0.5 * (ymin + ymax) - - obj.app.new_object("excellon", name + "_aligndrill", alligndrill_init_me) - - except Exception, e: - return "Operation failed: %s" % str(e) - - else: - try: - dist = float(kwa['dist']) - except KeyError: - dist = 0.0 - except ValueError: - return "Invalid distance: %s" % kwa['dist'] - - try: - px=dist - py=dist - obj.app.new_object("excellon", name + "_alligndrill", alligndrill_init_me) - except Exception, e: - return "Operation failed: %s" % str(e) - - return 'Ok' - - def drillcncjob(name=None, *args): - ''' - TCL shell command - see help section - :param name: name of object - :param args: array of arguments - :return: "Ok" if completed without errors - ''' - - try: - a, kwa = h(*args) - types = {'tools': str, - 'outname': str, - 'drillz': float, - 'travelz': float, - 'feedrate': float, - 'spindlespeed': int, - 'toolchange': int - } - - if name is None: - self.raise_tcl_error('Argument name is missing.') - - for key in kwa: - if key not in types: - self.raise_tcl_error('Unknown parameter: %s' % key) - try: - kwa[key] = types[key](kwa[key]) - except Exception, e: - self.raise_tcl_error("Cannot cast argument '%s' to type %s." % (key, str(types[key]))) - - try: - obj = self.collection.get_by_name(str(name)) - except: - self.raise_tcl_error("Could not retrieve object: %s" % name) - - if obj is None: - self.raise_tcl_error('Object not found: %s' % name) - - if not isinstance(obj, FlatCAMExcellon): - self.raise_tcl_error('Only Excellon objects can be drilled, got %s %s.' % (name, type(obj))) - - try: - # Get the tools from the list - job_name = kwa["outname"] - - # Object initialization function for app.new_object() - def job_init(job_obj, app_obj): - job_obj.z_cut = kwa["drillz"] - job_obj.z_move = kwa["travelz"] - job_obj.feedrate = kwa["feedrate"] - job_obj.spindlespeed = kwa["spindlespeed"] if "spindlespeed" in kwa else None - toolchange = True if "toolchange" in kwa and kwa["toolchange"] == 1 else False - job_obj.generate_from_excellon_by_tool(obj, kwa["tools"], toolchange) - job_obj.gcode_parse() - job_obj.create_geometry() - - obj.app.new_object("cncjob", job_name, job_init) - - except Exception, e: - self.raise_tcl_error("Operation failed: %s" % str(e)) - - except Exception as unknown: - self.raise_tcl_unknown_error(unknown) - - def millholes(name=None, *args): - ''' - TCL shell command - see help section - :param name: name of object - :param args: array of arguments - :return: "Ok" if completed without errors - ''' - - try: - a, kwa = h(*args) - types = {'tooldia': float, - 'tools': str, - 'outname': str} - - if name is None: - self.raise_tcl_error('Argument name is missing.') - - for key in kwa: - if key not in types: - self.raise_tcl_error('Unknown parameter: %s' % key) - try: - kwa[key] = types[key](kwa[key]) - except Exception, e: - self.raise_tcl_error("Cannot cast argument '%s' to type %s." % (key, types[key])) - - try: - if 'tools' in kwa: - kwa['tools'] = [x.strip() for x in kwa['tools'].split(",")] - except Exception as e: - self.raise_tcl_error("Bad tools: %s" % str(e)) - - try: - obj = self.collection.get_by_name(str(name)) - except: - self.raise_tcl_error("Could not retrieve object: %s" % name) - - if obj is None: - self.raise_tcl_error("Object not found: %s" % name) - - if not isinstance(obj, FlatCAMExcellon): - self.raise_tcl_error('Only Excellon objects can be mill-drilled, got %s %s.' % (name, type(obj))) - - try: - # This runs in the background: Block until done. - with wait_signal(self.new_object_available): - success, msg = obj.generate_milling(**kwa) - - except Exception as e: - self.raise_tcl_error("Operation failed: %s" % str(e)) - - if not success: - self.raise_tcl_error(msg) - - except Exception as unknown: - self.raise_tcl_unknown_error(unknown) - - def exteriors(name=None, *args): - ''' - TCL shell command - see help section - :param name: name of object - :param args: array of arguments - :return: "Ok" if completed without errors - ''' - - try: - a, kwa = h(*args) - types = {'outname': str} - - if name is None: - self.raise_tcl_error('Argument name is missing.') - - for key in kwa: - if key not in types: - self.raise_tcl_error('Unknown parameter: %s' % key) - try: - kwa[key] = types[key](kwa[key]) - except Exception, e: - self.raise_tcl_error("Cannot cast argument '%s' to type %s." % (key, types[key])) - - try: - obj = self.collection.get_by_name(str(name)) - except: - self.raise_tcl_error("Could not retrieve object: %s" % name) - - if obj is None: - self.raise_tcl_error("Object not found: %s" % name) - - if not isinstance(obj, Geometry): - self.raise_tcl_error('Expected Geometry, got %s %s.' % (name, type(obj))) - - def geo_init(geo_obj, app_obj): - geo_obj.solid_geometry = obj_exteriors - - if 'outname' in kwa: - outname = kwa['outname'] - else: - outname = name + ".exteriors" - - try: - obj_exteriors = obj.get_exteriors() - self.new_object('geometry', outname, geo_init) - except Exception as e: - self.raise_tcl_error("Failed: %s" % str(e)) - - except Exception as unknown: - self.raise_tcl_unknown_error(unknown) - - def interiors(name=None, *args): - ''' - TCL shell command - see help section - :param name: name of object - :param args: array of arguments - :return: "Ok" if completed without errors - ''' - - try: - a, kwa = h(*args) - types = {'outname': str} - - for key in kwa: - if key not in types: - self.raise_tcl_error('Unknown parameter: %s' % key) - try: - kwa[key] = types[key](kwa[key]) - except Exception, e: - self.raise_tcl_error("Cannot cast argument '%s' to type %s." % (key, types[key])) - - if name is None: - self.raise_tcl_error('Argument name is missing.') - - try: - obj = self.collection.get_by_name(str(name)) - except: - self.raise_tcl_error("Could not retrieve object: %s" % name) - - if obj is None: - self.raise_tcl_error("Object not found: %s" % name) - - if not isinstance(obj, Geometry): - self.raise_tcl_error('Expected Geometry, got %s %s.' % (name, type(obj))) - - def geo_init(geo_obj, app_obj): - geo_obj.solid_geometry = obj_interiors - - if 'outname' in kwa: - outname = kwa['outname'] - else: - outname = name + ".interiors" - - try: - obj_interiors = obj.get_interiors() - self.new_object('geometry', outname, geo_init) - except Exception as e: - self.raise_tcl_error("Failed: %s" % str(e)) - - except Exception as unknown: - self.raise_tcl_unknown_error(unknown) - - def isolate(name=None, *args): - ''' - TCL shell command - see help section - :param name: name of object - :param args: array of arguments - :return: "Ok" if completed without errors - ''' - a, kwa = h(*args) - types = {'dia': float, - 'passes': int, - 'overlap': float, - 'outname': str, - 'combine': int} - - for key in kwa: - if key not in types: - self.raise_tcl_error('Unknown parameter: %s' % key) - try: - kwa[key] = types[key](kwa[key]) - except Exception, e: - self.raise_tcl_error("Cannot cast argument '%s' to type %s." % (key, types[key])) - try: - obj = self.collection.get_by_name(str(name)) - except: - self.raise_tcl_error("Could not retrieve object: %s" % name) - - if obj is None: - self.raise_tcl_error("Object not found: %s" % name) - - assert isinstance(obj, FlatCAMGerber), \ - "Expected a FlatCAMGerber, got %s" % type(obj) - - if not isinstance(obj, FlatCAMGerber): - self.raise_tcl_error('Expected FlatCAMGerber, got %s %s.' % (name, type(obj))) - - try: - obj.isolate(**kwa) - except Exception, e: - self.raise_tcl_error("Operation failed: %s" % str(e)) - - return 'Ok' - - def cncjob(obj_name, *args): - a, kwa = h(*args) - - types = {'z_cut': float, - 'z_move': float, - 'feedrate': float, - 'tooldia': float, - 'outname': str, - 'spindlespeed': int, - 'multidepth' : bool, - 'depthperpass' : float - } - - for key in kwa: - if key not in types: - return 'Unknown parameter: %s' % key - kwa[key] = types[key](kwa[key]) - - try: - obj = self.collection.get_by_name(str(obj_name)) - except: - return "Could not retrieve object: %s" % obj_name - if obj is None: - return "Object not found: %s" % obj_name - - try: - obj.generatecncjob(**kwa) - except Exception, e: - return "Operation failed: %s" % str(e) - - return 'Ok' - - def write_gcode(obj_name, filename, preamble='', postamble=''): - """ - Requires obj_name to be available. It might still be in the - making at the time this function is called, so check for - promises and send to background if there are promises. - """ - - # If there are promised objects, wait until all promises have been fulfilled. - if self.collection.has_promises(): - - def write_gcode_on_object(new_object): - self.log.debug("write_gcode_on_object(): Disconnecting %s" % write_gcode_on_object) - self.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) - - return - - self.log.debug("write_gcode(): No promises. Continuing for %s." % obj_name) - - try: - obj = self.collection.get_by_name(str(obj_name)) - except: - return "Could not retrieve object: %s" % obj_name - - try: - obj.export_gcode(str(filename), str(preamble), str(postamble)) - except Exception, e: - return "Operation failed: %s" % str(e) - - def paint_poly(obj_name, inside_pt_x, inside_pt_y, tooldia, overlap): - try: - obj = self.collection.get_by_name(str(obj_name)) - except: - return "Could not retrieve object: %s" % obj_name - if obj is None: - return "Object not found: %s" % obj_name - obj.paint_poly([float(inside_pt_x), float(inside_pt_y)], float(tooldia), float(overlap)) - - def add_poly(obj_name, *args): - if len(args) % 2 != 0: - return "Incomplete coordinate." - - points = [[float(args[2*i]), float(args[2*i+1])] for i in range(len(args)/2)] - - try: - obj = self.collection.get_by_name(str(obj_name)) - except: - return "Could not retrieve object: %s" % obj_name - if obj is None: - return "Object not found: %s" % obj_name - - obj.add_polygon(points) - - def add_rectangle(obj_name, botleft_x, botleft_y, topright_x, topright_y): - return add_poly(obj_name, botleft_x, botleft_y, botleft_x, topright_y, - topright_x, topright_y, topright_x, botleft_y) - - def subtract_poly(obj_name, *args): - if len(args) % 2 != 0: - return "Incomplete coordinate." - - points = [[float(args[2*i]), float(args[2*i+1])] for i in range(len(args)/2)] - - try: - obj = self.collection.get_by_name(str(obj_name)) - except: - return "Could not retrieve object: %s" % obj_name - if obj is None: - return "Object not found: %s" % obj_name - - obj.subtract_polygon(points) - obj.plot() - - return "OK." - - def subtract_rectangle(obj_name, botleft_x, botleft_y, topright_x, topright_y): - return subtract_poly(obj_name, botleft_x, botleft_y, botleft_x, topright_y, - topright_x, topright_y, topright_x, botleft_y) - - def add_circle(obj_name, center_x, center_y, radius): - try: - obj = self.collection.get_by_name(str(obj_name)) - except: - return "Could not retrieve object: %s" % obj_name - if obj is None: - return "Object not found: %s" % obj_name - - obj.add_circle([float(center_x), float(center_y)], float(radius)) - - def set_active(obj_name): - try: - self.collection.set_active(str(obj_name)) - except Exception, e: - return "Command failed: %s" % str(e) - - def delete(obj_name): - try: - #deselect all to avoid delete selected object when run delete from shell - self.collection.set_all_inactive() - self.collection.set_active(str(obj_name)) - self.on_delete() - except Exception, e: - return "Command failed: %s" % str(e) - - def geo_union(obj_name): - - try: - obj = self.collection.get_by_name(str(obj_name)) - except: - return "Could not retrieve object: %s" % obj_name - if obj is None: - return "Object not found: %s" % obj_name - - obj.union() - - def join_geometries(obj_name, *obj_names): - objs = [] - for obj_n in obj_names: - obj = self.collection.get_by_name(str(obj_n)) - if obj is None: - return "Object not found: %s" % obj_n - else: - objs.append(obj) - - def initialize(obj, app): - FlatCAMGeometry.merge(objs, obj) - - if objs is not None: - self.new_object("geometry", obj_name, initialize) - - def join_excellons(obj_name, *obj_names): - objs = [] - for obj_n in obj_names: - obj = self.collection.get_by_name(str(obj_n)) - if obj is None: - return "Object not found: %s" % obj_n - else: - objs.append(obj) - - def initialize(obj, app): - FlatCAMExcellon.merge(objs, obj) - - if objs is not None: - self.new_object("excellon", obj_name, initialize) - - def panelize(name, *args): - a, kwa = h(*args) - types = {'box': str, - 'spacing_columns': float, - 'spacing_rows': float, - 'columns': int, - 'rows': int, - 'outname': str} - - for key in kwa: - if key not in types: - return 'Unknown parameter: %s' % key - kwa[key] = types[key](kwa[key]) - - # Get source object. - try: - obj = self.collection.get_by_name(str(name)) - except: - return "Could not retrieve object: %s" % name - - if obj is None: - return "Object not found: %s" % name - - if 'box' in kwa: - boxname=kwa['box'] - try: - box = self.collection.get_by_name(boxname) - except: - return "Could not retrieve object: %s" % name - else: - box=obj - - if 'columns' not in kwa or 'rows' not in kwa: - return "ERROR: Specify -columns and -rows" - - if 'outname' in kwa: - outname=kwa['outname'] - else: - outname=name+'_panelized' - - if 'spacing_columns' in kwa: - spacing_columns=kwa['spacing_columns'] - else: - spacing_columns=5 - - if 'spacing_rows' in kwa: - spacing_rows=kwa['spacing_rows'] - else: - spacing_rows=5 - - xmin, ymin, xmax, ymax = box.bounds() - lenghtx = xmax-xmin+spacing_columns - lenghty = ymax-ymin+spacing_rows - - currenty=0 - def initialize_local(obj_init, app): - obj_init.solid_geometry = obj.solid_geometry - obj_init.offset([float(currentx), float(currenty)]), - - def initialize_local_excellon(obj_init, app): - FlatCAMExcellon.merge(obj, obj_init) - obj_init.offset([float(currentx), float(currenty)]), - - def initialize_geometry(obj_init, app): - FlatCAMGeometry.merge(objs, obj_init) - - def initialize_excellon(obj_init, app): - FlatCAMExcellon.merge(objs, obj_init) - - objs=[] - if obj is not None: - - for row in range(kwa['rows']): - currentx=0 - for col in range(kwa['columns']): - local_outname=outname+".tmp."+str(col)+"."+str(row) - if isinstance(obj, FlatCAMExcellon): - new_obj=self.new_object("excellon", local_outname, initialize_local_excellon) - else: - new_obj=self.new_object("geometry", local_outname, initialize_local) - objs.append(new_obj) - currentx=currentx+lenghtx - currenty=currenty+lenghty - - if isinstance(obj, FlatCAMExcellon): - self.new_object("excellon", outname, initialize_excellon) - else: - self.new_object("geometry", outname, initialize_geometry) - - #deselect all to avoid delete selected object when run delete from shell - self.collection.set_all_inactive() - for delobj in objs: - self.collection.set_active(delobj.options['name']) - self.on_delete() - - else: - return "ERROR: obj is None" - - return "Ok" + # def mytest(*args): + # to = int(args[0]) + # + # try: + # for rec in self.recent: + # if rec['kind'] == 'gerber': + # self.open_gerber(str(rec['filename'])) + # break + # + # basename = self.collection.get_names()[0] + # isolate(basename, '-passes', '10', '-combine', '1') + # iso = self.collection.get_by_name(basename + "_iso") + # + # with wait_signal(self.new_object_available, to): + # iso.generatecncjob() + # # iso.generatecncjob() + # # wait_signal2(self.new_object_available, to) + # + # return str(self.collection.get_names()) + # + # except Exception as e: + # return str(e) + # + # def mytest2(*args): + # to = int(args[0]) + # + # for rec in self.recent: + # if rec['kind'] == 'gerber': + # self.open_gerber(str(rec['filename'])) + # break + # + # basename = self.collection.get_names()[0] + # isolate(basename, '-passes', '10', '-combine', '1') + # iso = self.collection.get_by_name(basename + "_iso") + # + # with wait_signal(self.new_object_available, to): + # 1/0 # Force exception + # iso.generatecncjob() + # + # return str(self.collection.get_names()) + # + # def mytest3(*args): + # to = int(args[0]) + # + # def sometask(*args): + # time.sleep(2) + # self.inform.emit("mytest3") + # + # with wait_signal(self.inform, to): + # self.worker_task.emit({'fcn': sometask, 'params': []}) + # + # return "mytest3 done" + # + # def mytest4(*args): + # to = int(args[0]) + # + # def sometask(*args): + # time.sleep(2) + # 1/0 # Force exception + # self.inform.emit("mytest4") + # + # with wait_signal(self.inform, to): + # self.worker_task.emit({'fcn': sometask, 'params': []}) + # + # return "mytest3 done" + + # --- Migrated to new architecture --- + # def export_svg(name, filename, *args): + # a, kwa = h(*args) + # types = {'scale_factor': float} + # + # for key in kwa: + # if key not in types: + # return 'Unknown parameter: %s' % key + # kwa[key] = types[key](kwa[key]) + # + # self.export_svg(str(name), str(filename), **kwa) + + # --- Migrated to new architecture --- + # def import_svg(filename, *args): + # a, kwa = h(*args) + # types = {'outname': str} + # + # for key in kwa: + # if key not in types: + # return 'Unknown parameter: %s' % key + # kwa[key] = types[key](kwa[key]) + # + # self.import_svg(str(filename), **kwa) + + # --- Migrated to new architecture + # def open_gerber(filename, *args): + # a, kwa = h(*args) + # types = {'follow': bool, + # 'outname': str} + # + # for key in kwa: + # if key not in types: + # return 'Unknown parameter: %s' % key + # kwa[key] = types[key](kwa[key]) + # + # self.open_gerber(str(filename), **kwa) + + # --- Migrated to new architecture --- + # def open_excellon(filename, *args): + # a, kwa = h(*args) + # types = {'outname': str} + # + # for key in kwa: + # if key not in types: + # return 'Unknown parameter: %s' % key + # kwa[key] = types[key](kwa[key]) + # + # self.open_excellon(str(filename), **kwa) + + # --- Migrated to new architecture --- + # def open_gcode(filename, *args): + # a, kwa = h(*args) + # types = {'outname': str} + # + # for key in kwa: + # if key not in types: + # return 'Unknown parameter: %s' % key + # kwa[key] = types[key](kwa[key]) + # + # self.open_gcode(str(filename), **kwa) + + # def cutout(name, *args): + # a, kwa = h(*args) + # types = {'dia': float, + # 'margin': float, + # 'gapsize': float, + # 'gaps': str} + # + # for key in kwa: + # if key not in types: + # return 'Unknown parameter: %s' % key + # kwa[key] = types[key](kwa[key]) + # + # try: + # obj = self.collection.get_by_name(str(name)) + # except: + # return "Could not retrieve object: %s" % name + # + # def geo_init_me(geo_obj, app_obj): + # margin = kwa['margin'] + kwa['dia'] / 2 + # gap_size = kwa['dia'] + kwa['gapsize'] + # minx, miny, maxx, maxy = obj.bounds() + # minx -= margin + # maxx += margin + # miny -= margin + # maxy += margin + # midx = 0.5 * (minx + maxx) + # midy = 0.5 * (miny + maxy) + # hgap = 0.5 * gap_size + # pts = [[midx - hgap, maxy], + # [minx, maxy], + # [minx, midy + hgap], + # [minx, midy - hgap], + # [minx, miny], + # [midx - hgap, miny], + # [midx + hgap, miny], + # [maxx, miny], + # [maxx, midy - hgap], + # [maxx, midy + hgap], + # [maxx, maxy], + # [midx + hgap, maxy]] + # cases = {"tb": [[pts[0], pts[1], pts[4], pts[5]], + # [pts[6], pts[7], pts[10], pts[11]]], + # "lr": [[pts[9], pts[10], pts[1], pts[2]], + # [pts[3], pts[4], pts[7], pts[8]]], + # "4": [[pts[0], pts[1], pts[2]], + # [pts[3], pts[4], pts[5]], + # [pts[6], pts[7], pts[8]], + # [pts[9], pts[10], pts[11]]]} + # cuts = cases[kwa['gaps']] + # geo_obj.solid_geometry = cascaded_union([LineString(segment) for segment in cuts]) + # + # try: + # obj.app.new_object("geometry", name + "_cutout", geo_init_me) + # except Exception, e: + # return "Operation failed: %s" % str(e) + # + # return 'Ok' + + # --- Migrated to new architecture --- + # def geocutout(name=None, *args): + # """ + # TCL shell command - see help section + # + # Subtract gaps from geometry, this will not create new object + # + # :param name: name of object + # :param args: array of arguments + # :return: "Ok" if completed without errors + # """ + # + # try: + # a, kwa = h(*args) + # types = {'dia': float, + # 'gapsize': float, + # 'gaps': str} + # + # # How gaps wil be rendered: + # # lr - left + right + # # tb - top + bottom + # # 4 - left + right +top + bottom + # # 2lr - 2*left + 2*right + # # 2tb - 2*top + 2*bottom + # # 8 - 2*left + 2*right +2*top + 2*bottom + # + # if name is None: + # self.raise_tcl_error('Argument name is missing.') + # + # for key in kwa: + # if key not in types: + # self.raise_tcl_error('Unknown parameter: %s' % key) + # try: + # kwa[key] = types[key](kwa[key]) + # except Exception, e: + # self.raise_tcl_error("Cannot cast argument '%s' to type %s." % (key, str(types[key]))) + # + # try: + # obj = self.collection.get_by_name(str(name)) + # except: + # self.raise_tcl_error("Could not retrieve object: %s" % name) + # + # # Get min and max data for each object as we just cut rectangles across X or Y + # xmin, ymin, xmax, ymax = obj.bounds() + # px = 0.5 * (xmin + xmax) + # py = 0.5 * (ymin + ymax) + # lenghtx = (xmax - xmin) + # lenghty = (ymax - ymin) + # gapsize = kwa['gapsize'] + kwa['dia'] / 2 + # + # if kwa['gaps'] == '8' or kwa['gaps'] == '2lr': + # + # subtract_rectangle(name, + # xmin - gapsize, + # py - gapsize + lenghty / 4, + # xmax + gapsize, + # py + gapsize + lenghty / 4) + # subtract_rectangle(name, + # xmin - gapsize, + # py - gapsize - lenghty / 4, + # xmax + gapsize, + # py + gapsize - lenghty / 4) + # + # if kwa['gaps'] == '8' or kwa['gaps'] == '2tb': + # subtract_rectangle(name, + # px - gapsize + lenghtx / 4, + # ymin - gapsize, + # px + gapsize + lenghtx / 4, + # ymax + gapsize) + # subtract_rectangle(name, + # px - gapsize - lenghtx / 4, + # ymin - gapsize, + # px + gapsize - lenghtx / 4, + # ymax + gapsize) + # + # if kwa['gaps'] == '4' or kwa['gaps'] == 'lr': + # subtract_rectangle(name, + # xmin - gapsize, + # py - gapsize, + # xmax + gapsize, + # py + gapsize) + # + # if kwa['gaps'] == '4' or kwa['gaps'] == 'tb': + # subtract_rectangle(name, + # px - gapsize, + # ymin - gapsize, + # px + gapsize, + # ymax + gapsize) + # + # except Exception as unknown: + # self.raise_tcl_unknown_error(unknown) + + # --- Migrated to new architecture --- + # def mirror(name, *args): + # a, kwa = h(*args) + # types = {'box': str, + # 'axis': str, + # 'dist': float} + # + # for key in kwa: + # if key not in types: + # return 'Unknown parameter: %s' % key + # kwa[key] = types[key](kwa[key]) + # + # # Get source object. + # try: + # obj = self.collection.get_by_name(str(name)) + # except: + # 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): + # return "ERROR: Only Gerber, Excellon and Geometry objects can be mirrored." + # + # # Axis + # try: + # axis = kwa['axis'].upper() + # except KeyError: + # return "ERROR: Specify -axis X or -axis Y" + # + # # Box + # if 'box' in kwa: + # try: + # box = self.collection.get_by_name(kwa['box']) + # except: + # return "Could not retrieve object box: %s" % kwa['box'] + # + # if box is None: + # return "Object box not found: %s" % kwa['box'] + # + # try: + # xmin, ymin, xmax, ymax = box.bounds() + # px = 0.5 * (xmin + xmax) + # py = 0.5 * (ymin + ymax) + # + # obj.mirror(axis, [px, py]) + # obj.plot() + # + # except Exception, e: + # return "Operation failed: %s" % str(e) + # + # else: + # try: + # dist = float(kwa['dist']) + # except KeyError: + # dist = 0.0 + # except ValueError: + # return "Invalid distance: %s" % kwa['dist'] + # + # try: + # obj.mirror(axis, [dist, dist]) + # obj.plot() + # except Exception, e: + # return "Operation failed: %s" % str(e) + # + # return 'Ok' + + # --- Migrated to new architecture --- + # def aligndrillgrid(outname, *args): + # a, kwa = h(*args) + # types = {'gridx': float, + # 'gridy': float, + # 'gridoffsetx': float, + # 'gridoffsety': float, + # 'columns':int, + # 'rows':int, + # 'dia': float + # } + # for key in kwa: + # if key not in types: + # return 'Unknown parameter: %s' % key + # kwa[key] = types[key](kwa[key]) + # + # if 'columns' not in kwa or 'rows' not in kwa: + # return "ERROR: Specify -columns and -rows" + # + # if 'gridx' not in kwa or 'gridy' not in kwa: + # return "ERROR: Specify -gridx and -gridy" + # + # if 'dia' not in kwa: + # return "ERROR: Specify -dia" + # + # if 'gridoffsetx' not in kwa: + # gridoffsetx = 0 + # else: + # gridoffsetx = kwa['gridoffsetx'] + # + # if 'gridoffsety' not in kwa: + # gridoffsety = 0 + # else: + # gridoffsety = kwa['gridoffsety'] + # + # # Tools + # tools = {"1": {"C": kwa['dia']}} + # + # def aligndrillgrid_init_me(init_obj, app_obj): + # drills = [] + # currenty = 0 + # + # for row in range(kwa['rows']): + # currentx = 0 + # + # for col in range(kwa['columns']): + # point = Point(currentx + gridoffsetx, currenty + gridoffsety) + # drills.append({"point": point, "tool": "1"}) + # currentx = currentx + kwa['gridx'] + # + # currenty = currenty + kwa['gridy'] + # + # init_obj.tools = tools + # init_obj.drills = drills + # init_obj.create_geometry() + # + # self.new_object("excellon", outname, aligndrillgrid_init_me) + + # --- Migrated to new architecture --- + # def aligndrill(name, *args): + # a, kwa = h(*args) + # types = {'box': str, + # 'axis': str, + # 'holes': str, + # 'grid': float, + # 'minoffset': float, + # 'gridoffset': float, + # 'axisoffset': float, + # 'dia': float, + # 'dist': float} + # + # for key in kwa: + # if key not in types: + # return 'Unknown parameter: %s' % key + # kwa[key] = types[key](kwa[key]) + # + # # Get source object. + # try: + # obj = self.collection.get_by_name(str(name)) + # except: + # 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): + # return "ERROR: Only Gerber, Geometry and Excellon objects can be used." + # + # # Axis + # try: + # axis = kwa['axis'].upper() + # except KeyError: + # return "ERROR: Specify -axis X or -axis Y" + # + # if not ('holes' in kwa or ('grid' in kwa and 'gridoffset' in kwa)): + # return "ERROR: Specify -holes or -grid with -gridoffset " + # + # if 'holes' in kwa: + # try: + # holes = eval("[" + kwa['holes'] + "]") + # except KeyError: + # return "ERROR: Wrong -holes format (X1,Y1),(X2,Y2)" + # + # xscale, yscale = {"X": (1.0, -1.0), "Y": (-1.0, 1.0)}[axis] + # + # # Tools + # tools = {"1": {"C": kwa['dia']}} + # + # def alligndrill_init_me(init_obj, app_obj): + # + # drills = [] + # if 'holes' in kwa: + # 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"}) + # else: + # if 'box' not in kwa: + # return "ERROR: -grid can be used only for -box" + # + # if 'axisoffset' in kwa: + # axisoffset = kwa['axisoffset'] + # else: + # axisoffset = 0 + # + # # This will align hole to given aligngridoffset and minimal offset from pcb, based on selected axis + # if axis == "X": + # firstpoint = kwa['gridoffset'] + # + # while (xmin - kwa['minoffset']) < firstpoint: + # firstpoint = firstpoint - kwa['grid'] + # + # lastpoint = kwa['gridoffset'] + # + # while (xmax + kwa['minoffset']) > lastpoint: + # lastpoint = lastpoint + kwa['grid'] + # + # localholes = (firstpoint, axisoffset), (lastpoint, axisoffset) + # + # else: + # firstpoint = kwa['gridoffset'] + # + # while (ymin - kwa['minoffset']) < firstpoint: + # firstpoint = firstpoint - kwa['grid'] + # + # lastpoint = kwa['gridoffset'] + # + # while (ymax + kwa['minoffset']) > lastpoint: + # lastpoint = lastpoint + kwa['grid'] + # + # localholes = (axisoffset, firstpoint), (axisoffset, lastpoint) + # + # 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"}) + # + # init_obj.tools = tools + # init_obj.drills = drills + # init_obj.create_geometry() + # + # # Box + # if 'box' in kwa: + # try: + # box = self.collection.get_by_name(kwa['box']) + # except: + # return "Could not retrieve object box: %s" % kwa['box'] + # + # if box is None: + # return "Object box not found: %s" % kwa['box'] + # + # try: + # xmin, ymin, xmax, ymax = box.bounds() + # px = 0.5 * (xmin + xmax) + # py = 0.5 * (ymin + ymax) + # + # obj.app.new_object("excellon", name + "_aligndrill", alligndrill_init_me) + # + # except Exception, e: + # return "Operation failed: %s" % str(e) + # + # else: + # try: + # dist = float(kwa['dist']) + # except KeyError: + # dist = 0.0 + # except ValueError: + # return "Invalid distance: %s" % kwa['dist'] + # + # try: + # px=dist + # py=dist + # obj.app.new_object("excellon", name + "_alligndrill", alligndrill_init_me) + # except Exception, e: + # return "Operation failed: %s" % str(e) + # + # return 'Ok' + + # Migrated but still used? + # def drillcncjob(name=None, *args): + # """ + # TCL shell command - see help section + # + # :param name: name of object + # :param args: array of arguments + # :return: "Ok" if completed without errors + # """ + # + # try: + # a, kwa = h(*args) + # types = {'tools': str, + # 'outname': str, + # 'drillz': float, + # 'travelz': float, + # 'feedrate': float, + # 'spindlespeed': int, + # 'toolchange': int + # } + # + # if name is None: + # self.raise_tcl_error('Argument name is missing.') + # + # for key in kwa: + # if key not in types: + # self.raise_tcl_error('Unknown parameter: %s' % key) + # try: + # kwa[key] = types[key](kwa[key]) + # except Exception as e: + # self.raise_tcl_error("Cannot cast argument '%s' to type %s." % (key, str(types[key]))) + # + # try: + # obj = self.collection.get_by_name(str(name)) + # except: + # self.raise_tcl_error("Could not retrieve object: %s" % name) + # + # if obj is None: + # self.raise_tcl_error('Object not found: %s' % name) + # + # if not isinstance(obj, FlatCAMExcellon): + # self.raise_tcl_error('Only Excellon objects can be drilled, got %s %s.' % (name, type(obj))) + # + # try: + # # Get the tools from the list + # job_name = kwa["outname"] + # + # # Object initialization function for app.new_object() + # def job_init(job_obj, app_obj): + # job_obj.z_cut = kwa["drillz"] + # job_obj.z_move = kwa["travelz"] + # job_obj.feedrate = kwa["feedrate"] + # job_obj.spindlespeed = kwa["spindlespeed"] if "spindlespeed" in kwa else None + # toolchange = True if "toolchange" in kwa and kwa["toolchange"] == 1 else False + # job_obj.generate_from_excellon_by_tool(obj, kwa["tools"], toolchange) + # job_obj.gcode_parse() + # job_obj.create_geometry() + # + # obj.app.new_object("cncjob", job_name, job_init) + # + # except Exception, e: + # self.raise_tcl_error("Operation failed: %s" % str(e)) + # + # except Exception as unknown: + # self.raise_tcl_unknown_error(unknown) + + # --- Migrated to new architecture --- + # def millholes(name=None, *args): + # """ + # TCL shell command - see help section + # :param name: name of object + # :param args: array of arguments + # :return: "Ok" if completed without errors + # """ + # + # try: + # a, kwa = h(*args) + # types = {'tooldia': float, + # 'tools': str, + # 'outname': str} + # + # if name is None: + # self.raise_tcl_error('Argument name is missing.') + # + # for key in kwa: + # if key not in types: + # self.raise_tcl_error('Unknown parameter: %s' % key) + # try: + # kwa[key] = types[key](kwa[key]) + # except Exception, e: + # self.raise_tcl_error("Cannot cast argument '%s' to type %s." % (key, types[key])) + # + # try: + # if 'tools' in kwa: + # kwa['tools'] = [x.strip() for x in kwa['tools'].split(",")] + # except Exception as e: + # self.raise_tcl_error("Bad tools: %s" % str(e)) + # + # try: + # obj = self.collection.get_by_name(str(name)) + # except: + # self.raise_tcl_error("Could not retrieve object: %s" % name) + # + # if obj is None: + # self.raise_tcl_error("Object not found: %s" % name) + # + # if not isinstance(obj, FlatCAMExcellon): + # self.raise_tcl_error('Only Excellon objects can be mill-drilled, got %s %s.' % (name, type(obj))) + # + # try: + # # This runs in the background: Block until done. + # with wait_signal(self.new_object_available): + # success, msg = obj.generate_milling(**kwa) + # + # except Exception as e: + # self.raise_tcl_error("Operation failed: %s" % str(e)) + # + # if not success: + # self.raise_tcl_error(msg) + # + # except Exception as unknown: + # self.raise_tcl_unknown_error(unknown) + + # --- Migrated to new architecture --- + # def exteriors(name=None, *args): + # """ + # TCL shell command - see help section + # :param name: name of object + # :param args: array of arguments + # :return: "Ok" if completed without errors + # """ + # + # try: + # a, kwa = h(*args) + # types = {'outname': str} + # + # if name is None: + # self.raise_tcl_error('Argument name is missing.') + # + # for key in kwa: + # if key not in types: + # self.raise_tcl_error('Unknown parameter: %s' % key) + # try: + # kwa[key] = types[key](kwa[key]) + # except Exception, e: + # self.raise_tcl_error("Cannot cast argument '%s' to type %s." % (key, types[key])) + # + # try: + # obj = self.collection.get_by_name(str(name)) + # except: + # self.raise_tcl_error("Could not retrieve object: %s" % name) + # + # if obj is None: + # self.raise_tcl_error("Object not found: %s" % name) + # + # if not isinstance(obj, Geometry): + # self.raise_tcl_error('Expected Geometry, got %s %s.' % (name, type(obj))) + # + # def geo_init(geo_obj, app_obj): + # geo_obj.solid_geometry = obj_exteriors + # + # if 'outname' in kwa: + # outname = kwa['outname'] + # else: + # outname = name + ".exteriors" + # + # try: + # obj_exteriors = obj.get_exteriors() + # self.new_object('geometry', outname, geo_init) + # except Exception as e: + # self.raise_tcl_error("Failed: %s" % str(e)) + # + # except Exception as unknown: + # self.raise_tcl_unknown_error(unknown) + + # --- Migrated to new architecture --- + # def interiors(name=None, *args): + # ''' + # TCL shell command - see help section + # :param name: name of object + # :param args: array of arguments + # :return: "Ok" if completed without errors + # ''' + # + # try: + # a, kwa = h(*args) + # types = {'outname': str} + # + # for key in kwa: + # if key not in types: + # self.raise_tcl_error('Unknown parameter: %s' % key) + # try: + # kwa[key] = types[key](kwa[key]) + # except Exception, e: + # self.raise_tcl_error("Cannot cast argument '%s' to type %s." % (key, types[key])) + # + # if name is None: + # self.raise_tcl_error('Argument name is missing.') + # + # try: + # obj = self.collection.get_by_name(str(name)) + # except: + # self.raise_tcl_error("Could not retrieve object: %s" % name) + # + # if obj is None: + # self.raise_tcl_error("Object not found: %s" % name) + # + # if not isinstance(obj, Geometry): + # self.raise_tcl_error('Expected Geometry, got %s %s.' % (name, type(obj))) + # + # def geo_init(geo_obj, app_obj): + # geo_obj.solid_geometry = obj_interiors + # + # if 'outname' in kwa: + # outname = kwa['outname'] + # else: + # outname = name + ".interiors" + # + # try: + # obj_interiors = obj.get_interiors() + # self.new_object('geometry', outname, geo_init) + # except Exception as e: + # self.raise_tcl_error("Failed: %s" % str(e)) + # + # except Exception as unknown: + # self.raise_tcl_unknown_error(unknown) + + # --- Migrated to new architecture --- + # def isolate(name=None, *args): + # """ + # TCL shell command - see help section + # :param name: name of object + # :param args: array of arguments + # :return: "Ok" if completed without errors + # """ + # + # a, kwa = h(*args) + # types = {'dia': float, + # 'passes': int, + # 'overlap': float, + # 'outname': str, + # 'combine': int} + # + # for key in kwa: + # if key not in types: + # self.raise_tcl_error('Unknown parameter: %s' % key) + # try: + # kwa[key] = types[key](kwa[key]) + # except Exception, e: + # self.raise_tcl_error("Cannot cast argument '%s' to type %s." % (key, types[key])) + # try: + # obj = self.collection.get_by_name(str(name)) + # except: + # self.raise_tcl_error("Could not retrieve object: %s" % name) + # + # if obj is None: + # self.raise_tcl_error("Object not found: %s" % name) + # + # assert isinstance(obj, FlatCAMGerber), \ + # "Expected a FlatCAMGerber, got %s" % type(obj) + # + # if not isinstance(obj, FlatCAMGerber): + # self.raise_tcl_error('Expected FlatCAMGerber, got %s %s.' % (name, type(obj))) + # + # try: + # obj.isolate(**kwa) + # except Exception, e: + # self.raise_tcl_error("Operation failed: %s" % str(e)) + # + # return 'Ok' + + # --- Migrated to new architecture --- + # def cncjob(obj_name, *args): + # a, kwa = h(*args) + # + # types = {'z_cut': float, + # 'z_move': float, + # 'feedrate': float, + # 'tooldia': float, + # 'outname': str, + # 'spindlespeed': int, + # 'multidepth' : bool, + # 'depthperpass' : float + # } + # + # for key in kwa: + # if key not in types: + # return 'Unknown parameter: %s' % key + # kwa[key] = types[key](kwa[key]) + # + # try: + # obj = self.collection.get_by_name(str(obj_name)) + # except: + # return "Could not retrieve object: %s" % obj_name + # if obj is None: + # return "Object not found: %s" % obj_name + # + # try: + # obj.generatecncjob(**kwa) + # except Exception, e: + # return "Operation failed: %s" % str(e) + # + # return 'Ok' + + # --- Migrated to new architecture --- + # def write_gcode(obj_name, filename, preamble='', postamble=''): + # """ + # Requires obj_name to be available. It might still be in the + # making at the time this function is called, so check for + # promises and send to background if there are promises. + # """ + # + # # If there are promised objects, wait until all promises have been fulfilled. + # if self.collection.has_promises(): + # + # def write_gcode_on_object(new_object): + # self.log.debug("write_gcode_on_object(): Disconnecting %s" % write_gcode_on_object) + # self.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) + # + # return + # + # self.log.debug("write_gcode(): No promises. Continuing for %s." % obj_name) + # + # try: + # obj = self.collection.get_by_name(str(obj_name)) + # except: + # return "Could not retrieve object: %s" % obj_name + # + # try: + # obj.export_gcode(str(filename), str(preamble), str(postamble)) + # except Exception, e: + # return "Operation failed: %s" % str(e) + + # --- Migrated to new architecture --- + # def paint_poly(obj_name, inside_pt_x, inside_pt_y, tooldia, overlap): + # try: + # obj = self.collection.get_by_name(str(obj_name)) + # except: + # return "Could not retrieve object: %s" % obj_name + # if obj is None: + # return "Object not found: %s" % obj_name + # obj.paint_poly([float(inside_pt_x), float(inside_pt_y)], float(tooldia), float(overlap)) + + # --- New version in new geometry exists, but required here temporarily. --- + # def add_poly(obj_name, *args): + # """ + # Required by: add_rectangle() + # + # :param obj_name: + # :param args: + # :return: + # """ + # if len(args) % 2 != 0: + # return "Incomplete coordinate." + # + # points = [[float(args[2*i]), float(args[2*i+1])] for i in range(len(args)/2)] + # + # try: + # obj = self.collection.get_by_name(str(obj_name)) + # except: + # return "Could not retrieve object: %s" % obj_name + # if obj is None: + # return "Object not found: %s" % obj_name + # + # obj.add_polygon(points) + + # --- Migrated to new architecture --- + # def add_rectangle(obj_name, botleft_x, botleft_y, topright_x, topright_y): + # return add_poly(obj_name, botleft_x, botleft_y, botleft_x, topright_y, + # topright_x, topright_y, topright_x, botleft_y) + + # --- Migrated to new architecture --- + # def subtract_poly(obj_name, *args): + # """ + # Required by: subtract_rectangle() + # + # :param obj_name: + # :param args: + # :return: + # """ + # if len(args) % 2 != 0: + # return "Incomplete coordinate." + # + # points = [[float(args[2 * i]), float(args[2 * i +1])] for i in range(len(args)/2)] + # + # try: + # obj = self.collection.get_by_name(str(obj_name)) + # except: + # return "Could not retrieve object: %s" % obj_name + # if obj is None: + # return "Object not found: %s" % obj_name + # + # obj.subtract_polygon(points) + # obj.plot() + # + # return "OK." + + # --- Migrated to new architecture --- + # def subtract_rectangle(obj_name, botleft_x, botleft_y, topright_x, topright_y): + # return subtract_poly(obj_name, botleft_x, botleft_y, botleft_x, topright_y, + # topright_x, topright_y, topright_x, botleft_y) + + # --- Migrated to new architecture --- + # def add_circle(obj_name, center_x, center_y, radius): + # try: + # obj = self.collection.get_by_name(str(obj_name)) + # except: + # return "Could not retrieve object: %s" % obj_name + # if obj is None: + # return "Object not found: %s" % obj_name + # + # obj.add_circle([float(center_x), float(center_y)], float(radius)) + + # --- Migrated to new architecture --- + # def set_active(obj_name): + # try: + # self.collection.set_active(str(obj_name)) + # except Exception, e: + # return "Command failed: %s" % str(e) + + # --- Migrated to new architecture --- + # def delete(obj_name): + # try: + # #deselect all to avoid delete selected object when run delete from shell + # self.collection.set_all_inactive() + # self.collection.set_active(str(obj_name)) + # self.on_delete() + # except Exception, e: + # return "Command failed: %s" % str(e) + + # --- Migrated to new architecture --- + # def geo_union(obj_name): + # + # try: + # obj = self.collection.get_by_name(str(obj_name)) + # except: + # return "Could not retrieve object: %s" % obj_name + # if obj is None: + # return "Object not found: %s" % obj_name + # + # obj.union() + + # --- Migrated to new architecture --- + # def join_geometries(obj_name, *obj_names): + # objs = [] + # for obj_n in obj_names: + # obj = self.collection.get_by_name(str(obj_n)) + # if obj is None: + # return "Object not found: %s" % obj_n + # else: + # objs.append(obj) + # + # def initialize(obj, app): + # FlatCAMGeometry.merge(objs, obj) + # + # if objs is not None: + # self.new_object("geometry", obj_name, initialize) + + # --- Migrated to new architecture --- + # def join_excellons(obj_name, *obj_names): + # objs = [] + # for obj_n in obj_names: + # obj = self.collection.get_by_name(str(obj_n)) + # if obj is None: + # return "Object not found: %s" % obj_n + # else: + # objs.append(obj) + # + # def initialize(obj, app): + # FlatCAMExcellon.merge(objs, obj) + # + # if objs is not None: + # self.new_object("excellon", obj_name, initialize) + + # --- Migrated to new architecture --- + # def panelize(name, *args): + # a, kwa = h(*args) + # types = {'box': str, + # 'spacing_columns': float, + # 'spacing_rows': float, + # 'columns': int, + # 'rows': int, + # 'outname': str} + # + # for key in kwa: + # if key not in types: + # return 'Unknown parameter: %s' % key + # kwa[key] = types[key](kwa[key]) + # + # # Get source object. + # try: + # obj = self.collection.get_by_name(str(name)) + # except: + # return "Could not retrieve object: %s" % name + # + # if obj is None: + # return "Object not found: %s" % name + # + # if 'box' in kwa: + # boxname = kwa['box'] + # try: + # box = self.collection.get_by_name(boxname) + # except: + # return "Could not retrieve object: %s" % name + # else: + # box = obj + # + # if 'columns' not in kwa or 'rows' not in kwa: + # return "ERROR: Specify -columns and -rows" + # + # if 'outname' in kwa: + # outname = kwa['outname'] + # else: + # outname = name + '_panelized' + # + # if 'spacing_columns' in kwa: + # spacing_columns = kwa['spacing_columns'] + # else: + # spacing_columns = 5 + # + # if 'spacing_rows' in kwa: + # spacing_rows = kwa['spacing_rows'] + # else: + # spacing_rows = 5 + # + # xmin, ymin, xmax, ymax = box.bounds() + # lenghtx = xmax - xmin + spacing_columns + # lenghty = ymax - ymin + spacing_rows + # + # currenty = 0 + # + # def initialize_local(obj_init, app): + # obj_init.solid_geometry = obj.solid_geometry + # obj_init.offset([float(currentx), float(currenty)]), + # + # def initialize_local_excellon(obj_init, app): + # FlatCAMExcellon.merge(obj, obj_init) + # obj_init.offset([float(currentx), float(currenty)]), + # + # def initialize_geometry(obj_init, app): + # FlatCAMGeometry.merge(objs, obj_init) + # + # def initialize_excellon(obj_init, app): + # FlatCAMExcellon.merge(objs, obj_init) + # + # objs = [] + # if obj is not None: + # + # for row in range(kwa['rows']): + # currentx = 0 + # for col in range(kwa['columns']): + # local_outname = outname + ".tmp." + str(col) + "." + str(row) + # if isinstance(obj, FlatCAMExcellon): + # self.new_object("excellon", local_outname, initialize_local_excellon) + # else: + # self.new_object("geometry", local_outname, initialize_local) + # + # currentx += lenghtx + # currenty += lenghty + # + # if isinstance(obj, FlatCAMExcellon): + # self.new_object("excellon", outname, initialize_excellon) + # else: + # self.new_object("geometry", outname, initialize_geometry) + # + # #deselect all to avoid delete selected object when run delete from shell + # self.collection.set_all_inactive() + # for delobj in objs: + # self.collection.set_active(delobj.options['name']) + # self.on_delete() + # + # else: + # return "ERROR: obj is None" + # + # return "Ok" def make_docs(): output = '' import collections od = collections.OrderedDict(sorted(commands.items())) - for cmd, val in od.iteritems(): + for cmd_, val in od.iteritems(): #print cmd, '\n', ''.join(['~']*len(cmd)) - output += cmd + ' \n' + ''.join(['~'] * len(cmd)) + '\n' + output += cmd_ + ' \n' + ''.join(['~'] * len(cmd_)) + '\n' t = val['help'] usage_i = t.find('>') @@ -3552,7 +3606,6 @@ class App(QtCore.QObject): extras = t[usage_i+end_usage_i+1:] parts = [s.strip() for s in extras.split('\n')] - #print ' ' + t[usage_i:usage_i+end_usage_i] output += ' ' + t[usage_i:usage_i+end_usage_i] + '\n' for p in parts: @@ -3643,349 +3696,384 @@ class App(QtCore.QObject): ''' commands = { - 'mytest': { - 'fcn': mytest, - 'help': "Test function. Only for testing." - }, - 'mytest2': { - 'fcn': mytest2, - 'help': "Test function. Only for testing." - }, - 'mytest3': { - 'fcn': mytest3, - 'help': "Test function. Only for testing." - }, - 'mytest4': { - 'fcn': mytest4, - 'help': "Test function. Only for testing." - }, + # 'mytest': { + # 'fcn': mytest, + # 'help': "Test function. Only for testing." + # }, + # 'mytest2': { + # 'fcn': mytest2, + # 'help': "Test function. Only for testing." + # }, + # 'mytest3': { + # 'fcn': mytest3, + # 'help': "Test function. Only for testing." + # }, + # 'mytest4': { + # 'fcn': mytest4, + # 'help': "Test function. Only for testing." + # }, 'help': { 'fcn': shelp, 'help': "Shows list of commands." }, - 'import_svg': { - 'fcn': import_svg, - 'help': "Import an SVG file as a Geometry Object.\n" + - "> import_svg " + - " filename: Path to the file to import." - }, - 'export_svg': { - 'fcn': export_svg, - 'help': "Export a Geometry Object as a SVG File\n" + - "> export_svg [-scale_factor <0.0 (float)>]\n" + - " name: Name of the geometry object to export.\n" + - " filename: Path to the file to export.\n" + - " scale_factor: Multiplication factor used for scaling line widths during export." - }, - 'open_gerber': { - 'fcn': open_gerber, - 'help': "Opens a Gerber file.\n" - "> open_gerber [-follow <0|1>] [-outname ]\n" - " filename: Path to file to open.\n" + - " follow: If 1, does not create polygons, just follows the gerber path.\n" + - " outname: Name of the created gerber object." - }, - 'open_excellon': { - 'fcn': open_excellon, - 'help': "Opens an Excellon file.\n" + - "> open_excellon [-outname ]\n" + - " filename: Path to file to open.\n" + - " outname: Name of the created excellon object." - }, - 'open_gcode': { - 'fcn': open_gcode, - 'help': "Opens an G-Code file.\n" + - "> open_gcode [-outname ]\n" + - " filename: Path to file to open.\n" + - " outname: Name of the created CNC Job object." - }, - 'open_project': { - 'fcn': self.open_project, - "help": "Opens a FlatCAM project.\n" + - "> open_project \n" + - " filename: Path to file to open." - }, - 'save_project': { - 'fcn': self.save_project, - 'help': "Saves the FlatCAM project to file.\n" + - "> save_project \n" + - " filename: Path to file to save." - }, - 'set_active': { - 'fcn': set_active, - 'help': "Sets a FlatCAM object as active.\n" + - "> set_active \n" + - " name: Name of the object." - }, - 'get_names': { - 'fcn': lambda: '\n'.join(self.collection.get_names()), - 'help': "Lists the names of objects in the project.\n" + - "> get_names" - }, - 'new': { - 'fcn': self.on_file_new, - 'help': "Starts a new project. Clears objects from memory.\n" + - "> new" - }, - 'options': { - 'fcn': options, - 'help': "Shows the settings for an object.\n" + - "> options \n" + - " name: Object name." - }, - 'isolate': { - 'fcn': isolate, - 'help': "Creates isolation routing geometry for the given Gerber.\n" + - "> isolate [-dia ] [-passes

] [-overlap ] [-combine 0|1]\n" + - " name: Name of the object.\n" - " dia: Tool diameter\n passes: # of tool width.\n" + - " overlap: Fraction of tool diameter to overlap passes." + - " combine: combine all passes into one geometry." + - " outname: Name of the resulting Geometry object." - }, - 'cutout': { - 'fcn': cutout, - 'help': "Creates board cutout.\n" + - "> cutout [-dia <3.0 (float)>] [-margin <0.0 (float)>] [-gapsize <0.5 (float)>] [-gaps ]\n" + - " name: Name of the object\n" + - " dia: Tool diameter\n" + - " margin: Margin over bounds\n" + - " gapsize: size of gap\n" + - " gaps: type of gaps" - }, - 'geocutout': { - 'fcn': geocutout, - 'help': "Cut holding gaps from geometry.\n" + - "> geocutout [-dia <3.0 (float)>] [-margin <0.0 (float)>] [-gapsize <0.5 (float)>] [-gaps ]\n" + - " name: Name of the geometry object\n" + - " dia: Tool diameter\n" + - " margin: Margin over bounds\n" + - " gapsize: size of gap\n" + - " gaps: type of gaps\n" + - "\n" + - " example:\n" + - "\n" + - " #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" + - " exteriors BCu_margin_iso -outname BCu_margin_iso_exterior\n" + - "\n" + - " #delete isolated object if you dond need id anymore\n" + - " delete BCu_margin_iso\n" + - "\n" + - " #finally cut holding gaps\n" + - " geocutout BCu_margin_iso_exterior -dia 3 -gapsize 0.6 -gaps 4\n" - }, - 'mirror': { - 'fcn': mirror, - 'help': "Mirror a layer.\n" + - "> mirror -axis [-box | -dist ]\n" + - " name: Name of the object (Gerber or Excellon) to mirror.\n" + - " box: Name of object which act as box (cutout for example.)\n" + - " axis: Mirror axis parallel to the X or Y axis.\n" + - " dist: Distance of the mirror axis to the X or Y axis." - }, - 'aligndrillgrid': { - 'fcn': aligndrillgrid, - 'help': "Create excellon with drills for aligment grid.\n" + - "> aligndrillgrid [-dia <3.0 (float)>] -gridx [-gridoffsetx <0 (float)>] -gridy [-gridoffsety <0 (float)>] -columns -rows \n" + - " outname: Name of the object to create.\n" + - " dia: Tool diameter\n" + - " gridx: grid size in X axis\n" + - " gridoffsetx: move grid from origin\n" + - " gridy: grid size in Y axis\n" + - " gridoffsety: move grid from origin\n" + - " colums: grid holes on X axis\n" + - " rows: grid holes on Y axis\n" - }, - 'aligndrill': { - 'fcn': aligndrill, - 'help': "Create excellon with drills for aligment.\n" + - "> aligndrill [-dia <3.0 (float)>] -axis [-box -minoffset [-grid <10 (float)> -gridoffset <5 (float)> [-axisoffset <0 (float)>]] | -dist ]\n" + - " name: Name of the object (Gerber or Excellon) to mirror.\n" + - " dia: Tool diameter\n" + - " box: Name of object which act as box (cutout for example.)\n" + - " grid: aligning to grid, for thouse, 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.\n" + - " dist: Distance of the mirror axis to the X or Y axis." - }, - 'exteriors': { - 'fcn': exteriors, - 'help': "Get exteriors of polygons.\n" + - "> exteriors [-outname ]\n" + - " name: Name of the source Geometry object.\n" + - " outname: Name of the resulting Geometry object." - }, - 'interiors': { - 'fcn': interiors, - 'help': "Get interiors of polygons.\n" + - "> interiors [-outname ]\n" + - " name: Name of the source Geometry object.\n" + - " outname: Name of the resulting Geometry object." - }, - 'drillcncjob': { - 'fcn': drillcncjob, - 'help': "Drill CNC job.\n" + - "> drillcncjob -tools -drillz " + - "-travelz -feedrate -outname " + - "[-spindlespeed (int)] [-toolchange (int)] \n" + - " name: Name of the object\n" + - " tools: Comma separated indexes of tools (example: 1,3 or 2)\n" + - " drillz: Drill depth into material (example: -2.0)\n" + - " travelz: Travel distance above material (example: 2.0)\n" + - " feedrate: Drilling feed rate\n" + - " outname: Name of object to create\n" + - " spindlespeed: Speed of the spindle in rpm (example: 4000)\n" + - " toolchange: Enable tool changes (example: 1)\n" - }, - 'millholes': { - 'fcn': millholes, - 'help': "Create Geometry Object for milling holes from Excellon.\n" + - "> millholes -tools -tooldia -outname \n" + - " name: Name of the Excellon Object\n" + - " tools: Comma separated indexes of tools (example: 1,3 or 2)\n" + - " tooldia: Diameter of the milling tool (example: 0.1)\n" + - " outname: Name of object to create\n" - }, - 'scale': { - 'fcn': lambda name, factor: self.collection.get_by_name(str(name)).scale(float(factor)), - 'help': "Resizes the object by a factor.\n" + - "> scale \n" + - " name: Name of the object\n factor: Fraction by which to scale" - }, - 'offset': { - 'fcn': lambda name, x, y: self.collection.get_by_name(str(name)).offset([float(x), float(y)]), - 'help': "Changes the position of the object.\n" + - "> offset \n" + - " name: Name of the object\n" + - " x: X-axis distance\n" + - " y: Y-axis distance" - }, - 'plot': { - 'fcn': self.plot_all, - 'help': 'Updates the plot on the user interface' - }, - 'cncjob': { - 'fcn': cncjob, - 'help': 'Generates a CNC Job from a Geometry Object.\n' + - '> cncjob [-z_cut ] [-z_move ] [-feedrate ] [-tooldia ] [-spindlespeed ] [-multidepth ] [-depthperpass ] [-outname ]\n' + - ' name: Name of the source object\n' + - ' z_cut: Z-axis cutting position\n' + - ' z_move: Z-axis moving position\n' + - ' feedrate: Moving speed when cutting\n' + - ' tooldia: Tool diameter to show on screen\n' + - ' spindlespeed: Speed of the spindle in rpm (example: 4000)\n' + - ' multidepth: Use or not multidepth cnccut\n'+ - ' depthperpass: Height of one layer for multidepth\n'+ - ' outname: Name of the output object' - }, - 'write_gcode': { - 'fcn': write_gcode, - 'help': 'Saves G-code of a CNC Job object to file.\n' + - '> write_gcode \n' + - ' name: Source CNC Job object\n' + - ' filename: Output filename' - }, - 'paint_poly': { - 'fcn': paint_poly, - 'help': 'Creates a geometry object with toolpath to cover the inside of a polygon.\n' + - '> paint_poly \n' + - ' name: Name of the sourge geometry object.\n' + - ' inside_pt_x, inside_pt_y: Coordinates of a point inside the polygon.\n' + - ' tooldia: Diameter of the tool to be used.\n' + - ' overlap: Fraction of the tool diameter to overlap cuts.' - }, - 'new_geometry': { - 'fcn': lambda name: self.new_object('geometry', str(name), lambda x, y: None), - 'help': 'Creates a new empty geometry object.\n' + - '> new_geometry \n' + - ' name: New object name' - }, - 'add_poly': { - 'fcn': add_poly, - 'help': 'Creates a polygon in the given Geometry object.\n' + - '> create_poly [x3 y3 [...]]\n' + - ' name: Name of the geometry object to which to append the polygon.\n' + - ' xi, yi: Coordinates of points in the polygon.' - }, - 'subtract_poly': { - 'fcn': subtract_poly, - 'help': 'Subtract polygon from the given Geometry object.\n' + - '> subtract_poly [x3 y3 [...]]\n' + - ' name: Name of the geometry object, which will be sutracted.\n' + - ' xi, yi: Coordinates of points in the polygon.' - }, - 'delete': { - 'fcn': delete, - 'help': 'Deletes the give object.\n' + - '> delete \n' + - ' name: Name of the object to delete.' - }, - 'geo_union': { - 'fcn': geo_union, - 'help': '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' + - 'a single larger polygon.\n' + - '> geo_union \n' + - ' name: Name of the geometry object.' - }, - 'join_geometries': { - 'fcn': join_geometries, - 'help': 'Runs a merge operation (join) on the geometry ' + - 'objects.' + - '> join_geometries ....\n' + - ' out_name: Name of the new geometry object.' + - ' obj_name_0... names of the objects to join' - }, - 'join_excellons': { - 'fcn': join_excellons, - 'help': 'Runs a merge operation (join) on the excellon ' + - 'objects.' + - '> join_excellons ....\n' + - ' out_name: Name of the new excellon object.' + - ' obj_name_0... names of the objects to join' - }, - 'panelize': { - 'fcn': panelize, - 'help': "Simple panelize geometries.\n" + - "> panelize [-box ] [-spacing_columns <5 (float)>] [-spacing_rows <5 (float)>] -columns -rows [-outname ]\n" + - " name: Name of the object to panelize.\n" + - " box: Name of object which act as box (cutout for example.) for cutout boundary. Object from name is used if not specified.\n" + - " spacing_columns: spacing between columns\n"+ - " spacing_rows: spacing between rows\n"+ - " columns: number of columns\n"+ - " rows: number of rows\n"+ - " outname: Name of the new geometry object." - }, - 'subtract_rect': { - 'fcn': subtract_rectangle, - 'help': 'Subtract rectange from the given Geometry object.\n' + - '> subtract_rect \n' + - ' name: Name of the geometry object, which will be subtracted.\n' + - ' botleft_x, botleft_y: Coordinates of the bottom left corner.\n' + - ' topright_x, topright_y Coordinates of the top right corner.' - }, - 'add_rect': { - 'fcn': add_rectangle, - 'help': 'Creates a rectange in the given Geometry object.\n' + - '> add_rect \n' + - ' name: Name of the geometry object to which to append the rectangle.\n' + - ' botleft_x, botleft_y: Coordinates of the bottom left corner.\n' + - ' topright_x, topright_y Coordinates of the top right corner.' - }, - 'add_circle': { - 'fcn': add_circle, - 'help': 'Creates a circle in the given Geometry object.\n' + - '> add_circle \n' + - ' name: Name of the geometry object to which to append the circle.\n' + - ' center_x, center_y: Coordinates of the center of the circle.\n' + - ' radius: Radius of the circle.' - }, + # --- Migrated to new architecture --- + # 'import_svg': { + # 'fcn': import_svg, + # 'help': "Import an SVG file as a Geometry Object.\n" + + # "> import_svg " + + # " filename: Path to the file to import." + # }, + # --- Migrated to new architecture --- + # 'export_svg': { + # 'fcn': export_svg, + # 'help': "Export a Geometry Object as a SVG File\n" + + # "> export_svg [-scale_factor <0.0 (float)>]\n" + + # " name: Name of the geometry object to export.\n" + + # " filename: Path to the file to export.\n" + + # " scale_factor: Multiplication factor used for scaling line widths during export." + # }, + # --- Migrated to new architecture --- + # 'open_gerber': { + # 'fcn': open_gerber, + # 'help': "Opens a Gerber file.\n" + # "> open_gerber [-follow <0|1>] [-outname ]\n" + # " filename: Path to file to open.\n" + + # " follow: If 1, does not create polygons, just follows the gerber path.\n" + + # " outname: Name of the created gerber object." + # }, + # --- Migrated to new architecture --- + # 'open_excellon': { + # 'fcn': open_excellon, + # 'help': "Opens an Excellon file.\n" + + # "> open_excellon [-outname ]\n" + + # " filename: Path to file to open.\n" + + # " outname: Name of the created excellon object." + # }, + # --- Migrated to new architecture --- + # 'open_gcode': { + # 'fcn': open_gcode, + # 'help': "Opens an G-Code file.\n" + + # "> open_gcode [-outname ]\n" + + # " filename: Path to file to open.\n" + + # " outname: Name of the created CNC Job object." + # }, + # --- Migrated to new architecture --- + # 'open_project': { + # 'fcn': self.open_project, + # "help": "Opens a FlatCAM project.\n" + + # "> open_project \n" + + # " filename: Path to file to open." + # }, + # --- Migrated to new architecture --- + # 'save_project': { + # 'fcn': self.save_project, + # 'help': "Saves the FlatCAM project to file.\n" + + # "> save_project \n" + + # " filename: Path to file to save." + # }, + # --- Migrated to new architecture --- + # 'set_active': { + # 'fcn': set_active, + # 'help': "Sets a FlatCAM object as active.\n" + + # "> set_active \n" + + # " name: Name of the object." + # }, + # --- Migrated to new architecture --- + # 'get_names': { + # 'fcn': lambda: '\n'.join(self.collection.get_names()), + # 'help': "Lists the names of objects in the project.\n" + + # "> get_names" + # }, + # --- Migrated to new architecture --- + # 'new': { + # 'fcn': self.on_file_new, + # 'help': "Starts a new project. Clears objects from memory.\n" + + # "> new" + # }, + # --- Migrated to new architecture --- + # 'options': { + # 'fcn': options, + # 'help': "Shows the settings for an object.\n" + + # "> options \n" + + # " name: Object name." + # }, + # --- Migrated to new architecture --- + # 'isolate': { + # 'fcn': isolate, + # 'help': "Creates isolation routing geometry for the given Gerber.\n" + + # "> isolate [-dia ] [-passes

] [-overlap ] [-combine 0|1]\n" + + # " name: Name of the object.\n" + # " dia: Tool diameter\n passes: # of tool width.\n" + + # " overlap: Fraction of tool diameter to overlap passes." + + # " combine: combine all passes into one geometry." + + # " outname: Name of the resulting Geometry object." + # }, + # 'cutout': { + # 'fcn': cutout, + # 'help': "Creates board cutout.\n" + + # "> cutout [-dia <3.0 (float)>] [-margin <0.0 (float)>] [-gapsize <0.5 (float)>] [-gaps ]\n" + + # " name: Name of the object\n" + + # " dia: Tool diameter\n" + + # " margin: Margin over bounds\n" + + # " gapsize: size of gap\n" + + # " gaps: type of gaps" + # }, + # --- Migrated to new architecture --- + # 'geocutout': { + # 'fcn': geocutout, + # 'help': "Cut holding gaps from geometry.\n" + + # "> geocutout [-dia <3.0 (float)>] [-margin <0.0 (float)>] [-gapsize <0.5 (float)>] [-gaps ]\n" + + # " name: Name of the geometry object\n" + + # " dia: Tool diameter\n" + + # " margin: Margin over bounds\n" + + # " gapsize: size of gap\n" + + # " gaps: type of gaps\n" + + # "\n" + + # " example:\n" + + # "\n" + + # " #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" + + # " exteriors BCu_margin_iso -outname BCu_margin_iso_exterior\n" + + # "\n" + + # " #delete isolated object if you dond need id anymore\n" + + # " delete BCu_margin_iso\n" + + # "\n" + + # " #finally cut holding gaps\n" + + # " geocutout BCu_margin_iso_exterior -dia 3 -gapsize 0.6 -gaps 4\n" + # }, + # --- Migrated to new architecture --- + # 'mirror': { + # 'fcn': mirror, + # 'help': "Mirror a layer.\n" + + # "> mirror -axis [-box | -dist ]\n" + + # " name: Name of the object (Gerber or Excellon) to mirror.\n" + + # " box: Name of object which act as box (cutout for example.)\n" + + # " axis: Mirror axis parallel to the X or Y axis.\n" + + # " dist: Distance of the mirror axis to the X or Y axis." + #}, + # --- Migrated to new architecture --- + # 'aligndrillgrid': { + # 'fcn': aligndrillgrid, + # 'help': "Create excellon with drills for aligment grid.\n" + + # "> aligndrillgrid [-dia <3.0 (float)>] -gridx [-gridoffsetx <0 (float)>] -gridy [-gridoffsety <0 (float)>] -columns -rows \n" + + # " outname: Name of the object to create.\n" + + # " dia: Tool diameter\n" + + # " gridx: grid size in X axis\n" + + # " gridoffsetx: move grid from origin\n" + + # " gridy: grid size in Y axis\n" + + # " gridoffsety: move grid from origin\n" + + # " colums: grid holes on X axis\n" + + # " rows: grid holes on Y axis\n" + # }, + # --- Migrated to new architecture --- + # 'aligndrill': { + # 'fcn': aligndrill, + # 'help': "Create excellon with drills for aligment.\n" + + # "> aligndrill [-dia <3.0 (float)>] -axis [-box -minoffset [-grid <10 (float)> -gridoffset <5 (float)> [-axisoffset <0 (float)>]] | -dist ]\n" + + # " name: Name of the object (Gerber or Excellon) to mirror.\n" + + # " dia: Tool diameter\n" + + # " box: Name of object which act as box (cutout for example.)\n" + + # " grid: aligning to grid, for thouse, 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.\n" + + # " dist: Distance of the mirror axis to the X or Y axis." + # }, + # --- Migrated to new architecture --- + # 'exteriors': { + # 'fcn': exteriors, + # 'help': "Get exteriors of polygons.\n" + + # "> exteriors [-outname ]\n" + + # " name: Name of the source Geometry object.\n" + + # " outname: Name of the resulting Geometry object." + # }, + # --- Migrated to new architecture --- + # 'interiors': { + # 'fcn': interiors, + # 'help': "Get interiors of polygons.\n" + + # "> interiors [-outname ]\n" + + # " name: Name of the source Geometry object.\n" + + # " outname: Name of the resulting Geometry object." + # }, + # --- Migrated to new architecture --- + # 'drillcncjob': { + # 'fcn': drillcncjob, + # 'help': "Drill CNC job.\n" + + # "> drillcncjob -tools -drillz " + + # "-travelz -feedrate -outname " + + # "[-spindlespeed (int)] [-toolchange (int)] \n" + + # " name: Name of the object\n" + + # " tools: Comma separated indexes of tools (example: 1,3 or 2)\n" + + # " drillz: Drill depth into material (example: -2.0)\n" + + # " travelz: Travel distance above material (example: 2.0)\n" + + # " feedrate: Drilling feed rate\n" + + # " outname: Name of object to create\n" + + # " spindlespeed: Speed of the spindle in rpm (example: 4000)\n" + + # " toolchange: Enable tool changes (example: 1)\n" + # }, + # 'millholes': { + # 'fcn': millholes, + # 'help': "Create Geometry Object for milling holes from Excellon.\n" + + # "> millholes -tools -tooldia -outname \n" + + # " name: Name of the Excellon Object\n" + + # " tools: Comma separated indexes of tools (example: 1,3 or 2)\n" + + # " tooldia: Diameter of the milling tool (example: 0.1)\n" + + # " outname: Name of object to create\n" + # }, + # --- Migrated to the new architecture --- + # 'scale': { + # 'fcn': lambda name, factor: self.collection.get_by_name(str(name)).scale(float(factor)), + # 'help': "Resizes the object by a factor.\n" + + # "> scale \n" + + # " name: Name of the object\n factor: Fraction by which to scale" + # }, + # --- Migrated to the new architecture --- + # 'offset': { + # 'fcn': lambda name, x, y: self.collection.get_by_name(str(name)).offset([float(x), float(y)]), + # 'help': "Changes the position of the object.\n" + + # "> offset \n" + + # " name: Name of the object\n" + + # " x: X-axis distance\n" + + # " y: Y-axis distance" + # }, + # --- Migrated to new architecture --- + # 'plot': { + # 'fcn': self.plot_all, + # 'help': 'Updates the plot on the user interface' + # }, + # --- Migrated to new architecture --- + # 'cncjob': { + # 'fcn': cncjob, + # 'help': 'Generates a CNC Job from a Geometry Object.\n' + + # '> cncjob [-z_cut ] [-z_move ] [-feedrate ] [-tooldia ] [-spindlespeed ] [-multidepth ] [-depthperpass ] [-outname ]\n' + + # ' name: Name of the source object\n' + + # ' z_cut: Z-axis cutting position\n' + + # ' z_move: Z-axis moving position\n' + + # ' feedrate: Moving speed when cutting\n' + + # ' tooldia: Tool diameter to show on screen\n' + + # ' spindlespeed: Speed of the spindle in rpm (example: 4000)\n' + + # ' multidepth: Use or not multidepth cnccut\n'+ + # ' depthperpass: Height of one layer for multidepth\n'+ + # ' outname: Name of the output object' + # }, + # --- Migrated to new architecture --- + # 'write_gcode': { + # 'fcn': write_gcode, + # 'help': 'Saves G-code of a CNC Job object to file.\n' + + # '> write_gcode \n' + + # ' name: Source CNC Job object\n' + + # ' filename: Output filename' + # }, + # --- Migrated to new architecture --- + # 'paint_poly': { + # 'fcn': paint_poly, + # 'help': 'Creates a geometry object with toolpath to cover the inside of a polygon.\n' + + # '> paint_poly \n' + + # ' name: Name of the sourge geometry object.\n' + + # ' inside_pt_x, inside_pt_y: Coordinates of a point inside the polygon.\n' + + # ' tooldia: Diameter of the tool to be used.\n' + + # ' overlap: Fraction of the tool diameter to overlap cuts.' + # }, + # --- Migrated to new architecture --- + # 'new_geometry': { + # 'fcn': lambda name: self.new_object('geometry', str(name), lambda x, y: None), + # 'help': 'Creates a new empty geometry object.\n' + + # '> new_geometry \n' + + # ' name: New object name' + # }, + # --- Migrated to new architecture --- + # 'add_poly': { + # 'fcn': add_poly, + # 'help': 'Creates a polygon in the given Geometry object.\n' + + # '> create_poly [x3 y3 [...]]\n' + + # ' name: Name of the geometry object to which to append the polygon.\n' + + # ' xi, yi: Coordinates of points in the polygon.' + # }, + # --- Migrated to new architecture --- + # 'subtract_poly': { + # 'fcn': subtract_poly, + # 'help': 'Subtract polygon from the given Geometry object.\n' + + # '> subtract_poly [x3 y3 [...]]\n' + + # ' name: Name of the geometry object, which will be sutracted.\n' + + # ' xi, yi: Coordinates of points in the polygon.' + # }, + # --- Migrated to new architecture --- + # 'delete': { + # 'fcn': delete, + # 'help': 'Deletes the give object.\n' + + # '> delete \n' + + # ' name: Name of the object to delete.' + # }, + # --- Migrated to new architecture --- + # 'geo_union': { + # 'fcn': geo_union, + # 'help': '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' + + # 'a single larger polygon.\n' + + # '> geo_union \n' + + # ' name: Name of the geometry object.' + # }, + # --- Migrated to new architecture --- + # 'join_geometries': { + # 'fcn': join_geometries, + # 'help': 'Runs a merge operation (join) on the geometry ' + + # 'objects.' + + # '> join_geometries ....\n' + + # ' out_name: Name of the new geometry object.' + + # ' obj_name_0... names of the objects to join' + # }, + # --- Migrated to new architecture --- + # 'join_excellons': { + # 'fcn': join_excellons, + # 'help': 'Runs a merge operation (join) on the excellon ' + + # 'objects.' + + # '> join_excellons ....\n' + + # ' out_name: Name of the new excellon object.' + + # ' obj_name_0... names of the objects to join' + # }, + # --- Migrated to new architecture --- + # 'panelize': { + # 'fcn': panelize, + # 'help': "Simple panelize geometries.\n" + + # "> panelize [-box ] [-spacing_columns <5 (float)>] [-spacing_rows <5 (float)>] -columns -rows [-outname ]\n" + + # " name: Name of the object to panelize.\n" + + # " box: Name of object which act as box (cutout for example.) for cutout boundary. Object from name is used if not specified.\n" + + # " spacing_columns: spacing between columns\n"+ + # " spacing_rows: spacing between rows\n"+ + # " columns: number of columns\n"+ + # " rows: number of rows\n"+ + # " outname: Name of the new geometry object." + # }, + # 'subtract_rect': { + # 'fcn': subtract_rectangle, + # 'help': 'Subtract rectange from the given Geometry object.\n' + + # '> subtract_rect \n' + + # ' name: Name of the geometry object, which will be subtracted.\n' + + # ' botleft_x, botleft_y: Coordinates of the bottom left corner.\n' + + # ' topright_x, topright_y Coordinates of the top right corner.' + # }, + # --- Migrated to new architecture --- + # 'add_rect': { + # 'fcn': add_rectangle, + # 'help': 'Creates a rectange in the given Geometry object.\n' + + # '> add_rect \n' + + # ' name: Name of the geometry object to which to append the rectangle.\n' + + # ' botleft_x, botleft_y: Coordinates of the bottom left corner.\n' + + # ' topright_x, topright_y Coordinates of the top right corner.' + # }, + # --- Migrated to new architecture --- + # 'add_circle': { + # 'fcn': add_circle, + # 'help': 'Creates a circle in the given Geometry object.\n' + + # '> add_circle \n' + + # ' name: Name of the geometry object to which to append the circle.\n' + + # ' center_x, center_y: Coordinates of the center of the circle.\n' + + # ' radius: Radius of the circle.' + # }, 'make_docs': { 'fcn': make_docs, 'help': 'Prints command rererence in reStructuredText format.' diff --git a/FlatCAMGUI.py b/FlatCAMGUI.py index 7fb69d03..17170bbe 100644 --- a/FlatCAMGUI.py +++ b/FlatCAMGUI.py @@ -77,12 +77,12 @@ class FlatCAMGUI(QtGui.QMainWindow): self.menufile.addAction(self.menufilesavedefaults) # Quit - exit_action = QtGui.QAction(QtGui.QIcon('share/power16.png'), '&Exit', self) + self.exit_action = QtGui.QAction(QtGui.QIcon('share/power16.png'), '&Exit', self) # exitAction.setShortcut('Ctrl+Q') # exitAction.setStatusTip('Exit application') - exit_action.triggered.connect(QtGui.qApp.quit) + #self.exit_action.triggered.connect(QtGui.qApp.quit) - self.menufile.addAction(exit_action) + self.menufile.addAction(self.exit_action) ### Edit ### self.menuedit = self.menu.addMenu('&Edit') diff --git a/tclCommands/TclCommand.py b/tclCommands/TclCommand.py index 470358fb..459a078f 100644 --- a/tclCommands/TclCommand.py +++ b/tclCommands/TclCommand.py @@ -116,7 +116,7 @@ class TclCommand(object): return "\t[" + option_symbol + help_key + " <" + type_name + ">: " + help_text + "]" def get_decorated_example(example_item): - return "> "+example_item + return "> " + example_item help_string = [self.help['main']] for alias in self.aliases: @@ -214,7 +214,6 @@ class TclCommand(object): return named_args, unnamed_args - def raise_tcl_unknown_error(self, unknownException): """ raise Exception if is different type than TclErrorException @@ -253,11 +252,11 @@ class TclCommand(object): try: self.log.debug("TCL command '%s' executed." % str(self.__class__)) - self.original_args=args + 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() + error_info = sys.exc_info() self.log.error("TCL command '%s' failed." % str(self)) self.app.display_tcl_error(unknown, error_info) self.raise_tcl_unknown_error(unknown) @@ -276,6 +275,7 @@ class TclCommand(object): raise NotImplementedError("Please Implement this method") + class TclCommandSignaled(TclCommand): """ !!! I left it here only for demonstration !!! @@ -297,12 +297,12 @@ class TclCommandSignaled(TclCommand): try: self.output = None - self.error=None - self.error_info=None + self.error = None + self.error_info = None self.output = self.execute(args, unnamed_args) except Exception as unknown: self.error_info = sys.exc_info() - self.error=unknown + self.error = unknown finally: self.app.shell_command_finished.emit(self) @@ -357,17 +357,19 @@ class TclCommandSignaled(TclCommand): raise ex[0] if status['timed_out']: - self.app.raise_tcl_unknown_error("Operation timed outed! Consider increasing option '-timeout ' for command or 'set_sys background_timeout '.") + self.app.raise_tcl_unknown_error("Operation timed outed! Consider increasing option " + "'-timeout ' for command or " + "'set_sys background_timeout '.") try: self.log.debug("TCL command '%s' executed." % str(self.__class__)) - self.original_args=args + self.original_args = args args, unnamed_args = self.check_args(args) if 'timeout' in args: - passed_timeout=args['timeout'] + passed_timeout = args['timeout'] del args['timeout'] else: - passed_timeout= self.app.defaults['background_timeout'] + passed_timeout = self.app.defaults['background_timeout'] # set detail for processing, it will be there until next open or close self.app.shell.open_proccessing(self.get_current_command()) @@ -392,7 +394,7 @@ class TclCommandSignaled(TclCommand): if self.error_info is not None: error_info = self.error_info else: - error_info=sys.exc_info() + error_info = sys.exc_info() self.log.error("TCL command '%s' failed." % str(self)) self.app.display_tcl_error(unknown, error_info) - self.raise_tcl_unknown_error(unknown) \ No newline at end of file + self.raise_tcl_unknown_error(unknown) diff --git a/tclCommands/TclCommandAddCircle.py b/tclCommands/TclCommandAddCircle.py new file mode 100644 index 00000000..281c918d --- /dev/null +++ b/tclCommands/TclCommandAddCircle.py @@ -0,0 +1,65 @@ +from ObjectCollection import * +import TclCommand + + +class TclCommandAddCircle(TclCommand.TclCommand): + """ + Tcl shell command to creates a circle in the given Geometry object. + + example: + + """ + + # List of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon) + aliases = ['export_svg'] + + # Dictionary of types from Tcl command, needs to be ordered + arg_names = collections.OrderedDict([ + ('name', str), + ('center_x', float), + ('center_y', float), + ('radius', float) + ]) + + # 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', 'center_x', 'center_y', 'radius'] + + # structured help for current command, args needs to be ordered + help = { + 'main': "Creates a circle in the given Geometry object.", + 'args': collections.OrderedDict([ + ('name', 'Name of the geometry object to which to append the circle.'), + ('center_x', 'X coordinate of the center of the circle.'), + ('center_y', 'Y coordinates of the center of the circle.'), + ('radius', 'Radius of the circle.') + ]), + 'examples': [] + } + + def execute(self, args, unnamed_args): + """ + + :param args: + :param unnamed_args: + :return: + """ + + name = args['name'] + center_x = args['center_x'] + center_y = args['center_y'] + radius = args['radius'] + + try: + obj = self.collection.get_by_name(name) + except: + return "Could not retrieve object: %s" % name + if obj is None: + return "Object not found: %s" % name + + obj.add_circle([float(center_x), float(center_y)], float(radius)) + diff --git a/tclCommands/TclCommandAddRectangle.py b/tclCommands/TclCommandAddRectangle.py new file mode 100644 index 00000000..e5439aad --- /dev/null +++ b/tclCommands/TclCommandAddRectangle.py @@ -0,0 +1,66 @@ +from ObjectCollection import * +import TclCommand + + +class TclCommandAddRectangle(TclCommand.TclCommandSignaled): + """ + Tcl shell command to add a rectange to the given Geometry object. + """ + + # array of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon) + aliases = ['add_rectangle'] + + # 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) + ]) + + # 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 = ['name', 'x0', 'y0', 'x1', 'y1'] + + # structured help for current command, args needs to be ordered + help = { + 'main': "Add a rectange to 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': [] + } + + 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 + """ + + obj_name = args['name'] + x0 = args['x0'] + y0 = args['y0'] + x1 = args['x1'] + y1 = args['y1'] + + try: + obj = self.app.collection.get_by_name(str(obj_name)) + except: + return "Could not retrieve object: %s" % obj_name + if obj is None: + return "Object not found: %s" % obj_name + + obj.add_polygon([(x0, y0), (x1, y0), (x1, y1), (x0, y1)]) diff --git a/tclCommands/TclCommandAlignDrill.py b/tclCommands/TclCommandAlignDrill.py new file mode 100644 index 00000000..54caca02 --- /dev/null +++ b/tclCommands/TclCommandAlignDrill.py @@ -0,0 +1,201 @@ +from ObjectCollection import * +import TclCommand + + +class TclCommandAlignDrill(TclCommand.TclCommandSignaled): + """ + Tcl shell command to create excellon with drills for aligment. + """ + + # array of all command aliases, to be able use old names for + # backward compatibility (add_poly, add_polygon) + aliases = ['aligndrill'] + + # 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([ + ('box', str), + ('axis', str), + ('holes', str), + ('grid', float), + ('minoffset', float), + ('gridoffset', float), + ('axisoffset', float), + ('dia', float), + ('dist', float), + ]) + + # array of mandatory options for current Tcl command: required = {'name','outname'} + required = ['name', 'axis'] + + # structured help for current command, args needs to be ordered + help = { + 'main': "Create excellon with drills for aligment.", + '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.)'), + ('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.') + ]), + 'examples': [] + } + + def execute(self, args, unnamed_args): + """ + execute current TCL shell command + + :param args: array of known named arguments and options + :param unnamed_args: array of other values which were passed into command + without -somename and we do not have them in known arg_names + :return: None or exception + """ + + name = args['name'] + + # Get source object. + try: + obj = self.app.collection.get_by_name(str(name)) + except: + 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): + return "ERROR: Only Gerber, Geometry and Excellon objects can be used." + + # Axis + try: + axis = args['axis'].upper() + except KeyError: + return "ERROR: Specify -axis X or -axis Y" + + if not ('holes' in args or ('grid' in args and 'gridoffset' in args)): + return "ERROR: Specify -holes or -grid with -gridoffset " + + if 'holes' in args: + try: + holes = eval("[" + args['holes'] + "]") + except KeyError: + return "ERROR: Wrong -holes format (X1,Y1),(X2,Y2)" + + xscale, yscale = {"X": (1.0, -1.0), "Y": (-1.0, 1.0)}[axis] + + # Tools + tools = {"1": {"C": args['dia']}} + + def alligndrill_init_me(init_obj, app_obj): + """ + This function is used to initialize the new + object once it's created. + + :param init_obj: The new object. + :param app_obj: The application (FlatCAMApp) + :return: None + """ + + drills = [] + if 'holes' in args: + 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"}) + else: + if 'box' not in args: + return "ERROR: -grid can be used only for -box" + + if 'axisoffset' in args: + axisoffset = args['axisoffset'] + else: + axisoffset = 0 + + # This will align hole to given aligngridoffset and minimal offset from pcb, based on selected axis + if axis == "X": + firstpoint = args['gridoffset'] + + while (xmin - args['minoffset']) < firstpoint: + firstpoint = firstpoint - args['grid'] + + lastpoint = args['gridoffset'] + + while (xmax + args['minoffset']) > lastpoint: + lastpoint = lastpoint + args['grid'] + + localholes = (firstpoint, axisoffset), (lastpoint, axisoffset) + + else: + firstpoint = args['gridoffset'] + + while (ymin - args['minoffset']) < firstpoint: + firstpoint = firstpoint - args['grid'] + + lastpoint = args['gridoffset'] + + while (ymax + args['minoffset']) > lastpoint: + lastpoint = lastpoint + args['grid'] + + localholes = (axisoffset, firstpoint), (axisoffset, lastpoint) + + 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"}) + + 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: + return "Could not retrieve object box: %s" % args['box'] + + if box is None: + return "Object box not found: %s" % args['box'] + + try: + xmin, ymin, xmax, ymax = box.bounds() + px = 0.5 * (xmin + xmax) + py = 0.5 * (ymin + ymax) + + obj.app.new_object("excellon", + name + "_aligndrill", + alligndrill_init_me) + + except Exception, e: + return "Operation failed: %s" % str(e) + + else: + try: + dist = float(args['dist']) + except KeyError: + dist = 0.0 + except ValueError: + return "Invalid distance: %s" % args['dist'] + + try: + px = dist + py = dist + obj.app.new_object("excellon", name + "_alligndrill", alligndrill_init_me) + except Exception, e: + return "Operation failed: %s" % str(e) + + return 'Ok' diff --git a/tclCommands/TclCommandAlignDrillGrid.py b/tclCommands/TclCommandAlignDrillGrid.py new file mode 100644 index 00000000..34d70168 --- /dev/null +++ b/tclCommands/TclCommandAlignDrillGrid.py @@ -0,0 +1,105 @@ +from ObjectCollection import * +import TclCommand + + +class TclCommandAlignDrillGrid(TclCommand.TclCommandSignaled): + """ + Tcl shell command to create an Excellon object + with drills for aligment grid. + + Todo: What is an alignment grid? + """ + + # array of all command aliases, to be able use old names for + # backward compatibility (add_poly, add_polygon) + aliases = ['aligndrillgrid'] + + # 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. + # For options like -optionname value + option_types = collections.OrderedDict([ + ('dia', float), + ('gridx', float), + ('gridxoffset', float), + ('gridy', float), + ('gridyoffset', float), + ('columns', int), + ('rows', int) + ]) + + # array of mandatory options for current Tcl command: required = {'name','outname'} + required = ['outname', 'gridx', 'gridy', 'columns', 'rows'] + + # structured help for current command, args needs to be ordered + help = { + 'main': "Create excellon with drills for aligment 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.'), + ('gridy', 'Grid size in Y axis.'), + ('gridoffsety', 'Move grid from origin.'), + ('colums', 'Number of grid holes on X axis.'), + ('rows', 'Number of grid holes on Y axis.'), + ]), + 'examples': [] + } + + def execute(self, args, unnamed_args): + """ + execute current TCL shell command + + :param args: array of known named arguments and options + :param unnamed_args: array of other values which were passed into command + without -somename and we do not have them in known arg_names + :return: None or exception + """ + + if 'gridoffsetx' not in args: + gridoffsetx = 0 + else: + gridoffsetx = args['gridoffsetx'] + + if 'gridoffsety' not in args: + gridoffsety = 0 + else: + gridoffsety = args['gridoffsety'] + + # Tools + tools = {"1": {"C": args['dia']}} + + def aligndrillgrid_init_me(init_obj, app_obj): + """ + This function is used to initialize the new + object once it's created. + + :param init_obj: The new object. + :param app_obj: The application (FlatCAMApp) + :return: None + """ + + drills = [] + currenty = 0 + + for row in range(args['rows']): + currentx = 0 + + for col in range(args['columns']): + point = Point(currentx + gridoffsetx, currenty + gridoffsety) + drills.append({"point": point, "tool": "1"}) + currentx = currentx + args['gridx'] + + currenty = currenty + args['gridy'] + + init_obj.tools = tools + init_obj.drills = drills + init_obj.create_geometry() + + # Create the new object + self.new_object("excellon", args['outname'], aligndrillgrid_init_me) diff --git a/tclCommands/TclCommandCncjob.py b/tclCommands/TclCommandCncjob.py index e6d84de3..f914eb8f 100644 --- a/tclCommands/TclCommandCncjob.py +++ b/tclCommands/TclCommandCncjob.py @@ -24,14 +24,14 @@ class TclCommandCncjob(TclCommand.TclCommandSignaled): # dictionary of types from Tcl command, needs to be ordered , this is for options like -optionname value option_types = collections.OrderedDict([ - ('z_cut',float), - ('z_move',float), - ('feedrate',float), - ('tooldia',float), - ('spindlespeed',int), - ('multidepth',bool), - ('depthperpass',float), - ('outname',str) + ('z_cut', float), + ('z_move', float), + ('feedrate', float), + ('tooldia', float), + ('spindlespeed', int), + ('multidepth', bool), + ('depthperpass', float), + ('outname', str) ]) # array of mandatory options for current Tcl command: required = {'name','outname'} @@ -77,4 +77,4 @@ class TclCommandCncjob(TclCommand.TclCommandSignaled): self.raise_tcl_error('Expected FlatCAMGeometry, got %s %s.' % (name, type(obj))) del args['name'] - obj.generatecncjob(use_thread = False, **args) \ No newline at end of file + obj.generatecncjob(use_thread=False, **args) diff --git a/tclCommands/TclCommandCutout.py b/tclCommands/TclCommandCutout.py new file mode 100644 index 00000000..c3057bae --- /dev/null +++ b/tclCommands/TclCommandCutout.py @@ -0,0 +1,99 @@ +from ObjectCollection import * +import TclCommand + + +class TclCommandCutout(TclCommand.TclCommand): + """ + Tcl shell command to create a board cutout geometry. + + example: + + """ + + # List of all command aliases, to be able use old + # names for backward compatibility (add_poly, add_polygon) + aliases = ['cutout'] + + # Dictionary of types from Tcl command, needs to be ordered + arg_names = collections.OrderedDict([ + ('name', str), + ]) + + # Dictionary of types from Tcl command, needs to be ordered, + # this is for options like -optionname value + option_types = collections.OrderedDict([ + ('dia', float), + ('margin', float), + ('gapsize', float), + ('gaps', 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 board cutout.', + 'args': collections.OrderedDict([ + ('name', 'Name of the object.'), + ('dia', 'Tool diameter.'), + ('margin', 'Margin over bounds.'), + ('gapsize', 'size of gap.'), + ('gaps', 'type of gaps.'), + ]), + 'examples': [] + } + + def execute(self, args, unnamed_args): + """ + + :param args: + :param unnamed_args: + :return: + """ + + name = args['name'] + + try: + obj = self.app.collection.get_by_name(str(name)) + except: + return "Could not retrieve object: %s" % name + + def geo_init_me(geo_obj, app_obj): + margin = args['margin'] + args['dia'] / 2 + gap_size = args['dia'] + args['gapsize'] + minx, miny, maxx, maxy = obj.bounds() + minx -= margin + maxx += margin + miny -= margin + maxy += margin + midx = 0.5 * (minx + maxx) + midy = 0.5 * (miny + maxy) + hgap = 0.5 * gap_size + pts = [[midx - hgap, maxy], + [minx, maxy], + [minx, midy + hgap], + [minx, midy - hgap], + [minx, miny], + [midx - hgap, miny], + [midx + hgap, miny], + [maxx, miny], + [maxx, midy - hgap], + [maxx, midy + hgap], + [maxx, maxy], + [midx + hgap, maxy]] + cases = {"tb": [[pts[0], pts[1], pts[4], pts[5]], + [pts[6], pts[7], pts[10], pts[11]]], + "lr": [[pts[9], pts[10], pts[1], pts[2]], + [pts[3], pts[4], pts[7], pts[8]]], + "4": [[pts[0], pts[1], pts[2]], + [pts[3], pts[4], pts[5]], + [pts[6], pts[7], pts[8]], + [pts[9], pts[10], pts[11]]]} + cuts = cases[args['gaps']] + geo_obj.solid_geometry = cascaded_union([LineString(segment) for segment in cuts]) + + try: + obj.app.new_object("geometry", name + "_cutout", geo_init_me) + except Exception, e: + return "Operation failed: %s" % str(e) diff --git a/tclCommands/TclCommandDelete.py b/tclCommands/TclCommandDelete.py new file mode 100644 index 00000000..c4f3ab7b --- /dev/null +++ b/tclCommands/TclCommandDelete.py @@ -0,0 +1,54 @@ +from ObjectCollection import * +import TclCommand + + +class TclCommandDelete(TclCommand.TclCommand): + """ + Tcl shell command to delete an object. + + example: + + """ + + # List of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon) + aliases = ['delete'] + + # 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 = ['name'] + + # structured help for current command, args needs to be ordered + help = { + 'main': 'Deletes the given object.', + 'args': collections.OrderedDict([ + ('name', 'Name of the Object.'), + ]), + 'examples': [] + } + + def execute(self, args, unnamed_args): + """ + + :param args: + :param unnamed_args: + :return: + """ + + obj_name = args['name'] + + 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) diff --git a/tclCommands/TclCommandDrillcncjob.py b/tclCommands/TclCommandDrillcncjob.py index 783b6599..53e3565a 100644 --- a/tclCommands/TclCommandDrillcncjob.py +++ b/tclCommands/TclCommandDrillcncjob.py @@ -17,13 +17,13 @@ class TclCommandDrillcncjob(TclCommand.TclCommandSignaled): # dictionary of types from Tcl command, needs to be ordered , this is for options like -optionname value option_types = collections.OrderedDict([ - ('tools',str), - ('drillz',float), - ('travelz',float), - ('feedrate',float), - ('spindlespeed',int), - ('toolchange',bool), - ('outname',str) + ('tools', str), + ('drillz', float), + ('travelz', float), + ('feedrate', float), + ('spindlespeed', int), + ('toolchange', bool), + ('outname', str) ]) # array of mandatory options for current Tcl command: required = {'name','outname'} diff --git a/tclCommands/TclCommandExportSVG.py b/tclCommands/TclCommandExportSVG.py new file mode 100644 index 00000000..986e92e7 --- /dev/null +++ b/tclCommands/TclCommandExportSVG.py @@ -0,0 +1,53 @@ +from ObjectCollection import * +import TclCommand + + +class TclCommandExportSVG(TclCommand.TclCommand): + """ + Tcl shell command to export a Geometry Object as an SVG File. + + example: + export_svg my_geometry filename + """ + + # List of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon) + aliases = ['export_svg'] + + # Dictionary of types from Tcl command, needs to be ordered + arg_names = collections.OrderedDict([ + ('name', str), + ('filename', str), + ('scale_factor', float) + ]) + + # 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', 'filename'] + + # structured help for current command, args needs to be ordered + help = { + '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.') + ]), + 'examples': ['export_svg my_geometry my_file.svg'] + } + + def execute(self, args, unnamed_args): + """ + + :param args: + :param unnamed_args: + :return: + """ + + name = args['name'] + filename = args['filename'] + + self.app.export_svg(name, filename, **args) diff --git a/tclCommands/TclCommandGeoCutout.py b/tclCommands/TclCommandGeoCutout.py new file mode 100644 index 00000000..3e7ba317 --- /dev/null +++ b/tclCommands/TclCommandGeoCutout.py @@ -0,0 +1,118 @@ +from ObjectCollection import * +import TclCommand + + +class TclCommandGeoCutout(TclCommand.TclCommandSignaled): + """ + Tcl shell command to cut holding gaps from geometry. + """ + + # array of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon) + aliases = ['geocutout'] + + # 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([ + ('dia', float), + ('margin', float), + ('gapsize', float), + ('gaps', 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': "Cut holding gaps from geometry.", + 'args': collections.OrderedDict([ + ('name', 'Name of the geometry object.'), + ('dia', 'Tool diameter.'), + ('margin', 'Margin over bounds.'), + ('gapsize', 'Size of gap.'), + ('gaps', 'Type of gaps.'), + ]), + 'examples': [] + } + + def execute(self, args, unnamed_args): + """ + execute current TCL shell command + + :param args: array of known named arguments and options + :param unnamed_args: array of other values which were passed into command + without -somename and we do not have them in known arg_names + :return: None or exception + """ + + # How gaps wil be rendered: + # lr - left + right + # tb - top + bottom + # 4 - left + right +top + bottom + # 2lr - 2*left + 2*right + # 2tb - 2*top + 2*bottom + # 8 - 2*left + 2*right +2*top + 2*bottom + + name = args['name'] + obj = None + + def subtract_rectangle(obj_, x0, y0, x1, y1): + pts = [(x0, y0), (x1, y0), (x1, y1), (x0, y1)] + obj_.subtract_polygon(pts) + + try: + obj = self.app.collection.get_by_name(str(name)) + except: + self.raise_tcl_error("Could not retrieve object: %s" % name) + + # Get min and max data for each object as we just cut rectangles across X or Y + xmin, ymin, xmax, ymax = obj.bounds() + px = 0.5 * (xmin + xmax) + py = 0.5 * (ymin + ymax) + lenghtx = (xmax - xmin) + lenghty = (ymax - ymin) + gapsize = args['gapsize'] + args['dia'] / 2 + + if args['gaps'] == '8' or args['gaps'] == '2lr': + subtract_rectangle(obj, + xmin - gapsize, # botleft_x + py - gapsize + lenghty / 4, # botleft_y + xmax + gapsize, # topright_x + py + gapsize + lenghty / 4) # topright_y + subtract_rectangle(obj, + xmin - gapsize, + py - gapsize - lenghty / 4, + xmax + gapsize, + py + gapsize - lenghty / 4) + + if args['gaps'] == '8' or args['gaps'] == '2tb': + subtract_rectangle(obj, + px - gapsize + lenghtx / 4, + ymin - gapsize, + px + gapsize + lenghtx / 4, + ymax + gapsize) + subtract_rectangle(obj, + px - gapsize - lenghtx / 4, + ymin - gapsize, + px + gapsize - lenghtx / 4, + ymax + gapsize) + + if args['gaps'] == '4' or args['gaps'] == 'lr': + subtract_rectangle(obj, + xmin - gapsize, + py - gapsize, + xmax + gapsize, + py + gapsize) + + if args['gaps'] == '4' or args['gaps'] == 'tb': + subtract_rectangle(obj, + px - gapsize, + ymin - gapsize, + px + gapsize, + ymax + gapsize) diff --git a/tclCommands/TclCommandGeoUnion.py b/tclCommands/TclCommandGeoUnion.py new file mode 100644 index 00000000..c18ade7f --- /dev/null +++ b/tclCommands/TclCommandGeoUnion.py @@ -0,0 +1,59 @@ +from ObjectCollection import * +import TclCommand + + +class TclCommandGeoUnion(TclCommand.TclCommand): + """ + Tcl shell command to run a union (addition) operation on the + components of a geometry object. + + example: + + """ + + # List of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon) + aliases = ['geo_union'] + + # 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 = ['name'] + + # structured help for current command, args needs to be ordered + 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' + 'a single larger polygon.'), + 'args': collections.OrderedDict([ + ('name', 'Name of the Geometry Object.'), + ]), + 'examples': [] + } + + def execute(self, args, unnamed_args): + """ + + :param args: + :param unnamed_args: + :return: + """ + + obj_name = args['name'] + + try: + obj = self.collection.get_by_name(str(obj_name)) + except: + return "Could not retrieve object: %s" % obj_name + if obj is None: + return "Object not found: %s" % obj_name + + obj.union() diff --git a/tclCommands/TclCommandGetNames.py b/tclCommands/TclCommandGetNames.py new file mode 100644 index 00000000..aa7ea3a3 --- /dev/null +++ b/tclCommands/TclCommandGetNames.py @@ -0,0 +1,46 @@ +from ObjectCollection import * +import TclCommand + + +class TclCommandGetNames(TclCommand.TclCommand): + """ + Tcl shell command to set an object as active in the GUI. + + example: + + """ + + # List of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon) + aliases = ['get_names'] + + # 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': 'Lists the names of objects in the project.', + 'args': collections.OrderedDict([ + + ]), + 'examples': [] + } + + def execute(self, args, unnamed_args): + """ + + :param args: + :param unnamed_args: + :return: + """ + + return '\n'.join(self.app.collection.get_names()) diff --git a/tclCommands/TclCommandIsolate.py b/tclCommands/TclCommandIsolate.py index 8c51f21e..f73aafa0 100644 --- a/tclCommands/TclCommandIsolate.py +++ b/tclCommands/TclCommandIsolate.py @@ -24,11 +24,11 @@ class TclCommandIsolate(TclCommand.TclCommandSignaled): # dictionary of types from Tcl command, needs to be ordered , this is for options like -optionname value option_types = collections.OrderedDict([ - ('dia',float), - ('passes',int), - ('overlap',float), - ('combine',int), - ('outname',str) + ('dia', float), + ('passes', int), + ('overlap', float), + ('combine', int), + ('outname', str) ]) # array of mandatory options for current Tcl command: required = {'name','outname'} diff --git a/tclCommands/TclCommandJoinExcellon.py b/tclCommands/TclCommandJoinExcellon.py new file mode 100644 index 00000000..134df2aa --- /dev/null +++ b/tclCommands/TclCommandJoinExcellon.py @@ -0,0 +1,64 @@ +from ObjectCollection import * +import TclCommand + + +class TclCommandJoinExcellon(TclCommand.TclCommand): + """ + Tcl shell command to merge Excellon objects. + + example: + + """ + + # List of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon) + aliases = ['join_excellon', 'join_excellons'] + + # Dictionary of types from Tcl command, needs to be ordered + arg_names = collections.OrderedDict([ + ('outname', 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 = ['outname'] + + # structured help for current command, args needs to be ordered + help = { + 'main': "Runs a merge operation (join) on the Excellon objects.", + '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') + ]), + 'examples': [] + } + + def execute(self, args, unnamed_args): + """ + + :param args: + :param unnamed_args: + :return: + """ + + outname = args['name'] + obj_names = unnamed_args + + objs = [] + for obj_n in obj_names: + obj = self.app.collection.get_by_name(str(obj_n)) + if obj is None: + return "Object not found: %s" % obj_n + else: + objs.append(obj) + + def initialize(obj_, app): + FlatCAMExcellon.merge(objs, obj_) + + if objs is not None: + self.app.new_object("excellon", outname, initialize) diff --git a/tclCommands/TclCommandJoinGeometry.py b/tclCommands/TclCommandJoinGeometry.py new file mode 100644 index 00000000..a45d6d4a --- /dev/null +++ b/tclCommands/TclCommandJoinGeometry.py @@ -0,0 +1,64 @@ +from ObjectCollection import * +import TclCommand + + +class TclCommandJoinGeometry(TclCommand.TclCommand): + """ + Tcl shell command to merge Excellon objects. + + example: + + """ + + # List of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon) + aliases = ['join_geometries', 'join_geometry'] + + # Dictionary of types from Tcl command, needs to be ordered + arg_names = collections.OrderedDict([ + ('outname', 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 = ['outname'] + + # structured help for current command, args needs to be ordered + help = { + 'main': "Runs a merge operation (join) on the Excellon objects.", + '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') + ]), + 'examples': [] + } + + def execute(self, args, unnamed_args): + """ + + :param args: + :param unnamed_args: + :return: + """ + + outname = args['name'] + obj_names = unnamed_args + + objs = [] + for obj_n in obj_names: + obj = self.app.collection.get_by_name(str(obj_n)) + if obj is None: + return "Object not found: %s" % obj_n + else: + objs.append(obj) + + def initialize(obj_, app): + FlatCAMGeometry.merge(objs, obj_) + + if objs is not None: + self.app.new_object("geometry", outname, initialize) diff --git a/tclCommands/TclCommandMillHoles.py b/tclCommands/TclCommandMillHoles.py new file mode 100644 index 00000000..5c7edbed --- /dev/null +++ b/tclCommands/TclCommandMillHoles.py @@ -0,0 +1,77 @@ +from ObjectCollection import * +import TclCommand + + +class TclCommandMillHoles(TclCommand.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'] + + # Dictionary of types from Tcl command, needs to be ordered + arg_names = collections.OrderedDict([ + ('name', str), + ('tools', str), + ('tooldia', float), + ('outname', 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': "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.') + ]), + 'examples': ['scale my_geometry 4.2'] + } + + def execute(self, args, unnamed_args): + """ + + :param args: + :param unnamed_args: + :return: + """ + + name = args['name'] + + try: + if 'tools' in args: + # Split and put back. We are passing the whole dictionary later. + args['tools'] = [x.strip() for x in args['tools'].split(",")] + 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))) + + try: + # This runs in the background... Is blocking handled? + success, msg = obj.generate_milling(**args) + + except Exception as e: + self.raise_tcl_error("Operation failed: %s" % str(e)) + + if not success: + self.raise_tcl_error(msg) diff --git a/tclCommands/TclCommandMirror.py b/tclCommands/TclCommandMirror.py new file mode 100644 index 00000000..33abbdcb --- /dev/null +++ b/tclCommands/TclCommandMirror.py @@ -0,0 +1,108 @@ +from ObjectCollection import * +import TclCommand + + +class TclCommandMirror(TclCommand.TclCommandSignaled): + """ + Tcl shell command to mirror an object. + """ + + # array of all command aliases, to be able use + # old names for backward compatibility (add_poly, add_polygon) + aliases = ['mirror'] + + # 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([ + ('axis', str), + ('box', str), + ('dist', float) + ]) + + # array of mandatory options for current Tcl command: required = {'name','outname'} + required = ['name', 'axis'] + + # structured help for current command, args needs to be ordered + help = { + 'main': "Opens an Excellon file.", + 'args': collections.OrderedDict([ + ('name', 'Name of the object (Gerber or Excellon) to mirror.'), + ('box', 'Name of object which act as box (cutout for example.)'), + ('axis', 'Mirror axis parallel to the X or Y axis.'), + ('dist', 'Distance of the mirror axis to the X or Y axis.') + ]), + 'examples': [] + } + + def execute(self, args, unnamed_args): + """ + Execute this 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: + 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): + 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" + + # 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'] + + if box is None: + return "Object box not found: %s" % args['box'] + + try: + xmin, ymin, xmax, ymax = box.bounds() + px = 0.5 * (xmin + xmax) + py = 0.5 * (ymin + ymax) + + obj.mirror(axis, [px, py]) + obj.plot() + + except Exception, e: + return "Operation failed: %s" % str(e) + + else: + try: + dist = float(args['dist']) + except KeyError: + dist = 0.0 + except ValueError: + return "Invalid distance: %s" % args['dist'] + + try: + obj.mirror(axis, [dist, dist]) + obj.plot() + except Exception, e: + return "Operation failed: %s" % str(e) diff --git a/tclCommands/TclCommandNew.py b/tclCommands/TclCommandNew.py index db3fe576..382fc86c 100644 --- a/tclCommands/TclCommandNew.py +++ b/tclCommands/TclCommandNew.py @@ -1,5 +1,4 @@ from ObjectCollection import * -from PyQt4 import QtCore import TclCommand @@ -23,7 +22,7 @@ class TclCommandNew(TclCommand.TclCommand): # structured help for current command, args needs to be ordered help = { 'main': "Starts a new project. Clears objects from memory.", - 'args': collections.OrderedDict(), + 'args': collections.OrderedDict(), 'examples': [] } diff --git a/tclCommands/TclCommandNewGeometry.py b/tclCommands/TclCommandNewGeometry.py new file mode 100644 index 00000000..3f757637 --- /dev/null +++ b/tclCommands/TclCommandNewGeometry.py @@ -0,0 +1,49 @@ +from ObjectCollection import * +import TclCommand + + +class TclCommandNewGeometry(TclCommand.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_geometry'] + + # 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 = ['name'] + + # structured help for current command, args needs to be ordered + help = { + 'main': "Creates a new empty geometry object.", + 'args': collections.OrderedDict([ + ('name', 'New object name.'), + ]), + 'examples': [] + } + + def execute(self, args, unnamed_args): + """ + execute current TCL shell command + + :param args: array of known named arguments and options + :param unnamed_args: array of other values which were passed into command + without -somename and we do not have them in known arg_names + :return: None or exception + """ + + name = args['name'] + + self.app.new_object('geometry', str(name), lambda x, y: None) diff --git a/tclCommands/TclCommandOffset.py b/tclCommands/TclCommandOffset.py new file mode 100644 index 00000000..17ffdaac --- /dev/null +++ b/tclCommands/TclCommandOffset.py @@ -0,0 +1,53 @@ +from ObjectCollection import * +import TclCommand + + +class TclCommandOffset(TclCommand.TclCommand): + """ + Tcl shell command to change the position of the object. + + example: + offset my_geometry 1.2 -0.3 + """ + + # List of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon) + aliases = ['offset'] + + # Dictionary of types from Tcl command, needs to be ordered + arg_names = collections.OrderedDict([ + ('name', str), + ('x', float), + ('y', float) + ]) + + # 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', 'x', 'y'] + + # structured help for current command, args needs to be ordered + help = { + 'main': "Changes the position of the object.", + 'args': collections.OrderedDict([ + ('name', 'Name of the object to offset.'), + ('x', 'Offset distance in the X axis.'), + ('y', 'Offset distance in the Y axis') + ]), + 'examples': ['offset my_geometry 1.2 -0.3'] + } + + def execute(self, args, unnamed_args): + """ + + :param args: + :param unnamed_args: + :return: + """ + + name = args['name'] + x, y = args['x'], args['y'] + + self.app.collection.get_by_name(name).offset(x, y) diff --git a/tclCommands/TclCommandOpenExcellon.py b/tclCommands/TclCommandOpenExcellon.py new file mode 100644 index 00000000..4bfce02a --- /dev/null +++ b/tclCommands/TclCommandOpenExcellon.py @@ -0,0 +1,48 @@ +from ObjectCollection import * +import TclCommand + + +class TclCommandOpenExcellon(TclCommand.TclCommandSignaled): + """ + Tcl shell command to open an Excellon file. + """ + + # array of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon) + aliases = ['open_gerber'] + + # Dictionary of types from Tcl command, needs to be ordered. + # For positional arguments + arg_names = collections.OrderedDict([ + ('filename', str) + ]) + + # Dictionary of types from Tcl command, needs to be ordered. + # For options like -optionname value + option_types = collections.OrderedDict([ + ('outname', str) + ]) + + # array of mandatory options for current Tcl command: required = {'name','outname'} + required = ['filename'] + + # structured help for current command, args needs to be ordered + help = { + 'main': "Opens an Excellon file.", + 'args': collections.OrderedDict([ + ('filename', 'Path to file to open.'), + ('outname', 'Name of the resulting Excellon object.') + ]), + 'examples': [] + } + + def execute(self, args, unnamed_args): + """ + execute current TCL shell command + + :param args: array of known named arguments and options + :param unnamed_args: array of other values which were passed into command + without -somename and we do not have them in known arg_names + :return: None or exception + """ + + self.app.open_excellon(args['filename'], **args) diff --git a/tclCommands/TclCommandOpenGCode.py b/tclCommands/TclCommandOpenGCode.py new file mode 100644 index 00000000..2926380e --- /dev/null +++ b/tclCommands/TclCommandOpenGCode.py @@ -0,0 +1,49 @@ +from ObjectCollection import * +import TclCommand + + +class TclCommandOpenGCode(TclCommand.TclCommandSignaled): + """ + Tcl shell command to open a G-Code file. + """ + + # array of all command aliases, to be able use old names for + # backward compatibility (add_poly, add_polygon) + aliases = ['open_gcode'] + + # Dictionary of types from Tcl command, needs to be ordered. + # For positional arguments + arg_names = collections.OrderedDict([ + ('filename', str) + ]) + + # Dictionary of types from Tcl command, needs to be ordered. + # For options like -optionname value + option_types = collections.OrderedDict([ + ('outname', str) + ]) + + # array of mandatory options for current Tcl command: required = {'name','outname'} + required = ['filename'] + + # structured help for current command, args needs to be ordered + help = { + 'main': "Opens a G-Code file.", + 'args': collections.OrderedDict([ + ('filename', 'Path to file to open.'), + ('outname', 'Name of the resulting CNCJob object.') + ]), + 'examples': [] + } + + def execute(self, args, unnamed_args): + """ + execute current TCL shell command + + :param args: array of known named arguments and options + :param unnamed_args: array of other values which were passed into command + without -somename and we do not have them in known arg_names + :return: None or exception + """ + + self.app.open_gcode(args['filename'], **args) diff --git a/tclCommands/TclCommandOpenGerber.py b/tclCommands/TclCommandOpenGerber.py index a951d8f3..afc7dd2a 100644 --- a/tclCommands/TclCommandOpenGerber.py +++ b/tclCommands/TclCommandOpenGerber.py @@ -27,10 +27,10 @@ class TclCommandOpenGerber(TclCommand.TclCommandSignaled): # structured help for current command, args needs to be ordered help = { 'main': "Opens a Gerber file.", - 'args': collections.OrderedDict([ + 'args': collections.OrderedDict([ ('filename', 'Path to file to open.'), ('follow', 'N If 1, does not create polygons, just follows the gerber path.'), - ('outname', 'Name of the resulting Geometry object.') + ('outname', 'Name of the resulting Gerber object.') ]), 'examples': [] } diff --git a/tclCommands/TclCommandOpenProject.py b/tclCommands/TclCommandOpenProject.py new file mode 100644 index 00000000..b4d394ca --- /dev/null +++ b/tclCommands/TclCommandOpenProject.py @@ -0,0 +1,47 @@ +from ObjectCollection import * +import TclCommand + + +class TclCommandOpenProject(TclCommand.TclCommandSignaled): + """ + Tcl shell command to open a FlatCAM project. + """ + + # array of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon) + aliases = ['open_project'] + + # Dictionary of types from Tcl command, needs to be ordered. + # For positional arguments + arg_names = collections.OrderedDict([ + ('filename', 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 = ['filename'] + + # structured help for current command, args needs to be ordered + help = { + 'main': "Opens a FlatCAM project.", + 'args': collections.OrderedDict([ + ('filename', 'Path to file to open.'), + ]), + 'examples': [] + } + + def execute(self, args, unnamed_args): + """ + execute current TCL shell command + + :param args: array of known named arguments and options + :param unnamed_args: array of other values which were passed into command + without -somename and we do not have them in known arg_names + :return: None or exception + """ + + self.app.open_project(args['filename']) diff --git a/tclCommands/TclCommandOptions.py b/tclCommands/TclCommandOptions.py new file mode 100644 index 00000000..3a15549f --- /dev/null +++ b/tclCommands/TclCommandOptions.py @@ -0,0 +1,50 @@ +from ObjectCollection import * +import TclCommand + + +class TclCommandOptions(TclCommand.TclCommandSignaled): + """ + Tcl shell command to open an Excellon file. + """ + + # array of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon) + aliases = ['options'] + + # 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 = ['name'] + + # structured help for current command, args needs to be ordered + help = { + 'main': "Shows the settings for an object.", + 'args': collections.OrderedDict([ + ('name', 'Object name.'), + ]), + 'examples': [] + } + + def execute(self, args, unnamed_args): + """ + execute current TCL shell command + + :param args: array of known named arguments and options + :param unnamed_args: array of other values which were passed into command + without -somename and we do not have them in known arg_names + :return: None or exception + """ + + name = args['name'] + + ops = self.app.collection.get_by_name(str(name)).options + return '\n'.join(["%s: %s" % (o, ops[o]) for o in ops]) diff --git a/tclCommands/TclCommandPanelize.py b/tclCommands/TclCommandPanelize.py new file mode 100644 index 00000000..9f77b78c --- /dev/null +++ b/tclCommands/TclCommandPanelize.py @@ -0,0 +1,145 @@ +from ObjectCollection import * +import TclCommand + + +class TclCommandPanelize(TclCommand.TclCommand): + """ + Tcl shell command to pannelize an object. + + example: + + """ + + # List of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon) + aliases = ['panelize'] + + # 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([ + ('rows', int), + ('columns', int), + ('spacing_columns', float), + ('spacing_rows', float), + ('box', str), + ('outname', str) + ]) + + # array of mandatory options for current Tcl command: required = {'name','outname'} + required = ['name', 'rows', 'columns'] + + # structured help for current command, args needs to be ordered + help = { + 'main': 'Rectangular panelizing.', + 'args': collections.OrderedDict([ + ('name', 'Name of the object to panelize.'), + ('box', 'Name of object which acts as box (cutout for example.)' + 'for cutout boundary. Object from name is used if not specified.'), + ('spacing_columns', 'Spacing between columns.'), + ('spacing_rows', 'Spacing between rows.'), + ('columns', 'Number of columns.'), + ('rows', 'Number of rows;'), + ('outname', 'Name of the new geometry object.') + ]), + 'examples': [] + } + + def execute(self, args, unnamed_args): + """ + + :param args: + :param unnamed_args: + :return: + """ + + name = args['name'] + + # Get source object. + try: + obj = self.app.collection.get_by_name(str(name)) + except: + return "Could not retrieve object: %s" % name + + if obj is None: + return "Object not found: %s" % name + + if 'box' in args: + boxname = args['box'] + try: + box = self.app.collection.get_by_name(boxname) + except: + 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 'outname' in args: + outname = args['outname'] + else: + outname = name + '_panelized' + + if 'spacing_columns' in args: + spacing_columns = args['spacing_columns'] + else: + spacing_columns = 5 + + if 'spacing_rows' in args: + spacing_rows = args['spacing_rows'] + else: + spacing_rows = 5 + + xmin, ymin, xmax, ymax = box.bounds() + lenghtx = xmax - xmin + spacing_columns + lenghty = ymax - ymin + spacing_rows + + currenty = 0 + + def initialize_local(obj_init, app): + obj_init.solid_geometry = obj.solid_geometry + obj_init.offset([float(currentx), float(currenty)]), + + def initialize_local_excellon(obj_init, app): + FlatCAMExcellon.merge(obj, obj_init) + obj_init.offset([float(currentx), float(currenty)]), + + def initialize_geometry(obj_init, app): + FlatCAMGeometry.merge(objs, obj_init) + + def initialize_excellon(obj_init, app): + FlatCAMExcellon.merge(objs, obj_init) + + objs = [] + if obj is not None: + + for row in range(args['rows']): + currentx = 0 + for col in range(args['columns']): + local_outname = outname + ".tmp." + str(col) + "." + str(row) + if isinstance(obj, FlatCAMExcellon): + self.app.new_object("excellon", local_outname, initialize_local_excellon) + else: + self.app.new_object("geometry", local_outname, initialize_local) + + currentx += lenghtx + currenty += lenghty + + if isinstance(obj, FlatCAMExcellon): + self.app.new_object("excellon", outname, initialize_excellon) + else: + self.app.new_object("geometry", outname, initialize_geometry) + + # deselect all to avoid delete selected object when run delete from shell + self.app.collection.set_all_inactive() + for delobj in objs: + self.app.collection.set_active(delobj.options['name']) + self.app.on_delete() + + else: + return "ERROR: obj is None" + + return "Ok" diff --git a/tclCommands/TclCommandPlot.py b/tclCommands/TclCommandPlot.py new file mode 100644 index 00000000..5fb59648 --- /dev/null +++ b/tclCommands/TclCommandPlot.py @@ -0,0 +1,46 @@ +from ObjectCollection import * +import TclCommand + + +class TclCommandPlot(TclCommand.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'] + + # 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': "Updates the plot on the user interface.", + 'args': collections.OrderedDict([ + + ]), + 'examples': [] + } + + def execute(self, args, unnamed_args): + """ + + :param args: + :param unnamed_args: + :return: + """ + + self.app.plot_all() diff --git a/tclCommands/TclCommandSaveProject.py b/tclCommands/TclCommandSaveProject.py new file mode 100644 index 00000000..0957eb47 --- /dev/null +++ b/tclCommands/TclCommandSaveProject.py @@ -0,0 +1,47 @@ +from ObjectCollection import * +import TclCommand + + +class TclCommandSaveProject(TclCommand.TclCommandSignaled): + """ + Tcl shell command to save the FlatCAM project to file. + """ + + # array of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon) + aliases = ['open_project'] + + # Dictionary of types from Tcl command, needs to be ordered. + # For positional arguments + arg_names = collections.OrderedDict([ + ('filename', 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 = ['filename'] + + # structured help for current command, args needs to be ordered + help = { + 'main': "Saves the FlatCAM project to file.", + 'args': collections.OrderedDict([ + ('filename', 'Path to file.'), + ]), + 'examples': [] + } + + def execute(self, args, unnamed_args): + """ + execute current TCL shell command + + :param args: array of known named arguments and options + :param unnamed_args: array of other values which were passed into command + without -somename and we do not have them in known arg_names + :return: None or exception + """ + + self.app.save_project(args['filename']) diff --git a/tclCommands/TclCommandScale.py b/tclCommands/TclCommandScale.py new file mode 100644 index 00000000..e6c107c7 --- /dev/null +++ b/tclCommands/TclCommandScale.py @@ -0,0 +1,51 @@ +from ObjectCollection import * +import TclCommand + + +class TclCommandScale(TclCommand.TclCommand): + """ + Tcl shell command to resizes the object by a factor. + + example: + scale my_geometry 4.2 + """ + + # List of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon) + aliases = ['scale'] + + # Dictionary of types from Tcl command, needs to be ordered + arg_names = collections.OrderedDict([ + ('name', str), + ('factor', float) + ]) + + # 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', 'factor'] + + # structured help for current command, args needs to be ordered + help = { + 'main': "Resizes the object by a factor.", + 'args': collections.OrderedDict([ + ('name', 'Name of the object to resize.'), + ('factor', 'Fraction by which to scale.') + ]), + 'examples': ['scale my_geometry 4.2'] + } + + def execute(self, args, unnamed_args): + """ + + :param args: + :param unnamed_args: + :return: + """ + + name = args['name'] + factor = args['factor'] + + self.app.collection.get_by_name(name).scale(factor) diff --git a/tclCommands/TclCommandSetActive.py b/tclCommands/TclCommandSetActive.py new file mode 100644 index 00000000..097f6f02 --- /dev/null +++ b/tclCommands/TclCommandSetActive.py @@ -0,0 +1,51 @@ +from ObjectCollection import * +import TclCommand + + +class TclCommandSetActive(TclCommand.TclCommand): + """ + Tcl shell command to set an object as active in the GUI. + + example: + + """ + + # List of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon) + aliases = ['set_active'] + + # 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 = ['name'] + + # structured help for current command, args needs to be ordered + help = { + 'main': 'Sets an object as active.', + 'args': collections.OrderedDict([ + ('name', 'Name of the Object.'), + ]), + 'examples': [] + } + + def execute(self, args, unnamed_args): + """ + + :param args: + :param unnamed_args: + :return: + """ + + obj_name = args['name'] + + try: + self.app.collection.set_active(str(obj_name)) + except Exception as e: + return "Command failed: %s" % str(e) diff --git a/tclCommands/TclCommandSubtractPoly.py b/tclCommands/TclCommandSubtractPoly.py new file mode 100644 index 00000000..03a6b122 --- /dev/null +++ b/tclCommands/TclCommandSubtractPoly.py @@ -0,0 +1,62 @@ +from ObjectCollection import * +import TclCommand + + +class TclCommandSubtractPoly(TclCommand.TclCommandSignaled): + """ + Tcl shell command to create a new empty Geometry object. + """ + + # array of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon) + aliases = ['subtract_poly'] + + # 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 = ['name'] + + # structured help for current command, args needs to be ordered + help = { + 'main': "Subtract polygon from the given Geometry object.", + 'args': collections.OrderedDict([ + ('name', 'Name of the Geometry object from which to subtract.'), + ('x0 y0 x1 y1 x2 y2 ...', 'Points defining the polygon.') + ]), + 'examples': [] + } + + def execute(self, args, unnamed_args): + """ + execute current TCL shell command + + :param args: array of known named arguments and options + :param unnamed_args: array of other values which were passed into command + without -somename and we do not have them in known arg_names + :return: None or exception + """ + + obj_name = args['name'] + + 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)] + + try: + obj = self.app.collection.get_by_name(str(obj_name)) + except: + return "Could not retrieve object: %s" % obj_name + if obj is None: + return "Object not found: %s" % obj_name + + obj.subtract_polygon(points) diff --git a/tclCommands/TclCommandSubtractRectangle.py b/tclCommands/TclCommandSubtractRectangle.py new file mode 100644 index 00000000..eab6aae8 --- /dev/null +++ b/tclCommands/TclCommandSubtractRectangle.py @@ -0,0 +1,66 @@ +from ObjectCollection import * +import TclCommand + + +class TclCommandSubtractRectangle(TclCommand.TclCommandSignaled): + """ + Tcl shell command to subtract a rectange 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'] + + # 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) + ]) + + # 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 = ['name', 'x0', 'y0', 'x1', 'y1'] + + # structured help for current command, args needs to be ordered + help = { + 'main': "Subtract rectange from the given Geometry object.", + 'args': collections.OrderedDict([ + ('name', 'Name of the Geometry object from which to subtract.'), + ('x0 y0', 'Bottom left corner coordinates.'), + ('x1 y1', 'Top right corner coordinates.') + ]), + 'examples': [] + } + + def execute(self, args, unnamed_args): + """ + execute current TCL shell command + + :param args: array of known named arguments and options + :param unnamed_args: array of other values which were passed into command + without -somename and we do not have them in known arg_names + :return: None or exception + """ + + obj_name = args['name'] + x0 = args['x0'] + y0 = args['y0'] + x1 = args['x1'] + y1 = args['y1'] + + try: + obj = self.app.collection.get_by_name(str(obj_name)) + except: + return "Could not retrieve object: %s" % obj_name + if obj is None: + return "Object not found: %s" % obj_name + + obj.subtract_polygon([(x0, y0), (x1, y0), (x1, y1), (x0, y1)]) diff --git a/tclCommands/TclCommandWriteGCode.py b/tclCommands/TclCommandWriteGCode.py new file mode 100644 index 00000000..4733bf82 --- /dev/null +++ b/tclCommands/TclCommandWriteGCode.py @@ -0,0 +1,87 @@ +from ObjectCollection import * +import TclCommand + + +class TclCommandWriteGCode(TclCommand.TclCommandSignaled): + """ + Tcl shell command to save the G-code of a CNC Job object to file. + """ + + # array of all command aliases, to be able use + # old names for backward compatibility (add_poly, add_polygon) + aliases = ['write_gcode'] + + # Dictionary of types from Tcl command, needs to be ordered. + # For positional arguments + arg_names = collections.OrderedDict([ + ('name', str), + ('filename', 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 = ['name', 'filename'] + + # structured help for current command, args needs to be ordered + help = { + 'main': "Saves G-code of a CNC Job object to file.", + 'args': collections.OrderedDict([ + ('name', 'Source CNC Job object.'), + ('filename', 'Output filename'), + ]), + 'examples': [] + } + + def execute(self, args, unnamed_args): + """ + execute current TCL shell command + + :param args: array of known named arguments and options + :param unnamed_args: array of other values which were passed into command + without -somename and we do not have them in known arg_names + :return: None or exception + """ + + """ + Requires obj_name to be available. It might still be in the + making at the time this function is called, so check for + promises and send to background if there are promises. + """ + + obj_name = args['name'] + filename = args['filename'] + + preamble = '' + postamble = '' + + # TODO: 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): + # self.log.debug("write_gcode_on_object(): Disconnecting %s" % write_gcode_on_object) + # self.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) + # + # return + + # self.log.debug("write_gcode(): No promises. Continuing for %s." % obj_name) + + try: + obj = self.app.collection.get_by_name(str(obj_name)) + except: + return "Could not retrieve object: %s" % obj_name + + try: + obj.export_gcode(str(filename), str(preamble), str(postamble)) + except Exception as e: + return "Operation failed: %s" % str(e) diff --git a/tclCommands/__init__.py b/tclCommands/__init__.py index 858fc3c9..3521a5f9 100644 --- a/tclCommands/__init__.py +++ b/tclCommands/__init__.py @@ -1,19 +1,48 @@ 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 import tclCommands.TclCommandAddPolyline +import tclCommands.TclCommandAddRectangle +import tclCommands.TclCommandAlignDrill +import tclCommands.TclCommandAlignDrillGrid import tclCommands.TclCommandCncjob +import tclCommands.TclCommandCutout +import tclCommands.TclCommandDelete import tclCommands.TclCommandDrillcncjob import tclCommands.TclCommandExportGcode +import tclCommands.TclCommandExportSVG import tclCommands.TclCommandExteriors +import tclCommands.TclCommandGeoCutout +import tclCommands.TclCommandGeoUnion +import tclCommands.TclCommandGetNames import tclCommands.TclCommandImportSvg import tclCommands.TclCommandInteriors import tclCommands.TclCommandIsolate +import tclCommands.TclCommandJoinExcellon +import tclCommands.TclCommandJoinGeometry +import tclCommands.TclCommandMillHoles +import tclCommands.TclCommandMirror import tclCommands.TclCommandNew +import tclCommands.TclCommandNewGeometry +import tclCommands.TclCommandOffset +import tclCommands.TclCommandOpenExcellon +import tclCommands.TclCommandOpenGCode import tclCommands.TclCommandOpenGerber +import tclCommands.TclCommandOpenProject +import tclCommands.TclCommandOptions import tclCommands.TclCommandPaint +import tclCommands.TclCommandPanelize +import tclCommands.TclCommandPlot +import tclCommands.TclCommandSaveProject +import tclCommands.TclCommandScale +import tclCommands.TclCommandSetActive +import tclCommands.TclCommandSubtractPoly +import tclCommands.TclCommandSubtractRectangle +import tclCommands.TclCommandWriteGCode __all__ = [] @@ -25,7 +54,7 @@ for loader, name, is_pkg in pkgutil.walk_packages(__path__): def register_all_commands(app, commands): """ - Static method which register all known commands. + Static method which registers all known commands. Command should be for now in directory tclCommands and module should start with TCLCommand Class have to follow same name as module.