Browse Source

Merged marius_stanciu/flatcam_beta/Beta 8.992 into Beta

Marius Stanciu 5 years ago
parent
commit
24502c8d02

+ 9 - 0
CHANGELOG.md

@@ -11,6 +11,15 @@ CHANGELOG for FlatCAM beta
 
 
 - handled a possible situation in App.load_defaults() method
 - handled a possible situation in App.load_defaults() method
 - fixed some issues in FlatCAMDB that may appear in certain scenarios
 - fixed some issues in FlatCAMDB that may appear in certain scenarios
+- some minor changes in the Python version detection
+- added a new Tcl Command named SetPath which will set a path to be used by the Tcl commands. Once set will serve as a fallback path in case that the files fail to be opened first time. It will be persistent, saved in preferences.
+- added the GUI for the new Open Example in the FIle -> Scripting menu.
+- I am modifying all the open ... handlers to add a parameter that will flag if the method was launched from Tcl Shell. This way if the method will fail to open the filename (which include the path) it will try to open from a set fallback path.
+- fixed issue #406, bug introduced recently (leftover changes).
+- modified the ImportSVG Tcl command name to OpenSVG (open_svg alias)
+- added a new Tcl command named OpenDXF (open_dxf alias)
+- fixed some errors in Scripting features
+- added a new Tcl command named GetPath as a convenient way to get the current default path stored in App.defaults['global_tcl_path']
 
 
 27.04.2020
 27.04.2020
 
 

+ 10 - 6
FlatCAM.py

@@ -14,6 +14,8 @@ if sys.platform == "win32":
     # cx_freeze 'module win32' workaround
     # cx_freeze 'module win32' workaround
     pass
     pass
 
 
+MIN_VERSION_MAJOR = 3
+MIN_VERSION_MINOR = 5
 
 
 def debug_trace():
 def debug_trace():
     """
     """
@@ -32,17 +34,19 @@ if __name__ == '__main__':
     # NOTE: Never talk to the GUI from threads! This is why I commented the above.
     # NOTE: Never talk to the GUI from threads! This is why I commented the above.
     freeze_support()
     freeze_support()
 
 
+    major_v = sys.version_info.major
+    minor_v = sys.version_info.minor
     # Supported Python version is >= 3.5
     # Supported Python version is >= 3.5
-    if sys.version_info.major >= 3:
-        if sys.version_info.minor >= 5:
+    if major_v >= MIN_VERSION_MAJOR:
+        if minor_v >= MIN_VERSION_MINOR:
             pass
             pass
         else:
         else:
-            print("FlatCAM BETA uses PYTHON 3. The version minimum is 3.5\n"
-                  "Your Python version is: %s" % str(sys.version_info))
+            print("FlatCAM BETA uses PYTHON 3 or later. The version minimum is %s.%s\n"
+                  "Your Python version is: %s.%s" % (MIN_VERSION_MAJOR, MIN_VERSION_MINOR, str(major_v), str(minor_v)))
             os._exit(0)
             os._exit(0)
     else:
     else:
-        print("FlatCAM BETA uses PYTHON 3. The version minimum is 3.5\n"
-              "Your Python version is: %s" % str(sys.version_info))
+        print("FlatCAM BETA uses PYTHON 3 or later. The version minimum is %s.%s\n"
+              "Your Python version is: %s.%s" % (MIN_VERSION_MAJOR, MIN_VERSION_MINOR, str(major_v), str(minor_v)))
         os._exit(0)
         os._exit(0)
 
 
     debug_trace()
     debug_trace()

+ 160 - 86
FlatCAMApp.py

@@ -455,6 +455,7 @@ class App(QtCore.QObject):
             "global_tpdf_rmargin": 20.0,
             "global_tpdf_rmargin": 20.0,
             "global_autosave": False,
             "global_autosave": False,
             "global_autosave_timeout": 300000,
             "global_autosave_timeout": 300000,
+            "global_tcl_path": '',
 
 
             # General
             # General
             "global_graphic_engine": '3D',
             "global_graphic_engine": '3D',
@@ -2029,6 +2030,7 @@ class App(QtCore.QObject):
 
 
         self.ui.menufilenewscript.triggered.connect(self.on_filenewscript)
         self.ui.menufilenewscript.triggered.connect(self.on_filenewscript)
         self.ui.menufileopenscript.triggered.connect(self.on_fileopenscript)
         self.ui.menufileopenscript.triggered.connect(self.on_fileopenscript)
+        self.ui.menufileopenscriptexample.triggered.connect(self.on_fileopenscript_example)
 
 
         self.ui.menufilerunscript.triggered.connect(self.on_filerunscript)
         self.ui.menufilerunscript.triggered.connect(self.on_filerunscript)
 
 
@@ -2342,15 +2344,16 @@ class App(QtCore.QObject):
                                   'del', 'drillcncjob', 'export_dxf', 'edxf', 'export_excellon',
                                   'del', 'drillcncjob', 'export_dxf', 'edxf', 'export_excellon',
                                   'export_exc',
                                   'export_exc',
                                   'export_gcode', 'export_gerber', 'export_svg', 'ext', 'exteriors', 'follow',
                                   'export_gcode', 'export_gerber', 'export_svg', 'ext', 'exteriors', 'follow',
-                                  'geo_union', 'geocutout', 'get_bounds', 'get_names', 'get_sys', 'help', 'import_svg',
+                                  'geo_union', 'geocutout', 'get_bounds', 'get_names', 'get_path', 'get_sys', 'help',
                                   'interiors', 'isolate', 'join_excellon',
                                   'interiors', 'isolate', 'join_excellon',
                                   'join_geometry', 'list_sys', 'milld', 'mills', 'milldrills', 'millslots',
                                   'join_geometry', 'list_sys', 'milld', 'mills', 'milldrills', 'millslots',
                                   'mirror', 'ncc',
                                   'mirror', 'ncc',
                                   'ncr', 'new', 'new_geometry', 'non_copper_regions', 'offset',
                                   'ncr', 'new', 'new_geometry', 'non_copper_regions', 'offset',
-                                  'open_excellon', 'open_gcode', 'open_gerber', 'open_project', 'options', 'origin',
+                                  'open_dxf', 'open_excellon', 'open_gcode', 'open_gerber', 'open_project', 'open_svg',
+                                  'options', 'origin',
                                   'paint', 'panelize', 'plot_all', 'plot_objects', 'plot_status', 'quit_flatcam',
                                   'paint', 'panelize', 'plot_all', 'plot_objects', 'plot_status', 'quit_flatcam',
                                   'save', 'save_project',
                                   'save', 'save_project',
-                                  'save_sys', 'scale', 'set_active', 'set_origin', 'set_sys',
+                                  'save_sys', 'scale', 'set_active', 'set_origin', 'set_path', 'set_sys',
                                   'skew', 'subtract_poly', 'subtract_rectangle',
                                   'skew', 'subtract_poly', 'subtract_rectangle',
                                   'version', 'write_gcode'
                                   'version', 'write_gcode'
                                   ]
                                   ]
@@ -4400,23 +4403,24 @@ class App(QtCore.QObject):
         if text is not None:
         if text is not None:
             new_source_file = text
             new_source_file = text
         else:
         else:
-            commands_list = "# AddCircle, AddPolygon, AddPolyline, AddRectangle, AlignDrill, " \
-                            "AlignDrillGrid, Bbox, Bounds, ClearShell, CopperClear,\n" \
-                            "# Cncjob, Cutout, Delete, Drillcncjob, ExportDXF, ExportExcellon, ExportGcode,\n" \
-                            "# ExportGerber, ExportSVG, Exteriors, Follow, GeoCutout, GeoUnion, GetNames,\n" \
-                            "# GetSys, ImportSvg, Interiors, Isolate, JoinExcellon, JoinGeometry, " \
-                            "ListSys, MillDrills,\n" \
-                            "# MillSlots, Mirror, New, NewExcellon, NewGeometry, NewGerber, Nregions, " \
-                            "Offset, OpenExcellon, OpenGCode, OpenGerber, OpenProject,\n" \
-                            "# Options, Paint, Panelize, PlotAl, PlotObjects, SaveProject, " \
-                            "SaveSys, Scale, SetActive, SetSys, SetOrigin, Skew, SubtractPoly,\n" \
-                            "# SubtractRectangle, Version, WriteGCode\n"
+            # commands_list = "# AddCircle, AddPolygon, AddPolyline, AddRectangle, AlignDrill, " \
+            #                 "AlignDrillGrid, Bbox, Bounds, ClearShell, CopperClear,\n" \
+            #                 "# Cncjob, Cutout, Delete, Drillcncjob, ExportDXF, ExportExcellon, ExportGcode,\n" \
+            #                 "# ExportGerber, ExportSVG, Exteriors, Follow, GeoCutout, GeoUnion, GetNames,\n" \
+            #                 "# GetSys, ImportSvg, Interiors, Isolate, JoinExcellon, JoinGeometry, " \
+            #                 "ListSys, MillDrills,\n" \
+            #                 "# MillSlots, Mirror, New, NewExcellon, NewGeometry, NewGerber, Nregions, " \
+            #                 "Offset, OpenExcellon, OpenGCode, OpenGerber, OpenProject,\n" \
+            #                 "# Options, Paint, Panelize, PlotAl, PlotObjects, SaveProject, " \
+            #                 "SaveSys, Scale, SetActive, SetSys, SetOrigin, Skew, SubtractPoly,\n" \
+            #                 "# SubtractRectangle, Version, WriteGCode\n"
 
 
             new_source_file = '# %s\n' % _('CREATE A NEW FLATCAM TCL SCRIPT') + \
             new_source_file = '# %s\n' % _('CREATE A NEW FLATCAM TCL SCRIPT') + \
                               '# %s:\n' % _('TCL Tutorial is here') + \
                               '# %s:\n' % _('TCL Tutorial is here') + \
                               '# https://www.tcl.tk/man/tcl8.5/tutorial/tcltutorial.html\n' + '\n\n' + \
                               '# https://www.tcl.tk/man/tcl8.5/tutorial/tcltutorial.html\n' + '\n\n' + \
                               '# %s:\n' % _("FlatCAM commands list")
                               '# %s:\n' % _("FlatCAM commands list")
-            new_source_file += commands_list + '\n'
+            new_source_file += '# %s\n\n' % _("Type >help< followed by Run Code for a list of FlatCAM Tcl Commands "
+                                              "(displayed in Tcl Shell).")
 
 
         def initialize(obj, app):
         def initialize(obj, app):
             obj.source_file = deepcopy(new_source_file)
             obj.source_file = deepcopy(new_source_file)
@@ -4736,11 +4740,11 @@ class App(QtCore.QObject):
                 self.prog_grid_lay.addWidget(QtWidgets.QLabel('<b>%s</b>' % _("Programmer")), 0, 0)
                 self.prog_grid_lay.addWidget(QtWidgets.QLabel('<b>%s</b>' % _("Programmer")), 0, 0)
                 self.prog_grid_lay.addWidget(QtWidgets.QLabel('<b>%s</b>' % _("Status")), 0, 1)
                 self.prog_grid_lay.addWidget(QtWidgets.QLabel('<b>%s</b>' % _("Status")), 0, 1)
                 self.prog_grid_lay.addWidget(QtWidgets.QLabel('<b>%s</b>' % _("E-mail")), 0, 2)
                 self.prog_grid_lay.addWidget(QtWidgets.QLabel('<b>%s</b>' % _("E-mail")), 0, 2)
+
                 self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Juan Pablo Caram"), 1, 0)
                 self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Juan Pablo Caram"), 1, 0)
                 self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Program Author"), 1, 1)
                 self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Program Author"), 1, 1)
                 self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "<>"), 1, 2)
                 self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "<>"), 1, 2)
                 self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Denis Hayrullin"), 2, 0)
                 self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Denis Hayrullin"), 2, 0)
-
                 self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Kamil Sopko"), 3, 0)
                 self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Kamil Sopko"), 3, 0)
                 self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Marius Stanciu"), 4, 0)
                 self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Marius Stanciu"), 4, 0)
                 self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % _("BETA Maintainer >= 2019")), 4, 1)
                 self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % _("BETA Maintainer >= 2019")), 4, 1)
@@ -4753,36 +4757,37 @@ class App(QtCore.QObject):
                 self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Victor Benso"), 9, 0)
                 self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Victor Benso"), 9, 0)
                 self.prog_grid_lay.addWidget(QtWidgets.QLabel(''), 10, 0)
                 self.prog_grid_lay.addWidget(QtWidgets.QLabel(''), 10, 0)
 
 
-                self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Barnaby Walters"), 11, 0)
                 self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Jørn Sandvik Nilsson"), 12, 0)
                 self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Jørn Sandvik Nilsson"), 12, 0)
                 self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Lei Zheng"), 13, 0)
                 self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Lei Zheng"), 13, 0)
-                self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Marco A Quezada"), 14, 0)
-                self.prog_grid_lay.addWidget(QtWidgets.QLabel(''), 12, 0)
+                self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Leandro Heck"), 14, 0)
+                self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Marco A Quezada"), 15, 0)
+                self.prog_grid_lay.addWidget(QtWidgets.QLabel(''), 16, 0)
 
 
-                self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Cedric Dussud"), 15, 0)
-                self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Chris Hemingway"), 16, 0)
-                self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Damian Wrobel"), 17, 0)
-                self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Daniel Sallin"), 18, 0)
-                self.prog_grid_lay.addWidget(QtWidgets.QLabel(''), 19, 0)
+                self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Cedric Dussud"), 20, 0)
+                self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Chris Hemingway"), 22, 0)
+                self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Damian Wrobel"), 24, 0)
+                self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Daniel Sallin"), 28, 0)
+                self.prog_grid_lay.addWidget(QtWidgets.QLabel(''), 32, 0)
 
 
-                self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Bruno Vunderl"), 20, 0)
-                self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Gonzalo Lopez"), 21, 0)
-                self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Jakob Staudt"), 22, 0)
-                self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Mike Smith"), 23, 0)
+                self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Bruno Vunderl"), 40, 0)
+                self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Gonzalo Lopez"), 42, 0)
+                self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Jakob Staudt"), 45, 0)
+                self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Mike Smith"), 49, 0)
 
 
-                self.prog_grid_lay.addWidget(QtWidgets.QLabel(''), 24, 0)
+                self.prog_grid_lay.addWidget(QtWidgets.QLabel(''), 52, 0)
 
 
-                self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Lubos Medovarsky"), 25, 0)
-                self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Steve Martina"), 26, 0)
-                self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Thomas Duffin"), 27, 0)
-                self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Andrey Kultyapov"), 28, 0)
+                self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Barnaby Walters"), 55, 0)
+                self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Steve Martina"), 57, 0)
+                self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Thomas Duffin"), 59, 0)
+                self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Andrey Kultyapov"), 61, 0)
 
 
-                self.prog_grid_lay.addWidget(QtWidgets.QLabel(''), 29, 0)
+                self.prog_grid_lay.addWidget(QtWidgets.QLabel(''), 63, 0)
 
 
-                self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Chris Breneman"), 30, 0)
-                self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Eric Varsanyi"), 31, 0)
+                self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Chris Breneman"), 65, 0)
+                self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Eric Varsanyi"), 67, 0)
+                self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Lubos Medovarsky"), 69, 0)
 
 
-                self.prog_grid_lay.addWidget(QtWidgets.QLabel(''), 34, 0)
+                self.prog_grid_lay.addWidget(QtWidgets.QLabel(''), 74, 0)
 
 
                 self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "@Idechix"), 100, 0)
                 self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "@Idechix"), 100, 0)
                 self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "@SM"), 101, 0)
                 self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "@SM"), 101, 0)
@@ -9371,7 +9376,7 @@ class App(QtCore.QObject):
                     pass
                     pass
 
 
         # tcl needs to be reinitialized, otherwise old shell variables etc  remains
         # tcl needs to be reinitialized, otherwise old shell variables etc  remains
-        self.init_tcl()
+        self.shell.init_tcl()
 
 
         self.delete_selection_shape()
         self.delete_selection_shape()
         self.collection.delete_all()
         self.collection.delete_all()
@@ -10365,6 +10370,45 @@ class App(QtCore.QObject):
                 if filename != '':
                 if filename != '':
                     self.worker_task.emit({'fcn': self.open_script, 'params': [filename]})
                     self.worker_task.emit({'fcn': self.open_script, 'params': [filename]})
 
 
+    def on_fileopenscript_example(self, name=None, silent=False):
+        """
+        Will open a Tcl script file into the Code Editor
+
+        :param silent: if True will not display status messages
+        :param name: name of a Tcl script file to open
+        :return:
+        """
+
+        self.report_usage("on_fileopenscript_example")
+        App.log.debug("on_fileopenscript_example()")
+
+        _filter_ = "TCL script .FlatScript (*.FlatScript);;TCL script .tcl (*.TCL);;TCL script .txt (*.TXT);;" \
+                   "All Files (*.*)"
+
+
+        # test if the app was frozen and choose the path for the configuration file
+        if getattr(sys, "frozen", False) is True:
+            example_path = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) + '\\assets\\examples'
+        else:
+            example_path = os.path.dirname(os.path.realpath(__file__)) + '\\assets\\examples'
+
+        if name:
+            filenames = [name]
+        else:
+            try:
+                filenames, _f = QtWidgets.QFileDialog.getOpenFileNames(
+                    caption=_("Open TCL script"), directory=example_path, filter=_filter_)
+            except TypeError:
+                filenames, _f = QtWidgets.QFileDialog.getOpenFileNames(caption=_("Open TCL script"), filter=_filter_)
+
+        if len(filenames) == 0:
+            if silent is False:
+                self.inform.emit('[WARNING_NOTCL] %s' % _("Cancelled."))
+        else:
+            for filename in filenames:
+                if filename != '':
+                    self.worker_task.emit({'fcn': self.open_script, 'params': [filename]})
+
     def on_filerunscript(self, name=None, silent=False):
     def on_filerunscript(self, name=None, silent=False):
         """
         """
         File menu callback for loading and running a TCL script.
         File menu callback for loading and running a TCL script.
@@ -11170,7 +11214,7 @@ class App(QtCore.QObject):
                 self.inform.emit('[WARNING_NOTCL] %s' % _('Could not export DXF file.'))
                 self.inform.emit('[WARNING_NOTCL] %s' % _('Could not export DXF file.'))
                 return
                 return
 
 
-    def import_svg(self, filename, geo_type='geometry', outname=None):
+    def import_svg(self, filename, geo_type='geometry', outname=None, plot=True):
         """
         """
         Adds a new Geometry Object to the projects and populates
         Adds a new Geometry Object to the projects and populates
         it with shapes extracted from the SVG file.
         it with shapes extracted from the SVG file.
@@ -11205,7 +11249,11 @@ class App(QtCore.QObject):
             # Object name
             # Object name
             name = outname or filename.split('/')[-1].split('\\')[-1]
             name = outname or filename.split('/')[-1].split('\\')[-1]
 
 
-            self.new_object(obj_type, name, obj_init, autoselected=False)
+            ret = self.new_object(obj_type, name, obj_init, autoselected=False, plot=plot)
+
+            if ret == 'fail':
+                self.inform.emit('[ERROR_NOTCL]%s' % _('Import failed.'))
+                return 'fail'
 
 
             # Register recent file
             # Register recent file
             self.file_opened.emit("svg", filename)
             self.file_opened.emit("svg", filename)
@@ -11213,7 +11261,7 @@ class App(QtCore.QObject):
             # GUI feedback
             # GUI feedback
             self.inform.emit('[success] %s: %s' % (_("Opened"), filename))
             self.inform.emit('[success] %s: %s' % (_("Opened"), filename))
 
 
-    def import_dxf(self, filename, geo_type='geometry', outname=None):
+    def import_dxf(self, filename, geo_type='geometry', outname=None, plot=True):
         """
         """
         Adds a new Geometry Object to the projects and populates
         Adds a new Geometry Object to the projects and populates
         it with shapes extracted from the DXF file.
         it with shapes extracted from the DXF file.
@@ -11241,27 +11289,34 @@ class App(QtCore.QObject):
             geo_obj.import_dxf(filename, obj_type, units=units)
             geo_obj.import_dxf(filename, obj_type, units=units)
             geo_obj.multigeo = False
             geo_obj.multigeo = False
 
 
-        with self.proc_container.new(_("Importing DXF")) as proc:
+        with self.proc_container.new(_("Importing DXF")):
 
 
             # Object name
             # Object name
             name = outname or filename.split('/')[-1].split('\\')[-1]
             name = outname or filename.split('/')[-1].split('\\')[-1]
 
 
-            self.new_object(obj_type, name, obj_init, autoselected=False)
+            ret = self.new_object(obj_type, name, obj_init, autoselected=False, plot=plot)
+
+            if ret == 'fail':
+                self.inform.emit('[ERROR_NOTCL]%s' % _('Import failed.'))
+                return 'fail'
+
             # Register recent file
             # Register recent file
             self.file_opened.emit("dxf", filename)
             self.file_opened.emit("dxf", filename)
 
 
             # GUI feedback
             # GUI feedback
             self.inform.emit('[success] %s: %s' % (_("Opened"), filename))
             self.inform.emit('[success] %s: %s' % (_("Opened"), filename))
 
 
-    def open_gerber(self, filename, outname=None):
+    def open_gerber(self, filename, outname=None, plot=True, from_tcl=False):
         """
         """
         Opens a Gerber file, parses it and creates a new object for
         Opens a Gerber file, parses it and creates a new object for
         it in the program. Thread-safe.
         it in the program. Thread-safe.
 
 
-        :param outname: Name of the resulting object. None causes the
-            name to be that of the file. Str.
-        :param filename: Gerber file filename
-        :type filename: str
+        :param outname:     Name of the resulting object. None causes the
+                            name to be that of the file. Str.
+        :param filename:    Gerber file filename
+        :type filename:     str
+        :param plot:        boolean, to plot or not the resulting object
+        :param from_tcl:    True if run from Tcl Shell
         :return: None
         :return: None
         """
         """
 
 
@@ -11295,15 +11350,19 @@ class App(QtCore.QObject):
 
 
         App.log.debug("open_gerber()")
         App.log.debug("open_gerber()")
 
 
-        with self.proc_container.new(_("Opening Gerber")) as proc:
+        with self.proc_container.new(_("Opening Gerber")):
             # Object name
             # Object name
             name = outname or filename.split('/')[-1].split('\\')[-1]
             name = outname or filename.split('/')[-1].split('\\')[-1]
 
 
             # # ## Object creation # ##
             # # ## Object creation # ##
-            ret = self.new_object("gerber", name, obj_init, autoselected=False)
-            if ret == 'fail':
-                self.inform.emit('[ERROR_NOTCL]%s' % _(' Open Gerber failed. Probable not a Gerber file.'))
-                return 'fail'
+            ret_val = self.new_object("gerber", name, obj_init, autoselected=False, plot=plot)
+            if ret_val == 'fail':
+                if from_tcl:
+                    filename = self.defaults['global_tcl_path'] + '/' + name
+                    ret_val = self.new_object("gerber", name, obj_init, autoselected=False, plot=plot)
+                if ret_val == 'fail':
+                    self.inform.emit('[ERROR_NOTCL]%s' % _('Open Gerber failed. Probable not a Gerber file.'))
+                    return 'fail'
 
 
             # Register recent file
             # Register recent file
             self.file_opened.emit("gerber", filename)
             self.file_opened.emit("gerber", filename)
@@ -11311,7 +11370,7 @@ class App(QtCore.QObject):
             # GUI feedback
             # GUI feedback
             self.inform.emit('[success] %s: %s' % (_("Opened"), filename))
             self.inform.emit('[success] %s: %s' % (_("Opened"), filename))
 
 
-    def open_excellon(self, filename, outname=None, plot=True):
+    def open_excellon(self, filename, outname=None, plot=True, from_tcl=False):
         """
         """
         Opens an Excellon file, parses it and creates a new object for
         Opens an Excellon file, parses it and creates a new object for
         it in the program. Thread-safe.
         it in the program. Thread-safe.
@@ -11320,6 +11379,7 @@ class App(QtCore.QObject):
         :param filename:    Excellon file filename
         :param filename:    Excellon file filename
         :type filename:     str
         :type filename:     str
         :param plot:        boolean, to plot or not the resulting object
         :param plot:        boolean, to plot or not the resulting object
+        :param from_tcl:    True if run from Tcl Shell
         :return:            None
         :return:            None
         """
         """
 
 
@@ -11355,28 +11415,29 @@ class App(QtCore.QObject):
             for tool in excellon_obj.tools:
             for tool in excellon_obj.tools:
                 if excellon_obj.tools[tool]['solid_geometry']:
                 if excellon_obj.tools[tool]['solid_geometry']:
                     return
                     return
-            app_obj.inform.emit('[ERROR_NOTCL] %s: %s' %
-                                (_("No geometry found in file"), filename))
+            app_obj.inform.emit('[ERROR_NOTCL] %s: %s' % (_("No geometry found in file"), filename))
             return "fail"
             return "fail"
 
 
         with self.proc_container.new(_("Opening Excellon.")):
         with self.proc_container.new(_("Opening Excellon.")):
-
             # Object name
             # Object name
             name = outname or filename.split('/')[-1].split('\\')[-1]
             name = outname or filename.split('/')[-1].split('\\')[-1]
             ret_val = self.new_object("excellon", name, obj_init, autoselected=False, plot=plot)
             ret_val = self.new_object("excellon", name, obj_init, autoselected=False, plot=plot)
             if ret_val == 'fail':
             if ret_val == 'fail':
-                self.inform.emit('[ERROR_NOTCL] %s' %
-                                 _('Open Excellon file failed. Probable not an Excellon file.'))
-                return
+                if from_tcl:
+                    filename = self.defaults['global_tcl_path'] + '/' + name
+                    ret_val = self.new_object("excellon", name, obj_init, autoselected=False, plot=plot)
+                if ret_val == 'fail':
+                    self.inform.emit('[ERROR_NOTCL] %s' %
+                                     _('Open Excellon file failed. Probable not an Excellon file.'))
+                    return
 
 
             # Register recent file
             # Register recent file
             self.file_opened.emit("excellon", filename)
             self.file_opened.emit("excellon", filename)
 
 
             # GUI feedback
             # GUI feedback
-            self.inform.emit('[success] %s: %s' %
-                             (_("Opened"), filename))
+            self.inform.emit('[success] %s: %s' % (_("Opened"), filename))
 
 
-    def open_gcode(self, filename, outname=None, force_parsing=None, plot=True):
+    def open_gcode(self, filename, outname=None, force_parsing=None, plot=True, from_tcl=False):
         """
         """
         Opens a G-gcode file, parses it and creates a new object for
         Opens a G-gcode file, parses it and creates a new object for
         it in the program. Thread-safe.
         it in the program. Thread-safe.
@@ -11384,7 +11445,8 @@ class App(QtCore.QObject):
         :param filename:        G-code file filename
         :param filename:        G-code file filename
         :param outname:         Name of the resulting object. None causes the name to be that of the file.
         :param outname:         Name of the resulting object. None causes the name to be that of the file.
         :param force_parsing:
         :param force_parsing:
-        :param plot:
+        :param plot:            If True plot the object on canvas
+        :param from_tcl:        True if run from Tcl Shell
         :return:                None
         :return:                None
         """
         """
         App.log.debug("open_gcode()")
         App.log.debug("open_gcode()")
@@ -11404,16 +11466,14 @@ class App(QtCore.QObject):
                 gcode = f.read()
                 gcode = f.read()
                 f.close()
                 f.close()
             except IOError:
             except IOError:
-                app_obj_.inform.emit('[ERROR_NOTCL] %s: %s' %
-                                     (_("Failed to open"), filename))
+                app_obj_.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Failed to open"), filename))
                 return "fail"
                 return "fail"
 
 
             job_obj.gcode = gcode
             job_obj.gcode = gcode
 
 
             gcode_ret = job_obj.gcode_parse(force_parsing=force_parsing)
             gcode_ret = job_obj.gcode_parse(force_parsing=force_parsing)
             if gcode_ret == "fail":
             if gcode_ret == "fail":
-                self.inform.emit('[ERROR_NOTCL] %s' %
-                                 _("This is not GCODE"))
+                self.inform.emit('[ERROR_NOTCL] %s' % _("This is not GCODE"))
                 return "fail"
                 return "fail"
 
 
             job_obj.create_geometry()
             job_obj.create_geometry()
@@ -11424,21 +11484,24 @@ class App(QtCore.QObject):
             name = outname or filename.split('/')[-1].split('\\')[-1]
             name = outname or filename.split('/')[-1].split('\\')[-1]
 
 
             # New object creation and file processing
             # New object creation and file processing
-            obj_ret = self.new_object("cncjob", name, obj_init, autoselected=False, plot=plot)
-            if obj_ret == 'fail':
-                self.inform.emit('[ERROR_NOTCL] %s' %
-                                 _("Failed to create CNCJob Object. Probable not a GCode file. "
-                                   "Try to load it from File menu.\n "
-                                   "Attempting to create a FlatCAM CNCJob Object from "
-                                   "G-Code file failed during processing"))
-                return "fail"
+            ret_val = self.new_object("cncjob", name, obj_init, autoselected=False, plot=plot)
+            if ret_val == 'fail':
+                if from_tcl:
+                    filename = self.defaults['global_tcl_path'] + '/' + name
+                    ret_val = self.new_object("cncjob", name, obj_init, autoselected=False, plot=plot)
+                if ret_val == 'fail':
+                    self.inform.emit('[ERROR_NOTCL] %s' %
+                                     _("Failed to create CNCJob Object. Probable not a GCode file. "
+                                       "Try to load it from File menu.\n "
+                                       "Attempting to create a FlatCAM CNCJob Object from "
+                                       "G-Code file failed during processing"))
+                    return "fail"
 
 
             # Register recent file
             # Register recent file
             self.file_opened.emit("cncjob", filename)
             self.file_opened.emit("cncjob", filename)
 
 
             # GUI feedback
             # GUI feedback
-            self.inform.emit('[success] %s: %s' %
-                             (_("Opened"), filename))
+            self.inform.emit('[success] %s: %s' % (_("Opened"), filename))
 
 
     def open_hpgl2(self, filename, outname=None):
     def open_hpgl2(self, filename, outname=None):
         """
         """
@@ -11586,7 +11649,7 @@ class App(QtCore.QObject):
                              (_("Failed to open config file"), filename))
                              (_("Failed to open config file"), filename))
             return
             return
 
 
-    def open_project(self, filename, run_from_arg=None, plot=True, cli=None):
+    def open_project(self, filename, run_from_arg=None, plot=True, cli=None, from_tcl=False):
         """
         """
         Loads a project from the specified file.
         Loads a project from the specified file.
 
 
@@ -11601,6 +11664,7 @@ class App(QtCore.QObject):
         :param run_from_arg:    True if run for arguments
         :param run_from_arg:    True if run for arguments
         :param plot:            If True plot all objects in the project
         :param plot:            If True plot all objects in the project
         :param cli:             Run from command line
         :param cli:             Run from command line
+        :param from_tcl:        True if run from Tcl Sehll
         :return:                None
         :return:                None
         """
         """
         App.log.debug("Opening project: " + filename)
         App.log.debug("Opening project: " + filename)
@@ -11624,16 +11688,25 @@ class App(QtCore.QObject):
         try:
         try:
             f = open(filename, 'r')
             f = open(filename, 'r')
         except IOError:
         except IOError:
-            App.log.error("Failed to open project file: %s" % filename)
-            self.inform.emit('[ERROR_NOTCL] %s: %s' %
-                             (_("Failed to open project file"), filename))
-            return
+            if from_tcl:
+                name = filename.split('/')[-1].split('\\')[-1]
+                filename = self.defaults['global_tcl_path'] + '/' + name
+                try:
+                    f = open(filename, 'r')
+                except IOError:
+                    log.error("Failed to open project file: %s" % filename)
+                    self.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Failed to open project file"), filename))
+                    return
+            else:
+                log.error("Failed to open project file: %s" % filename)
+                self.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Failed to open project file"), filename))
+                return
 
 
         try:
         try:
             d = json.load(f, object_hook=dict2obj)
             d = json.load(f, object_hook=dict2obj)
         except Exception as e:
         except Exception as e:
-            App.log.error("Failed to parse project file, trying to see if it loads as an LZMA archive: %s because %s" %
-                          (filename, str(e)))
+            log.error("Failed to parse project file, trying to see if it loads as an LZMA archive: %s because %s" %
+                      (filename, str(e)))
             f.close()
             f.close()
 
 
             # Open and parse a compressed Project file
             # Open and parse a compressed Project file
@@ -12589,7 +12662,7 @@ class App(QtCore.QObject):
         for obj in objects:
         for obj in objects:
             obj.on_generatecnc_button_click()
             obj.on_generatecnc_button_click()
 
 
-    def save_project(self, filename, quit_action=False, silent=False):
+    def save_project(self, filename, quit_action=False, silent=False, from_tcl=False):
         """
         """
         Saves the current project to the specified file.
         Saves the current project to the specified file.
 
 
@@ -12597,6 +12670,7 @@ class App(QtCore.QObject):
         :type filename:         str
         :type filename:         str
         :param quit_action:     if the project saving will be followed by an app quit; boolean
         :param quit_action:     if the project saving will be followed by an app quit; boolean
         :param silent:          if True will not display status messages
         :param silent:          if True will not display status messages
+        :param from_tcl         True is run from Tcl Shell
         :return:                None
         :return:                None
         """
         """
         self.log.debug("save_project()")
         self.log.debug("save_project()")

+ 11 - 4
flatcamGUI/FlatCAMGUI.py

@@ -183,6 +183,8 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
             QtGui.QIcon(self.app.resource_location + '/script_new16.png'), _('New Script ...'), self)
             QtGui.QIcon(self.app.resource_location + '/script_new16.png'), _('New Script ...'), self)
         self.menufileopenscript = QtWidgets.QAction(
         self.menufileopenscript = QtWidgets.QAction(
             QtGui.QIcon(self.app.resource_location + '/open_script32.png'), _('Open Script ...'), self)
             QtGui.QIcon(self.app.resource_location + '/open_script32.png'), _('Open Script ...'), self)
+        self.menufileopenscriptexample = QtWidgets.QAction(
+            QtGui.QIcon(self.app.resource_location + '/open_script32.png'), _('Open Example ...'), self)
         self.menufilerunscript = QtWidgets.QAction(
         self.menufilerunscript = QtWidgets.QAction(
             QtGui.QIcon(self.app.resource_location + '/script16.png'), '%s\tShift+S' % _('Run Script ...'), self)
             QtGui.QIcon(self.app.resource_location + '/script16.png'), '%s\tShift+S' % _('Run Script ...'), self)
         self.menufilerunscript.setToolTip(
         self.menufilerunscript.setToolTip(
@@ -192,6 +194,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
         )
         )
         self.menufile_scripting.addAction(self.menufilenewscript)
         self.menufile_scripting.addAction(self.menufilenewscript)
         self.menufile_scripting.addAction(self.menufileopenscript)
         self.menufile_scripting.addAction(self.menufileopenscript)
+        self.menufile_scripting.addAction(self.menufileopenscriptexample)
         self.menufile_scripting.addSeparator()
         self.menufile_scripting.addSeparator()
         self.menufile_scripting.addAction(self.menufilerunscript)
         self.menufile_scripting.addAction(self.menufilerunscript)
 
 
@@ -4284,20 +4287,24 @@ class FlatCAMInfoBar(QtWidgets.QWidget):
 
 
     def set_status(self, text, level="info"):
     def set_status(self, text, level="info"):
         level = str(level)
         level = str(level)
+
         self.pmap.fill()
         self.pmap.fill()
         if level == "ERROR" or level == "ERROR_NOTCL":
         if level == "ERROR" or level == "ERROR_NOTCL":
             self.pmap = QtGui.QPixmap(self.app.resource_location + '/redlight12.png')
             self.pmap = QtGui.QPixmap(self.app.resource_location + '/redlight12.png')
-        elif level == "success" or level == "SUCCESS":
+        elif level.lower() == "success":
             self.pmap = QtGui.QPixmap(self.app.resource_location + '/greenlight12.png')
             self.pmap = QtGui.QPixmap(self.app.resource_location + '/greenlight12.png')
         elif level == "WARNING" or level == "WARNING_NOTCL":
         elif level == "WARNING" or level == "WARNING_NOTCL":
             self.pmap = QtGui.QPixmap(self.app.resource_location + '/yellowlight12.png')
             self.pmap = QtGui.QPixmap(self.app.resource_location + '/yellowlight12.png')
-        elif level == "selected" or level == "SELECTED":
+        elif level.lower() == "selected":
             self.pmap = QtGui.QPixmap(self.app.resource_location + '/bluelight12.png')
             self.pmap = QtGui.QPixmap(self.app.resource_location + '/bluelight12.png')
         else:
         else:
             self.pmap = QtGui.QPixmap(self.app.resource_location + '/graylight12.png')
             self.pmap = QtGui.QPixmap(self.app.resource_location + '/graylight12.png')
 
 
-        self.set_text_(text)
-        self.icon.setPixmap(self.pmap)
+        try:
+            self.set_text_(text)
+            self.icon.setPixmap(self.pmap)
+        except Exception as e:
+            log.debug("FlatCAMInfoBar.set_status() --> %s" % str(e))
 
 
 
 
 class FlatCAMSystemTray(QtWidgets.QSystemTrayIcon):
 class FlatCAMSystemTray(QtWidgets.QSystemTrayIcon):

+ 2 - 1
flatcamObjects/FlatCAMCNCJob.py

@@ -716,7 +716,7 @@ class CNCJobObject(FlatCAMObj, CNCjob):
         else:
         else:
             return 'M02'
             return 'M02'
 
 
-    def export_gcode(self, filename=None, preamble='', postamble='', to_file=False):
+    def export_gcode(self, filename=None, preamble='', postamble='', to_file=False, from_tcl=False):
         """
         """
         This will save the GCode from the Gcode object to a file on the OS filesystem
         This will save the GCode from the Gcode object to a file on the OS filesystem
 
 
@@ -724,6 +724,7 @@ class CNCJobObject(FlatCAMObj, CNCjob):
         :param preamble:    a custom Gcode block to be added at the beginning of the Gcode file
         :param preamble:    a custom Gcode block to be added at the beginning of the Gcode file
         :param postamble:   a custom Gcode block to be added at the end of the Gcode file
         :param postamble:   a custom Gcode block to be added at the end of the Gcode file
         :param to_file:     if False then no actual file is saved but the app will know that a file was created
         :param to_file:     if False then no actual file is saved but the app will know that a file was created
+        :param from_tcl:    True if run from Tcl Shell
         :return:            None
         :return:            None
         """
         """
         # gcode = ''
         # gcode = ''

+ 4 - 4
flatcamObjects/FlatCAMScript.py

@@ -177,7 +177,7 @@ class ScriptObject(FlatCAMObj):
                 try:
                 try:
                     self.app.shell.open_processing()  # Disables input box.
                     self.app.shell.open_processing()  # Disables input box.
 
 
-                    result = self.app.tcl.eval(str(new_command))
+                    result = self.app.shell.tcl.eval(str(new_command))
                     if result != 'None':
                     if result != 'None':
                         self.app.shell.append_output(result + '\n')
                         self.app.shell.append_output(result + '\n')
 
 
@@ -189,9 +189,9 @@ class ScriptObject(FlatCAMObj):
 
 
         if old_line != '':
         if old_line != '':
             # it means that the script finished with an error
             # it means that the script finished with an error
-            result = self.app.tcl.eval("set errorInfo")
-            log.error("Exec command Exception: %s" % (result + '\n'))
-            self.app.shell.append_error('ERROR: ' + result + '\n')
+            result = self.app.shell.tcl.eval("set errorInfo")
+            log.error("Exec command Exception: %s\n" % result)
+            self.app.shell.append_error('ERROR: %s\n '% result)
 
 
         self.app.shell.close_processing()
         self.app.shell.close_processing()
 
 

+ 1 - 1
flatcamParsers/ParseGerber.py

@@ -1834,7 +1834,7 @@ class Gerber(Geometry):
             new_el = {}
             new_el = {}
             new_el['solid'] = pol
             new_el['solid'] = pol
             new_el['follow'] = pol.exterior
             new_el['follow'] = pol.exterior
-            self.apertures['0']['geometry'].append(deepcopy(new_el))
+            self.apertures['0']['geometry'].append(new_el)
 
 
     def scale(self, xfactor, yfactor=None, point=None):
     def scale(self, xfactor, yfactor=None, point=None):
         """
         """

+ 10 - 7
flatcamTools/ToolShell.py

@@ -268,6 +268,16 @@ class FCShell(TermWidget):
 
 
         self.tcl_commands_storage = {}
         self.tcl_commands_storage = {}
 
 
+        self.init_tcl()
+
+        self._edit.set_model_data(self.app.myKeywords)
+        self.setWindowIcon(self.app.ui.app_icon)
+        self.setWindowTitle("FlatCAM Shell")
+        self.resize(*self.app.defaults["global_shell_shape"])
+        self._append_to_browser('in', "FlatCAM %s - " % version)
+        self.append_output('%s\n\n' % _("Type >help< to get started"))
+
+    def init_tcl(self):
         if hasattr(self, 'tcl') and self.tcl is not None:
         if hasattr(self, 'tcl') and self.tcl is not None:
             # self.tcl = None
             # self.tcl = None
             # new object cannot be used here as it will not remember values created for next passes,
             # new object cannot be used here as it will not remember values created for next passes,
@@ -277,13 +287,6 @@ class FCShell(TermWidget):
             self.tcl = tk.Tcl()
             self.tcl = tk.Tcl()
             self.setup_shell()
             self.setup_shell()
 
 
-        self._edit.set_model_data(self.app.myKeywords)
-        self.setWindowIcon(self.app.ui.app_icon)
-        self.setWindowTitle("FlatCAM Shell")
-        self.resize(*self.app.defaults["global_shell_shape"])
-        self._append_to_browser('in', "FlatCAM %s - " % version)
-        self.append_output('%s\n\n' % _("Type >help< to get started"))
-
     def setup_shell(self):
     def setup_shell(self):
         """
         """
         Creates shell functions. Runs once at startup.
         Creates shell functions. Runs once at startup.

+ 4 - 0
make_freezed.py

@@ -58,6 +58,10 @@ if platform.architecture()[0] == '64bit':
 
 
 include_files.append(("locale", "lib/locale"))
 include_files.append(("locale", "lib/locale"))
 include_files.append(("preprocessors", "lib/preprocessors"))
 include_files.append(("preprocessors", "lib/preprocessors"))
+include_files.append(("assets", "lib/assets"))
+include_files.append(("assets/examples", "lib/assets/examples"))
+include_files.append(("assets/linux", "lib/assets/linux"))
+include_files.append(("assets/resources", "lib/assets/resources"))
 include_files.append(("share", "lib/share"))
 include_files.append(("share", "lib/share"))
 include_files.append(("flatcamGUI/VisPyData", "lib/vispy"))
 include_files.append(("flatcamGUI/VisPyData", "lib/vispy"))
 include_files.append(("config", "lib/config"))
 include_files.append(("config", "lib/config"))

+ 1 - 2
tclCommands/TclCommandBounds.py

@@ -78,7 +78,6 @@ class TclCommandBounds(TclCommand):
             xmin, ymin, xmax, ymax = obj.bounds()
             xmin, ymin, xmax, ymax = obj.bounds()
             result_list.append([xmin, ymin, xmax, ymax])
             result_list.append([xmin, ymin, xmax, ymax])
 
 
-        self.app.inform.emit('[success] %s ...' %
-                             _('TclCommand Bounds done.'))
+        self.app.inform.emit('[success] %s ...' % _('TclCommand Bounds done.'))
 
 
         return result_list
         return result_list

+ 66 - 0
tclCommands/TclCommandGetPath.py

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

+ 94 - 0
tclCommands/TclCommandOpenDXF.py

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

+ 2 - 1
tclCommands/TclCommandOpenExcellon.py

@@ -52,10 +52,11 @@ class TclCommandOpenExcellon(TclCommandSignaled):
         """
         """
 
 
         filename = args.pop('filename')
         filename = args.pop('filename')
-        # filename = filename.replace(' ', '')
+
         if ' ' in filename:
         if ' ' in filename:
             return "The absolute path to the project file contain spaces which is not allowed.\n" \
             return "The absolute path to the project file contain spaces which is not allowed.\n" \
                    "Please enclose the path within quotes."
                    "Please enclose the path within quotes."
 
 
         args['plot'] = False
         args['plot'] = False
+        args['from_tcl'] = True
         self.app.open_excellon(filename, **args)
         self.app.open_excellon(filename, **args)

+ 1 - 0
tclCommands/TclCommandOpenGCode.py

@@ -51,6 +51,7 @@ class TclCommandOpenGCode(TclCommandSignaled):
         :return: None or exception
         :return: None or exception
         """
         """
         args['plot'] = False
         args['plot'] = False
+        args['from_tcl'] = True
         filename = args["filename"]
         filename = args["filename"]
         if ' ' in filename:
         if ' ' in filename:
             return "The absolute path to the project file contain spaces which is not allowed.\n" \
             return "The absolute path to the project file contain spaces which is not allowed.\n" \

+ 7 - 32
tclCommands/TclCommandOpenGerber.py

@@ -49,25 +49,11 @@ class TclCommandOpenGerber(TclCommandSignaled):
         :return: None or exception
         :return: None or exception
         """
         """
 
 
-        # How the object should be initialized
-        def obj_init(gerber_obj, app_obj):
-
-            if gerber_obj.kind != 'gerber':
-                self.raise_tcl_error('Expected GerberObject, got %s %s.' % (outname, type(gerber_obj)))
-
-            # Opening the file happens here
-            try:
-                gerber_obj.parse_file(filename)
-            except IOError:
-                app_obj.inform.emit("[ERROR_NOTCL] Failed to open file: %s " % filename)
-                self.raise_tcl_error('Failed to open file: %s' % filename)
-
-            except ParseError as e:
-                app_obj.inform.emit("[ERROR_NOTCL] Failed to parse file: %s, %s " % (filename, str(e)))
-                self.log.error(str(e))
-                return
+        if 'follow' in args:
+            self.raise_tcl_error("The 'follow' parameter is obsolete. To create 'follow' geometry use the 'follow' "
+                                 "parameter for the Tcl Command isolate()")
 
 
-        filename = args['filename']
+        filename = args.pop('filename')
 
 
         if ' ' in filename:
         if ' ' in filename:
             return "The absolute path to the project file contain spaces which is not allowed.\n" \
             return "The absolute path to the project file contain spaces which is not allowed.\n" \
@@ -78,17 +64,6 @@ class TclCommandOpenGerber(TclCommandSignaled):
         else:
         else:
             outname = filename.split('/')[-1].split('\\')[-1]
             outname = filename.split('/')[-1].split('\\')[-1]
 
 
-        if 'follow' in args:
-            self.raise_tcl_error("The 'follow' parameter is obsolete. To create 'follow' geometry use the 'follow' "
-                                 "parameter for the Tcl Command isolate()")
-
-        with self.app.proc_container.new("Opening Gerber"):
-
-            # Object creation
-            self.app.new_object("gerber", outname, obj_init, plot=False)
-
-            # Register recent file
-            self.app.file_opened.emit("gerber", filename)
-
-            # GUI feedback
-            self.app.inform.emit("[success] Opened: " + filename)
+        args['plot'] = False
+        args['from_tcl'] = True
+        self.app.open_gerber(filename, outname, **args)

+ 1 - 1
tclCommands/TclCommandOpenProject.py

@@ -53,4 +53,4 @@ class TclCommandOpenProject(TclCommandSignaled):
             return "The absolute path to the project file contain spaces which is not allowed.\n" \
             return "The absolute path to the project file contain spaces which is not allowed.\n" \
                    "Please enclose the path within quotes."
                    "Please enclose the path within quotes."
 
 
-        self.app.open_project(filename, cli=True, plot=False)
+        self.app.open_project(filename, cli=True, plot=False, from_tcl=True)

+ 93 - 0
tclCommands/TclCommandOpenSVG.py

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

+ 1 - 1
tclCommands/TclCommandSaveProject.py

@@ -50,4 +50,4 @@ class TclCommandSaveProject(TclCommandSignaled):
         :return: None or exception
         :return: None or exception
         """
         """
 
 
-        self.app.save_project(args['filename'])
+        self.app.save_project(args['filename'], from_tcl=True)

+ 93 - 0
tclCommands/TclCommandSetPath.py

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

+ 1 - 1
tclCommands/TclCommandWriteGCode.py

@@ -103,7 +103,7 @@ class TclCommandWriteGCode(TclCommandSignaled):
                 return "fail"
                 return "fail"
 
 
         try:
         try:
-            obj.export_gcode(str(filename), str(preamble), str(postamble))
+            obj.export_gcode(str(filename), str(preamble), str(postamble), from_tcl=True)
         except Exception as e:
         except Exception as e:
             if muted is False:
             if muted is False:
                 return "Operation failed: %s" % str(e)
                 return "Operation failed: %s" % str(e)

+ 4 - 1
tclCommands/__init__.py

@@ -25,9 +25,9 @@ import tclCommands.TclCommandExteriors
 import tclCommands.TclCommandGeoCutout
 import tclCommands.TclCommandGeoCutout
 import tclCommands.TclCommandGeoUnion
 import tclCommands.TclCommandGeoUnion
 import tclCommands.TclCommandGetNames
 import tclCommands.TclCommandGetNames
+import tclCommands.TclCommandGetPath
 import tclCommands.TclCommandGetSys
 import tclCommands.TclCommandGetSys
 import tclCommands.TclCommandHelp
 import tclCommands.TclCommandHelp
-import tclCommands.TclCommandImportSvg
 import tclCommands.TclCommandInteriors
 import tclCommands.TclCommandInteriors
 import tclCommands.TclCommandIsolate
 import tclCommands.TclCommandIsolate
 import tclCommands.TclCommandFollow
 import tclCommands.TclCommandFollow
@@ -43,10 +43,12 @@ import tclCommands.TclCommandNewExcellon
 import tclCommands.TclCommandNewGeometry
 import tclCommands.TclCommandNewGeometry
 import tclCommands.TclCommandNewGerber
 import tclCommands.TclCommandNewGerber
 import tclCommands.TclCommandOffset
 import tclCommands.TclCommandOffset
+import tclCommands.TclCommandOpenDXF
 import tclCommands.TclCommandOpenExcellon
 import tclCommands.TclCommandOpenExcellon
 import tclCommands.TclCommandOpenGCode
 import tclCommands.TclCommandOpenGCode
 import tclCommands.TclCommandOpenGerber
 import tclCommands.TclCommandOpenGerber
 import tclCommands.TclCommandOpenProject
 import tclCommands.TclCommandOpenProject
+import tclCommands.TclCommandOpenSVG
 import tclCommands.TclCommandOptions
 import tclCommands.TclCommandOptions
 import tclCommands.TclCommandPaint
 import tclCommands.TclCommandPaint
 import tclCommands.TclCommandPanelize
 import tclCommands.TclCommandPanelize
@@ -58,6 +60,7 @@ import tclCommands.TclCommandSaveSys
 import tclCommands.TclCommandScale
 import tclCommands.TclCommandScale
 import tclCommands.TclCommandSetActive
 import tclCommands.TclCommandSetActive
 import tclCommands.TclCommandSetOrigin
 import tclCommands.TclCommandSetOrigin
+import tclCommands.TclCommandSetPath
 import tclCommands.TclCommandSetSys
 import tclCommands.TclCommandSetSys
 import tclCommands.TclCommandSkew
 import tclCommands.TclCommandSkew
 import tclCommands.TclCommandSubtractPoly
 import tclCommands.TclCommandSubtractPoly