Explorar el Código

Merged marius_stanciu/flatcam_beta/Beta_8.994 into Beta_unstable

Marius Stanciu hace 5 años
padre
commit
0104adcbe1

+ 19 - 0
CHANGELOG.md

@@ -7,6 +7,25 @@ CHANGELOG for FlatCAM beta
 
 
 =================================================
 =================================================
 
 
+7.06.2020
+
+- refactoring in camlib.py. Made sure that some conditions are met, if some of the parameters are None then return failure. Modifications in generate_from_geometry_2 and generate_from_multitool_geometry methods
+- fixed issue with trying to access GUI from different threads by adding a new signal for printing to shell messages
+- fixed a small issue in Gerber file opener filter that did not see the *.TOP extension or *.outline extension
+- in Excellon parser added a way to "guestimate" the units if no units are detected in the header. I may need to make it optional in Preferences
+- changed the Excellon defaults for zeros suppression to TZ (assumed that most Excellon without units in header will come out of older Eagle) and the Excellon export default is now with coordinates in decimal
+- made sure that the message that exclusion areas are deleted is displayed only if there are shapes in the exclusion areas storage
+- fixed bug: on first ever usage of FlatCAM beta the last loaded language (alphabetically) is used instead of English (in current state is Russian)
+- made sure the the GUI settings are cleared on each new install
+- added a new signal that is triggered by change in visibility for the Shell Dock and will change the status of the shell label in the status bar. In this way the label will really be changed each time the shell is toggled
+- optimized the GUI in Film Tool
+- optimized GUI in Alignment Tool
+
+6.06.2020
+
+- NCC Tool - added a message to warn the user that he needs at least one tool with clearing operation
+- added a GUI element in the Preferences to control the possibility to edit with mouse cursor objects in the Project Tab. It is named: "Allow Edit"
+
 5.06.2020
 5.06.2020
 
 
 - fixed a small issue in the Panelization Tool that blocked the usage of a Geometry object as panelization reference
 - fixed a small issue in the Panelization Tool that blocked the usage of a Geometry object as panelization reference

+ 4 - 1
Common.py

@@ -565,11 +565,12 @@ class ExclusionAreas(QtCore.QObject):
         :return:    None
         :return:    None
         :rtype:
         :rtype:
         """
         """
+        if self.exclusion_areas_storage:
+            self.app.inform.emit('%s' % _("All exclusion zones deleted."))
         self.exclusion_areas_storage.clear()
         self.exclusion_areas_storage.clear()
         AppTool.delete_moving_selection_shape(self)
         AppTool.delete_moving_selection_shape(self)
         self.app.delete_selection_shape()
         self.app.delete_selection_shape()
         AppTool.delete_tool_selection_shape(self, shapes_storage=self.exclusion_shapes)
         AppTool.delete_tool_selection_shape(self, shapes_storage=self.exclusion_shapes)
-        self.app.inform.emit('%s' % _("All exclusion zones deleted."))
 
 
     def delete_sel_shapes(self, idxs):
     def delete_sel_shapes(self, idxs):
         """
         """
@@ -604,6 +605,7 @@ class ExclusionAreas(QtCore.QObject):
         if self.app.is_legacy is True:
         if self.app.is_legacy is True:
             self.exclusion_shapes.redraw()
             self.exclusion_shapes.redraw()
 
 
+        # if there are still some exclusion areas in the storage
         if self.exclusion_areas_storage:
         if self.exclusion_areas_storage:
             self.app.inform.emit('[success] %s' % _("Selected exclusion zones deleted."))
             self.app.inform.emit('[success] %s' % _("Selected exclusion zones deleted."))
         else:
         else:
@@ -618,6 +620,7 @@ class ExclusionAreas(QtCore.QObject):
                                             """)
                                             """)
             self.cnc_button.setToolTip('%s' % _("Generate the CNC Job object."))
             self.cnc_button.setToolTip('%s' % _("Generate the CNC Job object."))
 
 
+            # there are no more exclusion areas in the storage, all have been selected and deleted
             self.app.inform.emit('%s' % _("All exclusion zones deleted."))
             self.app.inform.emit('%s' % _("All exclusion zones deleted."))
 
 
     def travel_coordinates(self, start_point, end_point, tooldia):
     def travel_coordinates(self, start_point, end_point, tooldia):

+ 35 - 22
appGUI/MainGUI.py

@@ -1705,6 +1705,8 @@ class MainGUI(QtWidgets.QMainWindow):
         self.geom_update[int, int, int, int, int].connect(self.save_geometry)
         self.geom_update[int, int, int, int, int].connect(self.save_geometry)
         self.final_save.connect(self.app.final_save)
         self.final_save.connect(self.app.final_save)
 
 
+        self.shell_dock.visibilityChanged.connect(self.on_shelldock_toggled)
+
     def save_geometry(self, x, y, width, height, notebook_width):
     def save_geometry(self, x, y, width, height, notebook_width):
         """
         """
         Will save the application geometry and positions in the defaults dicitionary to be restored at the next
         Will save the application geometry and positions in the defaults dicitionary to be restored at the next
@@ -1823,7 +1825,12 @@ class MainGUI(QtWidgets.QMainWindow):
             subprocess.Popen(['xdg-open', self.app.data_path])
             subprocess.Popen(['xdg-open', self.app.data_path])
         self.app.inform.emit('[success] %s' % _("FlatCAM Preferences Folder opened."))
         self.app.inform.emit('[success] %s' % _("FlatCAM Preferences Folder opened."))
 
 
-    def on_gui_clear(self):
+    def on_gui_clear(self, signal=None, forced_clear=False):
+        """
+        Will clear the settings that are stored in QSettings.
+        """
+        log.debug("Clearing the settings in QSettings. GUI settings cleared.")
+
         theme_settings = QtCore.QSettings("Open Source", "FlatCAM")
         theme_settings = QtCore.QSettings("Open Source", "FlatCAM")
         theme_settings.setValue('theme', 'white')
         theme_settings.setValue('theme', 'white')
 
 
@@ -1831,20 +1838,22 @@ class MainGUI(QtWidgets.QMainWindow):
 
 
         resource_loc = self.app.resource_location
         resource_loc = self.app.resource_location
 
 
-        msgbox = QtWidgets.QMessageBox()
-        msgbox.setText(_("Are you sure you want to delete the GUI Settings? \n"))
-        msgbox.setWindowTitle(_("Clear GUI Settings"))
-        msgbox.setWindowIcon(QtGui.QIcon(resource_loc + '/trash32.png'))
-        msgbox.setIcon(QtWidgets.QMessageBox.Question)
+        response = None
+        if forced_clear is False:
+            msgbox = QtWidgets.QMessageBox()
+            msgbox.setText(_("Are you sure you want to delete the GUI Settings? \n"))
+            msgbox.setWindowTitle(_("Clear GUI Settings"))
+            msgbox.setWindowIcon(QtGui.QIcon(resource_loc + '/trash32.png'))
+            msgbox.setIcon(QtWidgets.QMessageBox.Question)
 
 
-        bt_yes = msgbox.addButton(_('Yes'), QtWidgets.QMessageBox.YesRole)
-        bt_no = msgbox.addButton(_('No'), QtWidgets.QMessageBox.NoRole)
+            bt_yes = msgbox.addButton(_('Yes'), QtWidgets.QMessageBox.YesRole)
+            bt_no = msgbox.addButton(_('No'), QtWidgets.QMessageBox.NoRole)
 
 
-        msgbox.setDefaultButton(bt_no)
-        msgbox.exec_()
-        response = msgbox.clickedButton()
+            msgbox.setDefaultButton(bt_no)
+            msgbox.exec_()
+            response = msgbox.clickedButton()
 
 
-        if response == bt_yes:
+        if forced_clear is True or response == bt_yes:
             qsettings = QSettings("Open Source", "FlatCAM")
             qsettings = QSettings("Open Source", "FlatCAM")
             for key in qsettings.allKeys():
             for key in qsettings.allKeys():
                 qsettings.remove(key)
                 qsettings.remove(key)
@@ -3664,18 +3673,8 @@ class MainGUI(QtWidgets.QMainWindow):
         if self.shell_dock.isVisible():
         if self.shell_dock.isVisible():
             self.shell_dock.hide()
             self.shell_dock.hide()
             self.app.plotcanvas.native.setFocus()
             self.app.plotcanvas.native.setFocus()
-            self.shell_status_label.setStyleSheet("")
-            self.app.inform[str, bool].emit(_("Shell disabled."), False)
         else:
         else:
             self.shell_dock.show()
             self.shell_dock.show()
-            self.shell_status_label.setStyleSheet("""
-                                                  QLabel
-                                                  {
-                                                      color: black;
-                                                      background-color: lightcoral;
-                                                  }
-                                                  """)
-            self.app.inform[str, bool].emit(_("Shell enabled."), False)
 
 
             # I want to take the focus and give it to the Tcl Shell when the Tcl Shell is run
             # I want to take the focus and give it to the Tcl Shell when the Tcl Shell is run
             # self.shell._edit.setFocus()
             # self.shell._edit.setFocus()
@@ -3691,6 +3690,20 @@ class MainGUI(QtWidgets.QMainWindow):
             #                       no_km)
             #                       no_km)
             # QtWidgets.qApp.sendEvent(self.shell._edit, f)
             # QtWidgets.qApp.sendEvent(self.shell._edit, f)
 
 
+    def on_shelldock_toggled(self, visibility):
+        if visibility is True:
+            self.shell_status_label.setStyleSheet("""
+                                                  QLabel
+                                                  {
+                                                      color: black;
+                                                      background-color: lightcoral;
+                                                  }
+                                                  """)
+            self.app.inform[str, bool].emit(_("Shell enabled."), False)
+        else:
+            self.shell_status_label.setStyleSheet("")
+            self.app.inform[str, bool].emit(_("Shell disabled."), False)
+
 
 
 class ShortcutsTab(QtWidgets.QWidget):
 class ShortcutsTab(QtWidgets.QWidget):
 
 

+ 2 - 1
appGUI/preferences/PreferencesUIManager.py

@@ -91,7 +91,7 @@ class PreferencesUIManager:
             "global_proj_item_dis_color": self.ui.general_defaults_form.general_gui_group.proj_color_dis_entry,
             "global_proj_item_dis_color": self.ui.general_defaults_form.general_gui_group.proj_color_dis_entry,
             "global_project_autohide": self.ui.general_defaults_form.general_gui_group.project_autohide_cb,
             "global_project_autohide": self.ui.general_defaults_form.general_gui_group.project_autohide_cb,
 
 
-            # General GUI Settings
+            # General APP Settings
             "global_gridx": self.ui.general_defaults_form.general_app_set_group.gridx_entry,
             "global_gridx": self.ui.general_defaults_form.general_app_set_group.gridx_entry,
             "global_gridy": self.ui.general_defaults_form.general_app_set_group.gridy_entry,
             "global_gridy": self.ui.general_defaults_form.general_app_set_group.gridy_entry,
             "global_snap_max": self.ui.general_defaults_form.general_app_set_group.snap_max_dist_entry,
             "global_snap_max": self.ui.general_defaults_form.general_app_set_group.snap_max_dist_entry,
@@ -107,6 +107,7 @@ class PreferencesUIManager:
             "global_pan_button": self.ui.general_defaults_form.general_app_set_group.pan_button_radio,
             "global_pan_button": self.ui.general_defaults_form.general_app_set_group.pan_button_radio,
             "global_mselect_key": self.ui.general_defaults_form.general_app_set_group.mselect_radio,
             "global_mselect_key": self.ui.general_defaults_form.general_app_set_group.mselect_radio,
             "global_delete_confirmation": self.ui.general_defaults_form.general_app_set_group.delete_conf_cb,
             "global_delete_confirmation": self.ui.general_defaults_form.general_app_set_group.delete_conf_cb,
+            "global_allow_edit_in_project_tab": self.ui.general_defaults_form.general_app_set_group.allow_edit_cb,
             "global_open_style": self.ui.general_defaults_form.general_app_set_group.open_style_cb,
             "global_open_style": self.ui.general_defaults_form.general_app_set_group.open_style_cb,
             "global_toggle_tooltips": self.ui.general_defaults_form.general_app_set_group.toggle_tooltips_cb,
             "global_toggle_tooltips": self.ui.general_defaults_form.general_app_set_group.toggle_tooltips_cb,
             "global_machinist_setting": self.ui.general_defaults_form.general_app_set_group.machinist_cb,
             "global_machinist_setting": self.ui.general_defaults_form.general_app_set_group.machinist_cb,

+ 15 - 7
appGUI/preferences/general/GeneralAPPSetGroupUI.py

@@ -384,6 +384,14 @@ class GeneralAPPSetGroupUI(OptionsGroupUI):
         )
         )
         grid0.addWidget(self.delete_conf_cb, 30, 0, 1, 2)
         grid0.addWidget(self.delete_conf_cb, 30, 0, 1, 2)
 
 
+        self.allow_edit_cb = FCCheckBox(_("Allow Edit"))
+        self.allow_edit_cb.setToolTip(
+            _("When cheched, the user can edit the objects in the Project Tab\n"
+              "by using the left mouse button click on the object name.\n"
+              "Active after restart.")
+        )
+        grid0.addWidget(self.allow_edit_cb, 31, 0, 1, 2)
+
         # Open behavior
         # Open behavior
         self.open_style_cb = FCCheckBox('%s' % _('"Open" behavior'))
         self.open_style_cb = FCCheckBox('%s' % _('"Open" behavior'))
         self.open_style_cb.setToolTip(
         self.open_style_cb.setToolTip(
@@ -393,7 +401,7 @@ class GeneralAPPSetGroupUI(OptionsGroupUI):
               "path for saving files or the path for opening files.")
               "path for saving files or the path for opening files.")
         )
         )
 
 
-        grid0.addWidget(self.open_style_cb, 31, 0, 1, 2)
+        grid0.addWidget(self.open_style_cb, 32, 0, 1, 2)
 
 
         # Enable/Disable ToolTips globally
         # Enable/Disable ToolTips globally
         self.toggle_tooltips_cb = FCCheckBox(label=_('Enable ToolTips'))
         self.toggle_tooltips_cb = FCCheckBox(label=_('Enable ToolTips'))
@@ -402,7 +410,7 @@ class GeneralAPPSetGroupUI(OptionsGroupUI):
               "when hovering with mouse over items throughout the App.")
               "when hovering with mouse over items throughout the App.")
         )
         )
 
 
-        grid0.addWidget(self.toggle_tooltips_cb, 32, 0, 1, 2)
+        grid0.addWidget(self.toggle_tooltips_cb, 33, 0, 1, 2)
 
 
         # Machinist settings that allow unsafe settings
         # Machinist settings that allow unsafe settings
         self.machinist_cb = FCCheckBox(_("Allow Machinist Unsafe Settings"))
         self.machinist_cb = FCCheckBox(_("Allow Machinist Unsafe Settings"))
@@ -414,7 +422,7 @@ class GeneralAPPSetGroupUI(OptionsGroupUI):
               "<<WARNING>>: Don't change this unless you know what you are doing !!!")
               "<<WARNING>>: Don't change this unless you know what you are doing !!!")
         )
         )
 
 
-        grid0.addWidget(self.machinist_cb, 33, 0, 1, 2)
+        grid0.addWidget(self.machinist_cb, 34, 0, 1, 2)
 
 
         # Bookmarks Limit in the Help Menu
         # Bookmarks Limit in the Help Menu
         self.bm_limit_spinner = FCSpinner()
         self.bm_limit_spinner = FCSpinner()
@@ -426,8 +434,8 @@ class GeneralAPPSetGroupUI(OptionsGroupUI):
               "but the menu will hold only so much.")
               "but the menu will hold only so much.")
         )
         )
 
 
-        grid0.addWidget(self.bm_limit_label, 34, 0)
-        grid0.addWidget(self.bm_limit_spinner, 34, 1)
+        grid0.addWidget(self.bm_limit_label, 35, 0)
+        grid0.addWidget(self.bm_limit_spinner, 35, 1)
 
 
         # Activity monitor icon
         # Activity monitor icon
         self.activity_label = QtWidgets.QLabel('%s:' % _("Activity Icon"))
         self.activity_label = QtWidgets.QLabel('%s:' % _("Activity Icon"))
@@ -437,8 +445,8 @@ class GeneralAPPSetGroupUI(OptionsGroupUI):
         self.activity_combo = FCComboBox()
         self.activity_combo = FCComboBox()
         self.activity_combo.addItems(['Ball black', 'Ball green', 'Arrow green', 'Eclipse green'])
         self.activity_combo.addItems(['Ball black', 'Ball green', 'Arrow green', 'Eclipse green'])
 
 
-        grid0.addWidget(self.activity_label, 35, 0)
-        grid0.addWidget(self.activity_combo, 35, 1)
+        grid0.addWidget(self.activity_label, 36, 0)
+        grid0.addWidget(self.activity_combo, 36, 1)
 
 
         self.layout.addStretch()
         self.layout.addStretch()
 
 

+ 2 - 2
appObjects/AppObject.py

@@ -137,9 +137,9 @@ class AppObject(QtCore.QObject):
             return "fail"
             return "fail"
 
 
         t2 = time.time()
         t2 = time.time()
-        msg = "%f seconds executing initialize()." % (t2 - t1)
+        msg = "New object with name: %s. %f seconds executing initialize()." % (name, (t2 - t1))
         log.debug(msg)
         log.debug(msg)
-        self.app.shell_message(msg)
+        self.app.inform_shell.emit(msg)
 
 
         if return_value == 'fail':
         if return_value == 'fail':
             log.debug("Object (%s) parsing and/or geometry creation failed." % kind)
             log.debug("Object (%s) parsing and/or geometry creation failed." % kind)

+ 7 - 2
appObjects/ObjectCollection.py

@@ -50,11 +50,10 @@ class KeySensitiveListView(QtWidgets.QTreeView):
     def __init__(self, app, parent=None):
     def __init__(self, app, parent=None):
         super(KeySensitiveListView, self).__init__(parent)
         super(KeySensitiveListView, self).__init__(parent)
         self.setHeaderHidden(True)
         self.setHeaderHidden(True)
-        # self.setEditTriggers(QtWidgets.QTreeView.SelectedClicked)     # allow Edit on Tree
-        self.setEditTriggers(QtWidgets.QTreeView.NoEditTriggers)
 
 
         # self.setRootIsDecorated(False)
         # self.setRootIsDecorated(False)
         # self.setExpandsOnDoubleClick(False)
         # self.setExpandsOnDoubleClick(False)
+        self.setEditTriggers(QtWidgets.QTreeView.NoEditTriggers)    # No edit in the Project Tab Tree
 
 
         # Enable dragging and dropping onto the appGUI
         # Enable dragging and dropping onto the appGUI
         self.setAcceptDrops(True)
         self.setAcceptDrops(True)
@@ -309,6 +308,12 @@ class ObjectCollection(QtCore.QAbstractItemModel):
 
 
         self.view.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
         self.view.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
         self.view.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
         self.view.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
+
+        if self.app.defaults["global_allow_edit_in_project_tab"] is True:
+            self.view.setEditTriggers(QtWidgets.QTreeView.SelectedClicked)  # allow Edit on Tree
+        else:
+            self.view.setEditTriggers(QtWidgets.QTreeView.NoEditTriggers)
+
         # self.view.setDragDropMode(QtWidgets.QAbstractItemView.InternalMove)
         # self.view.setDragDropMode(QtWidgets.QAbstractItemView.InternalMove)
         # self.view.setDragEnabled(True)
         # self.view.setDragEnabled(True)
         # self.view.setAcceptDrops(True)
         # self.view.setAcceptDrops(True)

+ 32 - 5
appParsers/ParseExcellon.py

@@ -115,12 +115,12 @@ class Excellon(Geometry):
 
 
         # ## IN|MM -> Units are inherited from Geometry
         # ## IN|MM -> Units are inherited from Geometry
         self.units = self.app.defaults['units']
         self.units = self.app.defaults['units']
+        self.units_found = self.app.defaults['units']
 
 
         # Trailing "T" or leading "L" (default)
         # Trailing "T" or leading "L" (default)
         # self.zeros = "T"
         # self.zeros = "T"
         self.zeros = zeros or self.defaults["zeros"]
         self.zeros = zeros or self.defaults["zeros"]
         self.zeros_found = deepcopy(self.zeros)
         self.zeros_found = deepcopy(self.zeros)
-        self.units_found = deepcopy(self.units)
 
 
         # this will serve as a default if the Excellon file has no info regarding of tool diameters (this info may be
         # this will serve as a default if the Excellon file has no info regarding of tool diameters (this info may be
         # in another file like for PCB WIzard ECAD software
         # in another file like for PCB WIzard ECAD software
@@ -134,6 +134,8 @@ class Excellon(Geometry):
         self.excellon_format_upper_mm = excellon_format_upper_mm or self.defaults["excellon_format_upper_mm"]
         self.excellon_format_upper_mm = excellon_format_upper_mm or self.defaults["excellon_format_upper_mm"]
         self.excellon_format_lower_mm = excellon_format_lower_mm or self.defaults["excellon_format_lower_mm"]
         self.excellon_format_lower_mm = excellon_format_lower_mm or self.defaults["excellon_format_lower_mm"]
         self.excellon_units = excellon_units or self.defaults["excellon_units"]
         self.excellon_units = excellon_units or self.defaults["excellon_units"]
+        self.excellon_units_found = None
+
         # detected Excellon format is stored here:
         # detected Excellon format is stored here:
         self.excellon_format = None
         self.excellon_format = None
 
 
@@ -362,7 +364,7 @@ class Excellon(Geometry):
 
 
                         self.excellon_format_upper_in = match.group(1)
                         self.excellon_format_upper_in = match.group(1)
                         self.excellon_format_lower_in = match.group(2)
                         self.excellon_format_lower_in = match.group(2)
-                        log.warning("Altium Excellon format preset found in comments: %s:%s" %
+                        log.warning("Excellon format preset found in comments: %s:%s" %
                                     (match.group(1), match.group(2)))
                                     (match.group(1), match.group(2)))
                         continue
                         continue
                     else:
                     else:
@@ -384,6 +386,25 @@ class Excellon(Geometry):
                         if allegro_warning is True:
                         if allegro_warning is True:
                             name_tool = 0
                             name_tool = 0
                         log.warning("Found end of the header: %s" % eline)
                         log.warning("Found end of the header: %s" % eline)
+
+                        '''
+                        In case that the units were not found in the header, we have two choices:
+                        - one is to use the default value in the App Preferences
+                        - the other is to make an evaluation based on a threshold
+                        we process here the self.tools list and make a list with tools with diameter less or equal 
+                        with 0.1 and a list with tools with value greater than 0.1, 0.1 being the threshold value. 
+                        Most tools in Excellon are greater than 0.1mm therefore if most of the tools are under this 
+                        value it is safe to assume that the units are in INCH
+                        '''
+                        greater_tools = set()
+                        lower_tools = set()
+                        if not self.excellon_units_found and self.tools:
+                            for tool in self.tools:
+                                tool_dia = float(self.tools[tool]['C'])
+                                lower_tools.add(tool_dia) if tool_dia <= 0.1 else greater_tools.add(tool_dia)
+
+                            assumed_units = "IN" if len(lower_tools) > len(greater_tools) else "MM"
+                            self.units = assumed_units
                         continue
                         continue
 
 
                 # ## Alternative units format M71/M72
                 # ## Alternative units format M71/M72
@@ -802,6 +823,8 @@ class Excellon(Geometry):
                     match = self.units_re.match(eline)
                     match = self.units_re.match(eline)
                     if match:
                     if match:
                         self.units = {"METRIC": "MM", "INCH": "IN"}[match.group(1)]
                         self.units = {"METRIC": "MM", "INCH": "IN"}[match.group(1)]
+                        self.excellon_units_found = self.units
+
                         self.zeros = match.group(2)  # "T" or "L". Might be empty
                         self.zeros = match.group(2)  # "T" or "L". Might be empty
                         self.excellon_format = match.group(3)
                         self.excellon_format = match.group(3)
                         if self.excellon_format:
                         if self.excellon_format:
@@ -815,9 +838,9 @@ class Excellon(Geometry):
                                 self.excellon_format_lower_in = lower
                                 self.excellon_format_lower_in = lower
 
 
                         # Modified for issue #80
                         # Modified for issue #80
-                        log.warning("UNITS found inline before conversion: %s" % self.units)
+                        log.warning("UNITS found inline - Value before conversion: %s" % self.units)
                         self.convert_units(self.units)
                         self.convert_units(self.units)
-                        log.warning("UNITS found inline after conversion: %s" % self.units)
+                        log.warning("UNITS found inline - Value after conversion: %s" % self.units)
                         if self.units == 'MM':
                         if self.units == 'MM':
                             log.warning("Excellon format preset is: %s:%s" %
                             log.warning("Excellon format preset is: %s:%s" %
                                         (str(self.excellon_format_upper_mm), str(self.excellon_format_lower_mm)))
                                         (str(self.excellon_format_upper_mm), str(self.excellon_format_lower_mm)))
@@ -836,6 +859,7 @@ class Excellon(Geometry):
                         log.warning("Type of UNITS found inline, in header, after conversion: %s" % self.units)
                         log.warning("Type of UNITS found inline, in header, after conversion: %s" % self.units)
                         log.warning("Excellon format preset is: %s:%s" %
                         log.warning("Excellon format preset is: %s:%s" %
                                     (str(self.excellon_format_upper_in), str(self.excellon_format_lower_in)))
                                     (str(self.excellon_format_upper_in), str(self.excellon_format_lower_in)))
+                        self.excellon_units_found = "IN"
                         continue
                         continue
                     elif "METRIC" in eline:
                     elif "METRIC" in eline:
                         line_units = "MM"
                         line_units = "MM"
@@ -845,6 +869,7 @@ class Excellon(Geometry):
                         log.warning("Type of UNITS found inline, in header, after conversion: %s" % self.units)
                         log.warning("Type of UNITS found inline, in header, after conversion: %s" % self.units)
                         log.warning("Excellon format preset is: %s:%s" %
                         log.warning("Excellon format preset is: %s:%s" %
                                     (str(self.excellon_format_upper_mm), str(self.excellon_format_lower_mm)))
                                     (str(self.excellon_format_upper_mm), str(self.excellon_format_lower_mm)))
+                        self.excellon_units_found = "MM"
                         continue
                         continue
 
 
                     # Search for zeros type again because it might be alone on the line
                     # Search for zeros type again because it might be alone on the line
@@ -857,7 +882,9 @@ class Excellon(Geometry):
                 # ## Units and number format outside header# ##
                 # ## Units and number format outside header# ##
                 match = self.units_re.match(eline)
                 match = self.units_re.match(eline)
                 if match:
                 if match:
-                    self.units = self.units = {"METRIC": "MM", "INCH": "IN"}[match.group(1)]
+                    self.units = {"METRIC": "MM", "INCH": "IN"}[match.group(1)]
+                    self.excellon_units_found = self.units
+
                     self.zeros = match.group(2)  # "T" or "L". Might be empty
                     self.zeros = match.group(2)  # "T" or "L". Might be empty
                     self.excellon_format = match.group(3)
                     self.excellon_format = match.group(3)
                     if self.excellon_format:
                     if self.excellon_format:

+ 4 - 7
appTools/ToolAlignObjects.py

@@ -72,7 +72,7 @@ class AlignObjects(AppTool):
         self.type_obj_radio = RadioSet([
         self.type_obj_radio = RadioSet([
             {"label": _("Gerber"), "value": "grb"},
             {"label": _("Gerber"), "value": "grb"},
             {"label": _("Excellon"), "value": "exc"},
             {"label": _("Excellon"), "value": "exc"},
-        ], orientation='vertical', stretch=False)
+        ])
 
 
         grid0.addWidget(self.type_obj_radio, 3, 0, 1, 2)
         grid0.addWidget(self.type_obj_radio, 3, 0, 1, 2)
 
 
@@ -95,7 +95,7 @@ class AlignObjects(AppTool):
 
 
         grid0.addWidget(QtWidgets.QLabel(''), 6, 0, 1, 2)
         grid0.addWidget(QtWidgets.QLabel(''), 6, 0, 1, 2)
 
 
-        self.aligned_label = QtWidgets.QLabel('<b>%s:</b>' % _("TARGET object"))
+        self.aligned_label = QtWidgets.QLabel('<b>%s:</b>' % _("DESTINATION object"))
         self.aligned_label.setToolTip(
         self.aligned_label.setToolTip(
             _("Specify the type of object to be aligned to.\n"
             _("Specify the type of object to be aligned to.\n"
               "It can be of type: Gerber or Excellon.\n"
               "It can be of type: Gerber or Excellon.\n"
@@ -108,7 +108,7 @@ class AlignObjects(AppTool):
         self.type_aligner_obj_radio = RadioSet([
         self.type_aligner_obj_radio = RadioSet([
             {"label": _("Gerber"), "value": "grb"},
             {"label": _("Gerber"), "value": "grb"},
             {"label": _("Excellon"), "value": "exc"},
             {"label": _("Excellon"), "value": "exc"},
-        ], orientation='vertical', stretch=False)
+        ])
 
 
         grid0.addWidget(self.type_aligner_obj_radio, 8, 0, 1, 2)
         grid0.addWidget(self.type_aligner_obj_radio, 8, 0, 1, 2)
 
 
@@ -142,10 +142,7 @@ class AlignObjects(AppTool):
             [
             [
                 {'label': _('Single Point'), 'value': 'sp'},
                 {'label': _('Single Point'), 'value': 'sp'},
                 {'label': _('Dual Point'), 'value': 'dp'}
                 {'label': _('Dual Point'), 'value': 'dp'}
-            ],
-            orientation='vertical',
-            stretch=False
-        )
+            ])
 
 
         grid0.addWidget(self.a_type_lbl, 12, 0, 1, 2)
         grid0.addWidget(self.a_type_lbl, 12, 0, 1, 2)
         grid0.addWidget(self.a_type_radio, 13, 0, 1, 2)
         grid0.addWidget(self.a_type_radio, 13, 0, 1, 2)

+ 23 - 18
appTools/ToolFilm.py

@@ -9,7 +9,7 @@ from PyQt5 import QtCore, QtWidgets
 
 
 from appTool import AppTool
 from appTool import AppTool
 from appGUI.GUIElements import RadioSet, FCDoubleSpinner, FCCheckBox, \
 from appGUI.GUIElements import RadioSet, FCDoubleSpinner, FCCheckBox, \
-    OptionalHideInputSection, OptionalInputSection, FCComboBox, FCFileSaveDialog
+    OptionalHideInputSection, FCComboBox, FCFileSaveDialog
 
 
 from copy import deepcopy
 from copy import deepcopy
 import logging
 import logging
@@ -68,7 +68,7 @@ class Film(AppTool):
         self.tf_type_obj_combo = RadioSet([{'label': _('Gerber'), 'value': 'grb'},
         self.tf_type_obj_combo = RadioSet([{'label': _('Gerber'), 'value': 'grb'},
                                            {'label': _('Geometry'), 'value': 'geo'}])
                                            {'label': _('Geometry'), 'value': 'geo'}])
 
 
-        self.tf_type_obj_combo_label = QtWidgets.QLabel('%s:' % _("Object Type"))
+        self.tf_type_obj_combo_label = QtWidgets.QLabel('<b>%s</b>:' % _("Object"))
         self.tf_type_obj_combo_label.setToolTip(
         self.tf_type_obj_combo_label.setToolTip(
             _("Specify the type of object for which to create the film.\n"
             _("Specify the type of object for which to create the film.\n"
               "The object can be of type: Gerber or Geometry.\n"
               "The object can be of type: Gerber or Geometry.\n"
@@ -107,13 +107,6 @@ class Film(AppTool):
         self.tf_box_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
         self.tf_box_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
         self.tf_box_combo.is_last = True
         self.tf_box_combo.is_last = True
 
 
-        # self.tf_box_combo_label = QtWidgets.QLabel('%s:' % _("Box Object"))
-        # self.tf_box_combo_label.setToolTip(
-        #     _("The actual object that is used as container for the\n "
-        #       "selected object for which we create the film.\n"
-        #       "Usually it is the PCB outline but it can be also the\n"
-        #       "same object for which the film is created.")
-        # )
         grid0.addWidget(self.tf_box_combo, 3, 0, 1, 2)
         grid0.addWidget(self.tf_box_combo, 3, 0, 1, 2)
 
 
         separator_line = QtWidgets.QFrame()
         separator_line = QtWidgets.QFrame()
@@ -160,8 +153,13 @@ class Film(AppTool):
         grid0.addWidget(self.film_scaley_label, 8, 0)
         grid0.addWidget(self.film_scaley_label, 8, 0)
         grid0.addWidget(self.film_scaley_entry, 8, 1)
         grid0.addWidget(self.film_scaley_entry, 8, 1)
 
 
-        self.ois_scale = OptionalInputSection(self.film_scale_cb, [self.film_scalex_label, self.film_scalex_entry,
-                                                                   self.film_scaley_label,  self.film_scaley_entry])
+        self.ois_scale = OptionalHideInputSection(self.film_scale_cb,
+                                                  [
+                                                      self.film_scalex_label,
+                                                      self.film_scalex_entry,
+                                                      self.film_scaley_label,
+                                                      self.film_scaley_entry
+                                                  ])
 
 
         separator_line = QtWidgets.QFrame()
         separator_line = QtWidgets.QFrame()
         separator_line.setFrameShape(QtWidgets.QFrame.HLine)
         separator_line.setFrameShape(QtWidgets.QFrame.HLine)
@@ -214,9 +212,15 @@ class Film(AppTool):
         grid0.addWidget(self.film_skew_ref_label, 13, 0)
         grid0.addWidget(self.film_skew_ref_label, 13, 0)
         grid0.addWidget(self.film_skew_reference, 13, 1)
         grid0.addWidget(self.film_skew_reference, 13, 1)
 
 
-        self.ois_skew = OptionalInputSection(self.film_skew_cb, [self.film_skewx_label, self.film_skewx_entry,
-                                                                 self.film_skewy_label,  self.film_skewy_entry,
-                                                                 self.film_skew_reference])
+        self.ois_skew = OptionalHideInputSection(self.film_skew_cb,
+                                                 [
+                                                     self.film_skewx_label,
+                                                     self.film_skewx_entry,
+                                                     self.film_skewy_label,
+                                                     self.film_skewy_entry,
+                                                     self.film_skew_ref_label,
+                                                     self.film_skew_reference
+                                                 ])
 
 
         separator_line1 = QtWidgets.QFrame()
         separator_line1 = QtWidgets.QFrame()
         separator_line1.setFrameShape(QtWidgets.QFrame.HLine)
         separator_line1.setFrameShape(QtWidgets.QFrame.HLine)
@@ -245,8 +249,11 @@ class Film(AppTool):
         grid0.addWidget(self.film_mirror_axis_label, 16, 0)
         grid0.addWidget(self.film_mirror_axis_label, 16, 0)
         grid0.addWidget(self.film_mirror_axis, 16, 1)
         grid0.addWidget(self.film_mirror_axis, 16, 1)
 
 
-        self.ois_mirror = OptionalInputSection(self.film_mirror_cb,
-                                               [self.film_mirror_axis_label, self.film_mirror_axis])
+        self.ois_mirror = OptionalHideInputSection(self.film_mirror_cb,
+                                                   [
+                                                       self.film_mirror_axis_label,
+                                                       self.film_mirror_axis
+                                                   ])
 
 
         separator_line2 = QtWidgets.QFrame()
         separator_line2 = QtWidgets.QFrame()
         separator_line2.setFrameShape(QtWidgets.QFrame.HLine)
         separator_line2.setFrameShape(QtWidgets.QFrame.HLine)
@@ -272,8 +279,6 @@ class Film(AppTool):
         grid0.addWidget(self.film_scale_stroke_label, 19, 0)
         grid0.addWidget(self.film_scale_stroke_label, 19, 0)
         grid0.addWidget(self.film_scale_stroke_entry, 19, 1)
         grid0.addWidget(self.film_scale_stroke_entry, 19, 1)
 
 
-        grid0.addWidget(QtWidgets.QLabel(''), 20, 0)
-
         # Film Type
         # Film Type
         self.film_type = RadioSet([{'label': _('Positive'), 'value': 'pos'},
         self.film_type = RadioSet([{'label': _('Positive'), 'value': 'pos'},
                                    {'label': _('Negative'), 'value': 'neg'}],
                                    {'label': _('Negative'), 'value': 'neg'}],

+ 3 - 3
appTools/ToolIsolation.py

@@ -2083,14 +2083,14 @@ class ToolIsolation(AppTool, Gerber):
             self.app.inform.emit("[WARNING] %s" % _("Partial failure. The geometry was processed with all tools.\n"
             self.app.inform.emit("[WARNING] %s" % _("Partial failure. The geometry was processed with all tools.\n"
                                                     "But there are still not-isolated geometry elements. "
                                                     "But there are still not-isolated geometry elements. "
                                                     "Try to include a tool with smaller diameter."))
                                                     "Try to include a tool with smaller diameter."))
-            self.app.shell_message(msg=_("The following are coordinates for the copper features "
-                                         "that could not be isolated:"))
+            msg = _("The following are coordinates for the copper features that could not be isolated:")
+            self.app.inform_shell.emit(msg)
             msg = ''
             msg = ''
             for geo in work_geo:
             for geo in work_geo:
                 pt = geo.representative_point()
                 pt = geo.representative_point()
                 coords = '(%s, %s), ' % (str(pt.x), str(pt.y))
                 coords = '(%s, %s), ' % (str(pt.x), str(pt.y))
                 msg += coords
                 msg += coords
-            self.app.shell_message(msg=msg)
+            self.app.inform_shell.emit(msg=msg)
 
 
     def combined_normal(self, iso_obj, iso2geo, tools_storage, lim_area, negative_dia=None, plot=True):
     def combined_normal(self, iso_obj, iso2geo, tools_storage, lim_area, negative_dia=None, plot=True):
         """
         """

+ 16 - 23
appTools/ToolNCC.py

@@ -766,6 +766,7 @@ class NonCopperClear(AppTool, Gerber):
         try:
         try:
             current_uid = int(self.tools_table.item(current_row, 3).text())
             current_uid = int(self.tools_table.item(current_row, 3).text())
             self.ncc_tools[current_uid]['data']['tools_nccoperation'] = val
             self.ncc_tools[current_uid]['data']['tools_nccoperation'] = val
+            # TODO got a crash here, a KeyError exception; need to see it again and find out the why
         except AttributeError:
         except AttributeError:
             return
             return
 
 
@@ -1639,6 +1640,7 @@ class NonCopperClear(AppTool, Gerber):
                                                                     "use a number."))
                                                                     "use a number."))
                         continue
                         continue
 
 
+                # find out which tools is for isolation and which are for copper clearing
                 for uid_k, uid_v in self.ncc_tools.items():
                 for uid_k, uid_v in self.ncc_tools.items():
                     if round(uid_v['tooldia'], self.decimals) == round(self.tooldia, self.decimals):
                     if round(uid_v['tooldia'], self.decimals) == round(self.tooldia, self.decimals):
                         if uid_v['data']['tools_nccoperation'] == "iso":
                         if uid_v['data']['tools_nccoperation'] == "iso":
@@ -2300,23 +2302,30 @@ class NonCopperClear(AppTool, Gerber):
             self.app.inform.emit('[ERROR_NOTCL] %s' % _('Geometry could not be cleared completely'))
             self.app.inform.emit('[ERROR_NOTCL] %s' % _('Geometry could not be cleared completely'))
             return None
             return None
 
 
-    def clear_copper(self, ncc_obj, sel_obj=None, ncctooldia=None, isotooldia=None, outname=None, order=None,
+    def clear_copper(self, ncc_obj, ncctooldia, isotooldia, sel_obj=None, outname=None, order=None,
                      tools_storage=None, run_threaded=True):
                      tools_storage=None, run_threaded=True):
         """
         """
         Clear the excess copper from the entire object.
         Clear the excess copper from the entire object.
 
 
         :param ncc_obj:         ncc cleared object
         :param ncc_obj:         ncc cleared object
+        :type ncc_obj:          appObjects.FlatCAMGerber.GerberObject
+        :param ncctooldia:      a list of diameters of the tools to be used to ncc clear
+        :type ncctooldia:       list
+        :param isotooldia:      a list of diameters of the tools to be used for isolation
+        :type isotooldia:       list
         :param sel_obj:
         :param sel_obj:
-        :param ncctooldia:      a tuple or single element made out of diameters of the tools to be used to ncc clear
-        :param isotooldia:      a tuple or single element made out of diameters of the tools to be used for isolation
+        :type sel_obj:
         :param outname:         name of the resulting object
         :param outname:         name of the resulting object
+        :type outname:          str
         :param order:           Tools order
         :param order:           Tools order
         :param tools_storage:   whether to use the current tools_storage self.ncc_tools or a different one.
         :param tools_storage:   whether to use the current tools_storage self.ncc_tools or a different one.
                                 Usage of the different one is related to when this function is called
                                 Usage of the different one is related to when this function is called
                                 from a TcL command.
                                 from a TcL command.
+        :type tools_storage:    dict
 
 
         :param run_threaded:    If True the method will be run in a threaded way suitable for GUI usage; if False
         :param run_threaded:    If True the method will be run in a threaded way suitable for GUI usage; if False
                                 it will run non-threaded for TclShell usage
                                 it will run non-threaded for TclShell usage
+        :type run_threaded:     bool
         :return:
         :return:
         """
         """
         log.debug("Executing the handler ...")
         log.debug("Executing the handler ...")
@@ -2338,29 +2347,13 @@ class NonCopperClear(AppTool, Gerber):
 
 
         # determine if to use the progressive plotting
         # determine if to use the progressive plotting
         prog_plot = True if self.app.defaults["tools_ncc_plotting"] == 'progressive' else False
         prog_plot = True if self.app.defaults["tools_ncc_plotting"] == 'progressive' else False
-        tools_storage = tools_storage if tools_storage is not None else self.ncc_tools
 
 
-        # ######################################################################################################
-        # # Read the tooldia parameter and create a sorted list out them - they may be more than one diameter ##
-        # ######################################################################################################
-        sorted_clear_tools = []
-        if ncctooldia is not None:
-            try:
-                sorted_clear_tools = [float(eval(dia)) for dia in ncctooldia.split(",") if dia != '']
-            except AttributeError:
-                if not isinstance(ncctooldia, list):
-                    sorted_clear_tools = [float(ncctooldia)]
-                else:
-                    sorted_clear_tools = ncctooldia
-        else:
-            # for row in range(self.tools_table.rowCount()):
-            #     if self.tools_table.cellWidget(row, 1).currentText() == 'clear_op':
-            #         sorted_clear_tools.append(float(self.tools_table.item(row, 1).text()))
-            for tooluid in self.ncc_tools:
-                if self.ncc_tools[tooluid]['data']['tools_nccoperation'] == 'clear':
-                    sorted_clear_tools.append(self.ncc_tools[tooluid]['tooldia'])
+        tools_storage = tools_storage if tools_storage is not None else self.ncc_tools
+        sorted_clear_tools = ncctooldia
 
 
         if not sorted_clear_tools:
         if not sorted_clear_tools:
+            self.app.inform.emit('[ERROR_NOTCL] %s' % _("There is no copper clearing tool in the selection "
+                                                        "and at least one is needed."))
             return 'fail'
             return 'fail'
 
 
         # ########################################################################################################
         # ########################################################################################################

+ 1 - 1
appTranslation.py

@@ -135,7 +135,7 @@ def apply_language(domain, lang=None):
         if settings.contains("language"):
         if settings.contains("language"):
             name = settings.value('language')
             name = settings.value('language')
         else:
         else:
-            name = settings.value('English')
+            name = 'English'
             # in case the 'language' parameter is not in QSettings add it to QSettings and it's value is
             # in case the 'language' parameter is not in QSettings add it to QSettings and it's value is
             # the default language, English
             # the default language, English
             settings.setValue('language', 'English')
             settings.setValue('language', 'English')

+ 29 - 10
app_Main.py

@@ -162,9 +162,9 @@ class App(QtCore.QObject):
     # ###############################################################################################################
     # ###############################################################################################################
     # ################################### Version and VERSION DATE ##################################################
     # ################################### Version and VERSION DATE ##################################################
     # ###############################################################################################################
     # ###############################################################################################################
-    # version = "Unstable Version"
-    version = 8.993
-    version_date = "2020/06/05"
+    version = "Unstable Version"
+    # version = 8.994
+    version_date = "2020/07/05"
     beta = True
     beta = True
 
 
     engine = '3D'
     engine = '3D'
@@ -203,9 +203,10 @@ class App(QtCore.QObject):
     # ###############################################################################################################
     # ###############################################################################################################
 
 
     # Inform the user
     # Inform the user
-    # Handled by:
-    #  * App.info() --> Print on the status bar
+    # Handled by: App.info() --> Print on the status bar
     inform = QtCore.pyqtSignal([str], [str, bool])
     inform = QtCore.pyqtSignal([str], [str, bool])
+    # Handled by: App.info_shell() --> Print on the shell
+    inform_shell = QtCore.pyqtSignal(str)
 
 
     app_quit = QtCore.pyqtSignal()
     app_quit = QtCore.pyqtSignal()
 
 
@@ -448,6 +449,17 @@ class App(QtCore.QObject):
         # ###########################################################################################################
         # ###########################################################################################################
         self.pool = Pool()
         self.pool = Pool()
 
 
+        # ###########################################################################################################
+        # ###################################### Clear GUI Settings - once at first start ###########################
+        # ###########################################################################################################
+        if self.defaults["first_run"] is True:
+            # on first run clear the previous QSettings, therefore clearing the GUI settings
+            qsettings = QSettings("Open Source", "FlatCAM")
+            for key in qsettings.allKeys():
+                qsettings.remove(key)
+            # This will write the setting to the platform specific storage.
+            del qsettings
+
         # ###########################################################################################################
         # ###########################################################################################################
         # ###################################### Setting the Splash Screen ##########################################
         # ###################################### Setting the Splash Screen ##########################################
         # ###########################################################################################################
         # ###########################################################################################################
@@ -578,7 +590,7 @@ class App(QtCore.QObject):
         # ################################ It's done only once after install   #####################################
         # ################################ It's done only once after install   #####################################
         # ###########################################################################################################
         # ###########################################################################################################
         if self.defaults["first_run"] is True:
         if self.defaults["first_run"] is True:
-            # ONLY AT FIRST STARTUP INIT THE GUI LAYOUT TO 'COMPACT'
+            # ONLY AT FIRST STARTUP INIT THE GUI LAYOUT TO 'minimal'
             initial_lay = 'minimal'
             initial_lay = 'minimal'
             self.ui.general_defaults_form.general_gui_group.on_layout(lay=initial_lay)
             self.ui.general_defaults_form.general_gui_group.on_layout(lay=initial_lay)
 
 
@@ -762,6 +774,9 @@ class App(QtCore.QObject):
         self.inform[str].connect(self.info)
         self.inform[str].connect(self.info)
         self.inform[str, bool].connect(self.info)
         self.inform[str, bool].connect(self.info)
 
 
+        # signal for displaying messages in the shell
+        self.inform_shell.connect(self.info_shell)
+
         # signal to be called when the app is quiting
         # signal to be called when the app is quiting
         self.app_quit.connect(self.quit_application, type=Qt.QueuedConnection)
         self.app_quit.connect(self.quit_application, type=Qt.QueuedConnection)
         self.message.connect(lambda: message_dialog(parent=self.ui))
         self.message.connect(lambda: message_dialog(parent=self.ui))
@@ -2428,6 +2443,9 @@ class App(QtCore.QObject):
             if msg != '' and shell_echo is True:
             if msg != '' and shell_echo is True:
                 self.shell_message(msg)
                 self.shell_message(msg)
 
 
+    def info_shell(self, msg):
+        self.shell_message(msg=msg)
+
     def on_import_preferences(self):
     def on_import_preferences(self):
         """
         """
         Loads the application default settings from a saved file into
         Loads the application default settings from a saved file into
@@ -6699,10 +6717,11 @@ class App(QtCore.QObject):
         self.defaults.report_usage("on_fileopengerber")
         self.defaults.report_usage("on_fileopengerber")
         App.log.debug("on_fileopengerber()")
         App.log.debug("on_fileopengerber()")
 
 
-        _filter_ = "Gerber Files (*.gbr *.ger *.gtl *.gbl *.gts *.gbs *.gtp *.gbp *.gto *.gbo *.gm1 *.gml *.gm3 *" \
-                   ".gko *.cmp *.sol *.stc *.sts *.plc *.pls *.crc *.crs *.tsm *.bsm *.ly2 *.ly15 *.dim *.mil *.grb" \
-                   "*.top *.bot *.smt *.smb *.sst *.ssb *.spt *.spb *.pho *.gdo *.art *.gbd);;" \
-                   "Protel Files (*.gtl *.gbl *.gts *.gbs *.gto *.gbo *.gtp *.gbp *.gml *.gm1 *.gm3 *.gko);;" \
+        _filter_ = "Gerber Files (*.gbr *.ger *.gtl *.gbl *.gts *.gbs *.gtp *.gbp *.gto *.gbo *.gm1 *.gml *.gm3 " \
+                   "*.gko *.cmp *.sol *.stc *.sts *.plc *.pls *.crc *.crs *.tsm *.bsm *.ly2 *.ly15 *.dim *.mil *.grb " \
+                   "*.top *.bot *.smt *.smb *.sst *.ssb *.spt *.spb *.pho *.gdo *.art *.gbd *.outline);;" \
+                   "Protel Files (*.gtl *.gbl *.gts *.gbs *.gto *.gbo *.gtp *.gbp *.gml *.gm1 *.gm3 *.gko " \
+                   "*.outline);;" \
                    "Eagle Files (*.cmp *.sol *.stc *.sts *.plc *.pls *.crc *.crs *.tsm *.bsm *.ly2 *.ly15 *.dim " \
                    "Eagle Files (*.cmp *.sol *.stc *.sts *.plc *.pls *.crc *.crs *.tsm *.bsm *.ly2 *.ly15 *.dim " \
                    "*.mil);;" \
                    "*.mil);;" \
                    "OrCAD Files (*.top *.bot *.smt *.smb *.sst *.ssb *.spt *.spb);;" \
                    "OrCAD Files (*.top *.bot *.smt *.smb *.sst *.ssb *.spt *.spb);;" \

+ 52 - 64
camlib.py

@@ -3745,23 +3745,28 @@ class CNCjob(Geometry):
         flat_geometry = self.flatten(temp_solid_geometry, pathonly=True)
         flat_geometry = self.flatten(temp_solid_geometry, pathonly=True)
         log.debug("%d paths" % len(flat_geometry))
         log.debug("%d paths" % len(flat_geometry))
 
 
-        self.tooldia = float(tooldia) if tooldia else None
+        try:
+            self.tooldia = float(tooldia)
+        except Exception as e:
+            self.app.inform.emit('[ERROR] %s\n%s' % (_("Failed."), str(e)))
+            return 'fail'
+
         self.z_cut = float(z_cut) if z_cut else None
         self.z_cut = float(z_cut) if z_cut else None
         self.z_move = float(z_move) if z_move is not None else None
         self.z_move = float(z_move) if z_move is not None else None
 
 
-        self.feedrate = float(feedrate) if feedrate else None
-        self.z_feedrate = float(feedrate_z) if feedrate_z is not None else None
-        self.feedrate_rapid = float(feedrate_rapid) if feedrate_rapid else None
+        self.feedrate = float(feedrate) if feedrate else  self.app.defaults["geometry_feedrate"]
+        self.z_feedrate = float(feedrate_z) if feedrate_z is not None else  self.app.defaults["geometry_feedrate_z"]
+        self.feedrate_rapid = float(feedrate_rapid) if feedrate_rapid else  self.app.defaults["geometry_feedrate_rapid"]
 
 
         self.spindlespeed = int(spindlespeed) if spindlespeed != 0 else None
         self.spindlespeed = int(spindlespeed) if spindlespeed != 0 else None
         self.spindledir = spindledir
         self.spindledir = spindledir
         self.dwell = dwell
         self.dwell = dwell
-        self.dwelltime = float(dwelltime) if dwelltime else None
+        self.dwelltime = float(dwelltime) if dwelltime else  self.app.defaults["geometry_dwelltime"]
 
 
-        self.startz = float(startz) if startz is not None else None
-        self.z_end = float(endz) if endz is not None else None
+        self.startz = float(startz) if startz is not None else  self.app.defaults["geometry_startz"]
+        self.z_end = float(endz) if endz is not None else  self.app.defaults["geometry_endz"]
 
 
-        self.xy_end = re.sub('[()\[\]]', '', str(endxy)) if endxy else None
+        self.xy_end = re.sub('[()\[\]]', '', str(endxy)) if endxy else  self.app.defaults["geometry_endxy"]
 
 
         if self.xy_end and self.xy_end != '':
         if self.xy_end and self.xy_end != '':
             self.xy_end = [float(eval(a)) for a in self.xy_end.split(",")]
             self.xy_end = [float(eval(a)) for a in self.xy_end.split(",")]
@@ -3771,10 +3776,10 @@ class CNCjob(Geometry):
                                                    "in the format (x, y) but now there is only one value, not two."))
                                                    "in the format (x, y) but now there is only one value, not two."))
             return 'fail'
             return 'fail'
 
 
-        self.z_depthpercut = float(depthpercut) if depthpercut else None
+        self.z_depthpercut = float(depthpercut) if depthpercut else self.app.defaults["geometry_depthperpass"]
         self.multidepth = multidepth
         self.multidepth = multidepth
 
 
-        self.z_toolchange = float(toolchangez) if toolchangez is not None else None
+        self.z_toolchange = float(toolchangez) if toolchangez is not None else self.app.defaults["geometry_toolchangez"]
 
 
         # it servers in the preprocessor file
         # it servers in the preprocessor file
         self.tool = tool_no
         self.tool = tool_no
@@ -3783,7 +3788,8 @@ class CNCjob(Geometry):
             if toolchangexy == '':
             if toolchangexy == '':
                 self.xy_toolchange = None
                 self.xy_toolchange = None
             else:
             else:
-                self.xy_toolchange = re.sub('[()\[\]]', '', str(toolchangexy)) if toolchangexy else None
+                self.xy_toolchange = re.sub('[()\[\]]', '', str(toolchangexy)) \
+                    if toolchangexy else self.app.defaults["geometry_toolchangexy"]
 
 
                 if self.xy_toolchange and self.xy_toolchange != '':
                 if self.xy_toolchange and self.xy_toolchange != '':
                     self.xy_toolchange = [float(eval(a)) for a in self.xy_toolchange.split(",")]
                     self.xy_toolchange = [float(eval(a)) for a in self.xy_toolchange.split(",")]
@@ -3802,9 +3808,9 @@ class CNCjob(Geometry):
 
 
         if self.z_cut is None:
         if self.z_cut is None:
             if 'laser' not in self.pp_geometry_name:
             if 'laser' not in self.pp_geometry_name:
-                self.app.inform.emit('[ERROR_NOTCL] %s' %
-                                     _("Cut_Z parameter is None or zero. Most likely a bad combinations of "
-                                       "other parameters."))
+                self.app.inform.emit(
+                    '[ERROR_NOTCL] %s' % _("Cut_Z parameter is None or zero. Most likely a bad combinations of "
+                                           "other parameters."))
                 return 'fail'
                 return 'fail'
             else:
             else:
                 self.z_cut = 0
                 self.z_cut = 0
@@ -3959,9 +3965,7 @@ class CNCjob(Geometry):
                     total_cut = total_cut + geo.length
                     total_cut = total_cut + geo.length
 
 
                     self.gcode += self.create_gcode_single_pass(geo, current_tooldia, extracut, extracut_length,
                     self.gcode += self.create_gcode_single_pass(geo, current_tooldia, extracut, extracut_length,
-                                                                tolerance,
-                                                                z_move=z_move, postproc=p,
-                                                                old_point=current_pt)
+                                                                tolerance, z_move=z_move, old_point=current_pt)
 
 
                 # --------- Multi-pass ---------
                 # --------- Multi-pass ---------
                 else:
                 else:
@@ -3976,8 +3980,7 @@ class CNCjob(Geometry):
                     total_cut += (geo.length * nr_cuts)
                     total_cut += (geo.length * nr_cuts)
 
 
                     self.gcode += self.create_gcode_multi_pass(geo, current_tooldia, extracut, extracut_length,
                     self.gcode += self.create_gcode_multi_pass(geo, current_tooldia, extracut, extracut_length,
-                                                               tolerance,
-                                                               z_move=z_move, postproc=p,
+                                                               tolerance,  z_move=z_move, postproc=p,
                                                                old_point=current_pt)
                                                                old_point=current_pt)
 
 
                 # calculate the total distance
                 # calculate the total distance
@@ -4009,14 +4012,12 @@ class CNCjob(Geometry):
         )
         )
         return self.gcode
         return self.gcode
 
 
-    def generate_from_geometry_2(
-            self, geometry, append=True, tooldia=None, offset=0.0, tolerance=0, z_cut=None, z_move=None,
-            feedrate=None, feedrate_z=None, feedrate_rapid=None,
-            spindlespeed=None, spindledir='CW', dwell=False, dwelltime=None,
-            multidepth=False, depthpercut=None,
-            toolchange=False, toolchangez=None, toolchangexy="0.0, 0.0",
-            extracut=False, extracut_length=None, startz=None, endz=None, endxy='',
-            pp_geometry_name=None, tool_no=1):
+    def generate_from_geometry_2(self, geometry, append=True, tooldia=None, offset=0.0, tolerance=0, z_cut=None,
+                                 z_move=None, feedrate=None, feedrate_z=None, feedrate_rapid=None, spindlespeed=None,
+                                 spindledir='CW', dwell=False, dwelltime=None, multidepth=False, depthpercut=None,
+                                 toolchange=False, toolchangez=None, toolchangexy="0.0, 0.0", extracut=False,
+                                 extracut_length=None, startz=None, endz=None, endxy='', pp_geometry_name=None,
+                                 tool_no=1):
         """
         """
         Second algorithm to generate from Geometry.
         Second algorithm to generate from Geometry.
 
 
@@ -4126,7 +4127,7 @@ class CNCjob(Geometry):
         flat_geometry = self.flatten(temp_solid_geometry, pathonly=True)
         flat_geometry = self.flatten(temp_solid_geometry, pathonly=True)
         log.debug("%d paths" % len(flat_geometry))
         log.debug("%d paths" % len(flat_geometry))
 
 
-        default_dia = 0.01
+        default_dia = None
         if isinstance(self.app.defaults["geometry_cnctooldia"], float):
         if isinstance(self.app.defaults["geometry_cnctooldia"], float):
             default_dia = self.app.defaults["geometry_cnctooldia"]
             default_dia = self.app.defaults["geometry_cnctooldia"]
         else:
         else:
@@ -4142,6 +4143,10 @@ class CNCjob(Geometry):
         except ValueError:
         except ValueError:
             self.tooldia = [float(el) for el in tooldia.split(',') if el != ''] if tooldia is not None else default_dia
             self.tooldia = [float(el) for el in tooldia.split(',') if el != ''] if tooldia is not None else default_dia
 
 
+        if self.tooldia is None:
+            self.app.inform.emit('[ERROR] %s' % _("Failed."))
+            return 'fail'
+
         self.z_cut = float(z_cut) if z_cut is not None else self.app.defaults["geometry_cutz"]
         self.z_cut = float(z_cut) if z_cut is not None else self.app.defaults["geometry_cutz"]
         self.z_move = float(z_move) if z_move is not None else self.app.defaults["geometry_travelz"]
         self.z_move = float(z_move) if z_move is not None else self.app.defaults["geometry_travelz"]
 
 
@@ -4186,11 +4191,9 @@ class CNCjob(Geometry):
                     self.xy_toolchange = [float(eval(a)) for a in self.xy_toolchange.split(",")]
                     self.xy_toolchange = [float(eval(a)) for a in self.xy_toolchange.split(",")]
 
 
                 if len(self.xy_toolchange) < 2:
                 if len(self.xy_toolchange) < 2:
-                    self.app.inform.emit(
-                        '[ERROR] %s' %
-                        _("The Toolchange X,Y field in Edit -> Preferences has to be in the format (x, y) \n"
-                          "but now there is only one value, not two. ")
-                    )
+                    msg = _("The Toolchange X,Y field in Edit -> Preferences has to be in the format (x, y)\n"
+                            "but now there is only one value, not two.")
+                    self.app.inform.emit('[ERROR] %s' % msg)
                     return 'fail'
                     return 'fail'
         except Exception as e:
         except Exception as e:
             log.debug("camlib.CNCJob.generate_from_geometry_2() --> %s" % str(e))
             log.debug("camlib.CNCJob.generate_from_geometry_2() --> %s" % str(e))
@@ -4369,9 +4372,7 @@ class CNCjob(Geometry):
                     # calculate the cut distance
                     # calculate the cut distance
                     total_cut += geo.length
                     total_cut += geo.length
                     self.gcode += self.create_gcode_single_pass(geo, current_tooldia, extracut, self.extracut_length,
                     self.gcode += self.create_gcode_single_pass(geo, current_tooldia, extracut, self.extracut_length,
-                                                                tolerance,
-                                                                z_move=z_move, postproc=p,
-                                                                old_point=current_pt)
+                                                                tolerance, z_move=z_move, old_point=current_pt)
 
 
                 # --------- Multi-pass ---------
                 # --------- Multi-pass ---------
                 else:
                 else:
@@ -4386,8 +4387,7 @@ class CNCjob(Geometry):
                     total_cut += (geo.length * nr_cuts)
                     total_cut += (geo.length * nr_cuts)
 
 
                     self.gcode += self.create_gcode_multi_pass(geo, current_tooldia, extracut, self.extracut_length,
                     self.gcode += self.create_gcode_multi_pass(geo, current_tooldia, extracut, self.extracut_length,
-                                                               tolerance,
-                                                               z_move=z_move, postproc=p,
+                                                               tolerance, z_move=z_move, postproc=p,
                                                                old_point=current_pt)
                                                                old_point=current_pt)
 
 
                 # calculate the travel distance
                 # calculate the travel distance
@@ -4611,8 +4611,7 @@ class CNCjob(Geometry):
             gcode += self.doformat(p.lift_code)
             gcode += self.doformat(p.lift_code)
         return gcode
         return gcode
 
 
-    def create_gcode_single_pass(self, geometry, cdia, extracut, extracut_length, tolerance, z_move, postproc,
-                                 old_point=(0, 0)):
+    def create_gcode_single_pass(self, geometry, cdia, extracut, extracut_length, tolerance, z_move, old_point=(0, 0)):
         """
         """
         # G-code. Note: self.linear2gcode() and self.point2gcode() will lower and raise the tool every time.
         # G-code. Note: self.linear2gcode() and self.point2gcode() will lower and raise the tool every time.
 
 
@@ -4628,8 +4627,6 @@ class CNCjob(Geometry):
         :type tolerance:            float
         :type tolerance:            float
         :param z_move:              Travel Z
         :param z_move:              Travel Z
         :type z_move:               float
         :type z_move:               float
-        :param postproc:            Preprocessor class
-        :type postproc:             class
         :param old_point:           Previous point
         :param old_point:           Previous point
         :type old_point:            tuple
         :type old_point:            tuple
         :return:                    Gcode
         :return:                    Gcode
@@ -4638,19 +4635,15 @@ class CNCjob(Geometry):
         # p = postproc
         # p = postproc
 
 
         if type(geometry) == LineString or type(geometry) == LinearRing:
         if type(geometry) == LineString or type(geometry) == LinearRing:
-            if extracut is False:
-                gcode_single_pass = self.linear2gcode(geometry, z_move=z_move, dia=cdia, tolerance=tolerance,
+            if extracut is False or not geometry.is_ring:
+                gcode_single_pass = self.linear2gcode(geometry, cdia, z_move=z_move, tolerance=tolerance,
                                                       old_point=old_point)
                                                       old_point=old_point)
             else:
             else:
-                if geometry.is_ring:
-                    gcode_single_pass = self.linear2gcode_extra(geometry, extracut_length, tolerance=tolerance,
-                                                                z_move=z_move, dia=cdia,
-                                                                old_point=old_point)
-                else:
-                    gcode_single_pass = self.linear2gcode(geometry, tolerance=tolerance, z_move=z_move, dia=cdia,
-                                                          old_point=old_point)
+                gcode_single_pass = self.linear2gcode_extra(geometry, cdia, extracut_length, tolerance=tolerance,
+                                                            z_move=z_move, old_point=old_point)
+
         elif type(geometry) == Point:
         elif type(geometry) == Point:
-            gcode_single_pass = self.point2gcode(geometry, dia=cdia, z_move=z_move, old_point=old_point)
+            gcode_single_pass = self.point2gcode(geometry, cdia, z_move=z_move, old_point=old_point)
         else:
         else:
             log.warning("G-code generation not implemented for %s" % (str(type(geometry))))
             log.warning("G-code generation not implemented for %s" % (str(type(geometry))))
             return
             return
@@ -4708,22 +4701,17 @@ class CNCjob(Geometry):
             # at the first point if the tool is down (in the material).  So, an extra G00 should show up but
             # at the first point if the tool is down (in the material).  So, an extra G00 should show up but
             # is inconsequential.
             # is inconsequential.
             if type(geometry) == LineString or type(geometry) == LinearRing:
             if type(geometry) == LineString or type(geometry) == LinearRing:
-                if extracut is False:
-                    gcode_multi_pass += self.linear2gcode(geometry, tolerance=tolerance, z_cut=depth, up=False,
-                                                          z_move=z_move, dia=cdia, old_point=old_point)
+                if extracut is False or not geometry.is_ring:
+                    gcode_multi_pass += self.linear2gcode(geometry, cdia, tolerance=tolerance, z_cut=depth, up=False,
+                                                          z_move=z_move, old_point=old_point)
                 else:
                 else:
-                    if geometry.is_ring:
-                        gcode_multi_pass += self.linear2gcode_extra(geometry, extracut_length, tolerance=tolerance,
-                                                                    dia=cdia, z_move=z_move, z_cut=depth, up=False,
-                                                                    old_point=old_point)
-                    else:
-                        gcode_multi_pass += self.linear2gcode(geometry, tolerance=tolerance, z_cut=depth, up=False,
-                                                              dia=cdia, z_move=z_move,
-                                                              old_point=old_point)
+                    gcode_multi_pass += self.linear2gcode_extra(geometry, cdia, extracut_length, tolerance=tolerance,
+                                                                z_move=z_move, z_cut=depth, up=False,
+                                                                old_point=old_point)
 
 
             # Ignore multi-pass for points.
             # Ignore multi-pass for points.
             elif type(geometry) == Point:
             elif type(geometry) == Point:
-                gcode_multi_pass += self.point2gcode(geometry, dia=cdia, z_move=z_move, old_point=old_point)
+                gcode_multi_pass += self.point2gcode(geometry, cdia, z_move=z_move, old_point=old_point)
                 break  # Ignoring ...
                 break  # Ignoring ...
             else:
             else:
                 log.warning("G-code generation not implemented for %s" % (str(type(geometry))))
                 log.warning("G-code generation not implemented for %s" % (str(type(geometry))))

+ 74 - 65
defaults.py

@@ -22,70 +22,15 @@ log = logging.getLogger('FlatCAMDefaults')
 class FlatCAMDefaults:
 class FlatCAMDefaults:
 
 
     factory_defaults = {
     factory_defaults = {
-        # Global APP Preferences
-        "decimals_inch": 4,
-        "decimals_metric": 4,
-        "version": 8.992,   # defaults format version, not necessarily equal to app version
+        # Global
+        "version": 8.992,  # defaults format version, not necessarily equal to app version
         "first_run": True,
         "first_run": True,
-        "units": "MM",
         "root_folder_path": '',
         "root_folder_path": '',
         "global_serial": 0,
         "global_serial": 0,
         "global_stats": dict(),
         "global_stats": dict(),
         "global_tabs_detachable": True,
         "global_tabs_detachable": True,
         "global_jump_ref": 'abs',
         "global_jump_ref": 'abs',
         "global_locate_pt": 'bl',
         "global_locate_pt": 'bl',
-        "global_tpdf_tmargin": 15.0,
-        "global_tpdf_bmargin": 10.0,
-        "global_tpdf_lmargin": 20.0,
-        "global_tpdf_rmargin": 20.0,
-        "global_autosave": False,
-        "global_autosave_timeout": 300000,
-
-        # General
-        "global_graphic_engine": '3D',
-        "global_hud": True,
-        "global_app_level": 'b',
-        "global_portable": False,
-        "global_language": 'English',
-        "global_version_check": True,
-        "global_send_stats": True,
-        "global_pan_button": '2',
-        "global_mselect_key": 'Control',
-        "global_project_at_startup": False,
-        "global_systray_icon": True,
-        "global_project_autohide": True,
-        "global_toggle_tooltips": True,
-        "global_worker_number": 2,
-        "global_tolerance": 0.005,
-        "global_open_style": True,
-        "global_delete_confirmation": True,
-        "global_compression_level": 3,
-        "global_save_compressed": True,
-
-        "global_machinist_setting": False,
-
-        # Global GUI Preferences
-        "global_gridx": 1.0,
-        "global_gridy": 1.0,
-        "global_snap_max": 0.05,
-        "global_workspace": False,
-        "global_workspaceT": "A4",
-        "global_workspace_orientation": 'p',
-
-        "global_grid_context_menu": {
-            'in': [0.01, 0.02, 0.025, 0.05, 0.1],
-            'mm': [0.1, 0.2, 0.5, 1, 2.54]
-        },
-
-        "global_sel_fill": '#a5a5ffbf',
-        "global_sel_line": '#0000ffbf',
-        "global_alt_sel_fill": '#BBF268BF',
-        "global_alt_sel_line": '#006E20BF',
-        "global_draw_color": '#FF0000',
-        "global_sel_draw_color": '#0000FF',
-        "global_proj_item_color": '#000000',
-        "global_proj_item_dis_color": '#b7b7cb',
-        "global_activity_icon": 'Ball green',
 
 
         "global_toolbar_view": 511,
         "global_toolbar_view": 511,
 
 
@@ -95,6 +40,12 @@ class FlatCAMDefaults:
         # 1 = show trace(show trace always),
         # 1 = show trace(show trace always),
         # 2 = (For the future).
         # 2 = (For the future).
 
 
+        "global_grid_context_menu": {
+            'in': [0.01, 0.02, 0.025, 0.05, 0.1],
+            'mm': [0.1, 0.2, 0.5, 1, 2.54]
+        },
+        "global_hud": True,
+
         # Persistence
         # Persistence
         "global_last_folder": None,
         "global_last_folder": None,
         "global_last_save_folder": None,
         "global_last_save_folder": None,
@@ -109,12 +60,8 @@ class FlatCAMDefaults:
         # Constants...
         # Constants...
         "global_defaults_save_period_ms": 20000,  # Time between default saves.
         "global_defaults_save_period_ms": 20000,  # Time between default saves.
         "global_shell_shape": [500, 300],  # Shape of the shell in pixels.
         "global_shell_shape": [500, 300],  # Shape of the shell in pixels.
-        "global_shell_at_startup": False,  # Show the shell at startup.
         "global_recent_limit": 10,  # Max. items in recent list.
         "global_recent_limit": 10,  # Max. items in recent list.
 
 
-        "global_bookmarks": dict(),
-        "global_bookmarks_limit": 10,
-
         "fit_key": 'V',
         "fit_key": 'V',
         "zoom_out_key": '-',
         "zoom_out_key": '-',
         "zoom_in_key": '=',
         "zoom_in_key": '=',
@@ -125,18 +72,80 @@ class FlatCAMDefaults:
 
 
         "global_tcl_path": '',
         "global_tcl_path": '',
 
 
-        # General GUI Settings
+        # General APP Preferences
+        "units": "MM",
+        "decimals_inch": 4,
+        "decimals_metric": 4,
+        "global_graphic_engine": '3D',
+        "global_app_level": 'b',
+
+        "global_portable": False,
+        "global_language": 'English',
+
+
+        "global_systray_icon": True,
+        "global_shell_at_startup": False,  # Show the shell at startup.
+        "global_project_at_startup": False,
+        "global_version_check": True,
+        "global_send_stats": True,
+        "global_worker_number": 2,
+        "global_tolerance": 0.005,
+
+        "global_save_compressed": True,
+        "global_compression_level": 3,
+        "global_autosave": False,
+        "global_autosave_timeout": 300000,
+
+        "global_tpdf_tmargin": 15.0,
+        "global_tpdf_bmargin": 10.0,
+        "global_tpdf_lmargin": 20.0,
+        "global_tpdf_rmargin": 20.0,
+
+        # General GUI Preferences
         "global_theme": 'white',
         "global_theme": 'white',
         "global_gray_icons": False,
         "global_gray_icons": False,
+
+        "global_layout": "compact",
         "global_hover": False,
         "global_hover": False,
         "global_selection_shape": True,
         "global_selection_shape": True,
-        "global_layout": "compact",
+
+        "global_sel_fill": '#a5a5ffbf',
+        "global_sel_line": '#0000ffbf',
+        "global_alt_sel_fill": '#BBF268BF',
+        "global_alt_sel_line": '#006E20BF',
+        "global_draw_color": '#FF0000',
+        "global_sel_draw_color": '#0000FF',
+        "global_proj_item_color": '#000000',
+        "global_proj_item_dis_color": '#b7b7cb',
+        "global_project_autohide": True,
+
+        # General App Settings
+        "global_gridx": 1.0,
+        "global_gridy": 1.0,
+        "global_snap_max": 0.05,
+
+        "global_workspace": False,
+        "global_workspaceT": "A4",
+        "global_workspace_orientation": 'p',
+
         "global_cursor_type": "small",
         "global_cursor_type": "small",
         "global_cursor_size": 20,
         "global_cursor_size": 20,
         "global_cursor_width": 2,
         "global_cursor_width": 2,
         "global_cursor_color": '#FF0000',
         "global_cursor_color": '#FF0000',
         "global_cursor_color_enabled": True,
         "global_cursor_color_enabled": True,
 
 
+        "global_pan_button": '2',
+        "global_mselect_key": 'Control',
+
+        "global_delete_confirmation": True,
+        "global_allow_edit_in_project_tab": False,
+        "global_open_style": True,
+        "global_toggle_tooltips": True,
+        "global_machinist_setting": False,
+        "global_bookmarks": dict(),
+        "global_bookmarks_limit": 10,
+        "global_activity_icon": 'Ball green',
+
         # Gerber General
         # Gerber General
         "gerber_plot": True,
         "gerber_plot": True,
         "gerber_solid": True,
         "gerber_solid": True,
@@ -218,7 +227,7 @@ class FlatCAMDefaults:
         "excellon_format_lower_in": 4,
         "excellon_format_lower_in": 4,
         "excellon_format_upper_mm": 3,
         "excellon_format_upper_mm": 3,
         "excellon_format_lower_mm": 3,
         "excellon_format_lower_mm": 3,
-        "excellon_zeros": "L",
+        "excellon_zeros": "T",
         "excellon_units": "INCH",
         "excellon_units": "INCH",
         "excellon_update": True,
         "excellon_update": True,
 
 
@@ -273,7 +282,7 @@ class FlatCAMDefaults:
 
 
         # Excellon Export
         # Excellon Export
         "excellon_exp_units": 'INCH',
         "excellon_exp_units": 'INCH',
-        "excellon_exp_format": 'ndec',
+        "excellon_exp_format": 'dec',
         "excellon_exp_integer": 2,
         "excellon_exp_integer": 2,
         "excellon_exp_decimals": 4,
         "excellon_exp_decimals": 4,
         "excellon_exp_zeros": 'LZ',
         "excellon_exp_zeros": 'LZ',

+ 1 - 1
tclCommands/TclCommandSetOrigin.py

@@ -100,4 +100,4 @@ class TclCommandSetOrigin(TclCommand):
         self.app.on_set_zero_click(event=None, location=loc, noplot=True, use_thread=False)
         self.app.on_set_zero_click(event=None, location=loc, noplot=True, use_thread=False)
         msg = '[success] Tcl %s: %s' % (_('Origin set by offsetting all loaded objects with '),
         msg = '[success] Tcl %s: %s' % (_('Origin set by offsetting all loaded objects with '),
                                         '{0:.4f}, {0:.4f}'.format(loc[0], loc[1]))
                                         '{0:.4f}, {0:.4f}'.format(loc[0], loc[1]))
-        self.app.shell_message(msg, success=True, show=False)
+        self.app.inform_shell.emit(msg)

+ 4 - 6
tclCommands/TclCommandSetPath.py

@@ -79,18 +79,16 @@ class TclCommandSetPath(TclCommand):
                     "The provided path",
                     "The provided path",
                     str(path),
                     str(path),
                     "is a path to file and not a directory as expected.")
                     "is a path to file and not a directory as expected.")
-                self.app.shell_message(msg, success=True, show=False)
+                self.app.inform_shell.emit(msg)
                 return "Failed. The Tcl command set_path was used but it was not a directory."
                 return "Failed. The Tcl command set_path was used but it was not a directory."
             else:
             else:
                 msg = '[ERROR] %s: %s, %s' % (
                 msg = '[ERROR] %s: %s, %s' % (
-                    "The provided path",
-                    str(path),
-                    "do not exist. Check for typos.")
-                self.app.shell_message(msg, success=True, show=False)
+                    "The provided path", str(path), "do not exist. Check for typos.")
+                self.app.inform_shell.emit(msg)
                 return "Failed. The Tcl command set_path was used but it does not exist."
                 return "Failed. The Tcl command set_path was used but it does not exist."
 
 
         cd_command = 'cd %s' % path
         cd_command = 'cd %s' % path
         self.app.shell.exec_command(cd_command, no_echo=False)
         self.app.shell.exec_command(cd_command, no_echo=False)
         self.app.defaults["global_tcl_path"] = str(path)
         self.app.defaults["global_tcl_path"] = str(path)
         msg = '[success] %s: %s' % ("Relative path set to", str(path))
         msg = '[success] %s: %s' % ("Relative path set to", str(path))
-        self.app.shell_message(msg, success=True, show=False)
+        self.app.inform_shell.emit(msg)