فهرست منبع

- added a new setting named 'Allow Machinist Unsafe Settings' that will allow the Travel Z and Cut Z to take both positive and negative values

Marius Stanciu 6 سال پیش
والد
کامیت
14917456ab
8فایلهای تغییر یافته به همراه252 افزوده شده و 162 حذف شده
  1. 12 0
      FlatCAMApp.py
  2. 3 31
      FlatCAMObj.py
  3. 1 1
      FlatCAMPostProc.py
  4. 4 0
      README.md
  5. 113 112
      camlib.py
  6. 1 1
      flatcamGUI/GUIElements.py
  7. 54 9
      flatcamGUI/ObjectUI.py
  8. 64 8
      flatcamGUI/PreferencesUI.py

+ 12 - 0
FlatCAMApp.py

@@ -410,6 +410,8 @@ class App(QtCore.QObject):
             "global_compression_level": 3,
             "global_save_compressed": True,
 
+            "global_machinist_setting": False,
+
             # Global GUI Preferences
             "global_gridx": 0.0393701,
             "global_gridy": 0.0393701,
@@ -966,6 +968,7 @@ class App(QtCore.QObject):
             "global_save_compressed": self.ui.general_defaults_form.general_app_group.save_type_cb,
 
             "global_bookmarks_limit": self.ui.general_defaults_form.general_app_group.bm_limit_spinner,
+            "global_machinist_setting": self.ui.general_defaults_form.general_app_group.machinist_cb,
 
             # General GUI Preferences
             "global_gridx": self.ui.general_defaults_form.general_gui_group.gridx_entry,
@@ -4836,6 +4839,10 @@ class App(QtCore.QObject):
                 self.ui.general_defaults_form.general_gui_set_group.textbox_font_size_spinner.get_value()
             )
             settings.setValue('toolbar_lock', self.ui.lock_action.isChecked())
+            settings.setValue(
+                'machinist',
+                1 if self.ui.general_defaults_form.general_app_group.machinist_cb.get_value() else 0
+            )
 
             # This will write the setting to the platform specific storage.
             del settings
@@ -6723,6 +6730,11 @@ class App(QtCore.QObject):
         tb_fsize = self.ui.general_defaults_form.general_gui_set_group.textbox_font_size_spinner.get_value()
         settings.setValue('textbox_font_size', tb_fsize)
 
+        settings.setValue(
+            'machinist',
+            1 if self.ui.general_defaults_form.general_app_group.machinist_cb.get_value() else 0
+        )
+
         # This will write the setting to the platform specific storage.
         del settings
 

+ 3 - 31
FlatCAMObj.py

@@ -4862,16 +4862,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
                 elif dia_cnc_dict['offset'].lower() == 'out':
                     tool_offset = tooldia_val / 2
                 elif dia_cnc_dict['offset'].lower() == 'custom':
-                    try:
-                        offset_value = float(self.ui.tool_offset_entry.get_value())
-                    except ValueError:
-                        # try to convert comma to decimal point. if it's still not working error message and return
-                        try:
-                            offset_value = float(self.ui.tool_offset_entry.get_value().replace(',', '.'))
-                        except ValueError:
-                            self.app.inform.emit('[ERROR_NOTCL] %s' %
-                                                 _("Wrong value format entered, use a number."))
-                            return
+                    offset_value = float(self.ui.tool_offset_entry.get_value())
                     if offset_value:
                         tool_offset = float(offset_value)
                     else:
@@ -5075,27 +5066,8 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
             job_obj.segx = segx
             job_obj.segy = segy
 
-            try:
-                job_obj.z_pdepth = float(self.options["z_pdepth"])
-            except ValueError:
-                # try to convert comma to decimal point. if it's still not working error message and return
-                try:
-                    job_obj.z_pdepth = float(self.options["z_pdepth"].replace(',', '.'))
-                except ValueError:
-                    self.app.inform.emit('[ERROR_NOTCL] %s' %
-                                         _('Wrong value format for self.defaults["z_pdepth"] or '
-                                           'self.options["z_pdepth"]'))
-
-            try:
-                job_obj.feedrate_probe = float(self.options["feedrate_probe"])
-            except ValueError:
-                # try to convert comma to decimal point. if it's still not working error message and return
-                try:
-                    job_obj.feedrate_probe = float(self.options["feedrate_probe"].replace(',', '.'))
-                except ValueError:
-                    self.app.inform.emit('[ERROR_NOTCL] %s' %
-                                         _('Wrong value format for self.defaults["feedrate_probe"] '
-                                           'or self.options["feedrate_probe"]'))
+            job_obj.z_pdepth = float(self.options["z_pdepth"])
+            job_obj.feedrate_probe = float(self.options["feedrate_probe"])
 
             job_obj.options['xmin'] = self.options['xmin']
             job_obj.options['ymin'] = self.options['ymin']

+ 1 - 1
FlatCAMPostProc.py

@@ -18,7 +18,7 @@ postprocessors = {}
 
 
 class ABCPostProcRegister(ABCMeta):
-    # handles postprocessors registration on instantation
+    # handles postprocessors registration on instantiation
     def __new__(cls, clsname, bases, attrs):
         newclass = super(ABCPostProcRegister, cls).__new__(cls, clsname, bases, attrs)
         if object not in bases:

+ 4 - 0
README.md

@@ -9,6 +9,10 @@ CAD program, and create G-Code for Isolation routing.
 
 =================================================
 
+5.11.2019
+
+- added a new setting named 'Allow Machinist Unsafe Settings' that will allow the Travel Z and Cut Z to take both positive and negative values
+
 4.11.2019
 
 - wip

+ 113 - 112
camlib.py

@@ -7,7 +7,7 @@
 # ########################################################## ##
 
 
-from PyQt5 import QtWidgets
+from PyQt5 import QtWidgets, QtCore
 from io import StringIO
 
 import numpy as np
@@ -2143,6 +2143,12 @@ class CNCjob(Geometry):
         "excellon_optimization_type": "B",
     }
 
+    settings = QtCore.QSettings("Open Source", "FlatCAM")
+    if settings.contains("machinist"):
+        machinist_setting = settings.value('machinist', type=int)
+    else:
+        machinist_setting = 0
+
     def __init__(self,
                  units="in", kind="generic", tooldia=0.0,
                  z_cut=-0.002, z_move=0.1,
@@ -2368,21 +2374,21 @@ class CNCjob(Geometry):
         self.exc_drills = deepcopy(exobj.drills)
         self.exc_tools = deepcopy(exobj.tools)
 
-        if drillz > 0:
-            self.app.inform.emit('[WARNING] %s' %
-                                 _("The Cut Z parameter has positive value. "
-                                   "It is the depth value to drill into material.\n"
-                                   "The Cut Z parameter needs to have a negative value, assuming it is a typo "
-                                   "therefore the app will convert the value to negative. "
-                                   "Check the resulting CNC code (Gcode etc)."))
-            self.z_cut = -drillz
-        elif drillz == 0:
-            self.app.inform.emit('[WARNING] %s: %s' %
-                                 (_("The Cut Z parameter is zero. There will be no cut, skipping file"),
-                                  exobj.options['name']))
-            return 'fail'
-        else:
-            self.z_cut = drillz
+        self.z_cut = drillz
+        if self.machinist_setting == 0:
+            if drillz > 0:
+                self.app.inform.emit('[WARNING] %s' %
+                                     _("The Cut Z parameter has positive value. "
+                                       "It is the depth value to drill into material.\n"
+                                       "The Cut Z parameter needs to have a negative value, assuming it is a typo "
+                                       "therefore the app will convert the value to negative. "
+                                       "Check the resulting CNC code (Gcode etc)."))
+                self.z_cut = -drillz
+            elif drillz == 0:
+                self.app.inform.emit('[WARNING] %s: %s' %
+                                     (_("The Cut Z parameter is zero. There will be no cut, skipping file"),
+                                      exobj.options['name']))
+                return 'fail'
 
         self.z_toolchange = toolchangez
 
@@ -2512,8 +2518,7 @@ class CNCjob(Geometry):
         measured_up_to_zero_distance = 0.0
         measured_lift_distance = 0.0
 
-        self.app.inform.emit('%s...' %
-                             _("Starting G-Code"))
+        self.app.inform.emit('%s...' % _("Starting G-Code"))
 
         current_platform = platform.architecture()[0]
         if current_platform == '64bit':
@@ -2667,8 +2672,7 @@ class CNCjob(Geometry):
                                         old_disp_number = disp_number
 
                             else:
-                                self.app.inform.emit('[ERROR_NOTCL] %s...' %
-                                                     _('G91 coordinates not implemented'))
+                                self.app.inform.emit('[ERROR_NOTCL] %s...' % _('G91 coordinates not implemented'))
                                 return 'fail'
                 else:
                     log.debug("camlib.CNCJob.generate_from_excellon_by_tool() --> "
@@ -2814,8 +2818,7 @@ class CNCjob(Geometry):
                                         old_disp_number = disp_number
 
                             else:
-                                self.app.inform.emit('[ERROR_NOTCL] %s...' %
-                                                     _('G91 coordinates not implemented'))
+                                self.app.inform.emit('[ERROR_NOTCL] %s...' % _('G91 coordinates not implemented'))
                                 return 'fail'
                 else:
                     log.debug("camlib.CNCJob.generate_from_excellon_by_tool() --> "
@@ -2920,8 +2923,7 @@ class CNCjob(Geometry):
                                     self.app.proc_container.update_view_text(' %d%%' % disp_number)
                                     old_disp_number = disp_number
                         else:
-                            self.app.inform.emit('[ERROR_NOTCL] %s...' %
-                                                 _('G91 coordinates not implemented'))
+                            self.app.inform.emit('[ERROR_NOTCL] %s...' %  _('G91 coordinates not implemented'))
                             return 'fail'
                     else:
                         log.debug("camlib.CNCJob.generate_from_excellon_by_tool() --> "
@@ -2998,10 +3000,10 @@ class CNCjob(Geometry):
 
         self.tooldia = float(tooldia) if tooldia else None
         self.z_cut = float(z_cut) if z_cut else None
-        self.z_move = float(z_move) if z_move 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 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.spindlespeed = int(spindlespeed) if spindlespeed else None
@@ -3009,13 +3011,13 @@ class CNCjob(Geometry):
         self.dwell = dwell
         self.dwelltime = float(dwelltime) if dwelltime else None
 
-        self.startz = float(startz) if startz else None
-        self.z_end = float(endz) if endz else None
+        self.startz = float(startz) if startz is not None else None
+        self.z_end = float(endz) if endz is not None else None
 
         self.z_depthpercut = float(depthpercut) if depthpercut else None
         self.multidepth = multidepth
 
-        self.z_toolchange = float(toolchangez) if toolchangez else None
+        self.z_toolchange = float(toolchangez) if toolchangez is not None else None
 
         # it servers in the postprocessor file
         self.tool = tool_no
@@ -3040,46 +3042,47 @@ class CNCjob(Geometry):
         if self.z_cut is None:
             self.app.inform.emit('[ERROR_NOTCL] %s' %
                                  _("Cut_Z parameter is None or zero. Most likely a bad combinations of "
-                                 "other parameters."))
+                                   "other parameters."))
             return 'fail'
 
-        if self.z_cut > 0:
-            self.app.inform.emit('[WARNING] %s' %
-                                 _("The Cut Z parameter has positive value. "
-                                 "It is the depth value to cut into material.\n"
-                                 "The Cut Z parameter needs to have a negative value, assuming it is a typo "
-                                 "therefore the app will convert the value to negative."
-                                 "Check the resulting CNC code (Gcode etc)."))
-            self.z_cut = -self.z_cut
-        elif self.z_cut == 0:
-            self.app.inform.emit('[WARNING] %s: %s' %
-                                 (_("The Cut Z parameter is zero. There will be no cut, skipping file"),
-                                  self.options['name']))
-            return 'fail'
+        if self.machinist_setting == 0:
+            if self.z_cut > 0:
+                self.app.inform.emit('[WARNING] %s' %
+                                     _("The Cut Z parameter has positive value. "
+                                       "It is the depth value to cut into material.\n"
+                                       "The Cut Z parameter needs to have a negative value, assuming it is a typo "
+                                       "therefore the app will convert the value to negative."
+                                       "Check the resulting CNC code (Gcode etc)."))
+                self.z_cut = -self.z_cut
+            elif self.z_cut == 0:
+                self.app.inform.emit('[WARNING] %s: %s' %
+                                     (_("The Cut Z parameter is zero. There will be no cut, skipping file"),
+                                      self.options['name']))
+                return 'fail'
+
+            if self.z_move is None:
+                self.app.inform.emit('[ERROR_NOTCL] %s' %
+                                     _("Travel Z parameter is None or zero."))
+                return 'fail'
+
+            if self.z_move < 0:
+                self.app.inform.emit('[WARNING] %s' %
+                                     _("The Travel Z parameter has negative value. "
+                                     "It is the height value to travel between cuts.\n"
+                                     "The Z Travel parameter needs to have a positive value, assuming it is a typo "
+                                     "therefore the app will convert the value to positive."
+                                     "Check the resulting CNC code (Gcode etc)."))
+                self.z_move = -self.z_move
+            elif self.z_move == 0:
+                self.app.inform.emit('[WARNING] %s: %s' %
+                                     (_("The Z Travel parameter is zero. This is dangerous, skipping file"),
+                                      self.options['name']))
+                return 'fail'
 
         # made sure that depth_per_cut is no more then the z_cut
         if abs(self.z_cut) < self.z_depthpercut:
             self.z_depthpercut = abs(self.z_cut)
 
-        if self.z_move is None:
-            self.app.inform.emit('[ERROR_NOTCL] %s' %
-                                 _("Travel Z parameter is None or zero."))
-            return 'fail'
-
-        if self.z_move < 0:
-            self.app.inform.emit('[WARNING] %s' %
-                                 _("The Travel Z parameter has negative value. "
-                                 "It is the height value to travel between cuts.\n"
-                                 "The Z Travel parameter needs to have a positive value, assuming it is a typo "
-                                 "therefore the app will convert the value to positive."
-                                 "Check the resulting CNC code (Gcode etc)."))
-            self.z_move = -self.z_move
-        elif self.z_move == 0:
-            self.app.inform.emit('[WARNING] %s: %s' %
-                                 (_("The Z Travel parameter is zero. This is dangerous, skipping file"),
-                                  self.options['name']))
-            return 'fail'
-
         # ## Index first and last points in paths
         # What points to index.
         def get_pts(o):
@@ -3352,11 +3355,11 @@ class CNCjob(Geometry):
         except ValueError:
             self.tooldia = [float(el) for el in tooldia.split(',') if el != ''] if tooldia else None
 
-        self.z_cut = float(z_cut) if z_cut else None
-        self.z_move = float(z_move) if z_move else None
+        self.z_cut = float(z_cut) if z_cut 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 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.spindlespeed = int(spindlespeed) if spindlespeed else None
@@ -3364,11 +3367,11 @@ class CNCjob(Geometry):
         self.dwell = dwell
         self.dwelltime = float(dwelltime) if dwelltime else None
 
-        self.startz = float(startz) if startz else None
-        self.z_end = float(endz) if endz else None
+        self.startz = float(startz) if startz is not None else None
+        self.z_end = float(endz) if endz is not None else None
         self.z_depthpercut = float(depthpercut) if depthpercut else None
         self.multidepth = multidepth
-        self.z_toolchange = float(toolchangez) if toolchangez else None
+        self.z_toolchange = float(toolchangez) if toolchangez is not None else None
 
         try:
             if toolchangexy == '':
@@ -3387,44 +3390,45 @@ class CNCjob(Geometry):
         self.pp_geometry_name = pp_geometry_name if pp_geometry_name else 'default'
         self.f_plunge = self.app.defaults["geometry_f_plunge"]
 
-        if self.z_cut is None:
-            self.app.inform.emit('[ERROR_NOTCL] %s' %
-                                 _("Cut_Z parameter is None or zero. Most likely a bad combinations of "
-                                   "other parameters."))
-            return 'fail'
-
-        if self.z_cut > 0:
-            self.app.inform.emit('[WARNING] %s' %
-                                 _("The Cut Z parameter has positive value. "
-                                   "It is the depth value to cut into material.\n"
-                                   "The Cut Z parameter needs to have a negative value, assuming it is a typo "
-                                   "therefore the app will convert the value to negative."
-                                   "Check the resulting CNC code (Gcode etc)."))
-            self.z_cut = -self.z_cut
-        elif self.z_cut == 0:
-            self.app.inform.emit('[WARNING] %s: %s' %
-                                 (_("The Cut Z parameter is zero. There will be no cut, skipping file"),
-                                  geometry.options['name']))
-            return 'fail'
-
-        if self.z_move is None:
-            self.app.inform.emit('[ERROR_NOTCL] %s' %
-                                 _("Travel Z parameter is None or zero."))
-            return 'fail'
-
-        if self.z_move < 0:
-            self.app.inform.emit('[WARNING] %s' %
-                                 _("The Travel Z parameter has negative value. "
-                                   "It is the height value to travel between cuts.\n"
-                                   "The Z Travel parameter needs to have a positive value, assuming it is a typo "
-                                   "therefore the app will convert the value to positive."
-                                   "Check the resulting CNC code (Gcode etc)."))
-            self.z_move = -self.z_move
-        elif self.z_move == 0:
-            self.app.inform.emit('[WARNING] %s: %s' %
-                                 (_("The Z Travel parameter is zero. "
-                                   "This is dangerous, skipping file"), self.options['name']))
-            return 'fail'
+        if self.machinist_setting == 0:
+            if self.z_cut is None:
+                self.app.inform.emit('[ERROR_NOTCL] %s' %
+                                     _("Cut_Z parameter is None or zero. Most likely a bad combinations of "
+                                       "other parameters."))
+                return 'fail'
+
+            if self.z_cut > 0:
+                self.app.inform.emit('[WARNING] %s' %
+                                     _("The Cut Z parameter has positive value. "
+                                       "It is the depth value to cut into material.\n"
+                                       "The Cut Z parameter needs to have a negative value, assuming it is a typo "
+                                       "therefore the app will convert the value to negative."
+                                       "Check the resulting CNC code (Gcode etc)."))
+                self.z_cut = -self.z_cut
+            elif self.z_cut == 0:
+                self.app.inform.emit('[WARNING] %s: %s' %
+                                     (_("The Cut Z parameter is zero. There will be no cut, skipping file"),
+                                      geometry.options['name']))
+                return 'fail'
+
+            if self.z_move is None:
+                self.app.inform.emit('[ERROR_NOTCL] %s' %
+                                     _("Travel Z parameter is None or zero."))
+                return 'fail'
+
+            if self.z_move < 0:
+                self.app.inform.emit('[WARNING] %s' %
+                                     _("The Travel Z parameter has negative value. "
+                                       "It is the height value to travel between cuts.\n"
+                                       "The Z Travel parameter needs to have a positive value, assuming it is a typo "
+                                       "therefore the app will convert the value to positive."
+                                       "Check the resulting CNC code (Gcode etc)."))
+                self.z_move = -self.z_move
+            elif self.z_move == 0:
+                self.app.inform.emit('[WARNING] %s: %s' %
+                                     (_("The Z Travel parameter is zero. "
+                                       "This is dangerous, skipping file"), self.options['name']))
+                return 'fail'
 
         # made sure that depth_per_cut is no more then the z_cut
         if abs(self.z_cut) < self.z_depthpercut:
@@ -3586,12 +3590,9 @@ class CNCjob(Geometry):
         self.gcode += self.doformat(p.spindle_stop_code)
         self.gcode += self.doformat(p.lift_code, x=current_pt[0], y=current_pt[1])
         self.gcode += self.doformat(p.end_code, x=0, y=0)
-        self.app.inform.emit('%s... %s %s' %
-                             (_("Finished G-Code generation"),
-                              str(path_count),
-                             _(" paths traced.")
-                              )
-                             )
+        self.app.inform.emit(
+            '%s... %s %s' % (_("Finished G-Code generation"), str(path_count), _(" paths traced."))
+        )
 
         return self.gcode
 

+ 1 - 1
flatcamGUI/GUIElements.py

@@ -12,7 +12,7 @@
 # ##########################################################
 
 from PyQt5 import QtGui, QtCore, QtWidgets
-from PyQt5.QtCore import Qt, pyqtSlot
+from PyQt5.QtCore import Qt, pyqtSlot, QSettings
 from PyQt5.QtWidgets import QTextEdit, QCompleter, QAction
 from PyQt5.QtGui import QKeySequence, QTextCursor
 

+ 54 - 9
flatcamGUI/ObjectUI.py

@@ -22,6 +22,12 @@ fcTranslate.apply_language('strings')
 if '_' not in builtins.__dict__:
     _ = gettext.gettext
 
+settings = QtCore.QSettings("Open Source", "FlatCAM")
+if settings.contains("machinist"):
+    machinist_setting = settings.value('machinist', type=int)
+else:
+    machinist_setting = 0
+
 
 class ObjectUI(QtWidgets.QWidget):
     """
@@ -754,7 +760,12 @@ class ExcellonObjectUI(ObjectUI):
         grid1.addWidget(cutzlabel, 0, 0)
         self.cutz_entry = FCDoubleSpinner()
         self.cutz_entry.set_precision(self.decimals)
-        self.cutz_entry.setRange(-9999.9999, -0.000001)
+
+        if machinist_setting == 0:
+            self.cutz_entry.setRange(-9999.9999, -0.000001)
+        else:
+            self.cutz_entry.setRange(-9999.9999, 9999.9999)
+
         self.cutz_entry.setSingleStep(0.1)
 
         grid1.addWidget(self.cutz_entry, 0, 1)
@@ -768,7 +779,12 @@ class ExcellonObjectUI(ObjectUI):
         grid1.addWidget(travelzlabel, 1, 0)
         self.travelz_entry = FCDoubleSpinner()
         self.travelz_entry.set_precision(self.decimals)
-        self.travelz_entry.setRange(0.0, 9999.9999)
+
+        if machinist_setting == 0:
+            self.travelz_entry.setRange(0.00001, 9999.9999)
+        else:
+            self.travelz_entry.setRange(-9999.9999, 9999.9999)
+
         self.travelz_entry.setSingleStep(0.1)
 
         grid1.addWidget(self.travelz_entry, 1, 1)
@@ -790,7 +806,12 @@ class ExcellonObjectUI(ObjectUI):
         grid1.addWidget(toolchzlabel, 3, 0)
         self.toolchangez_entry = FCDoubleSpinner()
         self.toolchangez_entry.set_precision(self.decimals)
-        self.toolchangez_entry.setRange(0.0, 9999.9999)
+
+        if machinist_setting == 0:
+            self.toolchangez_entry.setRange(0.0, 9999.9999)
+        else:
+            self.toolchangez_entry.setRange(-9999.9999, 9999.9999)
+
         self.toolchangez_entry.setSingleStep(0.1)
 
         grid1.addWidget(self.toolchangez_entry, 3, 1)
@@ -815,7 +836,12 @@ class ExcellonObjectUI(ObjectUI):
         grid1.addWidget(self.eendz_label, 5, 0)
         self.eendz_entry = FCDoubleSpinner()
         self.eendz_entry.set_precision(self.decimals)
-        self.eendz_entry.setRange(0.0, 9999.9999)
+
+        if machinist_setting == 0:
+            self.eendz_entry.setRange(0.0, 9999.9999)
+        else:
+            self.eendz_entry.setRange(-9999.9999, 9999.9999)
+
         self.eendz_entry.setSingleStep(0.1)
 
         grid1.addWidget(self.eendz_entry, 5, 1)
@@ -1166,7 +1192,6 @@ class GeometryObjectUI(ObjectUI):
         self.grid1.addWidget(self.tool_offset_lbl, 0, 0)
         self.grid1.addWidget(self.tool_offset_entry, 0, 1, 1, 2)
 
-
         self.addtool_entry_lbl = QtWidgets.QLabel('<b>%s:</b>' % _('Tool Dia'))
         self.addtool_entry_lbl.setToolTip(
             _("Diameter for the new tool")
@@ -1279,7 +1304,12 @@ class GeometryObjectUI(ObjectUI):
         )
         self.cutz_entry = FCDoubleSpinner()
         self.cutz_entry.set_precision(self.decimals)
-        self.cutz_entry.setRange(-9999.9999, -0.00001)
+
+        if machinist_setting == 0:
+            self.cutz_entry.setRange(-9999.9999, -0.00001)
+        else:
+            self.cutz_entry.setRange(-9999.9999, 9999.9999)
+
         self.cutz_entry.setSingleStep(0.1)
 
         self.grid3.addWidget(cutzlabel, 3, 0)
@@ -1319,7 +1349,12 @@ class GeometryObjectUI(ObjectUI):
         )
         self.travelz_entry = FCDoubleSpinner()
         self.travelz_entry.set_precision(self.decimals)
-        self.travelz_entry.setRange(0, 9999.9999)
+
+        if machinist_setting == 0:
+            self.travelz_entry.setRange(0.00001, 9999.9999)
+        else:
+            self.travelz_entry.setRange(-9999.9999, 9999.9999)
+
         self.travelz_entry.setSingleStep(0.1)
 
         self.grid3.addWidget(travelzlabel, 5, 0)
@@ -1342,7 +1377,12 @@ class GeometryObjectUI(ObjectUI):
         )
         self.toolchangez_entry = FCDoubleSpinner()
         self.toolchangez_entry.set_precision(self.decimals)
-        self.toolchangez_entry.setRange(0, 9999.9999)
+
+        if machinist_setting == 0:
+            self.toolchangez_entry.setRange(0, 9999.9999)
+        else:
+            self.toolchangez_entry.setRange(-9999.9999, 9999.9999)
+
         self.toolchangez_entry.setSingleStep(0.1)
 
         self.grid3.addWidget(self.toolchangeg_cb, 6, 0, 1, 2)
@@ -1369,7 +1409,12 @@ class GeometryObjectUI(ObjectUI):
         )
         self.gendz_entry = FCDoubleSpinner()
         self.gendz_entry.set_precision(self.decimals)
-        self.gendz_entry.setRange(0, 9999.9999)
+
+        if machinist_setting == 0:
+            self.gendz_entry.setRange(0, 9999.9999)
+        else:
+            self.gendz_entry.setRange(-9999.9999, 9999.9999)
+
         self.gendz_entry.setSingleStep(0.1)
 
         self.grid3.addWidget(self.endzlabel, 9, 0)

+ 64 - 8
flatcamGUI/PreferencesUI.py

@@ -18,6 +18,12 @@ fcTranslate.apply_language('strings')
 if '_' not in builtins.__dict__:
     _ = gettext.gettext
 
+settings = QtCore.QSettings("Open Source", "FlatCAM")
+if settings.contains("machinist"):
+    machinist_setting = settings.value('machinist', type=int)
+else:
+    machinist_setting = 0
+
 
 class OptionsGroupUI(QtWidgets.QGroupBox):
     def __init__(self, title, parent=None):
@@ -1166,6 +1172,7 @@ class GeneralAppPrefGroupUI(OptionsGroupUI):
 
         self.proj_ois = OptionalInputSection(self.save_type_cb, [self.compress_label, self.compress_spinner], True)
 
+        # Bookmarks Limit in the Help Menu
         self.bm_limit_spinner = FCSpinner()
         self.bm_limit_label = QtWidgets.QLabel('%s:' % _('Bookmarks limit'))
         self.bm_limit_label.setToolTip(
@@ -1177,6 +1184,18 @@ class GeneralAppPrefGroupUI(OptionsGroupUI):
         grid0.addWidget(self.bm_limit_label, 18, 0)
         grid0.addWidget(self.bm_limit_spinner, 18, 1)
 
+        # Machinist settings that allow unsafe settings
+        self.machinist_cb = FCCheckBox(_("Allow Machinist Unsafe Settings"))
+        self.machinist_cb.setToolTip(
+            _("If checked, some of the application settings will be allowed\n"
+              "to have values that are usually unsafe to use.\n"
+              "Like Z travel negative values or Z Cut positive values.\n"
+              "It will applied at the next application start.\n"
+              "<<WARNING>>: Don't change this unless you know what you are doing !!!")
+        )
+
+        grid0.addWidget(self.machinist_cb, 19, 0, 1, 2)
+
         self.layout.addStretch()
 
         if sys.platform != 'win32':
@@ -2154,7 +2173,12 @@ class ExcellonOptPrefGroupUI(OptionsGroupUI):
         )
         grid2.addWidget(cutzlabel, 0, 0)
         self.cutz_entry = FCDoubleSpinner()
-        self.cutz_entry.set_range(-9999, -0.000001)
+
+        if machinist_setting == 0:
+            self.cutz_entry.set_range(-9999.9999, -0.000001)
+        else:
+            self.cutz_entry.set_range(-9999.9999, 9999.9999)
+
         self.cutz_entry.setSingleStep(0.1)
         self.cutz_entry.set_precision(4)
         grid2.addWidget(self.cutz_entry, 0, 1)
@@ -2168,7 +2192,11 @@ class ExcellonOptPrefGroupUI(OptionsGroupUI):
         grid2.addWidget(travelzlabel, 1, 0)
         self.travelz_entry = FCDoubleSpinner()
         self.travelz_entry.set_precision(4)
-        self.travelz_entry.set_range(0, 999)
+
+        if machinist_setting == 0:
+            self.travelz_entry.set_range(0.0001, 9999.9999)
+        else:
+            self.travelz_entry.set_range(-9999.9999, 9999.9999)
 
         grid2.addWidget(self.travelz_entry, 1, 1)
 
@@ -2190,7 +2218,11 @@ class ExcellonOptPrefGroupUI(OptionsGroupUI):
         grid2.addWidget(toolchangezlabel, 3, 0)
         self.toolchangez_entry = FCDoubleSpinner()
         self.toolchangez_entry.set_precision(4)
-        self.toolchangez_entry.set_range(0, 999)
+
+        if machinist_setting == 0:
+            self.toolchangez_entry.set_range(0.0001, 9999.9999)
+        else:
+            self.toolchangez_entry.set_range(-9999.9999, 9999.9999)
 
         grid2.addWidget(self.toolchangez_entry, 3, 1)
 
@@ -2202,7 +2234,11 @@ class ExcellonOptPrefGroupUI(OptionsGroupUI):
         )
         self.eendz_entry = FCDoubleSpinner()
         self.eendz_entry.set_precision(4)
-        self.eendz_entry.set_range(0, 999)
+
+        if machinist_setting == 0:
+            self.eendz_entry.set_range(0.0000, 9999.9999)
+        else:
+            self.eendz_entry.set_range(-9999.9999, 9999.9999)
 
         grid2.addWidget(endzlabel, 4, 0)
         grid2.addWidget(self.eendz_entry, 4, 1)
@@ -2975,7 +3011,12 @@ class GeometryOptPrefGroupUI(OptionsGroupUI):
               "below the copper surface.")
         )
         self.cutz_entry = FCDoubleSpinner()
-        self.cutz_entry.set_range(-999.999, -0.000001)
+
+        if machinist_setting == 0:
+            self.cutz_entry.set_range(-9999.9999, -0.000001)
+        else:
+            self.cutz_entry.set_range(-9999.9999, 9999.9999)
+
         self.cutz_entry.set_precision(4)
         self.cutz_entry.setSingleStep(0.1)
         self.cutz_entry.setWrapping(True)
@@ -3023,7 +3064,12 @@ class GeometryOptPrefGroupUI(OptionsGroupUI):
               "moving without cutting.")
         )
         self.travelz_entry = FCDoubleSpinner()
-        self.travelz_entry.set_range(0, 99999)
+
+        if machinist_setting == 0:
+            self.travelz_entry.set_range(0.0001, 9999.9999)
+        else:
+            self.travelz_entry.set_range(-9999.9999, 9999.9999)
+
         self.travelz_entry.set_precision(4)
         self.travelz_entry.setSingleStep(0.1)
         self.travelz_entry.setWrapping(True)
@@ -3052,7 +3098,12 @@ class GeometryOptPrefGroupUI(OptionsGroupUI):
             )
         )
         self.toolchangez_entry = FCDoubleSpinner()
-        self.toolchangez_entry.set_range(0, 99999)
+
+        if machinist_setting == 0:
+            self.toolchangez_entry.set_range(0.000, 9999.9999)
+        else:
+            self.toolchangez_entry.set_range(-9999.9999, 9999.9999)
+
         self.toolchangez_entry.set_precision(4)
         self.toolchangez_entry.setSingleStep(0.1)
         self.toolchangez_entry.setWrapping(True)
@@ -3067,7 +3118,12 @@ class GeometryOptPrefGroupUI(OptionsGroupUI):
               "the last move at the end of the job.")
         )
         self.gendz_entry = FCDoubleSpinner()
-        self.gendz_entry.set_range(0, 99999)
+
+        if machinist_setting == 0:
+            self.gendz_entry.set_range(0.000, 9999.9999)
+        else:
+            self.gendz_entry.set_range(-9999.9999, 9999.9999)
+
         self.gendz_entry.set_precision(4)
         self.gendz_entry.setSingleStep(0.1)
         self.gendz_entry.setWrapping(True)