소스 검색

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

Juan Pablo Caram 9 년 전
부모
커밋
f77403b0f2
37개의 변경된 파일3266개의 추가작업 그리고 1153개의 파일을 삭제
  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

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 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.

이 변경점에서 너무 많은 파일들이 변경되어 몇몇 파일들은 표시되지 않았습니다.