Bladeren bron

Bringing up to date with VisPyCanvas. Mostly migration of Tcl commands to new architecture.

Juan Pablo Caram 9 jaren geleden
bovenliggende
commit
f77403b0f2
37 gewijzigde bestanden met toevoegingen van 3266 en 1153 verwijderingen
  1. 1164 1111
      FlatCAMApp.py
  2. 3 3
      FlatCAMGUI.py
  3. 15 13
      tclCommands/TclCommand.py
  4. 65 0
      tclCommands/TclCommandAddCircle.py
  5. 66 0
      tclCommands/TclCommandAddRectangle.py
  6. 201 0
      tclCommands/TclCommandAlignDrill.py
  7. 105 0
      tclCommands/TclCommandAlignDrillGrid.py
  8. 9 9
      tclCommands/TclCommandCncjob.py
  9. 99 0
      tclCommands/TclCommandCutout.py
  10. 54 0
      tclCommands/TclCommandDelete.py
  11. 7 7
      tclCommands/TclCommandDrillcncjob.py
  12. 53 0
      tclCommands/TclCommandExportSVG.py
  13. 118 0
      tclCommands/TclCommandGeoCutout.py
  14. 59 0
      tclCommands/TclCommandGeoUnion.py
  15. 46 0
      tclCommands/TclCommandGetNames.py
  16. 5 5
      tclCommands/TclCommandIsolate.py
  17. 64 0
      tclCommands/TclCommandJoinExcellon.py
  18. 64 0
      tclCommands/TclCommandJoinGeometry.py
  19. 77 0
      tclCommands/TclCommandMillHoles.py
  20. 108 0
      tclCommands/TclCommandMirror.py
  21. 1 2
      tclCommands/TclCommandNew.py
  22. 49 0
      tclCommands/TclCommandNewGeometry.py
  23. 53 0
      tclCommands/TclCommandOffset.py
  24. 48 0
      tclCommands/TclCommandOpenExcellon.py
  25. 49 0
      tclCommands/TclCommandOpenGCode.py
  26. 2 2
      tclCommands/TclCommandOpenGerber.py
  27. 47 0
      tclCommands/TclCommandOpenProject.py
  28. 50 0
      tclCommands/TclCommandOptions.py
  29. 145 0
      tclCommands/TclCommandPanelize.py
  30. 46 0
      tclCommands/TclCommandPlot.py
  31. 47 0
      tclCommands/TclCommandSaveProject.py
  32. 51 0
      tclCommands/TclCommandScale.py
  33. 51 0
      tclCommands/TclCommandSetActive.py
  34. 62 0
      tclCommands/TclCommandSubtractPoly.py
  35. 66 0
      tclCommands/TclCommandSubtractRectangle.py
  36. 87 0
      tclCommands/TclCommandWriteGCode.py
  37. 30 1
      tclCommands/__init__.py

File diff suppressed because it is too large
+ 1164 - 1111
FlatCAMApp.py


+ 3 - 3
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')

+ 15 - 13
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 <miliseconds>' for command or 'set_sys background_timeout <miliseconds>'.")
+                self.app.raise_tcl_unknown_error("Operation timed outed! Consider increasing option "
+                                                 "'-timeout <miliseconds>' for command or "
+                                                 "'set_sys background_timeout <miliseconds>'.")
 
         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)
+            self.raise_tcl_unknown_error(unknown)

+ 65 - 0
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))
+

+ 66 - 0
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)])

+ 201 - 0
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'

+ 105 - 0
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)

+ 9 - 9
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)
+        obj.generatecncjob(use_thread=False, **args)

+ 99 - 0
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)

+ 54 - 0
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)

+ 7 - 7
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'}

+ 53 - 0
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)

+ 118 - 0
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)

+ 59 - 0
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()

+ 46 - 0
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())

+ 5 - 5
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'}

+ 64 - 0
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)

+ 64 - 0
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)

+ 77 - 0
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)

+ 108 - 0
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)

+ 1 - 2
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': []
     }
 

+ 49 - 0
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)

+ 53 - 0
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)

+ 48 - 0
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)

+ 49 - 0
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)

+ 2 - 2
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': []
     }

+ 47 - 0
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'])

+ 50 - 0
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])

+ 145 - 0
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"

+ 46 - 0
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()

+ 47 - 0
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'])

+ 51 - 0
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)

+ 51 - 0
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)

+ 62 - 0
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)

+ 66 - 0
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)])

+ 87 - 0
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)

+ 30 - 1
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.

Some files were not shown because too many files changed in this diff