Explorar o código

jpcgt/flatcam/Beta слито с Beta

Camellan %!s(int64=5) %!d(string=hai) anos
pai
achega
4a650d9c54
Modificáronse 100 ficheiros con 12629 adicións e 10475 borrados
  1. 53 0
      CHANGELOG.md
  2. 8 4
      FlatCAM.py
  3. 250 193
      FlatCAMApp.py
  4. 12 2
      FlatCAMBookmark.py
  5. 41 1
      FlatCAMDB.py
  6. 1 1
      FlatCAMTranslation.py
  7. 28 0
      assets/examples/cutout_a_gerber.FlatScript
  8. 69 0
      assets/examples/files/test.gbr
  9. 28 0
      assets/examples/files/test.txt
  10. 30 0
      assets/examples/files/test_1.gbr
  11. 26 0
      assets/examples/isolate_gerber.FlatScript
  12. 26 0
      assets/examples/open_file.FlatScript
  13. BIN=BIN
      assets/resources/dark_resources/flatcam_icon32_green.png
  14. BIN=BIN
      assets/resources/dark_resources/snap_16.png
  15. BIN=BIN
      assets/resources/dark_resources/snap_filled_16.png
  16. BIN=BIN
      assets/resources/snap_16.png
  17. BIN=BIN
      assets/resources/snap_filled_16.png
  18. 9 14
      camlib.py
  19. 7 2
      defaults.py
  20. 3 3
      flatcamEditors/FlatCAMExcEditor.py
  21. 1 1
      flatcamEditors/FlatCAMGeoEditor.py
  22. 357 191
      flatcamEditors/FlatCAMGrbEditor.py
  23. 2 2
      flatcamEditors/FlatCAMTextEditor.py
  24. 91 9
      flatcamGUI/FlatCAMGUI.py
  25. 3 3
      flatcamGUI/GUIElements.py
  26. 87 7
      flatcamGUI/ObjectUI.py
  27. 0 10003
      flatcamGUI/PreferencesUI.py
  28. 1 1
      flatcamGUI/VisPyVisuals.py
  29. 19 0
      flatcamGUI/preferences/OptionsGroupUI.py
  30. 1135 0
      flatcamGUI/preferences/PreferencesUIManager.py
  31. 15 0
      flatcamGUI/preferences/__init__.py
  32. 208 0
      flatcamGUI/preferences/cncjob/CNCJobAdvOptPrefGroupUI.py
  33. 389 0
      flatcamGUI/preferences/cncjob/CNCJobGenPrefGroupUI.py
  34. 80 0
      flatcamGUI/preferences/cncjob/CNCJobOptPrefGroupUI.py
  35. 27 0
      flatcamGUI/preferences/cncjob/CNCJobPreferencesUI.py
  36. 0 0
      flatcamGUI/preferences/cncjob/__init__.py
  37. 155 0
      flatcamGUI/preferences/excellon/ExcellonAdvOptPrefGroupUI.py
  38. 306 0
      flatcamGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py
  39. 168 0
      flatcamGUI/preferences/excellon/ExcellonExpPrefGroupUI.py
  40. 415 0
      flatcamGUI/preferences/excellon/ExcellonGenPrefGroupUI.py
  41. 317 0
      flatcamGUI/preferences/excellon/ExcellonOptPrefGroupUI.py
  42. 53 0
      flatcamGUI/preferences/excellon/ExcellonPreferencesUI.py
  43. 0 0
      flatcamGUI/preferences/excellon/__init__.py
  44. 483 0
      flatcamGUI/preferences/general/GeneralAPPSetGroupUI.py
  45. 402 0
      flatcamGUI/preferences/general/GeneralAppPrefGroupUI.py
  46. 787 0
      flatcamGUI/preferences/general/GeneralGUIPrefGroupUI.py
  47. 43 0
      flatcamGUI/preferences/general/GeneralPreferencesUI.py
  48. 0 0
      flatcamGUI/preferences/general/__init__.py
  49. 246 0
      flatcamGUI/preferences/geometry/GeometryAdvOptPrefGroupUI.py
  50. 67 0
      flatcamGUI/preferences/geometry/GeometryEditorPrefGroupUI.py
  51. 123 0
      flatcamGUI/preferences/geometry/GeometryGenPrefGroupUI.py
  52. 265 0
      flatcamGUI/preferences/geometry/GeometryOptPrefGroupUI.py
  53. 46 0
      flatcamGUI/preferences/geometry/GeometryPreferencesUI.py
  54. 0 0
      flatcamGUI/preferences/geometry/__init__.py
  55. 186 0
      flatcamGUI/preferences/gerber/GerberAdvOptPrefGroupUI.py
  56. 247 0
      flatcamGUI/preferences/gerber/GerberEditorPrefGroupUI.py
  57. 118 0
      flatcamGUI/preferences/gerber/GerberExpPrefGroupUI.py
  58. 273 0
      flatcamGUI/preferences/gerber/GerberGenPrefGroupUI.py
  59. 187 0
      flatcamGUI/preferences/gerber/GerberOptPrefGroupUI.py
  60. 53 0
      flatcamGUI/preferences/gerber/GerberPreferencesUI.py
  61. 0 0
      flatcamGUI/preferences/gerber/__init__.py
  62. 274 0
      flatcamGUI/preferences/tools/Tools2CThievingPrefGroupUI.py
  63. 138 0
      flatcamGUI/preferences/tools/Tools2CalPrefGroupUI.py
  64. 231 0
      flatcamGUI/preferences/tools/Tools2EDrillsPrefGroupUI.py
  65. 135 0
      flatcamGUI/preferences/tools/Tools2FiducialsPrefGroupUI.py
  66. 75 0
      flatcamGUI/preferences/tools/Tools2InvertPrefGroupUI.py
  67. 56 0
      flatcamGUI/preferences/tools/Tools2OptimalPrefGroupUI.py
  68. 89 0
      flatcamGUI/preferences/tools/Tools2PreferencesUI.py
  69. 233 0
      flatcamGUI/preferences/tools/Tools2PunchGerberPrefGroupUI.py
  70. 207 0
      flatcamGUI/preferences/tools/Tools2QRCodePrefGroupUI.py
  71. 242 0
      flatcamGUI/preferences/tools/Tools2RulesCheckPrefGroupUI.py
  72. 92 0
      flatcamGUI/preferences/tools/Tools2sidedPrefGroupUI.py
  73. 142 0
      flatcamGUI/preferences/tools/ToolsCalculatorsPrefGroupUI.py
  74. 177 0
      flatcamGUI/preferences/tools/ToolsCutoutPrefGroupUI.py
  75. 316 0
      flatcamGUI/preferences/tools/ToolsFilmPrefGroupUI.py
  76. 349 0
      flatcamGUI/preferences/tools/ToolsNCCPrefGroupUI.py
  77. 313 0
      flatcamGUI/preferences/tools/ToolsPaintPrefGroupUI.py
  78. 146 0
      flatcamGUI/preferences/tools/ToolsPanelizePrefGroupUI.py
  79. 94 0
      flatcamGUI/preferences/tools/ToolsPreferencesUI.py
  80. 246 0
      flatcamGUI/preferences/tools/ToolsSolderpastePrefGroupUI.py
  81. 42 0
      flatcamGUI/preferences/tools/ToolsSubPrefGroupUI.py
  82. 249 0
      flatcamGUI/preferences/tools/ToolsTransformPrefGroupUI.py
  83. 0 0
      flatcamGUI/preferences/tools/__init__.py
  84. 83 0
      flatcamGUI/preferences/utilities/AutoCompletePrefGroupUI.py
  85. 102 0
      flatcamGUI/preferences/utilities/FAExcPrefGroupUI.py
  86. 89 0
      flatcamGUI/preferences/utilities/FAGcoPrefGroupUI.py
  87. 89 0
      flatcamGUI/preferences/utilities/FAGrbPrefGroupUI.py
  88. 37 0
      flatcamGUI/preferences/utilities/UtilPreferencesUI.py
  89. 0 0
      flatcamGUI/preferences/utilities/__init__.py
  90. 1 1
      flatcamObjects/FlatCAMCNCJob.py
  91. 268 1
      flatcamObjects/FlatCAMGeometry.py
  92. 0 2
      flatcamObjects/FlatCAMGerber.py
  93. 33 6
      flatcamObjects/FlatCAMScript.py
  94. 1 1
      flatcamParsers/ParseExcellon.py
  95. 4 0
      flatcamParsers/ParseHPGL2.py
  96. 1 1
      flatcamTools/ToolCalculators.py
  97. 114 21
      flatcamTools/ToolCutOut.py
  98. 1 1
      flatcamTools/ToolFilm.py
  99. 7 2
      flatcamTools/ToolNCC.py
  100. 7 2
      flatcamTools/ToolPaint.py

+ 53 - 0
CHANGELOG.md

@@ -7,6 +7,54 @@ CHANGELOG for FlatCAM beta
 
 =================================================
 
+3.05.2020
+
+- small changes to allow making the x86 installer that is made from a Python 3.5 run FlatCAM beta 
+- fixed multiple parameter 'outname' in the Tcl commands OpenGerber and OpenGcode 
+- added more examples in the scripts Examples: isolate and cutout examples
+- updated the Italian translation
+- updated the translation files
+- changed the line endings for Makefile and setup_ubuntu.sh files
+- protected a dict in VispyVisuals from issuing errors of keys changed while iterating through it
+
+2.05.2020
+
+- working on a new feature: adding interdiction area for Gcode generation. They will be added in the Geometry Object
+
+2.05.2020
+
+- changed the icons for the grid snap in the status bar
+- moved some of the methods from FlatCAMApp.App to flatcamGUI.FlatCAMGUI class
+- fixed bug in Gerber Editor in which the units conversion wasn't calculated correct
+- fixed bug in Gerber Editor in which the QThread that is started on object edit was not stopped at clean up stage
+- fixed bug in Gerber Editor that kept all the apertures (including the geometry) of a previously edited object that was not saved after edit
+- modified the Cutout Tool to generate multi-geo objects therefore the set geometry parameters will populate the Geometry Object UI
+- modified the Panelize Tool to optimize the output from Cutout Tool such that there are no longer overlapping cuts
+- some string corrections
+- updated the Italian translation done by user @pcb-hobbyst (Golfetto Massimiliano)
+- RELEASE 8.992
+
+01.05.2020
+
+- added some ToolTips (strings needed to be translated too) for the Cut Z entry in Geometry Object UI that explain why is sometime disabled and reason for it's value (sometime is zero)
+- solve parenting issues when trying to load a FlatScript from Menu -> File -> Scripting
+- added a first new example script and added some files to work with
+- added a new parameter that will store the home folder of the FlatCAM installation so we can access the example folder
+- added in Gerber editor a method for zoom fit that takes into consideration the current geometry of the edited object
+
+30.04.2020 
+
+- made some corrections - due of recent refactoring PyCharm reported errors all over (not correct but it made programming difficult)
+- modified the requirements.txt file to force svg.path module to be at least version 4.0
+- fixed bug in Tools DB that crashed when a tool is copied
+- in Tools Database added a Save Button whose color is changed in Red if the DB was modified and back to default when the DB is saved.
+- fixed bug in Tool DB that crashed the app when the Tool Name was modified but there was no tree item (a tool in the list) selected in the Tree widget (list of tools)
+- now on tool add and tool copy, the last item (tool, which is the one added) is autoselected; o tool delete always the first item (tool) is selected
+- fixed issue #409; problem was due of an assert I used in the handler of the Menu ->Options -> Flip X(Y) menu entry
+- activated and updated the editing in the Aperture Table in the Gerber Editor; not all parameters can be edited for every type of aperture
+- some strings updated
+- fixed a small issue in loading the Projects
+
 29.04.2020
 
 - added a try-except clause in the FlatCAMTranslation.restart_program() when closing the Listener and the thread that runs it to adjust to MacOS usage
@@ -21,6 +69,11 @@ CHANGELOG for FlatCAM beta
 - fixed a SyntaxError Exception when checking for types of found old preferences
 - updated the French, German and Spanish Google translations
 - updated the Romanian translation
+- fixed units conversion issue
+- updated the units conversion method to convert all the convertible parameters in the Preferences
+- solved the problem with not closing all the tabs in Plot Area when creating a New Project; the issue was that once a tab was removed the indexes are remade (when tab 0 is removed then tab 1 becomes tab 0 and so on)
+- some more strings changed -> updated the translations
+- replaced some FormLayouts with Gridlayouts in Tool Cutout.
 
 28.04.2020
 

+ 8 - 4
FlatCAM.py

@@ -43,11 +43,15 @@ if __name__ == '__main__':
         else:
             print("FlatCAM BETA uses PYTHON 3 or later. The version minimum is %s.%s\n"
                   "Your Python version is: %s.%s" % (MIN_VERSION_MAJOR, MIN_VERSION_MINOR, str(major_v), str(minor_v)))
-            os._exit(0)
+
+            if minor_v >= 8:
+                os._exit(0)
+            else:
+                sys.exit(0)
     else:
         print("FlatCAM BETA uses PYTHON 3 or later. The version minimum is %s.%s\n"
               "Your Python version is: %s.%s" % (MIN_VERSION_MAJOR, MIN_VERSION_MINOR, str(major_v), str(minor_v)))
-        os._exit(0)
+        sys.exit(0)
 
     debug_trace()
     VisPyPatches.apply_patches()
@@ -86,5 +90,5 @@ if __name__ == '__main__':
         app.setStyle(style)
 
     fc = App()
-    # sys.exit(app.exec_())
-    app.exec_()
+    sys.exit(app.exec_())
+    # app.exec_()

+ 250 - 193
FlatCAMApp.py

@@ -20,6 +20,7 @@ import time
 import ctypes
 import traceback
 
+from PyQt5.QtCore import pyqtSlot, Qt
 from shapely.geometry import Point, MultiPolygon
 from io import StringIO
 
@@ -51,6 +52,8 @@ from vispy.io import write_png
 
 # FlatCAM Objects
 from defaults import FlatCAMDefaults
+from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
+from flatcamGUI.preferences.PreferencesUIManager import PreferencesUIManager
 from flatcamObjects.ObjectCollection import *
 from flatcamObjects.FlatCAMObj import FlatCAMObj
 from flatcamObjects.FlatCAMCNCJob import CNCJobObject
@@ -95,6 +98,7 @@ import builtins
 
 if sys.platform == 'win32':
     import winreg
+    from win32comext.shell import shell, shellcon
 
 fcTranslate.apply_language('strings')
 if '_' not in builtins.__dict__:
@@ -158,9 +162,10 @@ class App(QtCore.QObject):
     # ###############################################################################################################
     # ################################### Version and VERSION DATE ##################################################
     # ###############################################################################################################
-    version = 8.992
-    version_date = "2020/05/01"
+    version = 8.993
+    version_date = "2020/08/01"
     beta = True
+
     engine = '3D'
 
     # current date now
@@ -304,7 +309,6 @@ class App(QtCore.QObject):
 
         # Folder for user settings.
         if sys.platform == 'win32':
-            from win32comext.shell import shell, shellcon
             if platform.architecture()[0] == '32bit':
                 App.log.debug("Win32!")
             else:
@@ -429,6 +433,9 @@ class App(QtCore.QObject):
         # ################################# DEFAULTS - PREFERENCES STORAGE ###########################################
         # ############################################################################################################
         self.defaults = FlatCAMDefaults()
+
+        self.defaults["root_folder_path"] = self.app_home
+
         current_defaults_path = os.path.join(self.data_path, "current_defaults.FlatConfig")
         if user_defaults:
             self.defaults.load(filename=current_defaults_path)
@@ -500,7 +507,6 @@ class App(QtCore.QObject):
         QtCore.QObject.__init__(self)
 
         self.ui = FlatCAMGUI(self)
-        self.on_grid_snap_triggered(state=True)
 
         theme_settings = QtCore.QSettings("Open Source", "FlatCAM")
         if theme_settings.contains("theme"):
@@ -1068,9 +1074,6 @@ class App(QtCore.QObject):
         # signal emitted when a tab is closed in the Plot Area
         self.ui.plot_tab_area.tab_closed_signal.connect(self.on_plot_area_tab_closed)
 
-        self.ui.grid_snap_btn.triggered.connect(self.on_grid_snap_triggered)
-        self.ui.snap_infobar_label.clicked.connect(self.on_grid_icon_snap_clicked)
-
         # signal to close the application
         self.close_app_signal.connect(self.kill_app)
         # ################################# FINISHED CONNECTING SIGNALS #############################################
@@ -2726,7 +2729,7 @@ class App(QtCore.QObject):
         # Re-build the recent items menu
         self.setup_recent_items()
 
-    def new_object(self, kind, name, initialize, active=True, fit=True, plot=True, autoselected=True):
+    def new_object(self, kind, name, initialize, plot=True, autoselected=True):
         """
         Creates a new specialized FlatCAMObj and attaches it to the application,
         this is, updates the GUI accordingly, any other records and plots it.
@@ -2744,8 +2747,6 @@ class App(QtCore.QObject):
         :param initialize: Function to run after creation of the object but before it is attached to the application.
         The function is called with 2 parameters: the new object and the App instance.
         :type initialize: function
-        :param active:
-        :param fit:
         :param plot: If to plot the resulting object
         :param autoselected: if the resulting object is autoselected in the Project tab and therefore in the
         self.collection
@@ -2907,45 +2908,37 @@ class App(QtCore.QObject):
 
         self.new_object('gerber', 'new_grb', initialize, plot=False)
 
-    def new_script_object(self, name=None, text=None):
+    def new_script_object(self):
         """
         Creates a new, blank TCL Script object.
-        :param name: a name for the new object
-        :param text: pass a source file to the newly created script to be loaded in it
+
         :return: None
         """
         self.defaults.report_usage("new_script_object()")
 
-        if text is not None:
-            new_source_file = text
-        else:
-            # commands_list = "# AddCircle, AddPolygon, AddPolyline, AddRectangle, AlignDrill, " \
-            #                 "AlignDrillGrid, Bbox, Bounds, ClearShell, CopperClear,\n" \
-            #                 "# Cncjob, Cutout, Delete, Drillcncjob, ExportDXF, ExportExcellon, ExportGcode,\n" \
-            #                 "# ExportGerber, ExportSVG, Exteriors, Follow, GeoCutout, GeoUnion, GetNames,\n" \
-            #                 "# GetSys, ImportSvg, Interiors, Isolate, JoinExcellon, JoinGeometry, " \
-            #                 "ListSys, MillDrills,\n" \
-            #                 "# MillSlots, Mirror, New, NewExcellon, NewGeometry, NewGerber, Nregions, " \
-            #                 "Offset, OpenExcellon, OpenGCode, OpenGerber, OpenProject,\n" \
-            #                 "# Options, Paint, Panelize, PlotAl, PlotObjects, SaveProject, " \
-            #                 "SaveSys, Scale, SetActive, SetSys, SetOrigin, Skew, SubtractPoly,\n" \
-            #                 "# SubtractRectangle, Version, WriteGCode\n"
-
-            new_source_file = '# %s\n' % _('CREATE A NEW FLATCAM TCL SCRIPT') + \
-                              '# %s:\n' % _('TCL Tutorial is here') + \
-                              '# https://www.tcl.tk/man/tcl8.5/tutorial/tcltutorial.html\n' + '\n\n' + \
-                              '# %s:\n' % _("FlatCAM commands list")
-            new_source_file += '# %s\n\n' % _("Type >help< followed by Run Code for a list of FlatCAM Tcl Commands "
-                                              "(displayed in Tcl Shell).")
+        # commands_list = "# AddCircle, AddPolygon, AddPolyline, AddRectangle, AlignDrill, " \
+        #                 "AlignDrillGrid, Bbox, Bounds, ClearShell, CopperClear,\n" \
+        #                 "# Cncjob, Cutout, Delete, Drillcncjob, ExportDXF, ExportExcellon, ExportGcode,\n" \
+        #                 "# ExportGerber, ExportSVG, Exteriors, Follow, GeoCutout, GeoUnion, GetNames,\n" \
+        #                 "# GetSys, ImportSvg, Interiors, Isolate, JoinExcellon, JoinGeometry, " \
+        #                 "ListSys, MillDrills,\n" \
+        #                 "# MillSlots, Mirror, New, NewExcellon, NewGeometry, NewGerber, Nregions, " \
+        #                 "Offset, OpenExcellon, OpenGCode, OpenGerber, OpenProject,\n" \
+        #                 "# Options, Paint, Panelize, PlotAl, PlotObjects, SaveProject, " \
+        #                 "SaveSys, Scale, SetActive, SetSys, SetOrigin, Skew, SubtractPoly,\n" \
+        #                 "# SubtractRectangle, Version, WriteGCode\n"
+
+        new_source_file = '# %s\n' % _('CREATE A NEW FLATCAM TCL SCRIPT') + \
+                          '# %s:\n' % _('TCL Tutorial is here') + \
+                          '# https://www.tcl.tk/man/tcl8.5/tutorial/tcltutorial.html\n' + '\n\n' + \
+                          '# %s:\n' % _("FlatCAM commands list")
+        new_source_file += '# %s\n\n' % _("Type >help< followed by Run Code for a list of FlatCAM Tcl Commands "
+                                          "(displayed in Tcl Shell).")
 
         def initialize(obj, app):
             obj.source_file = deepcopy(new_source_file)
 
-        if name is None:
-            outname = 'new_script'
-        else:
-            outname = name
-
+        outname = 'new_script'
         self.new_object('script', outname, initialize, plot=False)
 
     def new_document_object(self):
@@ -3267,7 +3260,7 @@ class App(QtCore.QObject):
                 self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "<marius_adrian@yahoo.com>"), 4, 2)
                 self.prog_grid_lay.addWidget(QtWidgets.QLabel(''), 5, 0)
 
-                self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Alex Lazar"), 6, 0)
+                self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "David Robertson"), 6, 0)
                 self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Matthieu Berthomé"), 7, 0)
                 self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Mike Evans"), 8, 0)
                 self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Victor Benso"), 9, 0)
@@ -3299,6 +3292,7 @@ class App(QtCore.QObject):
 
                 self.prog_grid_lay.addWidget(QtWidgets.QLabel(''), 63, 0)
 
+                self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Alex Lazar"), 64, 0)
                 self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Chris Breneman"), 65, 0)
                 self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Eric Varsanyi"), 67, 0)
                 self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Lubos Medovarsky"), 69, 0)
@@ -3335,27 +3329,43 @@ class App(QtCore.QObject):
                 self.translator_grid_lay.addWidget(QtWidgets.QLabel('<b>%s</b>' % _("Translator")), 0, 1)
                 self.translator_grid_lay.addWidget(QtWidgets.QLabel('<b>%s</b>' % _("Corrections")), 0, 2)
                 self.translator_grid_lay.addWidget(QtWidgets.QLabel('<b>%s</b>' % _("E-mail")), 0, 3)
+
                 self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "BR - Portuguese"), 1, 0)
                 self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Carlos Stein"), 1, 1)
                 self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "<carlos.stein@gmail.com>"), 1, 3)
+
                 self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "French"), 2, 0)
                 self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Marius Stanciu (Google-Tr)"), 2, 1)
                 self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % ""), 2, 2)
                 self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % " "), 2, 3)
-                self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "German"), 3, 0)
-                self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Marius Stanciu (Google-Tr)"), 3, 1)
-                self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Jens Karstedt, Detlef Eckardt"), 3, 2)
+
+                self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Hungarian"), 3, 0)
+                self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % " "), 3, 1)
+                self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % " "), 3, 2)
                 self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % " "), 3, 3)
-                self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Romanian"), 4, 0)
-                self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Marius Stanciu"), 4, 1)
-                self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "<marius_adrian@yahoo.com>"), 4, 3)
-                self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Russian"), 5, 0)
-                self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Andrey Kultyapov"), 5, 1)
-                self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "<camellan@yandex.ru>"), 5, 3)
-                self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Spanish"), 6, 0)
-                self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Marius Stanciu (Google-Tr)"), 6, 1)
-                self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % ""), 6, 2)
-                self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % " "), 6, 3)
+
+                self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Italian"), 4, 0)
+                self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Golfetto Massimiliano"), 4, 1)
+                self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % " "), 4, 2)
+                self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "pcb@golfetto.eu"), 4, 3)
+
+                self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "German"), 5, 0)
+                self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Marius Stanciu (Google-Tr)"), 5, 1)
+                self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Jens Karstedt, Detlef Eckardt"), 5, 2)
+                self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % " "), 5, 3)
+
+                self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Romanian"), 6, 0)
+                self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Marius Stanciu"), 6, 1)
+                self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "<marius_adrian@yahoo.com>"), 6, 3)
+
+                self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Russian"), 7, 0)
+                self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Andrey Kultyapov"), 7, 1)
+                self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "<camellan@yandex.ru>"), 7, 3)
+
+                self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Spanish"), 8, 0)
+                self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Marius Stanciu (Google-Tr)"), 8, 1)
+                self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % ""), 8, 2)
+                self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % " "), 8, 3)
                 self.translator_grid_lay.setColumnStretch(0, 0)
                 self.translators_tab_layout.addStretch()
 
@@ -3597,9 +3607,11 @@ class App(QtCore.QObject):
 
         # When the main event loop is not started yet in which case the qApp.quit() will do nothing
         # we use the following command
-        # sys.exit(0)
-
-        os._exit(0)  # fix to work with Python 3.8
+        minor_v = sys.version_info.minor
+        if minor_v < 8:
+            sys.exit(0)
+        else:
+            os._exit(0)  # fix to work with Python 3.8
 
     @staticmethod
     def kill_app():
@@ -4117,9 +4129,11 @@ class App(QtCore.QObject):
         obj.multigeo = True
         for tooluid, dict_value in obj.tools.items():
             dict_value['solid_geometry'] = deepcopy(obj.solid_geometry)
+
         if not isinstance(obj.solid_geometry, list):
             obj.solid_geometry = [obj.solid_geometry]
-        obj.solid_geometry[:] = []
+
+        # obj.solid_geometry[:] = []
         obj.plot()
 
         self.should_we_save = True
@@ -4225,54 +4239,92 @@ class App(QtCore.QObject):
             return
 
         # Options to scale
-        dimensions = ['gerber_isotooldia', 'gerber_noncoppermargin', 'gerber_bboxmargin', "gerber_isooverlap",
-                      "gerber_editor_newsize", "gerber_editor_lin_pitch", "gerber_editor_buff_f",
+        dimensions = ['gerber_isotooldia', 'gerber_noncoppermargin', 'gerber_bboxmargin',
+                      "gerber_editor_newsize", "gerber_editor_lin_pitch", "gerber_editor_buff_f", "gerber_vtipdia",
+                      "gerber_vcutz", "gerber_editor_newdim", "gerber_editor_ma_low",
+                      "gerber_editor_ma_high",
 
                       'excellon_cutz', 'excellon_travelz', "excellon_toolchangexy", 'excellon_offset',
-                      'excellon_feedrate', 'excellon_feedrate_rapid', 'excellon_toolchangez',
+                      'excellon_feedrate_z', 'excellon_feedrate_rapid', 'excellon_toolchangez',
                       'excellon_tooldia', 'excellon_slot_tooldia', 'excellon_endz', 'excellon_endxy',
-                      "excellon_feedrate_probe",
+                      "excellon_feedrate_probe", "excellon_milling_dia",
                       "excellon_z_pdepth", "excellon_editor_newdia", "excellon_editor_lin_pitch",
-                      "excellon_editor_slot_lin_pitch",
+                      "excellon_editor_slot_lin_pitch", "excellon_editor_slot_length",
 
                       'geometry_cutz', "geometry_depthperpass", 'geometry_travelz', 'geometry_feedrate',
                       'geometry_feedrate_rapid', "geometry_toolchangez", "geometry_feedrate_z",
                       "geometry_toolchangexy", 'geometry_cnctooldia', 'geometry_endz', 'geometry_endxy',
-                      "geometry_z_pdepth",
-                      "geometry_feedrate_probe", "geometry_startz",
+                      "geometry_extracut_length", "geometry_z_pdepth",
+                      "geometry_feedrate_probe", "geometry_startz", "geometry_segx", "geometry_segy",
 
                       'cncjob_tooldia',
 
-                      'tools_paintmargin', 'tools_painttooldia', 'tools_paintoverlap',
-                      "tools_ncctools", "tools_nccoverlap", "tools_nccmargin", "tools_ncccutz", "tools_ncctipdia",
-                      "tools_nccnewdia",
-                      "tools_2sided_drilldia", "tools_film_boundary",
-                      "tools_cutouttooldia", 'tools_cutoutmargin', 'tools_cutoutgapsize',
-                      "tools_panelize_constrainx", "tools_panelize_constrainy",
+                      'tools_paintmargin', 'tools_painttooldia', "tools_paintcutz", "tools_painttipdia",
+                      "tools_paintnewdia",
+
+                      "tools_ncctools", "tools_nccmargin", "tools_ncccutz", "tools_ncctipdia",
+                      "tools_nccnewdia", "tools_ncc_offset_value",
+
+                      "tools_2sided_drilldia",
+                      "tools_film_boundary", "tools_film_scale_stroke",
+
+                      "tools_cutouttooldia", 'tools_cutoutmargin', 'tools_cutoutgapsize', "tools_cutout_z",
+                      "tools_cutout_depthperpass",
+
+                      "tools_panelize_constrainx", "tools_panelize_constrainy", "tools_panelize_spacing_columns",
+                      "tools_panelize_spacing_rows",
+
                       "tools_calc_vshape_tip_dia", "tools_calc_vshape_cut_z",
-                      "tools_transform_skew_x", "tools_transform_skew_y", "tools_transform_offset_x",
-                      "tools_transform_offset_y",
+
+                      "tools_transform_offset_x", "tools_transform_offset_y", "tools_transform_mirror_point",
+                      "tools_transform_buffer_dis",
 
                       "tools_solderpaste_tools", "tools_solderpaste_new", "tools_solderpaste_z_start",
                       "tools_solderpaste_z_dispense", "tools_solderpaste_z_stop", "tools_solderpaste_z_travel",
                       "tools_solderpaste_z_toolchange", "tools_solderpaste_xy_toolchange", "tools_solderpaste_frxy",
                       "tools_solderpaste_frz", "tools_solderpaste_frz_dispense",
+
                       "tools_cr_trace_size_val", "tools_cr_c2c_val", "tools_cr_c2o_val", "tools_cr_s2s_val",
                       "tools_cr_s2sm_val", "tools_cr_s2o_val", "tools_cr_sm2sm_val", "tools_cr_ri_val",
-                      "tools_cr_h2h_val", "tools_cr_dh_val", "tools_fiducials_dia", "tools_fiducials_margin",
-                      "tools_fiducials_line_thickness",
+                      "tools_cr_h2h_val", "tools_cr_dh_val",
+
+                      "tools_fiducials_dia", "tools_fiducials_margin", "tools_fiducials_line_thickness",
+
                       "tools_copper_thieving_clearance", "tools_copper_thieving_margin",
                       "tools_copper_thieving_dots_dia", "tools_copper_thieving_dots_spacing",
                       "tools_copper_thieving_squares_size", "tools_copper_thieving_squares_spacing",
                       "tools_copper_thieving_lines_size", "tools_copper_thieving_lines_spacing",
                       "tools_copper_thieving_rb_margin", "tools_copper_thieving_rb_thickness",
+                      "tools_copper_thieving_mask_clearance",
+
+                      "tools_cal_travelz", "tools_cal_verz", "tools_cal_toolchangez", "tools_cal_toolchange_xy",
+
+                      "tools_edrills_hole_fixed_dia", "tools_edrills_circular_ring", "tools_edrills_oblong_ring",
+                      "tools_edrills_square_ring", "tools_edrills_rectangular_ring", "tools_edrills_others_ring",
+
+                      "tools_punch_hole_fixed_dia", "tools_punch_circular_ring", "tools_punch_oblong_ring",
+                      "tools_punch_square_ring", "tools_punch_rectangular_ring", "tools_punch_others_ring",
+
+                      "tools_invert_margin",
 
                       'global_gridx', 'global_gridy', 'global_snap_max', "global_tolerance",
                       'global_tpdf_bmargin', 'global_tpdf_tmargin', 'global_tpdf_rmargin', 'global_tpdf_lmargin']
 
         def scale_defaults(sfactor):
             for dim in dimensions:
+
+                if dim == 'gerber_editor_newdim':
+                    if self.defaults["gerber_editor_newdim"] is None or self.defaults["gerber_editor_newdim"] == '':
+                        continue
+                    coordinates = self.defaults["gerber_editor_newdim"].split(",")
+                    coords_xy = [float(eval(a)) for a in coordinates if a != '']
+                    coords_xy[0] *= sfactor
+                    coords_xy[1] *= sfactor
+                    self.defaults['gerber_editor_newdim'] = "%.*f, %.*f" % (self.decimals, coords_xy[0],
+                                                                            self.decimals, coords_xy[1])
                 if dim == 'excellon_toolchangexy':
+                    if self.defaults["excellon_toolchangexy"] is None or self.defaults["excellon_toolchangexy"] == '':
+                        continue
                     coordinates = self.defaults["excellon_toolchangexy"].split(",")
                     coords_xy = [float(eval(a)) for a in coordinates if a != '']
                     coords_xy[0] *= sfactor
@@ -4280,6 +4332,8 @@ class App(QtCore.QObject):
                     self.defaults['excellon_toolchangexy'] = "%.*f, %.*f" % (self.decimals, coords_xy[0],
                                                                              self.decimals, coords_xy[1])
                 elif dim == 'geometry_toolchangexy':
+                    if self.defaults["geometry_toolchangexy"] is None or self.defaults["geometry_toolchangexy"] == '':
+                        continue
                     coordinates = self.defaults["geometry_toolchangexy"].split(",")
                     coords_xy = [float(eval(a)) for a in coordinates if a != '']
                     coords_xy[0] *= sfactor
@@ -4287,6 +4341,9 @@ class App(QtCore.QObject):
                     self.defaults['geometry_toolchangexy'] = "%.*f, %.*f" % (self.decimals, coords_xy[0],
                                                                              self.decimals, coords_xy[1])
                 elif dim == 'excellon_endxy':
+                    if self.defaults["excellon_endxy"] is None or self.defaults["excellon_endxy"] == '':
+                        continue
+
                     coordinates = self.defaults["excellon_endxy"].split(",")
                     end_coords_xy = [float(eval(a)) for a in coordinates if a != '']
                     end_coords_xy[0] *= sfactor
@@ -4294,14 +4351,19 @@ class App(QtCore.QObject):
                     self.defaults['excellon_endxy'] = "%.*f, %.*f" % (self.decimals, end_coords_xy[0],
                                                                       self.decimals, end_coords_xy[1])
                 elif dim == 'geometry_endxy':
+                    if self.defaults["geometry_endxy"] is None or self.defaults["geometry_endxy"] == '':
+                        continue
                     coordinates = self.defaults["geometry_endxy"].split(",")
                     end_coords_xy = [float(eval(a)) for a in coordinates if a != '']
                     end_coords_xy[0] *= sfactor
                     end_coords_xy[1] *= sfactor
                     self.defaults['geometry_endxy'] = "%.*f, %.*f" % (self.decimals, end_coords_xy[0],
                                                                       self.decimals, end_coords_xy[1])
+
                 elif dim == 'geometry_cnctooldia':
-                    if type(self.defaults["geometry_cnctooldia"]) == float:
+                    if self.defaults["geometry_cnctooldia"] is None or self.defaults["geometry_cnctooldia"] == '':
+                        continue
+                    if type(self.defaults["geometry_cnctooldia"]) is float:
                         tools_diameters = [self.defaults["geometry_cnctooldia"]]
                     else:
                         try:
@@ -4316,6 +4378,8 @@ class App(QtCore.QObject):
                         tools_diameters[t] *= sfactor
                         self.defaults['geometry_cnctooldia'] += "%.*f," % (self.decimals, tools_diameters[t])
                 elif dim == 'tools_ncctools':
+                    if self.defaults["tools_ncctools"] is None or self.defaults["tools_ncctools"] == '':
+                        continue
                     if type(self.defaults["tools_ncctools"]) == float:
                         ncctools = [self.defaults["tools_ncctools"]]
                     else:
@@ -4331,6 +4395,9 @@ class App(QtCore.QObject):
                         ncctools[t] *= sfactor
                         self.defaults['tools_ncctools'] += "%.*f," % (self.decimals, ncctools[t])
                 elif dim == 'tools_solderpaste_tools':
+                    if self.defaults["tools_solderpaste_tools"] is None or \
+                            self.defaults["tools_solderpaste_tools"] == '':
+                        continue
                     if type(self.defaults["tools_solderpaste_tools"]) == float:
                         sptools = [self.defaults["tools_solderpaste_tools"]]
                     else:
@@ -4346,6 +4413,9 @@ class App(QtCore.QObject):
                         sptools[t] *= sfactor
                         self.defaults['tools_solderpaste_tools'] += "%.*f," % (self.decimals, sptools[t])
                 elif dim == 'tools_solderpaste_xy_toolchange':
+                    if self.defaults["tools_solderpaste_xy_toolchange"] is None or \
+                            self.defaults["tools_solderpaste_xy_toolchange"] == '':
+                        continue
                     try:
                         coordinates = self.defaults["tools_solderpaste_xy_toolchange"].split(",")
                         sp_coords = [float(eval(a)) for a in coordinates if a != '']
@@ -4356,6 +4426,16 @@ class App(QtCore.QObject):
                     except Exception as e:
                         log.debug("App.on_toggle_units().scale_options() --> %s" % str(e))
                         continue
+                elif dim == 'tools_cal_toolchange_xy':
+                    if self.defaults["tools_cal_toolchange_xy"] is None or \
+                            self.defaults["tools_cal_toolchange_xy"] == '':
+                        continue
+                    coordinates = self.defaults["tools_cal_toolchange_xy"].split(",")
+                    end_coords_xy = [float(eval(a)) for a in coordinates if a != '']
+                    end_coords_xy[0] *= sfactor
+                    end_coords_xy[1] *= sfactor
+                    self.defaults['tools_cal_toolchange_xy'] = "%.*f, %.*f" % (self.decimals, end_coords_xy[0],
+                                                                               self.decimals, end_coords_xy[1])
 
                 elif dim == 'global_gridx' or dim == 'global_gridy':
                     if new_units == 'IN':
@@ -4461,7 +4541,8 @@ class App(QtCore.QObject):
 
         self.preferencesUiManager.defaults_read_form()
 
-        # the self.preferencesUiManager.defaults_read_form() will update all defaults values in self.defaults from the GUI elements but
+        # the self.preferencesUiManager.defaults_read_form() will update all defaults values
+        # in self.defaults from the GUI elements but
         # I don't want it for the grid values, so I update them here
         self.defaults['global_gridx'] = val_x
         self.defaults['global_gridy'] = val_y
@@ -4520,7 +4601,7 @@ class App(QtCore.QObject):
             self.ui.plot_tab_area.protectTab(0)
             return
 
-        if name != 'plotarea':
+        if name != 'plotarea_tab':
             self.ui.plot_tab_area.insertTab(0, self.ui.plot_tab, "Plot Area")
             # remove the close button from the Plot Area tab (first tab index = 0) as this one will always be ON
             self.ui.plot_tab_area.protectTab(0)
@@ -4569,7 +4650,7 @@ class App(QtCore.QObject):
         self.defaults.report_usage("on_toggle_grid()")
 
         self.ui.grid_snap_btn.trigger()
-        self.on_grid_snap_triggered(state=True)
+        self.ui.on_grid_snap_triggered(state=True)
 
     def on_toggle_grid_lines(self):
         self.defaults.report_usage("on_toggle_grd_lines()")
@@ -4618,11 +4699,6 @@ class App(QtCore.QObject):
                 self.app_cursor.enabled = True
                 self.app_cursor.enabled = False
 
-
-
-
-
-
     def on_update_exc_export(self, state):
         """
         This is handling the update of Excellon Export parameters based on the ones in the Excellon General but only
@@ -4915,7 +4991,6 @@ class App(QtCore.QObject):
         else:
             self.app_cursor.enabled = False
 
-
     def on_tool_add_keypress(self):
         # ## Current application units in Upper Case
         self.units = self.defaults['units'].upper()
@@ -4969,7 +5044,7 @@ class App(QtCore.QObject):
                 self.paste_tool.on_add_tool_by_key()
 
     # It's meant to delete tools in tool tables via a 'Delete' shortcut key but only if certain conditions are met
-    # See description bellow.
+    # See description below.
     def on_delete_keypress(self):
         notebook_widget_name = self.ui.notebook.currentWidget().objectName()
 
@@ -5601,8 +5676,6 @@ class App(QtCore.QObject):
             except Exception as e:
                 return "Operation failed: %s" % str(e)
 
-
-
     def on_copy_object2(self, custom_name):
 
         def initialize_geometry(obj_init, app):
@@ -5898,7 +5971,6 @@ class App(QtCore.QObject):
                 except AttributeError:
                     pass
 
-
     def on_tools_database(self, source='app'):
         """
         Adds the Tools Database in a Tab in Plot Area.
@@ -5963,6 +6035,7 @@ class App(QtCore.QObject):
         for idx in range(self.ui.plot_tab_area.count()):
             if self.ui.plot_tab_area.tabText(idx) == _("Tools Database"):
                 self.ui.plot_tab_area.tabBar.setTabTextColor(idx, QtGui.QColor('red'))
+                self.tools_db_tab.save_db_btn.setStyleSheet("QPushButton {color: red;}")
 
         self.tools_db_changed_flag = True
 
@@ -5992,7 +6065,7 @@ class App(QtCore.QObject):
         """
         Executed whenever a QTab is closed in the Plot Area.
 
-        :param title: The objectName of the Tab that was closed. This objectName is assigned on Tab creation
+        :param tab_obj_name: The objectName of the Tab that was closed. This objectName is assigned on Tab creation
         :return:
         """
 
@@ -6032,17 +6105,17 @@ class App(QtCore.QObject):
         else:
             return
 
-    def on_plotarea_tab_closed(self, tab_idx):
-        """
-
-        :param tab_idx: Index of the Tab from the plotarea that was closed
-        :return:
-        """
-        widget = self.ui.plot_tab_area.widget(tab_idx)
-
-        if widget is not None:
-            widget.deleteLater()
-        self.ui.plot_tab_area.removeTab(tab_idx)
+    # def on_plotarea_tab_closed(self, tab_idx):
+    #     """
+    #
+    #     :param tab_idx: Index of the Tab from the plotarea that was closed
+    #     :return:
+    #     """
+    #     widget = self.ui.plot_tab_area.widget(tab_idx)
+    #
+    #     if widget is not None:
+    #         widget.deleteLater()
+    #     self.ui.plot_tab_area.removeTab(tab_idx)
 
     def on_flipy(self):
         """
@@ -6887,7 +6960,6 @@ class App(QtCore.QObject):
                 else:
 
                     key_modifier = QtWidgets.QApplication.keyboardModifiers()
-
                     if key_modifier == QtCore.Qt.ShiftModifier:
                         mod_key = 'Shift'
                     elif key_modifier == QtCore.Qt.ControlModifier:
@@ -6896,20 +6968,19 @@ class App(QtCore.QObject):
                         mod_key = None
 
                     try:
-                        if mod_key == self.defaults["global_mselect_key"]:
+                        if self.command_active is None:
                             # If the CTRL key is pressed when the LMB is clicked then if the object is selected it will
                             # deselect, and if it's not selected then it will be selected
                             # If there is no active command (self.command_active is None) then we check if we clicked
                             # on a object by checking the bounding limits against mouse click position
-                            if self.command_active is None:
+                            if mod_key == self.defaults["global_mselect_key"]:
                                 self.select_objects(key='multisel')
-                                self.delete_hover_shape()
-                        else:
-                            # If there is no active command (self.command_active is None) then we check if we clicked
-                            # on a object by checking the bounding limits against mouse click position
-                            if self.command_active is None:
+                            else:
+                                # If there is no active command (self.command_active is None) then we check if
+                                # we clicked on a object by checking the bounding limits against mouse click position
                                 self.select_objects()
-                                self.delete_hover_shape()
+
+                            self.delete_hover_shape()
                     except Exception as e:
                         log.warning("FlatCAMApp.on_mouse_click_release_over_plot() select click --> Error: %s" % str(e))
                         return
@@ -7302,6 +7373,7 @@ class App(QtCore.QObject):
         # Remove everything from memory
         App.log.debug("on_file_new()")
 
+        # close any editor that might be open
         if self.call_source != 'app':
             self.editor2object(cleanup=True)
             # ## EDITOR section
@@ -7336,9 +7408,13 @@ class App(QtCore.QObject):
         # tcl needs to be reinitialized, otherwise old shell variables etc  remains
         self.shell.init_tcl()
 
+        # delete any selection shape on canvas
         self.delete_selection_shape()
+
+        # delete all FlatCAM objects
         self.collection.delete_all()
 
+        # add in Selected tab an initial text that describe the flow of work in FlatCAm
         self.setup_component_editor()
 
         # Clear project filename
@@ -7350,18 +7426,23 @@ class App(QtCore.QObject):
         # Re-fresh project options
         self.on_options_app2project()
 
-        # Init Tools
+        # Init FlatCAMTools
         self.init_tools()
 
+        # Try to close all tabs in the PlotArea but only if the GUI is active (CLI is None)
         if cli is None:
-            # Close any Tabs opened in the Plot Tab Area section
-            for index in range(self.ui.plot_tab_area.count()):
-                self.ui.plot_tab_area.closeTab(index)
-                # for whatever reason previous command does not close the last tab so I do it manually
-            self.ui.plot_tab_area.closeTab(0)
+            # we need to go in reverse because once we remove a tab then the index changes
+            # meaning that removing the first tab (idx = 0) then the tab at former idx = 1 will assume idx = 0
+            # and so on. Therefore the deletion should be done in reverse
+            wdg_count = self.ui.plot_tab_area.tabBar.count() - 1
+            for index in range(wdg_count, -1, -1):
+                try:
+                    self.ui.plot_tab_area.closeTab(index)
+                except Exception as e:
+                    log.debug("App.on_file_new() --> %s" % str(e))
 
             # # And then add again the Plot Area
-            self.ui.plot_tab_area.addTab(self.ui.plot_tab, "Plot Area")
+            self.ui.plot_tab_area.insertTab(0, self.ui.plot_tab, "Plot Area")
             self.ui.plot_tab_area.protectTab(0)
 
         # take the focus of the Notebook on Project Tab.
@@ -8095,7 +8176,6 @@ class App(QtCore.QObject):
     # ###############################################################################################################
     # ### The following section has the functions that are displayed and call the Editor tab CNCJob Tab #############
     # ###############################################################################################################
-
     def init_code_editor(self, name):
 
         self.text_editor_tab = TextEditor(app=self, plain_text=True)
@@ -8267,14 +8347,14 @@ class App(QtCore.QObject):
             # set cursor of the code editor with the cursor at the searcehd line
             self.ui.plot_tab_area.currentWidget().code_editor.setTextCursor(cursor)
 
-    def on_filenewscript(self, silent=False, name=None, text=None):
+    def on_filenewscript(self, silent=False):
         """
         Will create a new script file and open it in the Code Editor
 
-        :param silent: if True will not display status messages
-        :param name: if specified will be the name of the new script
-        :param text: pass a source file to the newly created script to be loaded in it
-        :return: None
+        :param silent:  if True will not display status messages
+        :param name:    if specified will be the name of the new script
+        :param text:    pass a source file to the newly created script to be loaded in it
+        :return:        None
         """
         if silent is False:
             self.inform.emit('[success] %s' % _("New TCL script file created in Code Editor."))
@@ -8283,10 +8363,7 @@ class App(QtCore.QObject):
         self.ui.position_label.setText("")
         self.ui.rel_position_label.setText("")
 
-        if name is not None:
-            self.new_script_object(name=name, text=text)
-        else:
-            self.new_script_object(text=text)
+        self.new_script_object()
 
         # script_text = script_obj.source_file
         #
@@ -8300,9 +8377,9 @@ class App(QtCore.QObject):
         """
         Will open a Tcl script file into the Code Editor
 
-        :param silent: if True will not display status messages
-        :param name: name of a Tcl script file to open
-        :return:
+        :param silent:  if True will not display status messages
+        :param name:    name of a Tcl script file to open
+        :return:        None
         """
 
         self.defaults.report_usage("on_fileopenscript")
@@ -8337,13 +8414,12 @@ class App(QtCore.QObject):
         :return:
         """
 
-        self.report_usage("on_fileopenscript_example")
-        App.log.debug("on_fileopenscript_example()")
+        self.defaults.report_usage("on_fileopenscript_example")
+        log.debug("on_fileopenscript_example()")
 
         _filter_ = "TCL script .FlatScript (*.FlatScript);;TCL script .tcl (*.TCL);;TCL script .txt (*.TXT);;" \
                    "All Files (*.*)"
 
-
         # test if the app was frozen and choose the path for the configuration file
         if getattr(sys, "frozen", False) is True:
             example_path = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) + '\\assets\\examples'
@@ -8540,7 +8616,7 @@ class App(QtCore.QObject):
             return
 
         if use_thread is True:
-            proc = self.proc_container.new(_("Printing PDF ... Please wait."))
+            self.proc_container.new(_("Printing PDF ... Please wait."))
             self.worker_task.emit({'fcn': self.save_pdf, 'params': [filename, obj_selection]})
         else:
             self.save_pdf(filename, obj_selection)
@@ -9177,9 +9253,10 @@ class App(QtCore.QObject):
         Adds a new Geometry Object to the projects and populates
         it with shapes extracted from the SVG file.
 
-        :param filename: Path to the SVG file.
-        :param geo_type: Type of FlatCAM object that will be created from SVG
-        :param outname:
+        :param plot:        If True then the resulting object will be plotted on canvas
+        :param filename:    Path to the SVG file.
+        :param geo_type:    Type of FlatCAM object that will be created from SVG
+        :param outname:     The name given to the resulting FlatCAM object
         :return:
         """
         self.defaults.report_usage("import_svg()")
@@ -9227,6 +9304,7 @@ class App(QtCore.QObject):
         :param filename:    Path to the DXF file.
         :param geo_type:    Type of FlatCAM object that will be created from DXF
         :param outname:     Name for the imported Geometry
+        :param plot:        If True then the resulting object will be plotted on canvas
         :return:
         """
         self.defaults.report_usage("import_dxf()")
@@ -9510,7 +9588,7 @@ class App(QtCore.QObject):
 
         App.log.debug("open_hpgl2()")
 
-        with self.proc_container.new(_("Opening HPGL2")) as proc:
+        with self.proc_container.new(_("Opening HPGL2")):
             # Object name
             name = outname or filename.split('/')[-1].split('\\')[-1]
 
@@ -9533,29 +9611,49 @@ class App(QtCore.QObject):
 
         :param outname:     Name of the resulting object. None causes the name to be that of the file.
         :param filename:    Script file filename
+        :param silent:      If True there will be no messages printed to StatusBar
         :return:            None
         """
-        App.log.debug("open_script()")
 
-        with self.proc_container.new(_("Opening TCL Script...")):
+        def obj_init(script_obj, app_obj):
 
-            try:
-                with open(filename, "r") as opened_script:
-                    script_content = opened_script.readlines()
-                    script_content = ''.join(script_content)
+            assert isinstance(script_obj, ScriptObject), \
+                "Expected to initialize a ScriptObject but got %s" % type(script_obj)
 
-                    if silent is False:
-                        self.inform.emit('[success] %s' % _("TCL script file opened in Code Editor."))
+            if silent is False:
+                app_obj.inform.emit('[success] %s' % _("TCL script file opened in Code Editor."))
+
+            try:
+                script_obj.parse_file(filename)
+            except IOError:
+                app_obj.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Failed to open file"), filename))
+                return "fail"
+            except ParseError as err:
+                app_obj.inform.emit('[ERROR_NOTCL] %s: %s. %s' % (_("Failed to parse file"), filename, str(err)))
+                app_obj.log.error(str(err))
+                return "fail"
             except Exception as e:
                 log.debug("App.open_script() -> %s" % str(e))
-                self.inform.emit('[ERROR_NOTCL] %s' % _("Failed to open TCL Script."))
-                return
+                msg = '[ERROR] %s' % _("An internal error has occurred. See shell.\n")
+                msg += traceback.format_exc()
+                app_obj.inform.emit(msg)
+                return "fail"
+
+        App.log.debug("open_script()")
+
+        with self.proc_container.new(_("Opening TCL Script...")):
 
             # Object name
             script_name = outname or filename.split('/')[-1].split('\\')[-1]
 
-            # New object creation and file processing
-            self.on_filenewscript(name=script_name, text=script_content)
+            # Object creation
+            ret_val = self.new_object("script", script_name, obj_init, autoselected=False, plot=False)
+            if ret_val == 'fail':
+                filename = self.defaults['global_tcl_path'] + '/' + script_name
+                ret_val = self.new_object("script", script_name, obj_init, autoselected=False, plot=False)
+                if ret_val == 'fail':
+                    self.inform.emit('[ERROR_NOTCL]%s' % _('Failed to open TCL Script.'))
+                    return 'fail'
 
             # Register recent file
             self.file_opened.emit("script", filename)
@@ -9674,8 +9772,7 @@ class App(QtCore.QObject):
                     d = json.loads(file_content, object_hook=dict2obj)
             except Exception as e:
                 App.log.error("Failed to open project file: %s with error: %s" % (filename, str(e)))
-                self.inform.emit('[ERROR_NOTCL] %s: %s' %
-                                 (_("Failed to open project file"), filename))
+                self.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Failed to open project file"), filename))
                 return
 
         # Clear the current project
@@ -9718,7 +9815,7 @@ class App(QtCore.QObject):
                                                               )
                                       )
 
-                self.new_object(obj['kind'], obj['options']['name'], obj_init, active=False, fit=False, plot=plot)
+                self.new_object(obj['kind'], obj['options']['name'], obj_init, plot=plot)
             except Exception as e:
                 print('App.open_project() --> ' + str(e))
 
@@ -9737,8 +9834,6 @@ class App(QtCore.QObject):
 
         App.log.debug(" **************** Finished PROJECT loading... **************** ")
 
-
-
     def plot_all(self, fit_view=True, use_thread=True):
         """
         Re-generates all plots from all objects.
@@ -10124,7 +10219,7 @@ class App(QtCore.QObject):
             # no_stats dict; just so it won't break things on website
             no_ststs_dict = {}
             no_ststs_dict["global_ststs"] = {}
-            full_url = App.version_url + "?s=" + str(self.defaults['global_serial']) + "&v=" + str(self.version) +\
+            full_url = App.version_url + "?s=" + str(self.defaults['global_serial']) + "&v=" + str(self.version) + \
                        "&os=" + str(self.os) + "&" + urllib.parse.urlencode(no_ststs_dict["global_ststs"])
 
         App.log.debug("Checking for updates @ %s" % full_url)
@@ -10540,29 +10635,6 @@ class App(QtCore.QObject):
                 update_colors=(new_color, new_line_color)
             )
 
-    def on_grid_snap_triggered(self, state):
-        """
-
-        :param state:   A parameter with the state of the grid, boolean
-
-        :return:
-        """
-        if state:
-            self.ui.snap_infobar_label.setPixmap(QtGui.QPixmap(self.resource_location + '/snap_filled_16.png'))
-        else:
-            self.ui.snap_infobar_label.setPixmap(QtGui.QPixmap(self.resource_location + '/snap_16.png'))
-
-        self.ui.snap_infobar_label.clicked_state = state
-
-    def on_grid_icon_snap_clicked(self):
-        """
-        Slot called by clicking a GUI element, in this case a FCLabel
-
-        :return:
-        """
-        if isinstance(self.sender(), FCLabel):
-            self.ui.grid_snap_btn.trigger()
-
     def generate_cnc_job(self, objects):
         """
         Slot that will be called by clicking an entry in the contextual menu generated in the Project Tab tree
@@ -10765,21 +10837,6 @@ class App(QtCore.QObject):
             #                       no_km)
             # QtWidgets.qApp.sendEvent(self.shell._edit, f)
 
-    def on_toggle_shell_from_settings(self, state):
-        """
-        Toggle shell: if is visible close it, if it is closed then open it
-        :return: None
-        """
-
-        self.defaults.report_usage("on_toggle_shell_from_settings()")
-
-        if state is True:
-            if not self.ui.shell_dock.isVisible():
-                self.ui.shell_dock.show()
-        else:
-            if self.ui.shell_dock.isVisible():
-                self.ui.shell_dock.hide()
-
     def shell_message(self, msg, show=False, error=False, warning=False, success=False, selected=False):
         """
         Shows a message on the FlatCAM Shell

+ 12 - 2
FlatCAMBookmark.py

@@ -17,7 +17,7 @@ if '_' not in builtins.__dict__:
 
 class BookmarkManager(QtWidgets.QWidget):
 
-    mark_rows = QtCore.pyqtSignal()
+    # mark_rows = QtCore.pyqtSignal()
 
     def __init__(self, app, storage, parent=None):
         super(BookmarkManager, self).__init__(parent)
@@ -119,9 +119,18 @@ class BookmarkManager(QtWidgets.QWidget):
         self.link_entry.returnPressed.connect(self.on_add_entry)
         # closebtn.clicked.connect(self.accept)
 
-        self.table_widget.drag_drop_sig.connect(self.mark_table_rows_for_actions)
+        self.ui_connect()
         self.build_bm_ui()
 
+    def ui_connect(self):
+        self.table_widget.drag_drop_sig.connect(self.mark_table_rows_for_actions)
+
+    def ui_disconnect(self):
+        try:
+            self.table_widget.drag_drop_sig.connect(self.mark_table_rows_for_actions)
+        except (TypeError, AttributeError):
+            pass
+
     def build_bm_ui(self):
 
         self.table_widget.setRowCount(len(self.bm_dict))
@@ -378,4 +387,5 @@ class BookmarkManager(QtWidgets.QWidget):
 
     def closeEvent(self, QCloseEvent):
         self.rebuild_actions()
+        self.ui_disconnect()
         super().closeEvent(QCloseEvent)

+ 41 - 1
FlatCAMDB.py

@@ -1672,6 +1672,12 @@ class ToolsDB2(QtWidgets.QWidget):
         )
         self.buttons_box.addWidget(import_db_btn)
 
+        self.save_db_btn = FCButton(_("Save DB"))
+        self.save_db_btn.setToolTip(
+            _("Save the Tools Database information's.")
+        )
+        self.buttons_box.addWidget(self.save_db_btn)
+
         self.add_tool_from_db = FCButton(_("Add Tool from Tools DB"))
         self.add_tool_from_db.setToolTip(
             _("Add a new tool in the Tools Table of the\n"
@@ -1796,6 +1802,7 @@ class ToolsDB2(QtWidgets.QWidget):
         remove_entry_btn.clicked.connect(self.on_tool_delete)
         export_db_btn.clicked.connect(self.on_export_tools_db_file)
         import_db_btn.clicked.connect(self.on_import_tools_db_file)
+        self.save_db_btn.clicked.connect(self.on_save_db_btn_click)
         # closebtn.clicked.connect(self.accept)
 
         self.add_tool_from_db.clicked.connect(self.on_tool_requested_from_app)
@@ -2026,6 +2033,14 @@ class ToolsDB2(QtWidgets.QWidget):
         # add the new entry to the Tools DB table
         self.update_storage()
         self.build_db_ui()
+
+        # select the last Tree item just added
+        nr_items = self.tree_widget.topLevelItemCount()
+        if nr_items:
+            last_item = self.tree_widget.topLevelItem(nr_items - 1)
+            self.tree_widget.setCurrentItem(last_item)
+            last_item.setSelected(True)
+
         self.app.inform.emit('[success] %s' % _("Tool added to DB."))
 
     def on_tool_copy(self):
@@ -2050,6 +2065,15 @@ class ToolsDB2(QtWidgets.QWidget):
 
         self.update_storage()
         self.build_db_ui()
+
+        # select the last Tree item just added
+        nr_items = self.tree_widget.topLevelItemCount()
+        if nr_items:
+            last_item = self.tree_widget.topLevelItem(nr_items - 1)
+            self.tree_widget.setCurrentItem(last_item)
+            last_item.setSelected(True)
+
+        self.callback_app()
         self.app.inform.emit('[success] %s' % _("Tool copied from Tools DB."))
 
     def on_tool_delete(self):
@@ -2069,6 +2093,14 @@ class ToolsDB2(QtWidgets.QWidget):
 
         self.update_storage()
         self.build_db_ui()
+
+        # select the first Tree item
+        nr_items = self.tree_widget.topLevelItemCount()
+        if nr_items:
+            first_item = self.tree_widget.topLevelItem(0)
+            self.tree_widget.setCurrentItem(first_item)
+            first_item.setSelected(True)
+
         self.app.inform.emit('[success] %s' % _("Tool removed from Tools DB."))
 
     def on_export_tools_db_file(self):
@@ -2167,6 +2199,7 @@ class ToolsDB2(QtWidgets.QWidget):
         for idx in range(self.app.ui.plot_tab_area.count()):
             if self.app.ui.plot_tab_area.tabText(idx) == _("Tools Database"):
                 self.app.ui.plot_tab_area.tabBar.setTabTextColor(idx, QtGui.QColor('black'))
+                self.save_db_btn.setStyleSheet("")
 
                 # Save Tools DB in a file
                 try:
@@ -2181,6 +2214,10 @@ class ToolsDB2(QtWidgets.QWidget):
                 if not silent:
                     self.app.inform.emit('[success] %s' % _("Saved Tools DB."))
 
+    def on_save_db_btn_click(self):
+        self.app.tools_db_changed_flag = False
+        self.on_save_tools_db()
+
     def ui_connect(self):
         # make sure that we don't make multiple connections to the widgets
         self.ui_disconnect()
@@ -2258,6 +2295,8 @@ class ToolsDB2(QtWidgets.QWidget):
         val = self.name_entry.get_value()
 
         item = self.tree_widget.currentItem()
+        if item is None:
+            return
         # I'm setting the value for the second column (designated by 1) because first column holds the ID
         # and second column holds the Name (this behavior is set in the build_ui method)
         item.setData(1, QtCore.Qt.DisplayRole, val)
@@ -2272,7 +2311,8 @@ class ToolsDB2(QtWidgets.QWidget):
         try:
             wdg = self.sender()
 
-            assert isinstance(wdg, QtWidgets.QWidget), "Expected a QWidget got %s" % type(wdg)
+            assert isinstance(wdg, QtWidgets.QWidget) or isinstance(wdg, QtWidgets.QAction), \
+                "Expected a QWidget got %s" % type(wdg)
 
             if wdg is None:
                 return

+ 1 - 1
FlatCAMTranslation.py

@@ -49,7 +49,7 @@ def load_languages():
         available_translations = next(os.walk(languages_path_search))[1]
     except StopIteration:
         if not available_translations:
-            languages_path_search = os.path.join(Path(__file__).parents[1], 'locale')
+            languages_path_search = os.path.join(str(Path(__file__).parents[1]), 'locale')
             try:
                 available_translations = next(os.walk(languages_path_search))[1]
             except StopIteration:

+ 28 - 0
assets/examples/cutout_a_gerber.FlatScript

@@ -0,0 +1,28 @@
+# #####################################################################################
+# DESCRIPTION:
+# Will cut a PCB piece with a pattern (Gerber file) out of the surrounding PCB material
+# #####################################################################################
+
+puts "**************** RUNNING an EXAMPLE SCRIPT = Cutout a Gerber file *******************"
+
+# ----------- START: This is needed only for the examples ----------------
+# first set the default location where to search for the files to be open and store it to the ROOT_FOLDER variable
+set ROOT_FOLDER  [get_sys root_folder_path]
+
+# calculate the resources path for the examples we need to run and store it inside the PATH varaible
+set PATH ${ROOT_FOLDER}/assets/examples/files
+# ----------- END: This is needed only for the examples ----------------
+
+# set the working path to the path that holds the files we are going to work with
+set_path $PATH
+
+# load the GERBER file test.gbr as gerber_file
+open_gerber test.gbr -outname gerber_file
+
+# cutout the Gerber file with name gerber_file using an endmill with diameter 1.2 at a distance of 0.1 units from
+# the Gerber object. Will add gaps (bridges to hold the PCB to the surrounding material) on each side of the PCB
+# (4 sides) and the resulting Geometry object that hold the cutout geometry will be named cutout_geo
+cutout gerber_file -dia 1.2 -margin 0.1 -gapsize 3 -gaps "4" -outname cutout_geo
+
+# plot the objects so we can see them; not required for the script but in this script we want to see the results
+plot_all

+ 69 - 0
assets/examples/files/test.gbr

@@ -0,0 +1,69 @@
+G04*
+G04 GERBER (RE)GENERATED BY FLATCAM v8.992 - www.flatcam.org - Version Date: 2020/05/01*
+G04 Filename: test.gbr*
+G04 Created on : Friday, 01 May 2020 at 17:03*
+%INBottom.gbr*%
+%MOMM*%
+%ADD13C,0.8*%
+%ADD14C,1.27*%
+%ADD15C,1.27*%
+%ADD16R,1.5X1.5*%
+%ADD17C,1.5*%
+%FSLAX53Y53*%
+G04*
+G71*
+G90*
+G75*
+G01*
+%LNBottom*%
+%LPD*%
+X20779Y24462D2*
+D13*
+Y27002D1*
+X18229D1*
+X14429D1*
+X18229D2*
+X19509D1*
+Y28272D1*
+X32209Y14302D2*
+X28399D1*
+Y16842D1*
+Y14302D2*
+X18239D1*
+X20779Y16842D2*
+X23329D1*
+Y28272D1*
+X27129D1*
+X20779Y19382D2*
+X14429D1*
+X20779Y21922D2*
+X18239D1*
+X28399D2*
+X32209D1*
+D14*
+X27129Y28272D3*
+D15*
+X19509D3*
+D14*
+X14429Y19382D3*
+D15*
+Y27002D3*
+D14*
+X18239Y21922D3*
+D15*
+Y14302D3*
+D14*
+X32209Y21922D3*
+D15*
+Y14302D3*
+D16*
+X28399Y24462D3*
+D17*
+Y21922D3*
+Y19382D3*
+Y16842D3*
+X20779D3*
+Y19382D3*
+Y21922D3*
+Y24462D3*
+M02*

+ 28 - 0
assets/examples/files/test.txt

@@ -0,0 +1,28 @@
+M48
+;EXCELLON GENERATED BY FLATCAM v8.992 - www.flatcam.org - Version Date: 2020/05/01
+;Filename: test.txt
+;Created on : Friday, 01 May 2020 at 17:04
+INCH,LZ
+;FILE_FORMAT=2:4
+T1F00S00C0.0220
+T2F00S00C0.0354
+%
+T01
+X010681Y011130
+X007681Y011130
+X005681Y010630
+X007181Y008630
+X005681Y007630
+X007181Y005630
+X012681Y005630
+X012681Y008630
+T02
+X011181Y009630
+X011181Y008630
+X011181Y007630
+X011181Y006630
+X008181Y006630
+X008181Y007630
+X008181Y008630
+X008181Y009630
+M30

+ 30 - 0
assets/examples/files/test_1.gbr

@@ -0,0 +1,30 @@
+G04*
+G04 GERBER (RE)GENERATED BY FLATCAM v8.992 - www.flatcam.org - Version Date: 2020/05/01*
+G04 Filename: test_1*
+G04 Created on : Friday, 01 May 2020 at 17:07*
+G04*
+G04 RS-274X GERBER GENERATED BY FLATCAM v8.992 - www.flatcam.org - Version Date: 2020/05/01*
+G04 Filename: test_1_edit*
+G04 Created on : Friday, 01 May 2020 at 17:06*
+%FSLAX24Y24*%
+%MOIN*%
+%ADD10C,0.003937*%
+
+G70*
+G90*
+G01*
+%LPD*%
+D10*
+X05118Y11417D02*
+X05118Y05118D01*
+X05118Y05118D02*
+X08661Y05118D01*
+X08661Y05118D02*
+X08661Y11417D01*
+X08661Y11417D02*
+X05118Y11417D01*
+X08661Y11417D02*
+X05118Y11417D01*
+X08661Y11417D02*
+X05118Y11417D01*
+M02*

+ 26 - 0
assets/examples/isolate_gerber.FlatScript

@@ -0,0 +1,26 @@
+# #####################################################################################
+# DESCRIPTION:
+# Will isolate copper features in a Gerber file by creating surrounding paths around
+# #####################################################################################
+
+puts "**************** RUNNING an EXAMPLE SCRIPT = Isolate a Gerber file *******************"
+
+# ----------- START: This is needed only for the examples ----------------
+# first set the default location where to search for the files to be open and store it to the ROOT_FOLDER variable
+set ROOT_FOLDER  [get_sys root_folder_path]
+
+# calculate the resources path for the examples we need to run and store it inside the PATH varaible
+set PATH ${ROOT_FOLDER}/assets/examples/files
+# ----------- END: This is needed only for the examples ----------------
+
+# set the working path to the path that holds the files we are going to work with
+set_path $PATH
+
+# load the GERBER file
+open_gerber test.gbr -outname gerber_file
+
+# isolate the Gerber file
+isolate gerber_file -dia 0.1 -passes 2 -overlap 90 -combine True -outname iso_geo
+
+# plot the objects so we can see them; not required for the script but in this script we want to see the results
+plot_all

+ 26 - 0
assets/examples/open_file.FlatScript

@@ -0,0 +1,26 @@
+# #####################################################################################
+# DESCRIPTION:
+# Will open a Gerber (and Excellon) file in FlatCAM
+# #####################################################################################
+
+puts "**************** RUNNING an EXAMPLE SCRIPT = Open a file *******************"
+
+# ----------- START: This is needed only for the examples ----------------
+# first set the default location where to search for the files to be open and store it to the ROOT_FOLDER variable 
+set ROOT_FOLDER  [get_sys root_folder_path]
+
+# calculate the resources path for the examples we need to run and store it inside the PATH varaible
+set PATH ${ROOT_FOLDER}/assets/examples/files
+# ----------- END: This is needed only for the examples ----------------
+
+# set the working path to the path that holds the files we are going to work with
+set_path $PATH
+
+# load the GERBER file and rename it to a known name so we can use it further
+open_gerber test.gbr -outname gerber_obj
+
+# load the Excellon file and rename it to a known name so we can use it further
+open_excellon test.txt -outname excellon_obj
+
+# plot them all so we can see them on canvas
+plot_all 

BIN=BIN
assets/resources/dark_resources/flatcam_icon32_green.png


BIN=BIN
assets/resources/dark_resources/snap_16.png


BIN=BIN
assets/resources/dark_resources/snap_filled_16.png


BIN=BIN
assets/resources/snap_16.png


BIN=BIN
assets/resources/snap_filled_16.png


+ 9 - 14
camlib.py

@@ -3472,8 +3472,7 @@ class CNCjob(Geometry):
                     else:
                         log.debug("camlib.CNCJob.generate_from_excellon_by_tool() --> "
                                   "The loaded Excellon file has no drills ...")
-                        self.app.inform.emit('[ERROR_NOTCL] %s...' %
-                                             _('The loaded Excellon file has no drills'))
+                        self.app.inform.emit('[ERROR_NOTCL] %s...' % _('The loaded Excellon file has no drills'))
                         return 'fail'
                 self.z_cut = deepcopy(old_zcut)
             log.debug("The total travel distance with Travelling Salesman Algorithm is: %s" % str(measured_distance))
@@ -3499,14 +3498,12 @@ class CNCjob(Geometry):
         self.app.inform.emit(_("Finished G-Code generation..."))
         return 'OK'
 
-    def generate_from_multitool_geometry(
-            self, geometry, append=True,
-            tooldia=None, offset=0.0, tolerance=0, z_cut=1.0, z_move=2.0,
-            feedrate=2.0, feedrate_z=2.0, feedrate_rapid=30,
-            spindlespeed=None, spindledir='CW', dwell=False, dwelltime=1.0,
-            multidepth=False, depthpercut=None,
-            toolchange=False, toolchangez=1.0, toolchangexy="0.0, 0.0", extracut=False, extracut_length=0.2,
-            startz=None, endz=2.0, endxy='', pp_geometry_name=None, tool_no=1):
+    def generate_from_multitool_geometry(self, geometry, append=True, tooldia=None, offset=0.0, tolerance=0, z_cut=1.0,
+                                         z_move=2.0, feedrate=2.0, feedrate_z=2.0, feedrate_rapid=30,
+                                         spindlespeed=None, spindledir='CW', dwell=False, dwelltime=1.0,
+                                         multidepth=False, depthpercut=None, toolchange=False, toolchangez=1.0,
+                                         toolchangexy="0.0, 0.0", extracut=False, extracut_length=0.2,
+                                         startz=None, endz=2.0, endxy='', pp_geometry_name=None, tool_no=1):
         """
         Algorithm to generate from multitool Geometry.
 
@@ -5025,8 +5022,7 @@ class CNCjob(Geometry):
 
         return path
 
-    def linear2gcode(self, linear, tolerance=0, down=True, up=True,
-                     z_cut=None, z_move=None, zdownrate=None,
+    def linear2gcode(self, linear, tolerance=0, down=True, up=True, z_cut=None, z_move=None, zdownrate=None,
                      feedrate=None, feedrate_z=None, feedrate_rapid=None, cont=False, old_point=(0, 0)):
         """
 
@@ -5119,8 +5115,7 @@ class CNCjob(Geometry):
                 # For Incremental coordinates type G91
                 # next_x = pt[0] - prev_x
                 # next_y = pt[1] - prev_y
-                self.app.inform.emit('[ERROR_NOTCL] %s' %
-                                     _('G91 coordinates not implemented ...'))
+                self.app.inform.emit('[ERROR_NOTCL] %s' % _('G91 coordinates not implemented ...'))
                 next_x = pt[0]
                 next_y = pt[1]
 

+ 7 - 2
defaults.py

@@ -28,6 +28,7 @@ class FlatCAMDefaults:
         "version": 8.992,   # defaults format version, not necessarily equal to app version
         "first_run": True,
         "units": "MM",
+        "root_folder_path": '',
         "global_serial": 0,
         "global_stats": dict(),
         "global_tabs_detachable": True,
@@ -340,6 +341,10 @@ class FlatCAMDefaults:
         "geometry_feedrate_probe": 75,
         "geometry_segx": 0.0,
         "geometry_segy": 0.0,
+        "geometry_area_exclusion": False,
+        "geometry_area_shape": "polygon",
+        "geometry_area_strategy": "over",
+        "geometry_area_overz": 1.0,
 
         # Geometry Editor
         "geometry_editor_sel_limit": 30,
@@ -457,8 +462,8 @@ class FlatCAMDefaults:
         "tools_film_pagesize": 'A4',
 
         # Panel Tool
-        "tools_panelize_spacing_columns": 0,
-        "tools_panelize_spacing_rows": 0,
+        "tools_panelize_spacing_columns": 0.0,
+        "tools_panelize_spacing_rows": 0.0,
         "tools_panelize_columns": 1,
         "tools_panelize_rows": 1,
         "tools_panelize_constrain": False,

+ 3 - 3
flatcamEditors/FlatCAMExcEditor.py

@@ -2837,7 +2837,7 @@ class FlatCAMExcEditor(QtCore.QObject):
         # start with GRID toolbar activated
         if self.app.ui.grid_snap_btn.isChecked() is False:
             self.app.ui.grid_snap_btn.trigger()
-            self.app.on_grid_snap_triggered(state=True)
+            self.app.ui.on_grid_snap_triggered(state=True)
 
         self.app.ui.popmenu_disable.setVisible(False)
         self.app.ui.cmenu_newmenu.menuAction().setVisible(False)
@@ -3226,7 +3226,7 @@ class FlatCAMExcEditor(QtCore.QObject):
             spec = {"C": float(tool_dia[0])}
             self.new_tools[name] = spec
 
-            # add in self.tools the 'solid_geometry' key, the value (a list) is populated bellow
+            # add in self.tools the 'solid_geometry' key, the value (a list) is populated below
             self.new_tools[name]['solid_geometry'] = []
 
             # create the self.drills for the new Excellon object (the one with edited content)
@@ -3258,7 +3258,7 @@ class FlatCAMExcEditor(QtCore.QObject):
                 spec = {"C": float(tool_dia[0])}
                 self.new_tools[name] = spec
 
-                # add in self.tools the 'solid_geometry' key, the value (a list) is populated bellow
+                # add in self.tools the 'solid_geometry' key, the value (a list) is populated below
                 self.new_tools[name]['solid_geometry'] = []
 
             # create the self.slots for the new Excellon object (the one with edited content)

+ 1 - 1
flatcamEditors/FlatCAMGeoEditor.py

@@ -4100,7 +4100,7 @@ class FlatCAMGeoEditor(QtCore.QObject):
         # start with GRID toolbar activated
         if self.app.ui.grid_snap_btn.isChecked() is False:
             self.app.ui.grid_snap_btn.trigger()
-            self.app.on_grid_snap_triggered(state=True)
+            self.app.ui.on_grid_snap_triggered(state=True)
 
     def on_buffer_tool(self):
         buff_tool = BufferSelectionTool(self.app, self)

+ 357 - 191
flatcamEditors/FlatCAMGrbEditor.py

@@ -8,10 +8,12 @@
 from PyQt5 import QtGui, QtCore, QtWidgets
 from PyQt5.QtCore import Qt, QSettings
 
-from shapely.geometry import LineString, LinearRing, MultiLineString, Point, Polygon, MultiPolygon
+from shapely.geometry import LineString, LinearRing, MultiLineString, Point, Polygon, MultiPolygon, box
 from shapely.ops import cascaded_union
 import shapely.affinity as affinity
 
+from vispy.geometry import Rect
+
 import threading
 import time
 from copy import copy, deepcopy
@@ -771,15 +773,15 @@ class FCPoligonize(FCShapeTool):
             self.draw_app.select_tool("select")
             return
 
-        apid_set = set()
+        apcode_set = set()
         for elem in self.draw_app.selected:
-            for apid in self.draw_app.storage_dict:
-                if 'geometry' in self.draw_app.storage_dict[apid]:
-                    if elem in self.draw_app.storage_dict[apid]['geometry']:
-                        apid_set.add(apid)
+            for apcode in self.draw_app.storage_dict:
+                if 'geometry' in self.draw_app.storage_dict[apcode]:
+                    if elem in self.draw_app.storage_dict[apcode]['geometry']:
+                        apcode_set.add(apcode)
                         break
 
-        if len(apid_set) > 1:
+        if len(apcode_set) > 1:
             self.draw_app.in_action = False
             self.complete = True
             self.draw_app.app.inform.emit('[WARNING_NOTCL] %s' %
@@ -809,7 +811,7 @@ class FCPoligonize(FCShapeTool):
                     try:
                         current_storage = self.draw_app.storage_dict['0']['geometry']
                     except KeyError:
-                        self.draw_app.on_aperture_add(apid='0')
+                        self.draw_app.on_aperture_add(apcode='0')
                         current_storage = self.draw_app.storage_dict['0']['geometry']
                 new_el = {}
                 new_el['solid'] = geo
@@ -823,7 +825,7 @@ class FCPoligonize(FCShapeTool):
                 try:
                     current_storage = self.draw_app.storage_dict['0']['geometry']
                 except KeyError:
-                    self.draw_app.on_aperture_add(apid='0')
+                    self.draw_app.on_aperture_add(apcode='0')
                     current_storage = self.draw_app.storage_dict['0']['geometry']
 
             new_el = {}
@@ -1065,7 +1067,7 @@ class FCRegion(FCShapeTool):
 
             # regions are added always in the '0' aperture
             if '0' not in self.draw_app.storage_dict:
-                self.draw_app.on_aperture_add(apid='0')
+                self.draw_app.on_aperture_add(apcode='0')
             else:
                 self.draw_app.last_aperture_selected = '0'
 
@@ -2485,6 +2487,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
         self.apertures_table.setColumnCount(5)
         self.apertures_table.setHorizontalHeaderLabels(['#', _('Code'), _('Type'), _('Size'), _('Dim')])
         self.apertures_table.setSortingEnabled(False)
+        self.apertures_table.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
 
         self.apertures_table.horizontalHeaderItem(0).setToolTip(
             _("Index"))
@@ -2924,7 +2927,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
         self.storage_dict = {}
         self.current_storage = []
 
-        self.sorted_apid = []
+        self.sorted_apcode = []
 
         self.new_apertures = {}
         self.new_aperture_macros = {}
@@ -2934,9 +2937,9 @@ class FlatCAMGrbEditor(QtCore.QObject):
 
         # dictionary to store the tool_row and aperture codes in Tool_table
         # it will be updated everytime self.build_ui() is called
-        self.olddia_newdia = {}
+        self.oldapcode_newapcode = {}
 
-        self.tool2tooldia = {}
+        self.tid2apcode = {}
 
         # this will store the value for the last selected tool, for use after clicking on canvas when the selection
         # is cleared but as a side effect also the selected tool is cleared
@@ -3119,22 +3122,22 @@ class FlatCAMGrbEditor(QtCore.QObject):
         self.units = self.app.defaults['units'].upper()
         self.decimals = self.app.decimals
 
-        self.olddia_newdia.clear()
-        self.tool2tooldia.clear()
+        self.oldapcode_newapcode.clear()
+        self.tid2apcode.clear()
 
-        # update the olddia_newdia dict to make sure we have an updated state of the tool_table
+        # update the oldapcode_newapcode dict to make sure we have an updated state of the tool_table
         for key in self.storage_dict:
-            self.olddia_newdia[key] = key
+            self.oldapcode_newapcode[key] = key
 
         sort_temp = []
-        for aperture in self.olddia_newdia:
+        for aperture in self.oldapcode_newapcode:
             sort_temp.append(int(aperture))
-        self.sorted_apid = sorted(sort_temp)
+        self.sorted_apcode = sorted(sort_temp)
 
         # populate self.intial_table_rows dict with the tool number as keys and aperture codes as values
-        for i in range(len(self.sorted_apid)):
-            tt_aperture = self.sorted_apid[i]
-            self.tool2tooldia[i + 1] = tt_aperture
+        for i in range(len(self.sorted_apcode)):
+            tt_aperture = self.sorted_apcode[i]
+            self.tid2apcode[i + 1] = tt_aperture
 
         # Init GUI
 
@@ -3197,15 +3200,15 @@ class FlatCAMGrbEditor(QtCore.QObject):
         for ap_code in sorted_apertures:
             ap_code = str(ap_code)
 
-            ap_id_item = QtWidgets.QTableWidgetItem('%d' % int(self.apertures_row + 1))
-            ap_id_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
-            self.apertures_table.setItem(self.apertures_row, 0, ap_id_item)  # Tool name/id
+            ap_code_item = QtWidgets.QTableWidgetItem('%d' % int(self.apertures_row + 1))
+            ap_code_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
+            self.apertures_table.setItem(self.apertures_row, 0, ap_code_item)  # Tool name/id
 
             ap_code_item = QtWidgets.QTableWidgetItem(ap_code)
-            ap_code_item.setFlags(QtCore.Qt.ItemIsEnabled)
+            ap_code_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
 
             ap_type_item = QtWidgets.QTableWidgetItem(str(self.storage_dict[ap_code]['type']))
-            ap_type_item.setFlags(QtCore.Qt.ItemIsEnabled)
+            ap_type_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
 
             if str(self.storage_dict[ap_code]['type']) == 'R' or str(self.storage_dict[ap_code]['type']) == 'O':
                 ap_dim_item = QtWidgets.QTableWidgetItem(
@@ -3213,16 +3216,16 @@ class FlatCAMGrbEditor(QtCore.QObject):
                                     self.decimals, self.storage_dict[ap_code]['height']
                                     )
                 )
-                ap_dim_item.setFlags(QtCore.Qt.ItemIsEnabled)
+                ap_dim_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsEditable)
             elif str(self.storage_dict[ap_code]['type']) == 'P':
                 ap_dim_item = QtWidgets.QTableWidgetItem(
                     '%.*f, %.*f' % (self.decimals, self.storage_dict[ap_code]['diam'],
                                     self.decimals, self.storage_dict[ap_code]['nVertices'])
                 )
-                ap_dim_item.setFlags(QtCore.Qt.ItemIsEnabled)
+                ap_dim_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsEditable)
             else:
                 ap_dim_item = QtWidgets.QTableWidgetItem('')
-                ap_dim_item.setFlags(QtCore.Qt.ItemIsEnabled)
+                ap_dim_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
 
             try:
                 if self.storage_dict[ap_code]['size'] is not None:
@@ -3232,11 +3235,15 @@ class FlatCAMGrbEditor(QtCore.QObject):
                     ap_size_item = QtWidgets.QTableWidgetItem('')
             except KeyError:
                 ap_size_item = QtWidgets.QTableWidgetItem('')
-            ap_size_item.setFlags(QtCore.Qt.ItemIsEnabled)
+
+            if str(self.storage_dict[ap_code]['type']) == 'C':
+                ap_size_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsEditable)
+            else:
+                ap_size_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
 
             self.apertures_table.setItem(self.apertures_row, 1, ap_code_item)  # Aperture Code
             self.apertures_table.setItem(self.apertures_row, 2, ap_type_item)  # Aperture Type
-            self.apertures_table.setItem(self.apertures_row, 3, ap_size_item)  # Aperture Dimensions
+            self.apertures_table.setItem(self.apertures_row, 3, ap_size_item)  # Aperture Size
             self.apertures_table.setItem(self.apertures_row, 4, ap_dim_item)  # Aperture Dimensions
 
             self.apertures_row += 1
@@ -3247,9 +3254,9 @@ class FlatCAMGrbEditor(QtCore.QObject):
         # for ap_code in sorted_macros:
         #     ap_code = str(ap_code)
         #
-        #     ap_id_item = QtWidgets.QTableWidgetItem('%d' % int(self.apertures_row + 1))
-        #     ap_id_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
-        #     self.apertures_table.setItem(self.apertures_row, 0, ap_id_item)  # Tool name/id
+        #     ap_code_item = QtWidgets.QTableWidgetItem('%d' % int(self.apertures_row + 1))
+        #     ap_code_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
+        #     self.apertures_table.setItem(self.apertures_row, 0, ap_code_item)  # Tool name/id
         #
         #     ap_code_item = QtWidgets.QTableWidgetItem(ap_code)
         #
@@ -3304,52 +3311,52 @@ class FlatCAMGrbEditor(QtCore.QObject):
 
         # for convenience set the next aperture code in the apcode field
         try:
-            self.apcode_entry.set_value(max(self.tool2tooldia.values()) + 1)
+            self.apcode_entry.set_value(max(self.tid2apcode.values()) + 1)
         except ValueError:
             # this means that the edited object has no apertures so we start with 10 (Gerber specifications)
             self.apcode_entry.set_value(self.app.defaults["gerber_editor_newcode"])
 
-    def on_aperture_add(self, apid=None):
+    def on_aperture_add(self, apcode=None):
         self.is_modified = True
-        if apid:
-            ap_id = apid
+        if apcode:
+            ap_code = apcode
         else:
             try:
-                ap_id = str(self.apcode_entry.get_value())
+                ap_code = str(self.apcode_entry.get_value())
             except ValueError:
                 self.app.inform.emit('[WARNING_NOTCL] %s' %
                                      _("Aperture code value is missing or wrong format. Add it and retry."))
                 return
-            if ap_id == '':
+            if ap_code == '':
                 self.app.inform.emit('[WARNING_NOTCL] %s' %
                                      _("Aperture code value is missing or wrong format. Add it and retry."))
                 return
 
-        if ap_id == '0':
-            if ap_id not in self.tool2tooldia:
-                self.storage_dict[ap_id] = {}
-                self.storage_dict[ap_id]['type'] = 'REG'
+        if ap_code == '0':
+            if ap_code not in self.tid2apcode:
+                self.storage_dict[ap_code] = {}
+                self.storage_dict[ap_code]['type'] = 'REG'
                 size_val = 0
                 self.apsize_entry.set_value(size_val)
-                self.storage_dict[ap_id]['size'] = size_val
+                self.storage_dict[ap_code]['size'] = size_val
 
-                self.storage_dict[ap_id]['geometry'] = []
+                self.storage_dict[ap_code]['geometry'] = []
 
-                # self.olddia_newdia dict keeps the evidence on current aperture codes as keys and
+                # self.oldapcode_newapcode dict keeps the evidence on current aperture codes as keys and
                 # gets updated on values each time a aperture code is edited or added
-                self.olddia_newdia[ap_id] = ap_id
+                self.oldapcode_newapcode[ap_code] = ap_code
         else:
-            if ap_id not in self.olddia_newdia:
-                self.storage_dict[ap_id] = {}
+            if ap_code not in self.oldapcode_newapcode:
+                self.storage_dict[ap_code] = {}
 
                 type_val = self.aptype_cb.currentText()
-                self.storage_dict[ap_id]['type'] = type_val
+                self.storage_dict[ap_code]['type'] = type_val
 
                 if type_val == 'R' or type_val == 'O':
                     try:
                         dims = self.apdim_entry.get_value()
-                        self.storage_dict[ap_id]['width'] = dims[0]
-                        self.storage_dict[ap_id]['height'] = dims[1]
+                        self.storage_dict[ap_code]['width'] = dims[0]
+                        self.storage_dict[ap_code]['height'] = dims[1]
 
                         size_val = np.sqrt((dims[0] ** 2) + (dims[1] ** 2))
                         self.apsize_entry.set_value(size_val)
@@ -3373,61 +3380,63 @@ class FlatCAMGrbEditor(QtCore.QObject):
                             self.app.inform.emit('[WARNING_NOTCL] %s' %
                                                  _("Aperture size value is missing or wrong format. Add it and retry."))
                             return
-                self.storage_dict[ap_id]['size'] = size_val
+                self.storage_dict[ap_code]['size'] = size_val
 
-                self.storage_dict[ap_id]['geometry'] = []
+                self.storage_dict[ap_code]['geometry'] = []
 
-                # self.olddia_newdia dict keeps the evidence on current aperture codes as keys and gets updated on
+                # self.oldapcode_newapcode dict keeps the evidence on current aperture codes as keys and gets updated on
                 # values  each time a aperture code is edited or added
-                self.olddia_newdia[ap_id] = ap_id
+                self.oldapcode_newapcode[ap_code] = ap_code
             else:
                 self.app.inform.emit('[WARNING_NOTCL] %s' %
                                      _("Aperture already in the aperture table."))
                 return
 
         # since we add a new tool, we update also the initial state of the tool_table through it's dictionary
-        # we add a new entry in the tool2tooldia dict
-        self.tool2tooldia[len(self.olddia_newdia)] = int(ap_id)
+        # we add a new entry in the tid2apcode dict
+        self.tid2apcode[len(self.oldapcode_newapcode)] = int(ap_code)
 
-        self.app.inform.emit('[success] %s: %s' %
-                             (_("Added new aperture with code"), str(ap_id)))
+        self.app.inform.emit('[success] %s: %s' % (_("Added new aperture with code"), str(ap_code)))
 
         self.build_ui()
 
-        self.last_aperture_selected = ap_id
+        self.last_aperture_selected = ap_code
 
-        # make a quick sort through the tool2tooldia dict so we find which row to select
+        # make a quick sort through the tid2apcode dict so we find which row to select
         row_to_be_selected = None
-        for key in sorted(self.tool2tooldia):
-            if self.tool2tooldia[key] == int(ap_id):
+        for key in sorted(self.tid2apcode):
+            if self.tid2apcode[key] == int(ap_code):
                 row_to_be_selected = int(key) - 1
                 break
         self.apertures_table.selectRow(row_to_be_selected)
 
-    def on_aperture_delete(self, ap_id=None):
+    def on_aperture_delete(self, ap_code=None):
+        """
+        Called for aperture deletion.
+
+        :param ap_code:     An Aperture code; String
+        :return:
+        """
         self.is_modified = True
-        deleted_apcode_list = []
 
         try:
-            if ap_id:
-                if isinstance(ap_id, list):
-                    for dd in ap_id:
-                        deleted_apcode_list.append(dd)
-                else:
-                    deleted_apcode_list.append(ap_id)
+            if ap_code:
+                try:
+                    deleted_apcode_list = [dd for dd in ap_code]
+                except TypeError:
+                    deleted_apcode_list = [ap_code]
             else:
                 # deleted_tool_dia = float(self.apertures_table.item(self.apertures_table.currentRow(), 1).text())
                 if len(self.apertures_table.selectionModel().selectedRows()) == 0:
-                    self.app.inform.emit('[WARNING_NOTCL]%s' %
-                                         _(" Select an aperture in Aperture Table"))
+                    self.app.inform.emit('[WARNING_NOTCL]%s' % _(" Select an aperture in Aperture Table"))
                     return
 
+                deleted_apcode_list = []
                 for index in self.apertures_table.selectionModel().selectedRows():
                     row = index.row()
                     deleted_apcode_list.append(self.apertures_table.item(row, 1).text())
         except Exception as exc:
-            self.app.inform.emit('[WARNING_NOTCL] %s %s' %
-                                 (_("Select an aperture in Aperture Table -->", str(exc))))
+            self.app.inform.emit('[WARNING_NOTCL] %s %s' % (_("Select an aperture in Aperture Table -->", str(exc))))
             return
 
         if deleted_apcode_list:
@@ -3435,23 +3444,13 @@ class FlatCAMGrbEditor(QtCore.QObject):
                 # delete the storage used for that tool
                 self.storage_dict.pop(deleted_aperture, None)
 
-                # I've added this flag_del variable because dictionary don't like
-                # having keys deleted while iterating through them
-                flag_del = []
-                for deleted_tool in self.tool2tooldia:
-                    if self.tool2tooldia[deleted_tool] == deleted_aperture:
-                        flag_del.append(deleted_tool)
-
-                if flag_del:
-                    for aperture_to_be_deleted in flag_del:
+                for deleted_tool in list(self.tid2apcode.keys()):
+                    if self.tid2apcode[deleted_tool] == deleted_aperture:
                         # delete the tool
-                        self.tool2tooldia.pop(aperture_to_be_deleted, None)
-
-                    self.olddia_newdia.pop(deleted_aperture, None)
+                        self.tid2apcode.pop(deleted_tool, None)
 
-                    self.app.inform.emit('[success] %s: %s' %
-                                         (_("Deleted aperture with code"), str(deleted_aperture)))
-                    flag_del.clear()
+                self.oldapcode_newapcode.pop(deleted_aperture, None)
+                self.app.inform.emit('[success] %s: %s' % (_("Deleted aperture with code"), str(deleted_aperture)))
 
         self.plot_all()
         self.build_ui()
@@ -3462,69 +3461,188 @@ class FlatCAMGrbEditor(QtCore.QObject):
         if self.last_aperture_selected in deleted_apcode_list:
             if self.apertures_table.rowCount() == 0:
                 self.on_aperture_add('10')
+                self.last_aperture_selected = '10'
             else:
                 self.last_aperture_selected = self.apertures_table.item(0, 1).text()
 
     def on_tool_edit(self):
+        if self.apertures_table.currentItem() is None:
+            return
 
         # if connected, disconnect the signal from the slot on item_changed as it creates issues
         self.apertures_table.itemChanged.disconnect()
         # self.apertures_table.cellPressed.disconnect()
 
         self.is_modified = True
-        current_table_dia_edited = None
+        val_edited = None
+
+        row_of_item_changed = self.apertures_table.currentRow()
+        col_of_item_changed = self.apertures_table.currentColumn()
 
-        if self.apertures_table.currentItem() is not None:
+        # rows start with 0, tools start with 1 so we adjust the value by 1
+        key_in_tid2apcode = row_of_item_changed + 1
+        ap_code_old = str(self.tid2apcode[key_in_tid2apcode])
+
+        ap_code_new = self.apertures_table.item(row_of_item_changed, 1).text()
+
+        if col_of_item_changed == 1:
+            # we edited the Aperture Code column (int)
             try:
-                current_table_dia_edited = float(self.apertures_table.currentItem().text())
+                val_edited = int(self.apertures_table.currentItem().text())
             except ValueError as e:
-                log.debug("FlatCAMExcEditor.on_tool_edit() --> %s" % str(e))
+                log.debug("FlatCAMGrbEditor.on_tool_edit() --> %s" % str(e))
                 # self.apertures_table.setCurrentItem(None)
+                # we reactivate the signals after the after the tool editing
+                self.apertures_table.itemChanged.connect(self.on_tool_edit)
+                return
+        elif col_of_item_changed == 3:
+            # we edited the Size column (float)
+            try:
+                val_edited = float(self.apertures_table.currentItem().text())
+            except ValueError as e:
+                log.debug("FlatCAMGrbEditor.on_tool_edit() --> %s" % str(e))
+                # self.apertures_table.setCurrentItem(None)
+                # we reactivate the signals after the after the tool editing
+                self.apertures_table.itemChanged.connect(self.on_tool_edit)
+                return
+        elif col_of_item_changed == 4:
+            # we edit the Dimensions column (tuple)
+            try:
+                val_edited = [
+                    float(x.strip()) for x in self.apertures_table.currentItem().text().split(",") if x != ''
+                ]
+            except ValueError as e:
+                log.debug("FlatCAMGrbEditor.on_tool_edit() --> %s" % str(e))
+                # we reactivate the signals after the after the tool editing
+                self.apertures_table.itemChanged.connect(self.on_tool_edit)
                 return
 
-        row_of_item_changed = self.apertures_table.currentRow()
-
-        # rows start with 0, tools start with 1 so we adjust the value by 1
-        key_in_tool2tooldia = row_of_item_changed + 1
+            if len(val_edited) != 2:
+                self.app.inform.emit("[WARNING_NOTCL] %s" % _("Dimensions need two float values separated by comma."))
+                old_dims_txt = '%s, %s' % (str(self.storage_dict[ap_code_new]['width']),
+                                           str(self.storage_dict[ap_code_new]['height']))
 
-        dia_changed = self.tool2tooldia[key_in_tool2tooldia]
+                self.apertures_table.currentItem().setText(old_dims_txt)
+                # we reactivate the signals after the after the tool editing
+                self.apertures_table.itemChanged.connect(self.on_tool_edit)
+                return
+            else:
+                self.app.inform.emit("[success] %s" % _("Dimensions edited."))
 
-        # aperture code is not used so we create a new tool with the desired diameter
-        if current_table_dia_edited not in self.olddia_newdia.values():
-            # update the dict that holds as keys our initial diameters and as values the edited diameters
-            self.olddia_newdia[dia_changed] = current_table_dia_edited
-            # update the dict that holds tool_no as key and tool_dia as value
-            self.tool2tooldia[key_in_tool2tooldia] = current_table_dia_edited
+        # In case we edited the Aperture Code therefore the val_edited holds a new Aperture Code
+        # TODO Edit of the Aperture Code is not active yet
+        if col_of_item_changed == 1:
+            # aperture code is not used so we create a new Aperture with the desired Aperture Code
+            if val_edited not in self.oldapcode_newapcode.values():
+                # update the dict that holds as keys old Aperture Codes and as values the new Aperture Codes
+                self.oldapcode_newapcode[ap_code_old] = val_edited
+                # update the dict that holds tool_no as key and tool_dia as value
+                self.tid2apcode[key_in_tid2apcode] = val_edited
 
-            # update the tool offset
-            modified_offset = self.gerber_obj.tool_offset.pop(dia_changed)
-            self.gerber_obj.tool_offset[current_table_dia_edited] = modified_offset
+                old_aperture_val = self.storage_dict.pop(ap_code_old)
+                self.storage_dict[val_edited] = old_aperture_val
 
-            self.plot_all()
-        else:
-            # aperture code is already in use so we move the pads from the prior tool to the new tool
-            factor = current_table_dia_edited / dia_changed
+            else:
+                # aperture code is already in use so we move the pads from the prior tool to the new tool
+                # but only if they are of the same type
+
+                if self.storage_dict[ap_code_old]['type'] == self.storage_dict[ap_code_new]['type']:
+                    # TODO I have to work here; if type == 'R' or 'O' have t otake care of all attributes ...
+                    factor = val_edited / float(ap_code_old)
+                    geometry = []
+                    for geo_el in self.storage_dict[ap_code_old]:
+                        geometric_data = geo_el.geo
+                        new_geo_el = {}
+                        if 'solid' in geometric_data:
+                            new_geo_el['solid'] = deepcopy(affinity.scale(geometric_data['solid'],
+                                                                          xfact=factor, yfact=factor))
+                        if 'follow' in geometric_data:
+                            new_geo_el['follow'] = deepcopy(affinity.scale(geometric_data['follow'],
+                                                                           xfact=factor, yfact=factor))
+                        if 'clear' in geometric_data:
+                            new_geo_el['clear'] = deepcopy(affinity.scale(geometric_data['clear'],
+                                                                          xfact=factor, yfact=factor))
+                        geometry.append(new_geo_el)
+
+                    self.add_gerber_shape(geometry, self.storage_dict[val_edited])
+
+                    self.on_aperture_delete(apcode=ap_code_old)
+
+        # In case we edited the Size of the Aperture therefore the val_edited holds the new Aperture Size
+        # It will happen only for the Aperture Type == 'C' - I make sure of that in the self.build_ui()
+        elif col_of_item_changed == 3:
+            old_size = float(self.storage_dict[ap_code_old]['size'])
+            new_size = float(val_edited)
+            adjust_size = (new_size - old_size) / 2
             geometry = []
-            for geo_el in self.storage_dict[dia_changed]:
-                geometric_data = geo_el.geo
+            for geo_el in self.storage_dict[ap_code_old]['geometry']:
+                g_data = geo_el.geo
                 new_geo_el = {}
-                if 'solid' in geometric_data:
-                    new_geo_el['solid'] = deepcopy(affinity.scale(geometric_data['solid'],
-                                                                  xfact=factor, yfact=factor))
-                if 'follow' in geometric_data:
-                    new_geo_el['follow'] = deepcopy(affinity.scale(geometric_data['follow'],
-                                                                   xfact=factor, yfact=factor))
-                if 'clear' in geometric_data:
-                    new_geo_el['clear'] = deepcopy(affinity.scale(geometric_data['clear'],
-                                                                  xfact=factor, yfact=factor))
-                geometry.append(new_geo_el)
-
-            self.add_gerber_shape(geometry, self.storage_dict[current_table_dia_edited])
+                if 'solid' in g_data:
+                    if 'follow' in g_data:
+                        if isinstance(g_data['follow'], Point):
+                            new_geo_el['solid'] = deepcopy(g_data['solid'].buffer(adjust_size))
+                        else:
+                            new_geo_el['solid'] = deepcopy(g_data['solid'].buffer(adjust_size, join_style=2))
+                if 'follow' in g_data:
+                    new_geo_el['follow'] = deepcopy(g_data['follow'])
+                if 'clear' in g_data:
+                    new_geo_el['clear'] = deepcopy(g_data['clear'].buffer(adjust_size, join_style=2))
+                geometry.append(DrawToolShape(new_geo_el))
+
+            self.storage_dict[ap_code_old]['geometry'].clear()
+            self.add_gerber_shape(geometry, self.storage_dict[ap_code_old]['geometry'])
+            # self.storage_dict[ap_code_old]['geometry'] = geometry
+
+        # In case we edited the Dims of the Aperture therefore the val_edited holds a list with the dimensions
+        # in the format [width, height]
+        # It will happen only for the Aperture Type in ['R', 'O'] - I make sure of that in the self.build_ui()
+        # and below
+        elif col_of_item_changed == 4:
+            if str(self.storage_dict[ap_code_old]['type']) == 'R' or str(self.storage_dict[ap_code_old]['type']) == 'O':
+                # use the biggest from them
+                buff_val_lines = max(val_edited)
+                new_width = val_edited[0]
+                new_height = val_edited[1]
+
+                geometry = []
+                for geo_el in self.storage_dict[ap_code_old]['geometry']:
+                    g_data = geo_el.geo
+                    new_geo_el = {}
+                    if 'solid' in g_data:
+                        if 'follow' in g_data:
+                            if isinstance(g_data['follow'], Point):
+                                x = g_data['follow'].x
+                                y = g_data['follow'].y
+                                minx = x - (new_width / 2)
+                                miny = y - (new_height / 2)
+                                maxx = x + (new_width / 2)
+                                maxy = y + (new_height / 2)
+                                geo = box(minx=minx, miny=miny, maxx=maxx, maxy=maxy)
+                                new_geo_el['solid'] = deepcopy(geo)
+                            else:
+                                new_geo_el['solid'] = deepcopy(g_data['solid'].buffer(buff_val_lines))
+                    if 'follow' in g_data:
+                        new_geo_el['follow'] = deepcopy(g_data['follow'])
+                    if 'clear' in g_data:
+                        if 'follow' in g_data:
+                            if isinstance(g_data['follow'], Point):
+                                x = g_data['follow'].x
+                                y = g_data['follow'].y
+                                minx = x - (new_width / 2)
+                                miny = y - (new_height / 2)
+                                maxx = x + (new_width / 2)
+                                maxy = y + (new_height / 2)
+                                geo = box(minx=minx, miny=miny, maxx=maxx, maxy=maxy)
+                                new_geo_el['clear'] = deepcopy(geo)
+                            else:
+                                new_geo_el['clear'] = deepcopy(g_data['clear'].buffer(buff_val_lines, join_style=2))
+                    geometry.append(DrawToolShape(new_geo_el))
 
-            self.on_aperture_delete(apid=dia_changed)
+                self.storage_dict[ap_code_old]['geometry'].clear()
+                self.add_gerber_shape(geometry, self.storage_dict[ap_code_old]['geometry'])
 
-            # delete the tool offset
-            self.gerber_obj.tool_offset.pop(dia_changed, None)
+        self.plot_all()
 
         # we reactivate the signals after the after the tool editing
         self.apertures_table.itemChanged.connect(self.on_tool_edit)
@@ -3557,12 +3675,12 @@ class FlatCAMGrbEditor(QtCore.QObject):
         # init working objects
         self.storage_dict = {}
         self.current_storage = []
-        self.sorted_apid = []
+        self.sorted_apcode = []
         self.new_apertures = {}
         self.new_aperture_macros = {}
         self.grb_plot_promises = []
-        self.olddia_newdia = {}
-        self.tool2tooldia = {}
+        self.oldapcode_newapcode = {}
+        self.tid2apcode = {}
 
         self.shapes.enabled = True
         self.tool_shape.enabled = True
@@ -3585,7 +3703,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
         # start with GRID toolbar activated
         if self.app.ui.grid_snap_btn.isChecked() is False:
             self.app.ui.grid_snap_btn.trigger()
-            self.app.on_grid_snap_triggered(state=True)
+            self.app.ui.on_grid_snap_triggered(state=True)
 
         # adjust the visibility of some of the canvas context menu
         self.app.ui.popmenu_edit.setVisible(False)
@@ -3605,6 +3723,8 @@ class FlatCAMGrbEditor(QtCore.QObject):
         except Exception as e:
             log.debug("FlatCAMGrbEditor.deactivate_grb_editor() --> %s" % str(e))
 
+        self.clear()
+
         # adjust the status of the menu entries related to the editor
         self.app.ui.menueditedit.setDisabled(False)
         self.app.ui.menueditok.setDisabled(True)
@@ -3613,7 +3733,6 @@ class FlatCAMGrbEditor(QtCore.QObject):
         self.app.ui.popmenu_save.setVisible(False)
 
         self.disconnect_canvas_event_handlers()
-        self.clear()
         self.app.ui.grb_edit_toolbar.setDisabled(True)
 
         settings = QSettings("Open Source", "FlatCAM")
@@ -3821,8 +3940,12 @@ class FlatCAMGrbEditor(QtCore.QObject):
             pass
 
     def clear(self):
+        self.thread.quit()
+
         self.active_tool = None
         self.selected = []
+        self.storage_dict.clear()
+        self.results.clear()
 
         self.shapes.clear(update=True)
         self.tool_shape.clear(update=True)
@@ -3852,7 +3975,16 @@ class FlatCAMGrbEditor(QtCore.QObject):
 
         file_units = self.gerber_obj.units if self.gerber_obj.units else 'IN'
         app_units = self.app.defaults['units']
-        self.conversion_factor = 25.4 if file_units == 'IN' else (1 / 25.4) if file_units != app_units else 1
+        # self.conversion_factor = 25.4 if file_units == 'IN' else (1 / 25.4) if file_units != app_units else 1
+
+        if file_units == app_units:
+            self.conversion_factor = 1
+        else:
+            if file_units == 'IN':
+                self.conversion_factor = 25.4
+            else:
+                self.conversion_factor = 0.0393700787401575
+
 
         # Hide original geometry
         orig_grb_obj.visible = False
@@ -3870,18 +4002,20 @@ class FlatCAMGrbEditor(QtCore.QObject):
 
         # apply the conversion factor on the obj.apertures
         conv_apertures = deepcopy(self.gerber_obj.apertures)
-        for apid in self.gerber_obj.apertures:
-            for key in self.gerber_obj.apertures[apid]:
+        for apcode in self.gerber_obj.apertures:
+            for key in self.gerber_obj.apertures[apcode]:
                 if key == 'width':
-                    conv_apertures[apid]['width'] = self.gerber_obj.apertures[apid]['width'] * self.conversion_factor
+                    conv_apertures[apcode]['width'] = self.gerber_obj.apertures[apcode]['width'] * \
+                                                      self.conversion_factor
                 elif key == 'height':
-                    conv_apertures[apid]['height'] = self.gerber_obj.apertures[apid]['height'] * self.conversion_factor
+                    conv_apertures[apcode]['height'] = self.gerber_obj.apertures[apcode]['height'] * \
+                                                       self.conversion_factor
                 elif key == 'diam':
-                    conv_apertures[apid]['diam'] = self.gerber_obj.apertures[apid]['diam'] * self.conversion_factor
+                    conv_apertures[apcode]['diam'] = self.gerber_obj.apertures[apcode]['diam'] * self.conversion_factor
                 elif key == 'size':
-                    conv_apertures[apid]['size'] = self.gerber_obj.apertures[apid]['size'] * self.conversion_factor
+                    conv_apertures[apcode]['size'] = self.gerber_obj.apertures[apcode]['size'] * self.conversion_factor
                 else:
-                    conv_apertures[apid][key] = self.gerber_obj.apertures[apid][key]
+                    conv_apertures[apcode][key] = self.gerber_obj.apertures[apcode][key]
 
         self.gerber_obj.apertures = conv_apertures
         self.gerber_obj.units = app_units
@@ -3916,9 +4050,9 @@ class FlatCAMGrbEditor(QtCore.QObject):
         #
         # # we create a job work each aperture, job that work in a threaded way to store the geometry in local storage
         # # as DrawToolShapes
-        # for ap_id in self.gerber_obj.apertures:
-        #     self.grb_plot_promises.append(ap_id)
-        #     self.app.worker_task.emit({'fcn': job_thread, 'params': [ap_id]})
+        # for ap_code in self.gerber_obj.apertures:
+        #     self.grb_plot_promises.append(ap_code)
+        #     self.app.worker_task.emit({'fcn': job_thread, 'params': [ap_code]})
         #
         # self.set_ui()
         #
@@ -3971,10 +4105,10 @@ class FlatCAMGrbEditor(QtCore.QObject):
 
                     # we subtract the big "negative" (clear) geometry from each solid polygon but only the part of
                     # clear geometry that fits inside the solid. otherwise we may loose the solid
-                    for ap_id in app_obj.gerber_obj.apertures:
+                    for ap_code in app_obj.gerber_obj.apertures:
                         temp_solid_geometry = []
-                        if 'geometry' in app_obj.gerber_obj.apertures[ap_id]:
-                            # for elem in self.gerber_obj.apertures[apid]['geometry']:
+                        if 'geometry' in app_obj.gerber_obj.apertures[ap_code]:
+                            # for elem in self.gerber_obj.apertures[apcode]['geometry']:
                             #     if 'solid' in elem:
                             #         solid_geo = elem['solid']
                             #         for clear_geo in global_clear_geo:
@@ -4001,7 +4135,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
                             #             if 'follow' in elem:
                             #                 new_elem['follow'] = solid_geo
                             #             temp_elem.append(deepcopy(new_elem))
-                            for elem in app_obj.gerber_obj.apertures[ap_id]['geometry']:
+                            for elem in app_obj.gerber_obj.apertures[ap_code]['geometry']:
                                 new_elem = {}
                                 if 'solid' in elem:
                                     solid_geo = elem['solid']
@@ -4022,15 +4156,15 @@ class FlatCAMGrbEditor(QtCore.QObject):
                                     new_elem['follow'] = elem['follow']
                                 temp_solid_geometry.append(deepcopy(new_elem))
 
-                            app_obj.gerber_obj.apertures[ap_id]['geometry'] = deepcopy(temp_solid_geometry)
+                            app_obj.gerber_obj.apertures[ap_code]['geometry'] = deepcopy(temp_solid_geometry)
 
                     log.warning("Polygon difference done for %d apertures." % len(app_obj.gerber_obj.apertures))
 
                     try:
                         # Loading the Geometry into Editor Storage
-                        for ap_id, ap_dict in app_obj.gerber_obj.apertures.items():
+                        for ap_code, ap_dict in app_obj.gerber_obj.apertures.items():
                             app_obj.results.append(
-                                app_obj.pool.apply_async(app_obj.add_apertures, args=(ap_id, ap_dict))
+                                app_obj.pool.apply_async(app_obj.add_apertures, args=(ap_code, ap_dict))
                             )
                     except Exception as ee:
                         log.debug(
@@ -4110,8 +4244,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
         else:
             new_grb_name = self.edited_obj_name + "_edit"
 
-        self.app.worker_task.emit({'fcn': self.new_edited_gerber,
-                                   'params': [new_grb_name, self.storage_dict]})
+        self.app.worker_task.emit({'fcn': self.new_edited_gerber, 'params': [new_grb_name, self.storage_dict]})
 
     @staticmethod
     def update_options(obj):
@@ -4158,12 +4291,12 @@ class FlatCAMGrbEditor(QtCore.QObject):
             poly_buffer = []
             follow_buffer = []
 
-            for storage_apid, storage_val in local_storage_dict.items():
-                grb_obj.apertures[storage_apid] = {}
+            for storage_apcode, storage_val in local_storage_dict.items():
+                grb_obj.apertures[storage_apcode] = {}
 
                 for k, val in storage_val.items():
                     if k == 'geometry':
-                        grb_obj.apertures[storage_apid][k] = []
+                        grb_obj.apertures[storage_apcode][k] = []
                         for geo_el in val:
                             geometric_data = geo_el.geo
                             new_geo_el = {}
@@ -4190,9 +4323,9 @@ class FlatCAMGrbEditor(QtCore.QObject):
                                 new_geo_el['clear'] = geometric_data['clear']
 
                             if new_geo_el:
-                                grb_obj.apertures[storage_apid][k].append(deepcopy(new_geo_el))
+                                grb_obj.apertures[storage_apcode][k].append(deepcopy(new_geo_el))
                     else:
-                        grb_obj.apertures[storage_apid][k] = val
+                        grb_obj.apertures[storage_apcode][k] = val
 
             grb_obj.aperture_macros = deepcopy(self.gerber_obj.aperture_macros)
 
@@ -4284,28 +4417,28 @@ class FlatCAMGrbEditor(QtCore.QObject):
                 self.active_tool = FCApertureSelect(self)
 
     def on_row_selected(self, row, col):
-        if col == 0:
-            key_modifier = QtWidgets.QApplication.keyboardModifiers()
-            if self.app.defaults["global_mselect_key"] == 'Control':
-                modifier_to_use = Qt.ControlModifier
-            else:
-                modifier_to_use = Qt.ShiftModifier
+        # if col == 0:
+        key_modifier = QtWidgets.QApplication.keyboardModifiers()
+        if self.app.defaults["global_mselect_key"] == 'Control':
+            modifier_to_use = Qt.ControlModifier
+        else:
+            modifier_to_use = Qt.ShiftModifier
 
-            if key_modifier == modifier_to_use:
-                pass
-            else:
-                self.selected = []
+        if key_modifier == modifier_to_use:
+            pass
+        else:
+            self.selected = []
 
-            try:
-                selected_ap_id = self.apertures_table.item(row, 1).text()
-                self.last_aperture_selected = copy(selected_ap_id)
+        try:
+            selected_ap_code = self.apertures_table.item(row, 1).text()
+            self.last_aperture_selected = copy(selected_ap_code)
 
-                for obj in self.storage_dict[selected_ap_id]['geometry']:
-                    self.selected.append(obj)
-            except Exception as e:
-                self.app.log.debug(str(e))
+            for obj in self.storage_dict[selected_ap_code]['geometry']:
+                self.selected.append(obj)
+        except Exception as e:
+            self.app.log.debug(str(e))
 
-            self.plot_all()
+        self.plot_all()
 
     # def toolbar_tool_toggle(self, key):
     #     """
@@ -4809,6 +4942,39 @@ class FlatCAMGrbEditor(QtCore.QObject):
     #     self.app.app_cursor.enabled = False
     #     self.app.app_cursor.enabled = True
 
+    def on_zoom_fit(self):
+        """
+        Callback for zoom-fit request in Gerber Editor
+
+        :return:        None
+        """
+        log.debug("FlatCAMGrbEditor.on_zoom_fit()")
+
+        # calculate all the geometry in the edited Gerber object
+        edit_geo = []
+        for ap_code in self.storage_dict:
+            for geo_el in self.storage_dict[ap_code]['geometry']:
+                actual_geo = geo_el.geo
+                if 'solid' in actual_geo:
+                    edit_geo.append(actual_geo['solid'])
+
+        all_geo = cascaded_union(edit_geo)
+
+        # calculate the bounds values for the edited Gerber object
+        xmin, ymin, xmax, ymax = all_geo.bounds
+
+        if self.app.is_legacy is False:
+            new_rect = Rect(xmin, ymin, xmax, ymax)
+            self.app.plotcanvas.fit_view(rect=new_rect)
+        else:
+            width = xmax - xmin
+            height = ymax - ymin
+            xmin -= 0.05 * width
+            xmax += 0.05 * width
+            ymin -= 0.05 * height
+            ymax += 0.05 * height
+            self.app.plotcanvas.adjust_axes(xmin, ymin, xmax, ymax)
+
     def get_selected(self):
         """
         Returns list of shapes that are selected in the editor.
@@ -4980,11 +5146,11 @@ class FlatCAMGrbEditor(QtCore.QObject):
 
         for x in self.apertures_table.selectedItems():
             try:
-                apid = self.apertures_table.item(x.row(), 1).text()
+                apcode = self.apertures_table.item(x.row(), 1).text()
 
-                temp_storage = deepcopy(buffer_recursion(self.storage_dict[apid]['geometry'], self.selected))
-                self.storage_dict[apid]['geometry'] = []
-                self.storage_dict[apid]['geometry'] = temp_storage
+                temp_storage = deepcopy(buffer_recursion(self.storage_dict[apcode]['geometry'], self.selected))
+                self.storage_dict[apcode]['geometry'] = []
+                self.storage_dict[apcode]['geometry'] = temp_storage
             except Exception as e:
                 log.debug("FlatCAMGrbEditor.buffer() --> %s" % str(e))
                 self.app.inform.emit('[ERROR_NOTCL] %s\n%s' % (_("Failed."), str(traceback.print_exc())))
@@ -5043,11 +5209,11 @@ class FlatCAMGrbEditor(QtCore.QObject):
 
         for x in self.apertures_table.selectedItems():
             try:
-                apid = self.apertures_table.item(x.row(), 1).text()
+                apcode = self.apertures_table.item(x.row(), 1).text()
 
-                temp_storage = deepcopy(scale_recursion(self.storage_dict[apid]['geometry'], self.selected))
-                self.storage_dict[apid]['geometry'] = []
-                self.storage_dict[apid]['geometry'] = temp_storage
+                temp_storage = deepcopy(scale_recursion(self.storage_dict[apcode]['geometry'], self.selected))
+                self.storage_dict[apcode]['geometry'] = []
+                self.storage_dict[apcode]['geometry'] = temp_storage
 
             except Exception as e:
                 log.debug("FlatCAMGrbEditor.on_scale() --> %s" % str(e))
@@ -5065,9 +5231,9 @@ class FlatCAMGrbEditor(QtCore.QObject):
         text = []
         position = []
 
-        for apid in self.storage_dict:
-            if 'geometry' in self.storage_dict[apid]:
-                for geo_el in self.storage_dict[apid]['geometry']:
+        for apcode in self.storage_dict:
+            if 'geometry' in self.storage_dict[apcode]:
+                for geo_el in self.storage_dict[apcode]['geometry']:
                     if 'solid' in geo_el.geo:
                         area = geo_el.geo['solid'].area
                         try:

+ 2 - 2
flatcamEditors/FlatCAMTextEditor.py

@@ -25,8 +25,8 @@ if '_' not in builtins.__dict__:
 
 class TextEditor(QtWidgets.QWidget):
 
-    def __init__(self, app, text=None, plain_text=None):
-        super().__init__()
+    def __init__(self, app, text=None, plain_text=None, parent=None):
+        super().__init__(parent=parent)
 
         self.app = app
         self.plain_text = plain_text

+ 91 - 9
flatcamGUI/FlatCAMGUI.py

@@ -10,16 +10,28 @@
 # File Modified (major mod): Marius Adrian Stanciu         #
 # Date: 3/10/2019                                          #
 # ##########################################################
-
-from flatcamGUI.PreferencesUI import *
+import platform
+
+from flatcamGUI.GUIElements import *
+from flatcamGUI.preferences import settings
+from flatcamGUI.preferences.cncjob.CNCJobPreferencesUI import CNCJobPreferencesUI
+from flatcamGUI.preferences.excellon.ExcellonPreferencesUI import ExcellonPreferencesUI
+from flatcamGUI.preferences.general.GeneralPreferencesUI import GeneralPreferencesUI
+from flatcamGUI.preferences.geometry.GeometryPreferencesUI import GeometryPreferencesUI
+from flatcamGUI.preferences.gerber.GerberPreferencesUI import GerberPreferencesUI
 from flatcamEditors.FlatCAMGeoEditor import FCShapeTool
 from matplotlib.backend_bases import KeyEvent as mpl_key_event
 
 import webbrowser
+
+from flatcamGUI.preferences.tools.Tools2PreferencesUI import Tools2PreferencesUI
+from flatcamGUI.preferences.tools.ToolsPreferencesUI import ToolsPreferencesUI
+from flatcamGUI.preferences.utilities.UtilPreferencesUI import UtilPreferencesUI
 from flatcamObjects.ObjectCollection import KeySensitiveListView
 
 import subprocess
 import os
+import sys
 import gettext
 import FlatCAMTranslation as fcTranslate
 import builtins
@@ -1169,7 +1181,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
         self.plot_tab_area.setTabsClosable(True)
 
         self.plot_tab = QtWidgets.QWidget()
-        self.plot_tab.setObjectName("plotarea")
+        self.plot_tab.setObjectName("plotarea_tab")
         self.plot_tab_area.addTab(self.plot_tab, _("Plot Area"))
 
         self.right_layout = QtWidgets.QVBoxLayout()
@@ -2354,7 +2366,6 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
         # ########################################################################
         # ######################## BUILD PREFERENCES #############################
         # ########################################################################
-
         self.general_defaults_form = GeneralPreferencesUI(decimals=self.decimals)
         self.gerber_defaults_form = GerberPreferencesUI(decimals=self.decimals)
         self.excellon_defaults_form = ExcellonPreferencesUI(decimals=self.decimals)
@@ -2369,7 +2380,6 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
         # ########################################################################
         # ################## RESTORE THE TOOLBAR STATE from file #################
         # ########################################################################
-
         flat_settings = QSettings("Open Source", "FlatCAM")
         if flat_settings.contains("saved_gui_state"):
             saved_gui_state = flat_settings.value('saved_gui_state')
@@ -2427,15 +2437,42 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
             del qsettings
 
         self.lock_toolbar(lock=lock_state)
+        self.on_grid_snap_triggered(state=True)
+
         self.lock_action.triggered[bool].connect(self.lock_toolbar)
 
         self.pref_open_button.clicked.connect(self.on_preferences_open_folder)
         self.clear_btn.clicked.connect(self.on_gui_clear)
+        self.grid_snap_btn.triggered.connect(self.on_grid_snap_triggered)
+        self.snap_infobar_label.clicked.connect(self.on_grid_icon_snap_clicked)
 
         # %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
         # %%%%%%%%%%%%%%%%% GUI Building FINISHED %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
         # %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
+    def on_grid_snap_triggered(self, state):
+        """
+
+        :param state:   A parameter with the state of the grid, boolean
+
+        :return:
+        """
+        if state:
+            self.snap_infobar_label.setPixmap(QtGui.QPixmap(self.app.resource_location + '/snap_filled_16.png'))
+        else:
+            self.snap_infobar_label.setPixmap(QtGui.QPixmap(self.app.resource_location + '/snap_16.png'))
+
+        self.snap_infobar_label.clicked_state = state
+
+    def on_grid_icon_snap_clicked(self):
+        """
+        Slot called by clicking a GUI element, in this case a FCLabel
+
+        :return:
+        """
+        if isinstance(self.sender(), FCLabel):
+            self.grid_snap_btn.trigger()
+
     def eventFilter(self, obj, event):
         """
         Filter the ToolTips display based on a Preferences setting
@@ -2853,14 +2890,17 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
             key = event.key
 
         if self.app.call_source == 'app':
+            # CTRL + ALT
             if modifiers == QtCore.Qt.ControlModifier | QtCore.Qt.AltModifier:
                 if key == QtCore.Qt.Key_X:
                     self.app.abort_all_tasks()
                     return
+            # CTRL + SHIFT
             if modifiers == QtCore.Qt.ControlModifier | QtCore.Qt.ShiftModifier:
                 if key == QtCore.Qt.Key_S:
                     self.app.on_file_saveprojectas()
                     return
+            # CTRL
             elif modifiers == QtCore.Qt.ControlModifier:
                 # Select All
                 if key == QtCore.Qt.Key_A:
@@ -2933,6 +2973,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
                     self.app.on_toggle_plotarea()
 
                 return
+            # SHIFT
             elif modifiers == QtCore.Qt.ShiftModifier:
 
                 # Copy Object Name
@@ -2985,6 +3026,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
                 if key == QtCore.Qt.Key_Y:
                     self.app.on_skewy()
                     return
+            # ALT
             elif modifiers == QtCore.Qt.AltModifier:
                 # Eanble all plots
                 if key == Qt.Key_1:
@@ -3103,6 +3145,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
                 if key == QtCore.Qt.Key_F10 or key == 'F10':
                     self.app.on_fullscreen()
                     return
+            # NO MODIFIER
             elif modifiers == QtCore.Qt.NoModifier:
                 # Open Manual
                 if key == QtCore.Qt.Key_F1 or key == 'F1':
@@ -3189,7 +3232,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
                         else:
                             self.app.collection.set_active(names_list[active_index-1])
 
-                # Select the object in the Tree bellow the current one
+                # Select the object in the Tree below the current one
                 if key == QtCore.Qt.Key_Down:
                     # make sure it works only for the Project Tab who is an instance of KeySensitiveListView
                     focused_wdg = QtWidgets.QApplication.focusWidget()
@@ -3300,6 +3343,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
 
                 return
         elif self.app.call_source == 'geo_editor':
+            # CTRL
             if modifiers == QtCore.Qt.ControlModifier:
                 # save (update) the current geometry and return to the App
                 if key == QtCore.Qt.Key_S or key == 'S':
@@ -3329,6 +3373,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
                         messagebox.setDefaultButton(QtWidgets.QMessageBox.Ok)
                         messagebox.exec_()
                     return
+            # SHIFT
             elif modifiers == QtCore.Qt.ShiftModifier:
                 # Run Distance Minimum Tool
                 if key == QtCore.Qt.Key_M or key == 'M':
@@ -3344,6 +3389,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
                 if key == QtCore.Qt.Key_Y or key == 'Y':
                     self.app.geo_editor.transform_tool.on_skewy_key()
                     return
+            # ALT
             elif modifiers == QtCore.Qt.AltModifier:
 
                 # Transformation Tool
@@ -3360,6 +3406,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
                 if key == QtCore.Qt.Key_Y or key == 'Y':
                     self.app.geo_editor.transform_tool.on_offy_key()
                     return
+            # NO MODIFIER
             elif modifiers == QtCore.Qt.NoModifier:
                 # toggle display of Notebook area
                 if key == QtCore.Qt.Key_QuoteLeft or key == '`':
@@ -3567,6 +3614,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
                 if key == 'F3':
                     self.app.on_shortcut_list()
         elif self.app.call_source == 'grb_editor':
+            # CTRL
             if modifiers == QtCore.Qt.ControlModifier:
                 # Eraser Tool
                 if key == QtCore.Qt.Key_E or key == 'E':
@@ -3582,11 +3630,13 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
                 if key == QtCore.Qt.Key_M or key == 'M':
                     self.app.distance_tool.run()
                     return
+            # SHIFT
             elif modifiers == QtCore.Qt.ShiftModifier:
                 # Run Distance Minimum Tool
                 if key == QtCore.Qt.Key_M or key == 'M':
                     self.app.distance_min_tool.run()
                     return
+            # ALT
             elif modifiers == QtCore.Qt.AltModifier:
                 # Mark Area Tool
                 if key == QtCore.Qt.Key_A or key == 'A':
@@ -3601,6 +3651,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
                 if key == QtCore.Qt.Key_R or key == 'R':
                     self.app.grb_editor.on_transform()
                     return
+            # NO MODIFIER
             elif modifiers == QtCore.Qt.NoModifier:
                 # Abort the current action
                 if key == QtCore.Qt.Key_Escape or key == 'Escape':
@@ -3785,10 +3836,10 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
                         self.app.grb_editor.select_tool('track')
                         return
 
-                    # Zoom Fit
+                    # Zoom fit
                     if key == QtCore.Qt.Key_V or key == 'V':
                         self.app.grb_editor.launched_from_shortcuts = True
-                        self.app.on_zoom_fit(None)
+                        self.app.grb_editor.on_zoom_fit()
                         return
 
                 # Show Shortcut list
@@ -3796,6 +3847,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
                     self.app.on_shortcut_list()
                     return
         elif self.app.call_source == 'exc_editor':
+            # CTRL
             if modifiers == QtCore.Qt.ControlModifier:
                 # save (update) the current geometry and return to the App
                 if key == QtCore.Qt.Key_S or key == 'S':
@@ -3806,13 +3858,16 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
                 if key == QtCore.Qt.Key_M or key == 'M':
                     self.app.distance_tool.run()
                     return
+            # SHIFT
             elif modifiers == QtCore.Qt.ShiftModifier:
                 # Run Distance Minimum Tool
                 if key == QtCore.Qt.Key_M or key == 'M':
                     self.app.distance_min_tool.run()
                     return
+            # ALT
             elif modifiers == QtCore.Qt.AltModifier:
                 pass
+            # NO MODIFIER
             elif modifiers == QtCore.Qt.NoModifier:
                 # Abort the current action
                 if key == QtCore.Qt.Key_Escape or key == 'Escape':
@@ -4025,6 +4080,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
                 pass
             elif modifiers == QtCore.Qt.ShiftModifier:
                 pass
+            # NO MODIFIER
             elif modifiers == QtCore.Qt.NoModifier:
                 if key == QtCore.Qt.Key_Escape or key == 'Escape':
                     # abort the measurement action
@@ -4040,6 +4096,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
                 if key == QtCore.Qt.Key_J or key == 'J':
                     self.app.on_jump_to()
         elif self.app.call_source == 'qrcode_tool':
+            # CTRL + ALT
             if modifiers == QtCore.Qt.ControlModifier | QtCore.Qt.AltModifier:
                 if key == QtCore.Qt.Key_X:
                     self.app.abort_all_tasks()
@@ -4051,6 +4108,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
                 pass
             elif modifiers == QtCore.Qt.AltModifier:
                 pass
+            # NO MODIFIER
             elif modifiers == QtCore.Qt.NoModifier:
                 # Escape = Deselect All
                 if key == QtCore.Qt.Key_Escape or key == 'Escape':
@@ -4064,17 +4122,18 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
                 if key == QtCore.Qt.Key_J:
                     self.app.on_jump_to()
         elif self.app.call_source == 'copper_thieving_tool':
+            # CTRL + ALT
             if modifiers == QtCore.Qt.ControlModifier | QtCore.Qt.AltModifier:
                 if key == QtCore.Qt.Key_X:
                     self.app.abort_all_tasks()
                     return
-
             elif modifiers == QtCore.Qt.ControlModifier:
                 pass
             elif modifiers == QtCore.Qt.ShiftModifier:
                 pass
             elif modifiers == QtCore.Qt.AltModifier:
                 pass
+            # NO MODIFIER
             elif modifiers == QtCore.Qt.NoModifier:
                 # Escape = Deselect All
                 if key == QtCore.Qt.Key_Escape or key == 'Escape':
@@ -4087,6 +4146,29 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
                 # Jump to coords
                 if key == QtCore.Qt.Key_J:
                     self.app.on_jump_to()
+        elif self.app.call_source == 'geometry':
+            if modifiers == QtCore.Qt.ControlModifier:
+                pass
+            elif modifiers == QtCore.Qt.AltModifier:
+                pass
+            elif modifiers == QtCore.Qt.ShiftModifier:
+                pass
+            # NO MODIFIER
+            elif modifiers == QtCore.Qt.NoModifier:
+                if key == QtCore.Qt.Key_Escape or key == 'Escape':
+                    sel_obj = self.app.collection.get_active()
+                    assert sel_obj.kind == 'geometry', "Expected a Geometry Object, got %s" % type(sel_obj)
+
+                    sel_obj.area_disconnect()
+                    return
+
+                if key == QtCore.Qt.Key_G or key == 'G':
+                    self.app.ui.grid_snap_btn.trigger()
+                    return
+
+                # Jump to coords
+                if key == QtCore.Qt.Key_J or key == 'J':
+                    self.app.on_jump_to()
 
     def createPopupMenu(self):
         menu = super().createPopupMenu()

+ 3 - 3
flatcamGUI/GUIElements.py

@@ -2076,7 +2076,7 @@ class FCDetachableTab(QtWidgets.QTabWidget):
 
 
 class FCDetachableTab2(FCDetachableTab):
-    tab_closed_signal = QtCore.pyqtSignal(object)
+    tab_closed_signal = QtCore.pyqtSignal(object, int)
 
     def __init__(self, protect=None, protect_by_name=None, parent=None):
         super(FCDetachableTab2, self).__init__(protect=protect, protect_by_name=protect_by_name, parent=parent)
@@ -2089,8 +2089,8 @@ class FCDetachableTab2(FCDetachableTab):
         :return:
         """
         # idx = self.currentIndex()
-        self.tab_name = self.widget(currentIndex).objectName()
-        self.tab_closed_signal.emit(self.tab_name)
+        tab_name = self.widget(currentIndex).objectName()
+        self.tab_closed_signal.emit(tab_name, currentIndex)
 
         self.removeTab(currentIndex)
 

+ 87 - 7
flatcamGUI/ObjectUI.py

@@ -2022,14 +2022,94 @@ class GeometryObjectUI(ObjectUI):
         grid4.addWidget(pp_label, 11, 0)
         grid4.addWidget(self.pp_geometry_name_cb, 11, 1)
 
-        grid4.addWidget(QtWidgets.QLabel(''), 12, 0, 1, 2)
+        # grid4.addWidget(QtWidgets.QLabel(''), 12, 0, 1, 2)
+
+        # Exclusion Areas
+        self.exclusion_cb = FCCheckBox('%s:' % _("Exclusion areas"))
+        self.exclusion_cb.setToolTip(
+            _(
+                "Include exclusion areas.\n"
+                "In those areas the travel of the tools\n"
+                "is forbidden."
+            )
+        )
+        grid4.addWidget(self.exclusion_cb, 12, 0, 1, 2)
+
+        # ------------------------------------------------------------------------------------------------------------
+        # ------------------------- EXCLUSION AREAS ------------------------------------------------------------------
+        # ------------------------------------------------------------------------------------------------------------
+        self.exclusion_frame = QtWidgets.QFrame()
+        self.exclusion_frame.setContentsMargins(0, 0, 0, 0)
+        grid4.addWidget(self.exclusion_frame, 14, 0, 1, 2)
+
+        self.exclusion_box = QtWidgets.QVBoxLayout()
+        self.exclusion_box.setContentsMargins(0, 0, 0, 0)
+        self.exclusion_frame.setLayout(self.exclusion_box)
+
+        h_lay = QtWidgets.QHBoxLayout()
+        self.exclusion_box.addLayout(h_lay)
+
+        # Button Add Area
+        self.add_area_button = QtWidgets.QPushButton(_('Add area'))
+        self.add_area_button.setToolTip(_("Add an Exclusion Area."))
+        h_lay.addWidget(self.add_area_button)
+
+        # Button Delete Area
+        self.delete_area_button = QtWidgets.QPushButton(_('Clear areas'))
+        self.delete_area_button.setToolTip(_("Delete all exclusion areas."))
+        h_lay.addWidget(self.delete_area_button)
+
+        grid_l = QtWidgets.QGridLayout()
+        grid_l.setColumnStretch(0, 0)
+        grid_l.setColumnStretch(1, 1)
+        self.exclusion_box.addLayout(grid_l)
+
+        # Area Selection shape
+        self.area_shape_label = QtWidgets.QLabel('%s:' % _("Shape"))
+        self.area_shape_label.setToolTip(
+            _("The kind of selection shape used for area selection.")
+        )
+
+        self.area_shape_radio = RadioSet([{'label': _("Square"), 'value': 'square'},
+                                          {'label': _("Polygon"), 'value': 'polygon'}])
+
+        grid_l.addWidget(self.area_shape_label, 0, 0)
+        grid_l.addWidget(self.area_shape_radio, 0, 1)
+
+        # Chose Strategy
+        self.strategy_label = FCLabel('%s:' % _("Strategy"))
+        self.strategy_label.setToolTip(_("The strategy followed when encountering an exclusion area.\n"
+                                         "Can be:\n"
+                                         "- Over -> when encountering the area, the tool will go to a set height\n"
+                                         "- Around -> will avoid the exclusion area by going around the area"))
+        self.strategy_radio = RadioSet([{'label': _('Over'), 'value': 'over'},
+                                        {'label': _('Around'), 'value': 'around'}])
+
+        grid_l.addWidget(self.strategy_label, 1, 0)
+        grid_l.addWidget(self.strategy_radio, 1, 1)
+
+        # Over Z
+        self.over_z_label = FCLabel('%s:' % _("Over Z"))
+        self.over_z_label.setToolTip(_("The height Z to which the tool will rise in order to avoid\n"
+                                       "an interdiction area."))
+        self.over_z_entry = FCDoubleSpinner()
+        self.over_z_entry.set_range(0.000, 9999.9999)
+        self.over_z_entry.set_precision(self.decimals)
+
+        grid_l.addWidget(self.over_z_label, 2, 0)
+        grid_l.addWidget(self.over_z_entry, 2, 1)
+
+        # -------------------------- EXCLUSION AREAS END -------------------------------------------------------------
+        # ------------------------------------------------------------------------------------------------------------
+        self.ois_exclusion_geo = OptionalInputSection(self.exclusion_cb, [self.exclusion_frame])
+
         warning_lbl = QtWidgets.QLabel(
             _(
                 "Add / Select at least one tool in the tool-table.\n"
                 "Click the # header to select all, or Ctrl + LMB\n"
                 "for custom selection of tools."
             ))
-        grid4.addWidget(warning_lbl, 13, 0, 1, 2)
+        grid4.addWidget(warning_lbl, 15, 0, 1, 2)
 
         # Button
         self.generate_cnc_button = QtWidgets.QPushButton(_('Generate CNCJob object'))
@@ -2042,9 +2122,9 @@ class GeometryObjectUI(ObjectUI):
                             font-weight: bold;
                         }
                         """)
-        grid4.addWidget(self.generate_cnc_button, 14, 0, 1, 2)
+        grid4.addWidget(self.generate_cnc_button, 17, 0, 1, 2)
 
-        grid4.addWidget(QtWidgets.QLabel(''), 15, 0, 1, 2)
+        grid4.addWidget(QtWidgets.QLabel(''), 19, 0, 1, 2)
 
         # ##############
         # Paint area ##
@@ -2053,7 +2133,7 @@ class GeometryObjectUI(ObjectUI):
         self.tools_label.setToolTip(
             _("Launch Paint Tool in Tools Tab.")
         )
-        grid4.addWidget(self.tools_label, 16, 0, 1, 2)
+        grid4.addWidget(self.tools_label, 20, 0, 1, 2)
 
         # Paint Button
         self.paint_tool_button = QtWidgets.QPushButton(_('Paint Tool'))
@@ -2071,7 +2151,7 @@ class GeometryObjectUI(ObjectUI):
                             font-weight: bold;
                         }
                         """)
-        grid4.addWidget(self.paint_tool_button, 17, 0, 1, 2)
+        grid4.addWidget(self.paint_tool_button, 22, 0, 1, 2)
 
         # NCC Tool
         self.generate_ncc_button = QtWidgets.QPushButton(_('NCC Tool'))
@@ -2085,7 +2165,7 @@ class GeometryObjectUI(ObjectUI):
                             font-weight: bold;
                         }
                         """)
-        grid4.addWidget(self.generate_ncc_button, 18, 0, 1, 2)
+        grid4.addWidget(self.generate_ncc_button, 24, 0, 1, 2)
 
 
 class CNCObjectUI(ObjectUI):

+ 0 - 10003
flatcamGUI/PreferencesUI.py

@@ -1,10003 +0,0 @@
-# ##########################################################
-# FlatCAM: 2D Post-processing for Manufacturing            #
-# File Author: Marius Adrian Stanciu (c)                   #
-# Date: 10/10/2019                                         #
-# MIT Licence                                              #
-#                                                          #
-# Modified by David Robertson   29.04.2020                 #
-# ##########################################################
-import os
-
-from defaults import FlatCAMDefaults
-from flatcamGUI.GUIElements import *
-import platform
-import sys
-import logging
-import gettext
-import FlatCAMTranslation as fcTranslate
-import builtins
-
-log = logging.getLogger('PreferencesUI')
-fcTranslate.apply_language('strings')
-if '_' not in builtins.__dict__:
-    _ = gettext.gettext
-
-settings = QSettings("Open Source", "FlatCAM")
-if settings.contains("machinist"):
-    machinist_setting = settings.value('machinist', type=int)
-else:
-    machinist_setting = 0
-
-
-class PreferencesUIManager:
-
-    def __init__(self, defaults: FlatCAMDefaults, data_path: str, ui, inform):
-        """
-        Class that control the Preferences Tab
-
-        :param defaults:    a dictionary storage where all the application settings are stored
-        :param data_path:   a path to the file where all the preferences are stored for persistence
-        :param ui:          reference to the FlatCAMGUI class which constructs the UI
-        :param inform:      a pyqtSignal used to display information's in the StatusBar of the GUI
-        """
-
-        self.defaults = defaults
-        self.data_path = data_path
-        self.ui = ui
-        self.inform = inform
-        self.ignore_tab_close_event = False
-
-        # if Preferences are changed in the Edit -> Preferences tab the value will be set to True
-        self.preferences_changed_flag = False
-
-        # when adding entries here read the comments in the  method found bellow named:
-        # def new_object(self, kind, name, initialize, active=True, fit=True, plot=True)
-        self.defaults_form_fields = {
-            # General App
-            "decimals_inch": self.ui.general_defaults_form.general_app_group.precision_inch_entry,
-            "decimals_metric": self.ui.general_defaults_form.general_app_group.precision_metric_entry,
-            "units": self.ui.general_defaults_form.general_app_group.units_radio,
-            "global_graphic_engine": self.ui.general_defaults_form.general_app_group.ge_radio,
-            "global_app_level": self.ui.general_defaults_form.general_app_group.app_level_radio,
-            "global_portable": self.ui.general_defaults_form.general_app_group.portability_cb,
-            "global_language": self.ui.general_defaults_form.general_app_group.language_cb,
-
-            "global_systray_icon": self.ui.general_defaults_form.general_app_group.systray_cb,
-            "global_shell_at_startup": self.ui.general_defaults_form.general_app_group.shell_startup_cb,
-            "global_project_at_startup": self.ui.general_defaults_form.general_app_group.project_startup_cb,
-            "global_version_check": self.ui.general_defaults_form.general_app_group.version_check_cb,
-            "global_send_stats": self.ui.general_defaults_form.general_app_group.send_stats_cb,
-
-            "global_worker_number": self.ui.general_defaults_form.general_app_group.worker_number_sb,
-            "global_tolerance": self.ui.general_defaults_form.general_app_group.tol_entry,
-
-            "global_compression_level": self.ui.general_defaults_form.general_app_group.compress_spinner,
-            "global_save_compressed": self.ui.general_defaults_form.general_app_group.save_type_cb,
-            "global_autosave": self.ui.general_defaults_form.general_app_group.autosave_cb,
-            "global_autosave_timeout": self.ui.general_defaults_form.general_app_group.autosave_entry,
-
-            "global_tpdf_tmargin": self.ui.general_defaults_form.general_app_group.tmargin_entry,
-            "global_tpdf_bmargin": self.ui.general_defaults_form.general_app_group.bmargin_entry,
-            "global_tpdf_lmargin": self.ui.general_defaults_form.general_app_group.lmargin_entry,
-            "global_tpdf_rmargin": self.ui.general_defaults_form.general_app_group.rmargin_entry,
-
-            # General GUI Preferences
-            "global_theme": self.ui.general_defaults_form.general_gui_group.theme_radio,
-            "global_gray_icons": self.ui.general_defaults_form.general_gui_group.gray_icons_cb,
-            "global_layout": self.ui.general_defaults_form.general_gui_group.layout_combo,
-            "global_hover": self.ui.general_defaults_form.general_gui_group.hover_cb,
-            "global_selection_shape": self.ui.general_defaults_form.general_gui_group.selection_cb,
-
-            "global_sel_fill": self.ui.general_defaults_form.general_gui_group.sf_color_entry,
-            "global_sel_line": self.ui.general_defaults_form.general_gui_group.sl_color_entry,
-            "global_alt_sel_fill": self.ui.general_defaults_form.general_gui_group.alt_sf_color_entry,
-            "global_alt_sel_line": self.ui.general_defaults_form.general_gui_group.alt_sl_color_entry,
-            "global_draw_color": self.ui.general_defaults_form.general_gui_group.draw_color_entry,
-            "global_sel_draw_color": self.ui.general_defaults_form.general_gui_group.sel_draw_color_entry,
-
-            "global_proj_item_color": self.ui.general_defaults_form.general_gui_group.proj_color_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,
-
-            # General GUI Settings
-            "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_snap_max": self.ui.general_defaults_form.general_app_set_group.snap_max_dist_entry,
-            "global_workspace": self.ui.general_defaults_form.general_app_set_group.workspace_cb,
-            "global_workspaceT": self.ui.general_defaults_form.general_app_set_group.wk_cb,
-            "global_workspace_orientation": self.ui.general_defaults_form.general_app_set_group.wk_orientation_radio,
-
-            "global_cursor_type": self.ui.general_defaults_form.general_app_set_group.cursor_radio,
-            "global_cursor_size": self.ui.general_defaults_form.general_app_set_group.cursor_size_entry,
-            "global_cursor_width": self.ui.general_defaults_form.general_app_set_group.cursor_width_entry,
-            "global_cursor_color_enabled": self.ui.general_defaults_form.general_app_set_group.mouse_cursor_color_cb,
-            "global_cursor_color": self.ui.general_defaults_form.general_app_set_group.mouse_cursor_entry,
-            "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_delete_confirmation": self.ui.general_defaults_form.general_app_set_group.delete_conf_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_machinist_setting": self.ui.general_defaults_form.general_app_set_group.machinist_cb,
-
-            "global_bookmarks_limit": self.ui.general_defaults_form.general_app_set_group.bm_limit_spinner,
-            "global_activity_icon": self.ui.general_defaults_form.general_app_set_group.activity_combo,
-
-            # Gerber General
-            "gerber_plot": self.ui.gerber_defaults_form.gerber_gen_group.plot_cb,
-            "gerber_solid": self.ui.gerber_defaults_form.gerber_gen_group.solid_cb,
-            "gerber_multicolored": self.ui.gerber_defaults_form.gerber_gen_group.multicolored_cb,
-            "gerber_circle_steps": self.ui.gerber_defaults_form.gerber_gen_group.circle_steps_entry,
-            "gerber_def_units": self.ui.gerber_defaults_form.gerber_gen_group.gerber_units_radio,
-            "gerber_def_zeros": self.ui.gerber_defaults_form.gerber_gen_group.gerber_zeros_radio,
-            "gerber_clean_apertures": self.ui.gerber_defaults_form.gerber_gen_group.gerber_clean_cb,
-            "gerber_extra_buffering": self.ui.gerber_defaults_form.gerber_gen_group.gerber_extra_buffering,
-            "gerber_plot_fill": self.ui.gerber_defaults_form.gerber_gen_group.pf_color_entry,
-            "gerber_plot_line": self.ui.gerber_defaults_form.gerber_gen_group.pl_color_entry,
-
-            # Gerber Options
-            "gerber_isotooldia": self.ui.gerber_defaults_form.gerber_opt_group.iso_tool_dia_entry,
-            "gerber_isopasses": self.ui.gerber_defaults_form.gerber_opt_group.iso_width_entry,
-            "gerber_isooverlap": self.ui.gerber_defaults_form.gerber_opt_group.iso_overlap_entry,
-            "gerber_combine_passes": self.ui.gerber_defaults_form.gerber_opt_group.combine_passes_cb,
-            "gerber_iso_scope": self.ui.gerber_defaults_form.gerber_opt_group.iso_scope_radio,
-            "gerber_milling_type": self.ui.gerber_defaults_form.gerber_opt_group.milling_type_radio,
-            "gerber_noncoppermargin": self.ui.gerber_defaults_form.gerber_opt_group.noncopper_margin_entry,
-            "gerber_noncopperrounded": self.ui.gerber_defaults_form.gerber_opt_group.noncopper_rounded_cb,
-            "gerber_bboxmargin": self.ui.gerber_defaults_form.gerber_opt_group.bbmargin_entry,
-            "gerber_bboxrounded": self.ui.gerber_defaults_form.gerber_opt_group.bbrounded_cb,
-
-            # Gerber Advanced Options
-            "gerber_aperture_display": self.ui.gerber_defaults_form.gerber_adv_opt_group.aperture_table_visibility_cb,
-            # "gerber_aperture_scale_factor": self.ui.gerber_defaults_form.gerber_adv_opt_group.scale_aperture_entry,
-            # "gerber_aperture_buffer_factor": self.ui.gerber_defaults_form.gerber_adv_opt_group.buffer_aperture_entry,
-            "gerber_follow": self.ui.gerber_defaults_form.gerber_adv_opt_group.follow_cb,
-            "gerber_tool_type": self.ui.gerber_defaults_form.gerber_adv_opt_group.tool_type_radio,
-            "gerber_vtipdia": self.ui.gerber_defaults_form.gerber_adv_opt_group.tipdia_spinner,
-            "gerber_vtipangle": self.ui.gerber_defaults_form.gerber_adv_opt_group.tipangle_spinner,
-            "gerber_vcutz": self.ui.gerber_defaults_form.gerber_adv_opt_group.cutz_spinner,
-            "gerber_iso_type": self.ui.gerber_defaults_form.gerber_adv_opt_group.iso_type_radio,
-
-            "gerber_buffering": self.ui.gerber_defaults_form.gerber_adv_opt_group.buffering_radio,
-            "gerber_simplification": self.ui.gerber_defaults_form.gerber_adv_opt_group.simplify_cb,
-            "gerber_simp_tolerance": self.ui.gerber_defaults_form.gerber_adv_opt_group.simplification_tol_spinner,
-
-            # Gerber Export
-            "gerber_exp_units": self.ui.gerber_defaults_form.gerber_exp_group.gerber_units_radio,
-            "gerber_exp_integer": self.ui.gerber_defaults_form.gerber_exp_group.format_whole_entry,
-            "gerber_exp_decimals": self.ui.gerber_defaults_form.gerber_exp_group.format_dec_entry,
-            "gerber_exp_zeros": self.ui.gerber_defaults_form.gerber_exp_group.zeros_radio,
-
-            # Gerber Editor
-            "gerber_editor_sel_limit": self.ui.gerber_defaults_form.gerber_editor_group.sel_limit_entry,
-            "gerber_editor_newcode": self.ui.gerber_defaults_form.gerber_editor_group.addcode_entry,
-            "gerber_editor_newsize": self.ui.gerber_defaults_form.gerber_editor_group.addsize_entry,
-            "gerber_editor_newtype": self.ui.gerber_defaults_form.gerber_editor_group.addtype_combo,
-            "gerber_editor_newdim": self.ui.gerber_defaults_form.gerber_editor_group.adddim_entry,
-            "gerber_editor_array_size": self.ui.gerber_defaults_form.gerber_editor_group.grb_array_size_entry,
-            "gerber_editor_lin_axis": self.ui.gerber_defaults_form.gerber_editor_group.grb_axis_radio,
-            "gerber_editor_lin_pitch": self.ui.gerber_defaults_form.gerber_editor_group.grb_pitch_entry,
-            "gerber_editor_lin_angle": self.ui.gerber_defaults_form.gerber_editor_group.grb_angle_entry,
-            "gerber_editor_circ_dir": self.ui.gerber_defaults_form.gerber_editor_group.grb_circular_dir_radio,
-            "gerber_editor_circ_angle":
-                self.ui.gerber_defaults_form.gerber_editor_group.grb_circular_angle_entry,
-            "gerber_editor_scale_f": self.ui.gerber_defaults_form.gerber_editor_group.grb_scale_entry,
-            "gerber_editor_buff_f": self.ui.gerber_defaults_form.gerber_editor_group.grb_buff_entry,
-            "gerber_editor_ma_low": self.ui.gerber_defaults_form.gerber_editor_group.grb_ma_low_entry,
-            "gerber_editor_ma_high": self.ui.gerber_defaults_form.gerber_editor_group.grb_ma_high_entry,
-
-            # Excellon General
-            "excellon_plot": self.ui.excellon_defaults_form.excellon_gen_group.plot_cb,
-            "excellon_solid": self.ui.excellon_defaults_form.excellon_gen_group.solid_cb,
-            "excellon_format_upper_in":
-                self.ui.excellon_defaults_form.excellon_gen_group.excellon_format_upper_in_entry,
-            "excellon_format_lower_in":
-                self.ui.excellon_defaults_form.excellon_gen_group.excellon_format_lower_in_entry,
-            "excellon_format_upper_mm":
-                self.ui.excellon_defaults_form.excellon_gen_group.excellon_format_upper_mm_entry,
-            "excellon_format_lower_mm":
-                self.ui.excellon_defaults_form.excellon_gen_group.excellon_format_lower_mm_entry,
-            "excellon_zeros": self.ui.excellon_defaults_form.excellon_gen_group.excellon_zeros_radio,
-            "excellon_units": self.ui.excellon_defaults_form.excellon_gen_group.excellon_units_radio,
-            "excellon_update": self.ui.excellon_defaults_form.excellon_gen_group.update_excellon_cb,
-            "excellon_optimization_type": self.ui.excellon_defaults_form.excellon_gen_group.excellon_optimization_radio,
-            "excellon_search_time": self.ui.excellon_defaults_form.excellon_gen_group.optimization_time_entry,
-            "excellon_plot_fill": self.ui.excellon_defaults_form.excellon_gen_group.fill_color_entry,
-            "excellon_plot_line": self.ui.excellon_defaults_form.excellon_gen_group.line_color_entry,
-
-            # Excellon Options
-            "excellon_operation": self.ui.excellon_defaults_form.excellon_opt_group.operation_radio,
-            "excellon_milling_type": self.ui.excellon_defaults_form.excellon_opt_group.milling_type_radio,
-
-            "excellon_milling_dia": self.ui.excellon_defaults_form.excellon_opt_group.mill_dia_entry,
-
-            "excellon_cutz": self.ui.excellon_defaults_form.excellon_opt_group.cutz_entry,
-            "excellon_multidepth": self.ui.excellon_defaults_form.excellon_opt_group.mpass_cb,
-            "excellon_depthperpass": self.ui.excellon_defaults_form.excellon_opt_group.maxdepth_entry,
-            "excellon_travelz": self.ui.excellon_defaults_form.excellon_opt_group.travelz_entry,
-            "excellon_endz": self.ui.excellon_defaults_form.excellon_opt_group.endz_entry,
-            "excellon_endxy": self.ui.excellon_defaults_form.excellon_opt_group.endxy_entry,
-
-            "excellon_feedrate_z": self.ui.excellon_defaults_form.excellon_opt_group.feedrate_z_entry,
-            "excellon_spindlespeed": self.ui.excellon_defaults_form.excellon_opt_group.spindlespeed_entry,
-            "excellon_dwell": self.ui.excellon_defaults_form.excellon_opt_group.dwell_cb,
-            "excellon_dwelltime": self.ui.excellon_defaults_form.excellon_opt_group.dwelltime_entry,
-            "excellon_toolchange": self.ui.excellon_defaults_form.excellon_opt_group.toolchange_cb,
-            "excellon_toolchangez": self.ui.excellon_defaults_form.excellon_opt_group.toolchangez_entry,
-            "excellon_ppname_e": self.ui.excellon_defaults_form.excellon_opt_group.pp_excellon_name_cb,
-            "excellon_tooldia": self.ui.excellon_defaults_form.excellon_opt_group.tooldia_entry,
-            "excellon_slot_tooldia": self.ui.excellon_defaults_form.excellon_opt_group.slot_tooldia_entry,
-            "excellon_gcode_type": self.ui.excellon_defaults_form.excellon_opt_group.excellon_gcode_type_radio,
-
-            # Excellon Advanced Options
-            "excellon_offset": self.ui.excellon_defaults_form.excellon_adv_opt_group.offset_entry,
-            "excellon_toolchangexy": self.ui.excellon_defaults_form.excellon_adv_opt_group.toolchangexy_entry,
-            "excellon_startz": self.ui.excellon_defaults_form.excellon_adv_opt_group.estartz_entry,
-            "excellon_feedrate_rapid": self.ui.excellon_defaults_form.excellon_adv_opt_group.feedrate_rapid_entry,
-            "excellon_z_pdepth": self.ui.excellon_defaults_form.excellon_adv_opt_group.pdepth_entry,
-            "excellon_feedrate_probe": self.ui.excellon_defaults_form.excellon_adv_opt_group.feedrate_probe_entry,
-            "excellon_spindledir": self.ui.excellon_defaults_form.excellon_adv_opt_group.spindledir_radio,
-            "excellon_f_plunge": self.ui.excellon_defaults_form.excellon_adv_opt_group.fplunge_cb,
-            "excellon_f_retract": self.ui.excellon_defaults_form.excellon_adv_opt_group.fretract_cb,
-
-            # Excellon Export
-            "excellon_exp_units": self.ui.excellon_defaults_form.excellon_exp_group.excellon_units_radio,
-            "excellon_exp_format": self.ui.excellon_defaults_form.excellon_exp_group.format_radio,
-            "excellon_exp_integer": self.ui.excellon_defaults_form.excellon_exp_group.format_whole_entry,
-            "excellon_exp_decimals": self.ui.excellon_defaults_form.excellon_exp_group.format_dec_entry,
-            "excellon_exp_zeros": self.ui.excellon_defaults_form.excellon_exp_group.zeros_radio,
-            "excellon_exp_slot_type": self.ui.excellon_defaults_form.excellon_exp_group.slot_type_radio,
-
-            # Excellon Editor
-            "excellon_editor_sel_limit": self.ui.excellon_defaults_form.excellon_editor_group.sel_limit_entry,
-            "excellon_editor_newdia": self.ui.excellon_defaults_form.excellon_editor_group.addtool_entry,
-            "excellon_editor_array_size": self.ui.excellon_defaults_form.excellon_editor_group.drill_array_size_entry,
-            "excellon_editor_lin_dir": self.ui.excellon_defaults_form.excellon_editor_group.drill_axis_radio,
-            "excellon_editor_lin_pitch": self.ui.excellon_defaults_form.excellon_editor_group.drill_pitch_entry,
-            "excellon_editor_lin_angle": self.ui.excellon_defaults_form.excellon_editor_group.drill_angle_entry,
-            "excellon_editor_circ_dir": self.ui.excellon_defaults_form.excellon_editor_group.drill_circular_dir_radio,
-            "excellon_editor_circ_angle":
-                self.ui.excellon_defaults_form.excellon_editor_group.drill_circular_angle_entry,
-            # Excellon Slots
-            "excellon_editor_slot_direction":
-                self.ui.excellon_defaults_form.excellon_editor_group.slot_axis_radio,
-            "excellon_editor_slot_angle":
-                self.ui.excellon_defaults_form.excellon_editor_group.slot_angle_spinner,
-            "excellon_editor_slot_length":
-                self.ui.excellon_defaults_form.excellon_editor_group.slot_length_entry,
-            # Excellon Slots
-            "excellon_editor_slot_array_size":
-                self.ui.excellon_defaults_form.excellon_editor_group.slot_array_size_entry,
-            "excellon_editor_slot_lin_dir": self.ui.excellon_defaults_form.excellon_editor_group.slot_array_axis_radio,
-            "excellon_editor_slot_lin_pitch":
-                self.ui.excellon_defaults_form.excellon_editor_group.slot_array_pitch_entry,
-            "excellon_editor_slot_lin_angle":
-                self.ui.excellon_defaults_form.excellon_editor_group.slot_array_angle_entry,
-            "excellon_editor_slot_circ_dir":
-                self.ui.excellon_defaults_form.excellon_editor_group.slot_array_circular_dir_radio,
-            "excellon_editor_slot_circ_angle":
-                self.ui.excellon_defaults_form.excellon_editor_group.slot_array_circular_angle_entry,
-
-            # Geometry General
-            "geometry_plot": self.ui.geometry_defaults_form.geometry_gen_group.plot_cb,
-            "geometry_circle_steps": self.ui.geometry_defaults_form.geometry_gen_group.circle_steps_entry,
-            "geometry_cnctooldia": self.ui.geometry_defaults_form.geometry_gen_group.cnctooldia_entry,
-            "geometry_plot_line": self.ui.geometry_defaults_form.geometry_gen_group.line_color_entry,
-
-            # Geometry Options
-            "geometry_cutz": self.ui.geometry_defaults_form.geometry_opt_group.cutz_entry,
-            "geometry_travelz": self.ui.geometry_defaults_form.geometry_opt_group.travelz_entry,
-            "geometry_feedrate": self.ui.geometry_defaults_form.geometry_opt_group.cncfeedrate_entry,
-            "geometry_feedrate_z": self.ui.geometry_defaults_form.geometry_opt_group.feedrate_z_entry,
-            "geometry_spindlespeed": self.ui.geometry_defaults_form.geometry_opt_group.cncspindlespeed_entry,
-            "geometry_dwell": self.ui.geometry_defaults_form.geometry_opt_group.dwell_cb,
-            "geometry_dwelltime": self.ui.geometry_defaults_form.geometry_opt_group.dwelltime_entry,
-            "geometry_ppname_g": self.ui.geometry_defaults_form.geometry_opt_group.pp_geometry_name_cb,
-            "geometry_toolchange": self.ui.geometry_defaults_form.geometry_opt_group.toolchange_cb,
-            "geometry_toolchangez": self.ui.geometry_defaults_form.geometry_opt_group.toolchangez_entry,
-            "geometry_endz": self.ui.geometry_defaults_form.geometry_opt_group.endz_entry,
-            "geometry_endxy": self.ui.geometry_defaults_form.geometry_opt_group.endxy_entry,
-            "geometry_depthperpass": self.ui.geometry_defaults_form.geometry_opt_group.depthperpass_entry,
-            "geometry_multidepth": self.ui.geometry_defaults_form.geometry_opt_group.multidepth_cb,
-
-            # Geometry Advanced Options
-            "geometry_toolchangexy": self.ui.geometry_defaults_form.geometry_adv_opt_group.toolchangexy_entry,
-            "geometry_startz": self.ui.geometry_defaults_form.geometry_adv_opt_group.gstartz_entry,
-            "geometry_feedrate_rapid": self.ui.geometry_defaults_form.geometry_adv_opt_group.feedrate_rapid_entry,
-            "geometry_extracut": self.ui.geometry_defaults_form.geometry_adv_opt_group.extracut_cb,
-            "geometry_extracut_length": self.ui.geometry_defaults_form.geometry_adv_opt_group.e_cut_entry,
-            "geometry_z_pdepth": self.ui.geometry_defaults_form.geometry_adv_opt_group.pdepth_entry,
-            "geometry_feedrate_probe": self.ui.geometry_defaults_form.geometry_adv_opt_group.feedrate_probe_entry,
-            "geometry_spindledir": self.ui.geometry_defaults_form.geometry_adv_opt_group.spindledir_radio,
-            "geometry_f_plunge": self.ui.geometry_defaults_form.geometry_adv_opt_group.fplunge_cb,
-            "geometry_segx": self.ui.geometry_defaults_form.geometry_adv_opt_group.segx_entry,
-            "geometry_segy": self.ui.geometry_defaults_form.geometry_adv_opt_group.segy_entry,
-
-            # Geometry Editor
-            "geometry_editor_sel_limit": self.ui.geometry_defaults_form.geometry_editor_group.sel_limit_entry,
-            "geometry_editor_milling_type": self.ui.geometry_defaults_form.geometry_editor_group.milling_type_radio,
-
-            # CNCJob General
-            "cncjob_plot": self.ui.cncjob_defaults_form.cncjob_gen_group.plot_cb,
-            "cncjob_plot_kind": self.ui.cncjob_defaults_form.cncjob_gen_group.cncplot_method_radio,
-            "cncjob_annotation": self.ui.cncjob_defaults_form.cncjob_gen_group.annotation_cb,
-
-            "cncjob_tooldia": self.ui.cncjob_defaults_form.cncjob_gen_group.tooldia_entry,
-            "cncjob_coords_type": self.ui.cncjob_defaults_form.cncjob_gen_group.coords_type_radio,
-            "cncjob_coords_decimals": self.ui.cncjob_defaults_form.cncjob_gen_group.coords_dec_entry,
-            "cncjob_fr_decimals": self.ui.cncjob_defaults_form.cncjob_gen_group.fr_dec_entry,
-            "cncjob_steps_per_circle": self.ui.cncjob_defaults_form.cncjob_gen_group.steps_per_circle_entry,
-            "cncjob_line_ending": self.ui.cncjob_defaults_form.cncjob_gen_group.line_ending_cb,
-            "cncjob_plot_line": self.ui.cncjob_defaults_form.cncjob_gen_group.line_color_entry,
-            "cncjob_plot_fill": self.ui.cncjob_defaults_form.cncjob_gen_group.fill_color_entry,
-            "cncjob_travel_line": self.ui.cncjob_defaults_form.cncjob_gen_group.tline_color_entry,
-            "cncjob_travel_fill": self.ui.cncjob_defaults_form.cncjob_gen_group.tfill_color_entry,
-
-            # CNC Job Options
-            "cncjob_prepend": self.ui.cncjob_defaults_form.cncjob_opt_group.prepend_text,
-            "cncjob_append": self.ui.cncjob_defaults_form.cncjob_opt_group.append_text,
-
-            # CNC Job Advanced Options
-            "cncjob_toolchange_macro": self.ui.cncjob_defaults_form.cncjob_adv_opt_group.toolchange_text,
-            "cncjob_toolchange_macro_enable": self.ui.cncjob_defaults_form.cncjob_adv_opt_group.toolchange_cb,
-            "cncjob_annotation_fontsize": self.ui.cncjob_defaults_form.cncjob_adv_opt_group.annotation_fontsize_sp,
-            "cncjob_annotation_fontcolor": self.ui.cncjob_defaults_form.cncjob_adv_opt_group.annotation_fontcolor_entry,
-
-            # NCC Tool
-            "tools_ncctools": self.ui.tools_defaults_form.tools_ncc_group.ncc_tool_dia_entry,
-            "tools_nccorder": self.ui.tools_defaults_form.tools_ncc_group.ncc_order_radio,
-            "tools_nccoverlap": self.ui.tools_defaults_form.tools_ncc_group.ncc_overlap_entry,
-            "tools_nccmargin": self.ui.tools_defaults_form.tools_ncc_group.ncc_margin_entry,
-            "tools_nccmethod": self.ui.tools_defaults_form.tools_ncc_group.ncc_method_combo,
-            "tools_nccconnect": self.ui.tools_defaults_form.tools_ncc_group.ncc_connect_cb,
-            "tools_ncccontour": self.ui.tools_defaults_form.tools_ncc_group.ncc_contour_cb,
-            "tools_nccrest": self.ui.tools_defaults_form.tools_ncc_group.ncc_rest_cb,
-            "tools_ncc_offset_choice": self.ui.tools_defaults_form.tools_ncc_group.ncc_choice_offset_cb,
-            "tools_ncc_offset_value": self.ui.tools_defaults_form.tools_ncc_group.ncc_offset_spinner,
-            "tools_nccref": self.ui.tools_defaults_form.tools_ncc_group.select_combo,
-            "tools_ncc_area_shape": self.ui.tools_defaults_form.tools_ncc_group.area_shape_radio,
-            "tools_ncc_plotting": self.ui.tools_defaults_form.tools_ncc_group.ncc_plotting_radio,
-            "tools_nccmilling_type": self.ui.tools_defaults_form.tools_ncc_group.milling_type_radio,
-            "tools_ncctool_type": self.ui.tools_defaults_form.tools_ncc_group.tool_type_radio,
-            "tools_ncccutz": self.ui.tools_defaults_form.tools_ncc_group.cutz_entry,
-            "tools_ncctipdia": self.ui.tools_defaults_form.tools_ncc_group.tipdia_entry,
-            "tools_ncctipangle": self.ui.tools_defaults_form.tools_ncc_group.tipangle_entry,
-            "tools_nccnewdia": self.ui.tools_defaults_form.tools_ncc_group.newdia_entry,
-
-            # CutOut Tool
-            "tools_cutouttooldia": self.ui.tools_defaults_form.tools_cutout_group.cutout_tooldia_entry,
-            "tools_cutoutkind": self.ui.tools_defaults_form.tools_cutout_group.obj_kind_combo,
-            "tools_cutoutmargin": self.ui.tools_defaults_form.tools_cutout_group.cutout_margin_entry,
-            "tools_cutout_z": self.ui.tools_defaults_form.tools_cutout_group.cutz_entry,
-            "tools_cutout_depthperpass": self.ui.tools_defaults_form.tools_cutout_group.maxdepth_entry,
-            "tools_cutout_mdepth": self.ui.tools_defaults_form.tools_cutout_group.mpass_cb,
-            "tools_cutoutgapsize": self.ui.tools_defaults_form.tools_cutout_group.cutout_gap_entry,
-            "tools_gaps_ff": self.ui.tools_defaults_form.tools_cutout_group.gaps_combo,
-            "tools_cutout_convexshape": self.ui.tools_defaults_form.tools_cutout_group.convex_box,
-
-            # Paint Area Tool
-            "tools_painttooldia": self.ui.tools_defaults_form.tools_paint_group.painttooldia_entry,
-            "tools_paintorder": self.ui.tools_defaults_form.tools_paint_group.paint_order_radio,
-            "tools_paintoverlap": self.ui.tools_defaults_form.tools_paint_group.paintoverlap_entry,
-            "tools_paintmargin": self.ui.tools_defaults_form.tools_paint_group.paintmargin_entry,
-            "tools_paintmethod": self.ui.tools_defaults_form.tools_paint_group.paintmethod_combo,
-            "tools_selectmethod": self.ui.tools_defaults_form.tools_paint_group.selectmethod_combo,
-            "tools_paint_area_shape": self.ui.tools_defaults_form.tools_paint_group.area_shape_radio,
-            "tools_pathconnect": self.ui.tools_defaults_form.tools_paint_group.pathconnect_cb,
-            "tools_paintcontour": self.ui.tools_defaults_form.tools_paint_group.contour_cb,
-            "tools_paint_plotting": self.ui.tools_defaults_form.tools_paint_group.paint_plotting_radio,
-
-            "tools_paintrest": self.ui.tools_defaults_form.tools_paint_group.rest_cb,
-            "tools_painttool_type": self.ui.tools_defaults_form.tools_paint_group.tool_type_radio,
-            "tools_paintcutz": self.ui.tools_defaults_form.tools_paint_group.cutz_entry,
-            "tools_painttipdia": self.ui.tools_defaults_form.tools_paint_group.tipdia_entry,
-            "tools_painttipangle": self.ui.tools_defaults_form.tools_paint_group.tipangle_entry,
-            "tools_paintnewdia": self.ui.tools_defaults_form.tools_paint_group.newdia_entry,
-
-            # 2-sided Tool
-            "tools_2sided_mirror_axis": self.ui.tools_defaults_form.tools_2sided_group.mirror_axis_radio,
-            "tools_2sided_axis_loc": self.ui.tools_defaults_form.tools_2sided_group.axis_location_radio,
-            "tools_2sided_drilldia": self.ui.tools_defaults_form.tools_2sided_group.drill_dia_entry,
-            "tools_2sided_allign_axis": self.ui.tools_defaults_form.tools_2sided_group.align_axis_radio,
-
-            # Film Tool
-            "tools_film_type": self.ui.tools_defaults_form.tools_film_group.film_type_radio,
-            "tools_film_boundary": self.ui.tools_defaults_form.tools_film_group.film_boundary_entry,
-            "tools_film_scale_stroke": self.ui.tools_defaults_form.tools_film_group.film_scale_stroke_entry,
-            "tools_film_color": self.ui.tools_defaults_form.tools_film_group.film_color_entry,
-            "tools_film_scale_cb": self.ui.tools_defaults_form.tools_film_group.film_scale_cb,
-            "tools_film_scale_x_entry": self.ui.tools_defaults_form.tools_film_group.film_scalex_entry,
-            "tools_film_scale_y_entry": self.ui.tools_defaults_form.tools_film_group.film_scaley_entry,
-            "tools_film_skew_cb": self.ui.tools_defaults_form.tools_film_group.film_skew_cb,
-            "tools_film_skew_x_entry": self.ui.tools_defaults_form.tools_film_group.film_skewx_entry,
-            "tools_film_skew_y_entry": self.ui.tools_defaults_form.tools_film_group.film_skewy_entry,
-            "tools_film_skew_ref_radio": self.ui.tools_defaults_form.tools_film_group.film_skew_reference,
-            "tools_film_mirror_cb": self.ui.tools_defaults_form.tools_film_group.film_mirror_cb,
-            "tools_film_mirror_axis_radio": self.ui.tools_defaults_form.tools_film_group.film_mirror_axis,
-            "tools_film_file_type_radio": self.ui.tools_defaults_form.tools_film_group.file_type_radio,
-            "tools_film_orientation": self.ui.tools_defaults_form.tools_film_group.orientation_radio,
-            "tools_film_pagesize": self.ui.tools_defaults_form.tools_film_group.pagesize_combo,
-
-            # Panelize Tool
-            "tools_panelize_spacing_columns": self.ui.tools_defaults_form.tools_panelize_group.pspacing_columns,
-            "tools_panelize_spacing_rows": self.ui.tools_defaults_form.tools_panelize_group.pspacing_rows,
-            "tools_panelize_columns": self.ui.tools_defaults_form.tools_panelize_group.pcolumns,
-            "tools_panelize_rows": self.ui.tools_defaults_form.tools_panelize_group.prows,
-            "tools_panelize_constrain": self.ui.tools_defaults_form.tools_panelize_group.pconstrain_cb,
-            "tools_panelize_constrainx": self.ui.tools_defaults_form.tools_panelize_group.px_width_entry,
-            "tools_panelize_constrainy": self.ui.tools_defaults_form.tools_panelize_group.py_height_entry,
-            "tools_panelize_panel_type": self.ui.tools_defaults_form.tools_panelize_group.panel_type_radio,
-
-            # Calculators Tool
-            "tools_calc_vshape_tip_dia": self.ui.tools_defaults_form.tools_calculators_group.tip_dia_entry,
-            "tools_calc_vshape_tip_angle": self.ui.tools_defaults_form.tools_calculators_group.tip_angle_entry,
-            "tools_calc_vshape_cut_z": self.ui.tools_defaults_form.tools_calculators_group.cut_z_entry,
-            "tools_calc_electro_length": self.ui.tools_defaults_form.tools_calculators_group.pcblength_entry,
-            "tools_calc_electro_width": self.ui.tools_defaults_form.tools_calculators_group.pcbwidth_entry,
-            "tools_calc_electro_cdensity": self.ui.tools_defaults_form.tools_calculators_group.cdensity_entry,
-            "tools_calc_electro_growth": self.ui.tools_defaults_form.tools_calculators_group.growth_entry,
-
-            # Transformations Tool
-            "tools_transform_rotate": self.ui.tools_defaults_form.tools_transform_group.rotate_entry,
-            "tools_transform_skew_x": self.ui.tools_defaults_form.tools_transform_group.skewx_entry,
-            "tools_transform_skew_y": self.ui.tools_defaults_form.tools_transform_group.skewy_entry,
-            "tools_transform_scale_x": self.ui.tools_defaults_form.tools_transform_group.scalex_entry,
-            "tools_transform_scale_y": self.ui.tools_defaults_form.tools_transform_group.scaley_entry,
-            "tools_transform_scale_link": self.ui.tools_defaults_form.tools_transform_group.link_cb,
-            "tools_transform_scale_reference": self.ui.tools_defaults_form.tools_transform_group.reference_cb,
-            "tools_transform_offset_x": self.ui.tools_defaults_form.tools_transform_group.offx_entry,
-            "tools_transform_offset_y": self.ui.tools_defaults_form.tools_transform_group.offy_entry,
-            "tools_transform_mirror_reference": self.ui.tools_defaults_form.tools_transform_group.mirror_reference_cb,
-            "tools_transform_mirror_point": self.ui.tools_defaults_form.tools_transform_group.flip_ref_entry,
-            "tools_transform_buffer_dis": self.ui.tools_defaults_form.tools_transform_group.buffer_entry,
-            "tools_transform_buffer_factor": self.ui.tools_defaults_form.tools_transform_group.buffer_factor_entry,
-            "tools_transform_buffer_corner": self.ui.tools_defaults_form.tools_transform_group.buffer_rounded_cb,
-
-            # SolderPaste Dispensing Tool
-            "tools_solderpaste_tools": self.ui.tools_defaults_form.tools_solderpaste_group.nozzle_tool_dia_entry,
-            "tools_solderpaste_new": self.ui.tools_defaults_form.tools_solderpaste_group.addtool_entry,
-            "tools_solderpaste_z_start": self.ui.tools_defaults_form.tools_solderpaste_group.z_start_entry,
-            "tools_solderpaste_z_dispense": self.ui.tools_defaults_form.tools_solderpaste_group.z_dispense_entry,
-            "tools_solderpaste_z_stop": self.ui.tools_defaults_form.tools_solderpaste_group.z_stop_entry,
-            "tools_solderpaste_z_travel": self.ui.tools_defaults_form.tools_solderpaste_group.z_travel_entry,
-            "tools_solderpaste_z_toolchange": self.ui.tools_defaults_form.tools_solderpaste_group.z_toolchange_entry,
-            "tools_solderpaste_xy_toolchange": self.ui.tools_defaults_form.tools_solderpaste_group.xy_toolchange_entry,
-            "tools_solderpaste_frxy": self.ui.tools_defaults_form.tools_solderpaste_group.frxy_entry,
-            "tools_solderpaste_frz": self.ui.tools_defaults_form.tools_solderpaste_group.frz_entry,
-            "tools_solderpaste_frz_dispense": self.ui.tools_defaults_form.tools_solderpaste_group.frz_dispense_entry,
-            "tools_solderpaste_speedfwd": self.ui.tools_defaults_form.tools_solderpaste_group.speedfwd_entry,
-            "tools_solderpaste_dwellfwd": self.ui.tools_defaults_form.tools_solderpaste_group.dwellfwd_entry,
-            "tools_solderpaste_speedrev": self.ui.tools_defaults_form.tools_solderpaste_group.speedrev_entry,
-            "tools_solderpaste_dwellrev": self.ui.tools_defaults_form.tools_solderpaste_group.dwellrev_entry,
-            "tools_solderpaste_pp": self.ui.tools_defaults_form.tools_solderpaste_group.pp_combo,
-            "tools_sub_close_paths": self.ui.tools_defaults_form.tools_sub_group.close_paths_cb,
-
-            # #######################################################################################################
-            # ########################################## TOOLS 2 ####################################################
-            # #######################################################################################################
-
-            # Optimal Tool
-            "tools_opt_precision": self.ui.tools2_defaults_form.tools2_optimal_group.precision_sp,
-
-            # Check Rules Tool
-            "tools_cr_trace_size": self.ui.tools2_defaults_form.tools2_checkrules_group.trace_size_cb,
-            "tools_cr_trace_size_val": self.ui.tools2_defaults_form.tools2_checkrules_group.trace_size_entry,
-            "tools_cr_c2c": self.ui.tools2_defaults_form.tools2_checkrules_group.clearance_copper2copper_cb,
-            "tools_cr_c2c_val": self.ui.tools2_defaults_form.tools2_checkrules_group.clearance_copper2copper_entry,
-            "tools_cr_c2o": self.ui.tools2_defaults_form.tools2_checkrules_group.clearance_copper2ol_cb,
-            "tools_cr_c2o_val": self.ui.tools2_defaults_form.tools2_checkrules_group.clearance_copper2ol_entry,
-            "tools_cr_s2s": self.ui.tools2_defaults_form.tools2_checkrules_group.clearance_silk2silk_cb,
-            "tools_cr_s2s_val": self.ui.tools2_defaults_form.tools2_checkrules_group.clearance_silk2silk_entry,
-            "tools_cr_s2sm": self.ui.tools2_defaults_form.tools2_checkrules_group.clearance_silk2sm_cb,
-            "tools_cr_s2sm_val": self.ui.tools2_defaults_form.tools2_checkrules_group.clearance_silk2sm_entry,
-            "tools_cr_s2o": self.ui.tools2_defaults_form.tools2_checkrules_group.clearance_silk2ol_cb,
-            "tools_cr_s2o_val": self.ui.tools2_defaults_form.tools2_checkrules_group.clearance_silk2ol_entry,
-            "tools_cr_sm2sm": self.ui.tools2_defaults_form.tools2_checkrules_group.clearance_sm2sm_cb,
-            "tools_cr_sm2sm_val": self.ui.tools2_defaults_form.tools2_checkrules_group.clearance_sm2sm_entry,
-            "tools_cr_ri": self.ui.tools2_defaults_form.tools2_checkrules_group.ring_integrity_cb,
-            "tools_cr_ri_val": self.ui.tools2_defaults_form.tools2_checkrules_group.ring_integrity_entry,
-            "tools_cr_h2h": self.ui.tools2_defaults_form.tools2_checkrules_group.clearance_d2d_cb,
-            "tools_cr_h2h_val": self.ui.tools2_defaults_form.tools2_checkrules_group.clearance_d2d_entry,
-            "tools_cr_dh": self.ui.tools2_defaults_form.tools2_checkrules_group.drill_size_cb,
-            "tools_cr_dh_val": self.ui.tools2_defaults_form.tools2_checkrules_group.drill_size_entry,
-
-            # QRCode Tool
-            "tools_qrcode_version": self.ui.tools2_defaults_form.tools2_qrcode_group.version_entry,
-            "tools_qrcode_error": self.ui.tools2_defaults_form.tools2_qrcode_group.error_radio,
-            "tools_qrcode_box_size": self.ui.tools2_defaults_form.tools2_qrcode_group.bsize_entry,
-            "tools_qrcode_border_size": self.ui.tools2_defaults_form.tools2_qrcode_group.border_size_entry,
-            "tools_qrcode_qrdata": self.ui.tools2_defaults_form.tools2_qrcode_group.text_data,
-            "tools_qrcode_polarity": self.ui.tools2_defaults_form.tools2_qrcode_group.pol_radio,
-            "tools_qrcode_rounded": self.ui.tools2_defaults_form.tools2_qrcode_group.bb_radio,
-            "tools_qrcode_fill_color": self.ui.tools2_defaults_form.tools2_qrcode_group.fill_color_entry,
-            "tools_qrcode_back_color": self.ui.tools2_defaults_form.tools2_qrcode_group.back_color_entry,
-            "tools_qrcode_sel_limit": self.ui.tools2_defaults_form.tools2_qrcode_group.sel_limit_entry,
-
-            # Copper Thieving Tool
-            "tools_copper_thieving_clearance": self.ui.tools2_defaults_form.tools2_cfill_group.clearance_entry,
-            "tools_copper_thieving_margin": self.ui.tools2_defaults_form.tools2_cfill_group.margin_entry,
-            "tools_copper_thieving_reference": self.ui.tools2_defaults_form.tools2_cfill_group.reference_radio,
-            "tools_copper_thieving_box_type": self.ui.tools2_defaults_form.tools2_cfill_group.bbox_type_radio,
-            "tools_copper_thieving_circle_steps": self.ui.tools2_defaults_form.tools2_cfill_group.circlesteps_entry,
-            "tools_copper_thieving_fill_type": self.ui.tools2_defaults_form.tools2_cfill_group.fill_type_radio,
-            "tools_copper_thieving_dots_dia": self.ui.tools2_defaults_form.tools2_cfill_group.dot_dia_entry,
-            "tools_copper_thieving_dots_spacing": self.ui.tools2_defaults_form.tools2_cfill_group.dot_spacing_entry,
-            "tools_copper_thieving_squares_size": self.ui.tools2_defaults_form.tools2_cfill_group.square_size_entry,
-            "tools_copper_thieving_squares_spacing":
-                self.ui.tools2_defaults_form.tools2_cfill_group.squares_spacing_entry,
-            "tools_copper_thieving_lines_size": self.ui.tools2_defaults_form.tools2_cfill_group.line_size_entry,
-            "tools_copper_thieving_lines_spacing": self.ui.tools2_defaults_form.tools2_cfill_group.lines_spacing_entry,
-            "tools_copper_thieving_rb_margin": self.ui.tools2_defaults_form.tools2_cfill_group.rb_margin_entry,
-            "tools_copper_thieving_rb_thickness": self.ui.tools2_defaults_form.tools2_cfill_group.rb_thickness_entry,
-            "tools_copper_thieving_mask_clearance": self.ui.tools2_defaults_form.tools2_cfill_group.clearance_ppm_entry,
-
-            # Fiducials Tool
-            "tools_fiducials_dia": self.ui.tools2_defaults_form.tools2_fiducials_group.dia_entry,
-            "tools_fiducials_margin": self.ui.tools2_defaults_form.tools2_fiducials_group.margin_entry,
-            "tools_fiducials_mode": self.ui.tools2_defaults_form.tools2_fiducials_group.mode_radio,
-            "tools_fiducials_second_pos": self.ui.tools2_defaults_form.tools2_fiducials_group.pos_radio,
-            "tools_fiducials_type": self.ui.tools2_defaults_form.tools2_fiducials_group.fid_type_radio,
-            "tools_fiducials_line_thickness": self.ui.tools2_defaults_form.tools2_fiducials_group.line_thickness_entry,
-
-            # Calibration Tool
-            "tools_cal_calsource": self.ui.tools2_defaults_form.tools2_cal_group.cal_source_radio,
-            "tools_cal_travelz": self.ui.tools2_defaults_form.tools2_cal_group.travelz_entry,
-            "tools_cal_verz": self.ui.tools2_defaults_form.tools2_cal_group.verz_entry,
-            "tools_cal_zeroz": self.ui.tools2_defaults_form.tools2_cal_group.zeroz_cb,
-            "tools_cal_toolchangez": self.ui.tools2_defaults_form.tools2_cal_group.toolchangez_entry,
-            "tools_cal_toolchange_xy": self.ui.tools2_defaults_form.tools2_cal_group.toolchange_xy_entry,
-            "tools_cal_sec_point": self.ui.tools2_defaults_form.tools2_cal_group.second_point_radio,
-
-            # Extract Drills Tool
-            "tools_edrills_hole_type": self.ui.tools2_defaults_form.tools2_edrills_group.hole_size_radio,
-            "tools_edrills_hole_fixed_dia": self.ui.tools2_defaults_form.tools2_edrills_group.dia_entry,
-            "tools_edrills_hole_prop_factor": self.ui.tools2_defaults_form.tools2_edrills_group.factor_entry,
-            "tools_edrills_circular_ring": self.ui.tools2_defaults_form.tools2_edrills_group.circular_ring_entry,
-            "tools_edrills_oblong_ring": self.ui.tools2_defaults_form.tools2_edrills_group.oblong_ring_entry,
-            "tools_edrills_square_ring": self.ui.tools2_defaults_form.tools2_edrills_group.square_ring_entry,
-            "tools_edrills_rectangular_ring": self.ui.tools2_defaults_form.tools2_edrills_group.rectangular_ring_entry,
-            "tools_edrills_others_ring": self.ui.tools2_defaults_form.tools2_edrills_group.other_ring_entry,
-            "tools_edrills_circular": self.ui.tools2_defaults_form.tools2_edrills_group.circular_cb,
-            "tools_edrills_oblong": self.ui.tools2_defaults_form.tools2_edrills_group.oblong_cb,
-            "tools_edrills_square": self.ui.tools2_defaults_form.tools2_edrills_group.square_cb,
-            "tools_edrills_rectangular": self.ui.tools2_defaults_form.tools2_edrills_group.rectangular_cb,
-            "tools_edrills_others": self.ui.tools2_defaults_form.tools2_edrills_group.other_cb,
-
-            # Punch Gerber Tool
-            "tools_punch_hole_type": self.ui.tools2_defaults_form.tools2_punch_group.hole_size_radio,
-            "tools_punch_hole_fixed_dia": self.ui.tools2_defaults_form.tools2_punch_group.dia_entry,
-            "tools_punch_hole_prop_factor": self.ui.tools2_defaults_form.tools2_punch_group.factor_entry,
-            "tools_punch_circular_ring": self.ui.tools2_defaults_form.tools2_punch_group.circular_ring_entry,
-            "tools_punch_oblong_ring": self.ui.tools2_defaults_form.tools2_punch_group.oblong_ring_entry,
-            "tools_punch_square_ring": self.ui.tools2_defaults_form.tools2_punch_group.square_ring_entry,
-            "tools_punch_rectangular_ring": self.ui.tools2_defaults_form.tools2_punch_group.rectangular_ring_entry,
-            "tools_punch_others_ring": self.ui.tools2_defaults_form.tools2_punch_group.other_ring_entry,
-            "tools_punch_circular": self.ui.tools2_defaults_form.tools2_punch_group.circular_cb,
-            "tools_punch_oblong": self.ui.tools2_defaults_form.tools2_punch_group.oblong_cb,
-            "tools_punch_square": self.ui.tools2_defaults_form.tools2_punch_group.square_cb,
-            "tools_punch_rectangular": self.ui.tools2_defaults_form.tools2_punch_group.rectangular_cb,
-            "tools_punch_others": self.ui.tools2_defaults_form.tools2_punch_group.other_cb,
-
-            # Invert Gerber Tool
-            "tools_invert_margin": self.ui.tools2_defaults_form.tools2_invert_group.margin_entry,
-            "tools_invert_join_style": self.ui.tools2_defaults_form.tools2_invert_group.join_radio,
-
-            # Utilities
-            # File associations
-            "fa_excellon": self.ui.util_defaults_form.fa_excellon_group.exc_list_text,
-            "fa_gcode": self.ui.util_defaults_form.fa_gcode_group.gco_list_text,
-            # "fa_geometry": self.ui.util_defaults_form.fa_geometry_group.close_paths_cb,
-            "fa_gerber": self.ui.util_defaults_form.fa_gerber_group.grb_list_text,
-            "util_autocomplete_keywords": self.ui.util_defaults_form.kw_group.kw_list_text,
-
-        }
-
-    def defaults_read_form(self):
-        """
-        Will read all the values in the Preferences GUI and update the defaults dictionary.
-
-        :return: None
-        """
-        for option in self.defaults_form_fields:
-            try:
-                self.defaults[option] = self.defaults_form_fields[option].get_value()
-            except Exception as e:
-                log.debug("App.defaults_read_form() --> %s" % str(e))
-
-    def defaults_write_form(self, factor=None, fl_units=None, source_dict=None):
-        """
-        Will set the values for all the GUI elements in Preferences GUI based on the values found in the
-        self.defaults dictionary.
-
-        :param factor: will apply a factor to the values that written in the GUI elements
-        :param fl_units: current measuring units in FlatCAM: Metric or Inch
-        :param source_dict: the repository of options, usually is the self.defaults
-        :return: None
-        """
-
-        options_storage = self.defaults if source_dict is None else source_dict
-
-        for option in options_storage:
-            if source_dict:
-                self.defaults_write_form_field(option, factor=factor, units=fl_units, defaults_dict=source_dict)
-            else:
-                self.defaults_write_form_field(option, factor=factor, units=fl_units)
-
-    def defaults_write_form_field(self, field, factor=None, units=None, defaults_dict=None):
-        """
-        Basically it is the worker in the self.defaults_write_form()
-
-        :param field: the GUI element in Preferences GUI to be updated
-        :param factor: factor to be applied to the field parameter
-        :param units: current FLatCAM measuring units
-        :param defaults_dict: the defaults storage
-        :return: None, it updates GUI elements
-        """
-
-        def_dict = self.defaults if defaults_dict is None else defaults_dict
-
-        try:
-            value = def_dict[field]
-            log.debug("value is " + str(value) + " and factor is "+str(factor))
-            if factor is not None:
-                value *= factor
-
-            form_field = self.defaults_form_fields[field]
-            if units is None:
-                form_field.set_value(value)
-            elif (units == 'IN' or units == 'MM') and (field == 'global_gridx' or field == 'global_gridy'):
-                form_field.set_value(value)
-
-        except KeyError:
-            pass
-        except AttributeError:
-            log.debug(field)
-
-    def show_preferences_gui(self):
-        """
-        Called to initialize and show the Preferences GUI
-
-        :return: None
-        """
-
-        gen_form = self.ui.general_defaults_form
-        try:
-            self.ui.general_scroll_area.takeWidget()
-        except Exception:
-            log.debug("Nothing to remove")
-        self.ui.general_scroll_area.setWidget(gen_form)
-        gen_form.show()
-
-        ger_form = self.ui.gerber_defaults_form
-        try:
-            self.ui.gerber_scroll_area.takeWidget()
-        except Exception:
-            log.debug("Nothing to remove")
-        self.ui.gerber_scroll_area.setWidget(ger_form)
-        ger_form.show()
-
-        exc_form = self.ui.excellon_defaults_form
-        try:
-            self.ui.excellon_scroll_area.takeWidget()
-        except Exception:
-            log.debug("Nothing to remove")
-        self.ui.excellon_scroll_area.setWidget(exc_form)
-        exc_form.show()
-
-        geo_form = self.ui.geometry_defaults_form
-        try:
-            self.ui.geometry_scroll_area.takeWidget()
-        except Exception:
-            log.debug("Nothing to remove")
-        self.ui.geometry_scroll_area.setWidget(geo_form)
-        geo_form.show()
-
-        cnc_form = self.ui.cncjob_defaults_form
-        try:
-            self.ui.cncjob_scroll_area.takeWidget()
-        except Exception:
-            log.debug("Nothing to remove")
-        self.ui.cncjob_scroll_area.setWidget(cnc_form)
-        cnc_form.show()
-
-        tools_form = self.ui.tools_defaults_form
-        try:
-            self.ui.tools_scroll_area.takeWidget()
-        except Exception:
-            log.debug("Nothing to remove")
-        self.ui.tools_scroll_area.setWidget(tools_form)
-        tools_form.show()
-
-        tools2_form = self.ui.tools2_defaults_form
-        try:
-            self.ui.tools2_scroll_area.takeWidget()
-        except Exception:
-            log.debug("Nothing to remove")
-        self.ui.tools2_scroll_area.setWidget(tools2_form)
-        tools2_form.show()
-
-        fa_form = self.ui.util_defaults_form
-        try:
-            self.ui.fa_scroll_area.takeWidget()
-        except Exception:
-            log.debug("Nothing to remove")
-        self.ui.fa_scroll_area.setWidget(fa_form)
-        fa_form.show()
-
-        # Initialize the color box's color in Preferences -> Global -> Colo
-        self.__init_color_pickers()
-
-        # Button handlers
-        self.ui.pref_save_button.clicked.connect(lambda: self.on_save_button(save_to_file=True))
-        self.ui.pref_apply_button.clicked.connect(lambda: self.on_save_button(save_to_file=False))
-        self.ui.pref_close_button.clicked.connect(self.on_pref_close_button)
-        self.ui.pref_defaults_button.clicked.connect(self.on_restore_defaults_preferences)
-
-        log.debug("Finished Preferences GUI form initialization.")
-
-    def __init_color_pickers(self):
-        # Init Gerber Plot Colors
-        self.ui.gerber_defaults_form.gerber_gen_group.pf_color_entry.set_value(self.defaults['gerber_plot_fill'])
-        self.ui.gerber_defaults_form.gerber_gen_group.pf_color_button.setStyleSheet(
-            "background-color:%s;"
-            "border-color: dimgray" % str(self.defaults['gerber_plot_fill'])[:7])
-        self.ui.gerber_defaults_form.gerber_gen_group.pf_color_alpha_spinner.set_value(
-            int(self.defaults['gerber_plot_fill'][7:9], 16))
-        self.ui.gerber_defaults_form.gerber_gen_group.pf_color_alpha_slider.setValue(
-            int(self.defaults['gerber_plot_fill'][7:9], 16))
-
-        self.ui.gerber_defaults_form.gerber_gen_group.pl_color_entry.set_value(self.defaults['gerber_plot_line'])
-        self.ui.gerber_defaults_form.gerber_gen_group.pl_color_button.setStyleSheet(
-            "background-color:%s;"
-            "border-color: dimgray" % str(self.defaults['gerber_plot_line'])[:7])
-
-        # Init Excellon Plot Colors
-        self.ui.excellon_defaults_form.excellon_gen_group.fill_color_entry.set_value(
-            self.defaults['excellon_plot_fill'])
-        self.ui.excellon_defaults_form.excellon_gen_group.fill_color_button.setStyleSheet(
-            "background-color:%s;"
-            "border-color: dimgray" % str(self.defaults['excellon_plot_fill'])[:7])
-        self.ui.excellon_defaults_form.excellon_gen_group.color_alpha_spinner.set_value(
-            int(self.defaults['excellon_plot_fill'][7:9], 16))
-        self.ui.excellon_defaults_form.excellon_gen_group.color_alpha_slider.setValue(
-            int(self.defaults['excellon_plot_fill'][7:9], 16))
-
-        self.ui.excellon_defaults_form.excellon_gen_group.line_color_entry.set_value(
-            self.defaults['excellon_plot_line'])
-        self.ui.excellon_defaults_form.excellon_gen_group.line_color_button.setStyleSheet(
-            "background-color:%s;"
-            "border-color: dimgray" % str(self.defaults['excellon_plot_line'])[:7])
-
-        # Init Geometry Plot Colors
-        self.ui.geometry_defaults_form.geometry_gen_group.line_color_entry.set_value(
-            self.defaults['geometry_plot_line'])
-        self.ui.geometry_defaults_form.geometry_gen_group.line_color_button.setStyleSheet(
-            "background-color:%s;"
-            "border-color: dimgray" % str(self.defaults['geometry_plot_line'])[:7])
-
-        # Init CNCJob Travel Line Colors
-        self.ui.cncjob_defaults_form.cncjob_gen_group.tfill_color_entry.set_value(
-            self.defaults['cncjob_travel_fill'])
-        self.ui.cncjob_defaults_form.cncjob_gen_group.tfill_color_button.setStyleSheet(
-            "background-color:%s;"
-            "border-color: dimgray" % str(self.defaults['cncjob_travel_fill'])[:7])
-        self.ui.cncjob_defaults_form.cncjob_gen_group.tcolor_alpha_spinner.set_value(
-            int(self.defaults['cncjob_travel_fill'][7:9], 16))
-        self.ui.cncjob_defaults_form.cncjob_gen_group.tcolor_alpha_slider.setValue(
-            int(self.defaults['cncjob_travel_fill'][7:9], 16))
-
-        self.ui.cncjob_defaults_form.cncjob_gen_group.tline_color_entry.set_value(
-            self.defaults['cncjob_travel_line'])
-        self.ui.cncjob_defaults_form.cncjob_gen_group.tline_color_button.setStyleSheet(
-            "background-color:%s;"
-            "border-color: dimgray" % str(self.defaults['cncjob_travel_line'])[:7])
-
-        # Init CNCJob Plot Colors
-        self.ui.cncjob_defaults_form.cncjob_gen_group.fill_color_entry.set_value(
-            self.defaults['cncjob_plot_fill'])
-        self.ui.cncjob_defaults_form.cncjob_gen_group.fill_color_button.setStyleSheet(
-            "background-color:%s;"
-            "border-color: dimgray" % str(self.defaults['cncjob_plot_fill'])[:7])
-
-        self.ui.cncjob_defaults_form.cncjob_gen_group.line_color_entry.set_value(
-            self.defaults['cncjob_plot_line'])
-        self.ui.cncjob_defaults_form.cncjob_gen_group.line_color_button.setStyleSheet(
-            "background-color:%s;"
-            "border-color: dimgray" % str(self.defaults['cncjob_plot_line'])[:7])
-
-        # Init Left-Right Selection colors
-        self.ui.general_defaults_form.general_gui_group.sf_color_entry.set_value(self.defaults['global_sel_fill'])
-        self.ui.general_defaults_form.general_gui_group.sf_color_button.setStyleSheet(
-            "background-color:%s;"
-            "border-color: dimgray" % str(self.defaults['global_sel_fill'])[:7])
-        self.ui.general_defaults_form.general_gui_group.sf_color_alpha_spinner.set_value(
-            int(self.defaults['global_sel_fill'][7:9], 16))
-        self.ui.general_defaults_form.general_gui_group.sf_color_alpha_slider.setValue(
-            int(self.defaults['global_sel_fill'][7:9], 16))
-
-        self.ui.general_defaults_form.general_gui_group.sl_color_entry.set_value(self.defaults['global_sel_line'])
-        self.ui.general_defaults_form.general_gui_group.sl_color_button.setStyleSheet(
-            "background-color:%s;"
-            "border-color: dimgray" % str(self.defaults['global_sel_line'])[:7])
-
-        # Init Right-Left Selection colors
-        self.ui.general_defaults_form.general_gui_group.alt_sf_color_entry.set_value(
-            self.defaults['global_alt_sel_fill'])
-        self.ui.general_defaults_form.general_gui_group.alt_sf_color_button.setStyleSheet(
-            "background-color:%s;"
-            "border-color: dimgray" % str(self.defaults['global_alt_sel_fill'])[:7])
-        self.ui.general_defaults_form.general_gui_group.alt_sf_color_alpha_spinner.set_value(
-            int(self.defaults['global_sel_fill'][7:9], 16))
-        self.ui.general_defaults_form.general_gui_group.alt_sf_color_alpha_slider.setValue(
-            int(self.defaults['global_sel_fill'][7:9], 16))
-
-        self.ui.general_defaults_form.general_gui_group.alt_sl_color_entry.set_value(
-            self.defaults['global_alt_sel_line'])
-        self.ui.general_defaults_form.general_gui_group.alt_sl_color_button.setStyleSheet(
-            "background-color:%s;"
-            "border-color: dimgray" % str(self.defaults['global_alt_sel_line'])[:7])
-
-        # Init Draw color and Selection Draw Color
-        self.ui.general_defaults_form.general_gui_group.draw_color_entry.set_value(
-            self.defaults['global_draw_color'])
-        self.ui.general_defaults_form.general_gui_group.draw_color_button.setStyleSheet(
-            "background-color:%s;"
-            "border-color: dimgray" % str(self.defaults['global_draw_color'])[:7])
-
-        self.ui.general_defaults_form.general_gui_group.sel_draw_color_entry.set_value(
-            self.defaults['global_sel_draw_color'])
-        self.ui.general_defaults_form.general_gui_group.sel_draw_color_button.setStyleSheet(
-            "background-color:%s;"
-            "border-color: dimgray" % str(self.defaults['global_sel_draw_color'])[:7])
-
-        # Init Project Items color
-        self.ui.general_defaults_form.general_gui_group.proj_color_entry.set_value(
-            self.defaults['global_proj_item_color'])
-        self.ui.general_defaults_form.general_gui_group.proj_color_button.setStyleSheet(
-            "background-color:%s;"
-            "border-color: dimgray" % str(self.defaults['global_proj_item_color'])[:7])
-
-        # Init Project Disabled Items color
-        self.ui.general_defaults_form.general_gui_group.proj_color_dis_entry.set_value(
-            self.defaults['global_proj_item_dis_color'])
-        self.ui.general_defaults_form.general_gui_group.proj_color_dis_button.setStyleSheet(
-            "background-color:%s;"
-            "border-color: dimgray" % str(self.defaults['global_proj_item_dis_color'])[:7])
-
-        # Init Project Disabled Items color
-        self.ui.general_defaults_form.general_app_set_group.mouse_cursor_entry.set_value(
-            self.defaults['global_cursor_color'])
-        self.ui.general_defaults_form.general_app_set_group.mouse_cursor_button.setStyleSheet(
-            "background-color:%s;"
-            "border-color: dimgray" % str(self.defaults['global_cursor_color'])[:7])
-
-        # Init the Annotation CNC Job color
-        self.ui.cncjob_defaults_form.cncjob_adv_opt_group.annotation_fontcolor_entry.set_value(
-            self.defaults['cncjob_annotation_fontcolor'])
-        self.ui.cncjob_defaults_form.cncjob_adv_opt_group.annotation_fontcolor_button.setStyleSheet(
-            "background-color:%s;"
-            "border-color: dimgray" % str(self.defaults['cncjob_annotation_fontcolor'])[:7])
-
-        # Init the Tool Film color
-        self.ui.tools_defaults_form.tools_film_group.film_color_entry.set_value(
-            self.defaults['tools_film_color'])
-        self.ui.tools_defaults_form.tools_film_group.film_color_button.setStyleSheet(
-            "background-color:%s;"
-            "border-color: dimgray" % str(self.defaults['tools_film_color'])[:7]
-        )
-
-        # Init the Tool QRCode colors
-        self.ui.tools2_defaults_form.tools2_qrcode_group.fill_color_entry.set_value(
-            self.defaults['tools_qrcode_fill_color'])
-        self.ui.tools2_defaults_form.tools2_qrcode_group.fill_color_button.setStyleSheet(
-            "background-color:%s;"
-            "border-color: dimgray" % str(self.defaults['tools_qrcode_fill_color'])[:7])
-
-        self.ui.tools2_defaults_form.tools2_qrcode_group.back_color_entry.set_value(
-            self.defaults['tools_qrcode_back_color'])
-        self.ui.tools2_defaults_form.tools2_qrcode_group.back_color_button.setStyleSheet(
-            "background-color:%s;"
-            "border-color: dimgray" % str(self.defaults['tools_qrcode_back_color'])[:7])
-
-    def on_save_button(self, save_to_file=True):
-        log.debug("on_save_button() --> Applying preferences to file.")
-
-        # Preferences saved, update flag
-        self.preferences_changed_flag = False
-
-        # Preferences save, update the color of the Preferences Tab text
-        for idx in range(self.ui.plot_tab_area.count()):
-            if self.ui.plot_tab_area.tabText(idx) == _("Preferences"):
-                self.ui.plot_tab_area.tabBar.setTabTextColor(idx, QtGui.QColor('black'))
-
-        # restore the default stylesheet by setting a blank one
-        self.ui.pref_apply_button.setStyleSheet("")
-
-        self.inform.emit('%s' % _("Preferences applied."))
-
-        # make sure we update the self.current_defaults dict used to undo changes to self.defaults
-        self.defaults.current_defaults.update(self.defaults)
-
-        if save_to_file:
-            self.save_defaults(silent=False)
-            # load the defaults so they are updated into the app
-            self.defaults.load(filename=os.path.join(self.data_path, 'current_defaults.FlatConfig'))
-
-        # Re-fresh project options
-        self.ui.app.on_options_app2project()
-
-        settgs = QSettings("Open Source", "FlatCAM")
-
-        # save the notebook font size
-        fsize = self.ui.general_defaults_form.general_app_set_group.notebook_font_size_spinner.get_value()
-        settgs.setValue('notebook_font_size', fsize)
-
-        # save the axis font size
-        g_fsize = self.ui.general_defaults_form.general_app_set_group.axis_font_size_spinner.get_value()
-        settgs.setValue('axis_font_size', g_fsize)
-
-        # save the textbox font size
-        tb_fsize = self.ui.general_defaults_form.general_app_set_group.textbox_font_size_spinner.get_value()
-        settgs.setValue('textbox_font_size', tb_fsize)
-
-        settgs.setValue(
-            'machinist',
-            1 if self.ui.general_defaults_form.general_app_set_group.machinist_cb.get_value() else 0
-        )
-
-        # This will write the setting to the platform specific storage.
-        del settgs
-
-        if save_to_file:
-            # close the tab and delete it
-            for idx in range(self.ui.plot_tab_area.count()):
-                if self.ui.plot_tab_area.tabText(idx) == _("Preferences"):
-                    self.ui.plot_tab_area.tabBar.setTabTextColor(idx, QtGui.QColor('black'))
-                    self.ui.plot_tab_area.closeTab(idx)
-                    break
-
-    def on_pref_close_button(self):
-        # Preferences saved, update flag
-        self.preferences_changed_flag = False
-        self.ignore_tab_close_event = True
-
-        try:
-            self.ui.general_defaults_form.general_app_group.units_radio.activated_custom.disconnect()
-        except (TypeError, AttributeError):
-            pass
-        self.defaults_write_form(source_dict=self.defaults.current_defaults)
-        self.ui.general_defaults_form.general_app_group.units_radio.activated_custom.connect(
-            lambda: self.ui.app.on_toggle_units(no_pref=False))
-        self.defaults.update(self.defaults.current_defaults)
-
-        # Preferences save, update the color of the Preferences Tab text
-        for idx in range(self.ui.plot_tab_area.count()):
-            if self.ui.plot_tab_area.tabText(idx) == _("Preferences"):
-                self.ui.plot_tab_area.tabBar.setTabTextColor(idx, QtGui.QColor('black'))
-                self.ui.plot_tab_area.closeTab(idx)
-                break
-
-        self.inform.emit('%s' % _("Preferences closed without saving."))
-        self.ignore_tab_close_event = False
-
-    def on_restore_defaults_preferences(self):
-        """
-        Loads the application's factory default settings into ``self.defaults``.
-
-        :return: None
-        """
-        log.debug("on_restore_defaults_preferences()")
-        self.defaults.reset_to_factory_defaults()
-        self.on_preferences_edited()
-        self.inform.emit('[success] %s' % _("Preferences default values are restored."))
-
-    def save_defaults(self, silent=False, data_path=None, first_time=False):
-        """
-        Saves application default options
-        ``self.defaults`` to current_defaults.FlatConfig file.
-        Save the toolbars visibility status to the preferences file (current_defaults.FlatConfig) to be
-        used at the next launch of the application.
-
-        :param silent:      Whether to display a message in status bar or not; boolean
-        :param data_path:   The path where to save the preferences file (current_defaults.FlatConfig)
-        When the application is portable it should be a mobile location.
-        :param first_time:  Boolean. If True will execute some code when the app is run first time
-        :return:            None
-        """
-        self.defaults.report_usage("save_defaults")
-
-        if data_path is None:
-            data_path = self.data_path
-
-        self.defaults.propagate_defaults()
-
-        if first_time is False:
-            self.save_toolbar_view()
-
-        # Save the options to disk
-        filename = os.path.join(data_path, "current_defaults.FlatConfig")
-        try:
-            self.defaults.write(filename=filename)
-        except Exception as e:
-            log.error("save_defaults() --> Failed to write defaults to file %s" % str(e))
-            self.inform.emit('[ERROR_NOTCL] %s %s' % (_("Failed to write defaults to file."), str(filename)))
-            return
-
-        if not silent:
-            self.inform.emit('[success] %s' % _("Preferences saved."))
-
-        # update the autosave timer
-        self.ui.app.save_project_auto_update()
-
-    def save_toolbar_view(self):
-        """
-        Will save the toolbar view state to the defaults
-
-        :return:            None
-        """
-
-        # Save the toolbar view
-        tb_status = 0
-        if self.ui.toolbarfile.isVisible():
-            tb_status += 1
-
-        if self.ui.toolbargeo.isVisible():
-            tb_status += 2
-
-        if self.ui.toolbarview.isVisible():
-            tb_status += 4
-
-        if self.ui.toolbartools.isVisible():
-            tb_status += 8
-
-        if self.ui.exc_edit_toolbar.isVisible():
-            tb_status += 16
-
-        if self.ui.geo_edit_toolbar.isVisible():
-            tb_status += 32
-
-        if self.ui.grb_edit_toolbar.isVisible():
-            tb_status += 64
-
-        if self.ui.snap_toolbar.isVisible():
-            tb_status += 128
-
-        if self.ui.toolbarshell.isVisible():
-            tb_status += 256
-
-        self.defaults["global_toolbar_view"] = tb_status
-
-    def on_preferences_edited(self):
-        """
-        Executed when a preference was changed in the Edit -> Preferences tab.
-        Will color the Preferences tab text to Red color.
-        :return:
-        """
-        if self.preferences_changed_flag is False:
-            self.inform.emit('[WARNING_NOTCL] %s' % _("Preferences edited but not saved."))
-
-            for idx in range(self.ui.plot_tab_area.count()):
-                if self.ui.plot_tab_area.tabText(idx) == _("Preferences"):
-                    self.ui.plot_tab_area.tabBar.setTabTextColor(idx, QtGui.QColor('red'))
-
-            self.ui.pref_apply_button.setStyleSheet("QPushButton {color: red;}")
-
-            self.preferences_changed_flag = True
-
-    def on_close_preferences_tab(self):
-        if self.ignore_tab_close_event:
-            return
-
-        # disconnect
-        for idx in range(self.ui.pref_tab_area.count()):
-            for tb in self.ui.pref_tab_area.widget(idx).findChildren(QtCore.QObject):
-                try:
-                    tb.textEdited.disconnect(self.on_preferences_edited)
-                except (TypeError, AttributeError):
-                    pass
-
-                try:
-                    tb.modificationChanged.disconnect(self.on_preferences_edited)
-                except (TypeError, AttributeError):
-                    pass
-
-                try:
-                    tb.toggled.disconnect(self.on_preferences_edited)
-                except (TypeError, AttributeError):
-                    pass
-
-                try:
-                    tb.valueChanged.disconnect(self.on_preferences_edited)
-                except (TypeError, AttributeError):
-                    pass
-
-                try:
-                    tb.currentIndexChanged.disconnect(self.on_preferences_edited)
-                except (TypeError, AttributeError):
-                    pass
-
-        # Prompt user to save
-        if self.preferences_changed_flag is True:
-            msgbox = QtWidgets.QMessageBox()
-            msgbox.setText(_("One or more values are changed.\n"
-                             "Do you want to save the Preferences?"))
-            msgbox.setWindowTitle(_("Save Preferences"))
-            msgbox.setWindowIcon(QtGui.QIcon(self.ui.app.resource_location + '/save_as.png'))
-
-            bt_yes = msgbox.addButton(_('Yes'), QtWidgets.QMessageBox.YesRole)
-            msgbox.addButton(_('No'), QtWidgets.QMessageBox.NoRole)
-
-            msgbox.setDefaultButton(bt_yes)
-            msgbox.exec_()
-            response = msgbox.clickedButton()
-
-            if response == bt_yes:
-                self.on_save_button(save_to_file=True)
-                self.inform.emit('[success] %s' % _("Preferences saved."))
-            else:
-                self.preferences_changed_flag = False
-                self.inform.emit('')
-                return
-
-
-class OptionsGroupUI(QtWidgets.QGroupBox):
-    app = None
-
-    def __init__(self, title, parent=None):
-        # QtGui.QGroupBox.__init__(self, title, parent=parent)
-        super(OptionsGroupUI, self).__init__()
-        self.setStyleSheet("""
-        QGroupBox
-        {
-            font-size: 16px;
-            font-weight: bold;
-        }
-        """)
-
-        self.layout = QtWidgets.QVBoxLayout()
-        self.setLayout(self.layout)
-
-
-class GeneralPreferencesUI(QtWidgets.QWidget):
-    def __init__(self, decimals, parent=None):
-        QtWidgets.QWidget.__init__(self, parent=parent)
-        self.layout = QtWidgets.QHBoxLayout()
-        self.setLayout(self.layout)
-        self.decimals = decimals
-
-        self.general_app_group = GeneralAppPrefGroupUI(decimals=self.decimals)
-        self.general_app_group.setMinimumWidth(250)
-
-        self.general_gui_group = GeneralGUIPrefGroupUI(decimals=self.decimals)
-        self.general_gui_group.setMinimumWidth(250)
-
-        self.general_app_set_group = GeneralAPPSetGroupUI(decimals=self.decimals)
-        self.general_app_set_group.setMinimumWidth(250)
-
-        self.layout.addWidget(self.general_app_group)
-        self.layout.addWidget(self.general_gui_group)
-        self.layout.addWidget(self.general_app_set_group)
-
-        self.layout.addStretch()
-
-
-class GerberPreferencesUI(QtWidgets.QWidget):
-
-    def __init__(self, decimals, parent=None):
-        QtWidgets.QWidget.__init__(self, parent=parent)
-        self.layout = QtWidgets.QHBoxLayout()
-        self.setLayout(self.layout)
-        self.decimals = decimals
-
-        self.gerber_gen_group = GerberGenPrefGroupUI(decimals=self.decimals)
-        self.gerber_gen_group.setMinimumWidth(250)
-        self.gerber_opt_group = GerberOptPrefGroupUI(decimals=self.decimals)
-        self.gerber_opt_group.setMinimumWidth(250)
-        self.gerber_exp_group = GerberExpPrefGroupUI(decimals=self.decimals)
-        self.gerber_exp_group.setMinimumWidth(230)
-        self.gerber_adv_opt_group = GerberAdvOptPrefGroupUI(decimals=self.decimals)
-        self.gerber_adv_opt_group.setMinimumWidth(200)
-        self.gerber_editor_group = GerberEditorPrefGroupUI(decimals=self.decimals)
-        self.gerber_editor_group.setMinimumWidth(200)
-
-        self.vlay = QtWidgets.QVBoxLayout()
-        self.vlay.addWidget(self.gerber_opt_group)
-        self.vlay.addWidget(self.gerber_exp_group)
-
-        self.layout.addWidget(self.gerber_gen_group)
-        self.layout.addLayout(self.vlay)
-        self.layout.addWidget(self.gerber_adv_opt_group)
-        self.layout.addWidget(self.gerber_editor_group)
-
-        self.layout.addStretch()
-
-
-class ExcellonPreferencesUI(QtWidgets.QWidget):
-
-    def __init__(self, decimals, parent=None):
-        QtWidgets.QWidget.__init__(self, parent=parent)
-        self.layout = QtWidgets.QHBoxLayout()
-        self.setLayout(self.layout)
-        self.decimals = decimals
-
-        self.excellon_gen_group = ExcellonGenPrefGroupUI(decimals=self.decimals)
-        self.excellon_gen_group.setMinimumWidth(220)
-        self.excellon_opt_group = ExcellonOptPrefGroupUI(decimals=self.decimals)
-        self.excellon_opt_group.setMinimumWidth(290)
-        self.excellon_exp_group = ExcellonExpPrefGroupUI(decimals=self.decimals)
-        self.excellon_exp_group.setMinimumWidth(250)
-        self.excellon_adv_opt_group = ExcellonAdvOptPrefGroupUI(decimals=self.decimals)
-        self.excellon_adv_opt_group.setMinimumWidth(250)
-        self.excellon_editor_group = ExcellonEditorPrefGroupUI(decimals=self.decimals)
-        self.excellon_editor_group.setMinimumWidth(260)
-
-        self.vlay = QtWidgets.QVBoxLayout()
-        self.vlay.addWidget(self.excellon_opt_group)
-        self.vlay.addWidget(self.excellon_exp_group)
-
-        self.layout.addWidget(self.excellon_gen_group)
-        self.layout.addLayout(self.vlay)
-        self.layout.addWidget(self.excellon_adv_opt_group)
-        self.layout.addWidget(self.excellon_editor_group)
-
-        self.layout.addStretch()
-
-
-class GeometryPreferencesUI(QtWidgets.QWidget):
-
-    def __init__(self, decimals, parent=None):
-        QtWidgets.QWidget.__init__(self, parent=parent)
-        self.layout = QtWidgets.QHBoxLayout()
-        self.setLayout(self.layout)
-        self.decimals = decimals
-
-        self.geometry_gen_group = GeometryGenPrefGroupUI(decimals=self.decimals)
-        self.geometry_gen_group.setMinimumWidth(220)
-        self.geometry_opt_group = GeometryOptPrefGroupUI(decimals=self.decimals)
-        self.geometry_opt_group.setMinimumWidth(300)
-        self.geometry_adv_opt_group = GeometryAdvOptPrefGroupUI(decimals=self.decimals)
-        self.geometry_adv_opt_group.setMinimumWidth(270)
-        self.geometry_editor_group = GeometryEditorPrefGroupUI(decimals=self.decimals)
-        self.geometry_editor_group.setMinimumWidth(250)
-
-        self.layout.addWidget(self.geometry_gen_group)
-        self.layout.addWidget(self.geometry_opt_group)
-        self.layout.addWidget(self.geometry_adv_opt_group)
-        self.layout.addWidget(self.geometry_editor_group)
-
-        self.layout.addStretch()
-
-
-class ToolsPreferencesUI(QtWidgets.QWidget):
-
-    def __init__(self, decimals, parent=None):
-        QtWidgets.QWidget.__init__(self, parent=parent)
-        self.layout = QtWidgets.QHBoxLayout()
-        self.setLayout(self.layout)
-        self.decimals = decimals
-
-        self.tools_ncc_group = ToolsNCCPrefGroupUI(decimals=self.decimals)
-        self.tools_ncc_group.setMinimumWidth(220)
-
-        self.tools_paint_group = ToolsPaintPrefGroupUI(decimals=self.decimals)
-        self.tools_paint_group.setMinimumWidth(220)
-
-        self.tools_cutout_group = ToolsCutoutPrefGroupUI(decimals=self.decimals)
-        self.tools_cutout_group.setMinimumWidth(220)
-
-        self.tools_2sided_group = Tools2sidedPrefGroupUI(decimals=self.decimals)
-        self.tools_2sided_group.setMinimumWidth(220)
-
-        self.tools_film_group = ToolsFilmPrefGroupUI(decimals=self.decimals)
-        self.tools_film_group.setMinimumWidth(220)
-
-        self.tools_panelize_group = ToolsPanelizePrefGroupUI(decimals=self.decimals)
-        self.tools_panelize_group.setMinimumWidth(220)
-
-        self.tools_calculators_group = ToolsCalculatorsPrefGroupUI(decimals=self.decimals)
-        self.tools_calculators_group.setMinimumWidth(220)
-
-        self.tools_transform_group = ToolsTransformPrefGroupUI(decimals=self.decimals)
-        self.tools_transform_group.setMinimumWidth(200)
-
-        self.tools_solderpaste_group = ToolsSolderpastePrefGroupUI(decimals=self.decimals)
-        self.tools_solderpaste_group.setMinimumWidth(200)
-
-        self.tools_sub_group = ToolsSubPrefGroupUI(decimals=self.decimals)
-        self.tools_sub_group.setMinimumWidth(200)
-
-        self.vlay = QtWidgets.QVBoxLayout()
-        self.vlay.addWidget(self.tools_ncc_group)
-        self.vlay.addWidget(self.tools_cutout_group)
-
-        self.vlay1 = QtWidgets.QVBoxLayout()
-        self.vlay1.addWidget(self.tools_paint_group)
-        self.vlay1.addWidget(self.tools_panelize_group)
-
-        self.vlay2 = QtWidgets.QVBoxLayout()
-        self.vlay2.addWidget(self.tools_transform_group)
-        self.vlay2.addWidget(self.tools_2sided_group)
-        self.vlay2.addWidget(self.tools_sub_group)
-
-        self.vlay3 = QtWidgets.QVBoxLayout()
-        self.vlay3.addWidget(self.tools_film_group)
-        self.vlay3.addWidget(self.tools_calculators_group)
-
-        self.vlay4 = QtWidgets.QVBoxLayout()
-        self.vlay4.addWidget(self.tools_solderpaste_group)
-
-        self.layout.addLayout(self.vlay)
-        self.layout.addLayout(self.vlay1)
-        self.layout.addLayout(self.vlay2)
-        self.layout.addLayout(self.vlay3)
-        self.layout.addLayout(self.vlay4)
-
-        self.layout.addStretch()
-
-
-class Tools2PreferencesUI(QtWidgets.QWidget):
-
-    def __init__(self, decimals, parent=None):
-        QtWidgets.QWidget.__init__(self, parent=parent)
-        self.layout = QtWidgets.QHBoxLayout()
-        self.setLayout(self.layout)
-        self.decimals = decimals
-
-        self.tools2_checkrules_group = Tools2RulesCheckPrefGroupUI(decimals=self.decimals)
-        self.tools2_checkrules_group.setMinimumWidth(220)
-
-        self.tools2_optimal_group = Tools2OptimalPrefGroupUI(decimals=self.decimals)
-        self.tools2_optimal_group.setMinimumWidth(220)
-
-        self.tools2_qrcode_group = Tools2QRCodePrefGroupUI(decimals=self.decimals)
-        self.tools2_qrcode_group.setMinimumWidth(220)
-
-        self.tools2_cfill_group = Tools2CThievingPrefGroupUI(decimals=self.decimals)
-        self.tools2_cfill_group.setMinimumWidth(220)
-
-        self.tools2_fiducials_group = Tools2FiducialsPrefGroupUI(decimals=self.decimals)
-        self.tools2_fiducials_group.setMinimumWidth(220)
-
-        self.tools2_cal_group = Tools2CalPrefGroupUI(decimals=self.decimals)
-        self.tools2_cal_group.setMinimumWidth(220)
-
-        self.tools2_edrills_group = Tools2EDrillsPrefGroupUI(decimals=self.decimals)
-        self.tools2_edrills_group.setMinimumWidth(220)
-
-        self.tools2_punch_group = Tools2PunchGerberPrefGroupUI(decimals=self.decimals)
-        self.tools2_punch_group.setMinimumWidth(220)
-
-        self.tools2_invert_group = Tools2InvertPrefGroupUI(decimals=self.decimals)
-        self.tools2_invert_group.setMinimumWidth(220)
-
-        self.vlay = QtWidgets.QVBoxLayout()
-        self.vlay.addWidget(self.tools2_checkrules_group)
-        self.vlay.addWidget(self.tools2_optimal_group)
-
-        self.vlay1 = QtWidgets.QVBoxLayout()
-        self.vlay1.addWidget(self.tools2_qrcode_group)
-        self.vlay1.addWidget(self.tools2_fiducials_group)
-
-        self.vlay2 = QtWidgets.QVBoxLayout()
-        self.vlay2.addWidget(self.tools2_cfill_group)
-
-        self.vlay3 = QtWidgets.QVBoxLayout()
-        self.vlay3.addWidget(self.tools2_cal_group)
-        self.vlay3.addWidget(self.tools2_edrills_group)
-
-        self.vlay4 = QtWidgets.QVBoxLayout()
-        self.vlay4.addWidget(self.tools2_punch_group)
-        self.vlay4.addWidget(self.tools2_invert_group)
-
-        self.layout.addLayout(self.vlay)
-        self.layout.addLayout(self.vlay1)
-        self.layout.addLayout(self.vlay2)
-        self.layout.addLayout(self.vlay3)
-        self.layout.addLayout(self.vlay4)
-
-        self.layout.addStretch()
-
-
-class CNCJobPreferencesUI(QtWidgets.QWidget):
-
-    def __init__(self, decimals, parent=None):
-        QtWidgets.QWidget.__init__(self, parent=parent)
-        self.layout = QtWidgets.QHBoxLayout()
-        self.setLayout(self.layout)
-        self.decimals = decimals
-
-        self.cncjob_gen_group = CNCJobGenPrefGroupUI(decimals=self.decimals)
-        self.cncjob_gen_group.setMinimumWidth(260)
-        self.cncjob_opt_group = CNCJobOptPrefGroupUI(decimals=self.decimals)
-        self.cncjob_opt_group.setMinimumWidth(260)
-        self.cncjob_adv_opt_group = CNCJobAdvOptPrefGroupUI(decimals=self.decimals)
-        self.cncjob_adv_opt_group.setMinimumWidth(260)
-
-        self.layout.addWidget(self.cncjob_gen_group)
-        self.layout.addWidget(self.cncjob_opt_group)
-        self.layout.addWidget(self.cncjob_adv_opt_group)
-
-        self.layout.addStretch()
-
-
-class UtilPreferencesUI(QtWidgets.QWidget):
-
-    def __init__(self, decimals, parent=None):
-        QtWidgets.QWidget.__init__(self, parent=parent)
-        self.layout = QtWidgets.QHBoxLayout()
-        self.setLayout(self.layout)
-        self.decimals = decimals
-
-        self.vlay = QtWidgets.QVBoxLayout()
-        self.fa_excellon_group = FAExcPrefGroupUI(decimals=self.decimals)
-        self.fa_excellon_group.setMinimumWidth(260)
-
-        self.fa_gcode_group = FAGcoPrefGroupUI(decimals=self.decimals)
-        self.fa_gcode_group.setMinimumWidth(260)
-
-        self.vlay.addWidget(self.fa_excellon_group)
-        self.vlay.addWidget(self.fa_gcode_group)
-
-        self.fa_gerber_group = FAGrbPrefGroupUI(decimals=self.decimals)
-        self.fa_gerber_group.setMinimumWidth(260)
-
-        self.kw_group = AutoCompletePrefGroupUI(decimals=self.decimals)
-        self.kw_group.setMinimumWidth(260)
-
-        self.layout.addLayout(self.vlay)
-        self.layout.addWidget(self.fa_gerber_group)
-        self.layout.addWidget(self.kw_group)
-
-        self.layout.addStretch()
-
-
-class GeneralGUIPrefGroupUI(OptionsGroupUI):
-    def __init__(self, decimals=4, parent=None):
-        super(GeneralGUIPrefGroupUI, self).__init__(self)
-
-        self.setTitle(str(_("GUI Preferences")))
-        self.decimals = decimals
-
-        # Create a grid layout for the Application general settings
-        grid0 = QtWidgets.QGridLayout()
-        self.layout.addLayout(grid0)
-        grid0.setColumnStretch(0, 0)
-        grid0.setColumnStretch(1, 1)
-
-        # Theme selection
-        self.theme_label = QtWidgets.QLabel('%s:' % _('Theme'))
-        self.theme_label.setToolTip(
-            _("Select a theme for FlatCAM.\n"
-              "It will theme the plot area.")
-        )
-
-        self.theme_radio = RadioSet([
-            {"label": _("Light"), "value": "white"},
-            {"label": _("Dark"), "value": "black"}
-        ], orientation='vertical')
-
-        grid0.addWidget(self.theme_label, 0, 0)
-        grid0.addWidget(self.theme_radio, 0, 1)
-
-        # Enable Gray Icons
-        self.gray_icons_cb = FCCheckBox('%s' % _('Use Gray Icons'))
-        self.gray_icons_cb.setToolTip(
-            _("Check this box to use a set of icons with\n"
-              "a lighter (gray) color. To be used when a\n"
-              "full dark theme is applied.")
-        )
-        grid0.addWidget(self.gray_icons_cb, 1, 0, 1, 3)
-
-        self.theme_button = FCButton(_("Apply Theme"))
-        self.theme_button.setToolTip(
-            _("Select a theme for FlatCAM.\n"
-              "It will theme the plot area.\n"
-              "The application will restart after change.")
-        )
-        grid0.addWidget(self.theme_button, 2, 0, 1, 3)
-
-        separator_line = QtWidgets.QFrame()
-        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
-        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
-        grid0.addWidget(separator_line, 3, 0, 1, 2)
-
-        # Layout selection
-        self.layout_label = QtWidgets.QLabel('%s:' % _('Layout'))
-        self.layout_label.setToolTip(
-            _("Select an layout for FlatCAM.\n"
-              "It is applied immediately.")
-        )
-        self.layout_combo = FCComboBox()
-        # don't translate the QCombo items as they are used in QSettings and identified by name
-        self.layout_combo.addItem("standard")
-        self.layout_combo.addItem("compact")
-        self.layout_combo.addItem("minimal")
-
-        grid0.addWidget(self.layout_label, 4, 0)
-        grid0.addWidget(self.layout_combo, 4, 1)
-
-        # Set the current index for layout_combo
-        qsettings = QSettings("Open Source", "FlatCAM")
-        if qsettings.contains("layout"):
-            layout = qsettings.value('layout', type=str)
-            idx = self.layout_combo.findText(layout.capitalize())
-            self.layout_combo.setCurrentIndex(idx)
-
-        # Style selection
-        self.style_label = QtWidgets.QLabel('%s:' % _('Style'))
-        self.style_label.setToolTip(
-            _("Select an style for FlatCAM.\n"
-              "It will be applied at the next app start.")
-        )
-        self.style_combo = FCComboBox()
-        self.style_combo.addItems(QtWidgets.QStyleFactory.keys())
-        # find current style
-        index = self.style_combo.findText(QtWidgets.qApp.style().objectName(), QtCore.Qt.MatchFixedString)
-        self.style_combo.setCurrentIndex(index)
-        self.style_combo.activated[str].connect(self.handle_style)
-
-        grid0.addWidget(self.style_label, 5, 0)
-        grid0.addWidget(self.style_combo, 5, 1)
-
-        # Enable High DPI Support
-        self.hdpi_cb = FCCheckBox('%s' % _('Activate HDPI Support'))
-        self.hdpi_cb.setToolTip(
-            _("Enable High DPI support for FlatCAM.\n"
-              "It will be applied at the next app start.")
-        )
-
-        qsettings = QSettings("Open Source", "FlatCAM")
-        if qsettings.contains("hdpi"):
-            self.hdpi_cb.set_value(qsettings.value('hdpi', type=int))
-        else:
-            self.hdpi_cb.set_value(False)
-        self.hdpi_cb.stateChanged.connect(self.handle_hdpi)
-
-        grid0.addWidget(self.hdpi_cb, 6, 0, 1, 3)
-
-        # Enable Hover box
-        self.hover_cb = FCCheckBox('%s' % _('Display Hover Shape'))
-        self.hover_cb.setToolTip(
-            _("Enable display of a hover shape for FlatCAM objects.\n"
-              "It is displayed whenever the mouse cursor is hovering\n"
-              "over any kind of not-selected object.")
-        )
-        grid0.addWidget(self.hover_cb, 8, 0, 1, 3)
-
-        # Enable Selection box
-        self.selection_cb = FCCheckBox('%s' % _('Display Selection Shape'))
-        self.selection_cb.setToolTip(
-            _("Enable the display of a selection shape for FlatCAM objects.\n"
-              "It is displayed whenever the mouse selects an object\n"
-              "either by clicking or dragging mouse from left to right or\n"
-              "right to left.")
-        )
-        grid0.addWidget(self.selection_cb, 9, 0, 1, 3)
-
-        separator_line = QtWidgets.QFrame()
-        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
-        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
-        grid0.addWidget(separator_line, 14, 0, 1, 2)
-
-        # Plot Selection (left - right) Color
-        self.sel_lr_label = QtWidgets.QLabel('<b>%s</b>' % _('Left-Right Selection Color'))
-        grid0.addWidget(self.sel_lr_label, 15, 0, 1, 2)
-
-        self.sl_color_label = QtWidgets.QLabel('%s:' % _('Outline'))
-        self.sl_color_label.setToolTip(
-            _("Set the line color for the 'left to right' selection box.")
-        )
-        self.sl_color_entry = FCEntry()
-        self.sl_color_button = QtWidgets.QPushButton()
-        self.sl_color_button.setFixedSize(15, 15)
-
-        self.form_box_child_4 = QtWidgets.QHBoxLayout()
-        self.form_box_child_4.addWidget(self.sl_color_entry)
-        self.form_box_child_4.addWidget(self.sl_color_button)
-        self.form_box_child_4.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
-
-        grid0.addWidget(self.sl_color_label, 16, 0)
-        grid0.addLayout(self.form_box_child_4, 16, 1)
-
-        self.sf_color_label = QtWidgets.QLabel('%s:' % _('Fill'))
-        self.sf_color_label.setToolTip(
-            _("Set the fill color for the selection box\n"
-              "in case that the selection is done from left to right.\n"
-              "First 6 digits are the color and the last 2\n"
-              "digits are for alpha (transparency) level.")
-        )
-        self.sf_color_entry = FCEntry()
-        self.sf_color_button = QtWidgets.QPushButton()
-        self.sf_color_button.setFixedSize(15, 15)
-
-        self.form_box_child_5 = QtWidgets.QHBoxLayout()
-        self.form_box_child_5.addWidget(self.sf_color_entry)
-        self.form_box_child_5.addWidget(self.sf_color_button)
-        self.form_box_child_5.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
-
-        grid0.addWidget(self.sf_color_label, 17, 0)
-        grid0.addLayout(self.form_box_child_5, 17, 1)
-
-        # Plot Selection (left - right) Fill Transparency Level
-        self.sf_alpha_label = QtWidgets.QLabel('%s:' % _('Alpha'))
-        self.sf_alpha_label.setToolTip(
-            _("Set the fill transparency for the 'left to right' selection box.")
-        )
-        self.sf_color_alpha_slider = QtWidgets.QSlider(QtCore.Qt.Horizontal)
-        self.sf_color_alpha_slider.setMinimum(0)
-        self.sf_color_alpha_slider.setMaximum(255)
-        self.sf_color_alpha_slider.setSingleStep(1)
-
-        self.sf_color_alpha_spinner = FCSpinner()
-        self.sf_color_alpha_spinner.setMinimumWidth(70)
-        self.sf_color_alpha_spinner.set_range(0, 255)
-
-        self.form_box_child_6 = QtWidgets.QHBoxLayout()
-        self.form_box_child_6.addWidget(self.sf_color_alpha_slider)
-        self.form_box_child_6.addWidget(self.sf_color_alpha_spinner)
-
-        grid0.addWidget(self.sf_alpha_label, 18, 0)
-        grid0.addLayout(self.form_box_child_6, 18, 1)
-
-        separator_line = QtWidgets.QFrame()
-        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
-        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
-        grid0.addWidget(separator_line, 19, 0, 1, 2)
-
-        # Plot Selection (left - right) Color
-        self.sel_rl_label = QtWidgets.QLabel('<b>%s</b>' % _('Right-Left Selection Color'))
-        grid0.addWidget(self.sel_rl_label, 20, 0, 1, 2)
-
-        # Plot Selection (right - left) Line Color
-        self.alt_sl_color_label = QtWidgets.QLabel('%s:' % _('Outline'))
-        self.alt_sl_color_label.setToolTip(
-            _("Set the line color for the 'right to left' selection box.")
-        )
-        self.alt_sl_color_entry = FCEntry()
-        self.alt_sl_color_button = QtWidgets.QPushButton()
-        self.alt_sl_color_button.setFixedSize(15, 15)
-
-        self.form_box_child_7 = QtWidgets.QHBoxLayout()
-        self.form_box_child_7.addWidget(self.alt_sl_color_entry)
-        self.form_box_child_7.addWidget(self.alt_sl_color_button)
-        self.form_box_child_7.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
-
-        grid0.addWidget(self.alt_sl_color_label, 21, 0)
-        grid0.addLayout(self.form_box_child_7, 21, 1)
-
-        # Plot Selection (right - left) Fill Color
-        self.alt_sf_color_label = QtWidgets.QLabel('%s:' % _('Fill'))
-        self.alt_sf_color_label.setToolTip(
-            _("Set the fill color for the selection box\n"
-              "in case that the selection is done from right to left.\n"
-              "First 6 digits are the color and the last 2\n"
-              "digits are for alpha (transparency) level.")
-        )
-        self.alt_sf_color_entry = FCEntry()
-        self.alt_sf_color_button = QtWidgets.QPushButton()
-        self.alt_sf_color_button.setFixedSize(15, 15)
-
-        self.form_box_child_8 = QtWidgets.QHBoxLayout()
-        self.form_box_child_8.addWidget(self.alt_sf_color_entry)
-        self.form_box_child_8.addWidget(self.alt_sf_color_button)
-        self.form_box_child_8.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
-
-        grid0.addWidget(self.alt_sf_color_label, 22, 0)
-        grid0.addLayout(self.form_box_child_8, 22, 1)
-
-        # Plot Selection (right - left) Fill Transparency Level
-        self.alt_sf_alpha_label = QtWidgets.QLabel('%s:' % _('Alpha'))
-        self.alt_sf_alpha_label.setToolTip(
-            _("Set the fill transparency for selection 'right to left' box.")
-        )
-        self.alt_sf_color_alpha_slider = QtWidgets.QSlider(QtCore.Qt.Horizontal)
-        self.alt_sf_color_alpha_slider.setMinimum(0)
-        self.alt_sf_color_alpha_slider.setMaximum(255)
-        self.alt_sf_color_alpha_slider.setSingleStep(1)
-
-        self.alt_sf_color_alpha_spinner = FCSpinner()
-        self.alt_sf_color_alpha_spinner.setMinimumWidth(70)
-        self.alt_sf_color_alpha_spinner.set_range(0, 255)
-
-        self.form_box_child_9 = QtWidgets.QHBoxLayout()
-        self.form_box_child_9.addWidget(self.alt_sf_color_alpha_slider)
-        self.form_box_child_9.addWidget(self.alt_sf_color_alpha_spinner)
-
-        grid0.addWidget(self.alt_sf_alpha_label, 23, 0)
-        grid0.addLayout(self.form_box_child_9, 23, 1)
-
-        separator_line = QtWidgets.QFrame()
-        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
-        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
-        grid0.addWidget(separator_line, 24, 0, 1, 2)
-
-        # ------------------------------------------------------------------
-        # ----------------------- Editor Color -----------------------------
-        # ------------------------------------------------------------------
-
-        self.editor_color_label = QtWidgets.QLabel('<b>%s</b>' % _('Editor Color'))
-        grid0.addWidget(self.editor_color_label, 25, 0, 1, 2)
-
-        # Editor Draw Color
-        self.draw_color_label = QtWidgets.QLabel('%s:' % _('Drawing'))
-        self.alt_sf_color_label.setToolTip(
-            _("Set the color for the shape.")
-        )
-        self.draw_color_entry = FCEntry()
-        self.draw_color_button = QtWidgets.QPushButton()
-        self.draw_color_button.setFixedSize(15, 15)
-
-        self.form_box_child_10 = QtWidgets.QHBoxLayout()
-        self.form_box_child_10.addWidget(self.draw_color_entry)
-        self.form_box_child_10.addWidget(self.draw_color_button)
-        self.form_box_child_10.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
-
-        grid0.addWidget(self.draw_color_label, 26, 0)
-        grid0.addLayout(self.form_box_child_10, 26, 1)
-
-        # Editor Draw Selection Color
-        self.sel_draw_color_label = QtWidgets.QLabel('%s:' % _('Selection'))
-        self.sel_draw_color_label.setToolTip(
-            _("Set the color of the shape when selected.")
-        )
-        self.sel_draw_color_entry = FCEntry()
-        self.sel_draw_color_button = QtWidgets.QPushButton()
-        self.sel_draw_color_button.setFixedSize(15, 15)
-
-        self.form_box_child_11 = QtWidgets.QHBoxLayout()
-        self.form_box_child_11.addWidget(self.sel_draw_color_entry)
-        self.form_box_child_11.addWidget(self.sel_draw_color_button)
-        self.form_box_child_11.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
-
-        grid0.addWidget(self.sel_draw_color_label, 27, 0)
-        grid0.addLayout(self.form_box_child_11, 27, 1)
-
-        separator_line = QtWidgets.QFrame()
-        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
-        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
-        grid0.addWidget(separator_line, 28, 0, 1, 2)
-
-        # ------------------------------------------------------------------
-        # ----------------------- Project Settings -----------------------------
-        # ------------------------------------------------------------------
-
-        self.proj_settings_label = QtWidgets.QLabel('<b>%s</b>' % _('Project Items Color'))
-        grid0.addWidget(self.proj_settings_label, 29, 0, 1, 2)
-
-        # Project Tab items color
-        self.proj_color_label = QtWidgets.QLabel('%s:' % _('Enabled'))
-        self.proj_color_label.setToolTip(
-            _("Set the color of the items in Project Tab Tree.")
-        )
-        self.proj_color_entry = FCEntry()
-        self.proj_color_button = QtWidgets.QPushButton()
-        self.proj_color_button.setFixedSize(15, 15)
-
-        self.form_box_child_12 = QtWidgets.QHBoxLayout()
-        self.form_box_child_12.addWidget(self.proj_color_entry)
-        self.form_box_child_12.addWidget(self.proj_color_button)
-        self.form_box_child_12.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
-
-        grid0.addWidget(self.proj_color_label, 30, 0)
-        grid0.addLayout(self.form_box_child_12, 30, 1)
-
-        self.proj_color_dis_label = QtWidgets.QLabel('%s:' % _('Disabled'))
-        self.proj_color_dis_label.setToolTip(
-            _("Set the color of the items in Project Tab Tree,\n"
-              "for the case when the items are disabled.")
-        )
-        self.proj_color_dis_entry = FCEntry()
-        self.proj_color_dis_button = QtWidgets.QPushButton()
-        self.proj_color_dis_button.setFixedSize(15, 15)
-
-        self.form_box_child_13 = QtWidgets.QHBoxLayout()
-        self.form_box_child_13.addWidget(self.proj_color_dis_entry)
-        self.form_box_child_13.addWidget(self.proj_color_dis_button)
-        self.form_box_child_13.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
-
-        grid0.addWidget(self.proj_color_dis_label, 31, 0)
-        grid0.addLayout(self.form_box_child_13, 31, 1)
-
-        # Project autohide CB
-        self.project_autohide_cb = FCCheckBox(label=_('Project AutoHide'))
-        self.project_autohide_cb.setToolTip(
-            _("Check this box if you want the project/selected/tool tab area to\n"
-              "hide automatically when there are no objects loaded and\n"
-              "to show whenever a new object is created.")
-        )
-
-        grid0.addWidget(self.project_autohide_cb, 32, 0, 1, 2)
-
-        # Just to add empty rows
-        grid0.addWidget(QtWidgets.QLabel(''), 33, 0, 1, 2)
-
-        self.layout.addStretch()
-
-        self.theme_button.clicked.connect(self.on_theme_change)
-
-        # #############################################################################
-        # ############################# GUI COLORS SIGNALS ############################
-        # #############################################################################
-
-        # Setting selection (left - right) colors signals
-        self.sf_color_entry.editingFinished.connect(self.on_sf_color_entry)
-        self.sf_color_button.clicked.connect(self.on_sf_color_button)
-        self.sf_color_alpha_spinner.valueChanged.connect(self.on_sf_color_spinner)
-        self.sf_color_alpha_slider.valueChanged.connect(self.on_sf_color_slider)
-        self.sl_color_entry.editingFinished.connect(self.on_sl_color_entry)
-        self.sl_color_button.clicked.connect(self.on_sl_color_button)
-
-        # Setting selection (right - left) colors signals
-        self.alt_sf_color_entry.editingFinished.connect(self.on_alt_sf_color_entry)
-        self.alt_sf_color_button.clicked.connect(self.on_alt_sf_color_button)
-        self.alt_sf_color_alpha_spinner.valueChanged.connect(self.on_alt_sf_color_spinner)
-        self.alt_sf_color_alpha_slider.valueChanged.connect(self.on_alt_sf_color_slider)
-        self.alt_sl_color_entry.editingFinished.connect(self.on_alt_sl_color_entry)
-        self.alt_sl_color_button.clicked.connect(self.on_alt_sl_color_button)
-
-        # Setting Editor Draw colors signals
-        self.draw_color_entry.editingFinished.connect(self.on_draw_color_entry)
-        self.draw_color_button.clicked.connect(self.on_draw_color_button)
-
-        self.sel_draw_color_entry.editingFinished.connect(self.on_sel_draw_color_entry)
-        self.sel_draw_color_button.clicked.connect(self.on_sel_draw_color_button)
-
-        self.proj_color_entry.editingFinished.connect(self.on_proj_color_entry)
-        self.proj_color_button.clicked.connect(self.on_proj_color_button)
-
-        self.proj_color_dis_entry.editingFinished.connect(self.on_proj_color_dis_entry)
-        self.proj_color_dis_button.clicked.connect(self.on_proj_color_dis_button)
-
-        self.layout_combo.activated.connect(self.on_layout)
-
-    def on_theme_change(self):
-        val = self.theme_radio.get_value()
-        qsettings = QSettings("Open Source", "FlatCAM")
-        qsettings.setValue('theme', val)
-
-        # This will write the setting to the platform specific storage.
-        del qsettings
-
-        self.app.on_app_restart()
-
-    @staticmethod
-    def handle_style(style):
-        # set current style
-        qsettings = QSettings("Open Source", "FlatCAM")
-        qsettings.setValue('style', style)
-
-        # This will write the setting to the platform specific storage.
-        del qsettings
-
-    @staticmethod
-    def handle_hdpi(state):
-        # set current HDPI
-        qsettings = QSettings("Open Source", "FlatCAM")
-        qsettings.setValue('hdpi', state)
-
-        # This will write the setting to the platform specific storage.
-        del qsettings
-
-    # Setting selection colors (left - right) handlers
-    def on_sf_color_entry(self):
-        self.app.defaults['global_sel_fill'] = self.app.defaults['global_sel_fill'][7:9]
-        self.sf_color_button.setStyleSheet("background-color:%s" % str(self.app.defaults['global_sel_fill'])[:7])
-
-    def on_sf_color_button(self):
-        current_color = QtGui.QColor(self.app.defaults['global_sel_fill'][:7])
-
-        c_dialog = QtWidgets.QColorDialog()
-        plot_fill_color = c_dialog.getColor(initial=current_color)
-
-        if plot_fill_color.isValid() is False:
-            return
-
-        self.sf_color_button.setStyleSheet("background-color:%s" % str(plot_fill_color.name()))
-
-        new_val = str(plot_fill_color.name()) + str(self.app.defaults['global_sel_fill'][7:9])
-        self.sf_color_entry.set_value(new_val)
-        self.app.defaults['global_sel_fill'] = new_val
-
-    def on_sf_color_spinner(self):
-        spinner_value = self.sf_color_alpha_spinner.value()
-        self.sf_color_alpha_slider.setValue(spinner_value)
-        self.app.defaults['global_sel_fill'] = self.app.defaults['global_sel_fill'][:7] + \
-            (hex(spinner_value)[2:] if int(hex(spinner_value)[2:], 16) > 0 else '00')
-        self.app.defaults['global_sel_line'] = self.app.defaults['global_sel_line'][:7] + \
-            (hex(spinner_value)[2:] if int(hex(spinner_value)[2:], 16) > 0 else '00')
-
-    def on_sf_color_slider(self):
-        slider_value = self.sf_color_alpha_slider.value()
-        self.sf_color_alpha_spinner.setValue(slider_value)
-
-    def on_sl_color_entry(self):
-        self.app.defaults['global_sel_line'] = self.sl_color_entry.get_value()[:7] + \
-            self.app.defaults['global_sel_line'][7:9]
-        self.sl_color_button.setStyleSheet("background-color:%s" % str(self.app.defaults['global_sel_line'])[:7])
-
-    def on_sl_color_button(self):
-        current_color = QtGui.QColor(self.app.defaults['global_sel_line'][:7])
-
-        c_dialog = QtWidgets.QColorDialog()
-        plot_line_color = c_dialog.getColor(initial=current_color)
-
-        if plot_line_color.isValid() is False:
-            return
-
-        self.sl_color_button.setStyleSheet("background-color:%s" % str(plot_line_color.name()))
-
-        new_val_line = str(plot_line_color.name()) + str(self.app.defaults['global_sel_line'][7:9])
-        self.sl_color_entry.set_value(new_val_line)
-        self.app.defaults['global_sel_line'] = new_val_line
-
-    # Setting selection colors (right - left) handlers
-    def on_alt_sf_color_entry(self):
-        self.app.defaults['global_alt_sel_fill'] = self.alt_sf_color_entry.get_value()[:7] + \
-                                                   self.app.defaults['global_alt_sel_fill'][7:9]
-        self.alt_sf_color_button.setStyleSheet(
-            "background-color:%s" % str(self.app.defaults['global_alt_sel_fill'])[:7]
-        )
-
-    def on_alt_sf_color_button(self):
-        current_color = QtGui.QColor(self.app.defaults['global_alt_sel_fill'][:7])
-
-        c_dialog = QtWidgets.QColorDialog()
-        plot_fill_color = c_dialog.getColor(initial=current_color)
-
-        if plot_fill_color.isValid() is False:
-            return
-
-        self.alt_sf_color_button.setStyleSheet("background-color:%s" % str(plot_fill_color.name()))
-
-        new_val = str(plot_fill_color.name()) + str(self.app.defaults['global_alt_sel_fill'][7:9])
-        self.alt_sf_color_entry.set_value(new_val)
-        self.app.defaults['global_alt_sel_fill'] = new_val
-
-    def on_alt_sf_color_spinner(self):
-        spinner_value = self.alt_sf_color_alpha_spinner.value()
-        self.alt_sf_color_alpha_slider.setValue(spinner_value)
-        self.app.defaults['global_alt_sel_fill'] = self.app.defaults['global_alt_sel_fill'][:7] + \
-            (hex(spinner_value)[2:] if int(hex(spinner_value)[2:], 16) > 0 else '00')
-        self.app.defaults['global_alt_sel_line'] = self.app.defaults['global_alt_sel_line'][:7] + \
-            (hex(spinner_value)[2:] if int(hex(spinner_value)[2:], 16) > 0 else '00')
-
-    def on_alt_sf_color_slider(self):
-        slider_value = self.alt_sf_color_alpha_slider.value()
-        self.alt_sf_color_alpha_spinner.setValue(slider_value)
-
-    def on_alt_sl_color_entry(self):
-        self.app.defaults['global_alt_sel_line'] = self.alt_sl_color_entry.get_value()[:7] + \
-                                                   self.app.defaults['global_alt_sel_line'][7:9]
-        self.alt_sl_color_button.setStyleSheet(
-            "background-color:%s" % str(self.app.defaults['global_alt_sel_line'])[:7]
-        )
-
-    def on_alt_sl_color_button(self):
-        current_color = QtGui.QColor(self.app.defaults['global_alt_sel_line'][:7])
-
-        c_dialog = QtWidgets.QColorDialog()
-        plot_line_color = c_dialog.getColor(initial=current_color)
-
-        if plot_line_color.isValid() is False:
-            return
-
-        self.alt_sl_color_button.setStyleSheet("background-color:%s" % str(plot_line_color.name()))
-
-        new_val_line = str(plot_line_color.name()) + str(self.app.defaults['global_alt_sel_line'][7:9])
-        self.alt_sl_color_entry.set_value(new_val_line)
-        self.app.defaults['global_alt_sel_line'] = new_val_line
-
-    # Setting Editor colors
-    def on_draw_color_entry(self):
-        self.app.defaults['global_draw_color'] = self.draw_color_entry.get_value()
-        self.draw_color_button.setStyleSheet("background-color:%s" % str(self.app.defaults['global_draw_color']))
-
-    def on_draw_color_button(self):
-        current_color = QtGui.QColor(self.app.defaults['global_draw_color'])
-
-        c_dialog = QtWidgets.QColorDialog()
-        draw_color = c_dialog.getColor(initial=current_color)
-
-        if draw_color.isValid() is False:
-            return
-
-        self.draw_color_button.setStyleSheet("background-color:%s" % str(draw_color.name()))
-
-        new_val = str(draw_color.name())
-        self.draw_color_entry.set_value(new_val)
-        self.app.defaults['global_draw_color'] = new_val
-
-    def on_sel_draw_color_entry(self):
-        self.app.defaults['global_sel_draw_color'] = self.sel_draw_color_entry.get_value()
-        self.sel_draw_color_button.setStyleSheet(
-            "background-color:%s" % str(self.app.defaults['global_sel_draw_color']))
-
-    def on_sel_draw_color_button(self):
-        current_color = QtGui.QColor(self.app.defaults['global_sel_draw_color'])
-
-        c_dialog = QtWidgets.QColorDialog()
-        sel_draw_color = c_dialog.getColor(initial=current_color)
-
-        if sel_draw_color.isValid() is False:
-            return
-
-        self.sel_draw_color_button.setStyleSheet("background-color:%s" % str(sel_draw_color.name()))
-
-        new_val_sel = str(sel_draw_color.name())
-        self.sel_draw_color_entry.set_value(new_val_sel)
-        self.app.defaults['global_sel_draw_color'] = new_val_sel
-
-    def on_proj_color_entry(self):
-        self.app.defaults['global_proj_item_color'] = self.proj_color_entry.get_value()
-        self.proj_color_button.setStyleSheet(
-            "background-color:%s" % str(self.app.defaults['global_proj_item_color']))
-
-    def on_proj_color_button(self):
-        current_color = QtGui.QColor(self.app.defaults['global_proj_item_color'])
-
-        c_dialog = QtWidgets.QColorDialog()
-        proj_color = c_dialog.getColor(initial=current_color)
-
-        if proj_color.isValid() is False:
-            return
-
-        self.proj_color_button.setStyleSheet("background-color:%s" % str(proj_color.name()))
-
-        new_val_sel = str(proj_color.name())
-        self.proj_color_entry.set_value(new_val_sel)
-        self.app.defaults['global_proj_item_color'] = new_val_sel
-
-    def on_proj_color_dis_entry(self):
-        self.app.defaults['global_proj_item_dis_color'] = self.proj_color_dis_entry.get_value()
-        self.proj_color_dis_button.setStyleSheet(
-            "background-color:%s" % str(self.app.defaults['global_proj_item_dis_color']))
-
-    def on_proj_color_dis_button(self):
-        current_color = QtGui.QColor(self.app.defaults['global_proj_item_dis_color'])
-
-        c_dialog = QtWidgets.QColorDialog()
-        proj_color = c_dialog.getColor(initial=current_color)
-
-        if proj_color.isValid() is False:
-            return
-
-        self.proj_color_dis_button.setStyleSheet("background-color:%s" % str(proj_color.name()))
-
-        new_val_sel = str(proj_color.name())
-        self.proj_color_dis_entry.set_value(new_val_sel)
-        self.app.defaults['global_proj_item_dis_color'] = new_val_sel
-
-    def on_layout(self, index=None, lay=None):
-        """
-        Set the toolbars layout (location)
-
-        :param index:
-        :param lay:     Type of layout to be set on the toolbard
-        :return:        None
-        """
-        self.app.defaults.report_usage("on_layout()")
-        if lay:
-            current_layout = lay
-        else:
-            current_layout = self.layout_combo.get_value()
-
-        lay_settings = QSettings("Open Source", "FlatCAM")
-        lay_settings.setValue('layout', current_layout)
-
-        # This will write the setting to the platform specific storage.
-        del lay_settings
-
-        # first remove the toolbars:
-        try:
-            self.app.ui.removeToolBar(self.app.ui.toolbarfile)
-            self.app.ui.removeToolBar(self.app.ui.toolbargeo)
-            self.app.ui.removeToolBar(self.app.ui.toolbarview)
-            self.app.ui.removeToolBar(self.app.ui.toolbarshell)
-            self.app.ui.removeToolBar(self.app.ui.toolbartools)
-            self.app.ui.removeToolBar(self.app.ui.exc_edit_toolbar)
-            self.app.ui.removeToolBar(self.app.ui.geo_edit_toolbar)
-            self.app.ui.removeToolBar(self.app.ui.grb_edit_toolbar)
-            self.app.ui.removeToolBar(self.app.ui.snap_toolbar)
-            self.app.ui.removeToolBar(self.app.ui.toolbarshell)
-        except Exception:
-            pass
-
-        if current_layout == 'compact':
-            # ## TOOLBAR INSTALLATION # ##
-            self.app.ui.toolbarfile = QtWidgets.QToolBar('File Toolbar')
-            self.app.ui.toolbarfile.setObjectName('File_TB')
-            self.app.ui.addToolBar(Qt.LeftToolBarArea, self.app.ui.toolbarfile)
-
-            self.app.ui.toolbargeo = QtWidgets.QToolBar('Edit Toolbar')
-            self.app.ui.toolbargeo.setObjectName('Edit_TB')
-            self.app.ui.addToolBar(Qt.LeftToolBarArea, self.app.ui.toolbargeo)
-
-            self.app.ui.toolbarshell = QtWidgets.QToolBar('Shell Toolbar')
-            self.app.ui.toolbarshell.setObjectName('Shell_TB')
-            self.app.ui.addToolBar(Qt.LeftToolBarArea, self.app.ui.toolbarshell)
-
-            self.app.ui.toolbartools = QtWidgets.QToolBar('Tools Toolbar')
-            self.app.ui.toolbartools.setObjectName('Tools_TB')
-            self.app.ui.addToolBar(Qt.LeftToolBarArea, self.app.ui.toolbartools)
-
-            self.app.ui.geo_edit_toolbar = QtWidgets.QToolBar('Geometry Editor Toolbar')
-            # self.app.ui.geo_edit_toolbar.setVisible(False)
-            self.app.ui.geo_edit_toolbar.setObjectName('GeoEditor_TB')
-            self.app.ui.addToolBar(Qt.RightToolBarArea, self.app.ui.geo_edit_toolbar)
-
-            self.app.ui.toolbarview = QtWidgets.QToolBar('View Toolbar')
-            self.app.ui.toolbarview.setObjectName('View_TB')
-            self.app.ui.addToolBar(Qt.RightToolBarArea, self.app.ui.toolbarview)
-
-            self.app.ui.addToolBarBreak(area=Qt.RightToolBarArea)
-
-            self.app.ui.grb_edit_toolbar = QtWidgets.QToolBar('Gerber Editor Toolbar')
-            # self.app.ui.grb_edit_toolbar.setVisible(False)
-            self.app.ui.grb_edit_toolbar.setObjectName('GrbEditor_TB')
-            self.app.ui.addToolBar(Qt.RightToolBarArea, self.app.ui.grb_edit_toolbar)
-
-            self.app.ui.exc_edit_toolbar = QtWidgets.QToolBar('Excellon Editor Toolbar')
-            self.app.ui.exc_edit_toolbar.setObjectName('ExcEditor_TB')
-            self.app.ui.addToolBar(Qt.RightToolBarArea, self.app.ui.exc_edit_toolbar)
-
-            self.app.ui.snap_toolbar = QtWidgets.QToolBar('Grid Toolbar')
-            self.app.ui.snap_toolbar.setObjectName('Snap_TB')
-            self.app.ui.snap_toolbar.setMaximumHeight(30)
-            self.app.ui.splitter_left.addWidget(self.app.ui.snap_toolbar)
-
-            self.app.ui.corner_snap_btn.setVisible(True)
-            self.app.ui.snap_magnet.setVisible(True)
-        else:
-            # ## TOOLBAR INSTALLATION # ##
-            self.app.ui.toolbarfile = QtWidgets.QToolBar('File Toolbar')
-            self.app.ui.toolbarfile.setObjectName('File_TB')
-            self.app.ui.addToolBar(self.app.ui.toolbarfile)
-
-            self.app.ui.toolbargeo = QtWidgets.QToolBar('Edit Toolbar')
-            self.app.ui.toolbargeo.setObjectName('Edit_TB')
-            self.app.ui.addToolBar(self.app.ui.toolbargeo)
-
-            self.app.ui.toolbarview = QtWidgets.QToolBar('View Toolbar')
-            self.app.ui.toolbarview.setObjectName('View_TB')
-            self.app.ui.addToolBar(self.app.ui.toolbarview)
-
-            self.app.ui.toolbarshell = QtWidgets.QToolBar('Shell Toolbar')
-            self.app.ui.toolbarshell.setObjectName('Shell_TB')
-            self.app.ui.addToolBar(self.app.ui.toolbarshell)
-
-            self.app.ui.toolbartools = QtWidgets.QToolBar('Tools Toolbar')
-            self.app.ui.toolbartools.setObjectName('Tools_TB')
-            self.app.ui.addToolBar(self.app.ui.toolbartools)
-
-            self.app.ui.exc_edit_toolbar = QtWidgets.QToolBar('Excellon Editor Toolbar')
-            # self.app.ui.exc_edit_toolbar.setVisible(False)
-            self.app.ui.exc_edit_toolbar.setObjectName('ExcEditor_TB')
-            self.app.ui.addToolBar(self.app.ui.exc_edit_toolbar)
-
-            self.app.ui.addToolBarBreak()
-
-            self.app.ui.geo_edit_toolbar = QtWidgets.QToolBar('Geometry Editor Toolbar')
-            # self.app.ui.geo_edit_toolbar.setVisible(False)
-            self.app.ui.geo_edit_toolbar.setObjectName('GeoEditor_TB')
-            self.app.ui.addToolBar(self.app.ui.geo_edit_toolbar)
-
-            self.app.ui.grb_edit_toolbar = QtWidgets.QToolBar('Gerber Editor Toolbar')
-            # self.app.ui.grb_edit_toolbar.setVisible(False)
-            self.app.ui.grb_edit_toolbar.setObjectName('GrbEditor_TB')
-            self.app.ui.addToolBar(self.app.ui.grb_edit_toolbar)
-
-            self.app.ui.snap_toolbar = QtWidgets.QToolBar('Grid Toolbar')
-            self.app.ui.snap_toolbar.setObjectName('Snap_TB')
-            # self.app.ui.snap_toolbar.setMaximumHeight(30)
-            self.app.ui.addToolBar(self.app.ui.snap_toolbar)
-
-            self.app.ui.corner_snap_btn.setVisible(False)
-            self.app.ui.snap_magnet.setVisible(False)
-
-        if current_layout == 'minimal':
-            self.app.ui.toolbarview.setVisible(False)
-            self.app.ui.toolbarshell.setVisible(False)
-            self.app.ui.snap_toolbar.setVisible(False)
-            self.app.ui.geo_edit_toolbar.setVisible(False)
-            self.app.ui.grb_edit_toolbar.setVisible(False)
-            self.app.ui.exc_edit_toolbar.setVisible(False)
-            self.app.ui.lock_toolbar(lock=True)
-
-        # add all the actions to the toolbars
-        self.app.ui.populate_toolbars()
-
-        # reconnect all the signals to the toolbar actions
-        self.app.connect_toolbar_signals()
-
-        self.app.ui.grid_snap_btn.setChecked(True)
-        self.app.on_grid_snap_triggered(state=True)
-
-        self.app.ui.grid_gap_x_entry.setText(str(self.app.defaults["global_gridx"]))
-        self.app.ui.grid_gap_y_entry.setText(str(self.app.defaults["global_gridy"]))
-        self.app.ui.snap_max_dist_entry.setText(str(self.app.defaults["global_snap_max"]))
-        self.app.ui.grid_gap_link_cb.setChecked(True)
-
-
-class GeneralAPPSetGroupUI(OptionsGroupUI):
-    def __init__(self, decimals=4, parent=None):
-        super(GeneralAPPSetGroupUI, self).__init__(self)
-
-        self.setTitle(str(_("App Settings")))
-        self.decimals = decimals
-
-        theme_settings = QtCore.QSettings("Open Source", "FlatCAM")
-        if theme_settings.contains("theme"):
-            theme = theme_settings.value('theme', type=str)
-        else:
-            theme = 'white'
-
-        if theme == 'white':
-            self.resource_loc = 'assets/resources'
-        else:
-            self.resource_loc = 'assets/resources'
-
-        # Create a grid layout for the Application general settings
-        grid0 = QtWidgets.QGridLayout()
-        self.layout.addLayout(grid0)
-        grid0.setColumnStretch(0, 0)
-        grid0.setColumnStretch(1, 1)
-
-        # GRID Settings
-        self.grid_label = QtWidgets.QLabel('<b>%s</b>' % _('Grid Settings'))
-        grid0.addWidget(self.grid_label, 0, 0, 1, 2)
-
-        # Grid X Entry
-        self.gridx_label = QtWidgets.QLabel('%s:' % _('X value'))
-        self.gridx_label.setToolTip(
-           _("This is the Grid snap value on X axis.")
-        )
-        self.gridx_entry = FCDoubleSpinner()
-        self.gridx_entry.set_precision(self.decimals)
-        self.gridx_entry.setSingleStep(0.1)
-
-        grid0.addWidget(self.gridx_label, 1, 0)
-        grid0.addWidget(self.gridx_entry, 1, 1)
-
-        # Grid Y Entry
-        self.gridy_label = QtWidgets.QLabel('%s:' % _('Y value'))
-        self.gridy_label.setToolTip(
-            _("This is the Grid snap value on Y axis.")
-        )
-        self.gridy_entry = FCDoubleSpinner()
-        self.gridy_entry.set_precision(self.decimals)
-        self.gridy_entry.setSingleStep(0.1)
-
-        grid0.addWidget(self.gridy_label, 2, 0)
-        grid0.addWidget(self.gridy_entry, 2, 1)
-
-        # Snap Max Entry
-        self.snap_max_label = QtWidgets.QLabel('%s:' % _('Snap Max'))
-        self.snap_max_label.setToolTip(_("Max. magnet distance"))
-        self.snap_max_dist_entry = FCDoubleSpinner()
-        self.snap_max_dist_entry.set_precision(self.decimals)
-        self.snap_max_dist_entry.setSingleStep(0.1)
-
-        grid0.addWidget(self.snap_max_label, 3, 0)
-        grid0.addWidget(self.snap_max_dist_entry, 3, 1)
-
-        separator_line = QtWidgets.QFrame()
-        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
-        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
-        grid0.addWidget(separator_line, 4, 0, 1, 2)
-
-        # Workspace
-        self.workspace_label = QtWidgets.QLabel('<b>%s</b>' % _('Workspace Settings'))
-        grid0.addWidget(self.workspace_label, 5, 0, 1, 2)
-
-        self.workspace_cb = FCCheckBox('%s' % _('Active'))
-        self.workspace_cb.setToolTip(
-           _("Draw a delimiting rectangle on canvas.\n"
-             "The purpose is to illustrate the limits for our work.")
-        )
-
-        grid0.addWidget(self.workspace_cb, 6, 0, 1, 2)
-
-        self.workspace_type_lbl = QtWidgets.QLabel('%s:' % _('Size'))
-        self.workspace_type_lbl.setToolTip(
-           _("Select the type of rectangle to be used on canvas,\n"
-             "as valid workspace.")
-        )
-        self.wk_cb = FCComboBox()
-
-        grid0.addWidget(self.workspace_type_lbl, 7, 0)
-        grid0.addWidget(self.wk_cb, 7, 1)
-
-        self.pagesize = {}
-        self.pagesize.update(
-            {
-                'A0': (841, 1189),
-                'A1': (594, 841),
-                'A2': (420, 594),
-                'A3': (297, 420),
-                'A4': (210, 297),
-                'A5': (148, 210),
-                'A6': (105, 148),
-                'A7': (74, 105),
-                'A8': (52, 74),
-                'A9': (37, 52),
-                'A10': (26, 37),
-
-                'B0': (1000, 1414),
-                'B1': (707, 1000),
-                'B2': (500, 707),
-                'B3': (353, 500),
-                'B4': (250, 353),
-                'B5': (176, 250),
-                'B6': (125, 176),
-                'B7': (88, 125),
-                'B8': (62, 88),
-                'B9': (44, 62),
-                'B10': (31, 44),
-
-                'C0': (917, 1297),
-                'C1': (648, 917),
-                'C2': (458, 648),
-                'C3': (324, 458),
-                'C4': (229, 324),
-                'C5': (162, 229),
-                'C6': (114, 162),
-                'C7': (81, 114),
-                'C8': (57, 81),
-                'C9': (40, 57),
-                'C10': (28, 40),
-
-                # American paper sizes
-                'LETTER': (8.5, 11),
-                'LEGAL': (8.5, 14),
-                'ELEVENSEVENTEEN': (11, 17),
-
-                # From https://en.wikipedia.org/wiki/Paper_size
-                'JUNIOR_LEGAL': (5, 8),
-                'HALF_LETTER': (5.5, 8),
-                'GOV_LETTER': (8, 10.5),
-                'GOV_LEGAL': (8.5, 13),
-                'LEDGER': (17, 11),
-            }
-        )
-
-        page_size_list = list(self.pagesize.keys())
-
-        self.wk_cb.addItems(page_size_list)
-
-        # Page orientation
-        self.wk_orientation_label = QtWidgets.QLabel('%s:' % _("Orientation"))
-        self.wk_orientation_label.setToolTip(_("Can be:\n"
-                                               "- Portrait\n"
-                                               "- Landscape"))
-
-        self.wk_orientation_radio = RadioSet([{'label': _('Portrait'), 'value': 'p'},
-                                              {'label': _('Landscape'), 'value': 'l'},
-                                              ], stretch=False)
-
-        self.wks = OptionalInputSection(self.workspace_cb,
-                                        [
-                                            self.workspace_type_lbl,
-                                            self.wk_cb,
-                                            self.wk_orientation_label,
-                                            self.wk_orientation_radio
-                                        ])
-
-        grid0.addWidget(self.wk_orientation_label, 8, 0)
-        grid0.addWidget(self.wk_orientation_radio, 8, 1)
-
-        separator_line = QtWidgets.QFrame()
-        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
-        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
-        grid0.addWidget(separator_line, 9, 0, 1, 2)
-
-        # Font Size
-        self.font_size_label = QtWidgets.QLabel('<b>%s</b>' % _('Font Size'))
-        grid0.addWidget(self.font_size_label, 10, 0, 1, 2)
-
-        # Notebook Font Size
-        self.notebook_font_size_label = QtWidgets.QLabel('%s:' % _('Notebook'))
-        self.notebook_font_size_label.setToolTip(
-            _("This sets the font size for the elements found in the Notebook.\n"
-              "The notebook is the collapsible area in the left side of the GUI,\n"
-              "and include the Project, Selected and Tool tabs.")
-        )
-
-        self.notebook_font_size_spinner = FCSpinner()
-        self.notebook_font_size_spinner.set_range(8, 40)
-        self.notebook_font_size_spinner.setWrapping(True)
-
-        qsettings = QSettings("Open Source", "FlatCAM")
-        if qsettings.contains("notebook_font_size"):
-            self.notebook_font_size_spinner.set_value(qsettings.value('notebook_font_size', type=int))
-        else:
-            self.notebook_font_size_spinner.set_value(12)
-
-        grid0.addWidget(self.notebook_font_size_label, 11, 0)
-        grid0.addWidget(self.notebook_font_size_spinner, 11, 1)
-
-        # Axis Font Size
-        self.axis_font_size_label = QtWidgets.QLabel('%s:' % _('Axis'))
-        self.axis_font_size_label.setToolTip(
-            _("This sets the font size for canvas axis.")
-        )
-
-        self.axis_font_size_spinner = FCSpinner()
-        self.axis_font_size_spinner.set_range(0, 40)
-        self.axis_font_size_spinner.setWrapping(True)
-
-        qsettings = QSettings("Open Source", "FlatCAM")
-        if qsettings.contains("axis_font_size"):
-            self.axis_font_size_spinner.set_value(qsettings.value('axis_font_size', type=int))
-        else:
-            self.axis_font_size_spinner.set_value(8)
-
-        grid0.addWidget(self.axis_font_size_label, 12, 0)
-        grid0.addWidget(self.axis_font_size_spinner, 12, 1)
-
-        # TextBox Font Size
-        self.textbox_font_size_label = QtWidgets.QLabel('%s:' % _('Textbox'))
-        self.textbox_font_size_label.setToolTip(
-            _("This sets the font size for the Textbox GUI\n"
-              "elements that are used in FlatCAM.")
-        )
-
-        self.textbox_font_size_spinner = FCSpinner()
-        self.textbox_font_size_spinner.set_range(8, 40)
-        self.textbox_font_size_spinner.setWrapping(True)
-
-        qsettings = QSettings("Open Source", "FlatCAM")
-        if qsettings.contains("textbox_font_size"):
-            self.textbox_font_size_spinner.set_value(settings.value('textbox_font_size', type=int))
-        else:
-            self.textbox_font_size_spinner.set_value(10)
-
-        grid0.addWidget(self.textbox_font_size_label, 13, 0)
-        grid0.addWidget(self.textbox_font_size_spinner, 13, 1)
-
-        separator_line = QtWidgets.QFrame()
-        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
-        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
-        grid0.addWidget(separator_line, 14, 0, 1, 2)
-
-        # -----------------------------------------------------------
-        # -------------- MOUSE SETTINGS -----------------------------
-        # -----------------------------------------------------------
-
-        self.mouse_lbl = QtWidgets.QLabel('<b>%s</b>' % _('Mouse Settings'))
-        grid0.addWidget(self.mouse_lbl, 21, 0, 1, 2)
-
-        # Mouse Cursor Shape
-        self.cursor_lbl = QtWidgets.QLabel('%s:' % _('Cursor Shape'))
-        self.cursor_lbl.setToolTip(
-           _("Choose a mouse cursor shape.\n"
-             "- Small -> with a customizable size.\n"
-             "- Big -> Infinite lines")
-        )
-
-        self.cursor_radio = RadioSet([
-            {"label": _("Small"), "value": "small"},
-            {"label": _("Big"), "value": "big"}
-        ], orientation='horizontal', stretch=False)
-
-        grid0.addWidget(self.cursor_lbl, 22, 0)
-        grid0.addWidget(self.cursor_radio, 22, 1)
-
-        # Mouse Cursor Size
-        self.cursor_size_lbl = QtWidgets.QLabel('%s:' % _('Cursor Size'))
-        self.cursor_size_lbl.setToolTip(
-           _("Set the size of the mouse cursor, in pixels.")
-        )
-
-        self.cursor_size_entry = FCSpinner()
-        self.cursor_size_entry.set_range(10, 70)
-        self.cursor_size_entry.setWrapping(True)
-
-        grid0.addWidget(self.cursor_size_lbl, 23, 0)
-        grid0.addWidget(self.cursor_size_entry, 23, 1)
-
-        # Cursor Width
-        self.cursor_width_lbl = QtWidgets.QLabel('%s:' % _('Cursor Width'))
-        self.cursor_width_lbl.setToolTip(
-           _("Set the line width of the mouse cursor, in pixels.")
-        )
-
-        self.cursor_width_entry = FCSpinner()
-        self.cursor_width_entry.set_range(1, 10)
-        self.cursor_width_entry.setWrapping(True)
-
-        grid0.addWidget(self.cursor_width_lbl, 24, 0)
-        grid0.addWidget(self.cursor_width_entry, 24, 1)
-
-        # Cursor Color Enable
-        self.mouse_cursor_color_cb = FCCheckBox(label='%s' % _('Cursor Color'))
-        self.mouse_cursor_color_cb.setToolTip(
-            _("Check this box to color mouse cursor.")
-        )
-        grid0.addWidget(self.mouse_cursor_color_cb, 25, 0, 1, 2)
-
-        # Cursor Color
-        self.mouse_color_label = QtWidgets.QLabel('%s:' % _('Cursor Color'))
-        self.mouse_color_label.setToolTip(
-            _("Set the color of the mouse cursor.")
-        )
-        self.mouse_cursor_entry = FCEntry()
-        self.mouse_cursor_button = QtWidgets.QPushButton()
-        self.mouse_cursor_button.setFixedSize(15, 15)
-
-        self.form_box_child_1 = QtWidgets.QHBoxLayout()
-        self.form_box_child_1.addWidget(self.mouse_cursor_entry)
-        self.form_box_child_1.addWidget(self.mouse_cursor_button)
-        self.form_box_child_1.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
-
-        grid0.addWidget(self.mouse_color_label, 26, 0)
-        grid0.addLayout(self.form_box_child_1, 26, 1)
-
-        self.mois = OptionalInputSection(
-            self.mouse_cursor_color_cb,
-            [
-                self.mouse_color_label,
-                self.mouse_cursor_entry,
-                self.mouse_cursor_button
-            ]
-        )
-        # Select mouse pan button
-        self.panbuttonlabel = QtWidgets.QLabel('%s:' % _('Pan Button'))
-        self.panbuttonlabel.setToolTip(
-            _("Select the mouse button to use for panning:\n"
-              "- MMB --> Middle Mouse Button\n"
-              "- RMB --> Right Mouse Button")
-        )
-        self.pan_button_radio = RadioSet([{'label': _('MMB'), 'value': '3'},
-                                          {'label': _('RMB'), 'value': '2'}])
-
-        grid0.addWidget(self.panbuttonlabel, 27, 0)
-        grid0.addWidget(self.pan_button_radio, 27, 1)
-
-        # Multiple Selection Modifier Key
-        self.mselectlabel = QtWidgets.QLabel('%s:' % _('Multiple Selection'))
-        self.mselectlabel.setToolTip(
-            _("Select the key used for multiple selection.")
-        )
-        self.mselect_radio = RadioSet([{'label': _('CTRL'), 'value': 'Control'},
-                                       {'label': _('SHIFT'), 'value': 'Shift'}])
-
-        grid0.addWidget(self.mselectlabel, 28, 0)
-        grid0.addWidget(self.mselect_radio, 28, 1)
-
-        separator_line = QtWidgets.QFrame()
-        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
-        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
-        grid0.addWidget(separator_line, 29, 0, 1, 2)
-
-        # Delete confirmation
-        self.delete_conf_cb = FCCheckBox(_('Delete object confirmation'))
-        self.delete_conf_cb.setToolTip(
-            _("When checked the application will ask for user confirmation\n"
-              "whenever the Delete object(s) event is triggered, either by\n"
-              "menu shortcut or key shortcut.")
-        )
-        grid0.addWidget(self.delete_conf_cb, 30, 0, 1, 2)
-
-        # Open behavior
-        self.open_style_cb = FCCheckBox('%s' % _('"Open" behavior'))
-        self.open_style_cb.setToolTip(
-            _("When checked the path for the last saved file is used when saving files,\n"
-              "and the path for the last opened file is used when opening files.\n\n"
-              "When unchecked the path for opening files is the one used last: either the\n"
-              "path for saving files or the path for opening files.")
-        )
-
-        grid0.addWidget(self.open_style_cb, 31, 0, 1, 2)
-
-        # Enable/Disable ToolTips globally
-        self.toggle_tooltips_cb = FCCheckBox(label=_('Enable ToolTips'))
-        self.toggle_tooltips_cb.setToolTip(
-            _("Check this box if you want to have toolTips displayed\n"
-              "when hovering with mouse over items throughout the App.")
-        )
-
-        grid0.addWidget(self.toggle_tooltips_cb, 32, 0, 1, 2)
-
-        # 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, 33, 0, 1, 2)
-
-        # Bookmarks Limit in the Help Menu
-        self.bm_limit_spinner = FCSpinner()
-        self.bm_limit_spinner.set_range(0, 9999)
-        self.bm_limit_label = QtWidgets.QLabel('%s:' % _('Bookmarks limit'))
-        self.bm_limit_label.setToolTip(
-            _("The maximum number of bookmarks that may be installed in the menu.\n"
-              "The number of bookmarks in the bookmark manager may be greater\n"
-              "but the menu will hold only so much.")
-        )
-
-        grid0.addWidget(self.bm_limit_label, 34, 0)
-        grid0.addWidget(self.bm_limit_spinner, 34, 1)
-
-        # Activity monitor icon
-        self.activity_label = QtWidgets.QLabel('%s:' % _("Activity Icon"))
-        self.activity_label.setToolTip(
-            _("Select the GIF that show activity when FlatCAM is active.")
-        )
-        self.activity_combo = FCComboBox()
-        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)
-
-        self.layout.addStretch()
-
-        self.mouse_cursor_color_cb.stateChanged.connect(self.on_mouse_cursor_color_enable)
-
-        self.mouse_cursor_entry.editingFinished.connect(self.on_mouse_cursor_entry)
-        self.mouse_cursor_button.clicked.connect(self.on_mouse_cursor_button)
-
-    def on_mouse_cursor_color_enable(self, val):
-        if val:
-            self.app.cursor_color_3D = self.app.defaults["global_cursor_color"]
-        else:
-            theme_settings = QtCore.QSettings("Open Source", "FlatCAM")
-            if theme_settings.contains("theme"):
-                theme = theme_settings.value('theme', type=str)
-            else:
-                theme = 'white'
-
-            if theme == 'white':
-                self.app.cursor_color_3D = 'black'
-            else:
-                self.app.cursor_color_3D = 'gray'
-
-    def on_mouse_cursor_entry(self):
-        self.app.defaults['global_cursor_color'] = self.mouse_cursor_entry.get_value()
-        self.mouse_cursor_button.setStyleSheet("background-color:%s" % str(self.app.defaults['global_cursor_color']))
-
-        self.app.cursor_color_3D = self.app.defaults["global_cursor_color"]
-
-    def on_mouse_cursor_button(self):
-        current_color = QtGui.QColor(self.app.defaults['global_cursor_color'])
-
-        c_dialog = QtWidgets.QColorDialog()
-        proj_color = c_dialog.getColor(initial=current_color)
-
-        if proj_color.isValid() is False:
-            return
-
-        self.mouse_cursor_button.setStyleSheet("background-color:%s" % str(proj_color.name()))
-
-        new_val_sel = str(proj_color.name())
-        self.mouse_cursor_entry.set_value(new_val_sel)
-        self.app.defaults['global_cursor_color'] = new_val_sel
-
-        self.app.cursor_color_3D = self.app.defaults["global_cursor_color"]
-
-
-class GeneralAppPrefGroupUI(OptionsGroupUI):
-    def __init__(self, decimals=4, parent=None):
-        super(GeneralAppPrefGroupUI, self).__init__(self)
-
-        self.setTitle(_("App Preferences"))
-        self.decimals = decimals
-
-        # Create a form layout for the Application general settings
-        grid0 = QtWidgets.QGridLayout()
-        self.layout.addLayout(grid0)
-        grid0.setColumnStretch(0, 0)
-        grid0.setColumnStretch(1, 1)
-
-        # Units for FlatCAM
-        self.unitslabel = QtWidgets.QLabel('<span style="color:red;"><b>%s:</b></span>' % _('Units'))
-        self.unitslabel.setToolTip(_("The default value for FlatCAM units.\n"
-                                     "Whatever is selected here is set every time\n"
-                                     "FLatCAM is started."))
-        self.units_radio = RadioSet([{'label': _('MM'), 'value': 'MM'},
-                                     {'label': _('IN'), 'value': 'IN'}])
-
-        grid0.addWidget(self.unitslabel, 0, 0)
-        grid0.addWidget(self.units_radio, 0, 1)
-
-        # Precision Metric
-        self.precision_metric_label = QtWidgets.QLabel('%s:' % _('Precision MM'))
-        self.precision_metric_label.setToolTip(
-            _("The number of decimals used throughout the application\n"
-              "when the set units are in METRIC system.\n"
-              "Any change here require an application restart.")
-        )
-        self.precision_metric_entry = FCSpinner()
-        self.precision_metric_entry.set_range(2, 16)
-        self.precision_metric_entry.setWrapping(True)
-
-        grid0.addWidget(self.precision_metric_label, 1, 0)
-        grid0.addWidget(self.precision_metric_entry, 1, 1)
-
-        # Precision Inch
-        self.precision_inch_label = QtWidgets.QLabel('%s:' % _('Precision INCH'))
-        self.precision_inch_label.setToolTip(
-            _("The number of decimals used throughout the application\n"
-              "when the set units are in INCH system.\n"
-              "Any change here require an application restart.")
-        )
-        self.precision_inch_entry = FCSpinner()
-        self.precision_inch_entry.set_range(2, 16)
-        self.precision_inch_entry.setWrapping(True)
-
-        grid0.addWidget(self.precision_inch_label, 2, 0)
-        grid0.addWidget(self.precision_inch_entry, 2, 1)
-
-        # Graphic Engine for FlatCAM
-        self.ge_label = QtWidgets.QLabel('<b>%s:</b>' % _('Graphic Engine'))
-        self.ge_label.setToolTip(_("Choose what graphic engine to use in FlatCAM.\n"
-                                   "Legacy(2D) -> reduced functionality, slow performance but enhanced compatibility.\n"
-                                   "OpenGL(3D) -> full functionality, high performance\n"
-                                   "Some graphic cards are too old and do not work in OpenGL(3D) mode, like:\n"
-                                   "Intel HD3000 or older. In this case the plot area will be black therefore\n"
-                                   "use the Legacy(2D) mode."))
-        self.ge_radio = RadioSet([{'label': _('Legacy(2D)'), 'value': '2D'},
-                                  {'label': _('OpenGL(3D)'), 'value': '3D'}],
-                                 orientation='vertical')
-
-        grid0.addWidget(self.ge_label, 3, 0)
-        grid0.addWidget(self.ge_radio, 3, 1)
-
-        separator_line = QtWidgets.QFrame()
-        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
-        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
-        grid0.addWidget(separator_line, 4, 0, 1, 2)
-
-        # Application Level for FlatCAM
-        self.app_level_label = QtWidgets.QLabel('<span style="color:red;"><b>%s:</b></span>' % _('APP. LEVEL'))
-        self.app_level_label.setToolTip(_("Choose the default level of usage for FlatCAM.\n"
-                                          "BASIC level -> reduced functionality, best for beginner's.\n"
-                                          "ADVANCED level -> full functionality.\n\n"
-                                          "The choice here will influence the parameters in\n"
-                                          "the Selected Tab for all kinds of FlatCAM objects."))
-        self.app_level_radio = RadioSet([{'label': _('Basic'), 'value': 'b'},
-                                         {'label': _('Advanced'), 'value': 'a'}])
-
-        grid0.addWidget(self.app_level_label, 5, 0)
-        grid0.addWidget(self.app_level_radio, 5, 1)
-
-        # Portability for FlatCAM
-        self.portability_cb = FCCheckBox('%s' % _('Portable app'))
-        self.portability_cb.setToolTip(_("Choose if the application should run as portable.\n\n"
-                                         "If Checked the application will run portable,\n"
-                                         "which means that the preferences files will be saved\n"
-                                         "in the application folder, in the lib\\config subfolder."))
-
-        grid0.addWidget(self.portability_cb, 6, 0, 1, 2)
-
-        separator_line = QtWidgets.QFrame()
-        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
-        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
-        grid0.addWidget(separator_line, 7, 0, 1, 2)
-
-        # Languages for FlatCAM
-        self.languagelabel = QtWidgets.QLabel('<b>%s</b>' % _('Languages'))
-        self.languagelabel.setToolTip(_("Set the language used throughout FlatCAM."))
-        self.language_cb = FCComboBox()
-
-        grid0.addWidget(self.languagelabel, 8, 0, 1, 2)
-        grid0.addWidget(self.language_cb, 9, 0, 1, 2)
-
-        self.language_apply_btn = FCButton(_("Apply Language"))
-        self.language_apply_btn.setToolTip(_("Set the language used throughout FlatCAM.\n"
-                                             "The app will restart after click."))
-
-        grid0.addWidget(self.language_apply_btn, 15, 0, 1, 2)
-
-        separator_line = QtWidgets.QFrame()
-        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
-        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
-        grid0.addWidget(separator_line, 16, 0, 1, 2)
-
-        # -----------------------------------------------------------
-        # ----------- APPLICATION STARTUP SETTINGS ------------------
-        # -----------------------------------------------------------
-
-        self.startup_label = QtWidgets.QLabel('<b>%s</b>' % _('Startup Settings'))
-        grid0.addWidget(self.startup_label, 17, 0, 1, 2)
-
-        # Splash Screen
-        self.splash_cb = FCCheckBox('%s' % _('Splash Screen'))
-        self.splash_cb.setToolTip(
-            _("Enable display of the splash screen at application startup.")
-        )
-
-        qsettings = QSettings("Open Source", "FlatCAM")
-        if qsettings.value("splash_screen"):
-            self.splash_cb.set_value(True)
-        else:
-            self.splash_cb.set_value(False)
-
-        grid0.addWidget(self.splash_cb, 18, 0, 1, 2)
-
-        # Sys Tray Icon
-        self.systray_cb = FCCheckBox('%s' % _('Sys Tray Icon'))
-        self.systray_cb.setToolTip(
-            _("Enable display of FlatCAM icon in Sys Tray.")
-        )
-        grid0.addWidget(self.systray_cb, 19, 0, 1, 2)
-
-        # Shell StartUp CB
-        self.shell_startup_cb = FCCheckBox(label='%s' % _('Show Shell'))
-        self.shell_startup_cb.setToolTip(
-            _("Check this box if you want the shell to\n"
-              "start automatically at startup.")
-        )
-
-        grid0.addWidget(self.shell_startup_cb, 20, 0, 1, 2)
-
-        # Project at StartUp CB
-        self.project_startup_cb = FCCheckBox(label='%s' % _('Show Project'))
-        self.project_startup_cb.setToolTip(
-            _("Check this box if you want the project/selected/tool tab area to\n"
-              "to be shown automatically at startup.")
-        )
-        grid0.addWidget(self.project_startup_cb, 21, 0, 1, 2)
-
-        # Version Check CB
-        self.version_check_cb = FCCheckBox(label='%s' % _('Version Check'))
-        self.version_check_cb.setToolTip(
-            _("Check this box if you want to check\n"
-              "for a new version automatically at startup.")
-        )
-
-        grid0.addWidget(self.version_check_cb, 22, 0, 1, 2)
-
-        # Send Stats CB
-        self.send_stats_cb = FCCheckBox(label='%s' % _('Send Statistics'))
-        self.send_stats_cb.setToolTip(
-            _("Check this box if you agree to send anonymous\n"
-              "stats automatically at startup, to help improve FlatCAM.")
-        )
-
-        grid0.addWidget(self.send_stats_cb, 23, 0, 1, 2)
-
-        self.ois_version_check = OptionalInputSection(self.version_check_cb, [self.send_stats_cb])
-
-        separator_line = QtWidgets.QFrame()
-        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
-        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
-        grid0.addWidget(separator_line, 24, 0, 1, 2)
-
-        # Worker Numbers
-        self.worker_number_label = QtWidgets.QLabel('%s:' % _('Workers number'))
-        self.worker_number_label.setToolTip(
-            _("The number of Qthreads made available to the App.\n"
-              "A bigger number may finish the jobs more quickly but\n"
-              "depending on your computer speed, may make the App\n"
-              "unresponsive. Can have a value between 2 and 16.\n"
-              "Default value is 2.\n"
-              "After change, it will be applied at next App start.")
-        )
-        self.worker_number_sb = FCSpinner()
-        self.worker_number_sb.set_range(2, 16)
-
-        grid0.addWidget(self.worker_number_label, 25, 0)
-        grid0.addWidget(self.worker_number_sb, 25, 1)
-
-        # Geometric tolerance
-        tol_label = QtWidgets.QLabel('%s:' % _("Geo Tolerance"))
-        tol_label.setToolTip(_(
-            "This value can counter the effect of the Circle Steps\n"
-            "parameter. Default value is 0.005.\n"
-            "A lower value will increase the detail both in image\n"
-            "and in Gcode for the circles, with a higher cost in\n"
-            "performance. Higher value will provide more\n"
-            "performance at the expense of level of detail."
-        ))
-        self.tol_entry = FCDoubleSpinner()
-        self.tol_entry.setSingleStep(0.001)
-        self.tol_entry.set_precision(6)
-
-        grid0.addWidget(tol_label, 26, 0)
-        grid0.addWidget(self.tol_entry, 26, 1)
-
-        separator_line = QtWidgets.QFrame()
-        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
-        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
-        grid0.addWidget(separator_line, 27, 0, 1, 2)
-
-        # Save Settings
-        self.save_label = QtWidgets.QLabel('<b>%s</b>' % _("Save Settings"))
-        grid0.addWidget(self.save_label, 28, 0, 1, 2)
-
-        # Save compressed project CB
-        self.save_type_cb = FCCheckBox(_('Save Compressed Project'))
-        self.save_type_cb.setToolTip(
-            _("Whether to save a compressed or uncompressed project.\n"
-              "When checked it will save a compressed FlatCAM project.")
-        )
-
-        grid0.addWidget(self.save_type_cb, 29, 0, 1, 2)
-
-        # Project LZMA Comppression Level
-        self.compress_spinner = FCSpinner()
-        self.compress_spinner.set_range(0, 9)
-        self.compress_label = QtWidgets.QLabel('%s:' % _('Compression'))
-        self.compress_label.setToolTip(
-            _("The level of compression used when saving\n"
-              "a FlatCAM project. Higher value means better compression\n"
-              "but require more RAM usage and more processing time.")
-        )
-
-        grid0.addWidget(self.compress_label, 30, 0)
-        grid0.addWidget(self.compress_spinner, 30, 1)
-
-        self.proj_ois = OptionalInputSection(self.save_type_cb, [self.compress_label, self.compress_spinner], True)
-
-        # Auto save CB
-        self.autosave_cb = FCCheckBox(_('Enable Auto Save'))
-        self.autosave_cb.setToolTip(
-            _("Check to enable the autosave feature.\n"
-              "When enabled, the application will try to save a project\n"
-              "at the set interval.")
-        )
-
-        grid0.addWidget(self.autosave_cb, 31, 0, 1, 2)
-
-        # Auto Save Timeout Interval
-        self.autosave_entry = FCSpinner()
-        self.autosave_entry.set_range(0, 9999999)
-        self.autosave_label = QtWidgets.QLabel('%s:' % _('Interval'))
-        self.autosave_label.setToolTip(
-            _("Time interval for autosaving. In milliseconds.\n"
-              "The application will try to save periodically but only\n"
-              "if the project was saved manually at least once.\n"
-              "While active, some operations may block this feature.")
-        )
-
-        grid0.addWidget(self.autosave_label, 32, 0)
-        grid0.addWidget(self.autosave_entry, 32, 1)
-
-        # self.as_ois = OptionalInputSection(self.autosave_cb, [self.autosave_label, self.autosave_entry], True)
-
-        separator_line = QtWidgets.QFrame()
-        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
-        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
-        grid0.addWidget(separator_line, 33, 0, 1, 2)
-
-        self.pdf_param_label = QtWidgets.QLabel('<B>%s:</b>' % _("Text to PDF parameters"))
-        self.pdf_param_label.setToolTip(
-            _("Used when saving text in Code Editor or in FlatCAM Document objects.")
-        )
-        grid0.addWidget(self.pdf_param_label, 34, 0, 1, 2)
-
-        # Top Margin value
-        self.tmargin_entry = FCDoubleSpinner()
-        self.tmargin_entry.set_precision(self.decimals)
-        self.tmargin_entry.set_range(0.0000, 9999.9999)
-
-        self.tmargin_label = QtWidgets.QLabel('%s:' % _("Top Margin"))
-        self.tmargin_label.setToolTip(
-            _("Distance between text body and the top of the PDF file.")
-        )
-
-        grid0.addWidget(self.tmargin_label, 35, 0)
-        grid0.addWidget(self.tmargin_entry, 35, 1)
-
-        # Bottom Margin value
-        self.bmargin_entry = FCDoubleSpinner()
-        self.bmargin_entry.set_precision(self.decimals)
-        self.bmargin_entry.set_range(0.0000, 9999.9999)
-
-        self.bmargin_label = QtWidgets.QLabel('%s:' % _("Bottom Margin"))
-        self.bmargin_label.setToolTip(
-            _("Distance between text body and the bottom of the PDF file.")
-        )
-
-        grid0.addWidget(self.bmargin_label, 36, 0)
-        grid0.addWidget(self.bmargin_entry, 36, 1)
-
-        # Left Margin value
-        self.lmargin_entry = FCDoubleSpinner()
-        self.lmargin_entry.set_precision(self.decimals)
-        self.lmargin_entry.set_range(0.0000, 9999.9999)
-
-        self.lmargin_label = QtWidgets.QLabel('%s:' % _("Left Margin"))
-        self.lmargin_label.setToolTip(
-            _("Distance between text body and the left of the PDF file.")
-        )
-
-        grid0.addWidget(self.lmargin_label, 37, 0)
-        grid0.addWidget(self.lmargin_entry, 37, 1)
-
-        # Right Margin value
-        self.rmargin_entry = FCDoubleSpinner()
-        self.rmargin_entry.set_precision(self.decimals)
-        self.rmargin_entry.set_range(0.0000, 9999.9999)
-
-        self.rmargin_label = QtWidgets.QLabel('%s:' % _("Right Margin"))
-        self.rmargin_label.setToolTip(
-            _("Distance between text body and the right of the PDF file.")
-        )
-
-        grid0.addWidget(self.rmargin_label, 38, 0)
-        grid0.addWidget(self.rmargin_entry, 38, 1)
-
-        self.layout.addStretch()
-
-        if sys.platform != 'win32':
-            self.portability_cb.hide()
-
-        # splash screen button signal
-        self.splash_cb.stateChanged.connect(self.on_splash_changed)
-
-        # Monitor the checkbox from the Application Defaults Tab and show the TCL shell or not depending on it's value
-        self.shell_startup_cb.clicked.connect(self.app.on_toggle_shell_from_settings)
-
-        self.language_apply_btn.clicked.connect(lambda: fcTranslate.on_language_apply_click(app=self.app, restart=True))
-
-    @staticmethod
-    def on_splash_changed(state):
-        qsettings = QSettings("Open Source", "FlatCAM")
-        qsettings.setValue('splash_screen', 1) if state else qsettings.setValue('splash_screen', 0)
-
-        # This will write the setting to the platform specific storage.
-        del qsettings
-
-
-class GerberGenPrefGroupUI(OptionsGroupUI):
-    def __init__(self, decimals=4, parent=None):
-        # OptionsGroupUI.__init__(self, "Gerber General Preferences", parent=parent)
-        super(GerberGenPrefGroupUI, self).__init__(self)
-
-        self.parent = parent
-        self.setTitle(str(_("Gerber General")))
-        self.decimals = decimals
-
-        # ## Plot options
-        self.plot_options_label = QtWidgets.QLabel("<b>%s:</b>" % _("Plot Options"))
-        self.layout.addWidget(self.plot_options_label)
-
-        grid0 = QtWidgets.QGridLayout()
-        self.layout.addLayout(grid0)
-
-        # Solid CB
-        self.solid_cb = FCCheckBox(label='%s' % _('Solid'))
-        self.solid_cb.setToolTip(
-            _("Solid color polygons.")
-        )
-        grid0.addWidget(self.solid_cb, 0, 0)
-
-        # Multicolored CB
-        self.multicolored_cb = FCCheckBox(label='%s' % _('M-Color'))
-        self.multicolored_cb.setToolTip(
-            _("Draw polygons in different colors.")
-        )
-        grid0.addWidget(self.multicolored_cb, 0, 1)
-
-        # Plot CB
-        self.plot_cb = FCCheckBox(label='%s' % _('Plot'))
-        self.plot_options_label.setToolTip(
-            _("Plot (show) this object.")
-        )
-        grid0.addWidget(self.plot_cb, 0, 2)
-
-        # Number of circle steps for circular aperture linear approximation
-        self.circle_steps_label = QtWidgets.QLabel('%s:' % _("Circle Steps"))
-        self.circle_steps_label.setToolTip(
-            _("The number of circle steps for Gerber \n"
-              "circular aperture linear approximation.")
-        )
-        self.circle_steps_entry = FCSpinner()
-        self.circle_steps_entry.set_range(0, 9999)
-
-        grid0.addWidget(self.circle_steps_label, 1, 0)
-        grid0.addWidget(self.circle_steps_entry, 1, 1, 1, 2)
-
-        grid0.addWidget(QtWidgets.QLabel(''), 2, 0, 1, 3)
-
-        # Default format for Gerber
-        self.gerber_default_label = QtWidgets.QLabel('<b>%s:</b>' % _('Default Values'))
-        self.gerber_default_label.setToolTip(
-            _("Those values will be used as fallback values\n"
-              "in case that they are not found in the Gerber file.")
-        )
-
-        grid0.addWidget(self.gerber_default_label, 3, 0, 1, 3)
-
-        # Gerber Units
-        self.gerber_units_label = QtWidgets.QLabel('%s:' % _('Units'))
-        self.gerber_units_label.setToolTip(
-            _("The units used in the Gerber file.")
-        )
-
-        self.gerber_units_radio = RadioSet([{'label': _('INCH'), 'value': 'IN'},
-                                            {'label': _('MM'), 'value': 'MM'}])
-        self.gerber_units_radio.setToolTip(
-            _("The units used in the Gerber file.")
-        )
-
-        grid0.addWidget(self.gerber_units_label, 4, 0)
-        grid0.addWidget(self.gerber_units_radio, 4, 1, 1, 2)
-
-        # Gerber Zeros
-        self.gerber_zeros_label = QtWidgets.QLabel('%s:' % _('Zeros'))
-        self.gerber_zeros_label.setAlignment(QtCore.Qt.AlignLeft)
-        self.gerber_zeros_label.setToolTip(
-            _("This sets the type of Gerber zeros.\n"
-              "If LZ then Leading Zeros are removed and\n"
-              "Trailing Zeros are kept.\n"
-              "If TZ is checked then Trailing Zeros are removed\n"
-              "and Leading Zeros are kept.")
-        )
-
-        self.gerber_zeros_radio = RadioSet([{'label': _('LZ'), 'value': 'L'},
-                                            {'label': _('TZ'), 'value': 'T'}])
-        self.gerber_zeros_radio.setToolTip(
-            _("This sets the type of Gerber zeros.\n"
-              "If LZ then Leading Zeros are removed and\n"
-              "Trailing Zeros are kept.\n"
-              "If TZ is checked then Trailing Zeros are removed\n"
-              "and Leading Zeros are kept.")
-        )
-
-        grid0.addWidget(self.gerber_zeros_label, 5, 0)
-        grid0.addWidget(self.gerber_zeros_radio, 5, 1, 1, 2)
-
-        separator_line = QtWidgets.QFrame()
-        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
-        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
-        grid0.addWidget(separator_line, 6, 0, 1, 3)
-
-        # Apertures Cleaning
-        self.gerber_clean_cb = FCCheckBox(label='%s' % _('Clean Apertures'))
-        self.gerber_clean_cb.setToolTip(
-            _("Will remove apertures that do not have geometry\n"
-              "thus lowering the number of apertures in the Gerber object.")
-        )
-        grid0.addWidget(self.gerber_clean_cb, 7, 0, 1, 3)
-
-        # Apply Extra Buffering
-        self.gerber_extra_buffering = FCCheckBox(label='%s' % _('Polarity change buffer'))
-        self.gerber_extra_buffering.setToolTip(
-            _("Will apply extra buffering for the\n"
-              "solid geometry when we have polarity changes.\n"
-              "May help loading Gerber files that otherwise\n"
-              "do not load correctly.")
-        )
-        grid0.addWidget(self.gerber_extra_buffering, 8, 0, 1, 3)
-
-        separator_line = QtWidgets.QFrame()
-        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
-        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
-        grid0.addWidget(separator_line, 9, 0, 1, 3)
-
-        # Gerber Object Color
-        self.gerber_color_label = QtWidgets.QLabel('<b>%s</b>' % _('Gerber Object Color'))
-        grid0.addWidget(self.gerber_color_label, 10, 0, 1, 3)
-
-        # Plot Line Color
-        self.pl_color_label = QtWidgets.QLabel('%s:' % _('Outline'))
-        self.pl_color_label.setToolTip(
-            _("Set the line color for plotted objects.")
-        )
-        self.pl_color_entry = FCEntry()
-        self.pl_color_button = QtWidgets.QPushButton()
-        self.pl_color_button.setFixedSize(15, 15)
-
-        self.form_box_child_2 = QtWidgets.QHBoxLayout()
-        self.form_box_child_2.addWidget(self.pl_color_entry)
-        self.form_box_child_2.addWidget(self.pl_color_button)
-        self.form_box_child_2.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
-
-        grid0.addWidget(self.pl_color_label, 11, 0)
-        grid0.addLayout(self.form_box_child_2, 11, 1, 1, 2)
-
-        # Plot Fill Color
-        self.pf_color_label = QtWidgets.QLabel('%s:' % _('Fill'))
-        self.pf_color_label.setToolTip(
-            _("Set the fill color for plotted objects.\n"
-              "First 6 digits are the color and the last 2\n"
-              "digits are for alpha (transparency) level.")
-        )
-        self.pf_color_entry = FCEntry()
-        self.pf_color_button = QtWidgets.QPushButton()
-        self.pf_color_button.setFixedSize(15, 15)
-
-        self.form_box_child_1 = QtWidgets.QHBoxLayout()
-        self.form_box_child_1.addWidget(self.pf_color_entry)
-        self.form_box_child_1.addWidget(self.pf_color_button)
-        self.form_box_child_1.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
-
-        grid0.addWidget(self.pf_color_label, 12, 0)
-        grid0.addLayout(self.form_box_child_1, 12, 1, 1, 2)
-
-        # Plot Fill Transparency Level
-        self.pf_alpha_label = QtWidgets.QLabel('%s:' % _('Alpha'))
-        self.pf_alpha_label.setToolTip(
-            _("Set the fill transparency for plotted objects.")
-        )
-        self.pf_color_alpha_slider = QtWidgets.QSlider(QtCore.Qt.Horizontal)
-        self.pf_color_alpha_slider.setMinimum(0)
-        self.pf_color_alpha_slider.setMaximum(255)
-        self.pf_color_alpha_slider.setSingleStep(1)
-
-        self.pf_color_alpha_spinner = FCSpinner()
-        self.pf_color_alpha_spinner.setMinimumWidth(70)
-        self.pf_color_alpha_spinner.set_range(0, 255)
-
-        self.form_box_child_3 = QtWidgets.QHBoxLayout()
-        self.form_box_child_3.addWidget(self.pf_color_alpha_slider)
-        self.form_box_child_3.addWidget(self.pf_color_alpha_spinner)
-
-        grid0.addWidget(self.pf_alpha_label, 13, 0)
-        grid0.addLayout(self.form_box_child_3, 13, 1, 1, 2)
-
-        self.layout.addStretch()
-
-        # Setting plot colors signals
-        self.pl_color_entry.editingFinished.connect(self.on_pl_color_entry)
-        self.pl_color_button.clicked.connect(self.on_pl_color_button)
-        self.pf_color_entry.editingFinished.connect(self.on_pf_color_entry)
-        self.pf_color_button.clicked.connect(self.on_pf_color_button)
-        self.pf_color_alpha_spinner.valueChanged.connect(self.on_pf_color_spinner)
-        self.pf_color_alpha_slider.valueChanged.connect(self.on_pf_color_slider)
-
-    # Setting plot colors handlers
-    def on_pf_color_entry(self):
-        self.app.defaults['gerber_plot_fill'] = self.pf_color_entry.get_value()[:7] + \
-            self.app.defaults['gerber_plot_fill'][7:9]
-        self.pf_color_button.setStyleSheet("background-color:%s" % str(self.app.defaults['gerber_plot_fill'])[:7])
-
-    def on_pf_color_button(self):
-        current_color = QtGui.QColor(self.app.defaults['gerber_plot_fill'][:7])
-
-        c_dialog = QtWidgets.QColorDialog()
-        plot_fill_color = c_dialog.getColor(initial=current_color)
-
-        if plot_fill_color.isValid() is False:
-            return
-
-        self.pf_color_button.setStyleSheet("background-color:%s" % str(plot_fill_color.name()))
-
-        new_val = str(plot_fill_color.name()) + str(self.app.defaults['gerber_plot_fill'][7:9])
-        self.pf_color_entry.set_value(new_val)
-        self.app.defaults['gerber_plot_fill'] = new_val
-
-    def on_pf_color_spinner(self):
-        spinner_value = self.pf_color_alpha_spinner.value()
-        self.pf_color_alpha_slider.setValue(spinner_value)
-        self.app.defaults['gerber_plot_fill'] = \
-            self.app.defaults['gerber_plot_fill'][:7] + \
-            (hex(spinner_value)[2:] if int(hex(spinner_value)[2:], 16) > 0 else '00')
-        self.app.defaults['gerber_plot_line'] = \
-            self.app.defaults['gerber_plot_line'][:7] + \
-            (hex(spinner_value)[2:] if int(hex(spinner_value)[2:], 16) > 0 else '00')
-
-    def on_pf_color_slider(self):
-        slider_value = self.pf_color_alpha_slider.value()
-        self.pf_color_alpha_spinner.setValue(slider_value)
-
-    def on_pl_color_entry(self):
-        self.app.defaults['gerber_plot_line'] = self.pl_color_entry.get_value()[:7] + \
-                                                self.app.defaults['gerber_plot_line'][7:9]
-        self.pl_color_button.setStyleSheet("background-color:%s" % str(self.app.defaults['gerber_plot_line'])[:7])
-
-    def on_pl_color_button(self):
-        current_color = QtGui.QColor(self.app.defaults['gerber_plot_line'][:7])
-        # print(current_color)
-
-        c_dialog = QtWidgets.QColorDialog()
-        plot_line_color = c_dialog.getColor(initial=current_color)
-
-        if plot_line_color.isValid() is False:
-            return
-
-        self.pl_color_button.setStyleSheet("background-color:%s" % str(plot_line_color.name()))
-
-        new_val_line = str(plot_line_color.name()) + str(self.app.defaults['gerber_plot_line'][7:9])
-        self.pl_color_entry.set_value(new_val_line)
-        self.app.defaults['gerber_plot_line'] = new_val_line
-
-
-class GerberOptPrefGroupUI(OptionsGroupUI):
-    def __init__(self, decimals=4, parent=None):
-        # OptionsGroupUI.__init__(self, "Gerber Options Preferences", parent=parent)
-        super(GerberOptPrefGroupUI, self).__init__(self)
-        self.decimals = decimals
-
-        self.setTitle(str(_("Gerber Options")))
-
-        # ## Isolation Routing
-        self.isolation_routing_label = QtWidgets.QLabel("<b>%s:</b>" % _("Isolation Routing"))
-        self.isolation_routing_label.setToolTip(
-            _("Create a Geometry object with\n"
-              "toolpaths to cut outside polygons.")
-        )
-        self.layout.addWidget(self.isolation_routing_label)
-
-        # Cutting Tool Diameter
-        grid0 = QtWidgets.QGridLayout()
-        self.layout.addLayout(grid0)
-
-        tdlabel = QtWidgets.QLabel('%s:' % _('Tool dia'))
-        tdlabel.setToolTip(
-            _("Diameter of the cutting tool.")
-        )
-        grid0.addWidget(tdlabel, 0, 0)
-        self.iso_tool_dia_entry = FCDoubleSpinner()
-        self.iso_tool_dia_entry.set_precision(self.decimals)
-        self.iso_tool_dia_entry.setSingleStep(0.1)
-        self.iso_tool_dia_entry.set_range(-9999, 9999)
-
-        grid0.addWidget(self.iso_tool_dia_entry, 0, 1)
-
-        # Nr of passes
-        passlabel = QtWidgets.QLabel('%s:' % _('# Passes'))
-        passlabel.setToolTip(
-            _("Width of the isolation gap in\n"
-              "number (integer) of tool widths.")
-        )
-        self.iso_width_entry = FCSpinner()
-        self.iso_width_entry.set_range(1, 999)
-
-        grid0.addWidget(passlabel, 1, 0)
-        grid0.addWidget(self.iso_width_entry, 1, 1)
-
-        # Pass overlap
-        overlabel = QtWidgets.QLabel('%s:' % _('Pass overlap'))
-        overlabel.setToolTip(
-            _("How much (percentage) of the tool width to overlap each tool pass.")
-        )
-        self.iso_overlap_entry = FCDoubleSpinner(suffix='%')
-        self.iso_overlap_entry.set_precision(self.decimals)
-        self.iso_overlap_entry.setWrapping(True)
-        self.iso_overlap_entry.setRange(0.0000, 99.9999)
-        self.iso_overlap_entry.setSingleStep(0.1)
-
-        grid0.addWidget(overlabel, 2, 0)
-        grid0.addWidget(self.iso_overlap_entry, 2, 1)
-
-        # Isolation Scope
-        self.iso_scope_label = QtWidgets.QLabel('%s:' % _('Scope'))
-        self.iso_scope_label.setToolTip(
-            _("Isolation scope. Choose what to isolate:\n"
-              "- 'All' -> Isolate all the polygons in the object\n"
-              "- 'Selection' -> Isolate a selection of polygons.")
-        )
-        self.iso_scope_radio = RadioSet([{'label': _('All'), 'value': 'all'},
-                                         {'label': _('Selection'), 'value': 'single'}])
-
-        grid0.addWidget(self.iso_scope_label, 3, 0)
-        grid0.addWidget(self.iso_scope_radio, 3, 1, 1, 2)
-
-        # Milling Type
-        milling_type_label = QtWidgets.QLabel('%s:' % _('Milling Type'))
-        milling_type_label.setToolTip(
-            _("Milling type:\n"
-              "- climb / best for precision milling and to reduce tool usage\n"
-              "- conventional / useful when there is no backlash compensation")
-        )
-        grid0.addWidget(milling_type_label, 4, 0)
-        self.milling_type_radio = RadioSet([{'label': _('Climb'), 'value': 'cl'},
-                                            {'label': _('Conventional'), 'value': 'cv'}])
-        grid0.addWidget(self.milling_type_radio, 4, 1)
-
-        # Combine passes
-        self.combine_passes_cb = FCCheckBox(label=_('Combine Passes'))
-        self.combine_passes_cb.setToolTip(
-            _("Combine all passes into one object")
-        )
-        grid0.addWidget(self.combine_passes_cb, 5, 0, 1, 2)
-
-        separator_line = QtWidgets.QFrame()
-        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
-        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
-        grid0.addWidget(separator_line, 6, 0, 1, 2)
-
-        # ## Clear non-copper regions
-        self.clearcopper_label = QtWidgets.QLabel("<b>%s:</b>" % _("Non-copper regions"))
-        self.clearcopper_label.setToolTip(
-            _("Create polygons covering the\n"
-              "areas without copper on the PCB.\n"
-              "Equivalent to the inverse of this\n"
-              "object. Can be used to remove all\n"
-              "copper from a specified region.")
-        )
-        self.layout.addWidget(self.clearcopper_label)
-
-        grid1 = QtWidgets.QGridLayout()
-        self.layout.addLayout(grid1)
-
-        # Margin
-        bmlabel = QtWidgets.QLabel('%s:' % _('Boundary Margin'))
-        bmlabel.setToolTip(
-            _("Specify the edge of the PCB\n"
-              "by drawing a box around all\n"
-              "objects with this minimum\n"
-              "distance.")
-        )
-        grid1.addWidget(bmlabel, 0, 0)
-        self.noncopper_margin_entry = FCDoubleSpinner()
-        self.noncopper_margin_entry.set_precision(self.decimals)
-        self.noncopper_margin_entry.setSingleStep(0.1)
-        self.noncopper_margin_entry.set_range(-9999, 9999)
-        grid1.addWidget(self.noncopper_margin_entry, 0, 1)
-
-        # Rounded corners
-        self.noncopper_rounded_cb = FCCheckBox(label=_("Rounded Geo"))
-        self.noncopper_rounded_cb.setToolTip(
-            _("Resulting geometry will have rounded corners.")
-        )
-        grid1.addWidget(self.noncopper_rounded_cb, 1, 0, 1, 2)
-
-        separator_line = QtWidgets.QFrame()
-        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
-        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
-        grid1.addWidget(separator_line, 2, 0, 1, 2)
-
-        # ## Bounding box
-        self.boundingbox_label = QtWidgets.QLabel('<b>%s:</b>' % _('Bounding Box'))
-        self.layout.addWidget(self.boundingbox_label)
-
-        grid2 = QtWidgets.QGridLayout()
-        self.layout.addLayout(grid2)
-
-        bbmargin = QtWidgets.QLabel('%s:' % _('Boundary Margin'))
-        bbmargin.setToolTip(
-            _("Distance of the edges of the box\n"
-              "to the nearest polygon.")
-        )
-        self.bbmargin_entry = FCDoubleSpinner()
-        self.bbmargin_entry.set_precision(self.decimals)
-        self.bbmargin_entry.setSingleStep(0.1)
-        self.bbmargin_entry.set_range(-9999, 9999)
-
-        grid2.addWidget(bbmargin, 0, 0)
-        grid2.addWidget(self.bbmargin_entry, 0, 1)
-
-        self.bbrounded_cb = FCCheckBox(label='%s' % _("Rounded Geo"))
-        self.bbrounded_cb.setToolTip(
-            _("If the bounding box is \n"
-              "to have rounded corners\n"
-              "their radius is equal to\n"
-              "the margin.")
-        )
-        grid2.addWidget(self.bbrounded_cb, 1, 0, 1, 2)
-        self.layout.addStretch()
-
-
-class GerberAdvOptPrefGroupUI(OptionsGroupUI):
-    def __init__(self, decimals=4, parent=None):
-        # OptionsGroupUI.__init__(self, "Gerber Adv. Options Preferences", parent=parent)
-        super(GerberAdvOptPrefGroupUI, self).__init__(self)
-
-        self.setTitle(str(_("Gerber Adv. Options")))
-        self.decimals = decimals
-
-        # ## Advanced Gerber Parameters
-        self.adv_param_label = QtWidgets.QLabel('<b>%s:</b>' % _('Advanced Options'))
-        self.adv_param_label.setToolTip(
-            _("A list of Gerber advanced parameters.\n"
-              "Those parameters are available only for\n"
-              "Advanced App. Level.")
-        )
-        self.layout.addWidget(self.adv_param_label)
-
-        grid0 = QtWidgets.QGridLayout()
-        self.layout.addLayout(grid0)
-
-        # Follow Attribute
-        self.follow_cb = FCCheckBox(label=_('"Follow"'))
-        self.follow_cb.setToolTip(
-            _("Generate a 'Follow' geometry.\n"
-              "This means that it will cut through\n"
-              "the middle of the trace.")
-        )
-        grid0.addWidget(self.follow_cb, 0, 0, 1, 2)
-
-        # Aperture Table Visibility CB
-        self.aperture_table_visibility_cb = FCCheckBox(label=_('Table Show/Hide'))
-        self.aperture_table_visibility_cb.setToolTip(
-            _("Toggle the display of the Gerber Apertures Table.\n"
-              "Also, on hide, it will delete all mark shapes\n"
-              "that are drawn on canvas.")
-
-        )
-        grid0.addWidget(self.aperture_table_visibility_cb, 1, 0, 1, 2)
-
-        separator_line = QtWidgets.QFrame()
-        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
-        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
-        grid0.addWidget(separator_line, 2, 0, 1, 2)
-
-        # Tool Type
-        self.tool_type_label = QtWidgets.QLabel('<b>%s</b>' % _('Tool Type'))
-        self.tool_type_label.setToolTip(
-            _("Choose which tool to use for Gerber isolation:\n"
-              "'Circular' or 'V-shape'.\n"
-              "When the 'V-shape' is selected then the tool\n"
-              "diameter will depend on the chosen cut depth.")
-        )
-        self.tool_type_radio = RadioSet([{'label': 'Circular', 'value': 'circular'},
-                                         {'label': 'V-Shape', 'value': 'v'}])
-
-        grid0.addWidget(self.tool_type_label, 3, 0)
-        grid0.addWidget(self.tool_type_radio, 3, 1, 1, 2)
-
-        # Tip Dia
-        self.tipdialabel = QtWidgets.QLabel('%s:' % _('V-Tip Dia'))
-        self.tipdialabel.setToolTip(
-            _("The tip diameter for V-Shape Tool")
-        )
-        self.tipdia_spinner = FCDoubleSpinner()
-        self.tipdia_spinner.set_precision(self.decimals)
-        self.tipdia_spinner.set_range(-99.9999, 99.9999)
-        self.tipdia_spinner.setSingleStep(0.1)
-        self.tipdia_spinner.setWrapping(True)
-        grid0.addWidget(self.tipdialabel, 4, 0)
-        grid0.addWidget(self.tipdia_spinner, 4, 1, 1, 2)
-
-        # Tip Angle
-        self.tipanglelabel = QtWidgets.QLabel('%s:' % _('V-Tip Angle'))
-        self.tipanglelabel.setToolTip(
-            _("The tip angle for V-Shape Tool.\n"
-              "In degree.")
-        )
-        self.tipangle_spinner = FCSpinner()
-        self.tipangle_spinner.set_range(1, 180)
-        self.tipangle_spinner.set_step(5)
-        self.tipangle_spinner.setWrapping(True)
-        grid0.addWidget(self.tipanglelabel, 5, 0)
-        grid0.addWidget(self.tipangle_spinner, 5, 1, 1, 2)
-
-        # Cut Z
-        self.cutzlabel = QtWidgets.QLabel('%s:' % _('Cut Z'))
-        self.cutzlabel.setToolTip(
-            _("Cutting depth (negative)\n"
-              "below the copper surface.")
-        )
-        self.cutz_spinner = FCDoubleSpinner()
-        self.cutz_spinner.set_precision(self.decimals)
-        self.cutz_spinner.set_range(-99.9999, 0.0000)
-        self.cutz_spinner.setSingleStep(0.1)
-        self.cutz_spinner.setWrapping(True)
-
-        grid0.addWidget(self.cutzlabel, 6, 0)
-        grid0.addWidget(self.cutz_spinner, 6, 1, 1, 2)
-
-        # Isolation Type
-        self.iso_type_label = QtWidgets.QLabel('%s:' % _('Isolation Type'))
-        self.iso_type_label.setToolTip(
-            _("Choose how the isolation will be executed:\n"
-              "- 'Full' -> complete isolation of polygons\n"
-              "- 'Ext' -> will isolate only on the outside\n"
-              "- 'Int' -> will isolate only on the inside\n"
-              "'Exterior' isolation is almost always possible\n"
-              "(with the right tool) but 'Interior'\n"
-              "isolation can be done only when there is an opening\n"
-              "inside of the polygon (e.g polygon is a 'doughnut' shape).")
-        )
-        self.iso_type_radio = RadioSet([{'label': _('Full'), 'value': 'full'},
-                                        {'label': _('Exterior'), 'value': 'ext'},
-                                        {'label': _('Interior'), 'value': 'int'}])
-
-        grid0.addWidget(self.iso_type_label, 7, 0,)
-        grid0.addWidget(self.iso_type_radio, 7, 1, 1, 2)
-
-        separator_line = QtWidgets.QFrame()
-        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
-        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
-        grid0.addWidget(separator_line, 8, 0, 1, 2)
-
-        # Buffering Type
-        buffering_label = QtWidgets.QLabel('%s:' % _('Buffering'))
-        buffering_label.setToolTip(
-            _("Buffering type:\n"
-              "- None --> best performance, fast file loading but no so good display\n"
-              "- Full --> slow file loading but good visuals. This is the default.\n"
-              "<<WARNING>>: Don't change this unless you know what you are doing !!!")
-        )
-        self.buffering_radio = RadioSet([{'label': _('None'), 'value': 'no'},
-                                         {'label': _('Full'), 'value': 'full'}])
-        grid0.addWidget(buffering_label, 9, 0)
-        grid0.addWidget(self.buffering_radio, 9, 1)
-
-        # Simplification
-        self.simplify_cb = FCCheckBox(label=_('Simplify'))
-        self.simplify_cb.setToolTip(
-            _("When checked all the Gerber polygons will be\n"
-              "loaded with simplification having a set tolerance.\n"
-              "<<WARNING>>: Don't change this unless you know what you are doing !!!")
-                                    )
-        grid0.addWidget(self.simplify_cb, 10, 0, 1, 2)
-
-        # Simplification tolerance
-        self.simplification_tol_label = QtWidgets.QLabel(_('Tolerance'))
-        self.simplification_tol_label.setToolTip(_("Tolerance for polygon simplification."))
-
-        self.simplification_tol_spinner = FCDoubleSpinner()
-        self.simplification_tol_spinner.set_precision(self.decimals + 1)
-        self.simplification_tol_spinner.setWrapping(True)
-        self.simplification_tol_spinner.setRange(0.00000, 0.01000)
-        self.simplification_tol_spinner.setSingleStep(0.0001)
-
-        grid0.addWidget(self.simplification_tol_label, 11, 0)
-        grid0.addWidget(self.simplification_tol_spinner, 11, 1)
-        self.ois_simplif = OptionalInputSection(
-            self.simplify_cb,
-            [
-                self.simplification_tol_label, self.simplification_tol_spinner
-            ],
-            logic=True)
-
-        self.layout.addStretch()
-
-
-class GerberExpPrefGroupUI(OptionsGroupUI):
-
-    def __init__(self, decimals=4, parent=None):
-        super(GerberExpPrefGroupUI, self).__init__(self)
-
-        self.setTitle(str(_("Gerber Export")))
-        self.decimals = decimals
-
-        # Plot options
-        self.export_options_label = QtWidgets.QLabel("<b>%s:</b>" % _("Export Options"))
-        self.export_options_label.setToolTip(
-            _("The parameters set here are used in the file exported\n"
-              "when using the File -> Export -> Export Gerber menu entry.")
-        )
-        self.layout.addWidget(self.export_options_label)
-
-        form = QtWidgets.QFormLayout()
-        self.layout.addLayout(form)
-
-        # Gerber Units
-        self.gerber_units_label = QtWidgets.QLabel('%s:' % _('Units'))
-        self.gerber_units_label.setToolTip(
-            _("The units used in the Gerber file.")
-        )
-
-        self.gerber_units_radio = RadioSet([{'label': _('INCH'), 'value': 'IN'},
-                                            {'label': _('MM'), 'value': 'MM'}])
-        self.gerber_units_radio.setToolTip(
-            _("The units used in the Gerber file.")
-        )
-
-        form.addRow(self.gerber_units_label, self.gerber_units_radio)
-
-        # Gerber format
-        self.digits_label = QtWidgets.QLabel("%s:" % _("Int/Decimals"))
-        self.digits_label.setToolTip(
-            _("The number of digits in the whole part of the number\n"
-              "and in the fractional part of the number.")
-        )
-
-        hlay1 = QtWidgets.QHBoxLayout()
-
-        self.format_whole_entry = FCSpinner()
-        self.format_whole_entry.set_range(0, 9)
-        self.format_whole_entry.set_step(1)
-        self.format_whole_entry.setWrapping(True)
-
-        self.format_whole_entry.setMinimumWidth(30)
-        self.format_whole_entry.setToolTip(
-            _("This numbers signify the number of digits in\n"
-              "the whole part of Gerber coordinates.")
-        )
-        hlay1.addWidget(self.format_whole_entry, QtCore.Qt.AlignLeft)
-
-        gerber_separator_label = QtWidgets.QLabel(':')
-        gerber_separator_label.setFixedWidth(5)
-        hlay1.addWidget(gerber_separator_label, QtCore.Qt.AlignLeft)
-
-        self.format_dec_entry = FCSpinner()
-        self.format_dec_entry.set_range(0, 9)
-        self.format_dec_entry.set_step(1)
-        self.format_dec_entry.setWrapping(True)
-
-        self.format_dec_entry.setMinimumWidth(30)
-        self.format_dec_entry.setToolTip(
-            _("This numbers signify the number of digits in\n"
-              "the decimal part of Gerber coordinates.")
-        )
-        hlay1.addWidget(self.format_dec_entry, QtCore.Qt.AlignLeft)
-        hlay1.addStretch()
-
-        form.addRow(self.digits_label, hlay1)
-
-        # Gerber Zeros
-        self.zeros_label = QtWidgets.QLabel('%s:' % _('Zeros'))
-        self.zeros_label.setAlignment(QtCore.Qt.AlignLeft)
-        self.zeros_label.setToolTip(
-            _("This sets the type of Gerber zeros.\n"
-              "If LZ then Leading Zeros are removed and\n"
-              "Trailing Zeros are kept.\n"
-              "If TZ is checked then Trailing Zeros are removed\n"
-              "and Leading Zeros are kept.")
-        )
-
-        self.zeros_radio = RadioSet([{'label': _('LZ'), 'value': 'L'},
-                                     {'label': _('TZ'), 'value': 'T'}])
-        self.zeros_radio.setToolTip(
-            _("This sets the type of Gerber zeros.\n"
-              "If LZ then Leading Zeros are removed and\n"
-              "Trailing Zeros are kept.\n"
-              "If TZ is checked then Trailing Zeros are removed\n"
-              "and Leading Zeros are kept.")
-        )
-
-        form.addRow(self.zeros_label, self.zeros_radio)
-
-        self.layout.addStretch()
-
-
-class GerberEditorPrefGroupUI(OptionsGroupUI):
-    def __init__(self, decimals=4, parent=None):
-        # OptionsGroupUI.__init__(self, "Gerber Adv. Options Preferences", parent=parent)
-        super(GerberEditorPrefGroupUI, self).__init__(self)
-
-        self.setTitle(str(_("Gerber Editor")))
-        self.decimals = decimals
-
-        # Advanced Gerber Parameters
-        self.param_label = QtWidgets.QLabel("<b>%s:</b>" % _("Parameters"))
-        self.param_label.setToolTip(
-            _("A list of Gerber Editor parameters.")
-        )
-        self.layout.addWidget(self.param_label)
-
-        grid0 = QtWidgets.QGridLayout()
-        self.layout.addLayout(grid0)
-
-        # Selection Limit
-        self.sel_limit_label = QtWidgets.QLabel('%s:' % _("Selection limit"))
-        self.sel_limit_label.setToolTip(
-            _("Set the number of selected Gerber geometry\n"
-              "items above which the utility geometry\n"
-              "becomes just a selection rectangle.\n"
-              "Increases the performance when moving a\n"
-              "large number of geometric elements.")
-        )
-        self.sel_limit_entry = FCSpinner()
-        self.sel_limit_entry.set_range(0, 9999)
-
-        grid0.addWidget(self.sel_limit_label, 0, 0)
-        grid0.addWidget(self.sel_limit_entry, 0, 1)
-
-        # New aperture code
-        self.addcode_entry_lbl = QtWidgets.QLabel('%s:' % _('New Aperture code'))
-        self.addcode_entry_lbl.setToolTip(
-            _("Code for the new aperture")
-        )
-
-        self.addcode_entry = FCSpinner()
-        self.addcode_entry.set_range(10, 99)
-        self.addcode_entry.setWrapping(True)
-
-        grid0.addWidget(self.addcode_entry_lbl, 1, 0)
-        grid0.addWidget(self.addcode_entry, 1, 1)
-
-        # New aperture size
-        self.addsize_entry_lbl = QtWidgets.QLabel('%s:' % _('New Aperture size'))
-        self.addsize_entry_lbl.setToolTip(
-            _("Size for the new aperture")
-        )
-
-        self.addsize_entry = FCDoubleSpinner()
-        self.addsize_entry.set_range(0, 100)
-        self.addsize_entry.set_precision(self.decimals)
-
-        grid0.addWidget(self.addsize_entry_lbl, 2, 0)
-        grid0.addWidget(self.addsize_entry, 2, 1)
-
-        # New aperture type
-        self.addtype_combo_lbl = QtWidgets.QLabel('%s:' % _('New Aperture type'))
-        self.addtype_combo_lbl.setToolTip(
-            _("Type for the new aperture.\n"
-              "Can be 'C', 'R' or 'O'.")
-        )
-
-        self.addtype_combo = FCComboBox()
-        self.addtype_combo.addItems(['C', 'R', 'O'])
-
-        grid0.addWidget(self.addtype_combo_lbl, 3, 0)
-        grid0.addWidget(self.addtype_combo, 3, 1)
-
-        # Number of pads in a pad array
-        self.grb_array_size_label = QtWidgets.QLabel('%s:' % _('Nr of pads'))
-        self.grb_array_size_label.setToolTip(
-            _("Specify how many pads to be in the array.")
-        )
-
-        self.grb_array_size_entry = FCSpinner()
-        self.grb_array_size_entry.set_range(0, 9999)
-
-        grid0.addWidget(self.grb_array_size_label, 4, 0)
-        grid0.addWidget(self.grb_array_size_entry, 4, 1)
-
-        self.adddim_label = QtWidgets.QLabel('%s:' % _('Aperture Dimensions'))
-        self.adddim_label.setToolTip(
-            _("Diameters of the tools, separated by comma.\n"
-              "The value of the diameter has to use the dot decimals separator.\n"
-              "Valid values: 0.3, 1.0")
-        )
-        grid0.addWidget(self.adddim_label, 5, 0)
-        self.adddim_entry = FCEntry()
-        grid0.addWidget(self.adddim_entry, 5, 1)
-
-        self.grb_array_linear_label = QtWidgets.QLabel('<b>%s:</b>' % _('Linear Pad Array'))
-        grid0.addWidget(self.grb_array_linear_label, 6, 0, 1, 2)
-
-        # Linear Pad Array direction
-        self.grb_axis_label = QtWidgets.QLabel('%s:' % _('Linear Direction'))
-        self.grb_axis_label.setToolTip(
-            _("Direction on which the linear array is oriented:\n"
-              "- 'X' - horizontal axis \n"
-              "- 'Y' - vertical axis or \n"
-              "- 'Angle' - a custom angle for the array inclination")
-        )
-
-        self.grb_axis_radio = RadioSet([{'label': _('X'), 'value': 'X'},
-                                        {'label': _('Y'), 'value': 'Y'},
-                                        {'label': _('Angle'), 'value': 'A'}])
-
-        grid0.addWidget(self.grb_axis_label, 7, 0)
-        grid0.addWidget(self.grb_axis_radio, 7, 1)
-
-        # Linear Pad Array pitch distance
-        self.grb_pitch_label = QtWidgets.QLabel('%s:' % _('Pitch'))
-        self.grb_pitch_label.setToolTip(
-            _("Pitch = Distance between elements of the array.")
-        )
-        # self.drill_pitch_label.setMinimumWidth(100)
-        self.grb_pitch_entry = FCDoubleSpinner()
-        self.grb_pitch_entry.set_precision(self.decimals)
-
-        grid0.addWidget(self.grb_pitch_label, 8, 0)
-        grid0.addWidget(self.grb_pitch_entry, 8, 1)
-
-        # Linear Pad Array custom angle
-        self.grb_angle_label = QtWidgets.QLabel('%s:' % _('Angle'))
-        self.grb_angle_label.setToolTip(
-            _("Angle at which each element in circular array is placed.")
-        )
-        self.grb_angle_entry = FCDoubleSpinner()
-        self.grb_angle_entry.set_precision(self.decimals)
-        self.grb_angle_entry.set_range(-360, 360)
-        self.grb_angle_entry.setSingleStep(5)
-
-        grid0.addWidget(self.grb_angle_label, 9, 0)
-        grid0.addWidget(self.grb_angle_entry, 9, 1)
-
-        self.grb_array_circ_label = QtWidgets.QLabel('<b>%s:</b>' % _('Circular Pad Array'))
-        grid0.addWidget(self.grb_array_circ_label, 10, 0, 1, 2)
-
-        # Circular Pad Array direction
-        self.grb_circular_direction_label = QtWidgets.QLabel('%s:' % _('Circular Direction'))
-        self.grb_circular_direction_label.setToolTip(
-            _("Direction for circular array.\n"
-              "Can be CW = clockwise or CCW = counter clockwise.")
-        )
-
-        self.grb_circular_dir_radio = RadioSet([{'label': _('CW'), 'value': 'CW'},
-                                                {'label': _('CCW'), 'value': 'CCW'}])
-
-        grid0.addWidget(self.grb_circular_direction_label, 11, 0)
-        grid0.addWidget(self.grb_circular_dir_radio, 11, 1)
-
-        # Circular Pad Array Angle
-        self.grb_circular_angle_label = QtWidgets.QLabel('%s:' % _('Circular Angle'))
-        self.grb_circular_angle_label.setToolTip(
-            _("Angle at which each element in circular array is placed.")
-        )
-        self.grb_circular_angle_entry = FCDoubleSpinner()
-        self.grb_circular_angle_entry.set_precision(self.decimals)
-        self.grb_circular_angle_entry.set_range(-360, 360)
-
-        self.grb_circular_angle_entry.setSingleStep(5)
-
-        grid0.addWidget(self.grb_circular_angle_label, 12, 0)
-        grid0.addWidget(self.grb_circular_angle_entry, 12, 1)
-
-        self.grb_array_tools_b_label = QtWidgets.QLabel('<b>%s:</b>' % _('Buffer Tool'))
-        grid0.addWidget(self.grb_array_tools_b_label, 13, 0, 1, 2)
-
-        # Buffer Distance
-        self.grb_buff_label = QtWidgets.QLabel('%s:' % _('Buffer distance'))
-        self.grb_buff_label.setToolTip(
-            _("Distance at which to buffer the Gerber element.")
-        )
-        self.grb_buff_entry = FCDoubleSpinner()
-        self.grb_buff_entry.set_precision(self.decimals)
-        self.grb_buff_entry.set_range(-9999, 9999)
-
-        grid0.addWidget(self.grb_buff_label, 14, 0)
-        grid0.addWidget(self.grb_buff_entry, 14, 1)
-
-        self.grb_array_tools_s_label = QtWidgets.QLabel('<b>%s:</b>' % _('Scale Tool'))
-        grid0.addWidget(self.grb_array_tools_s_label, 15, 0, 1, 2)
-
-        # Scale Factor
-        self.grb_scale_label = QtWidgets.QLabel('%s:' % _('Scale factor'))
-        self.grb_scale_label.setToolTip(
-            _("Factor to scale the Gerber element.")
-        )
-        self.grb_scale_entry = FCDoubleSpinner()
-        self.grb_scale_entry.set_precision(self.decimals)
-        self.grb_scale_entry.set_range(0, 9999)
-
-        grid0.addWidget(self.grb_scale_label, 16, 0)
-        grid0.addWidget(self.grb_scale_entry, 16, 1)
-
-        self.grb_array_tools_ma_label = QtWidgets.QLabel('<b>%s:</b>' % _('Mark Area Tool'))
-        grid0.addWidget(self.grb_array_tools_ma_label, 17, 0, 1, 2)
-
-        # Mark area Tool low threshold
-        self.grb_ma_low_label = QtWidgets.QLabel('%s:' % _('Threshold low'))
-        self.grb_ma_low_label.setToolTip(
-            _("Threshold value under which the apertures are not marked.")
-        )
-        self.grb_ma_low_entry = FCDoubleSpinner()
-        self.grb_ma_low_entry.set_precision(self.decimals)
-        self.grb_ma_low_entry.set_range(0, 9999)
-
-        grid0.addWidget(self.grb_ma_low_label, 18, 0)
-        grid0.addWidget(self.grb_ma_low_entry, 18, 1)
-
-        # Mark area Tool high threshold
-        self.grb_ma_high_label = QtWidgets.QLabel('%s:' % _('Threshold high'))
-        self.grb_ma_high_label.setToolTip(
-            _("Threshold value over which the apertures are not marked.")
-        )
-        self.grb_ma_high_entry = FCDoubleSpinner()
-        self.grb_ma_high_entry.set_precision(self.decimals)
-        self.grb_ma_high_entry.set_range(0, 9999)
-
-        grid0.addWidget(self.grb_ma_high_label, 19, 0)
-        grid0.addWidget(self.grb_ma_high_entry, 19, 1)
-
-        self.layout.addStretch()
-
-
-class ExcellonGenPrefGroupUI(OptionsGroupUI):
-
-    def __init__(self, decimals=4, parent=None):
-        # OptionsGroupUI.__init__(self, "Excellon Options", parent=parent)
-        super(ExcellonGenPrefGroupUI, self).__init__(self)
-
-        self.setTitle(str(_("Excellon General")))
-        self.decimals = decimals
-
-        # Plot options
-        self.plot_options_label = QtWidgets.QLabel("<b>%s:</b>" % _("Plot Options"))
-        self.layout.addWidget(self.plot_options_label)
-
-        grid1 = QtWidgets.QGridLayout()
-        self.layout.addLayout(grid1)
-
-        self.plot_cb = FCCheckBox(label=_('Plot'))
-        self.plot_cb.setToolTip(
-            "Plot (show) this object."
-        )
-        grid1.addWidget(self.plot_cb, 0, 0)
-
-        self.solid_cb = FCCheckBox(label=_('Solid'))
-        self.solid_cb.setToolTip(
-            "Plot as solid circles."
-        )
-        grid1.addWidget(self.solid_cb, 0, 1)
-
-        separator_line = QtWidgets.QFrame()
-        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
-        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
-        grid1.addWidget(separator_line, 1, 0, 1, 2)
-
-        grid2 = QtWidgets.QGridLayout()
-        self.layout.addLayout(grid2)
-        grid2.setColumnStretch(0, 0)
-        grid2.setColumnStretch(1, 1)
-
-        # Excellon format
-        self.excellon_format_label = QtWidgets.QLabel("<b>%s:</b>" % _("Excellon Format"))
-        self.excellon_format_label.setToolTip(
-            _("The NC drill files, usually named Excellon files\n"
-              "are files that can be found in different formats.\n"
-              "Here we set the format used when the provided\n"
-              "coordinates are not using period.\n"
-              "\n"
-              "Possible presets:\n"
-              "\n"
-              "PROTEUS 3:3 MM LZ\n"
-              "DipTrace 5:2 MM TZ\n"
-              "DipTrace 4:3 MM LZ\n"
-              "\n"
-              "EAGLE 3:3 MM TZ\n"
-              "EAGLE 4:3 MM TZ\n"
-              "EAGLE 2:5 INCH TZ\n"
-              "EAGLE 3:5 INCH TZ\n"
-              "\n"
-              "ALTIUM 2:4 INCH LZ\n"
-              "Sprint Layout 2:4 INCH LZ"
-              "\n"
-              "KiCAD 3:5 INCH TZ")
-        )
-        grid2.addWidget(self.excellon_format_label, 0, 0, 1, 2)
-
-        self.excellon_format_in_label = QtWidgets.QLabel('%s:' % _("INCH"))
-        self.excellon_format_in_label.setToolTip(_("Default values for INCH are 2:4"))
-
-        hlay1 = QtWidgets.QHBoxLayout()
-        self.excellon_format_upper_in_entry = FCSpinner()
-        self.excellon_format_upper_in_entry.set_range(0, 9)
-        self.excellon_format_upper_in_entry.setMinimumWidth(30)
-        self.excellon_format_upper_in_entry.setToolTip(
-           _("This numbers signify the number of digits in\n"
-             "the whole part of Excellon coordinates.")
-        )
-        hlay1.addWidget(self.excellon_format_upper_in_entry)
-
-        excellon_separator_in_label = QtWidgets.QLabel(':')
-        excellon_separator_in_label.setFixedWidth(5)
-        hlay1.addWidget(excellon_separator_in_label)
-
-        self.excellon_format_lower_in_entry = FCSpinner()
-        self.excellon_format_lower_in_entry.set_range(0, 9)
-        self.excellon_format_lower_in_entry.setMinimumWidth(30)
-        self.excellon_format_lower_in_entry.setToolTip(
-            _("This numbers signify the number of digits in\n"
-              "the decimal part of Excellon coordinates.")
-        )
-        hlay1.addWidget(self.excellon_format_lower_in_entry)
-
-        grid2.addWidget(self.excellon_format_in_label, 1, 0)
-        grid2.addLayout(hlay1, 1, 1)
-
-        self.excellon_format_mm_label = QtWidgets.QLabel('%s:' % _("METRIC"))
-        self.excellon_format_mm_label.setToolTip(_("Default values for METRIC are 3:3"))
-
-        hlay2 = QtWidgets.QHBoxLayout()
-        self.excellon_format_upper_mm_entry = FCSpinner()
-        self.excellon_format_upper_mm_entry.set_range(0, 9)
-        self.excellon_format_upper_mm_entry.setMinimumWidth(30)
-        self.excellon_format_upper_mm_entry.setToolTip(
-            _("This numbers signify the number of digits in\n"
-              "the whole part of Excellon coordinates.")
-        )
-        hlay2.addWidget(self.excellon_format_upper_mm_entry)
-
-        excellon_separator_mm_label = QtWidgets.QLabel(':')
-        excellon_separator_mm_label.setFixedWidth(5)
-        hlay2.addWidget(excellon_separator_mm_label, QtCore.Qt.AlignLeft)
-
-        self.excellon_format_lower_mm_entry = FCSpinner()
-        self.excellon_format_lower_mm_entry.set_range(0, 9)
-        self.excellon_format_lower_mm_entry.setMinimumWidth(30)
-        self.excellon_format_lower_mm_entry.setToolTip(
-            _("This numbers signify the number of digits in\n"
-              "the decimal part of Excellon coordinates.")
-        )
-        hlay2.addWidget(self.excellon_format_lower_mm_entry)
-
-        grid2.addWidget(self.excellon_format_mm_label, 2, 0)
-        grid2.addLayout(hlay2, 2, 1)
-
-        self.excellon_zeros_label = QtWidgets.QLabel('%s:' % _('Zeros'))
-        self.excellon_zeros_label.setAlignment(QtCore.Qt.AlignLeft)
-        self.excellon_zeros_label.setToolTip(
-            _("This sets the type of Excellon zeros.\n"
-              "If LZ then Leading Zeros are kept and\n"
-              "Trailing Zeros are removed.\n"
-              "If TZ is checked then Trailing Zeros are kept\n"
-              "and Leading Zeros are removed.\n\n"
-              "This is used when there is no information\n"
-              "stored in the Excellon file.")
-        )
-        grid2.addWidget(self.excellon_zeros_label, 3, 0)
-
-        self.excellon_zeros_radio = RadioSet([{'label': _('LZ'), 'value': 'L'},
-                                              {'label': _('TZ'), 'value': 'T'}])
-
-        grid2.addWidget(self.excellon_zeros_radio, 3, 1)
-
-        self.excellon_units_label = QtWidgets.QLabel('%s:' % _('Units'))
-        self.excellon_units_label.setAlignment(QtCore.Qt.AlignLeft)
-        self.excellon_units_label.setToolTip(
-            _("This sets the default units of Excellon files.\n"
-              "If it is not detected in the parsed file the value here\n"
-              "will be used."
-              "Some Excellon files don't have an header\n"
-              "therefore this parameter will be used.")
-        )
-
-        self.excellon_units_radio = RadioSet([{'label': _('INCH'), 'value': 'INCH'},
-                                              {'label': _('MM'), 'value': 'METRIC'}])
-        self.excellon_units_radio.setToolTip(
-            _("This sets the units of Excellon files.\n"
-              "Some Excellon files don't have an header\n"
-              "therefore this parameter will be used.")
-        )
-
-        grid2.addWidget(self.excellon_units_label, 4, 0)
-        grid2.addWidget(self.excellon_units_radio, 4, 1)
-
-        self.update_excellon_cb = FCCheckBox(label=_('Update Export settings'))
-        self.update_excellon_cb.setToolTip(
-            "If checked, the Excellon Export settings will be updated with the ones above."
-        )
-        grid2.addWidget(self.update_excellon_cb, 5, 0, 1, 2)
-
-        # Adding the Excellon Format Defaults Button
-        self.excellon_defaults_button = QtWidgets.QPushButton()
-        self.excellon_defaults_button.setText(str(_("Restore Defaults")))
-        self.excellon_defaults_button.setMinimumWidth(80)
-        grid2.addWidget(self.excellon_defaults_button, 6, 0, 1, 2)
-
-        separator_line = QtWidgets.QFrame()
-        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
-        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
-        grid2.addWidget(separator_line, 7, 0, 1, 2)
-
-        self.excellon_general_label = QtWidgets.QLabel("<b>%s:</b>" % _("Excellon Optimization"))
-        grid2.addWidget(self.excellon_general_label, 8, 0, 1, 2)
-
-        self.excellon_optimization_label = QtWidgets.QLabel(_('Algorithm:'))
-        self.excellon_optimization_label.setToolTip(
-            _("This sets the optimization type for the Excellon drill path.\n"
-              "If <<MetaHeuristic>> is checked then Google OR-Tools algorithm with\n"
-              "MetaHeuristic Guided Local Path is used. Default search time is 3sec.\n"
-              "If <<Basic>> is checked then Google OR-Tools Basic algorithm is used.\n"
-              "If <<TSA>> is checked then Travelling Salesman algorithm is used for\n"
-              "drill path optimization.\n"
-              "\n"
-              "If this control is disabled, then FlatCAM works in 32bit mode and it uses\n"
-              "Travelling Salesman algorithm for path optimization.")
-        )
-
-        self.excellon_optimization_radio = RadioSet([{'label': _('MetaHeuristic'), 'value': 'M'},
-                                                     {'label': _('Basic'), 'value': 'B'},
-                                                     {'label': _('TSA'), 'value': 'T'}],
-                                                    orientation='vertical', stretch=False)
-        self.excellon_optimization_radio.setToolTip(
-            _("This sets the optimization type for the Excellon drill path.\n"
-              "If <<MetaHeuristic>> is checked then Google OR-Tools algorithm with\n"
-              "MetaHeuristic Guided Local Path is used. Default search time is 3sec.\n"
-              "If <<Basic>> is checked then Google OR-Tools Basic algorithm is used.\n"
-              "If <<TSA>> is checked then Travelling Salesman algorithm is used for\n"
-              "drill path optimization.\n"
-              "\n"
-              "If this control is disabled, then FlatCAM works in 32bit mode and it uses\n"
-              "Travelling Salesman algorithm for path optimization.")
-        )
-
-        grid2.addWidget(self.excellon_optimization_label, 9, 0)
-        grid2.addWidget(self.excellon_optimization_radio, 9, 1)
-
-        self.optimization_time_label = QtWidgets.QLabel('%s:' % _('Duration'))
-        self.optimization_time_label.setAlignment(QtCore.Qt.AlignLeft)
-        self.optimization_time_label.setToolTip(
-            _("When OR-Tools Metaheuristic (MH) is enabled there is a\n"
-              "maximum threshold for how much time is spent doing the\n"
-              "path optimization. This max duration is set here.\n"
-              "In seconds.")
-
-        )
-
-        self.optimization_time_entry = FCSpinner()
-        self.optimization_time_entry.set_range(0, 999)
-
-        grid2.addWidget(self.optimization_time_label, 10, 0)
-        grid2.addWidget(self.optimization_time_entry, 10, 1)
-
-        separator_line = QtWidgets.QFrame()
-        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
-        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
-        grid2.addWidget(separator_line, 11, 0, 1, 2)
-
-        # Excellon Object Color
-        self.gerber_color_label = QtWidgets.QLabel('<b>%s</b>' % _('Excellon Object Color'))
-        grid2.addWidget(self.gerber_color_label, 12, 0, 1, 2)
-
-        # Plot Line Color
-        self.line_color_label = QtWidgets.QLabel('%s:' % _('Outline'))
-        self.line_color_label.setToolTip(
-            _("Set the line color for plotted objects.")
-        )
-        self.line_color_entry = FCEntry()
-        self.line_color_button = QtWidgets.QPushButton()
-        self.line_color_button.setFixedSize(15, 15)
-
-        self.form_box_child_2 = QtWidgets.QHBoxLayout()
-        self.form_box_child_2.addWidget(self.line_color_entry)
-        self.form_box_child_2.addWidget(self.line_color_button)
-        self.form_box_child_2.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
-
-        grid2.addWidget(self.line_color_label, 13, 0)
-        grid2.addLayout(self.form_box_child_2, 13, 1)
-
-        # Plot Fill Color
-        self.fill_color_label = QtWidgets.QLabel('%s:' % _('Fill'))
-        self.fill_color_label.setToolTip(
-            _("Set the fill color for plotted objects.\n"
-              "First 6 digits are the color and the last 2\n"
-              "digits are for alpha (transparency) level.")
-        )
-        self.fill_color_entry = FCEntry()
-        self.fill_color_button = QtWidgets.QPushButton()
-        self.fill_color_button.setFixedSize(15, 15)
-
-        self.form_box_child_1 = QtWidgets.QHBoxLayout()
-        self.form_box_child_1.addWidget(self.fill_color_entry)
-        self.form_box_child_1.addWidget(self.fill_color_button)
-        self.form_box_child_1.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
-
-        grid2.addWidget(self.fill_color_label, 14, 0)
-        grid2.addLayout(self.form_box_child_1, 14, 1)
-
-        # Plot Fill Transparency Level
-        self.alpha_label = QtWidgets.QLabel('%s:' % _('Alpha'))
-        self.alpha_label.setToolTip(
-            _("Set the fill transparency for plotted objects.")
-        )
-        self.color_alpha_slider = QtWidgets.QSlider(QtCore.Qt.Horizontal)
-        self.color_alpha_slider.setMinimum(0)
-        self.color_alpha_slider.setMaximum(255)
-        self.color_alpha_slider.setSingleStep(1)
-
-        self.color_alpha_spinner = FCSpinner()
-        self.color_alpha_spinner.setMinimumWidth(70)
-        self.color_alpha_spinner.set_range(0, 255)
-
-        self.form_box_child_3 = QtWidgets.QHBoxLayout()
-        self.form_box_child_3.addWidget(self.color_alpha_slider)
-        self.form_box_child_3.addWidget(self.color_alpha_spinner)
-
-        grid2.addWidget(self.alpha_label, 15, 0)
-        grid2.addLayout(self.form_box_child_3, 15, 1)
-
-        self.layout.addStretch()
-
-        current_platform = platform.architecture()[0]
-        if current_platform == '64bit':
-            self.excellon_optimization_label.setDisabled(False)
-            self.excellon_optimization_radio.setDisabled(False)
-            self.optimization_time_label.setDisabled(False)
-            self.optimization_time_entry.setDisabled(False)
-            self.excellon_optimization_radio.activated_custom.connect(self.optimization_selection)
-
-        else:
-            self.excellon_optimization_label.setDisabled(True)
-            self.excellon_optimization_radio.setDisabled(True)
-            self.optimization_time_label.setDisabled(True)
-            self.optimization_time_entry.setDisabled(True)
-
-        # Setting plot colors signals
-        self.line_color_entry.editingFinished.connect(self.on_line_color_entry)
-        self.line_color_button.clicked.connect(self.on_line_color_button)
-        self.fill_color_entry.editingFinished.connect(self.on_fill_color_entry)
-        self.fill_color_button.clicked.connect(self.on_fill_color_button)
-        self.color_alpha_spinner.valueChanged.connect(self.on_color_spinner)
-        self.color_alpha_slider.valueChanged.connect(self.on_color_slider)
-
-        # Load the defaults values into the Excellon Format and Excellon Zeros fields
-        self.excellon_defaults_button.clicked.connect(self.on_excellon_defaults_button)
-
-    def optimization_selection(self):
-        if self.excellon_optimization_radio.get_value() == 'M':
-            self.optimization_time_label.setDisabled(False)
-            self.optimization_time_entry.setDisabled(False)
-        else:
-            self.optimization_time_label.setDisabled(True)
-            self.optimization_time_entry.setDisabled(True)
-
-    # Setting plot colors handlers
-    def on_fill_color_entry(self):
-        self.app.defaults['excellon_plot_fill'] = self.fill_color_entry.get_value()[:7] + \
-            self.app.defaults['excellon_plot_fill'][7:9]
-        self.fill_color_button.setStyleSheet("background-color:%s" % str(self.app.defaults['excellon_plot_fill'])[:7])
-
-    def on_fill_color_button(self):
-        current_color = QtGui.QColor(self.app.defaults['excellon_plot_fill'][:7])
-
-        c_dialog = QtWidgets.QColorDialog()
-        plot_fill_color = c_dialog.getColor(initial=current_color)
-
-        if plot_fill_color.isValid() is False:
-            return
-
-        self.fill_color_button.setStyleSheet("background-color:%s" % str(plot_fill_color.name()))
-
-        new_val = str(plot_fill_color.name()) + str(self.app.defaults['excellon_plot_fill'][7:9])
-        self.fill_color_entry.set_value(new_val)
-        self.app.defaults['excellon_plot_fill'] = new_val
-
-    def on_color_spinner(self):
-        spinner_value = self.color_alpha_spinner.value()
-        self.color_alpha_slider.setValue(spinner_value)
-        self.app.defaults['excellon_plot_fill'] = \
-            self.app.defaults['excellon_plot_fill'][:7] + \
-            (hex(spinner_value)[2:] if int(hex(spinner_value)[2:], 16) > 0 else '00')
-        self.app.defaults['excellon_plot_line'] = \
-            self.app.defaults['excellon_plot_line'][:7] + \
-            (hex(spinner_value)[2:] if int(hex(spinner_value)[2:], 16) > 0 else '00')
-
-    def on_color_slider(self):
-        slider_value = self.color_alpha_slider.value()
-        self.color_alpha_spinner.setValue(slider_value)
-
-    def on_line_color_entry(self):
-        self.app.defaults['excellon_plot_line'] = self.line_color_entry.get_value()[:7] + \
-                                                self.app.defaults['excellon_plot_line'][7:9]
-        self.line_color_button.setStyleSheet("background-color:%s" % str(self.app.defaults['excellon_plot_line'])[:7])
-
-    def on_line_color_button(self):
-        current_color = QtGui.QColor(self.app.defaults['excellon_plot_line'][:7])
-        # print(current_color)
-
-        c_dialog = QtWidgets.QColorDialog()
-        plot_line_color = c_dialog.getColor(initial=current_color)
-
-        if plot_line_color.isValid() is False:
-            return
-
-        self.line_color_button.setStyleSheet("background-color:%s" % str(plot_line_color.name()))
-
-        new_val_line = str(plot_line_color.name()) + str(self.app.defaults['excellon_plot_line'][7:9])
-        self.line_color_entry.set_value(new_val_line)
-        self.app.defaults['excellon_plot_line'] = new_val_line
-
-    def on_excellon_defaults_button(self):
-        self.app.preferencesUiManager.defaults_form_fields["excellon_format_lower_in"].set_value('4')
-        self.app.preferencesUiManager.defaults_form_fields["excellon_format_upper_in"].set_value('2')
-        self.app.preferencesUiManager.defaults_form_fields["excellon_format_lower_mm"].set_value('3')
-        self.app.preferencesUiManager.defaults_form_fields["excellon_format_upper_mm"].set_value('3')
-        self.app.preferencesUiManager.defaults_form_fields["excellon_zeros"].set_value('L')
-        self.app.preferencesUiManager.defaults_form_fields["excellon_units"].set_value('INCH')
-
-
-class ExcellonOptPrefGroupUI(OptionsGroupUI):
-
-    def __init__(self, decimals=4, parent=None):
-        # OptionsGroupUI.__init__(self, "Excellon Options", parent=parent)
-        super(ExcellonOptPrefGroupUI, self).__init__(self)
-
-        self.setTitle(str(_("Excellon Options")))
-        self.decimals = decimals
-
-        # ## Create CNC Job
-        self.cncjob_label = QtWidgets.QLabel('<b>%s</b>' % _('Create CNC Job'))
-        self.cncjob_label.setToolTip(
-            _("Parameters used to create a CNC Job object\n"
-              "for this drill object.")
-        )
-        self.layout.addWidget(self.cncjob_label)
-
-        grid2 = QtWidgets.QGridLayout()
-        self.layout.addLayout(grid2)
-        grid2.setColumnStretch(0, 0)
-        grid2.setColumnStretch(1, 1)
-
-        # Operation Type
-        self.operation_label = QtWidgets.QLabel('<b>%s:</b>' % _('Operation'))
-        self.operation_label.setToolTip(
-            _("Operation type:\n"
-              "- Drilling -> will drill the drills/slots associated with this tool\n"
-              "- Milling -> will mill the drills/slots")
-        )
-        self.operation_radio = RadioSet(
-            [
-                {'label': _('Drilling'), 'value': 'drill'},
-                {'label': _("Milling"), 'value': 'mill'}
-            ]
-        )
-
-        grid2.addWidget(self.operation_label, 0, 0)
-        grid2.addWidget(self.operation_radio, 0, 1)
-
-        self.mill_type_label = QtWidgets.QLabel('%s:' % _('Milling Type'))
-        self.mill_type_label.setToolTip(
-            _("Milling type:\n"
-              "- Drills -> will mill the drills associated with this tool\n"
-              "- Slots -> will mill the slots associated with this tool\n"
-              "- Both -> will mill both drills and mills or whatever is available")
-        )
-        self.milling_type_radio = RadioSet(
-            [
-                {'label': _('Drills'), 'value': 'drills'},
-                {'label': _("Slots"), 'value': 'slots'},
-                {'label': _("Both"), 'value': 'both'},
-            ]
-        )
-
-        grid2.addWidget(self.mill_type_label, 1, 0)
-        grid2.addWidget(self.milling_type_radio, 1, 1)
-
-        self.mill_dia_label = QtWidgets.QLabel('%s:' % _('Milling Diameter'))
-        self.mill_dia_label.setToolTip(
-            _("The diameter of the tool who will do the milling")
-        )
-
-        self.mill_dia_entry = FCDoubleSpinner()
-        self.mill_dia_entry.set_precision(self.decimals)
-        self.mill_dia_entry.set_range(0.0000, 9999.9999)
-
-        grid2.addWidget(self.mill_dia_label, 2, 0)
-        grid2.addWidget(self.mill_dia_entry, 2, 1)
-
-        # Cut Z
-        cutzlabel = QtWidgets.QLabel('%s:' % _('Cut Z'))
-        cutzlabel.setToolTip(
-            _("Drill depth (negative)\n"
-              "below the copper surface.")
-        )
-
-        self.cutz_entry = FCDoubleSpinner()
-
-        if machinist_setting == 0:
-            self.cutz_entry.set_range(-9999.9999, 0.0000)
-        else:
-            self.cutz_entry.set_range(-9999.9999, 9999.9999)
-
-        self.cutz_entry.setSingleStep(0.1)
-        self.cutz_entry.set_precision(self.decimals)
-
-        grid2.addWidget(cutzlabel, 3, 0)
-        grid2.addWidget(self.cutz_entry, 3, 1)
-
-        # Multi-Depth
-        self.mpass_cb = FCCheckBox('%s:' % _("Multi-Depth"))
-        self.mpass_cb.setToolTip(
-            _(
-                "Use multiple passes to limit\n"
-                "the cut depth in each pass. Will\n"
-                "cut multiple times until Cut Z is\n"
-                "reached."
-            )
-        )
-
-        self.maxdepth_entry = FCDoubleSpinner()
-        self.maxdepth_entry.set_precision(self.decimals)
-        self.maxdepth_entry.set_range(0, 9999.9999)
-        self.maxdepth_entry.setSingleStep(0.1)
-
-        self.maxdepth_entry.setToolTip(_("Depth of each pass (positive)."))
-
-        grid2.addWidget(self.mpass_cb, 4, 0)
-        grid2.addWidget(self.maxdepth_entry, 4, 1)
-
-        # Travel Z
-        travelzlabel = QtWidgets.QLabel('%s:' % _('Travel Z'))
-        travelzlabel.setToolTip(
-            _("Tool height when travelling\n"
-              "across the XY plane.")
-        )
-
-        self.travelz_entry = FCDoubleSpinner()
-        self.travelz_entry.set_precision(self.decimals)
-
-        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(travelzlabel, 5, 0)
-        grid2.addWidget(self.travelz_entry, 5, 1)
-
-        # Tool change:
-        self.toolchange_cb = FCCheckBox('%s' % _("Tool change"))
-        self.toolchange_cb.setToolTip(
-            _("Include tool-change sequence\n"
-              "in G-Code (Pause for tool change).")
-        )
-        grid2.addWidget(self.toolchange_cb, 6, 0, 1, 2)
-
-        # Tool Change Z
-        toolchangezlabel = QtWidgets.QLabel('%s:' % _('Toolchange Z'))
-        toolchangezlabel.setToolTip(
-            _("Z-axis position (height) for\n"
-              "tool change.")
-        )
-
-        self.toolchangez_entry = FCDoubleSpinner()
-        self.toolchangez_entry.set_precision(self.decimals)
-
-        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(toolchangezlabel, 7, 0)
-        grid2.addWidget(self.toolchangez_entry, 7, 1)
-
-        # End Move Z
-        endz_label = QtWidgets.QLabel('%s:' % _('End move Z'))
-        endz_label.setToolTip(
-            _("Height of the tool after\n"
-              "the last move at the end of the job.")
-        )
-        self.endz_entry = FCDoubleSpinner()
-        self.endz_entry.set_precision(self.decimals)
-
-        if machinist_setting == 0:
-            self.endz_entry.set_range(0.0000, 9999.9999)
-        else:
-            self.endz_entry.set_range(-9999.9999, 9999.9999)
-
-        grid2.addWidget(endz_label, 8, 0)
-        grid2.addWidget(self.endz_entry, 8, 1)
-
-        # End Move X,Y
-        endmove_xy_label = QtWidgets.QLabel('%s:' % _('End move X,Y'))
-        endmove_xy_label.setToolTip(
-            _("End move X,Y position. In format (x,y).\n"
-              "If no value is entered then there is no move\n"
-              "on X,Y plane at the end of the job.")
-        )
-        self.endxy_entry = FCEntry()
-
-        grid2.addWidget(endmove_xy_label, 9, 0)
-        grid2.addWidget(self.endxy_entry, 9, 1)
-
-        # Feedrate Z
-        frlabel = QtWidgets.QLabel('%s:' % _('Feedrate Z'))
-        frlabel.setToolTip(
-            _("Tool speed while drilling\n"
-              "(in units per minute).\n"
-              "So called 'Plunge' feedrate.\n"
-              "This is for linear move G01.")
-        )
-        self.feedrate_z_entry = FCDoubleSpinner()
-        self.feedrate_z_entry.set_precision(self.decimals)
-        self.feedrate_z_entry.set_range(0, 99999.9999)
-
-        grid2.addWidget(frlabel, 10, 0)
-        grid2.addWidget(self.feedrate_z_entry, 10, 1)
-
-        # Spindle speed
-        spdlabel = QtWidgets.QLabel('%s:' % _('Spindle Speed'))
-        spdlabel.setToolTip(
-            _("Speed of the spindle\n"
-              "in RPM (optional)")
-        )
-
-        self.spindlespeed_entry = FCSpinner()
-        self.spindlespeed_entry.set_range(0, 1000000)
-        self.spindlespeed_entry.set_step(100)
-
-        grid2.addWidget(spdlabel, 11, 0)
-        grid2.addWidget(self.spindlespeed_entry, 11, 1)
-
-        # Dwell
-        self.dwell_cb = FCCheckBox('%s' % _('Enable Dwell'))
-        self.dwell_cb .setToolTip(
-            _("Pause to allow the spindle to reach its\n"
-              "speed before cutting.")
-        )
-
-        grid2.addWidget(self.dwell_cb, 12, 0, 1, 2)
-
-        # Dwell Time
-        dwelltime = QtWidgets.QLabel('%s:' % _('Duration'))
-        dwelltime.setToolTip(_("Number of time units for spindle to dwell."))
-        self.dwelltime_entry = FCDoubleSpinner()
-        self.dwelltime_entry.set_precision(self.decimals)
-        self.dwelltime_entry.set_range(0, 99999.9999)
-
-        grid2.addWidget(dwelltime, 13, 0)
-        grid2.addWidget(self.dwelltime_entry, 13, 1)
-
-        self.ois_dwell_exc = OptionalInputSection(self.dwell_cb, [self.dwelltime_entry])
-
-        # preprocessor selection
-        pp_excellon_label = QtWidgets.QLabel('%s:' % _("Preprocessor"))
-        pp_excellon_label.setToolTip(
-            _("The preprocessor JSON file that dictates\n"
-              "Gcode output.")
-        )
-
-        self.pp_excellon_name_cb = FCComboBox()
-        self.pp_excellon_name_cb.setFocusPolicy(Qt.StrongFocus)
-
-        grid2.addWidget(pp_excellon_label, 14, 0)
-        grid2.addWidget(self.pp_excellon_name_cb, 14, 1)
-
-        # ### Choose what to use for Gcode creation: Drills, Slots or Both
-        excellon_gcode_type_label = QtWidgets.QLabel('<b>%s</b>' % _('Gcode'))
-        excellon_gcode_type_label.setToolTip(
-            _("Choose what to use for GCode generation:\n"
-              "'Drills', 'Slots' or 'Both'.\n"
-              "When choosing 'Slots' or 'Both', slots will be\n"
-              "converted to drills.")
-        )
-        self.excellon_gcode_type_radio = RadioSet([{'label': 'Drills', 'value': 'drills'},
-                                                   {'label': 'Slots', 'value': 'slots'},
-                                                   {'label': 'Both', 'value': 'both'}])
-        grid2.addWidget(excellon_gcode_type_label, 15, 0)
-        grid2.addWidget(self.excellon_gcode_type_radio, 15, 1)
-
-        # until I decide to implement this feature those remain disabled
-        excellon_gcode_type_label.hide()
-        self.excellon_gcode_type_radio.setVisible(False)
-
-        # ### Milling Holes ## ##
-        self.mill_hole_label = QtWidgets.QLabel('<b>%s</b>' % _('Mill Holes'))
-        self.mill_hole_label.setToolTip(
-            _("Create Geometry for milling holes.")
-        )
-        grid2.addWidget(self.mill_hole_label, 16, 0, 1, 2)
-
-        tdlabel = QtWidgets.QLabel('%s:' % _('Drill Tool dia'))
-        tdlabel.setToolTip(
-            _("Diameter of the cutting tool.")
-        )
-        self.tooldia_entry = FCDoubleSpinner()
-        self.tooldia_entry.set_precision(self.decimals)
-        self.tooldia_entry.set_range(0, 999.9999)
-
-        grid2.addWidget(tdlabel, 18, 0)
-        grid2.addWidget(self.tooldia_entry, 18, 1)
-
-        stdlabel = QtWidgets.QLabel('%s:' % _('Slot Tool dia'))
-        stdlabel.setToolTip(
-            _("Diameter of the cutting tool\n"
-              "when milling slots.")
-        )
-        self.slot_tooldia_entry = FCDoubleSpinner()
-        self.slot_tooldia_entry.set_precision(self.decimals)
-        self.slot_tooldia_entry.set_range(0, 999.9999)
-
-        grid2.addWidget(stdlabel, 21, 0)
-        grid2.addWidget(self.slot_tooldia_entry, 21, 1)
-
-        self.layout.addStretch()
-
-
-class ExcellonAdvOptPrefGroupUI(OptionsGroupUI):
-
-    def __init__(self, decimals=4, parent=None):
-        # OptionsGroupUI.__init__(self, "Excellon Advanced Options", parent=parent)
-        super(ExcellonAdvOptPrefGroupUI, self).__init__(self)
-
-        self.setTitle(str(_("Excellon Adv. Options")))
-        self.decimals = decimals
-
-        # #######################
-        # ## ADVANCED OPTIONS ###
-        # #######################
-
-        self.exc_label = QtWidgets.QLabel('<b>%s:</b>' % _('Advanced Options'))
-        self.exc_label.setToolTip(
-            _("A list of Excellon advanced parameters.\n"
-              "Those parameters are available only for\n"
-              "Advanced App. Level.")
-        )
-        self.layout.addWidget(self.exc_label)
-
-        grid1 = QtWidgets.QGridLayout()
-        self.layout.addLayout(grid1)
-
-        # Offset Z
-        offsetlabel = QtWidgets.QLabel('%s:' % _('Offset Z'))
-        offsetlabel.setToolTip(
-            _("Some drill bits (the larger ones) need to drill deeper\n"
-              "to create the desired exit hole diameter due of the tip shape.\n"
-              "The value here can compensate the Cut Z parameter."))
-        self.offset_entry = FCDoubleSpinner()
-        self.offset_entry.set_precision(self.decimals)
-        self.offset_entry.set_range(-999.9999, 999.9999)
-
-        grid1.addWidget(offsetlabel, 0, 0)
-        grid1.addWidget(self.offset_entry, 0, 1)
-
-        # ToolChange X,Y
-        toolchange_xy_label = QtWidgets.QLabel('%s:' % _('Toolchange X,Y'))
-        toolchange_xy_label.setToolTip(
-            _("Toolchange X,Y position.")
-        )
-        self.toolchangexy_entry = FCEntry()
-
-        grid1.addWidget(toolchange_xy_label, 1, 0)
-        grid1.addWidget(self.toolchangexy_entry, 1, 1)
-
-        # Start Z
-        startzlabel = QtWidgets.QLabel('%s:' % _('Start Z'))
-        startzlabel.setToolTip(
-            _("Height of the tool just after start.\n"
-              "Delete the value if you don't need this feature.")
-        )
-        self.estartz_entry = FloatEntry()
-
-        grid1.addWidget(startzlabel, 2, 0)
-        grid1.addWidget(self.estartz_entry, 2, 1)
-
-        # Feedrate Rapids
-        fr_rapid_label = QtWidgets.QLabel('%s:' % _('Feedrate Rapids'))
-        fr_rapid_label.setToolTip(
-            _("Tool speed while drilling\n"
-              "(in units per minute).\n"
-              "This is for the rapid move G00.\n"
-              "It is useful only for Marlin,\n"
-              "ignore for any other cases.")
-        )
-        self.feedrate_rapid_entry = FCDoubleSpinner()
-        self.feedrate_rapid_entry.set_precision(self.decimals)
-        self.feedrate_rapid_entry.set_range(0, 99999.9999)
-
-        grid1.addWidget(fr_rapid_label, 3, 0)
-        grid1.addWidget(self.feedrate_rapid_entry, 3, 1)
-
-        # Probe depth
-        self.pdepth_label = QtWidgets.QLabel('%s:' % _("Probe Z depth"))
-        self.pdepth_label.setToolTip(
-            _("The maximum depth that the probe is allowed\n"
-              "to probe. Negative value, in current units.")
-        )
-        self.pdepth_entry = FCDoubleSpinner()
-        self.pdepth_entry.set_precision(self.decimals)
-        self.pdepth_entry.set_range(-99999.9999, 0.0000)
-
-        grid1.addWidget(self.pdepth_label, 4, 0)
-        grid1.addWidget(self.pdepth_entry, 4, 1)
-
-        # Probe feedrate
-        self.feedrate_probe_label = QtWidgets.QLabel('%s:' % _("Feedrate Probe"))
-        self.feedrate_probe_label.setToolTip(
-           _("The feedrate used while the probe is probing.")
-        )
-        self.feedrate_probe_entry = FCDoubleSpinner()
-        self.feedrate_probe_entry.set_precision(self.decimals)
-        self.feedrate_probe_entry.set_range(0, 99999.9999)
-
-        grid1.addWidget(self.feedrate_probe_label, 5, 0)
-        grid1.addWidget(self.feedrate_probe_entry, 5, 1)
-
-        # Spindle direction
-        spindle_dir_label = QtWidgets.QLabel('%s:' % _('Spindle direction'))
-        spindle_dir_label.setToolTip(
-            _("This sets the direction that the spindle is rotating.\n"
-              "It can be either:\n"
-              "- CW = clockwise or\n"
-              "- CCW = counter clockwise")
-        )
-
-        self.spindledir_radio = RadioSet([{'label': _('CW'), 'value': 'CW'},
-                                          {'label': _('CCW'), 'value': 'CCW'}])
-        grid1.addWidget(spindle_dir_label, 6, 0)
-        grid1.addWidget(self.spindledir_radio, 6, 1)
-
-        self.fplunge_cb = FCCheckBox('%s' % _('Fast Plunge'))
-        self.fplunge_cb.setToolTip(
-            _("By checking this, the vertical move from\n"
-              "Z_Toolchange to Z_move is done with G0,\n"
-              "meaning the fastest speed available.\n"
-              "WARNING: the move is done at Toolchange X,Y coords.")
-        )
-        grid1.addWidget(self.fplunge_cb, 7, 0, 1, 2)
-
-        self.fretract_cb = FCCheckBox('%s' % _('Fast Retract'))
-        self.fretract_cb.setToolTip(
-            _("Exit hole strategy.\n"
-              " - When uncheked, while exiting the drilled hole the drill bit\n"
-              "will travel slow, with set feedrate (G1), up to zero depth and then\n"
-              "travel as fast as possible (G0) to the Z Move (travel height).\n"
-              " - When checked the travel from Z cut (cut depth) to Z_move\n"
-              "(travel height) is done as fast as possible (G0) in one move.")
-        )
-
-        grid1.addWidget(self.fretract_cb, 8, 0, 1, 2)
-
-        self.layout.addStretch()
-
-
-class ExcellonExpPrefGroupUI(OptionsGroupUI):
-
-    def __init__(self, decimals=4, parent=None):
-        super(ExcellonExpPrefGroupUI, self).__init__(self)
-
-        self.setTitle(str(_("Excellon Export")))
-        self.decimals = decimals
-
-        # Plot options
-        self.export_options_label = QtWidgets.QLabel("<b>%s:</b>" % _("Export Options"))
-        self.export_options_label.setToolTip(
-            _("The parameters set here are used in the file exported\n"
-              "when using the File -> Export -> Export Excellon menu entry.")
-        )
-        self.layout.addWidget(self.export_options_label)
-
-        form = QtWidgets.QFormLayout()
-        self.layout.addLayout(form)
-
-        # Excellon Units
-        self.excellon_units_label = QtWidgets.QLabel('%s:' % _('Units'))
-        self.excellon_units_label.setToolTip(
-            _("The units used in the Excellon file.")
-        )
-
-        self.excellon_units_radio = RadioSet([{'label': _('INCH'), 'value': 'INCH'},
-                                              {'label': _('MM'), 'value': 'METRIC'}])
-        self.excellon_units_radio.setToolTip(
-            _("The units used in the Excellon file.")
-        )
-
-        form.addRow(self.excellon_units_label, self.excellon_units_radio)
-
-        # Excellon non-decimal format
-        self.digits_label = QtWidgets.QLabel("%s:" % _("Int/Decimals"))
-        self.digits_label.setToolTip(
-            _("The NC drill files, usually named Excellon files\n"
-              "are files that can be found in different formats.\n"
-              "Here we set the format used when the provided\n"
-              "coordinates are not using period.")
-        )
-
-        hlay1 = QtWidgets.QHBoxLayout()
-
-        self.format_whole_entry = FCSpinner()
-        self.format_whole_entry.set_range(0, 9)
-        self.format_whole_entry.setMinimumWidth(30)
-        self.format_whole_entry.setToolTip(
-            _("This numbers signify the number of digits in\n"
-              "the whole part of Excellon coordinates.")
-        )
-        hlay1.addWidget(self.format_whole_entry, QtCore.Qt.AlignLeft)
-
-        excellon_separator_label = QtWidgets.QLabel(':')
-        excellon_separator_label.setFixedWidth(5)
-        hlay1.addWidget(excellon_separator_label, QtCore.Qt.AlignLeft)
-
-        self.format_dec_entry = FCSpinner()
-        self.format_dec_entry.set_range(0, 9)
-        self.format_dec_entry.setMinimumWidth(30)
-        self.format_dec_entry.setToolTip(
-            _("This numbers signify the number of digits in\n"
-              "the decimal part of Excellon coordinates.")
-        )
-        hlay1.addWidget(self.format_dec_entry, QtCore.Qt.AlignLeft)
-        hlay1.addStretch()
-
-        form.addRow(self.digits_label, hlay1)
-
-        # Select the Excellon Format
-        self.format_label = QtWidgets.QLabel("%s:" % _("Format"))
-        self.format_label.setToolTip(
-            _("Select the kind of coordinates format used.\n"
-              "Coordinates can be saved with decimal point or without.\n"
-              "When there is no decimal point, it is required to specify\n"
-              "the number of digits for integer part and the number of decimals.\n"
-              "Also it will have to be specified if LZ = leading zeros are kept\n"
-              "or TZ = trailing zeros are kept.")
-        )
-        self.format_radio = RadioSet([{'label': _('Decimal'), 'value': 'dec'},
-                                      {'label': _('No-Decimal'), 'value': 'ndec'}])
-        self.format_radio.setToolTip(
-            _("Select the kind of coordinates format used.\n"
-              "Coordinates can be saved with decimal point or without.\n"
-              "When there is no decimal point, it is required to specify\n"
-              "the number of digits for integer part and the number of decimals.\n"
-              "Also it will have to be specified if LZ = leading zeros are kept\n"
-              "or TZ = trailing zeros are kept.")
-        )
-
-        form.addRow(self.format_label, self.format_radio)
-
-        # Excellon Zeros
-        self.zeros_label = QtWidgets.QLabel('%s:' % _('Zeros'))
-        self.zeros_label.setAlignment(QtCore.Qt.AlignLeft)
-        self.zeros_label.setToolTip(
-            _("This sets the type of Excellon zeros.\n"
-              "If LZ then Leading Zeros are kept and\n"
-              "Trailing Zeros are removed.\n"
-              "If TZ is checked then Trailing Zeros are kept\n"
-              "and Leading Zeros are removed.")
-        )
-
-        self.zeros_radio = RadioSet([{'label': _('LZ'), 'value': 'LZ'},
-                                     {'label': _('TZ'), 'value': 'TZ'}])
-        self.zeros_radio.setToolTip(
-            _("This sets the default type of Excellon zeros.\n"
-              "If LZ then Leading Zeros are kept and\n"
-              "Trailing Zeros are removed.\n"
-              "If TZ is checked then Trailing Zeros are kept\n"
-              "and Leading Zeros are removed.")
-        )
-
-        form.addRow(self.zeros_label, self.zeros_radio)
-
-        # Slot type
-        self.slot_type_label = QtWidgets.QLabel('%s:' % _('Slot type'))
-        self.slot_type_label.setAlignment(QtCore.Qt.AlignLeft)
-        self.slot_type_label.setToolTip(
-            _("This sets how the slots will be exported.\n"
-              "If ROUTED then the slots will be routed\n"
-              "using M15/M16 commands.\n"
-              "If DRILLED(G85) the slots will be exported\n"
-              "using the Drilled slot command (G85).")
-        )
-
-        self.slot_type_radio = RadioSet([{'label': _('Routed'), 'value': 'routing'},
-                                         {'label': _('Drilled(G85)'), 'value': 'drilling'}])
-        self.slot_type_radio.setToolTip(
-            _("This sets how the slots will be exported.\n"
-              "If ROUTED then the slots will be routed\n"
-              "using M15/M16 commands.\n"
-              "If DRILLED(G85) the slots will be exported\n"
-              "using the Drilled slot command (G85).")
-        )
-
-        form.addRow(self.slot_type_label, self.slot_type_radio)
-
-        self.layout.addStretch()
-        self.format_radio.activated_custom.connect(self.optimization_selection)
-
-    def optimization_selection(self):
-        if self.format_radio.get_value() == 'dec':
-            self.zeros_label.setDisabled(True)
-            self.zeros_radio.setDisabled(True)
-        else:
-            self.zeros_label.setDisabled(False)
-            self.zeros_radio.setDisabled(False)
-
-
-class ExcellonEditorPrefGroupUI(OptionsGroupUI):
-    def __init__(self, decimals=4, parent=None):
-        super(ExcellonEditorPrefGroupUI, self).__init__(self)
-
-        self.setTitle(str(_("Excellon Editor")))
-        self.decimals = decimals
-
-        # Excellon Editor Parameters
-        self.param_label = QtWidgets.QLabel("<b>%s:</b>" % _("Parameters"))
-        self.param_label.setToolTip(
-            _("A list of Excellon Editor parameters.")
-        )
-        self.layout.addWidget(self.param_label)
-
-        grid0 = QtWidgets.QGridLayout()
-        self.layout.addLayout(grid0)
-
-        # Selection Limit
-        self.sel_limit_label = QtWidgets.QLabel('%s:' % _("Selection limit"))
-        self.sel_limit_label.setToolTip(
-            _("Set the number of selected Excellon geometry\n"
-              "items above which the utility geometry\n"
-              "becomes just a selection rectangle.\n"
-              "Increases the performance when moving a\n"
-              "large number of geometric elements.")
-        )
-        self.sel_limit_entry = FCSpinner()
-        self.sel_limit_entry.set_range(0, 99999)
-
-        grid0.addWidget(self.sel_limit_label, 0, 0)
-        grid0.addWidget(self.sel_limit_entry, 0, 1)
-
-        # New Diameter
-        self.addtool_entry_lbl = QtWidgets.QLabel('%s:' % _('New Dia'))
-        self.addtool_entry_lbl.setToolTip(
-            _("Diameter for the new tool")
-        )
-
-        self.addtool_entry = FCDoubleSpinner()
-        self.addtool_entry.set_range(0.000001, 99.9999)
-        self.addtool_entry.set_precision(self.decimals)
-
-        grid0.addWidget(self.addtool_entry_lbl, 1, 0)
-        grid0.addWidget(self.addtool_entry, 1, 1)
-
-        # Number of drill holes in a drill array
-        self.drill_array_size_label = QtWidgets.QLabel('%s:' % _('Nr of drills'))
-        self.drill_array_size_label.setToolTip(
-            _("Specify how many drills to be in the array.")
-        )
-        # self.drill_array_size_label.setMinimumWidth(100)
-
-        self.drill_array_size_entry = FCSpinner()
-        self.drill_array_size_entry.set_range(0, 9999)
-
-        grid0.addWidget(self.drill_array_size_label, 2, 0)
-        grid0.addWidget(self.drill_array_size_entry, 2, 1)
-
-        self.drill_array_linear_label = QtWidgets.QLabel('<b>%s:</b>' % _('Linear Drill Array'))
-        grid0.addWidget(self.drill_array_linear_label, 3, 0, 1, 2)
-
-        # Linear Drill Array direction
-        self.drill_axis_label = QtWidgets.QLabel('%s:' % _('Linear Direction'))
-        self.drill_axis_label.setToolTip(
-            _("Direction on which the linear array is oriented:\n"
-              "- 'X' - horizontal axis \n"
-              "- 'Y' - vertical axis or \n"
-              "- 'Angle' - a custom angle for the array inclination")
-        )
-        # self.drill_axis_label.setMinimumWidth(100)
-        self.drill_axis_radio = RadioSet([{'label': _('X'), 'value': 'X'},
-                                          {'label': _('Y'), 'value': 'Y'},
-                                          {'label': _('Angle'), 'value': 'A'}])
-
-        grid0.addWidget(self.drill_axis_label, 4, 0)
-        grid0.addWidget(self.drill_axis_radio, 4, 1)
-
-        # Linear Drill Array pitch distance
-        self.drill_pitch_label = QtWidgets.QLabel('%s:' % _('Pitch'))
-        self.drill_pitch_label.setToolTip(
-            _("Pitch = Distance between elements of the array.")
-        )
-        # self.drill_pitch_label.setMinimumWidth(100)
-        self.drill_pitch_entry = FCDoubleSpinner()
-        self.drill_pitch_entry.set_range(0, 99999.9999)
-        self.drill_pitch_entry.set_precision(self.decimals)
-
-        grid0.addWidget(self.drill_pitch_label, 5, 0)
-        grid0.addWidget(self.drill_pitch_entry, 5, 1)
-
-        # Linear Drill Array custom angle
-        self.drill_angle_label = QtWidgets.QLabel('%s:' % _('Angle'))
-        self.drill_angle_label.setToolTip(
-            _("Angle at which each element in circular array is placed.")
-        )
-        self.drill_angle_entry = FCDoubleSpinner()
-        self.drill_pitch_entry.set_range(-360, 360)
-        self.drill_pitch_entry.set_precision(self.decimals)
-        self.drill_angle_entry.setWrapping(True)
-        self.drill_angle_entry.setSingleStep(5)
-
-        grid0.addWidget(self.drill_angle_label, 6, 0)
-        grid0.addWidget(self.drill_angle_entry, 6, 1)
-
-        self.drill_array_circ_label = QtWidgets.QLabel('<b>%s:</b>' % _('Circular Drill Array'))
-        grid0.addWidget(self.drill_array_circ_label, 7, 0, 1, 2)
-
-        # Circular Drill Array direction
-        self.drill_circular_direction_label = QtWidgets.QLabel('%s:' % _('Circular Direction'))
-        self.drill_circular_direction_label.setToolTip(
-            _("Direction for circular array.\n"
-              "Can be CW = clockwise or CCW = counter clockwise.")
-        )
-
-        self.drill_circular_dir_radio = RadioSet([{'label': _('CW'), 'value': 'CW'},
-                                                  {'label': _('CCW'), 'value': 'CCW'}])
-
-        grid0.addWidget(self.drill_circular_direction_label, 8, 0)
-        grid0.addWidget(self.drill_circular_dir_radio, 8, 1)
-
-        # Circular Drill Array Angle
-        self.drill_circular_angle_label = QtWidgets.QLabel('%s:' % _('Circular Angle'))
-        self.drill_circular_angle_label.setToolTip(
-            _("Angle at which each element in circular array is placed.")
-        )
-        self.drill_circular_angle_entry = FCDoubleSpinner()
-        self.drill_circular_angle_entry.set_range(-360, 360)
-        self.drill_circular_angle_entry.set_precision(self.decimals)
-        self.drill_circular_angle_entry.setWrapping(True)
-        self.drill_circular_angle_entry.setSingleStep(5)
-
-        grid0.addWidget(self.drill_circular_angle_label, 9, 0)
-        grid0.addWidget(self.drill_circular_angle_entry, 9, 1)
-
-        # ##### SLOTS #####
-        # #################
-        self.drill_array_circ_label = QtWidgets.QLabel('<b>%s:</b>' % _('Slots'))
-        grid0.addWidget(self.drill_array_circ_label, 10, 0, 1, 2)
-
-        # Slot length
-        self.slot_length_label = QtWidgets.QLabel('%s:' % _('Length'))
-        self.slot_length_label.setToolTip(
-            _("Length = The length of the slot.")
-        )
-        self.slot_length_label.setMinimumWidth(100)
-
-        self.slot_length_entry = FCDoubleSpinner()
-        self.slot_length_entry.set_range(0, 99999)
-        self.slot_length_entry.set_precision(self.decimals)
-        self.slot_length_entry.setWrapping(True)
-        self.slot_length_entry.setSingleStep(1)
-
-        grid0.addWidget(self.slot_length_label, 11, 0)
-        grid0.addWidget(self.slot_length_entry, 11, 1)
-
-        # Slot direction
-        self.slot_axis_label = QtWidgets.QLabel('%s:' % _('Direction'))
-        self.slot_axis_label.setToolTip(
-            _("Direction on which the slot is oriented:\n"
-              "- 'X' - horizontal axis \n"
-              "- 'Y' - vertical axis or \n"
-              "- 'Angle' - a custom angle for the slot inclination")
-        )
-        self.slot_axis_label.setMinimumWidth(100)
-
-        self.slot_axis_radio = RadioSet([{'label': _('X'), 'value': 'X'},
-                                         {'label': _('Y'), 'value': 'Y'},
-                                         {'label': _('Angle'), 'value': 'A'}])
-        grid0.addWidget(self.slot_axis_label, 12, 0)
-        grid0.addWidget(self.slot_axis_radio, 12, 1)
-
-        # Slot custom angle
-        self.slot_angle_label = QtWidgets.QLabel('%s:' % _('Angle'))
-        self.slot_angle_label.setToolTip(
-            _("Angle at which the slot is placed.\n"
-              "The precision is of max 2 decimals.\n"
-              "Min value is: -359.99 degrees.\n"
-              "Max value is:  360.00 degrees.")
-        )
-        self.slot_angle_label.setMinimumWidth(100)
-
-        self.slot_angle_spinner = FCDoubleSpinner()
-        self.slot_angle_spinner.set_precision(self.decimals)
-        self.slot_angle_spinner.setWrapping(True)
-        self.slot_angle_spinner.setRange(-359.99, 360.00)
-        self.slot_angle_spinner.setSingleStep(5)
-
-        grid0.addWidget(self.slot_angle_label, 13, 0)
-        grid0.addWidget(self.slot_angle_spinner, 13, 1)
-
-        # #### SLOTS ARRAY #######
-        # ########################
-
-        self.slot_array_linear_label = QtWidgets.QLabel('<b>%s:</b>' % _('Linear Slot Array'))
-        grid0.addWidget(self.slot_array_linear_label, 14, 0, 1, 2)
-
-        # Number of slot holes in a drill array
-        self.slot_array_size_label = QtWidgets.QLabel('%s:' % _('Nr of slots'))
-        self.drill_array_size_label.setToolTip(
-            _("Specify how many slots to be in the array.")
-        )
-        # self.slot_array_size_label.setMinimumWidth(100)
-
-        self.slot_array_size_entry = FCSpinner()
-        self.slot_array_size_entry.set_range(0, 999999)
-
-        grid0.addWidget(self.slot_array_size_label, 15, 0)
-        grid0.addWidget(self.slot_array_size_entry, 15, 1)
-
-        # Linear Slot Array direction
-        self.slot_array_axis_label = QtWidgets.QLabel('%s:' % _('Linear Direction'))
-        self.slot_array_axis_label.setToolTip(
-            _("Direction on which the linear array is oriented:\n"
-              "- 'X' - horizontal axis \n"
-              "- 'Y' - vertical axis or \n"
-              "- 'Angle' - a custom angle for the array inclination")
-        )
-        # self.slot_axis_label.setMinimumWidth(100)
-        self.slot_array_axis_radio = RadioSet([{'label': _('X'), 'value': 'X'},
-                                               {'label': _('Y'), 'value': 'Y'},
-                                               {'label': _('Angle'), 'value': 'A'}])
-
-        grid0.addWidget(self.slot_array_axis_label, 16, 0)
-        grid0.addWidget(self.slot_array_axis_radio, 16, 1)
-
-        # Linear Slot Array pitch distance
-        self.slot_array_pitch_label = QtWidgets.QLabel('%s:' % _('Pitch'))
-        self.slot_array_pitch_label.setToolTip(
-            _("Pitch = Distance between elements of the array.")
-        )
-        # self.drill_pitch_label.setMinimumWidth(100)
-        self.slot_array_pitch_entry = FCDoubleSpinner()
-        self.slot_array_pitch_entry.set_precision(self.decimals)
-        self.slot_array_pitch_entry.setWrapping(True)
-        self.slot_array_pitch_entry.setRange(0, 999999)
-        self.slot_array_pitch_entry.setSingleStep(1)
-
-        grid0.addWidget(self.slot_array_pitch_label, 17, 0)
-        grid0.addWidget(self.slot_array_pitch_entry, 17, 1)
-
-        # Linear Slot Array custom angle
-        self.slot_array_angle_label = QtWidgets.QLabel('%s:' % _('Angle'))
-        self.slot_array_angle_label.setToolTip(
-            _("Angle at which each element in circular array is placed.")
-        )
-        self.slot_array_angle_entry = FCDoubleSpinner()
-        self.slot_array_angle_entry.set_precision(self.decimals)
-        self.slot_array_angle_entry.setWrapping(True)
-        self.slot_array_angle_entry.setRange(-360, 360)
-        self.slot_array_angle_entry.setSingleStep(5)
-
-        grid0.addWidget(self.slot_array_angle_label, 18, 0)
-        grid0.addWidget(self.slot_array_angle_entry, 18, 1)
-
-        self.slot_array_circ_label = QtWidgets.QLabel('<b>%s:</b>' % _('Circular Slot Array'))
-        grid0.addWidget(self.slot_array_circ_label, 19, 0, 1, 2)
-
-        # Circular Slot Array direction
-        self.slot_array_circular_direction_label = QtWidgets.QLabel('%s:' % _('Circular Direction'))
-        self.slot_array_circular_direction_label.setToolTip(
-            _("Direction for circular array.\n"
-              "Can be CW = clockwise or CCW = counter clockwise.")
-        )
-
-        self.slot_array_circular_dir_radio = RadioSet([{'label': _('CW'), 'value': 'CW'},
-                                                       {'label': _('CCW'), 'value': 'CCW'}])
-
-        grid0.addWidget(self.slot_array_circular_direction_label, 20, 0)
-        grid0.addWidget(self.slot_array_circular_dir_radio, 20, 1)
-
-        # Circular Slot Array Angle
-        self.slot_array_circular_angle_label = QtWidgets.QLabel('%s:' % _('Circular Angle'))
-        self.slot_array_circular_angle_label.setToolTip(
-            _("Angle at which each element in circular array is placed.")
-        )
-        self.slot_array_circular_angle_entry = FCDoubleSpinner()
-        self.slot_array_circular_angle_entry.set_precision(self.decimals)
-        self.slot_array_circular_angle_entry.setWrapping(True)
-        self.slot_array_circular_angle_entry.setRange(-360, 360)
-        self.slot_array_circular_angle_entry.setSingleStep(5)
-
-        grid0.addWidget(self.slot_array_circular_angle_label, 21, 0)
-        grid0.addWidget(self.slot_array_circular_angle_entry, 21, 1)
-
-        self.layout.addStretch()
-
-
-class GeometryGenPrefGroupUI(OptionsGroupUI):
-    def __init__(self, decimals=4, parent=None):
-        # OptionsGroupUI.__init__(self, "Geometry General Preferences", parent=parent)
-        super(GeometryGenPrefGroupUI, self).__init__(self)
-
-        self.setTitle(str(_("Geometry General")))
-        self.decimals = decimals
-
-        # ## Plot options
-        self.plot_options_label = QtWidgets.QLabel("<b>%s:</b>" % _("Plot Options"))
-        self.layout.addWidget(self.plot_options_label)
-
-        # Plot CB
-        self.plot_cb = FCCheckBox(label=_('Plot'))
-        self.plot_cb.setToolTip(
-            _("Plot (show) this object.")
-        )
-        self.layout.addWidget(self.plot_cb)
-
-        grid0 = QtWidgets.QGridLayout()
-        self.layout.addLayout(grid0)
-        grid0.setColumnStretch(0, 0)
-        grid0.setColumnStretch(1, 1)
-
-        # Number of circle steps for circular aperture linear approximation
-        self.circle_steps_label = QtWidgets.QLabel('%s:' % _("Circle Steps"))
-        self.circle_steps_label.setToolTip(
-            _("The number of circle steps for <b>Geometry</b> \n"
-              "circle and arc shapes linear approximation.")
-        )
-        self.circle_steps_entry = FCSpinner()
-        self.circle_steps_entry.set_range(0, 999)
-
-        grid0.addWidget(self.circle_steps_label, 1, 0)
-        grid0.addWidget(self.circle_steps_entry, 1, 1)
-
-        # Tools
-        self.tools_label = QtWidgets.QLabel("<b>%s:</b>" % _("Tools"))
-        grid0.addWidget(self.tools_label, 2, 0, 1, 2)
-
-        # Tooldia
-        tdlabel = QtWidgets.QLabel('<b><font color="green">%s:</font></b>' % _('Tools Dia'))
-        tdlabel.setToolTip(
-            _("Diameters of the tools, separated by comma.\n"
-              "The value of the diameter has to use the dot decimals separator.\n"
-              "Valid values: 0.3, 1.0")
-        )
-        self.cnctooldia_entry = FCEntry()
-
-        grid0.addWidget(tdlabel, 3, 0)
-        grid0.addWidget(self.cnctooldia_entry, 3, 1)
-
-        separator_line = QtWidgets.QFrame()
-        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
-        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
-        grid0.addWidget(separator_line, 9, 0, 1, 2)
-
-        # Geometry Object Color
-        self.gerber_color_label = QtWidgets.QLabel('<b>%s</b>' % _('Geometry Object Color'))
-        grid0.addWidget(self.gerber_color_label, 10, 0, 1, 2)
-
-        # Plot Line Color
-        self.line_color_label = QtWidgets.QLabel('%s:' % _('Outline'))
-        self.line_color_label.setToolTip(
-            _("Set the line color for plotted objects.")
-        )
-        self.line_color_entry = FCEntry()
-        self.line_color_button = QtWidgets.QPushButton()
-        self.line_color_button.setFixedSize(15, 15)
-
-        self.form_box_child_2 = QtWidgets.QHBoxLayout()
-        self.form_box_child_2.addWidget(self.line_color_entry)
-        self.form_box_child_2.addWidget(self.line_color_button)
-        self.form_box_child_2.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
-
-        grid0.addWidget(self.line_color_label, 11, 0)
-        grid0.addLayout(self.form_box_child_2, 11, 1)
-
-        self.layout.addStretch()
-
-        # Setting plot colors signals
-        self.line_color_entry.editingFinished.connect(self.on_line_color_entry)
-        self.line_color_button.clicked.connect(self.on_line_color_button)
-
-    def on_line_color_entry(self):
-        self.app.defaults['geometry_plot_line'] = self.line_color_entry.get_value()[:7] + 'FF'
-        self.line_color_button.setStyleSheet("background-color:%s" % str(self.app.defaults['geometry_plot_line'])[:7])
-
-    def on_line_color_button(self):
-        current_color = QtGui.QColor(self.app.defaults['geometry_plot_line'][:7])
-        # print(current_color)
-
-        c_dialog = QtWidgets.QColorDialog()
-        plot_line_color = c_dialog.getColor(initial=current_color)
-
-        if plot_line_color.isValid() is False:
-            return
-
-        self.line_color_button.setStyleSheet("background-color:%s" % str(plot_line_color.name()))
-
-        new_val_line = str(plot_line_color.name()) + str(self.app.defaults['geometry_plot_line'][7:9])
-        self.line_color_entry.set_value(new_val_line)
-
-
-class GeometryOptPrefGroupUI(OptionsGroupUI):
-    def __init__(self, decimals=4, parent=None):
-        # OptionsGroupUI.__init__(self, "Geometry Options Preferences", parent=parent)
-        super(GeometryOptPrefGroupUI, self).__init__(self)
-
-        self.setTitle(str(_("Geometry Options")))
-        self.decimals = decimals
-
-        # ------------------------------
-        # ## Create CNC Job
-        # ------------------------------
-        self.cncjob_label = QtWidgets.QLabel('<b>%s:</b>' % _('Create CNC Job'))
-        self.cncjob_label.setToolTip(
-            _("Create a CNC Job object\n"
-              "tracing the contours of this\n"
-              "Geometry object.")
-        )
-        self.layout.addWidget(self.cncjob_label)
-
-        grid1 = QtWidgets.QGridLayout()
-        self.layout.addLayout(grid1)
-        grid1.setColumnStretch(0, 0)
-        grid1.setColumnStretch(1, 1)
-
-        # Cut Z
-        cutzlabel = QtWidgets.QLabel('%s:' % _('Cut Z'))
-        cutzlabel.setToolTip(
-            _("Cutting depth (negative)\n"
-              "below the copper surface.")
-        )
-        self.cutz_entry = FCDoubleSpinner()
-
-        if machinist_setting == 0:
-            self.cutz_entry.set_range(-9999.9999, 0.0000)
-        else:
-            self.cutz_entry.set_range(-9999.9999, 9999.9999)
-
-        self.cutz_entry.set_precision(self.decimals)
-        self.cutz_entry.setSingleStep(0.1)
-        self.cutz_entry.setWrapping(True)
-
-        grid1.addWidget(cutzlabel, 0, 0)
-        grid1.addWidget(self.cutz_entry, 0, 1)
-
-        # Multidepth CheckBox
-        self.multidepth_cb = FCCheckBox(label=_('Multi-Depth'))
-        self.multidepth_cb.setToolTip(
-            _(
-                "Use multiple passes to limit\n"
-                "the cut depth in each pass. Will\n"
-                "cut multiple times until Cut Z is\n"
-                "reached."
-            )
-        )
-        grid1.addWidget(self.multidepth_cb, 1, 0)
-
-        # Depth/pass
-        dplabel = QtWidgets.QLabel('%s:' % _('Depth/Pass'))
-        dplabel.setToolTip(
-            _("The depth to cut on each pass,\n"
-              "when multidepth is enabled.\n"
-              "It has positive value although\n"
-              "it is a fraction from the depth\n"
-              "which has negative value.")
-        )
-
-        self.depthperpass_entry = FCDoubleSpinner()
-        self.depthperpass_entry.set_range(0, 99999)
-        self.depthperpass_entry.set_precision(self.decimals)
-        self.depthperpass_entry.setSingleStep(0.1)
-        self.depthperpass_entry.setWrapping(True)
-
-        grid1.addWidget(dplabel, 2, 0)
-        grid1.addWidget(self.depthperpass_entry, 2, 1)
-
-        self.ois_multidepth = OptionalInputSection(self.multidepth_cb, [self.depthperpass_entry])
-
-        # Travel Z
-        travelzlabel = QtWidgets.QLabel('%s:' % _('Travel Z'))
-        travelzlabel.setToolTip(
-            _("Height of the tool when\n"
-              "moving without cutting.")
-        )
-        self.travelz_entry = FCDoubleSpinner()
-
-        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(self.decimals)
-        self.travelz_entry.setSingleStep(0.1)
-        self.travelz_entry.setWrapping(True)
-
-        grid1.addWidget(travelzlabel, 3, 0)
-        grid1.addWidget(self.travelz_entry, 3, 1)
-
-        # Tool change:
-        self.toolchange_cb = FCCheckBox('%s' % _("Tool change"))
-        self.toolchange_cb.setToolTip(
-            _(
-                "Include tool-change sequence\n"
-                "in the Machine Code (Pause for tool change)."
-            )
-        )
-        grid1.addWidget(self.toolchange_cb, 4, 0, 1, 2)
-
-        # Toolchange Z
-        toolchangezlabel = QtWidgets.QLabel('%s:' % _('Toolchange Z'))
-        toolchangezlabel.setToolTip(
-            _(
-                "Z-axis position (height) for\n"
-                "tool change."
-            )
-        )
-        self.toolchangez_entry = FCDoubleSpinner()
-
-        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(self.decimals)
-        self.toolchangez_entry.setSingleStep(0.1)
-        self.toolchangez_entry.setWrapping(True)
-
-        grid1.addWidget(toolchangezlabel, 5, 0)
-        grid1.addWidget(self.toolchangez_entry, 5, 1)
-
-        # End move Z
-        endz_label = QtWidgets.QLabel('%s:' % _('End move Z'))
-        endz_label.setToolTip(
-            _("Height of the tool after\n"
-              "the last move at the end of the job.")
-        )
-        self.endz_entry = FCDoubleSpinner()
-
-        if machinist_setting == 0:
-            self.endz_entry.set_range(0.000, 9999.9999)
-        else:
-            self.endz_entry.set_range(-9999.9999, 9999.9999)
-
-        self.endz_entry.set_precision(self.decimals)
-        self.endz_entry.setSingleStep(0.1)
-        self.endz_entry.setWrapping(True)
-
-        grid1.addWidget(endz_label, 6, 0)
-        grid1.addWidget(self.endz_entry, 6, 1)
-
-        # End Move X,Y
-        endmove_xy_label = QtWidgets.QLabel('%s:' % _('End move X,Y'))
-        endmove_xy_label.setToolTip(
-            _("End move X,Y position. In format (x,y).\n"
-              "If no value is entered then there is no move\n"
-              "on X,Y plane at the end of the job.")
-        )
-        self.endxy_entry = FCEntry()
-
-        grid1.addWidget(endmove_xy_label, 7, 0)
-        grid1.addWidget(self.endxy_entry, 7, 1)
-
-        # Feedrate X-Y
-        frlabel = QtWidgets.QLabel('%s:' % _('Feedrate X-Y'))
-        frlabel.setToolTip(
-            _("Cutting speed in the XY\n"
-              "plane in units per minute")
-        )
-        self.cncfeedrate_entry = FCDoubleSpinner()
-        self.cncfeedrate_entry.set_range(0, 99999.9999)
-        self.cncfeedrate_entry.set_precision(self.decimals)
-        self.cncfeedrate_entry.setSingleStep(0.1)
-        self.cncfeedrate_entry.setWrapping(True)
-
-        grid1.addWidget(frlabel, 8, 0)
-        grid1.addWidget(self.cncfeedrate_entry, 8, 1)
-
-        # Feedrate Z (Plunge)
-        frz_label = QtWidgets.QLabel('%s:' % _('Feedrate Z'))
-        frz_label.setToolTip(
-            _("Cutting speed in the XY\n"
-              "plane in units per minute.\n"
-              "It is called also Plunge.")
-        )
-        self.feedrate_z_entry = FCDoubleSpinner()
-        self.feedrate_z_entry.set_range(0, 99999.9999)
-        self.feedrate_z_entry.set_precision(self.decimals)
-        self.feedrate_z_entry.setSingleStep(0.1)
-        self.feedrate_z_entry.setWrapping(True)
-
-        grid1.addWidget(frz_label, 9, 0)
-        grid1.addWidget(self.feedrate_z_entry, 9, 1)
-
-        # Spindle Speed
-        spdlabel = QtWidgets.QLabel('%s:' % _('Spindle speed'))
-        spdlabel.setToolTip(
-            _(
-                "Speed of the spindle in RPM (optional).\n"
-                "If LASER preprocessor is used,\n"
-                "this value is the power of laser."
-            )
-        )
-        self.cncspindlespeed_entry = FCSpinner()
-        self.cncspindlespeed_entry.set_range(0, 1000000)
-        self.cncspindlespeed_entry.set_step(100)
-
-        grid1.addWidget(spdlabel, 10, 0)
-        grid1.addWidget(self.cncspindlespeed_entry, 10, 1)
-
-        # Dwell
-        self.dwell_cb = FCCheckBox(label='%s' % _('Enable Dwell'))
-        self.dwell_cb.setToolTip(
-            _("Pause to allow the spindle to reach its\n"
-              "speed before cutting.")
-        )
-        dwelltime = QtWidgets.QLabel('%s:' % _('Duration'))
-        dwelltime.setToolTip(
-            _("Number of time units for spindle to dwell.")
-        )
-        self.dwelltime_entry = FCDoubleSpinner()
-        self.dwelltime_entry.set_range(0, 99999)
-        self.dwelltime_entry.set_precision(self.decimals)
-        self.dwelltime_entry.setSingleStep(0.1)
-        self.dwelltime_entry.setWrapping(True)
-
-        grid1.addWidget(self.dwell_cb, 11, 0)
-        grid1.addWidget(dwelltime, 12, 0)
-        grid1.addWidget(self.dwelltime_entry, 12, 1)
-
-        self.ois_dwell = OptionalInputSection(self.dwell_cb, [self.dwelltime_entry])
-
-        # preprocessor selection
-        pp_label = QtWidgets.QLabel('%s:' % _("Preprocessor"))
-        pp_label.setToolTip(
-            _("The Preprocessor file that dictates\n"
-              "the Machine Code (like GCode, RML, HPGL) output.")
-        )
-        self.pp_geometry_name_cb = FCComboBox()
-        self.pp_geometry_name_cb.setFocusPolicy(Qt.StrongFocus)
-
-        grid1.addWidget(pp_label, 13, 0)
-        grid1.addWidget(self.pp_geometry_name_cb, 13, 1)
-
-        self.layout.addStretch()
-
-
-class GeometryAdvOptPrefGroupUI(OptionsGroupUI):
-    def __init__(self, decimals=4, parent=None):
-        # OptionsGroupUI.__init__(self, "Geometry Advanced Options Preferences", parent=parent)
-        super(GeometryAdvOptPrefGroupUI, self).__init__(self)
-
-        self.setTitle(str(_("Geometry Adv. Options")))
-        self.decimals = decimals
-
-        # ------------------------------
-        # ## Advanced Options
-        # ------------------------------
-        self.geo_label = QtWidgets.QLabel('<b>%s:</b>' % _('Advanced Options'))
-        self.geo_label.setToolTip(
-            _("A list of Geometry advanced parameters.\n"
-              "Those parameters are available only for\n"
-              "Advanced App. Level.")
-        )
-        self.layout.addWidget(self.geo_label)
-
-        grid1 = QtWidgets.QGridLayout()
-        self.layout.addLayout(grid1)
-
-        # Toolchange X,Y
-        toolchange_xy_label = QtWidgets.QLabel('%s:' % _('Toolchange X-Y'))
-        toolchange_xy_label.setToolTip(
-            _("Toolchange X,Y position.")
-        )
-        grid1.addWidget(toolchange_xy_label, 1, 0)
-        self.toolchangexy_entry = FCEntry()
-        grid1.addWidget(self.toolchangexy_entry, 1, 1)
-
-        # Start move Z
-        startzlabel = QtWidgets.QLabel('%s:' % _('Start Z'))
-        startzlabel.setToolTip(
-            _("Height of the tool just after starting the work.\n"
-              "Delete the value if you don't need this feature.")
-        )
-        grid1.addWidget(startzlabel, 2, 0)
-        self.gstartz_entry = FloatEntry()
-        grid1.addWidget(self.gstartz_entry, 2, 1)
-
-        # Feedrate rapids
-        fr_rapid_label = QtWidgets.QLabel('%s:' % _('Feedrate Rapids'))
-        fr_rapid_label.setToolTip(
-            _("Cutting speed in the XY plane\n"
-              "(in units per minute).\n"
-              "This is for the rapid move G00.\n"
-              "It is useful only for Marlin,\n"
-              "ignore for any other cases.")
-        )
-        self.feedrate_rapid_entry = FCDoubleSpinner()
-        self.feedrate_rapid_entry.set_range(0, 99999.9999)
-        self.feedrate_rapid_entry.set_precision(self.decimals)
-        self.feedrate_rapid_entry.setSingleStep(0.1)
-        self.feedrate_rapid_entry.setWrapping(True)
-
-        grid1.addWidget(fr_rapid_label, 4, 0)
-        grid1.addWidget(self.feedrate_rapid_entry, 4, 1)
-
-        # End move extra cut
-        self.extracut_cb = FCCheckBox('%s' % _('Re-cut'))
-        self.extracut_cb.setToolTip(
-            _("In order to remove possible\n"
-              "copper leftovers where first cut\n"
-              "meet with last cut, we generate an\n"
-              "extended cut over the first cut section.")
-        )
-
-        self.e_cut_entry = FCDoubleSpinner()
-        self.e_cut_entry.set_range(0, 99999)
-        self.e_cut_entry.set_precision(self.decimals)
-        self.e_cut_entry.setSingleStep(0.1)
-        self.e_cut_entry.setWrapping(True)
-        self.e_cut_entry.setToolTip(
-            _("In order to remove possible\n"
-              "copper leftovers where first cut\n"
-              "meet with last cut, we generate an\n"
-              "extended cut over the first cut section.")
-        )
-        grid1.addWidget(self.extracut_cb, 5, 0)
-        grid1.addWidget(self.e_cut_entry, 5, 1)
-
-        # Probe depth
-        self.pdepth_label = QtWidgets.QLabel('%s:' % _("Probe Z depth"))
-        self.pdepth_label.setToolTip(
-            _("The maximum depth that the probe is allowed\n"
-              "to probe. Negative value, in current units.")
-        )
-        self.pdepth_entry = FCDoubleSpinner()
-        self.pdepth_entry.set_range(-99999, 0.0000)
-        self.pdepth_entry.set_precision(self.decimals)
-        self.pdepth_entry.setSingleStep(0.1)
-        self.pdepth_entry.setWrapping(True)
-
-        grid1.addWidget(self.pdepth_label, 6, 0)
-        grid1.addWidget(self.pdepth_entry, 6, 1)
-
-        # Probe feedrate
-        self.feedrate_probe_label = QtWidgets.QLabel('%s:' % _("Feedrate Probe"))
-        self.feedrate_probe_label.setToolTip(
-            _("The feedrate used while the probe is probing.")
-        )
-        self.feedrate_probe_entry = FCDoubleSpinner()
-        self.feedrate_probe_entry.set_range(0, 99999.9999)
-        self.feedrate_probe_entry.set_precision(self.decimals)
-        self.feedrate_probe_entry.setSingleStep(0.1)
-        self.feedrate_probe_entry.setWrapping(True)
-
-        grid1.addWidget(self.feedrate_probe_label, 7, 0)
-        grid1.addWidget(self.feedrate_probe_entry, 7, 1)
-
-        # Spindle direction
-        spindle_dir_label = QtWidgets.QLabel('%s:' % _('Spindle direction'))
-        spindle_dir_label.setToolTip(
-            _("This sets the direction that the spindle is rotating.\n"
-              "It can be either:\n"
-              "- CW = clockwise or\n"
-              "- CCW = counter clockwise")
-        )
-
-        self.spindledir_radio = RadioSet([{'label': _('CW'), 'value': 'CW'},
-                                          {'label': _('CCW'), 'value': 'CCW'}])
-        grid1.addWidget(spindle_dir_label, 8, 0)
-        grid1.addWidget(self.spindledir_radio, 8, 1)
-
-        # Fast Move from Z Toolchange
-        self.fplunge_cb = FCCheckBox('%s' % _('Fast Plunge'))
-        self.fplunge_cb.setToolTip(
-            _("By checking this, the vertical move from\n"
-              "Z_Toolchange to Z_move is done with G0,\n"
-              "meaning the fastest speed available.\n"
-              "WARNING: the move is done at Toolchange X,Y coords.")
-        )
-        grid1.addWidget(self.fplunge_cb, 9, 0, 1, 2)
-
-        # Size of trace segment on X axis
-        segx_label = QtWidgets.QLabel('%s:' % _("Segment X size"))
-        segx_label.setToolTip(
-            _("The size of the trace segment on the X axis.\n"
-              "Useful for auto-leveling.\n"
-              "A value of 0 means no segmentation on the X axis.")
-        )
-        self.segx_entry = FCDoubleSpinner()
-        self.segx_entry.set_range(0, 99999)
-        self.segx_entry.set_precision(self.decimals)
-        self.segx_entry.setSingleStep(0.1)
-        self.segx_entry.setWrapping(True)
-
-        grid1.addWidget(segx_label, 10, 0)
-        grid1.addWidget(self.segx_entry, 10, 1)
-
-        # Size of trace segment on Y axis
-        segy_label = QtWidgets.QLabel('%s:' % _("Segment Y size"))
-        segy_label.setToolTip(
-            _("The size of the trace segment on the Y axis.\n"
-              "Useful for auto-leveling.\n"
-              "A value of 0 means no segmentation on the Y axis.")
-        )
-        self.segy_entry = FCDoubleSpinner()
-        self.segy_entry.set_range(0, 99999)
-        self.segy_entry.set_precision(self.decimals)
-        self.segy_entry.setSingleStep(0.1)
-        self.segy_entry.setWrapping(True)
-
-        grid1.addWidget(segy_label, 11, 0)
-        grid1.addWidget(self.segy_entry, 11, 1)
-
-        self.layout.addStretch()
-
-
-class GeometryEditorPrefGroupUI(OptionsGroupUI):
-    def __init__(self, decimals=4, parent=None):
-        # OptionsGroupUI.__init__(self, "Gerber Adv. Options Preferences", parent=parent)
-        super(GeometryEditorPrefGroupUI, self).__init__(self)
-
-        self.setTitle(str(_("Geometry Editor")))
-        self.decimals = decimals
-
-        # Advanced Geometry Parameters
-        self.param_label = QtWidgets.QLabel("<b>%s:</b>" % _("Parameters"))
-        self.param_label.setToolTip(
-            _("A list of Geometry Editor parameters.")
-        )
-        self.layout.addWidget(self.param_label)
-
-        grid0 = QtWidgets.QGridLayout()
-        self.layout.addLayout(grid0)
-
-        # Selection Limit
-        self.sel_limit_label = QtWidgets.QLabel('%s:' % _("Selection limit"))
-        self.sel_limit_label.setToolTip(
-            _("Set the number of selected geometry\n"
-              "items above which the utility geometry\n"
-              "becomes just a selection rectangle.\n"
-              "Increases the performance when moving a\n"
-              "large number of geometric elements.")
-        )
-        self.sel_limit_entry = FCSpinner()
-        self.sel_limit_entry.set_range(0, 9999)
-
-        grid0.addWidget(self.sel_limit_label, 0, 0)
-        grid0.addWidget(self.sel_limit_entry, 0, 1)
-
-        # Milling Type
-        milling_type_label = QtWidgets.QLabel('%s:' % _('Milling Type'))
-        milling_type_label.setToolTip(
-            _("Milling type:\n"
-              "- climb / best for precision milling and to reduce tool usage\n"
-              "- conventional / useful when there is no backlash compensation")
-        )
-        self.milling_type_radio = RadioSet([{'label': _('Climb'), 'value': 'cl'},
-                                            {'label': _('Conventional'), 'value': 'cv'}])
-        grid0.addWidget(milling_type_label, 1, 0)
-        grid0.addWidget(self.milling_type_radio, 1, 1)
-
-        self.layout.addStretch()
-
-
-class CNCJobGenPrefGroupUI(OptionsGroupUI):
-    def __init__(self, decimals=4, parent=None):
-        # OptionsGroupUI.__init__(self, "CNC Job General Preferences", parent=None)
-        super(CNCJobGenPrefGroupUI, self).__init__(self)
-
-        self.setTitle(str(_("CNC Job General")))
-        self.decimals = decimals
-
-        # ## Plot options
-        self.plot_options_label = QtWidgets.QLabel("<b>%s:</b>" % _("Plot Options"))
-        self.layout.addWidget(self.plot_options_label)
-
-        grid0 = QtWidgets.QGridLayout()
-        self.layout.addLayout(grid0)
-        grid0.setColumnStretch(0, 0)
-        grid0.setColumnStretch(1, 1)
-
-        # Plot CB
-        # self.plot_cb = QtWidgets.QCheckBox('Plot')
-        self.plot_cb = FCCheckBox(_('Plot Object'))
-        self.plot_cb.setToolTip(_("Plot (show) this object."))
-        grid0.addWidget(self.plot_cb, 0, 0, 1, 2)
-
-        # Plot Kind
-        self.cncplot_method_label = QtWidgets.QLabel('%s:' % _("Plot kind"))
-        self.cncplot_method_label.setToolTip(
-            _("This selects the kind of geometries on the canvas to plot.\n"
-              "Those can be either of type 'Travel' which means the moves\n"
-              "above the work piece or it can be of type 'Cut',\n"
-              "which means the moves that cut into the material.")
-        )
-
-        self.cncplot_method_radio = RadioSet([
-            {"label": _("All"), "value": "all"},
-            {"label": _("Travel"), "value": "travel"},
-            {"label": _("Cut"), "value": "cut"}
-        ], orientation='vertical')
-
-        grid0.addWidget(self.cncplot_method_label, 1, 0)
-        grid0.addWidget(self.cncplot_method_radio, 1, 1)
-        grid0.addWidget(QtWidgets.QLabel(''), 1, 2)
-
-        # Display Annotation
-        self.annotation_cb = FCCheckBox(_("Display Annotation"))
-        self.annotation_cb.setToolTip(
-            _("This selects if to display text annotation on the plot.\n"
-              "When checked it will display numbers in order for each end\n"
-              "of a travel line."
-              )
-        )
-
-        grid0.addWidget(self.annotation_cb, 2, 0, 1, 3)
-
-        # ###################################################################
-        # Number of circle steps for circular aperture linear approximation #
-        # ###################################################################
-        self.steps_per_circle_label = QtWidgets.QLabel('%s:' % _("Circle Steps"))
-        self.steps_per_circle_label.setToolTip(
-            _("The number of circle steps for <b>GCode</b> \n"
-              "circle and arc shapes linear approximation.")
-        )
-        grid0.addWidget(self.steps_per_circle_label, 3, 0)
-        self.steps_per_circle_entry = FCSpinner()
-        self.steps_per_circle_entry.set_range(0, 99999)
-        grid0.addWidget(self.steps_per_circle_entry, 3, 1)
-
-        # Tool dia for plot
-        tdlabel = QtWidgets.QLabel('%s:' % _('Travel dia'))
-        tdlabel.setToolTip(
-            _("The width of the travel lines to be\n"
-              "rendered in the plot.")
-        )
-        self.tooldia_entry = FCDoubleSpinner()
-        self.tooldia_entry.set_range(0, 99999)
-        self.tooldia_entry.set_precision(self.decimals)
-        self.tooldia_entry.setSingleStep(0.1)
-        self.tooldia_entry.setWrapping(True)
-
-        grid0.addWidget(tdlabel, 4, 0)
-        grid0.addWidget(self.tooldia_entry, 4, 1)
-
-        # add a space
-        grid0.addWidget(QtWidgets.QLabel('<b>%s:</b>' % _("G-code Decimals")), 5, 0, 1, 2)
-
-        # Number of decimals to use in GCODE coordinates
-        cdeclabel = QtWidgets.QLabel('%s:' % _('Coordinates'))
-        cdeclabel.setToolTip(
-            _("The number of decimals to be used for \n"
-              "the X, Y, Z coordinates in CNC code (GCODE, etc.)")
-        )
-        self.coords_dec_entry = FCSpinner()
-        self.coords_dec_entry.set_range(0, 9)
-        self.coords_dec_entry.setWrapping(True)
-
-        grid0.addWidget(cdeclabel, 6, 0)
-        grid0.addWidget(self.coords_dec_entry, 6, 1)
-
-        # Number of decimals to use in GCODE feedrate
-        frdeclabel = QtWidgets.QLabel('%s:' % _('Feedrate'))
-        frdeclabel.setToolTip(
-            _("The number of decimals to be used for \n"
-              "the Feedrate parameter in CNC code (GCODE, etc.)")
-        )
-        self.fr_dec_entry = FCSpinner()
-        self.fr_dec_entry.set_range(0, 9)
-        self.fr_dec_entry.setWrapping(True)
-
-        grid0.addWidget(frdeclabel, 7, 0)
-        grid0.addWidget(self.fr_dec_entry, 7, 1)
-
-        # The type of coordinates used in the Gcode: Absolute or Incremental
-        coords_type_label = QtWidgets.QLabel('%s:' % _('Coordinates type'))
-        coords_type_label.setToolTip(
-            _("The type of coordinates to be used in Gcode.\n"
-              "Can be:\n"
-              "- Absolute G90 -> the reference is the origin x=0, y=0\n"
-              "- Incremental G91 -> the reference is the previous position")
-        )
-        self.coords_type_radio = RadioSet([
-            {"label": _("Absolute G90"), "value": "G90"},
-            {"label": _("Incremental G91"), "value": "G91"}
-        ], orientation='vertical', stretch=False)
-        grid0.addWidget(coords_type_label, 8, 0)
-        grid0.addWidget(self.coords_type_radio, 8, 1)
-
-        # hidden for the time being, until implemented
-        coords_type_label.hide()
-        self.coords_type_radio.hide()
-
-        # Line Endings
-        self.line_ending_cb = FCCheckBox(_("Force Windows style line-ending"))
-        self.line_ending_cb.setToolTip(
-            _("When checked will force a Windows style line-ending\n"
-              "(\\r\\n) on non-Windows OS's.")
-        )
-
-        grid0.addWidget(self.line_ending_cb, 9, 0, 1, 3)
-
-        separator_line = QtWidgets.QFrame()
-        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
-        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
-        grid0.addWidget(separator_line, 12, 0, 1, 2)
-
-        # Travel Line Color
-        self.travel_color_label = QtWidgets.QLabel('<b>%s</b>' % _('Travel Line Color'))
-        grid0.addWidget(self.travel_color_label, 13, 0, 1, 2)
-
-        # Plot Line Color
-        self.tline_color_label = QtWidgets.QLabel('%s:' % _('Outline'))
-        self.tline_color_label.setToolTip(
-            _("Set the travel line color for plotted objects.")
-        )
-        self.tline_color_entry = FCEntry()
-        self.tline_color_button = QtWidgets.QPushButton()
-        self.tline_color_button.setFixedSize(15, 15)
-
-        self.form_box_child_2 = QtWidgets.QHBoxLayout()
-        self.form_box_child_2.addWidget(self.tline_color_entry)
-        self.form_box_child_2.addWidget(self.tline_color_button)
-        self.form_box_child_2.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
-
-        grid0.addWidget(self.tline_color_label, 14, 0)
-        grid0.addLayout(self.form_box_child_2, 14, 1)
-
-        # Plot Fill Color
-        self.tfill_color_label = QtWidgets.QLabel('%s:' % _('Fill'))
-        self.tfill_color_label.setToolTip(
-            _("Set the fill color for plotted objects.\n"
-              "First 6 digits are the color and the last 2\n"
-              "digits are for alpha (transparency) level.")
-        )
-        self.tfill_color_entry = FCEntry()
-        self.tfill_color_button = QtWidgets.QPushButton()
-        self.tfill_color_button.setFixedSize(15, 15)
-
-        self.form_box_child_1 = QtWidgets.QHBoxLayout()
-        self.form_box_child_1.addWidget(self.tfill_color_entry)
-        self.form_box_child_1.addWidget(self.tfill_color_button)
-        self.form_box_child_1.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
-
-        grid0.addWidget(self.tfill_color_label, 15, 0)
-        grid0.addLayout(self.form_box_child_1, 15, 1)
-
-        # Plot Fill Transparency Level
-        self.alpha_label = QtWidgets.QLabel('%s:' % _('Alpha'))
-        self.alpha_label.setToolTip(
-            _("Set the fill transparency for plotted objects.")
-        )
-        self.tcolor_alpha_slider = QtWidgets.QSlider(QtCore.Qt.Horizontal)
-        self.tcolor_alpha_slider.setMinimum(0)
-        self.tcolor_alpha_slider.setMaximum(255)
-        self.tcolor_alpha_slider.setSingleStep(1)
-
-        self.tcolor_alpha_spinner = FCSpinner()
-        self.tcolor_alpha_spinner.setMinimumWidth(70)
-        self.tcolor_alpha_spinner.set_range(0, 255)
-
-        self.form_box_child_3 = QtWidgets.QHBoxLayout()
-        self.form_box_child_3.addWidget(self.tcolor_alpha_slider)
-        self.form_box_child_3.addWidget(self.tcolor_alpha_spinner)
-
-        grid0.addWidget(self.alpha_label, 16, 0)
-        grid0.addLayout(self.form_box_child_3, 16, 1)
-
-        separator_line = QtWidgets.QFrame()
-        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
-        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
-        grid0.addWidget(separator_line, 17, 0, 1, 2)
-
-        # CNCJob Object Color
-        self.cnc_color_label = QtWidgets.QLabel('<b>%s</b>' % _('CNCJob Object Color'))
-        grid0.addWidget(self.cnc_color_label, 18, 0, 1, 2)
-
-        # Plot Line Color
-        self.line_color_label = QtWidgets.QLabel('%s:' % _('Outline'))
-        self.line_color_label.setToolTip(
-            _("Set the color for plotted objects.")
-        )
-        self.line_color_entry = FCEntry()
-        self.line_color_button = QtWidgets.QPushButton()
-        self.line_color_button.setFixedSize(15, 15)
-
-        self.form_box_child_2 = QtWidgets.QHBoxLayout()
-        self.form_box_child_2.addWidget(self.line_color_entry)
-        self.form_box_child_2.addWidget(self.line_color_button)
-        self.form_box_child_2.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
-
-        grid0.addWidget(self.line_color_label, 19, 0)
-        grid0.addLayout(self.form_box_child_2, 19, 1)
-
-        # Plot Fill Color
-        self.fill_color_label = QtWidgets.QLabel('%s:' % _('Fill'))
-        self.fill_color_label.setToolTip(
-            _("Set the fill color for plotted objects.\n"
-              "First 6 digits are the color and the last 2\n"
-              "digits are for alpha (transparency) level.")
-        )
-        self.fill_color_entry = FCEntry()
-        self.fill_color_button = QtWidgets.QPushButton()
-        self.fill_color_button.setFixedSize(15, 15)
-
-        self.form_box_child_1 = QtWidgets.QHBoxLayout()
-        self.form_box_child_1.addWidget(self.fill_color_entry)
-        self.form_box_child_1.addWidget(self.fill_color_button)
-        self.form_box_child_1.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
-
-        grid0.addWidget(self.fill_color_label, 20, 0)
-        grid0.addLayout(self.form_box_child_1, 20, 1)
-
-        self.layout.addStretch()
-
-        # Setting plot colors signals
-        self.tline_color_entry.editingFinished.connect(self.on_tline_color_entry)
-        self.tline_color_button.clicked.connect(self.on_tline_color_button)
-        self.tfill_color_entry.editingFinished.connect(self.on_tfill_color_entry)
-        self.tfill_color_button.clicked.connect(self.on_tfill_color_button)
-        self.tcolor_alpha_spinner.valueChanged.connect(self.on_tcolor_spinner)
-        self.tcolor_alpha_slider.valueChanged.connect(self.on_tcolor_slider)
-
-        self.line_color_entry.editingFinished.connect(self.on_line_color_entry)
-        self.line_color_button.clicked.connect(self.on_line_color_button)
-        self.fill_color_entry.editingFinished.connect(self.on_fill_color_entry)
-        self.fill_color_button.clicked.connect(self.on_fill_color_button)
-
-    # ------------------------------------------------------
-    # Setting travel colors handlers
-    # ------------------------------------------------------
-    def on_tfill_color_entry(self):
-        self.app.defaults['cncjob_travel_fill'] = self.tfill_color_entry.get_value()[:7] + \
-                                                  self.app.defaults['cncjob_travel_fill'][7:9]
-        self.tfill_color_button.setStyleSheet(
-            "background-color:%s" % str(self.app.defaults['cncjob_travel_fill'])[:7])
-
-    def on_tfill_color_button(self):
-        current_color = QtGui.QColor(self.app.defaults['cncjob_travel_fill'][:7])
-
-        c_dialog = QtWidgets.QColorDialog()
-        plot_fill_color = c_dialog.getColor(initial=current_color)
-
-        if plot_fill_color.isValid() is False:
-            return
-
-        self.tfill_color_button.setStyleSheet("background-color:%s" % str(plot_fill_color.name()))
-
-        new_val = str(plot_fill_color.name()) + str(self.app.defaults['cncjob_travel_fill'][7:9])
-        self.tfill_color_entry.set_value(new_val)
-        self.app.defaults['cncjob_travel_fill'] = new_val
-
-    def on_tcolor_spinner(self):
-        spinner_value = self.tcolor_alpha_spinner.value()
-        self.tcolor_alpha_slider.setValue(spinner_value)
-        self.app.defaults['cncjob_travel_fill'] = \
-            self.app.defaults['cncjob_travel_fill'][:7] + \
-            (hex(spinner_value)[2:] if int(hex(spinner_value)[2:], 16) > 0 else '00')
-        self.app.defaults['cncjob_travel_line'] = \
-            self.app.defaults['cncjob_travel_line'][:7] + \
-            (hex(spinner_value)[2:] if int(hex(spinner_value)[2:], 16) > 0 else '00')
-
-    def on_tcolor_slider(self):
-        slider_value = self.tcolor_alpha_slider.value()
-        self.tcolor_alpha_spinner.setValue(slider_value)
-
-    def on_tline_color_entry(self):
-        self.app.defaults['cncjob_travel_line'] = self.tline_color_entry.get_value()[:7] + \
-                                                  self.app.defaults['cncjob_travel_line'][7:9]
-        self.tline_color_button.setStyleSheet(
-            "background-color:%s" % str(self.app.defaults['cncjob_travel_line'])[:7])
-
-    def on_tline_color_button(self):
-        current_color = QtGui.QColor(self.app.defaults['cncjob_travel_line'][:7])
-        # print(current_color)
-
-        c_dialog = QtWidgets.QColorDialog()
-        plot_line_color = c_dialog.getColor(initial=current_color)
-
-        if plot_line_color.isValid() is False:
-            return
-
-        self.tline_color_button.setStyleSheet("background-color:%s" % str(plot_line_color.name()))
-
-        new_val_line = str(plot_line_color.name()) + str(self.app.defaults['cncjob_travel_line'][7:9])
-        self.tline_color_entry.set_value(new_val_line)
-        self.app.defaults['cncjob_travel_line'] = new_val_line
-
-    # ------------------------------------------------------
-    # Setting plot colors handlers
-    # ------------------------------------------------------
-    def on_fill_color_entry(self):
-        self.app.defaults['cncjob_plot_fill'] = self.fill_color_entry.get_value()[:7] + \
-                                                  self.app.defaults['cncjob_plot_fill'][7:9]
-        self.fill_color_button.setStyleSheet(
-            "background-color:%s" % str(self.app.defaults['cncjob_plot_fill'])[:7])
-
-    def on_fill_color_button(self):
-        current_color = QtGui.QColor(self.app.defaults['cncjob_plot_fill'][:7])
-
-        c_dialog = QtWidgets.QColorDialog()
-        plot_fill_color = c_dialog.getColor(initial=current_color)
-
-        if plot_fill_color.isValid() is False:
-            return
-
-        self.fill_color_button.setStyleSheet("background-color:%s" % str(plot_fill_color.name()))
-
-        new_val = str(plot_fill_color.name()) + str(self.app.defaults['cncjob_plot_fill'][7:9])
-        self.fill_color_entry.set_value(new_val)
-        self.app.defaults['cncjob_plot_fill'] = new_val
-
-    def on_line_color_entry(self):
-        self.app.defaults['cncjob_plot_line'] = self.line_color_entry.get_value()[:7] + \
-                                                  self.app.defaults['cncjob_plot_line'][7:9]
-        self.line_color_button.setStyleSheet(
-            "background-color:%s" % str(self.app.defaults['cncjob_plot_line'])[:7])
-
-    def on_line_color_button(self):
-        current_color = QtGui.QColor(self.app.defaults['cncjob_plot_line'][:7])
-        # print(current_color)
-
-        c_dialog = QtWidgets.QColorDialog()
-        plot_line_color = c_dialog.getColor(initial=current_color)
-
-        if plot_line_color.isValid() is False:
-            return
-
-        self.line_color_button.setStyleSheet("background-color:%s" % str(plot_line_color.name()))
-
-        new_val_line = str(plot_line_color.name()) + str(self.app.defaults['cncjob_plot_line'][7:9])
-        self.line_color_entry.set_value(new_val_line)
-        self.app.defaults['cncjob_plot_line'] = new_val_line
-
-
-class CNCJobOptPrefGroupUI(OptionsGroupUI):
-    def __init__(self, decimals=4, parent=None):
-        # OptionsGroupUI.__init__(self, "CNC Job Options Preferences", parent=None)
-        super(CNCJobOptPrefGroupUI, self).__init__(self)
-
-        self.setTitle(str(_("CNC Job Options")))
-        self.decimals = decimals
-
-        # ## Export G-Code
-        self.export_gcode_label = QtWidgets.QLabel("<b>%s:</b>" % _("Export G-Code"))
-        self.export_gcode_label.setToolTip(
-            _("Export and save G-Code to\n"
-              "make this object to a file.")
-        )
-        self.layout.addWidget(self.export_gcode_label)
-
-        qsettings = QSettings("Open Source", "FlatCAM")
-        if qsettings.contains("textbox_font_size"):
-            tb_fsize = qsettings.value('textbox_font_size', type=int)
-        else:
-            tb_fsize = 10
-        font = QtGui.QFont()
-        font.setPointSize(tb_fsize)
-
-        # Prepend to G-Code
-        prependlabel = QtWidgets.QLabel('%s:' % _('Prepend to G-Code'))
-        prependlabel.setToolTip(
-            _("Type here any G-Code commands you would\n"
-              "like to add at the beginning of the G-Code file.")
-        )
-        self.layout.addWidget(prependlabel)
-
-        self.prepend_text = FCTextArea()
-        self.prepend_text.setPlaceholderText(
-            _("Type here any G-Code commands you would "
-              "like to add at the beginning of the G-Code file.")
-        )
-        self.layout.addWidget(self.prepend_text)
-        self.prepend_text.setFont(font)
-
-        # Append text to G-Code
-        appendlabel = QtWidgets.QLabel('%s:' % _('Append to G-Code'))
-        appendlabel.setToolTip(
-            _("Type here any G-Code commands you would\n"
-              "like to append to the generated file.\n"
-              "I.e.: M2 (End of program)")
-        )
-        self.layout.addWidget(appendlabel)
-
-        self.append_text = FCTextArea()
-        self.append_text.setPlaceholderText(
-            _("Type here any G-Code commands you would "
-              "like to append to the generated file.\n"
-              "I.e.: M2 (End of program)")
-        )
-        self.layout.addWidget(self.append_text)
-        self.append_text.setFont(font)
-
-        self.layout.addStretch()
-
-
-class CNCJobAdvOptPrefGroupUI(OptionsGroupUI):
-    def __init__(self, decimals=4, parent=None):
-        # OptionsGroupUI.__init__(self, "CNC Job Advanced Options Preferences", parent=None)
-        super(CNCJobAdvOptPrefGroupUI, self).__init__(self)
-        self.decimals = decimals
-
-        self.setTitle(str(_("CNC Job Adv. Options")))
-
-        # ## Export G-Code
-        self.export_gcode_label = QtWidgets.QLabel("<b>%s:</b>" % _("Export CNC Code"))
-        self.export_gcode_label.setToolTip(
-            _("Export and save G-Code to\n"
-              "make this object to a file.")
-        )
-        self.layout.addWidget(self.export_gcode_label)
-
-        # Prepend to G-Code
-        toolchangelabel = QtWidgets.QLabel('%s' % _('Toolchange G-Code'))
-        toolchangelabel.setToolTip(
-            _(
-                "Type here any G-Code commands you would\n"
-                "like to be executed when Toolchange event is encountered.\n"
-                "This will constitute a Custom Toolchange GCode,\n"
-                "or a Toolchange Macro.\n"
-                "The FlatCAM variables are surrounded by '%' symbol.\n\n"
-                "WARNING: it can be used only with a preprocessor file\n"
-                "that has 'toolchange_custom' in it's name and this is built\n"
-                "having as template the 'Toolchange Custom' posprocessor file."
-            )
-        )
-        self.layout.addWidget(toolchangelabel)
-
-        qsettings = QSettings("Open Source", "FlatCAM")
-        if qsettings.contains("textbox_font_size"):
-            tb_fsize = qsettings.value('textbox_font_size', type=int)
-        else:
-            tb_fsize = 10
-        font = QtGui.QFont()
-        font.setPointSize(tb_fsize)
-
-        self.toolchange_text = FCTextArea()
-        self.toolchange_text.setPlaceholderText(
-            _(
-                "Type here any G-Code commands you would "
-                "like to be executed when Toolchange event is encountered.\n"
-                "This will constitute a Custom Toolchange GCode, "
-                "or a Toolchange Macro.\n"
-                "The FlatCAM variables are surrounded by '%' symbol.\n"
-                "WARNING: it can be used only with a preprocessor file "
-                "that has 'toolchange_custom' in it's name."
-            )
-        )
-        self.layout.addWidget(self.toolchange_text)
-        self.toolchange_text.setFont(font)
-
-        hlay = QtWidgets.QHBoxLayout()
-        self.layout.addLayout(hlay)
-
-        # Toolchange Replacement GCode
-        self.toolchange_cb = FCCheckBox(label='%s' % _('Use Toolchange Macro'))
-        self.toolchange_cb.setToolTip(
-            _("Check this box if you want to use\n"
-              "a Custom Toolchange GCode (macro).")
-        )
-        hlay.addWidget(self.toolchange_cb)
-        hlay.addStretch()
-
-        hlay1 = QtWidgets.QHBoxLayout()
-        self.layout.addLayout(hlay1)
-
-        # Variable list
-        self.tc_variable_combo = FCComboBox()
-        self.tc_variable_combo.setToolTip(
-            _("A list of the FlatCAM variables that can be used\n"
-              "in the Toolchange event.\n"
-              "They have to be surrounded by the '%' symbol")
-        )
-        hlay1.addWidget(self.tc_variable_combo)
-
-        # Populate the Combo Box
-        variables = [_('Parameters'), 'tool', 'tooldia', 't_drills', 'x_toolchange', 'y_toolchange', 'z_toolchange',
-                     'z_cut', 'z_move', 'z_depthpercut', 'spindlespeed', 'dwelltime']
-        self.tc_variable_combo.addItems(variables)
-        self.tc_variable_combo.insertSeparator(1)
-
-        self.tc_variable_combo.setItemData(0, _("FlatCAM CNC parameters"), Qt.ToolTipRole)
-        fnt = QtGui.QFont()
-        fnt.setBold(True)
-        self.tc_variable_combo.setItemData(0, fnt, Qt.FontRole)
-
-        self.tc_variable_combo.setItemData(2, 'tool = %s' % _("tool number"), Qt.ToolTipRole)
-        self.tc_variable_combo.setItemData(3, 'tooldia = %s' % _("tool diameter"), Qt.ToolTipRole)
-        self.tc_variable_combo.setItemData(4, 't_drills = %s' % _("for Excellon, total number of drills"),
-                                           Qt.ToolTipRole)
-        self.tc_variable_combo.setItemData(5, 'x_toolchange = %s' % _("X coord for Toolchange"), Qt.ToolTipRole)
-        self.tc_variable_combo.setItemData(6, 'y_toolchange = %s' % _("Y coord for Toolchange"),
-                                           Qt.ToolTipRole)
-        self.tc_variable_combo.setItemData(7, 'z_toolchange = %s' % _("Z coord for Toolchange"), Qt.ToolTipRole)
-        self.tc_variable_combo.setItemData(8, 'z_cut = %s' % _("Z depth for the cut"), Qt.ToolTipRole)
-        self.tc_variable_combo.setItemData(9, 'z_move = %s' % _("Z height for travel"), Qt.ToolTipRole)
-        self.tc_variable_combo.setItemData(10, 'z_depthpercut = %s' % _("the step value for multidepth cut"),
-                                           Qt.ToolTipRole)
-        self.tc_variable_combo.setItemData(11, 'spindlesspeed = %s' % _("the value for the spindle speed"),
-                                           Qt.ToolTipRole)
-        self.tc_variable_combo.setItemData(12,
-                                           _("dwelltime = time to dwell to allow the spindle to reach it's set RPM"),
-                                           Qt.ToolTipRole)
-
-        # hlay1.addStretch()
-
-        # Insert Variable into the Toolchange G-Code Text Box
-        # self.tc_insert_buton = FCButton("Insert")
-        # self.tc_insert_buton.setToolTip(
-        #     "Insert the variable in the GCode Box\n"
-        #     "surrounded by the '%' symbol."
-        # )
-        # hlay1.addWidget(self.tc_insert_buton)
-
-        grid0 = QtWidgets.QGridLayout()
-        self.layout.addLayout(grid0)
-
-        grid0.addWidget(QtWidgets.QLabel(''), 1, 0, 1, 2)
-
-        # Annotation Font Size
-        self.annotation_fontsize_label = QtWidgets.QLabel('%s:' % _("Annotation Size"))
-        self.annotation_fontsize_label.setToolTip(
-            _("The font size of the annotation text. In pixels.")
-        )
-        grid0.addWidget(self.annotation_fontsize_label, 2, 0)
-        self.annotation_fontsize_sp = FCSpinner()
-        self.annotation_fontsize_sp.set_range(0, 9999)
-
-        grid0.addWidget(self.annotation_fontsize_sp, 2, 1)
-        grid0.addWidget(QtWidgets.QLabel(''), 2, 2)
-
-        # Annotation Font Color
-        self.annotation_color_label = QtWidgets.QLabel('%s:' % _('Annotation Color'))
-        self.annotation_color_label.setToolTip(
-            _("Set the font color for the annotation texts.")
-        )
-        self.annotation_fontcolor_entry = FCEntry()
-        self.annotation_fontcolor_button = QtWidgets.QPushButton()
-        self.annotation_fontcolor_button.setFixedSize(15, 15)
-
-        self.form_box_child = QtWidgets.QHBoxLayout()
-        self.form_box_child.setContentsMargins(0, 0, 0, 0)
-        self.form_box_child.addWidget(self.annotation_fontcolor_entry)
-        self.form_box_child.addWidget(self.annotation_fontcolor_button, alignment=Qt.AlignRight)
-        self.form_box_child.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
-
-        color_widget = QtWidgets.QWidget()
-        color_widget.setLayout(self.form_box_child)
-        grid0.addWidget(self.annotation_color_label, 3, 0)
-        grid0.addWidget(color_widget, 3, 1)
-        grid0.addWidget(QtWidgets.QLabel(''), 3, 2)
-
-        self.layout.addStretch()
-
-        self.tc_variable_combo.currentIndexChanged[str].connect(self.on_cnc_custom_parameters)
-
-        self.annotation_fontcolor_entry.editingFinished.connect(self.on_annotation_fontcolor_entry)
-        self.annotation_fontcolor_button.clicked.connect(self.on_annotation_fontcolor_button)
-
-    def on_cnc_custom_parameters(self, signal_text):
-        if signal_text == 'Parameters':
-            return
-        else:
-            self.toolchange_text.insertPlainText('%%%s%%' % signal_text)
-
-    def on_annotation_fontcolor_entry(self):
-        self.app.defaults['cncjob_annotation_fontcolor'] = self.annotation_fontcolor_entry.get_value()
-        self.annotation_fontcolor_button.setStyleSheet(
-            "background-color:%s" % str(self.app.defaults['cncjob_annotation_fontcolor']))
-
-    def on_annotation_fontcolor_button(self):
-        current_color = QtGui.QColor(self.app.defaults['cncjob_annotation_fontcolor'])
-
-        c_dialog = QtWidgets.QColorDialog()
-        annotation_color = c_dialog.getColor(initial=current_color)
-
-        if annotation_color.isValid() is False:
-            return
-
-        self.annotation_fontcolor_button.setStyleSheet("background-color:%s" % str(annotation_color.name()))
-
-        new_val_sel = str(annotation_color.name())
-        self.annotation_fontcolor_entry.set_value(new_val_sel)
-        self.app.defaults['cncjob_annotation_fontcolor'] = new_val_sel
-
-
-class ToolsNCCPrefGroupUI(OptionsGroupUI):
-    def __init__(self, decimals=4, parent=None):
-        # OptionsGroupUI.__init__(self, "NCC Tool Options", parent=parent)
-        super(ToolsNCCPrefGroupUI, self).__init__(self)
-
-        self.setTitle(str(_("NCC Tool Options")))
-        self.decimals = decimals
-
-        # ## Clear non-copper regions
-        self.clearcopper_label = QtWidgets.QLabel("<b>%s:</b>" % _("Parameters"))
-        self.clearcopper_label.setToolTip(
-            _("Create a Geometry object with\n"
-              "toolpaths to cut all non-copper regions.")
-        )
-        self.layout.addWidget(self.clearcopper_label)
-
-        grid0 = QtWidgets.QGridLayout()
-        self.layout.addLayout(grid0)
-
-        ncctdlabel = QtWidgets.QLabel('<b><font color="green">%s:</font></b>' % _('Tools Dia'))
-        ncctdlabel.setToolTip(
-            _("Diameters of the tools, separated by comma.\n"
-              "The value of the diameter has to use the dot decimals separator.\n"
-              "Valid values: 0.3, 1.0")
-        )
-        grid0.addWidget(ncctdlabel, 0, 0)
-        self.ncc_tool_dia_entry = FCEntry(border_color='#0069A9')
-        self.ncc_tool_dia_entry.setPlaceholderText(_("Comma separated values"))
-        grid0.addWidget(self.ncc_tool_dia_entry, 0, 1)
-
-        # Tool Type Radio Button
-        self.tool_type_label = QtWidgets.QLabel('%s:' % _('Tool Type'))
-        self.tool_type_label.setToolTip(
-            _("Default tool type:\n"
-              "- 'V-shape'\n"
-              "- Circular")
-        )
-
-        self.tool_type_radio = RadioSet([{'label': _('V-shape'), 'value': 'V'},
-                                         {'label': _('Circular'), 'value': 'C1'}])
-        self.tool_type_radio.setToolTip(
-            _("Default tool type:\n"
-              "- 'V-shape'\n"
-              "- Circular")
-        )
-
-        grid0.addWidget(self.tool_type_label, 1, 0)
-        grid0.addWidget(self.tool_type_radio, 1, 1)
-
-        # Tip Dia
-        self.tipdialabel = QtWidgets.QLabel('%s:' % _('V-Tip Dia'))
-        self.tipdialabel.setToolTip(
-            _("The tip diameter for V-Shape Tool"))
-        self.tipdia_entry = FCDoubleSpinner()
-        self.tipdia_entry.set_precision(self.decimals)
-        self.tipdia_entry.set_range(0, 1000)
-        self.tipdia_entry.setSingleStep(0.1)
-
-        grid0.addWidget(self.tipdialabel, 2, 0)
-        grid0.addWidget(self.tipdia_entry, 2, 1)
-
-        # Tip Angle
-        self.tipanglelabel = QtWidgets.QLabel('%s:' % _('V-Tip Angle'))
-        self.tipanglelabel.setToolTip(
-            _("The tip angle for V-Shape Tool.\n"
-              "In degree."))
-        self.tipangle_entry = FCDoubleSpinner()
-        self.tipangle_entry.set_precision(self.decimals)
-        self.tipangle_entry.set_range(1, 180)
-        self.tipangle_entry.setSingleStep(5)
-        self.tipangle_entry.setWrapping(True)
-
-        grid0.addWidget(self.tipanglelabel, 3, 0)
-        grid0.addWidget(self.tipangle_entry, 3, 1)
-
-        # Cut Z entry
-        cutzlabel = QtWidgets.QLabel('%s:' % _('Cut Z'))
-        cutzlabel.setToolTip(
-           _("Depth of cut into material. Negative value.\n"
-             "In FlatCAM units.")
-        )
-        self.cutz_entry = FCDoubleSpinner()
-        self.cutz_entry.set_precision(self.decimals)
-        self.cutz_entry.set_range(-9999.9999, 0.0000)
-        self.cutz_entry.setSingleStep(0.1)
-
-        self.cutz_entry.setToolTip(
-           _("Depth of cut into material. Negative value.\n"
-             "In FlatCAM units.")
-        )
-
-        grid0.addWidget(cutzlabel, 4, 0)
-        grid0.addWidget(self.cutz_entry, 4, 1)
-
-        # New Diameter
-        self.newdialabel = QtWidgets.QLabel('%s:' % _('New Dia'))
-        self.newdialabel.setToolTip(
-            _("Diameter for the new tool to add in the Tool Table.\n"
-              "If the tool is V-shape type then this value is automatically\n"
-              "calculated from the other parameters.")
-        )
-        self.newdia_entry = FCDoubleSpinner()
-        self.newdia_entry.set_precision(self.decimals)
-        self.newdia_entry.set_range(0.0001, 9999.9999)
-        self.newdia_entry.setSingleStep(0.1)
-
-        grid0.addWidget(self.newdialabel, 5, 0)
-        grid0.addWidget(self.newdia_entry, 5, 1)
-
-        separator_line = QtWidgets.QFrame()
-        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
-        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
-        grid0.addWidget(separator_line, 6, 0, 1, 2)
-
-        # Milling Type Radio Button
-        self.milling_type_label = QtWidgets.QLabel('%s:' % _('Milling Type'))
-        self.milling_type_label.setToolTip(
-            _("Milling type when the selected tool is of type: 'iso_op':\n"
-              "- climb / best for precision milling and to reduce tool usage\n"
-              "- conventional / useful when there is no backlash compensation")
-        )
-
-        self.milling_type_radio = RadioSet([{'label': _('Climb'), 'value': 'cl'},
-                                            {'label': _('Conventional'), 'value': 'cv'}])
-        self.milling_type_radio.setToolTip(
-            _("Milling type when the selected tool is of type: 'iso_op':\n"
-              "- climb / best for precision milling and to reduce tool usage\n"
-              "- conventional / useful when there is no backlash compensation")
-        )
-
-        grid0.addWidget(self.milling_type_label, 7, 0)
-        grid0.addWidget(self.milling_type_radio, 7, 1)
-
-        # Tool order Radio Button
-        self.ncc_order_label = QtWidgets.QLabel('%s:' % _('Tool order'))
-        self.ncc_order_label.setToolTip(_("This set the way that the tools in the tools table are used.\n"
-                                          "'No' --> means that the used order is the one in the tool table\n"
-                                          "'Forward' --> means that the tools will be ordered from small to big\n"
-                                          "'Reverse' --> menas that the tools will ordered from big to small\n\n"
-                                          "WARNING: using rest machining will automatically set the order\n"
-                                          "in reverse and disable this control."))
-
-        self.ncc_order_radio = RadioSet([{'label': _('No'), 'value': 'no'},
-                                         {'label': _('Forward'), 'value': 'fwd'},
-                                         {'label': _('Reverse'), 'value': 'rev'}])
-        self.ncc_order_radio.setToolTip(_("This set the way that the tools in the tools table are used.\n"
-                                          "'No' --> means that the used order is the one in the tool table\n"
-                                          "'Forward' --> means that the tools will be ordered from small to big\n"
-                                          "'Reverse' --> menas that the tools will ordered from big to small\n\n"
-                                          "WARNING: using rest machining will automatically set the order\n"
-                                          "in reverse and disable this control."))
-        grid0.addWidget(self.ncc_order_label, 8, 0)
-        grid0.addWidget(self.ncc_order_radio, 8, 1)
-
-        separator_line = QtWidgets.QFrame()
-        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
-        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
-        grid0.addWidget(separator_line, 9, 0, 1, 2)
-
-        # Overlap Entry
-        nccoverlabel = QtWidgets.QLabel('%s:' % _('Overlap'))
-        nccoverlabel.setToolTip(
-           _("How much (percentage) of the tool width to overlap each tool pass.\n"
-             "Adjust the value starting with lower values\n"
-             "and increasing it if areas that should be cleared are still \n"
-             "not cleared.\n"
-             "Lower values = faster processing, faster execution on CNC.\n"
-             "Higher values = slow processing and slow execution on CNC\n"
-             "due of too many paths.")
-        )
-        self.ncc_overlap_entry = FCDoubleSpinner(suffix='%')
-        self.ncc_overlap_entry.set_precision(self.decimals)
-        self.ncc_overlap_entry.setWrapping(True)
-        self.ncc_overlap_entry.setRange(0.0000, 99.9999)
-        self.ncc_overlap_entry.setSingleStep(0.1)
-
-        grid0.addWidget(nccoverlabel, 10, 0)
-        grid0.addWidget(self.ncc_overlap_entry, 10, 1)
-
-        # Margin entry
-        nccmarginlabel = QtWidgets.QLabel('%s:' % _('Margin'))
-        nccmarginlabel.setToolTip(
-            _("Bounding box margin.")
-        )
-        self.ncc_margin_entry = FCDoubleSpinner()
-        self.ncc_margin_entry.set_precision(self.decimals)
-        self.ncc_margin_entry.set_range(-10000, 10000)
-        self.ncc_margin_entry.setSingleStep(0.1)
-
-        grid0.addWidget(nccmarginlabel, 11, 0)
-        grid0.addWidget(self.ncc_margin_entry, 11, 1)
-
-        # Method
-        methodlabel = QtWidgets.QLabel('%s:' % _('Method'))
-        methodlabel.setToolTip(
-            _("Algorithm for copper clearing:\n"
-              "- Standard: Fixed step inwards.\n"
-              "- Seed-based: Outwards from seed.\n"
-              "- Line-based: Parallel lines.")
-        )
-
-        # self.ncc_method_radio = RadioSet([
-        #     {"label": _("Standard"), "value": "standard"},
-        #     {"label": _("Seed-based"), "value": "seed"},
-        #     {"label": _("Straight lines"), "value": "lines"}
-        # ], orientation='vertical', stretch=False)
-        self.ncc_method_combo = FCComboBox()
-        self.ncc_method_combo.addItems(
-            [_("Standard"), _("Seed"), _("Lines")]
-        )
-
-        grid0.addWidget(methodlabel, 12, 0)
-        grid0.addWidget(self.ncc_method_combo, 12, 1)
-
-        # Connect lines
-        self.ncc_connect_cb = FCCheckBox('%s' % _("Connect"))
-        self.ncc_connect_cb.setToolTip(
-            _("Draw lines between resulting\n"
-              "segments to minimize tool lifts.")
-        )
-
-        grid0.addWidget(self.ncc_connect_cb, 13, 0)
-
-        # Contour Checkbox
-        self.ncc_contour_cb = FCCheckBox('%s' % _("Contour"))
-        self.ncc_contour_cb.setToolTip(
-           _("Cut around the perimeter of the polygon\n"
-             "to trim rough edges.")
-        )
-
-        grid0.addWidget(self.ncc_contour_cb, 13, 1)
-
-        # ## NCC Offset choice
-        self.ncc_choice_offset_cb = FCCheckBox('%s' % _("Offset"))
-        self.ncc_choice_offset_cb.setToolTip(
-            _("If used, it will add an offset to the copper features.\n"
-              "The copper clearing will finish to a distance\n"
-              "from the copper features.\n"
-              "The value can be between 0 and 10 FlatCAM units.")
-        )
-
-        grid0.addWidget(self.ncc_choice_offset_cb, 14, 0, 1, 2)
-
-        # ## NCC Offset value
-        self.ncc_offset_label = QtWidgets.QLabel('%s:' % _("Offset value"))
-        self.ncc_offset_label.setToolTip(
-            _("If used, it will add an offset to the copper features.\n"
-              "The copper clearing will finish to a distance\n"
-              "from the copper features.\n"
-              "The value can be between 0.0 and 9999.9 FlatCAM units.")
-        )
-        self.ncc_offset_spinner = FCDoubleSpinner()
-        self.ncc_offset_spinner.set_range(0.00, 9999.9999)
-        self.ncc_offset_spinner.set_precision(self.decimals)
-        self.ncc_offset_spinner.setWrapping(True)
-        self.ncc_offset_spinner.setSingleStep(0.1)
-
-        grid0.addWidget(self.ncc_offset_label, 15, 0)
-        grid0.addWidget(self.ncc_offset_spinner, 15, 1)
-
-        separator_line = QtWidgets.QFrame()
-        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
-        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
-        grid0.addWidget(separator_line, 16, 0, 1, 2)
-
-        # Rest machining CheckBox
-        self.ncc_rest_cb = FCCheckBox('%s' % _("Rest Machining"))
-        self.ncc_rest_cb.setToolTip(
-            _("If checked, use 'rest machining'.\n"
-              "Basically it will clear copper outside PCB features,\n"
-              "using the biggest tool and continue with the next tools,\n"
-              "from bigger to smaller, to clear areas of copper that\n"
-              "could not be cleared by previous tool, until there is\n"
-              "no more copper to clear or there are no more tools.\n"
-              "If not checked, use the standard algorithm.")
-        )
-
-        grid0.addWidget(self.ncc_rest_cb, 17, 0, 1, 2)
-
-        # ## Reference
-        # self.reference_radio = RadioSet([{'label': _('Itself'), 'value': 'itself'},
-        #                                  {"label": _("Area Selection"), "value": "area"},
-        #                                  {'label': _('Reference Object'), 'value': 'box'}],
-        #                                 orientation='vertical',
-        #                                 stretch=None)
-        self.select_combo = FCComboBox()
-        self.select_combo.addItems(
-            [_("Itself"), _("Area Selection"), _("Reference Object")]
-        )
-        select_label = QtWidgets.QLabel('%s:' % _("Selection"))
-        select_label.setToolTip(
-            _("Selection of area to be processed.\n"
-              "- 'Itself' - the processing extent is based on the object that is processed.\n "
-              "- 'Area Selection' - left mouse click to start selection of the area to be processed.\n"
-              "- 'Reference Object' - will process the area specified by another object.")
-        )
-
-        grid0.addWidget(select_label, 18, 0)
-        grid0.addWidget(self.select_combo, 18, 1)
-
-        self.area_shape_label = QtWidgets.QLabel('%s:' % _("Shape"))
-        self.area_shape_label.setToolTip(
-            _("The kind of selection shape used for area selection.")
-        )
-
-        self.area_shape_radio = RadioSet([{'label': _("Square"), 'value': 'square'},
-                                          {'label': _("Polygon"), 'value': 'polygon'}])
-
-        grid0.addWidget(self.area_shape_label, 19, 0)
-        grid0.addWidget(self.area_shape_radio, 19, 1)
-
-        separator_line = QtWidgets.QFrame()
-        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
-        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
-        grid0.addWidget(separator_line, 20, 0, 1, 2)
-
-        # ## Plotting type
-        self.ncc_plotting_radio = RadioSet([{'label': _('Normal'), 'value': 'normal'},
-                                            {"label": _("Progressive"), "value": "progressive"}])
-        plotting_label = QtWidgets.QLabel('%s:' % _("NCC Plotting"))
-        plotting_label.setToolTip(
-            _("- 'Normal' -  normal plotting, done at the end of the NCC job\n"
-              "- 'Progressive' - after each shape is generated it will be plotted.")
-        )
-        grid0.addWidget(plotting_label, 21, 0)
-        grid0.addWidget(self.ncc_plotting_radio, 21, 1)
-
-        self.layout.addStretch()
-
-
-class ToolsCutoutPrefGroupUI(OptionsGroupUI):
-    def __init__(self, decimals=4, parent=None):
-        # OptionsGroupUI.__init__(self, "Cutout Tool Options", parent=parent)
-        super(ToolsCutoutPrefGroupUI, self).__init__(self)
-
-        self.setTitle(str(_("Cutout Tool Options")))
-        self.decimals = decimals
-
-        # ## Board cutout
-        self.board_cutout_label = QtWidgets.QLabel("<b>%s:</b>" % _("Parameters"))
-        self.board_cutout_label.setToolTip(
-            _("Create toolpaths to cut around\n"
-              "the PCB and separate it from\n"
-              "the original board.")
-        )
-        self.layout.addWidget(self.board_cutout_label)
-
-        grid0 = QtWidgets.QGridLayout()
-        self.layout.addLayout(grid0)
-
-        tdclabel = QtWidgets.QLabel('%s:' % _('Tool Diameter'))
-        tdclabel.setToolTip(
-            _("Diameter of the tool used to cutout\n"
-              "the PCB shape out of the surrounding material.")
-        )
-
-        self.cutout_tooldia_entry = FCDoubleSpinner()
-        self.cutout_tooldia_entry.set_range(0.000001, 9999.9999)
-        self.cutout_tooldia_entry.set_precision(self.decimals)
-        self.cutout_tooldia_entry.setSingleStep(0.1)
-
-        grid0.addWidget(tdclabel, 0, 0)
-        grid0.addWidget(self.cutout_tooldia_entry, 0, 1)
-
-        # Cut Z
-        cutzlabel = QtWidgets.QLabel('%s:' % _('Cut Z'))
-        cutzlabel.setToolTip(
-            _(
-                "Cutting depth (negative)\n"
-                "below the copper surface."
-            )
-        )
-        self.cutz_entry = FCDoubleSpinner()
-        self.cutz_entry.set_precision(self.decimals)
-
-        if machinist_setting == 0:
-            self.cutz_entry.setRange(-9999.9999, 0.0000)
-        else:
-            self.cutz_entry.setRange(-9999.9999, 9999.9999)
-
-        self.cutz_entry.setSingleStep(0.1)
-
-        grid0.addWidget(cutzlabel, 1, 0)
-        grid0.addWidget(self.cutz_entry, 1, 1)
-
-        # Multi-pass
-        self.mpass_cb = FCCheckBox('%s:' % _("Multi-Depth"))
-        self.mpass_cb.setToolTip(
-            _(
-                "Use multiple passes to limit\n"
-                "the cut depth in each pass. Will\n"
-                "cut multiple times until Cut Z is\n"
-                "reached."
-            )
-        )
-
-        self.maxdepth_entry = FCDoubleSpinner()
-        self.maxdepth_entry.set_precision(self.decimals)
-        self.maxdepth_entry.setRange(0, 9999.9999)
-        self.maxdepth_entry.setSingleStep(0.1)
-
-        self.maxdepth_entry.setToolTip(_("Depth of each pass (positive)."))
-
-        grid0.addWidget(self.mpass_cb, 2, 0)
-        grid0.addWidget(self.maxdepth_entry, 2, 1)
-
-        # Object kind
-        kindlabel = QtWidgets.QLabel('%s:' % _('Object kind'))
-        kindlabel.setToolTip(
-            _("Choice of what kind the object we want to cutout is.<BR>"
-              "- <B>Single</B>: contain a single PCB Gerber outline object.<BR>"
-              "- <B>Panel</B>: a panel PCB Gerber object, which is made\n"
-              "out of many individual PCB outlines.")
-        )
-
-        self.obj_kind_combo = RadioSet([
-            {"label": _("Single"), "value": "single"},
-            {"label": _("Panel"), "value": "panel"},
-        ])
-        grid0.addWidget(kindlabel, 3, 0)
-        grid0.addWidget(self.obj_kind_combo, 3, 1)
-
-        marginlabel = QtWidgets.QLabel('%s:' % _('Margin'))
-        marginlabel.setToolTip(
-            _("Margin over bounds. A positive value here\n"
-              "will make the cutout of the PCB further from\n"
-              "the actual PCB border")
-        )
-
-        self.cutout_margin_entry = FCDoubleSpinner()
-        self.cutout_margin_entry.set_range(-9999.9999, 9999.9999)
-        self.cutout_margin_entry.set_precision(self.decimals)
-        self.cutout_margin_entry.setSingleStep(0.1)
-
-        grid0.addWidget(marginlabel, 4, 0)
-        grid0.addWidget(self.cutout_margin_entry, 4, 1)
-
-        gaplabel = QtWidgets.QLabel('%s:' % _('Gap size'))
-        gaplabel.setToolTip(
-            _("The size of the bridge gaps in the cutout\n"
-              "used to keep the board connected to\n"
-              "the surrounding material (the one \n"
-              "from which the PCB is cutout).")
-        )
-
-        self.cutout_gap_entry = FCDoubleSpinner()
-        self.cutout_gap_entry.set_range(0.000001, 9999.9999)
-        self.cutout_gap_entry.set_precision(self.decimals)
-        self.cutout_gap_entry.setSingleStep(0.1)
-
-        grid0.addWidget(gaplabel, 5, 0)
-        grid0.addWidget(self.cutout_gap_entry, 5, 1)
-
-        gaps_label = QtWidgets.QLabel('%s:' % _('Gaps'))
-        gaps_label.setToolTip(
-            _("Number of gaps used for the cutout.\n"
-              "There can be maximum 8 bridges/gaps.\n"
-              "The choices are:\n"
-              "- None  - no gaps\n"
-              "- lr    - left + right\n"
-              "- tb    - top + bottom\n"
-              "- 4     - left + right +top + bottom\n"
-              "- 2lr   - 2*left + 2*right\n"
-              "- 2tb  - 2*top + 2*bottom\n"
-              "- 8     - 2*left + 2*right +2*top + 2*bottom")
-        )
-
-        self.gaps_combo = FCComboBox()
-        grid0.addWidget(gaps_label, 6, 0)
-        grid0.addWidget(self.gaps_combo, 6, 1)
-
-        gaps_items = ['None', 'LR', 'TB', '4', '2LR', '2TB', '8']
-        for it in gaps_items:
-            self.gaps_combo.addItem(it)
-            self.gaps_combo.setStyleSheet('background-color: rgb(255,255,255)')
-
-        # Surrounding convex box shape
-        self.convex_box = FCCheckBox('%s' % _("Convex Shape"))
-        self.convex_box.setToolTip(
-            _("Create a convex shape surrounding the entire PCB.\n"
-              "Used only if the source object type is Gerber.")
-        )
-        grid0.addWidget(self.convex_box, 7, 0, 1, 2)
-
-        self.layout.addStretch()
-
-
-class Tools2sidedPrefGroupUI(OptionsGroupUI):
-    def __init__(self, decimals=4, parent=None):
-        # OptionsGroupUI.__init__(self, "2sided Tool Options", parent=parent)
-        super(Tools2sidedPrefGroupUI, self).__init__(self)
-
-        self.setTitle(str(_("2Sided Tool Options")))
-        self.decimals = decimals
-
-        # ## Board cuttout
-        self.dblsided_label = QtWidgets.QLabel("<b>%s:</b>" % _("Parameters"))
-        self.dblsided_label.setToolTip(
-            _("A tool to help in creating a double sided\n"
-              "PCB using alignment holes.")
-        )
-        self.layout.addWidget(self.dblsided_label)
-
-        grid0 = QtWidgets.QGridLayout()
-        self.layout.addLayout(grid0)
-
-        # ## Drill diameter for alignment holes
-        self.drill_dia_entry = FCDoubleSpinner()
-        self.drill_dia_entry.set_range(0.000001, 9999.9999)
-        self.drill_dia_entry.set_precision(self.decimals)
-        self.drill_dia_entry.setSingleStep(0.1)
-
-        self.dd_label = QtWidgets.QLabel('%s:' % _("Drill dia"))
-        self.dd_label.setToolTip(
-            _("Diameter of the drill for the "
-              "alignment holes.")
-        )
-        grid0.addWidget(self.dd_label, 0, 0)
-        grid0.addWidget(self.drill_dia_entry, 0, 1)
-
-        # ## Alignment Axis
-        self.align_ax_label = QtWidgets.QLabel('%s:' % _("Align Axis"))
-        self.align_ax_label.setToolTip(
-            _("Mirror vertically (X) or horizontally (Y).")
-        )
-        self.align_axis_radio = RadioSet([{'label': 'X', 'value': 'X'},
-                                          {'label': 'Y', 'value': 'Y'}])
-
-        grid0.addWidget(self.align_ax_label, 1, 0)
-        grid0.addWidget(self.align_axis_radio, 1, 1)
-
-        # ## Axis
-        self.mirror_axis_radio = RadioSet([{'label': 'X', 'value': 'X'},
-                                           {'label': 'Y', 'value': 'Y'}])
-        self.mirax_label = QtWidgets.QLabel(_("Mirror Axis:"))
-        self.mirax_label.setToolTip(
-            _("Mirror vertically (X) or horizontally (Y).")
-        )
-
-        self.empty_lb1 = QtWidgets.QLabel("")
-        grid0.addWidget(self.empty_lb1, 2, 0)
-        grid0.addWidget(self.mirax_label, 3, 0)
-        grid0.addWidget(self.mirror_axis_radio, 3, 1)
-
-        # ## Axis Location
-        self.axis_location_radio = RadioSet([{'label': _('Point'), 'value': 'point'},
-                                             {'label': _('Box'), 'value': 'box'}])
-        self.axloc_label = QtWidgets.QLabel('%s:' % _("Axis Ref"))
-        self.axloc_label.setToolTip(
-            _("The axis should pass through a <b>point</b> or cut\n "
-              "a specified <b>box</b> (in a FlatCAM object) through \n"
-              "the center.")
-        )
-
-        grid0.addWidget(self.axloc_label, 4, 0)
-        grid0.addWidget(self.axis_location_radio, 4, 1)
-
-        self.layout.addStretch()
-
-
-class ToolsPaintPrefGroupUI(OptionsGroupUI):
-    def __init__(self, decimals=4, parent=None):
-        # OptionsGroupUI.__init__(self, "Paint Area Tool Options", parent=parent)
-        super(ToolsPaintPrefGroupUI, self).__init__(self)
-
-        self.setTitle(str(_("Paint Tool Options")))
-        self.decimals = decimals
-
-        # ------------------------------
-        # ## Paint area
-        # ------------------------------
-        self.paint_label = QtWidgets.QLabel(_('<b>Parameters:</b>'))
-        self.paint_label.setToolTip(
-            _("Creates tool paths to cover the\n"
-              "whole area of a polygon (remove\n"
-              "all copper). You will be asked\n"
-              "to click on the desired polygon.")
-        )
-        self.layout.addWidget(self.paint_label)
-
-        grid0 = QtWidgets.QGridLayout()
-        grid0.setColumnStretch(0, 0)
-        grid0.setColumnStretch(1, 1)
-        self.layout.addLayout(grid0)
-
-        # Tool dia
-        ptdlabel = QtWidgets.QLabel('<b><font color="green">%s:</font></b>' % _('Tools Dia'))
-        ptdlabel.setToolTip(
-            _("Diameters of the tools, separated by comma.\n"
-              "The value of the diameter has to use the dot decimals separator.\n"
-              "Valid values: 0.3, 1.0")
-        )
-        grid0.addWidget(ptdlabel, 0, 0)
-
-        self.painttooldia_entry = FCEntry(border_color='#0069A9')
-        self.painttooldia_entry.setPlaceholderText(_("Comma separated values"))
-
-        grid0.addWidget(self.painttooldia_entry, 0, 1)
-
-        # Tool Type Radio Button
-        self.tool_type_label = QtWidgets.QLabel('%s:' % _('Tool Type'))
-        self.tool_type_label.setToolTip(
-            _("Default tool type:\n"
-              "- 'V-shape'\n"
-              "- Circular")
-        )
-
-        self.tool_type_radio = RadioSet([{'label': _('V-shape'), 'value': 'V'},
-                                         {'label': _('Circular'), 'value': 'C1'}])
-
-        self.tool_type_radio.setObjectName(_("Tool Type"))
-
-        grid0.addWidget(self.tool_type_label, 1, 0)
-        grid0.addWidget(self.tool_type_radio, 1, 1)
-
-        # Tip Dia
-        self.tipdialabel = QtWidgets.QLabel('%s:' % _('V-Tip Dia'))
-        self.tipdialabel.setToolTip(
-            _("The tip diameter for V-Shape Tool"))
-        self.tipdia_entry = FCDoubleSpinner()
-        self.tipdia_entry.set_precision(self.decimals)
-        self.tipdia_entry.set_range(0.0000, 9999.9999)
-        self.tipdia_entry.setSingleStep(0.1)
-        self.tipdia_entry.setObjectName(_("V-Tip Dia"))
-
-        grid0.addWidget(self.tipdialabel, 2, 0)
-        grid0.addWidget(self.tipdia_entry, 2, 1)
-
-        # Tip Angle
-        self.tipanglelabel = QtWidgets.QLabel('%s:' % _('V-Tip Angle'))
-        self.tipanglelabel.setToolTip(
-            _("The tip angle for V-Shape Tool.\n"
-              "In degree."))
-        self.tipangle_entry = FCDoubleSpinner()
-        self.tipangle_entry.set_precision(self.decimals)
-        self.tipangle_entry.set_range(1.0000, 180.0000)
-        self.tipangle_entry.setSingleStep(5)
-        self.tipangle_entry.setObjectName(_("V-Tip Angle"))
-
-        grid0.addWidget(self.tipanglelabel, 3, 0)
-        grid0.addWidget(self.tipangle_entry, 3, 1)
-
-        # Cut Z entry
-        cutzlabel = QtWidgets.QLabel('%s:' % _('Cut Z'))
-        cutzlabel.setToolTip(
-            _("Depth of cut into material. Negative value.\n"
-              "In FlatCAM units.")
-        )
-        self.cutz_entry = FCDoubleSpinner()
-        self.cutz_entry.set_precision(self.decimals)
-        self.cutz_entry.set_range(-99999.9999, 0.0000)
-        self.cutz_entry.setObjectName(_("Cut Z"))
-
-        self.cutz_entry.setToolTip(
-            _("Depth of cut into material. Negative value.\n"
-              "In FlatCAM units.")
-        )
-        grid0.addWidget(cutzlabel, 4, 0)
-        grid0.addWidget(self.cutz_entry, 4, 1)
-
-        # ### Tool Diameter ####
-        self.newdialabel = QtWidgets.QLabel('%s:' % _('New Dia'))
-        self.newdialabel.setToolTip(
-            _("Diameter for the new tool to add in the Tool Table.\n"
-              "If the tool is V-shape type then this value is automatically\n"
-              "calculated from the other parameters.")
-        )
-        self.newdia_entry = FCDoubleSpinner()
-        self.newdia_entry.set_precision(self.decimals)
-        self.newdia_entry.set_range(0.000, 9999.9999)
-        self.newdia_entry.setObjectName(_("Tool Dia"))
-
-        grid0.addWidget(self.newdialabel, 5, 0)
-        grid0.addWidget(self.newdia_entry, 5, 1)
-
-        separator_line = QtWidgets.QFrame()
-        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
-        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
-        grid0.addWidget(separator_line, 6, 0, 1, 2)
-
-        self.paint_order_label = QtWidgets.QLabel('%s:' % _('Tool order'))
-        self.paint_order_label.setToolTip(_("This set the way that the tools in the tools table are used.\n"
-                                            "'No' --> means that the used order is the one in the tool table\n"
-                                            "'Forward' --> means that the tools will be ordered from small to big\n"
-                                            "'Reverse' --> menas that the tools will ordered from big to small\n\n"
-                                            "WARNING: using rest machining will automatically set the order\n"
-                                            "in reverse and disable this control."))
-
-        self.paint_order_radio = RadioSet([{'label': _('No'), 'value': 'no'},
-                                           {'label': _('Forward'), 'value': 'fwd'},
-                                           {'label': _('Reverse'), 'value': 'rev'}])
-
-        grid0.addWidget(self.paint_order_label, 7, 0)
-        grid0.addWidget(self.paint_order_radio, 7, 1)
-
-        separator_line = QtWidgets.QFrame()
-        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
-        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
-        grid0.addWidget(separator_line, 8, 0, 1, 2)
-
-        # Overlap
-        ovlabel = QtWidgets.QLabel('%s:' % _('Overlap'))
-        ovlabel.setToolTip(
-            _("How much (percentage) of the tool width to overlap each tool pass.\n"
-              "Adjust the value starting with lower values\n"
-              "and increasing it if areas that should be painted are still \n"
-              "not painted.\n"
-              "Lower values = faster processing, faster execution on CNC.\n"
-              "Higher values = slow processing and slow execution on CNC\n"
-              "due of too many paths.")
-        )
-        self.paintoverlap_entry = FCDoubleSpinner(suffix='%')
-        self.paintoverlap_entry.set_precision(self.decimals)
-        self.paintoverlap_entry.setWrapping(True)
-        self.paintoverlap_entry.setRange(0.0000, 99.9999)
-        self.paintoverlap_entry.setSingleStep(0.1)
-
-        grid0.addWidget(ovlabel, 9, 0)
-        grid0.addWidget(self.paintoverlap_entry, 9, 1)
-
-        # Margin
-        marginlabel = QtWidgets.QLabel('%s:' % _('Margin'))
-        marginlabel.setToolTip(
-            _("Distance by which to avoid\n"
-              "the edges of the polygon to\n"
-              "be painted.")
-        )
-        self.paintmargin_entry = FCDoubleSpinner()
-        self.paintmargin_entry.set_range(-9999.9999, 9999.9999)
-        self.paintmargin_entry.set_precision(self.decimals)
-        self.paintmargin_entry.setSingleStep(0.1)
-
-        grid0.addWidget(marginlabel, 10, 0)
-        grid0.addWidget(self.paintmargin_entry, 10, 1)
-
-        # Method
-        methodlabel = QtWidgets.QLabel('%s:' % _('Method'))
-        methodlabel.setToolTip(
-            _("Algorithm for painting:\n"
-              "- Standard: Fixed step inwards.\n"
-              "- Seed-based: Outwards from seed.\n"
-              "- Line-based: Parallel lines.\n"
-              "- Laser-lines: Active only for Gerber objects.\n"
-              "Will create lines that follow the traces.\n"
-              "- Combo: In case of failure a new method will be picked from the above\n"
-              "in the order specified.")
-        )
-
-        # self.paintmethod_combo = RadioSet([
-        #     {"label": _("Standard"), "value": "standard"},
-        #     {"label": _("Seed-based"), "value": "seed"},
-        #     {"label": _("Straight lines"), "value": "lines"}
-        # ], orientation='vertical', stretch=False)
-        self.paintmethod_combo = FCComboBox()
-        self.paintmethod_combo.addItems(
-            [_("Standard"), _("Seed"), _("Lines"), _("Laser_lines"), _("Combo")]
-        )
-
-        grid0.addWidget(methodlabel, 11, 0)
-        grid0.addWidget(self.paintmethod_combo, 11, 1)
-
-        # Connect lines
-        self.pathconnect_cb = FCCheckBox('%s' % _("Connect"))
-        self.pathconnect_cb.setToolTip(
-            _("Draw lines between resulting\n"
-              "segments to minimize tool lifts.")
-        )
-        grid0.addWidget(self.pathconnect_cb, 12, 0)
-
-        # Paint contour
-        self.contour_cb = FCCheckBox('%s' % _("Contour"))
-        self.contour_cb.setToolTip(
-            _("Cut around the perimeter of the polygon\n"
-              "to trim rough edges.")
-        )
-        grid0.addWidget(self.contour_cb, 12, 1)
-
-        separator_line = QtWidgets.QFrame()
-        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
-        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
-        grid0.addWidget(separator_line, 13, 0, 1, 2)
-
-        self.rest_cb = FCCheckBox('%s' % _("Rest Machining"))
-        self.rest_cb.setObjectName(_("Rest Machining"))
-        self.rest_cb.setToolTip(
-            _("If checked, use 'rest machining'.\n"
-              "Basically it will clear copper outside PCB features,\n"
-              "using the biggest tool and continue with the next tools,\n"
-              "from bigger to smaller, to clear areas of copper that\n"
-              "could not be cleared by previous tool, until there is\n"
-              "no more copper to clear or there are no more tools.\n\n"
-              "If not checked, use the standard algorithm.")
-        )
-        grid0.addWidget(self.rest_cb, 14, 0, 1, 2)
-
-        # Polygon selection
-        selectlabel = QtWidgets.QLabel('%s:' % _('Selection'))
-        selectlabel.setToolTip(
-            _("Selection of area to be processed.\n"
-              "- 'Polygon Selection' - left mouse click to add/remove polygons to be processed.\n"
-              "- 'Area Selection' - left mouse click to start selection of the area to be processed.\n"
-              "Keeping a modifier key pressed (CTRL or SHIFT) will allow to add multiple areas.\n"
-              "- 'All Polygons' - the process will start after click.\n"
-              "- 'Reference Object' - will process the area specified by another object.")
-        )
-
-        # self.selectmethod_combo = RadioSet(
-        #     [
-        #         {"label": _("Polygon Selection"), "value": "single"},
-        #         {"label": _("Area Selection"), "value": "area"},
-        #         {"label": _("All Polygons"), "value": "all"},
-        #         {"label": _("Reference Object"), "value": "ref"}
-        #     ],
-        #     orientation='vertical',
-        #     stretch=None
-        # )
-        self.selectmethod_combo = FCComboBox()
-        self.selectmethod_combo.addItems(
-            [_("Polygon Selection"), _("Area Selection"), _("All Polygons"), _("Reference Object")]
-        )
-
-        grid0.addWidget(selectlabel, 15, 0)
-        grid0.addWidget(self.selectmethod_combo, 15, 1)
-
-        self.area_shape_label = QtWidgets.QLabel('%s:' % _("Shape"))
-        self.area_shape_label.setToolTip(
-            _("The kind of selection shape used for area selection.")
-        )
-
-        self.area_shape_radio = RadioSet([{'label': _("Square"), 'value': 'square'},
-                                          {'label': _("Polygon"), 'value': 'polygon'}])
-
-        grid0.addWidget(self.area_shape_label, 18, 0)
-        grid0.addWidget(self.area_shape_radio, 18, 1)
-
-        separator_line = QtWidgets.QFrame()
-        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
-        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
-        grid0.addWidget(separator_line, 19, 0, 1, 2)
-
-        # ## Plotting type
-        self.paint_plotting_radio = RadioSet([{'label': _('Normal'), 'value': 'normal'},
-                                              {"label": _("Progressive"), "value": "progressive"}])
-        plotting_label = QtWidgets.QLabel('%s:' % _("Paint Plotting"))
-        plotting_label.setToolTip(
-            _("- 'Normal' -  normal plotting, done at the end of the Paint job\n"
-              "- 'Progressive' - after each shape is generated it will be plotted.")
-        )
-        grid0.addWidget(plotting_label, 20, 0)
-        grid0.addWidget(self.paint_plotting_radio, 20, 1)
-
-        self.layout.addStretch()
-
-
-class ToolsFilmPrefGroupUI(OptionsGroupUI):
-    def __init__(self, decimals=4, parent=None):
-        # OptionsGroupUI.__init__(self, "Cutout Tool Options", parent=parent)
-        super(ToolsFilmPrefGroupUI, self).__init__(self)
-
-        self.setTitle(str(_("Film Tool Options")))
-        self.decimals = decimals
-
-        # ## Parameters
-        self.film_label = QtWidgets.QLabel("<b>%s:</b>" % _("Parameters"))
-        self.film_label.setToolTip(
-            _("Create a PCB film from a Gerber or Geometry\n"
-              "FlatCAM object.\n"
-              "The file is saved in SVG format.")
-        )
-        self.layout.addWidget(self.film_label)
-
-        grid0 = QtWidgets.QGridLayout()
-        self.layout.addLayout(grid0)
-
-        self.film_type_radio = RadioSet([{'label': 'Pos', 'value': 'pos'},
-                                         {'label': 'Neg', 'value': 'neg'}])
-        ftypelbl = QtWidgets.QLabel('%s:' % _('Film Type'))
-        ftypelbl.setToolTip(
-            _("Generate a Positive black film or a Negative film.\n"
-              "Positive means that it will print the features\n"
-              "with black on a white canvas.\n"
-              "Negative means that it will print the features\n"
-              "with white on a black canvas.\n"
-              "The Film format is SVG.")
-        )
-        grid0.addWidget(ftypelbl, 0, 0)
-        grid0.addWidget(self.film_type_radio, 0, 1)
-
-        # Film Color
-        self.film_color_label = QtWidgets.QLabel('%s:' % _('Film Color'))
-        self.film_color_label.setToolTip(
-            _("Set the film color when positive film is selected.")
-        )
-        self.film_color_entry = FCEntry()
-        self.film_color_button = QtWidgets.QPushButton()
-        self.film_color_button.setFixedSize(15, 15)
-
-        self.form_box_child = QtWidgets.QHBoxLayout()
-        self.form_box_child.setContentsMargins(0, 0, 0, 0)
-        self.form_box_child.addWidget(self.film_color_entry)
-        self.form_box_child.addWidget(self.film_color_button, alignment=Qt.AlignRight)
-        self.form_box_child.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
-
-        film_color_widget = QtWidgets.QWidget()
-        film_color_widget.setLayout(self.form_box_child)
-        grid0.addWidget(self.film_color_label, 1, 0)
-        grid0.addWidget(film_color_widget, 1, 1)
-
-        # Film Border
-        self.film_boundary_entry = FCDoubleSpinner()
-        self.film_boundary_entry.set_precision(self.decimals)
-        self.film_boundary_entry.set_range(0, 9999.9999)
-        self.film_boundary_entry.setSingleStep(0.1)
-
-        self.film_boundary_label = QtWidgets.QLabel('%s:' % _("Border"))
-        self.film_boundary_label.setToolTip(
-            _("Specify a border around the object.\n"
-              "Only for negative film.\n"
-              "It helps if we use as a Box Object the same \n"
-              "object as in Film Object. It will create a thick\n"
-              "black bar around the actual print allowing for a\n"
-              "better delimitation of the outline features which are of\n"
-              "white color like the rest and which may confound with the\n"
-              "surroundings if not for this border.")
-        )
-        grid0.addWidget(self.film_boundary_label, 2, 0)
-        grid0.addWidget(self.film_boundary_entry, 2, 1)
-
-        self.film_scale_stroke_entry = FCDoubleSpinner()
-        self.film_scale_stroke_entry.set_precision(self.decimals)
-        self.film_scale_stroke_entry.set_range(0, 9999.9999)
-        self.film_scale_stroke_entry.setSingleStep(0.1)
-
-        self.film_scale_stroke_label = QtWidgets.QLabel('%s:' % _("Scale Stroke"))
-        self.film_scale_stroke_label.setToolTip(
-            _("Scale the line stroke thickness of each feature in the SVG file.\n"
-              "It means that the line that envelope each SVG feature will be thicker or thinner,\n"
-              "therefore the fine features may be more affected by this parameter.")
-        )
-        grid0.addWidget(self.film_scale_stroke_label, 3, 0)
-        grid0.addWidget(self.film_scale_stroke_entry, 3, 1)
-
-        self.film_adj_label = QtWidgets.QLabel('<b>%s</b>' % _("Film Adjustments"))
-        self.film_adj_label.setToolTip(
-            _("Sometime the printers will distort the print shape, especially the Laser types.\n"
-              "This section provide the tools to compensate for the print distortions.")
-        )
-
-        grid0.addWidget(self.film_adj_label, 4, 0, 1, 2)
-
-        # Scale Geometry
-        self.film_scale_cb = FCCheckBox('%s' % _("Scale Film geometry"))
-        self.film_scale_cb.setToolTip(
-            _("A value greater than 1 will stretch the film\n"
-              "while a value less than 1 will jolt it.")
-        )
-        self.film_scale_cb.setStyleSheet(
-            """
-            QCheckBox {font-weight: bold; color: black}
-            """
-        )
-        grid0.addWidget(self.film_scale_cb, 5, 0, 1, 2)
-
-        self.film_scalex_label = QtWidgets.QLabel('%s:' % _("X factor"))
-        self.film_scalex_entry = FCDoubleSpinner()
-        self.film_scalex_entry.set_range(-999.9999, 999.9999)
-        self.film_scalex_entry.set_precision(self.decimals)
-        self.film_scalex_entry.setSingleStep(0.01)
-
-        grid0.addWidget(self.film_scalex_label, 6, 0)
-        grid0.addWidget(self.film_scalex_entry, 6, 1)
-
-        self.film_scaley_label = QtWidgets.QLabel('%s:' % _("Y factor"))
-        self.film_scaley_entry = FCDoubleSpinner()
-        self.film_scaley_entry.set_range(-999.9999, 999.9999)
-        self.film_scaley_entry.set_precision(self.decimals)
-        self.film_scaley_entry.setSingleStep(0.01)
-
-        grid0.addWidget(self.film_scaley_label, 7, 0)
-        grid0.addWidget(self.film_scaley_entry, 7, 1)
-
-        # Skew Geometry
-        self.film_skew_cb = FCCheckBox('%s' % _("Skew Film geometry"))
-        self.film_skew_cb.setToolTip(
-            _("Positive values will skew to the right\n"
-              "while negative values will skew to the left.")
-        )
-        self.film_skew_cb.setStyleSheet(
-            """
-            QCheckBox {font-weight: bold; color: black}
-            """
-        )
-        grid0.addWidget(self.film_skew_cb, 8, 0, 1, 2)
-
-        self.film_skewx_label = QtWidgets.QLabel('%s:' % _("X angle"))
-        self.film_skewx_entry = FCDoubleSpinner()
-        self.film_skewx_entry.set_range(-999.9999, 999.9999)
-        self.film_skewx_entry.set_precision(self.decimals)
-        self.film_skewx_entry.setSingleStep(0.01)
-
-        grid0.addWidget(self.film_skewx_label, 9, 0)
-        grid0.addWidget(self.film_skewx_entry, 9, 1)
-
-        self.film_skewy_label = QtWidgets.QLabel('%s:' % _("Y angle"))
-        self.film_skewy_entry = FCDoubleSpinner()
-        self.film_skewy_entry.set_range(-999.9999, 999.9999)
-        self.film_skewy_entry.set_precision(self.decimals)
-        self.film_skewy_entry.setSingleStep(0.01)
-
-        grid0.addWidget(self.film_skewy_label, 10, 0)
-        grid0.addWidget(self.film_skewy_entry, 10, 1)
-
-        self.film_skew_ref_label = QtWidgets.QLabel('%s:' % _("Reference"))
-        self.film_skew_ref_label.setToolTip(
-            _("The reference point to be used as origin for the skew.\n"
-              "It can be one of the four points of the geometry bounding box.")
-        )
-        self.film_skew_reference = RadioSet([{'label': _('Bottom Left'), 'value': 'bottomleft'},
-                                             {'label': _('Top Left'), 'value': 'topleft'},
-                                             {'label': _('Bottom Right'), 'value': 'bottomright'},
-                                             {'label': _('Top right'), 'value': 'topright'}],
-                                            orientation='vertical',
-                                            stretch=False)
-
-        grid0.addWidget(self.film_skew_ref_label, 11, 0)
-        grid0.addWidget(self.film_skew_reference, 11, 1)
-
-        # Mirror Geometry
-        self.film_mirror_cb = FCCheckBox('%s' % _("Mirror Film geometry"))
-        self.film_mirror_cb.setToolTip(
-            _("Mirror the film geometry on the selected axis or on both.")
-        )
-        self.film_mirror_cb.setStyleSheet(
-            """
-            QCheckBox {font-weight: bold; color: black}
-            """
-        )
-        grid0.addWidget(self.film_mirror_cb, 12, 0, 1, 2)
-
-        self.film_mirror_axis = RadioSet([{'label': _('None'), 'value': 'none'},
-                                          {'label': _('X'), 'value': 'x'},
-                                          {'label': _('Y'), 'value': 'y'},
-                                          {'label': _('Both'), 'value': 'both'}],
-                                         stretch=False)
-        self.film_mirror_axis_label = QtWidgets.QLabel('%s:' % _("Mirror axis"))
-
-        grid0.addWidget(self.film_mirror_axis_label, 13, 0)
-        grid0.addWidget(self.film_mirror_axis, 13, 1)
-
-        separator_line3 = QtWidgets.QFrame()
-        separator_line3.setFrameShape(QtWidgets.QFrame.HLine)
-        separator_line3.setFrameShadow(QtWidgets.QFrame.Sunken)
-        grid0.addWidget(separator_line3, 14, 0, 1, 2)
-
-        self.file_type_radio = RadioSet([{'label': _('SVG'), 'value': 'svg'},
-                                         {'label': _('PNG'), 'value': 'png'},
-                                         {'label': _('PDF'), 'value': 'pdf'}
-                                         ], stretch=False)
-
-        self.file_type_label = QtWidgets.QLabel(_("Film Type:"))
-        self.file_type_label.setToolTip(
-            _("The file type of the saved film. Can be:\n"
-              "- 'SVG' -> open-source vectorial format\n"
-              "- 'PNG' -> raster image\n"
-              "- 'PDF' -> portable document format")
-        )
-        grid0.addWidget(self.file_type_label, 15, 0)
-        grid0.addWidget(self.file_type_radio, 15, 1)
-
-        # Page orientation
-        self.orientation_label = QtWidgets.QLabel('%s:' % _("Page Orientation"))
-        self.orientation_label.setToolTip(_("Can be:\n"
-                                            "- Portrait\n"
-                                            "- Landscape"))
-
-        self.orientation_radio = RadioSet([{'label': _('Portrait'), 'value': 'p'},
-                                           {'label': _('Landscape'), 'value': 'l'},
-                                           ], stretch=False)
-
-        grid0.addWidget(self.orientation_label, 16, 0)
-        grid0.addWidget(self.orientation_radio, 16, 1)
-
-        # Page Size
-        self.pagesize_label = QtWidgets.QLabel('%s:' % _("Page Size"))
-        self.pagesize_label.setToolTip(_("A selection of standard ISO 216 page sizes."))
-
-        self.pagesize_combo = FCComboBox()
-
-        self.pagesize = {}
-        self.pagesize.update(
-            {
-                'Bounds': None,
-                'A0': (841, 1189),
-                'A1': (594, 841),
-                'A2': (420, 594),
-                'A3': (297, 420),
-                'A4': (210, 297),
-                'A5': (148, 210),
-                'A6': (105, 148),
-                'A7': (74, 105),
-                'A8': (52, 74),
-                'A9': (37, 52),
-                'A10': (26, 37),
-
-                'B0': (1000, 1414),
-                'B1': (707, 1000),
-                'B2': (500, 707),
-                'B3': (353, 500),
-                'B4': (250, 353),
-                'B5': (176, 250),
-                'B6': (125, 176),
-                'B7': (88, 125),
-                'B8': (62, 88),
-                'B9': (44, 62),
-                'B10': (31, 44),
-
-                'C0': (917, 1297),
-                'C1': (648, 917),
-                'C2': (458, 648),
-                'C3': (324, 458),
-                'C4': (229, 324),
-                'C5': (162, 229),
-                'C6': (114, 162),
-                'C7': (81, 114),
-                'C8': (57, 81),
-                'C9': (40, 57),
-                'C10': (28, 40),
-
-                # American paper sizes
-                'LETTER': (8.5, 11),
-                'LEGAL': (8.5, 14),
-                'ELEVENSEVENTEEN': (11, 17),
-
-                # From https://en.wikipedia.org/wiki/Paper_size
-                'JUNIOR_LEGAL': (5, 8),
-                'HALF_LETTER': (5.5, 8),
-                'GOV_LETTER': (8, 10.5),
-                'GOV_LEGAL': (8.5, 13),
-                'LEDGER': (17, 11),
-            }
-        )
-
-        page_size_list = list(self.pagesize.keys())
-        self.pagesize_combo.addItems(page_size_list)
-
-        grid0.addWidget(self.pagesize_label, 17, 0)
-        grid0.addWidget(self.pagesize_combo, 17, 1)
-
-        self.layout.addStretch()
-
-
-class ToolsPanelizePrefGroupUI(OptionsGroupUI):
-    def __init__(self, decimals=4, parent=None):
-        # OptionsGroupUI.__init__(self, "Cutout Tool Options", parent=parent)
-        super(ToolsPanelizePrefGroupUI, self).__init__(self)
-
-        self.setTitle(str(_("Panelize Tool Options")))
-        self.decimals = decimals
-
-        # ## Board cuttout
-        self.panelize_label = QtWidgets.QLabel("<b>%s:</b>" % _("Parameters"))
-        self.panelize_label.setToolTip(
-            _("Create an object that contains an array of (x, y) elements,\n"
-              "each element is a copy of the source object spaced\n"
-              "at a X distance, Y distance of each other.")
-        )
-        self.layout.addWidget(self.panelize_label)
-
-        grid0 = QtWidgets.QGridLayout()
-        self.layout.addLayout(grid0)
-        grid0.setColumnStretch(0, 0)
-        grid0.setColumnStretch(1, 1)
-
-        # ## Spacing Columns
-        self.pspacing_columns = FCDoubleSpinner()
-        self.pspacing_columns.set_range(0.000001, 9999.9999)
-        self.pspacing_columns.set_precision(self.decimals)
-        self.pspacing_columns.setSingleStep(0.1)
-
-        self.spacing_columns_label = QtWidgets.QLabel('%s:' % _("Spacing cols"))
-        self.spacing_columns_label.setToolTip(
-            _("Spacing between columns of the desired panel.\n"
-              "In current units.")
-        )
-        grid0.addWidget(self.spacing_columns_label, 0, 0)
-        grid0.addWidget(self.pspacing_columns, 0, 1)
-
-        # ## Spacing Rows
-        self.pspacing_rows = FCDoubleSpinner()
-        self.pspacing_rows.set_range(0.000001, 9999.9999)
-        self.pspacing_rows.set_precision(self.decimals)
-        self.pspacing_rows.setSingleStep(0.1)
-
-        self.spacing_rows_label = QtWidgets.QLabel('%s:' % _("Spacing rows"))
-        self.spacing_rows_label.setToolTip(
-            _("Spacing between rows of the desired panel.\n"
-              "In current units.")
-        )
-        grid0.addWidget(self.spacing_rows_label, 1, 0)
-        grid0.addWidget(self.pspacing_rows, 1, 1)
-
-        # ## Columns
-        self.pcolumns = FCSpinner()
-        self.pcolumns.set_range(1, 1000)
-        self.pcolumns.set_step(1)
-
-        self.columns_label = QtWidgets.QLabel('%s:' % _("Columns"))
-        self.columns_label.setToolTip(
-            _("Number of columns of the desired panel")
-        )
-        grid0.addWidget(self.columns_label, 2, 0)
-        grid0.addWidget(self.pcolumns, 2, 1)
-
-        # ## Rows
-        self.prows = FCSpinner()
-        self.prows.set_range(1, 1000)
-        self.prows.set_step(1)
-
-        self.rows_label = QtWidgets.QLabel('%s:' % _("Rows"))
-        self.rows_label.setToolTip(
-            _("Number of rows of the desired panel")
-        )
-        grid0.addWidget(self.rows_label, 3, 0)
-        grid0.addWidget(self.prows, 3, 1)
-
-        # ## Type of resulting Panel object
-        self.panel_type_radio = RadioSet([{'label': _('Gerber'), 'value': 'gerber'},
-                                          {'label': _('Geo'), 'value': 'geometry'}])
-        self.panel_type_label = QtWidgets.QLabel('%s:' % _("Panel Type"))
-        self.panel_type_label.setToolTip(
-           _("Choose the type of object for the panel object:\n"
-             "- Gerber\n"
-             "- Geometry")
-        )
-
-        grid0.addWidget(self.panel_type_label, 4, 0)
-        grid0.addWidget(self.panel_type_radio, 4, 1)
-
-        # ## Constrains
-        self.pconstrain_cb = FCCheckBox('%s:' % _("Constrain within"))
-        self.pconstrain_cb.setToolTip(
-            _("Area define by DX and DY within to constrain the panel.\n"
-              "DX and DY values are in current units.\n"
-              "Regardless of how many columns and rows are desired,\n"
-              "the final panel will have as many columns and rows as\n"
-              "they fit completely within selected area.")
-        )
-        grid0.addWidget(self.pconstrain_cb, 5, 0, 1, 2)
-
-        self.px_width_entry = FCDoubleSpinner()
-        self.px_width_entry.set_range(0.000001, 9999.9999)
-        self.px_width_entry.set_precision(self.decimals)
-        self.px_width_entry.setSingleStep(0.1)
-
-        self.x_width_lbl = QtWidgets.QLabel('%s:' % _("Width (DX)"))
-        self.x_width_lbl.setToolTip(
-            _("The width (DX) within which the panel must fit.\n"
-              "In current units.")
-        )
-        grid0.addWidget(self.x_width_lbl, 6, 0)
-        grid0.addWidget(self.px_width_entry, 6, 1)
-
-        self.py_height_entry = FCDoubleSpinner()
-        self.py_height_entry.set_range(0.000001, 9999.9999)
-        self.py_height_entry.set_precision(self.decimals)
-        self.py_height_entry.setSingleStep(0.1)
-
-        self.y_height_lbl = QtWidgets.QLabel('%s:' % _("Height (DY)"))
-        self.y_height_lbl.setToolTip(
-            _("The height (DY)within which the panel must fit.\n"
-              "In current units.")
-        )
-        grid0.addWidget(self.y_height_lbl, 7, 0)
-        grid0.addWidget(self.py_height_entry, 7, 1)
-
-        self.layout.addStretch()
-
-
-class ToolsCalculatorsPrefGroupUI(OptionsGroupUI):
-    def __init__(self, decimals=4, parent=None):
-        # OptionsGroupUI.__init__(self, "Calculators Tool Options", parent=parent)
-        super(ToolsCalculatorsPrefGroupUI, self).__init__(self)
-
-        self.setTitle(str(_("Calculators Tool Options")))
-        self.decimals = decimals
-
-        # ## V-shape Calculator Tool
-        self.vshape_tool_label = QtWidgets.QLabel("<b>%s:</b>" % _("V-Shape Tool Calculator"))
-        self.vshape_tool_label.setToolTip(
-            _("Calculate the tool diameter for a given V-shape tool,\n"
-              "having the tip diameter, tip angle and\n"
-              "depth-of-cut as parameters.")
-        )
-        self.layout.addWidget(self.vshape_tool_label)
-
-        grid0 = QtWidgets.QGridLayout()
-        grid0.setColumnStretch(0, 0)
-        grid0.setColumnStretch(1, 1)
-        self.layout.addLayout(grid0)
-
-        # ## Tip Diameter
-        self.tip_dia_entry = FCDoubleSpinner()
-        self.tip_dia_entry.set_range(0.000001, 9999.9999)
-        self.tip_dia_entry.set_precision(self.decimals)
-        self.tip_dia_entry.setSingleStep(0.1)
-
-        self.tip_dia_label = QtWidgets.QLabel('%s:' % _("Tip Diameter"))
-        self.tip_dia_label.setToolTip(
-            _("This is the tool tip diameter.\n"
-              "It is specified by manufacturer.")
-        )
-        grid0.addWidget(self.tip_dia_label, 0, 0)
-        grid0.addWidget(self.tip_dia_entry, 0, 1)
-
-        # ## Tip angle
-        self.tip_angle_entry = FCDoubleSpinner()
-        self.tip_angle_entry.set_range(0.0, 180.0)
-        self.tip_angle_entry.set_precision(self.decimals)
-        self.tip_angle_entry.setSingleStep(5)
-
-        self.tip_angle_label = QtWidgets.QLabel('%s:' % _("Tip Angle"))
-        self.tip_angle_label.setToolTip(
-            _("This is the angle on the tip of the tool.\n"
-              "It is specified by manufacturer.")
-        )
-        grid0.addWidget(self.tip_angle_label, 1, 0)
-        grid0.addWidget(self.tip_angle_entry, 1, 1)
-
-        # ## Depth-of-cut Cut Z
-        self.cut_z_entry = FCDoubleSpinner()
-        self.cut_z_entry.set_range(-9999.9999, 0.0000)
-        self.cut_z_entry.set_precision(self.decimals)
-        self.cut_z_entry.setSingleStep(0.01)
-
-        self.cut_z_label = QtWidgets.QLabel('%s:' % _("Cut Z"))
-        self.cut_z_label.setToolTip(
-            _("This is depth to cut into material.\n"
-              "In the CNCJob object it is the CutZ parameter.")
-        )
-        grid0.addWidget(self.cut_z_label, 2, 0)
-        grid0.addWidget(self.cut_z_entry, 2, 1)
-
-        # ## Electroplating Calculator Tool
-        self.plate_title_label = QtWidgets.QLabel("<b>%s:</b>" % _("ElectroPlating Calculator"))
-        self.plate_title_label.setToolTip(
-            _("This calculator is useful for those who plate the via/pad/drill holes,\n"
-              "using a method like grahite ink or calcium hypophosphite ink or palladium chloride.")
-        )
-        grid0.addWidget(self.plate_title_label, 3, 0, 1, 2)
-
-        # ## PCB Length
-        self.pcblength_entry = FCDoubleSpinner()
-        self.pcblength_entry.set_range(0.000001, 9999.9999)
-        self.pcblength_entry.set_precision(self.decimals)
-        self.pcblength_entry.setSingleStep(0.1)
-
-        self.pcblengthlabel = QtWidgets.QLabel('%s:' % _("Board Length"))
-
-        self.pcblengthlabel.setToolTip(_('This is the board length. In centimeters.'))
-        grid0.addWidget(self.pcblengthlabel, 4, 0)
-        grid0.addWidget(self.pcblength_entry, 4, 1)
-
-        # ## PCB Width
-        self.pcbwidth_entry = FCDoubleSpinner()
-        self.pcbwidth_entry.set_range(0.000001, 9999.9999)
-        self.pcbwidth_entry.set_precision(self.decimals)
-        self.pcbwidth_entry.setSingleStep(0.1)
-
-        self.pcbwidthlabel = QtWidgets.QLabel('%s:' % _("Board Width"))
-
-        self.pcbwidthlabel.setToolTip(_('This is the board width.In centimeters.'))
-        grid0.addWidget(self.pcbwidthlabel, 5, 0)
-        grid0.addWidget(self.pcbwidth_entry, 5, 1)
-
-        # ## Current Density
-        self.cdensity_label = QtWidgets.QLabel('%s:' % _("Current Density"))
-        self.cdensity_entry = FCDoubleSpinner()
-        self.cdensity_entry.set_range(0.000001, 9999.9999)
-        self.cdensity_entry.set_precision(self.decimals)
-        self.cdensity_entry.setSingleStep(0.1)
-
-        self.cdensity_label.setToolTip(_("Current density to pass through the board. \n"
-                                         "In Amps per Square Feet ASF."))
-        grid0.addWidget(self.cdensity_label, 6, 0)
-        grid0.addWidget(self.cdensity_entry, 6, 1)
-
-        # ## PCB Copper Growth
-        self.growth_label = QtWidgets.QLabel('%s:' % _("Copper Growth"))
-        self.growth_entry = FCDoubleSpinner()
-        self.growth_entry.set_range(0.000001, 9999.9999)
-        self.growth_entry.set_precision(self.decimals)
-        self.growth_entry.setSingleStep(0.01)
-
-        self.growth_label.setToolTip(_("How thick the copper growth is intended to be.\n"
-                                       "In microns."))
-        grid0.addWidget(self.growth_label, 7, 0)
-        grid0.addWidget(self.growth_entry, 7, 1)
-
-        self.layout.addStretch()
-
-
-class ToolsTransformPrefGroupUI(OptionsGroupUI):
-    def __init__(self, decimals=4, parent=None):
-
-        super(ToolsTransformPrefGroupUI, self).__init__(self)
-
-        self.setTitle(str(_("Transform Tool Options")))
-        self.decimals = decimals
-
-        # ## Transformations
-        self.transform_label = QtWidgets.QLabel("<b>%s:</b>" % _("Parameters"))
-        self.transform_label.setToolTip(
-            _("Various transformations that can be applied\n"
-              "on a FlatCAM object.")
-        )
-        self.layout.addWidget(self.transform_label)
-
-        grid0 = QtWidgets.QGridLayout()
-        self.layout.addLayout(grid0)
-        grid0.setColumnStretch(0, 0)
-        grid0.setColumnStretch(1, 1)
-
-        # ## Rotate Angle
-
-        rotate_title_lbl = QtWidgets.QLabel('<b>%s</b>' % _("Rotate"))
-        grid0.addWidget(rotate_title_lbl, 0, 0, 1, 2)
-
-        self.rotate_entry = FCDoubleSpinner()
-        self.rotate_entry.set_range(-360.0, 360.0)
-        self.rotate_entry.set_precision(self.decimals)
-        self.rotate_entry.setSingleStep(15)
-
-        self.rotate_label = QtWidgets.QLabel('%s:' % _("Angle"))
-        self.rotate_label.setToolTip(
-            _("Angle for Rotation action, in degrees.\n"
-              "Float number between -360 and 359.\n"
-              "Positive numbers for CW motion.\n"
-              "Negative numbers for CCW motion.")
-        )
-        grid0.addWidget(self.rotate_label, 1, 0)
-        grid0.addWidget(self.rotate_entry, 1, 1)
-
-        # ## Skew/Shear Angle on X axis
-        skew_title_lbl = QtWidgets.QLabel('<b>%s</b>' % _("Skew"))
-        grid0.addWidget(skew_title_lbl, 2, 0, 1, 2)
-
-        self.skewx_entry = FCDoubleSpinner()
-        self.skewx_entry.set_range(-360.0, 360.0)
-        self.skewx_entry.set_precision(self.decimals)
-        self.skewx_entry.setSingleStep(0.1)
-
-        self.skewx_label = QtWidgets.QLabel('%s:' % _("X angle"))
-        self.skewx_label.setToolTip(
-            _("Angle for Skew action, in degrees.\n"
-              "Float number between -360 and 359.")
-        )
-        grid0.addWidget(self.skewx_label, 3, 0)
-        grid0.addWidget(self.skewx_entry, 3, 1)
-
-        # ## Skew/Shear Angle on Y axis
-        self.skewy_entry = FCDoubleSpinner()
-        self.skewy_entry.set_range(-360.0, 360.0)
-        self.skewy_entry.set_precision(self.decimals)
-        self.skewy_entry.setSingleStep(0.1)
-
-        self.skewy_label = QtWidgets.QLabel('%s:' % _("Y angle"))
-        self.skewy_label.setToolTip(
-            _("Angle for Skew action, in degrees.\n"
-              "Float number between -360 and 359.")
-        )
-        grid0.addWidget(self.skewy_label, 4, 0)
-        grid0.addWidget(self.skewy_entry, 4, 1)
-
-        # ## Scale
-        scale_title_lbl = QtWidgets.QLabel('<b>%s</b>' % _("Scale"))
-        grid0.addWidget(scale_title_lbl, 5, 0, 1, 2)
-
-        self.scalex_entry = FCDoubleSpinner()
-        self.scalex_entry.set_range(0, 9999.9999)
-        self.scalex_entry.set_precision(self.decimals)
-        self.scalex_entry.setSingleStep(0.1)
-
-        self.scalex_label = QtWidgets.QLabel('%s:' % _("X factor"))
-        self.scalex_label.setToolTip(
-            _("Factor for scaling on X axis.")
-        )
-        grid0.addWidget(self.scalex_label, 6, 0)
-        grid0.addWidget(self.scalex_entry, 6, 1)
-
-        # ## Scale factor on X axis
-        self.scaley_entry = FCDoubleSpinner()
-        self.scaley_entry.set_range(0, 9999.9999)
-        self.scaley_entry.set_precision(self.decimals)
-        self.scaley_entry.setSingleStep(0.1)
-
-        self.scaley_label = QtWidgets.QLabel('%s:' % _("Y factor"))
-        self.scaley_label.setToolTip(
-            _("Factor for scaling on Y axis.")
-        )
-        grid0.addWidget(self.scaley_label, 7, 0)
-        grid0.addWidget(self.scaley_entry, 7, 1)
-
-        # ## Link Scale factors
-        self.link_cb = FCCheckBox(_("Link"))
-        self.link_cb.setToolTip(
-            _("Scale the selected object(s)\n"
-              "using the Scale_X factor for both axis.")
-        )
-        grid0.addWidget(self.link_cb, 8, 0)
-
-        # ## Scale Reference
-        self.reference_cb = FCCheckBox('%s' % _("Scale Reference"))
-        self.reference_cb.setToolTip(
-            _("Scale the selected object(s)\n"
-              "using the origin reference when checked,\n"
-              "and the center of the biggest bounding box\n"
-              "of the selected objects when unchecked.")
-        )
-        grid0.addWidget(self.reference_cb, 8, 1)
-
-        # ## Offset
-        offset_title_lbl = QtWidgets.QLabel('<b>%s</b>' % _("Offset"))
-        grid0.addWidget(offset_title_lbl, 9, 0, 1, 2)
-
-        self.offx_entry = FCDoubleSpinner()
-        self.offx_entry.set_range(-9999.9999, 9999.9999)
-        self.offx_entry.set_precision(self.decimals)
-        self.offx_entry.setSingleStep(0.1)
-
-        self.offx_label = QtWidgets.QLabel('%s:' % _("X val"))
-        self.offx_label.setToolTip(
-           _("Distance to offset on X axis. In current units.")
-        )
-        grid0.addWidget(self.offx_label, 10, 0)
-        grid0.addWidget(self.offx_entry, 10, 1)
-
-        # ## Offset distance on Y axis
-        self.offy_entry = FCDoubleSpinner()
-        self.offy_entry.set_range(-9999.9999, 9999.9999)
-        self.offy_entry.set_precision(self.decimals)
-        self.offy_entry.setSingleStep(0.1)
-
-        self.offy_label = QtWidgets.QLabel('%s:' % _("Y val"))
-        self.offy_label.setToolTip(
-            _("Distance to offset on Y axis. In current units.")
-        )
-        grid0.addWidget(self.offy_label, 11, 0)
-        grid0.addWidget(self.offy_entry, 11, 1)
-
-        # ## Mirror
-        mirror_title_lbl = QtWidgets.QLabel('<b>%s</b>' % _("Mirror"))
-        grid0.addWidget(mirror_title_lbl, 12, 0, 1, 2)
-
-        # ## Mirror (Flip) Reference Point
-        self.mirror_reference_cb = FCCheckBox('%s' % _("Mirror Reference"))
-        self.mirror_reference_cb.setToolTip(
-            _("Flip the selected object(s)\n"
-              "around the point in Point Entry Field.\n"
-              "\n"
-              "The point coordinates can be captured by\n"
-              "left click on canvas together with pressing\n"
-              "SHIFT key. \n"
-              "Then click Add button to insert coordinates.\n"
-              "Or enter the coords in format (x, y) in the\n"
-              "Point Entry field and click Flip on X(Y)"))
-        grid0.addWidget(self.mirror_reference_cb, 13, 0, 1, 2)
-
-        self.flip_ref_label = QtWidgets.QLabel('%s' % _("Mirror Reference point"))
-        self.flip_ref_label.setToolTip(
-            _("Coordinates in format (x, y) used as reference for mirroring.\n"
-              "The 'x' in (x, y) will be used when using Flip on X and\n"
-              "the 'y' in (x, y) will be used when using Flip on Y and")
-        )
-        self.flip_ref_entry = EvalEntry2("(0, 0)")
-
-        grid0.addWidget(self.flip_ref_label, 14, 0, 1, 2)
-        grid0.addWidget(self.flip_ref_entry, 15, 0, 1, 2)
-
-        # ## Buffer
-        buffer_title_lbl = QtWidgets.QLabel('<b>%s</b>' % _("Buffer"))
-        grid0.addWidget(buffer_title_lbl, 16, 0, 1, 2)
-
-        self.buffer_label = QtWidgets.QLabel('%s:' % _("Distance"))
-        self.buffer_label.setToolTip(
-            _("A positive value will create the effect of dilation,\n"
-              "while a negative value will create the effect of erosion.\n"
-              "Each geometry element of the object will be increased\n"
-              "or decreased with the 'distance'.")
-        )
-
-        self.buffer_entry = FCDoubleSpinner()
-        self.buffer_entry.set_precision(self.decimals)
-        self.buffer_entry.setSingleStep(0.1)
-        self.buffer_entry.setWrapping(True)
-        self.buffer_entry.set_range(-9999.9999, 9999.9999)
-
-        grid0.addWidget(self.buffer_label, 17, 0)
-        grid0.addWidget(self.buffer_entry, 17, 1)
-
-        self.buffer_factor_label = QtWidgets.QLabel('%s:' % _("Value"))
-        self.buffer_factor_label.setToolTip(
-            _("A positive value will create the effect of dilation,\n"
-              "while a negative value will create the effect of erosion.\n"
-              "Each geometry element of the object will be increased\n"
-              "or decreased to fit the 'Value'. Value is a percentage\n"
-              "of the initial dimension.")
-        )
-
-        self.buffer_factor_entry = FCDoubleSpinner(suffix='%')
-        self.buffer_factor_entry.set_range(-100.0000, 1000.0000)
-        self.buffer_factor_entry.set_precision(self.decimals)
-        self.buffer_factor_entry.setWrapping(True)
-        self.buffer_factor_entry.setSingleStep(1)
-
-        grid0.addWidget(self.buffer_factor_label, 18, 0)
-        grid0.addWidget(self.buffer_factor_entry, 18, 1)
-
-        self.buffer_rounded_cb = FCCheckBox()
-        self.buffer_rounded_cb.setText('%s' % _("Rounded"))
-        self.buffer_rounded_cb.setToolTip(
-            _("If checked then the buffer will surround the buffered shape,\n"
-              "every corner will be rounded.\n"
-              "If not checked then the buffer will follow the exact geometry\n"
-              "of the buffered shape.")
-        )
-
-        grid0.addWidget(self.buffer_rounded_cb, 19, 0, 1, 2)
-
-        self.layout.addStretch()
-
-
-class ToolsSolderpastePrefGroupUI(OptionsGroupUI):
-    def __init__(self, decimals=4, parent=None):
-
-        super(ToolsSolderpastePrefGroupUI, self).__init__(self)
-
-        self.setTitle(str(_("SolderPaste Tool Options")))
-        self.decimals = decimals
-
-        # ## Solder Paste Dispensing
-        self.solderpastelabel = QtWidgets.QLabel("<b>%s:</b>" % _("Parameters"))
-        self.solderpastelabel.setToolTip(
-            _("A tool to create GCode for dispensing\n"
-              "solder paste onto a PCB.")
-        )
-        self.layout.addWidget(self.solderpastelabel)
-
-        grid0 = QtWidgets.QGridLayout()
-        self.layout.addLayout(grid0)
-
-        # Nozzle Tool Diameters
-        nozzletdlabel = QtWidgets.QLabel('<b><font color="green">%s:</font></b>' % _('Tools Dia'))
-        nozzletdlabel.setToolTip(
-            _("Diameters of the tools, separated by comma.\n"
-              "The value of the diameter has to use the dot decimals separator.\n"
-              "Valid values: 0.3, 1.0")
-        )
-        self.nozzle_tool_dia_entry = FCEntry()
-
-        grid0.addWidget(nozzletdlabel, 0, 0)
-        grid0.addWidget(self.nozzle_tool_dia_entry, 0, 1)
-
-        # New Nozzle Tool Dia
-        self.addtool_entry_lbl = QtWidgets.QLabel('<b>%s:</b>' % _('New Nozzle Dia'))
-        self.addtool_entry_lbl.setToolTip(
-            _("Diameter for the new Nozzle tool to add in the Tool Table")
-        )
-        self.addtool_entry = FCDoubleSpinner()
-        self.addtool_entry.set_precision(self.decimals)
-        self.addtool_entry.set_range(0.0000001, 9999.9999)
-        self.addtool_entry.setSingleStep(0.1)
-
-        grid0.addWidget(self.addtool_entry_lbl, 1, 0)
-        grid0.addWidget(self.addtool_entry, 1, 1)
-
-        # Z dispense start
-        self.z_start_entry = FCDoubleSpinner()
-        self.z_start_entry.set_precision(self.decimals)
-        self.z_start_entry.set_range(0.0000001, 9999.9999)
-        self.z_start_entry.setSingleStep(0.1)
-
-        self.z_start_label = QtWidgets.QLabel('%s:' % _("Z Dispense Start"))
-        self.z_start_label.setToolTip(
-            _("The height (Z) when solder paste dispensing starts.")
-        )
-        grid0.addWidget(self.z_start_label, 2, 0)
-        grid0.addWidget(self.z_start_entry, 2, 1)
-
-        # Z dispense
-        self.z_dispense_entry = FCDoubleSpinner()
-        self.z_dispense_entry.set_precision(self.decimals)
-        self.z_dispense_entry.set_range(0.0000001, 9999.9999)
-        self.z_dispense_entry.setSingleStep(0.1)
-
-        self.z_dispense_label = QtWidgets.QLabel('%s:' % _("Z Dispense"))
-        self.z_dispense_label.setToolTip(
-            _("The height (Z) when doing solder paste dispensing.")
-        )
-        grid0.addWidget(self.z_dispense_label, 3, 0)
-        grid0.addWidget(self.z_dispense_entry, 3, 1)
-
-        # Z dispense stop
-        self.z_stop_entry = FCDoubleSpinner()
-        self.z_stop_entry.set_precision(self.decimals)
-        self.z_stop_entry.set_range(0.0000001, 9999.9999)
-        self.z_stop_entry.setSingleStep(0.1)
-
-        self.z_stop_label = QtWidgets.QLabel('%s:' % _("Z Dispense Stop"))
-        self.z_stop_label.setToolTip(
-            _("The height (Z) when solder paste dispensing stops.")
-        )
-        grid0.addWidget(self.z_stop_label, 4, 0)
-        grid0.addWidget(self.z_stop_entry, 4, 1)
-
-        # Z travel
-        self.z_travel_entry = FCDoubleSpinner()
-        self.z_travel_entry.set_precision(self.decimals)
-        self.z_travel_entry.set_range(0.0000001, 9999.9999)
-        self.z_travel_entry.setSingleStep(0.1)
-
-        self.z_travel_label = QtWidgets.QLabel('%s:' % _("Z Travel"))
-        self.z_travel_label.setToolTip(
-            _("The height (Z) for travel between pads\n"
-              "(without dispensing solder paste).")
-        )
-        grid0.addWidget(self.z_travel_label, 5, 0)
-        grid0.addWidget(self.z_travel_entry, 5, 1)
-
-        # Z toolchange location
-        self.z_toolchange_entry = FCDoubleSpinner()
-        self.z_toolchange_entry.set_precision(self.decimals)
-        self.z_toolchange_entry.set_range(0.0000001, 9999.9999)
-        self.z_toolchange_entry.setSingleStep(0.1)
-
-        self.z_toolchange_label = QtWidgets.QLabel('%s:' % _("Z Toolchange"))
-        self.z_toolchange_label.setToolTip(
-            _("The height (Z) for tool (nozzle) change.")
-        )
-        grid0.addWidget(self.z_toolchange_label, 6, 0)
-        grid0.addWidget(self.z_toolchange_entry, 6, 1)
-
-        # X,Y Toolchange location
-        self.xy_toolchange_entry = FCEntry()
-        self.xy_toolchange_label = QtWidgets.QLabel('%s:' % _("Toolchange X-Y"))
-        self.xy_toolchange_label.setToolTip(
-            _("The X,Y location for tool (nozzle) change.\n"
-              "The format is (x, y) where x and y are real numbers.")
-        )
-        grid0.addWidget(self.xy_toolchange_label, 7, 0)
-        grid0.addWidget(self.xy_toolchange_entry, 7, 1)
-
-        # Feedrate X-Y
-        self.frxy_entry = FCDoubleSpinner()
-        self.frxy_entry.set_precision(self.decimals)
-        self.frxy_entry.set_range(0.0000001, 99999.9999)
-        self.frxy_entry.setSingleStep(0.1)
-
-        self.frxy_label = QtWidgets.QLabel('%s:' % _("Feedrate X-Y"))
-        self.frxy_label.setToolTip(
-            _("Feedrate (speed) while moving on the X-Y plane.")
-        )
-        grid0.addWidget(self.frxy_label, 8, 0)
-        grid0.addWidget(self.frxy_entry, 8, 1)
-
-        # Feedrate Z
-        self.frz_entry = FCDoubleSpinner()
-        self.frz_entry.set_precision(self.decimals)
-        self.frz_entry.set_range(0.0000001, 99999.9999)
-        self.frz_entry.setSingleStep(0.1)
-
-        self.frz_label = QtWidgets.QLabel('%s:' % _("Feedrate Z"))
-        self.frz_label.setToolTip(
-            _("Feedrate (speed) while moving vertically\n"
-              "(on Z plane).")
-        )
-        grid0.addWidget(self.frz_label, 9, 0)
-        grid0.addWidget(self.frz_entry, 9, 1)
-
-        # Feedrate Z Dispense
-        self.frz_dispense_entry = FCDoubleSpinner()
-        self.frz_dispense_entry.set_precision(self.decimals)
-        self.frz_dispense_entry.set_range(0.0000001, 99999.9999)
-        self.frz_dispense_entry.setSingleStep(0.1)
-
-        self.frz_dispense_label = QtWidgets.QLabel('%s:' % _("Feedrate Z Dispense"))
-        self.frz_dispense_label.setToolTip(
-            _("Feedrate (speed) while moving up vertically\n"
-              "to Dispense position (on Z plane).")
-        )
-        grid0.addWidget(self.frz_dispense_label, 10, 0)
-        grid0.addWidget(self.frz_dispense_entry, 10, 1)
-
-        # Spindle Speed Forward
-        self.speedfwd_entry = FCSpinner()
-        self.speedfwd_entry.set_range(0, 99999)
-        self.speedfwd_entry.set_step(1000)
-
-        self.speedfwd_label = QtWidgets.QLabel('%s:' % _("Spindle Speed FWD"))
-        self.speedfwd_label.setToolTip(
-            _("The dispenser speed while pushing solder paste\n"
-              "through the dispenser nozzle.")
-        )
-        grid0.addWidget(self.speedfwd_label, 11, 0)
-        grid0.addWidget(self.speedfwd_entry, 11, 1)
-
-        # Dwell Forward
-        self.dwellfwd_entry = FCDoubleSpinner()
-        self.dwellfwd_entry.set_precision(self.decimals)
-        self.dwellfwd_entry.set_range(0.0000001, 9999.9999)
-        self.dwellfwd_entry.setSingleStep(0.1)
-
-        self.dwellfwd_label = QtWidgets.QLabel('%s:' % _("Dwell FWD"))
-        self.dwellfwd_label.setToolTip(
-            _("Pause after solder dispensing.")
-        )
-        grid0.addWidget(self.dwellfwd_label, 12, 0)
-        grid0.addWidget(self.dwellfwd_entry, 12, 1)
-
-        # Spindle Speed Reverse
-        self.speedrev_entry = FCSpinner()
-        self.speedrev_entry.set_range(0, 999999)
-        self.speedrev_entry.set_step(1000)
-
-        self.speedrev_label = QtWidgets.QLabel('%s:' % _("Spindle Speed REV"))
-        self.speedrev_label.setToolTip(
-            _("The dispenser speed while retracting solder paste\n"
-              "through the dispenser nozzle.")
-        )
-        grid0.addWidget(self.speedrev_label, 13, 0)
-        grid0.addWidget(self.speedrev_entry, 13, 1)
-
-        # Dwell Reverse
-        self.dwellrev_entry = FCDoubleSpinner()
-        self.dwellrev_entry.set_precision(self.decimals)
-        self.dwellrev_entry.set_range(0.0000001, 9999.9999)
-        self.dwellrev_entry.setSingleStep(0.1)
-
-        self.dwellrev_label = QtWidgets.QLabel('%s:' % _("Dwell REV"))
-        self.dwellrev_label.setToolTip(
-            _("Pause after solder paste dispenser retracted,\n"
-              "to allow pressure equilibrium.")
-        )
-        grid0.addWidget(self.dwellrev_label, 14, 0)
-        grid0.addWidget(self.dwellrev_entry, 14, 1)
-
-        # Preprocessors
-        pp_label = QtWidgets.QLabel('%s:' % _('Preprocessor'))
-        pp_label.setToolTip(
-            _("Files that control the GCode generation.")
-        )
-
-        self.pp_combo = FCComboBox()
-        grid0.addWidget(pp_label, 15, 0)
-        grid0.addWidget(self.pp_combo, 15, 1)
-
-        self.layout.addStretch()
-
-
-class ToolsSubPrefGroupUI(OptionsGroupUI):
-    def __init__(self, decimals=4, parent=None):
-
-        super(ToolsSubPrefGroupUI, self).__init__(self)
-
-        self.setTitle(str(_("Substractor Tool Options")))
-        self.decimals = decimals
-
-        # ## Subtractor Tool Parameters
-        self.sublabel = QtWidgets.QLabel("<b>%s:</b>" % _("Parameters"))
-        self.sublabel.setToolTip(
-            _("A tool to substract one Gerber or Geometry object\n"
-              "from another of the same type.")
-        )
-        self.layout.addWidget(self.sublabel)
-
-        self.close_paths_cb = FCCheckBox(_("Close paths"))
-        self.close_paths_cb.setToolTip(_("Checking this will close the paths cut by the Geometry substractor object."))
-        self.layout.addWidget(self.close_paths_cb)
-
-        self.layout.addStretch()
-
-
-class Tools2RulesCheckPrefGroupUI(OptionsGroupUI):
-    def __init__(self, decimals=4, parent=None):
-
-        super(Tools2RulesCheckPrefGroupUI, self).__init__(self)
-
-        self.setTitle(str(_("Check Rules Tool Options")))
-        self.decimals = decimals
-
-        self.crlabel = QtWidgets.QLabel("<b>%s:</b>" % _("Parameters"))
-        self.crlabel.setToolTip(
-            _("A tool to check if Gerber files are within a set\n"
-              "of Manufacturing Rules.")
-        )
-        self.layout.addWidget(self.crlabel)
-
-        # Form Layout
-        self.form_layout_1 = QtWidgets.QFormLayout()
-        self.layout.addLayout(self.form_layout_1)
-
-        # Trace size
-        self.trace_size_cb = FCCheckBox('%s:' % _("Trace Size"))
-        self.trace_size_cb.setToolTip(
-            _("This checks if the minimum size for traces is met.")
-        )
-        self.form_layout_1.addRow(self.trace_size_cb)
-
-        # Trace size value
-        self.trace_size_entry = FCDoubleSpinner()
-        self.trace_size_entry.set_range(0.00001, 999.99999)
-        self.trace_size_entry.set_precision(self.decimals)
-        self.trace_size_entry.setSingleStep(0.1)
-
-        self.trace_size_lbl = QtWidgets.QLabel('%s:' % _("Min value"))
-        self.trace_size_lbl.setToolTip(
-            _("Minimum acceptable trace size.")
-        )
-        self.form_layout_1.addRow(self.trace_size_lbl, self.trace_size_entry)
-
-        # Copper2copper clearance
-        self.clearance_copper2copper_cb = FCCheckBox('%s:' % _("Copper to Copper clearance"))
-        self.clearance_copper2copper_cb.setToolTip(
-            _("This checks if the minimum clearance between copper\n"
-              "features is met.")
-        )
-        self.form_layout_1.addRow(self.clearance_copper2copper_cb)
-
-        # Copper2copper clearance value
-        self.clearance_copper2copper_entry = FCDoubleSpinner()
-        self.clearance_copper2copper_entry.set_range(0.00001, 999.99999)
-        self.clearance_copper2copper_entry.set_precision(self.decimals)
-        self.clearance_copper2copper_entry.setSingleStep(0.1)
-
-        self.clearance_copper2copper_lbl = QtWidgets.QLabel('%s:' % _("Min value"))
-        self.clearance_copper2copper_lbl.setToolTip(
-            _("Minimum acceptable clearance value.")
-        )
-        self.form_layout_1.addRow(self.clearance_copper2copper_lbl, self.clearance_copper2copper_entry)
-
-        # Copper2outline clearance
-        self.clearance_copper2ol_cb = FCCheckBox('%s:' % _("Copper to Outline clearance"))
-        self.clearance_copper2ol_cb.setToolTip(
-            _("This checks if the minimum clearance between copper\n"
-              "features and the outline is met.")
-        )
-        self.form_layout_1.addRow(self.clearance_copper2ol_cb)
-
-        # Copper2outline clearance value
-        self.clearance_copper2ol_entry = FCDoubleSpinner()
-        self.clearance_copper2ol_entry.set_range(0.00001, 999.99999)
-        self.clearance_copper2ol_entry.set_precision(self.decimals)
-        self.clearance_copper2ol_entry.setSingleStep(0.1)
-
-        self.clearance_copper2ol_lbl = QtWidgets.QLabel('%s:' % _("Min value"))
-        self.clearance_copper2ol_lbl.setToolTip(
-            _("Minimum acceptable clearance value.")
-        )
-        self.form_layout_1.addRow(self.clearance_copper2ol_lbl, self.clearance_copper2ol_entry)
-
-        # Silkscreen2silkscreen clearance
-        self.clearance_silk2silk_cb = FCCheckBox('%s:' % _("Silk to Silk Clearance"))
-        self.clearance_silk2silk_cb.setToolTip(
-            _("This checks if the minimum clearance between silkscreen\n"
-              "features and silkscreen features is met.")
-        )
-        self.form_layout_1.addRow(self.clearance_silk2silk_cb)
-
-        # Copper2silkscreen clearance value
-        self.clearance_silk2silk_entry = FCDoubleSpinner()
-        self.clearance_silk2silk_entry.set_range(0.00001, 999.99999)
-        self.clearance_silk2silk_entry.set_precision(self.decimals)
-        self.clearance_silk2silk_entry.setSingleStep(0.1)
-
-        self.clearance_silk2silk_lbl = QtWidgets.QLabel('%s:' % _("Min value"))
-        self.clearance_silk2silk_lbl.setToolTip(
-            _("Minimum acceptable clearance value.")
-        )
-        self.form_layout_1.addRow(self.clearance_silk2silk_lbl, self.clearance_silk2silk_entry)
-
-        # Silkscreen2soldermask clearance
-        self.clearance_silk2sm_cb = FCCheckBox('%s:' % _("Silk to Solder Mask Clearance"))
-        self.clearance_silk2sm_cb.setToolTip(
-            _("This checks if the minimum clearance between silkscreen\n"
-              "features and soldermask features is met.")
-        )
-        self.form_layout_1.addRow(self.clearance_silk2sm_cb)
-
-        # Silkscreen2soldermask clearance value
-        self.clearance_silk2sm_entry = FCDoubleSpinner()
-        self.clearance_silk2sm_entry.set_range(0.00001, 999.99999)
-        self.clearance_silk2sm_entry.set_precision(self.decimals)
-        self.clearance_silk2sm_entry.setSingleStep(0.1)
-
-        self.clearance_silk2sm_lbl = QtWidgets.QLabel('%s:' % _("Min value"))
-        self.clearance_silk2sm_lbl.setToolTip(
-            _("Minimum acceptable clearance value.")
-        )
-        self.form_layout_1.addRow(self.clearance_silk2sm_lbl, self.clearance_silk2sm_entry)
-
-        # Silk2outline clearance
-        self.clearance_silk2ol_cb = FCCheckBox('%s:' % _("Silk to Outline Clearance"))
-        self.clearance_silk2ol_cb.setToolTip(
-            _("This checks if the minimum clearance between silk\n"
-              "features and the outline is met.")
-        )
-        self.form_layout_1.addRow(self.clearance_silk2ol_cb)
-
-        # Silk2outline clearance value
-        self.clearance_silk2ol_entry = FCDoubleSpinner()
-        self.clearance_silk2ol_entry.set_range(0.00001, 999.99999)
-        self.clearance_silk2ol_entry.set_precision(self.decimals)
-        self.clearance_silk2ol_entry.setSingleStep(0.1)
-
-        self.clearance_silk2ol_lbl = QtWidgets.QLabel('%s:' % _("Min value"))
-        self.clearance_silk2ol_lbl.setToolTip(
-            _("Minimum acceptable clearance value.")
-        )
-        self.form_layout_1.addRow(self.clearance_silk2ol_lbl, self.clearance_silk2ol_entry)
-
-        # Soldermask2soldermask clearance
-        self.clearance_sm2sm_cb = FCCheckBox('%s:' % _("Minimum Solder Mask Sliver"))
-        self.clearance_sm2sm_cb.setToolTip(
-            _("This checks if the minimum clearance between soldermask\n"
-              "features and soldermask features is met.")
-        )
-        self.form_layout_1.addRow(self.clearance_sm2sm_cb)
-
-        # Soldermask2soldermask clearance value
-        self.clearance_sm2sm_entry = FCDoubleSpinner()
-        self.clearance_sm2sm_entry.set_range(0.00001, 999.99999)
-        self.clearance_sm2sm_entry.set_precision(self.decimals)
-        self.clearance_sm2sm_entry.setSingleStep(0.1)
-
-        self.clearance_sm2sm_lbl = QtWidgets.QLabel('%s:' % _("Min value"))
-        self.clearance_sm2sm_lbl.setToolTip(
-            _("Minimum acceptable clearance value.")
-        )
-        self.form_layout_1.addRow(self.clearance_sm2sm_lbl, self.clearance_sm2sm_entry)
-
-        # Ring integrity check
-        self.ring_integrity_cb = FCCheckBox('%s:' % _("Minimum Annular Ring"))
-        self.ring_integrity_cb.setToolTip(
-            _("This checks if the minimum copper ring left by drilling\n"
-              "a hole into a pad is met.")
-        )
-        self.form_layout_1.addRow(self.ring_integrity_cb)
-
-        # Ring integrity value
-        self.ring_integrity_entry = FCDoubleSpinner()
-        self.ring_integrity_entry.set_range(0.00001, 999.99999)
-        self.ring_integrity_entry.set_precision(self.decimals)
-        self.ring_integrity_entry.setSingleStep(0.1)
-
-        self.ring_integrity_lbl = QtWidgets.QLabel('%s:' % _("Min value"))
-        self.ring_integrity_lbl.setToolTip(
-            _("Minimum acceptable ring value.")
-        )
-        self.form_layout_1.addRow(self.ring_integrity_lbl, self.ring_integrity_entry)
-
-        self.form_layout_1.addRow(QtWidgets.QLabel(""))
-
-        # Hole2Hole clearance
-        self.clearance_d2d_cb = FCCheckBox('%s:' % _("Hole to Hole Clearance"))
-        self.clearance_d2d_cb.setToolTip(
-            _("This checks if the minimum clearance between a drill hole\n"
-              "and another drill hole is met.")
-        )
-        self.form_layout_1.addRow(self.clearance_d2d_cb)
-
-        # Hole2Hole clearance value
-        self.clearance_d2d_entry = FCDoubleSpinner()
-        self.clearance_d2d_entry.set_range(0.00001, 999.99999)
-        self.clearance_d2d_entry.set_precision(self.decimals)
-        self.clearance_d2d_entry.setSingleStep(0.1)
-
-        self.clearance_d2d_lbl = QtWidgets.QLabel('%s:' % _("Min value"))
-        self.clearance_d2d_lbl.setToolTip(
-            _("Minimum acceptable drill size.")
-        )
-        self.form_layout_1.addRow(self.clearance_d2d_lbl, self.clearance_d2d_entry)
-
-        # Drill holes size check
-        self.drill_size_cb = FCCheckBox('%s:' % _("Hole Size"))
-        self.drill_size_cb.setToolTip(
-            _("This checks if the drill holes\n"
-              "sizes are above the threshold.")
-        )
-        self.form_layout_1.addRow(self.drill_size_cb)
-
-        # Drile holes value
-        self.drill_size_entry = FCDoubleSpinner()
-        self.drill_size_entry.set_range(0.00001, 999.99999)
-        self.drill_size_entry.set_precision(self.decimals)
-        self.drill_size_entry.setSingleStep(0.1)
-
-        self.drill_size_lbl = QtWidgets.QLabel('%s:' % _("Min value"))
-        self.drill_size_lbl.setToolTip(
-            _("Minimum acceptable clearance value.")
-        )
-        self.form_layout_1.addRow(self.drill_size_lbl, self.drill_size_entry)
-
-        self.layout.addStretch()
-
-
-class Tools2OptimalPrefGroupUI(OptionsGroupUI):
-    def __init__(self, decimals=4, parent=None):
-
-        super(Tools2OptimalPrefGroupUI, self).__init__(self)
-
-        self.setTitle(str(_("Optimal Tool Options")))
-        self.decimals = decimals
-
-        # ## Parameters
-        self.optlabel = QtWidgets.QLabel("<b>%s:</b>" % _("Parameters"))
-        self.optlabel.setToolTip(
-            _("A tool to find the minimum distance between\n"
-              "every two Gerber geometric elements")
-        )
-        self.layout.addWidget(self.optlabel)
-
-        grid0 = QtWidgets.QGridLayout()
-        self.layout.addLayout(grid0)
-        grid0.setColumnStretch(0, 0)
-        grid0.setColumnStretch(1, 1)
-
-        self.precision_sp = FCSpinner()
-        self.precision_sp.set_range(2, 10)
-        self.precision_sp.set_step(1)
-        self.precision_sp.setWrapping(True)
-
-        self.precision_lbl = QtWidgets.QLabel('%s:' % _("Precision"))
-        self.precision_lbl.setToolTip(
-            _("Number of decimals for the distances and coordinates in this tool.")
-        )
-
-        grid0.addWidget(self.precision_lbl, 0, 0)
-        grid0.addWidget(self.precision_sp, 0, 1)
-
-        self.layout.addStretch()
-
-
-class Tools2QRCodePrefGroupUI(OptionsGroupUI):
-    def __init__(self, decimals=4, parent=None):
-
-        super(Tools2QRCodePrefGroupUI, self).__init__(self)
-
-        self.setTitle(str(_("QRCode Tool Options")))
-        self.decimals = decimals
-
-        # ## Parameters
-        self.qrlabel = QtWidgets.QLabel("<b>%s:</b>" % _("Parameters"))
-        self.qrlabel.setToolTip(
-            _("A tool to create a QRCode that can be inserted\n"
-              "into a selected Gerber file, or it can be exported as a file.")
-        )
-        self.layout.addWidget(self.qrlabel)
-
-        # ## Grid Layout
-        grid_lay = QtWidgets.QGridLayout()
-        self.layout.addLayout(grid_lay)
-        grid_lay.setColumnStretch(0, 0)
-        grid_lay.setColumnStretch(1, 1)
-
-        # VERSION #
-        self.version_label = QtWidgets.QLabel('%s:' % _("Version"))
-        self.version_label.setToolTip(
-            _("QRCode version can have values from 1 (21x21 boxes)\n"
-              "to 40 (177x177 boxes).")
-        )
-        self.version_entry = FCSpinner()
-        self.version_entry.set_range(1, 40)
-        self.version_entry.setWrapping(True)
-
-        grid_lay.addWidget(self.version_label, 1, 0)
-        grid_lay.addWidget(self.version_entry, 1, 1)
-
-        # ERROR CORRECTION #
-        self.error_label = QtWidgets.QLabel('%s:' % _("Error correction"))
-        self.error_label.setToolTip(
-            _("Parameter that controls the error correction used for the QR Code.\n"
-              "L = maximum 7%% errors can be corrected\n"
-              "M = maximum 15%% errors can be corrected\n"
-              "Q = maximum 25%% errors can be corrected\n"
-              "H = maximum 30%% errors can be corrected.")
-        )
-        self.error_radio = RadioSet([{'label': 'L', 'value': 'L'},
-                                     {'label': 'M', 'value': 'M'},
-                                     {'label': 'Q', 'value': 'Q'},
-                                     {'label': 'H', 'value': 'H'}])
-        self.error_radio.setToolTip(
-            _("Parameter that controls the error correction used for the QR Code.\n"
-              "L = maximum 7%% errors can be corrected\n"
-              "M = maximum 15%% errors can be corrected\n"
-              "Q = maximum 25%% errors can be corrected\n"
-              "H = maximum 30%% errors can be corrected.")
-        )
-        grid_lay.addWidget(self.error_label, 2, 0)
-        grid_lay.addWidget(self.error_radio, 2, 1)
-
-        # BOX SIZE #
-        self.bsize_label = QtWidgets.QLabel('%s:' % _("Box Size"))
-        self.bsize_label.setToolTip(
-            _("Box size control the overall size of the QRcode\n"
-              "by adjusting the size of each box in the code.")
-        )
-        self.bsize_entry = FCSpinner()
-        self.bsize_entry.set_range(1, 9999)
-        self.bsize_entry.setWrapping(True)
-
-        grid_lay.addWidget(self.bsize_label, 3, 0)
-        grid_lay.addWidget(self.bsize_entry, 3, 1)
-
-        # BORDER SIZE #
-        self.border_size_label = QtWidgets.QLabel('%s:' % _("Border Size"))
-        self.border_size_label.setToolTip(
-            _("Size of the QRCode border. How many boxes thick is the border.\n"
-              "Default value is 4. The width of the clearance around the QRCode.")
-        )
-        self.border_size_entry = FCSpinner()
-        self.border_size_entry.set_range(1, 9999)
-        self.border_size_entry.setWrapping(True)
-
-        grid_lay.addWidget(self.border_size_label, 4, 0)
-        grid_lay.addWidget(self.border_size_entry, 4, 1)
-
-        # Text box
-        self.text_label = QtWidgets.QLabel('%s:' % _("QRCode Data"))
-        self.text_label.setToolTip(
-            _("QRCode Data. Alphanumeric text to be encoded in the QRCode.")
-        )
-        self.text_data = FCTextArea()
-        self.text_data.setPlaceholderText(
-            _("Add here the text to be included in the QRCode...")
-        )
-        grid_lay.addWidget(self.text_label, 5, 0)
-        grid_lay.addWidget(self.text_data, 6, 0, 1, 2)
-
-        # POLARITY CHOICE #
-        self.pol_label = QtWidgets.QLabel('%s:' % _("Polarity"))
-        self.pol_label.setToolTip(
-            _("Choose the polarity of the QRCode.\n"
-              "It can be drawn in a negative way (squares are clear)\n"
-              "or in a positive way (squares are opaque).")
-        )
-        self.pol_radio = RadioSet([{'label': _('Negative'), 'value': 'neg'},
-                                   {'label': _('Positive'), 'value': 'pos'}])
-        self.pol_radio.setToolTip(
-            _("Choose the type of QRCode to be created.\n"
-              "If added on a Silkscreen Gerber file the QRCode may\n"
-              "be added as positive. If it is added to a Copper Gerber\n"
-              "file then perhaps the QRCode can be added as negative.")
-        )
-        grid_lay.addWidget(self.pol_label, 7, 0)
-        grid_lay.addWidget(self.pol_radio, 7, 1)
-
-        # BOUNDING BOX TYPE #
-        self.bb_label = QtWidgets.QLabel('%s:' % _("Bounding Box"))
-        self.bb_label.setToolTip(
-            _("The bounding box, meaning the empty space that surrounds\n"
-              "the QRCode geometry, can have a rounded or a square shape.")
-        )
-        self.bb_radio = RadioSet([{'label': _('Rounded'), 'value': 'r'},
-                                  {'label': _('Square'), 'value': 's'}])
-        self.bb_radio.setToolTip(
-            _("The bounding box, meaning the empty space that surrounds\n"
-              "the QRCode geometry, can have a rounded or a square shape.")
-        )
-        grid_lay.addWidget(self.bb_label, 8, 0)
-        grid_lay.addWidget(self.bb_radio, 8, 1)
-
-        # FILL COLOR #
-        self.fill_color_label = QtWidgets.QLabel('%s:' % _('Fill Color'))
-        self.fill_color_label.setToolTip(
-            _("Set the QRCode fill color (squares color).")
-        )
-        self.fill_color_entry = FCEntry()
-        self.fill_color_button = QtWidgets.QPushButton()
-        self.fill_color_button.setFixedSize(15, 15)
-
-        fill_lay_child = QtWidgets.QHBoxLayout()
-        fill_lay_child.setContentsMargins(0, 0, 0, 0)
-        fill_lay_child.addWidget(self.fill_color_entry)
-        fill_lay_child.addWidget(self.fill_color_button, alignment=Qt.AlignRight)
-        fill_lay_child.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
-
-        fill_color_widget = QtWidgets.QWidget()
-        fill_color_widget.setLayout(fill_lay_child)
-
-        grid_lay.addWidget(self.fill_color_label, 9, 0)
-        grid_lay.addWidget(fill_color_widget, 9, 1)
-
-        # BACK COLOR #
-        self.back_color_label = QtWidgets.QLabel('%s:' % _('Back Color'))
-        self.back_color_label.setToolTip(
-            _("Set the QRCode background color.")
-        )
-        self.back_color_entry = FCEntry()
-        self.back_color_button = QtWidgets.QPushButton()
-        self.back_color_button.setFixedSize(15, 15)
-
-        back_lay_child = QtWidgets.QHBoxLayout()
-        back_lay_child.setContentsMargins(0, 0, 0, 0)
-        back_lay_child.addWidget(self.back_color_entry)
-        back_lay_child.addWidget(self.back_color_button, alignment=Qt.AlignRight)
-        back_lay_child.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
-
-        back_color_widget = QtWidgets.QWidget()
-        back_color_widget.setLayout(back_lay_child)
-
-        grid_lay.addWidget(self.back_color_label, 10, 0)
-        grid_lay.addWidget(back_color_widget, 10, 1)
-
-        # Selection Limit
-        self.sel_limit_label = QtWidgets.QLabel('%s:' % _("Selection limit"))
-        self.sel_limit_label.setToolTip(
-            _("Set the number of selected geometry\n"
-              "items above which the utility geometry\n"
-              "becomes just a selection rectangle.\n"
-              "Increases the performance when moving a\n"
-              "large number of geometric elements.")
-        )
-        self.sel_limit_entry = FCSpinner()
-        self.sel_limit_entry.set_range(0, 9999)
-
-        grid_lay.addWidget(self.sel_limit_label, 11, 0)
-        grid_lay.addWidget(self.sel_limit_entry, 11, 1)
-        # self.layout.addStretch()
-
-
-class Tools2CThievingPrefGroupUI(OptionsGroupUI):
-    def __init__(self, decimals=4, parent=None):
-
-        super(Tools2CThievingPrefGroupUI, self).__init__(self)
-
-        self.setTitle(str(_("Copper Thieving Tool Options")))
-        self.decimals = decimals
-
-        # ## Grid Layout
-        grid_lay = QtWidgets.QGridLayout()
-        self.layout.addLayout(grid_lay)
-        grid_lay.setColumnStretch(0, 0)
-        grid_lay.setColumnStretch(1, 1)
-
-        # ## Parameters
-        self.cflabel = QtWidgets.QLabel('<b>%s</b>' % _('Parameters'))
-        self.cflabel.setToolTip(
-            _("A tool to generate a Copper Thieving that can be added\n"
-              "to a selected Gerber file.")
-        )
-        grid_lay.addWidget(self.cflabel, 0, 0, 1, 2)
-
-        # CIRCLE STEPS - to be used when buffering
-        self.circle_steps_lbl = QtWidgets.QLabel('%s:' % _("Circle Steps"))
-        self.circle_steps_lbl.setToolTip(
-            _("Number of steps (lines) used to interpolate circles.")
-        )
-
-        self.circlesteps_entry = FCSpinner()
-        self.circlesteps_entry.set_range(1, 9999)
-
-        grid_lay.addWidget(self.circle_steps_lbl, 1, 0)
-        grid_lay.addWidget(self.circlesteps_entry, 1, 1)
-
-        # CLEARANCE #
-        self.clearance_label = QtWidgets.QLabel('%s:' % _("Clearance"))
-        self.clearance_label.setToolTip(
-            _("This set the distance between the copper Thieving components\n"
-              "(the polygon fill may be split in multiple polygons)\n"
-              "and the copper traces in the Gerber file.")
-        )
-        self.clearance_entry = FCDoubleSpinner()
-        self.clearance_entry.setMinimum(0.00001)
-        self.clearance_entry.set_precision(self.decimals)
-        self.clearance_entry.setSingleStep(0.1)
-
-        grid_lay.addWidget(self.clearance_label, 2, 0)
-        grid_lay.addWidget(self.clearance_entry, 2, 1)
-
-        # MARGIN #
-        self.margin_label = QtWidgets.QLabel('%s:' % _("Margin"))
-        self.margin_label.setToolTip(
-            _("Bounding box margin.")
-        )
-        self.margin_entry = FCDoubleSpinner()
-        self.margin_entry.setMinimum(0.0)
-        self.margin_entry.set_precision(self.decimals)
-        self.margin_entry.setSingleStep(0.1)
-
-        grid_lay.addWidget(self.margin_label, 3, 0)
-        grid_lay.addWidget(self.margin_entry, 3, 1)
-
-        # Reference #
-        self.reference_radio = RadioSet([
-            {'label': _('Itself'), 'value': 'itself'},
-            {"label": _("Area Selection"), "value": "area"},
-            {'label': _("Reference Object"), 'value': 'box'}
-        ], orientation='vertical', stretch=False)
-        self.reference_label = QtWidgets.QLabel(_("Reference:"))
-        self.reference_label.setToolTip(
-            _("- 'Itself' - the copper Thieving extent is based on the object extent.\n"
-              "- 'Area Selection' - left mouse click to start selection of the area to be filled.\n"
-              "- 'Reference Object' - will do copper thieving within the area specified by another object.")
-        )
-        grid_lay.addWidget(self.reference_label, 4, 0)
-        grid_lay.addWidget(self.reference_radio, 4, 1)
-
-        # Bounding Box Type #
-        self.bbox_type_radio = RadioSet([
-            {'label': _('Rectangular'), 'value': 'rect'},
-            {"label": _("Minimal"), "value": "min"}
-        ], stretch=False)
-        self.bbox_type_label = QtWidgets.QLabel(_("Box Type:"))
-        self.bbox_type_label.setToolTip(
-            _("- 'Rectangular' - the bounding box will be of rectangular shape.\n"
-              "- 'Minimal' - the bounding box will be the convex hull shape.")
-        )
-        grid_lay.addWidget(self.bbox_type_label, 5, 0)
-        grid_lay.addWidget(self.bbox_type_radio, 5, 1)
-
-        separator_line = QtWidgets.QFrame()
-        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
-        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
-        grid_lay.addWidget(separator_line, 6, 0, 1, 2)
-
-        # Fill Type
-        self.fill_type_radio = RadioSet([
-            {'label': _('Solid'), 'value': 'solid'},
-            {"label": _("Dots Grid"), "value": "dot"},
-            {"label": _("Squares Grid"), "value": "square"},
-            {"label": _("Lines Grid"), "value": "line"}
-        ], orientation='vertical', stretch=False)
-        self.fill_type_label = QtWidgets.QLabel(_("Fill Type:"))
-        self.fill_type_label.setToolTip(
-            _("- 'Solid' - copper thieving will be a solid polygon.\n"
-              "- 'Dots Grid' - the empty area will be filled with a pattern of dots.\n"
-              "- 'Squares Grid' - the empty area will be filled with a pattern of squares.\n"
-              "- 'Lines Grid' - the empty area will be filled with a pattern of lines.")
-        )
-        grid_lay.addWidget(self.fill_type_label, 7, 0)
-        grid_lay.addWidget(self.fill_type_radio, 7, 1)
-
-        self.dots_label = QtWidgets.QLabel('<b>%s</b>:' % _("Dots Grid Parameters"))
-        grid_lay.addWidget(self.dots_label, 8, 0, 1, 2)
-
-        # Dot diameter #
-        self.dotdia_label = QtWidgets.QLabel('%s:' % _("Dia"))
-        self.dotdia_label.setToolTip(
-            _("Dot diameter in Dots Grid.")
-        )
-        self.dot_dia_entry = FCDoubleSpinner()
-        self.dot_dia_entry.set_range(0.0, 9999.9999)
-        self.dot_dia_entry.set_precision(self.decimals)
-        self.dot_dia_entry.setSingleStep(0.1)
-
-        grid_lay.addWidget(self.dotdia_label, 9, 0)
-        grid_lay.addWidget(self.dot_dia_entry, 9, 1)
-
-        # Dot spacing #
-        self.dotspacing_label = QtWidgets.QLabel('%s:' % _("Spacing"))
-        self.dotspacing_label.setToolTip(
-            _("Distance between each two dots in Dots Grid.")
-        )
-        self.dot_spacing_entry = FCDoubleSpinner()
-        self.dot_spacing_entry.set_range(0.0, 9999.9999)
-        self.dot_spacing_entry.set_precision(self.decimals)
-        self.dot_spacing_entry.setSingleStep(0.1)
-
-        grid_lay.addWidget(self.dotspacing_label, 10, 0)
-        grid_lay.addWidget(self.dot_spacing_entry, 10, 1)
-
-        self.squares_label = QtWidgets.QLabel('<b>%s</b>:' % _("Squares Grid Parameters"))
-        grid_lay.addWidget(self.squares_label, 11, 0, 1, 2)
-
-        # Square Size #
-        self.square_size_label = QtWidgets.QLabel('%s:' % _("Size"))
-        self.square_size_label.setToolTip(
-            _("Square side size in Squares Grid.")
-        )
-        self.square_size_entry = FCDoubleSpinner()
-        self.square_size_entry.set_range(0.0, 9999.9999)
-        self.square_size_entry.set_precision(self.decimals)
-        self.square_size_entry.setSingleStep(0.1)
-
-        grid_lay.addWidget(self.square_size_label, 12, 0)
-        grid_lay.addWidget(self.square_size_entry, 12, 1)
-
-        # Squares spacing #
-        self.squares_spacing_label = QtWidgets.QLabel('%s:' % _("Spacing"))
-        self.squares_spacing_label.setToolTip(
-            _("Distance between each two squares in Squares Grid.")
-        )
-        self.squares_spacing_entry = FCDoubleSpinner()
-        self.squares_spacing_entry.set_range(0.0, 9999.9999)
-        self.squares_spacing_entry.set_precision(self.decimals)
-        self.squares_spacing_entry.setSingleStep(0.1)
-
-        grid_lay.addWidget(self.squares_spacing_label, 13, 0)
-        grid_lay.addWidget(self.squares_spacing_entry, 13, 1)
-
-        self.lines_label = QtWidgets.QLabel('<b>%s</b>:' % _("Lines Grid Parameters"))
-        grid_lay.addWidget(self.lines_label, 14, 0, 1, 2)
-
-        # Square Size #
-        self.line_size_label = QtWidgets.QLabel('%s:' % _("Size"))
-        self.line_size_label.setToolTip(
-            _("Line thickness size in Lines Grid.")
-        )
-        self.line_size_entry = FCDoubleSpinner()
-        self.line_size_entry.set_range(0.0, 9999.9999)
-        self.line_size_entry.set_precision(self.decimals)
-        self.line_size_entry.setSingleStep(0.1)
-
-        grid_lay.addWidget(self.line_size_label, 15, 0)
-        grid_lay.addWidget(self.line_size_entry, 15, 1)
-
-        # Lines spacing #
-        self.lines_spacing_label = QtWidgets.QLabel('%s:' % _("Spacing"))
-        self.lines_spacing_label.setToolTip(
-            _("Distance between each two lines in Lines Grid.")
-        )
-        self.lines_spacing_entry = FCDoubleSpinner()
-        self.lines_spacing_entry.set_range(0.0, 9999.9999)
-        self.lines_spacing_entry.set_precision(self.decimals)
-        self.lines_spacing_entry.setSingleStep(0.1)
-
-        grid_lay.addWidget(self.lines_spacing_label, 16, 0)
-        grid_lay.addWidget(self.lines_spacing_entry, 16, 1)
-
-        self.robber_bar_label = QtWidgets.QLabel('<b>%s</b>' % _('Robber Bar Parameters'))
-        self.robber_bar_label.setToolTip(
-            _("Parameters used for the robber bar.\n"
-              "Robber bar = copper border to help in pattern hole plating.")
-        )
-        grid_lay.addWidget(self.robber_bar_label, 17, 0, 1, 2)
-
-        # ROBBER BAR MARGIN #
-        self.rb_margin_label = QtWidgets.QLabel('%s:' % _("Margin"))
-        self.rb_margin_label.setToolTip(
-            _("Bounding box margin for robber bar.")
-        )
-        self.rb_margin_entry = FCDoubleSpinner()
-        self.rb_margin_entry.set_range(-9999.9999, 9999.9999)
-        self.rb_margin_entry.set_precision(self.decimals)
-        self.rb_margin_entry.setSingleStep(0.1)
-
-        grid_lay.addWidget(self.rb_margin_label, 18, 0)
-        grid_lay.addWidget(self.rb_margin_entry, 18, 1)
-
-        # THICKNESS #
-        self.rb_thickness_label = QtWidgets.QLabel('%s:' % _("Thickness"))
-        self.rb_thickness_label.setToolTip(
-            _("The robber bar thickness.")
-        )
-        self.rb_thickness_entry = FCDoubleSpinner()
-        self.rb_thickness_entry.set_range(0.0000, 9999.9999)
-        self.rb_thickness_entry.set_precision(self.decimals)
-        self.rb_thickness_entry.setSingleStep(0.1)
-
-        grid_lay.addWidget(self.rb_thickness_label, 19, 0)
-        grid_lay.addWidget(self.rb_thickness_entry, 19, 1)
-
-        self.patern_mask_label = QtWidgets.QLabel('<b>%s</b>' % _('Pattern Plating Mask'))
-        self.patern_mask_label.setToolTip(
-            _("Generate a mask for pattern plating.")
-        )
-        grid_lay.addWidget(self.patern_mask_label, 20, 0, 1, 2)
-
-        # Openings CLEARANCE #
-        self.clearance_ppm_label = QtWidgets.QLabel('%s:' % _("Clearance"))
-        self.clearance_ppm_label.setToolTip(
-            _("The distance between the possible copper thieving elements\n"
-              "and/or robber bar and the actual openings in the mask.")
-        )
-        self.clearance_ppm_entry = FCDoubleSpinner()
-        self.clearance_ppm_entry.set_range(-9999.9999, 9999.9999)
-        self.clearance_ppm_entry.set_precision(self.decimals)
-        self.clearance_ppm_entry.setSingleStep(0.1)
-
-        grid_lay.addWidget(self.clearance_ppm_label, 21, 0)
-        grid_lay.addWidget(self.clearance_ppm_entry, 21, 1)
-
-        self.layout.addStretch()
-
-
-class Tools2FiducialsPrefGroupUI(OptionsGroupUI):
-    def __init__(self, decimals=4, parent=None):
-
-        super(Tools2FiducialsPrefGroupUI, self).__init__(self)
-
-        self.setTitle(str(_("Fiducials Tool Options")))
-        self.decimals = decimals
-
-        # ## Grid Layout
-        grid_lay = QtWidgets.QGridLayout()
-        self.layout.addLayout(grid_lay)
-        grid_lay.setColumnStretch(0, 0)
-        grid_lay.setColumnStretch(1, 1)
-
-        self.param_label = QtWidgets.QLabel('<b>%s:</b>' % _('Parameters'))
-        self.param_label.setToolTip(
-            _("Parameters used for this tool.")
-        )
-        grid_lay.addWidget(self.param_label, 0, 0, 1, 2)
-
-        # DIAMETER #
-        self.dia_label = QtWidgets.QLabel('%s:' % _("Size"))
-        self.dia_label.setToolTip(
-            _("This set the fiducial diameter if fiducial type is circular,\n"
-              "otherwise is the size of the fiducial.\n"
-              "The soldermask opening is double than that.")
-        )
-        self.dia_entry = FCDoubleSpinner()
-        self.dia_entry.set_range(1.0000, 3.0000)
-        self.dia_entry.set_precision(self.decimals)
-        self.dia_entry.setWrapping(True)
-        self.dia_entry.setSingleStep(0.1)
-
-        grid_lay.addWidget(self.dia_label, 1, 0)
-        grid_lay.addWidget(self.dia_entry, 1, 1)
-
-        # MARGIN #
-        self.margin_label = QtWidgets.QLabel('%s:' % _("Margin"))
-        self.margin_label.setToolTip(
-            _("Bounding box margin.")
-        )
-        self.margin_entry = FCDoubleSpinner()
-        self.margin_entry.set_range(-9999.9999, 9999.9999)
-        self.margin_entry.set_precision(self.decimals)
-        self.margin_entry.setSingleStep(0.1)
-
-        grid_lay.addWidget(self.margin_label, 2, 0)
-        grid_lay.addWidget(self.margin_entry, 2, 1)
-
-        # Mode #
-        self.mode_radio = RadioSet([
-            {'label': _('Auto'), 'value': 'auto'},
-            {"label": _("Manual"), "value": "manual"}
-        ], stretch=False)
-        self.mode_label = QtWidgets.QLabel(_("Mode:"))
-        self.mode_label.setToolTip(
-            _("- 'Auto' - automatic placement of fiducials in the corners of the bounding box.\n"
-              "- 'Manual' - manual placement of fiducials.")
-        )
-        grid_lay.addWidget(self.mode_label, 3, 0)
-        grid_lay.addWidget(self.mode_radio, 3, 1)
-
-        # Position for second fiducial #
-        self.pos_radio = RadioSet([
-            {'label': _('Up'), 'value': 'up'},
-            {"label": _("Down"), "value": "down"},
-            {"label": _("None"), "value": "no"}
-        ], stretch=False)
-        self.pos_label = QtWidgets.QLabel('%s:' % _("Second fiducial"))
-        self.pos_label.setToolTip(
-            _("The position for the second fiducial.\n"
-              "- 'Up' - the order is: bottom-left, top-left, top-right.\n"
-              "- 'Down' - the order is: bottom-left, bottom-right, top-right.\n"
-              "- 'None' - there is no second fiducial. The order is: bottom-left, top-right.")
-        )
-        grid_lay.addWidget(self.pos_label, 4, 0)
-        grid_lay.addWidget(self.pos_radio, 4, 1)
-
-        separator_line = QtWidgets.QFrame()
-        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
-        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
-        grid_lay.addWidget(separator_line, 5, 0, 1, 2)
-
-        # Fiducial type #
-        self.fid_type_radio = RadioSet([
-            {'label': _('Circular'), 'value': 'circular'},
-            {"label": _("Cross"), "value": "cross"},
-            {"label": _("Chess"), "value": "chess"}
-        ], stretch=False)
-
-        self.fid_type_label = QtWidgets.QLabel('%s:' % _("Fiducial Type"))
-        self.fid_type_label.setToolTip(
-            _("The type of fiducial.\n"
-              "- 'Circular' - this is the regular fiducial.\n"
-              "- 'Cross' - cross lines fiducial.\n"
-              "- 'Chess' - chess pattern fiducial.")
-        )
-        grid_lay.addWidget(self.fid_type_label, 6, 0)
-        grid_lay.addWidget(self.fid_type_radio, 6, 1)
-
-        # Line Thickness #
-        self.line_thickness_label = QtWidgets.QLabel('%s:' % _("Line thickness"))
-        self.line_thickness_label.setToolTip(
-            _("Bounding box margin.")
-        )
-        self.line_thickness_entry = FCDoubleSpinner()
-        self.line_thickness_entry.set_range(0.00001, 9999.9999)
-        self.line_thickness_entry.set_precision(self.decimals)
-        self.line_thickness_entry.setSingleStep(0.1)
-
-        grid_lay.addWidget(self.line_thickness_label, 7, 0)
-        grid_lay.addWidget(self.line_thickness_entry, 7, 1)
-
-        self.layout.addStretch()
-
-
-class Tools2CalPrefGroupUI(OptionsGroupUI):
-    def __init__(self, decimals=4, parent=None):
-
-        super(Tools2CalPrefGroupUI, self).__init__(self)
-
-        self.setTitle(str(_("Calibration Tool Options")))
-        self.decimals = decimals
-
-        # ## Grid Layout
-        grid_lay = QtWidgets.QGridLayout()
-        self.layout.addLayout(grid_lay)
-        grid_lay.setColumnStretch(0, 0)
-        grid_lay.setColumnStretch(1, 1)
-
-        self.param_label = QtWidgets.QLabel('<b>%s:</b>' % _('Parameters'))
-        self.param_label.setToolTip(
-            _("Parameters used for this tool.")
-        )
-        grid_lay.addWidget(self.param_label, 0, 0, 1, 2)
-
-        # Calibration source
-        self.cal_source_lbl = QtWidgets.QLabel("<b>%s:</b>" % _("Source Type"))
-        self.cal_source_lbl.setToolTip(_("The source of calibration points.\n"
-                                         "It can be:\n"
-                                         "- Object -> click a hole geo for Excellon or a pad for Gerber\n"
-                                         "- Free -> click freely on canvas to acquire the calibration points"))
-        self.cal_source_radio = RadioSet([{'label': _('Object'), 'value': 'object'},
-                                          {'label': _('Free'), 'value': 'free'}],
-                                         stretch=False)
-
-        grid_lay.addWidget(self.cal_source_lbl, 1, 0)
-        grid_lay.addWidget(self.cal_source_radio, 1, 1, 1, 2)
-
-        separator_line = QtWidgets.QFrame()
-        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
-        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
-        grid_lay.addWidget(separator_line, 2, 0, 1, 2)
-
-        # Travel Z entry
-        travelz_lbl = QtWidgets.QLabel('%s:' % _("Travel Z"))
-        travelz_lbl.setToolTip(
-            _("Height (Z) for travelling between the points.")
-        )
-
-        self.travelz_entry = FCDoubleSpinner()
-        self.travelz_entry.set_range(-9999.9999, 9999.9999)
-        self.travelz_entry.set_precision(self.decimals)
-        self.travelz_entry.setSingleStep(0.1)
-
-        grid_lay.addWidget(travelz_lbl, 3, 0)
-        grid_lay.addWidget(self.travelz_entry, 3, 1, 1, 2)
-
-        # Verification Z entry
-        verz_lbl = QtWidgets.QLabel('%s:' % _("Verification Z"))
-        verz_lbl.setToolTip(
-            _("Height (Z) for checking the point.")
-        )
-
-        self.verz_entry = FCDoubleSpinner()
-        self.verz_entry.set_range(-9999.9999, 9999.9999)
-        self.verz_entry.set_precision(self.decimals)
-        self.verz_entry.setSingleStep(0.1)
-
-        grid_lay.addWidget(verz_lbl, 4, 0)
-        grid_lay.addWidget(self.verz_entry, 4, 1, 1, 2)
-
-        # Zero the Z of the verification tool
-        self.zeroz_cb = FCCheckBox('%s' % _("Zero Z tool"))
-        self.zeroz_cb.setToolTip(
-            _("Include a sequence to zero the height (Z)\n"
-              "of the verification tool.")
-        )
-
-        grid_lay.addWidget(self.zeroz_cb, 5, 0, 1, 3)
-
-        # Toochange Z entry
-        toolchangez_lbl = QtWidgets.QLabel('%s:' % _("Toolchange Z"))
-        toolchangez_lbl.setToolTip(
-            _("Height (Z) for mounting the verification probe.")
-        )
-
-        self.toolchangez_entry = FCDoubleSpinner()
-        self.toolchangez_entry.set_range(0.0000, 9999.9999)
-        self.toolchangez_entry.set_precision(self.decimals)
-        self.toolchangez_entry.setSingleStep(0.1)
-
-        grid_lay.addWidget(toolchangez_lbl, 6, 0)
-        grid_lay.addWidget(self.toolchangez_entry, 6, 1, 1, 2)
-
-        # Toolchange X-Y entry
-        toolchangexy_lbl = QtWidgets.QLabel('%s:' % _('Toolchange X-Y'))
-        toolchangexy_lbl.setToolTip(
-            _("Toolchange X,Y position.\n"
-              "If no value is entered then the current\n"
-              "(x, y) point will be used,")
-        )
-
-        self.toolchange_xy_entry = FCEntry()
-
-        grid_lay.addWidget(toolchangexy_lbl, 7, 0)
-        grid_lay.addWidget(self.toolchange_xy_entry, 7, 1, 1, 2)
-
-        # Second point choice
-        second_point_lbl = QtWidgets.QLabel('%s:' % _("Second point"))
-        second_point_lbl.setToolTip(
-            _("Second point in the Gcode verification can be:\n"
-              "- top-left -> the user will align the PCB vertically\n"
-              "- bottom-right -> the user will align the PCB horizontally")
-        )
-        self.second_point_radio = RadioSet([{'label': _('Top-Left'), 'value': 'tl'},
-                                            {'label': _('Bottom-Right'), 'value': 'br'}],
-                                           orientation='vertical')
-
-        grid_lay.addWidget(second_point_lbl, 8, 0)
-        grid_lay.addWidget(self.second_point_radio, 8, 1, 1, 2)
-
-        self.layout.addStretch()
-
-
-class Tools2EDrillsPrefGroupUI(OptionsGroupUI):
-    def __init__(self, decimals=4, parent=None):
-
-        super(Tools2EDrillsPrefGroupUI, self).__init__(self)
-
-        self.setTitle(str(_("Extract Drills Options")))
-        self.decimals = decimals
-
-        # ## Grid Layout
-        grid_lay = QtWidgets.QGridLayout()
-        self.layout.addLayout(grid_lay)
-        grid_lay.setColumnStretch(0, 0)
-        grid_lay.setColumnStretch(1, 1)
-
-        self.param_label = QtWidgets.QLabel('<b>%s:</b>' % _('Parameters'))
-        self.param_label.setToolTip(
-            _("Parameters used for this tool.")
-        )
-        grid_lay.addWidget(self.param_label, 0, 0, 1, 2)
-
-        self.padt_label = QtWidgets.QLabel("<b>%s:</b>" % _("Processed Pads Type"))
-        self.padt_label.setToolTip(
-            _("The type of pads shape to be processed.\n"
-              "If the PCB has many SMD pads with rectangular pads,\n"
-              "disable the Rectangular aperture.")
-        )
-
-        grid_lay.addWidget(self.padt_label, 2, 0, 1, 2)
-
-        # Circular Aperture Selection
-        self.circular_cb = FCCheckBox('%s' % _("Circular"))
-        self.circular_cb.setToolTip(
-            _("Process Circular Pads.")
-        )
-
-        grid_lay.addWidget(self.circular_cb, 3, 0, 1, 2)
-
-        # Oblong Aperture Selection
-        self.oblong_cb = FCCheckBox('%s' % _("Oblong"))
-        self.oblong_cb.setToolTip(
-            _("Process Oblong Pads.")
-        )
-
-        grid_lay.addWidget(self.oblong_cb, 4, 0, 1, 2)
-
-        # Square Aperture Selection
-        self.square_cb = FCCheckBox('%s' % _("Square"))
-        self.square_cb.setToolTip(
-            _("Process Square Pads.")
-        )
-
-        grid_lay.addWidget(self.square_cb, 5, 0, 1, 2)
-
-        # Rectangular Aperture Selection
-        self.rectangular_cb = FCCheckBox('%s' % _("Rectangular"))
-        self.rectangular_cb.setToolTip(
-            _("Process Rectangular Pads.")
-        )
-
-        grid_lay.addWidget(self.rectangular_cb, 6, 0, 1, 2)
-
-        # Others type of Apertures Selection
-        self.other_cb = FCCheckBox('%s' % _("Others"))
-        self.other_cb.setToolTip(
-            _("Process pads not in the categories above.")
-        )
-
-        grid_lay.addWidget(self.other_cb, 7, 0, 1, 2)
-
-        separator_line = QtWidgets.QFrame()
-        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
-        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
-        grid_lay.addWidget(separator_line, 8, 0, 1, 2)
-
-        # ## Axis
-        self.hole_size_radio = RadioSet(
-            [
-                {'label': _("Fixed Diameter"), 'value': 'fixed'},
-                {'label': _("Fixed Annular Ring"), 'value': 'ring'},
-                {'label': _("Proportional"), 'value': 'prop'}
-            ],
-            orientation='vertical',
-            stretch=False)
-        self.hole_size_label = QtWidgets.QLabel('<b>%s:</b>' % _("Method"))
-        self.hole_size_label.setToolTip(
-            _("The method for processing pads. Can be:\n"
-              "- Fixed Diameter -> all holes will have a set size\n"
-              "- Fixed Annular Ring -> all holes will have a set annular ring\n"
-              "- Proportional -> each hole size will be a fraction of the pad size"))
-
-        grid_lay.addWidget(self.hole_size_label, 9, 0)
-        grid_lay.addWidget(self.hole_size_radio, 9, 1)
-
-        # grid_lay1.addWidget(QtWidgets.QLabel(''))
-
-        separator_line = QtWidgets.QFrame()
-        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
-        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
-        grid_lay.addWidget(separator_line, 10, 0, 1, 2)
-
-        # Annular Ring
-        self.fixed_label = QtWidgets.QLabel('<b>%s</b>' % _("Fixed Diameter"))
-        grid_lay.addWidget(self.fixed_label, 11, 0, 1, 2)
-
-        # Diameter value
-        self.dia_entry = FCDoubleSpinner()
-        self.dia_entry.set_precision(self.decimals)
-        self.dia_entry.set_range(0.0000, 9999.9999)
-
-        self.dia_label = QtWidgets.QLabel('%s:' % _("Value"))
-        self.dia_label.setToolTip(
-            _("Fixed hole diameter.")
-        )
-
-        grid_lay.addWidget(self.dia_label, 12, 0)
-        grid_lay.addWidget(self.dia_entry, 12, 1)
-
-        # Annular Ring value
-        self.ring_label = QtWidgets.QLabel('<b>%s</b>' % _("Fixed Annular Ring"))
-        self.ring_label.setToolTip(
-            _("The size of annular ring.\n"
-              "The copper sliver between the hole exterior\n"
-              "and the margin of the copper pad.")
-        )
-        grid_lay.addWidget(self.ring_label, 13, 0, 1, 2)
-
-        # Circular Annular Ring Value
-        self.circular_ring_label = QtWidgets.QLabel('%s:' % _("Circular"))
-        self.circular_ring_label.setToolTip(
-            _("The size of annular ring for circular pads.")
-        )
-
-        self.circular_ring_entry = FCDoubleSpinner()
-        self.circular_ring_entry.set_precision(self.decimals)
-        self.circular_ring_entry.set_range(0.0000, 9999.9999)
-
-        grid_lay.addWidget(self.circular_ring_label, 14, 0)
-        grid_lay.addWidget(self.circular_ring_entry, 14, 1)
-
-        # Oblong Annular Ring Value
-        self.oblong_ring_label = QtWidgets.QLabel('%s:' % _("Oblong"))
-        self.oblong_ring_label.setToolTip(
-            _("The size of annular ring for oblong pads.")
-        )
-
-        self.oblong_ring_entry = FCDoubleSpinner()
-        self.oblong_ring_entry.set_precision(self.decimals)
-        self.oblong_ring_entry.set_range(0.0000, 9999.9999)
-
-        grid_lay.addWidget(self.oblong_ring_label, 15, 0)
-        grid_lay.addWidget(self.oblong_ring_entry, 15, 1)
-
-        # Square Annular Ring Value
-        self.square_ring_label = QtWidgets.QLabel('%s:' % _("Square"))
-        self.square_ring_label.setToolTip(
-            _("The size of annular ring for square pads.")
-        )
-
-        self.square_ring_entry = FCDoubleSpinner()
-        self.square_ring_entry.set_precision(self.decimals)
-        self.square_ring_entry.set_range(0.0000, 9999.9999)
-
-        grid_lay.addWidget(self.square_ring_label, 16, 0)
-        grid_lay.addWidget(self.square_ring_entry, 16, 1)
-
-        # Rectangular Annular Ring Value
-        self.rectangular_ring_label = QtWidgets.QLabel('%s:' % _("Rectangular"))
-        self.rectangular_ring_label.setToolTip(
-            _("The size of annular ring for rectangular pads.")
-        )
-
-        self.rectangular_ring_entry = FCDoubleSpinner()
-        self.rectangular_ring_entry.set_precision(self.decimals)
-        self.rectangular_ring_entry.set_range(0.0000, 9999.9999)
-
-        grid_lay.addWidget(self.rectangular_ring_label, 17, 0)
-        grid_lay.addWidget(self.rectangular_ring_entry, 17, 1)
-
-        # Others Annular Ring Value
-        self.other_ring_label = QtWidgets.QLabel('%s:' % _("Others"))
-        self.other_ring_label.setToolTip(
-            _("The size of annular ring for other pads.")
-        )
-
-        self.other_ring_entry = FCDoubleSpinner()
-        self.other_ring_entry.set_precision(self.decimals)
-        self.other_ring_entry.set_range(0.0000, 9999.9999)
-
-        grid_lay.addWidget(self.other_ring_label, 18, 0)
-        grid_lay.addWidget(self.other_ring_entry, 18, 1)
-
-        self.prop_label = QtWidgets.QLabel('<b>%s</b>' % _("Proportional Diameter"))
-        grid_lay.addWidget(self.prop_label, 19, 0, 1, 2)
-
-        # Factor value
-        self.factor_entry = FCDoubleSpinner(suffix='%')
-        self.factor_entry.set_precision(self.decimals)
-        self.factor_entry.set_range(0.0000, 100.0000)
-        self.factor_entry.setSingleStep(0.1)
-
-        self.factor_label = QtWidgets.QLabel('%s:' % _("Factor"))
-        self.factor_label.setToolTip(
-            _("Proportional Diameter.\n"
-              "The hole diameter will be a fraction of the pad size.")
-        )
-
-        grid_lay.addWidget(self.factor_label, 20, 0)
-        grid_lay.addWidget(self.factor_entry, 20, 1)
-
-        self.layout.addStretch()
-
-
-class Tools2PunchGerberPrefGroupUI(OptionsGroupUI):
-    def __init__(self, decimals=4, parent=None):
-
-        super(Tools2PunchGerberPrefGroupUI, self).__init__(self)
-
-        self.setTitle(str(_("Punch Gerber Options")))
-        self.decimals = decimals
-
-        # ## Grid Layout
-        grid_lay = QtWidgets.QGridLayout()
-        self.layout.addLayout(grid_lay)
-        grid_lay.setColumnStretch(0, 0)
-        grid_lay.setColumnStretch(1, 1)
-
-        self.param_label = QtWidgets.QLabel('<b>%s:</b>' % _('Parameters'))
-        self.param_label.setToolTip(
-            _("Parameters used for this tool.")
-        )
-        grid_lay.addWidget(self.param_label, 0, 0, 1, 2)
-
-        self.padt_label = QtWidgets.QLabel("<b>%s:</b>" % _("Processed Pads Type"))
-        self.padt_label.setToolTip(
-            _("The type of pads shape to be processed.\n"
-              "If the PCB has many SMD pads with rectangular pads,\n"
-              "disable the Rectangular aperture.")
-        )
-
-        grid_lay.addWidget(self.padt_label, 2, 0, 1, 2)
-
-        # Circular Aperture Selection
-        self.circular_cb = FCCheckBox('%s' % _("Circular"))
-        self.circular_cb.setToolTip(
-            _("Process Circular Pads.")
-        )
-
-        grid_lay.addWidget(self.circular_cb, 3, 0, 1, 2)
-
-        # Oblong Aperture Selection
-        self.oblong_cb = FCCheckBox('%s' % _("Oblong"))
-        self.oblong_cb.setToolTip(
-            _("Process Oblong Pads.")
-        )
-
-        grid_lay.addWidget(self.oblong_cb, 4, 0, 1, 2)
-
-        # Square Aperture Selection
-        self.square_cb = FCCheckBox('%s' % _("Square"))
-        self.square_cb.setToolTip(
-            _("Process Square Pads.")
-        )
-
-        grid_lay.addWidget(self.square_cb, 5, 0, 1, 2)
-
-        # Rectangular Aperture Selection
-        self.rectangular_cb = FCCheckBox('%s' % _("Rectangular"))
-        self.rectangular_cb.setToolTip(
-            _("Process Rectangular Pads.")
-        )
-
-        grid_lay.addWidget(self.rectangular_cb, 6, 0, 1, 2)
-
-        # Others type of Apertures Selection
-        self.other_cb = FCCheckBox('%s' % _("Others"))
-        self.other_cb.setToolTip(
-            _("Process pads not in the categories above.")
-        )
-
-        grid_lay.addWidget(self.other_cb, 7, 0, 1, 2)
-
-        separator_line = QtWidgets.QFrame()
-        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
-        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
-        grid_lay.addWidget(separator_line, 8, 0, 1, 2)
-
-        # ## Axis
-        self.hole_size_radio = RadioSet(
-            [
-                {'label': _("Excellon"), 'value': 'exc'},
-                {'label': _("Fixed Diameter"), 'value': 'fixed'},
-                {'label': _("Fixed Annular Ring"), 'value': 'ring'},
-                {'label': _("Proportional"), 'value': 'prop'}
-            ],
-            orientation='vertical',
-            stretch=False)
-        self.hole_size_label = QtWidgets.QLabel('<b>%s:</b>' % _("Method"))
-        self.hole_size_label.setToolTip(
-            _("The punch hole source can be:\n"
-              "- Excellon Object-> the Excellon object drills center will serve as reference.\n"
-              "- Fixed Diameter -> will try to use the pads center as reference adding fixed diameter holes.\n"
-              "- Fixed Annular Ring -> will try to keep a set annular ring.\n"
-              "- Proportional -> will make a Gerber punch hole having the diameter a percentage of the pad diameter.")
-        )
-        grid_lay.addWidget(self.hole_size_label, 9, 0)
-        grid_lay.addWidget(self.hole_size_radio, 9, 1)
-
-        # grid_lay1.addWidget(QtWidgets.QLabel(''))
-
-        separator_line = QtWidgets.QFrame()
-        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
-        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
-        grid_lay.addWidget(separator_line, 10, 0, 1, 2)
-
-        # Annular Ring
-        self.fixed_label = QtWidgets.QLabel('<b>%s</b>' % _("Fixed Diameter"))
-        grid_lay.addWidget(self.fixed_label, 11, 0, 1, 2)
-
-        # Diameter value
-        self.dia_entry = FCDoubleSpinner()
-        self.dia_entry.set_precision(self.decimals)
-        self.dia_entry.set_range(0.0000, 9999.9999)
-
-        self.dia_label = QtWidgets.QLabel('%s:' % _("Value"))
-        self.dia_label.setToolTip(
-            _("Fixed hole diameter.")
-        )
-
-        grid_lay.addWidget(self.dia_label, 12, 0)
-        grid_lay.addWidget(self.dia_entry, 12, 1)
-
-        # Annular Ring value
-        self.ring_label = QtWidgets.QLabel('<b>%s</b>' % _("Fixed Annular Ring"))
-        self.ring_label.setToolTip(
-            _("The size of annular ring.\n"
-              "The copper sliver between the hole exterior\n"
-              "and the margin of the copper pad.")
-        )
-        grid_lay.addWidget(self.ring_label, 13, 0, 1, 2)
-
-        # Circular Annular Ring Value
-        self.circular_ring_label = QtWidgets.QLabel('%s:' % _("Circular"))
-        self.circular_ring_label.setToolTip(
-            _("The size of annular ring for circular pads.")
-        )
-
-        self.circular_ring_entry = FCDoubleSpinner()
-        self.circular_ring_entry.set_precision(self.decimals)
-        self.circular_ring_entry.set_range(0.0000, 9999.9999)
-
-        grid_lay.addWidget(self.circular_ring_label, 14, 0)
-        grid_lay.addWidget(self.circular_ring_entry, 14, 1)
-
-        # Oblong Annular Ring Value
-        self.oblong_ring_label = QtWidgets.QLabel('%s:' % _("Oblong"))
-        self.oblong_ring_label.setToolTip(
-            _("The size of annular ring for oblong pads.")
-        )
-
-        self.oblong_ring_entry = FCDoubleSpinner()
-        self.oblong_ring_entry.set_precision(self.decimals)
-        self.oblong_ring_entry.set_range(0.0000, 9999.9999)
-
-        grid_lay.addWidget(self.oblong_ring_label, 15, 0)
-        grid_lay.addWidget(self.oblong_ring_entry, 15, 1)
-
-        # Square Annular Ring Value
-        self.square_ring_label = QtWidgets.QLabel('%s:' % _("Square"))
-        self.square_ring_label.setToolTip(
-            _("The size of annular ring for square pads.")
-        )
-
-        self.square_ring_entry = FCDoubleSpinner()
-        self.square_ring_entry.set_precision(self.decimals)
-        self.square_ring_entry.set_range(0.0000, 9999.9999)
-
-        grid_lay.addWidget(self.square_ring_label, 16, 0)
-        grid_lay.addWidget(self.square_ring_entry, 16, 1)
-
-        # Rectangular Annular Ring Value
-        self.rectangular_ring_label = QtWidgets.QLabel('%s:' % _("Rectangular"))
-        self.rectangular_ring_label.setToolTip(
-            _("The size of annular ring for rectangular pads.")
-        )
-
-        self.rectangular_ring_entry = FCDoubleSpinner()
-        self.rectangular_ring_entry.set_precision(self.decimals)
-        self.rectangular_ring_entry.set_range(0.0000, 9999.9999)
-
-        grid_lay.addWidget(self.rectangular_ring_label, 17, 0)
-        grid_lay.addWidget(self.rectangular_ring_entry, 17, 1)
-
-        # Others Annular Ring Value
-        self.other_ring_label = QtWidgets.QLabel('%s:' % _("Others"))
-        self.other_ring_label.setToolTip(
-            _("The size of annular ring for other pads.")
-        )
-
-        self.other_ring_entry = FCDoubleSpinner()
-        self.other_ring_entry.set_precision(self.decimals)
-        self.other_ring_entry.set_range(0.0000, 9999.9999)
-
-        grid_lay.addWidget(self.other_ring_label, 18, 0)
-        grid_lay.addWidget(self.other_ring_entry, 18, 1)
-
-        self.prop_label = QtWidgets.QLabel('<b>%s</b>' % _("Proportional Diameter"))
-        grid_lay.addWidget(self.prop_label, 19, 0, 1, 2)
-
-        # Factor value
-        self.factor_entry = FCDoubleSpinner(suffix='%')
-        self.factor_entry.set_precision(self.decimals)
-        self.factor_entry.set_range(0.0000, 100.0000)
-        self.factor_entry.setSingleStep(0.1)
-
-        self.factor_label = QtWidgets.QLabel('%s:' % _("Factor"))
-        self.factor_label.setToolTip(
-            _("Proportional Diameter.\n"
-              "The hole diameter will be a fraction of the pad size.")
-        )
-
-        grid_lay.addWidget(self.factor_label, 20, 0)
-        grid_lay.addWidget(self.factor_entry, 20, 1)
-
-        self.layout.addStretch()
-
-
-class Tools2InvertPrefGroupUI(OptionsGroupUI):
-    def __init__(self, decimals=4, parent=None):
-
-        super(Tools2InvertPrefGroupUI, self).__init__(self)
-
-        self.setTitle(str(_("Invert Gerber Tool Options")))
-        self.decimals = decimals
-
-        # ## Subtractor Tool Parameters
-        self.sublabel = QtWidgets.QLabel("<b>%s:</b>" % _("Parameters"))
-        self.sublabel.setToolTip(
-            _("A tool to invert Gerber geometry from positive to negative\n"
-              "and in revers.")
-        )
-        self.layout.addWidget(self.sublabel)
-
-        # Grid Layout
-        grid0 = QtWidgets.QGridLayout()
-        grid0.setColumnStretch(0, 0)
-        grid0.setColumnStretch(1, 1)
-        self.layout.addLayout(grid0)
-
-        # Margin
-        self.margin_label = QtWidgets.QLabel('%s:' % _('Margin'))
-        self.margin_label.setToolTip(
-            _("Distance by which to avoid\n"
-              "the edges of the Gerber object.")
-        )
-        self.margin_entry = FCDoubleSpinner()
-        self.margin_entry.set_precision(self.decimals)
-        self.margin_entry.set_range(0.0000, 9999.9999)
-        self.margin_entry.setObjectName(_("Margin"))
-
-        grid0.addWidget(self.margin_label, 2, 0, 1, 2)
-        grid0.addWidget(self.margin_entry, 3, 0, 1, 2)
-
-        self.join_label = QtWidgets.QLabel('%s:' % _("Lines Join Style"))
-        self.join_label.setToolTip(
-            _("The way that the lines in the object outline will be joined.\n"
-              "Can be:\n"
-              "- rounded -> an arc is added between two joining lines\n"
-              "- square -> the lines meet in 90 degrees angle\n"
-              "- bevel -> the lines are joined by a third line")
-        )
-        self.join_radio = RadioSet([
-            {'label': 'Rounded', 'value': 'r'},
-            {'label': 'Square', 'value': 's'},
-            {'label': 'Bevel', 'value': 'b'}
-        ], orientation='vertical', stretch=False)
-
-        grid0.addWidget(self.join_label, 5, 0, 1, 2)
-        grid0.addWidget(self.join_radio, 7, 0, 1, 2)
-
-        self.layout.addStretch()
-
-
-class FAExcPrefGroupUI(OptionsGroupUI):
-    def __init__(self, decimals=4, parent=None):
-        # OptionsGroupUI.__init__(self, "Excellon File associations Preferences", parent=None)
-        super().__init__(self)
-
-        self.setTitle(str(_("Excellon File associations")))
-        self.decimals = decimals
-
-        self.layout.setContentsMargins(2, 2, 2, 2)
-
-        self.vertical_lay = QtWidgets.QVBoxLayout()
-        scroll_widget = QtWidgets.QWidget()
-
-        scroll = VerticalScrollArea()
-        scroll.setWidget(scroll_widget)
-        scroll.setWidgetResizable(True)
-        scroll.setFrameShape(QtWidgets.QFrame.NoFrame)
-
-        self.restore_btn = FCButton(_("Restore"))
-        self.restore_btn.setToolTip(_("Restore the extension list to the default state."))
-        self.del_all_btn = FCButton(_("Delete All"))
-        self.del_all_btn.setToolTip(_("Delete all extensions from the list."))
-
-        hlay0 = QtWidgets.QHBoxLayout()
-        hlay0.addWidget(self.restore_btn)
-        hlay0.addWidget(self.del_all_btn)
-        self.vertical_lay.addLayout(hlay0)
-
-        # # ## Excellon associations
-        list_label = QtWidgets.QLabel("<b>%s:</b>" % _("Extensions list"))
-        list_label.setToolTip(
-            _("List of file extensions to be\n"
-              "associated with FlatCAM.")
-        )
-        self.vertical_lay.addWidget(list_label)
-
-        qsettings = QSettings("Open Source", "FlatCAM")
-        if qsettings.contains("textbox_font_size"):
-            tb_fsize = qsettings.value('textbox_font_size', type=int)
-        else:
-            tb_fsize = 10
-
-        self.exc_list_text = FCTextArea()
-        self.exc_list_text.setReadOnly(True)
-        # self.exc_list_text.sizeHint(custom_sizehint=150)
-        font = QtGui.QFont()
-        font.setPointSize(tb_fsize)
-        self.exc_list_text.setFont(font)
-
-        self.vertical_lay.addWidget(self.exc_list_text)
-
-        self.ext_label = QtWidgets.QLabel('%s:' % _("Extension"))
-        self.ext_label.setToolTip(_("A file extension to be added or deleted to the list."))
-        self.ext_entry = FCEntry()
-
-        hlay1 = QtWidgets.QHBoxLayout()
-        self.vertical_lay.addLayout(hlay1)
-        hlay1.addWidget(self.ext_label)
-        hlay1.addWidget(self.ext_entry)
-
-        self.add_btn = FCButton(_("Add Extension"))
-        self.add_btn.setToolTip(_("Add a file extension to the list"))
-        self.del_btn = FCButton(_("Delete Extension"))
-        self.del_btn.setToolTip(_("Delete a file extension from the list"))
-
-        hlay2 = QtWidgets.QHBoxLayout()
-        self.vertical_lay.addLayout(hlay2)
-        hlay2.addWidget(self.add_btn)
-        hlay2.addWidget(self.del_btn)
-
-        self.exc_list_btn = FCButton(_("Apply Association"))
-        self.exc_list_btn.setToolTip(_("Apply the file associations between\n"
-                                       "FlatCAM and the files with above extensions.\n"
-                                       "They will be active after next logon.\n"
-                                       "This work only in Windows."))
-        self.vertical_lay.addWidget(self.exc_list_btn)
-
-        scroll_widget.setLayout(self.vertical_lay)
-        self.layout.addWidget(scroll)
-
-        # self.vertical_lay.addStretch()
-
-
-class FAGcoPrefGroupUI(OptionsGroupUI):
-    def __init__(self, decimals=4, parent=None):
-        # OptionsGroupUI.__init__(self, "Gcode File associations Preferences", parent=None)
-        super(FAGcoPrefGroupUI, self).__init__(self)
-
-        self.setTitle(str(_("GCode File associations")))
-        self.decimals = decimals
-
-        self.restore_btn = FCButton(_("Restore"))
-        self.restore_btn.setToolTip(_("Restore the extension list to the default state."))
-        self.del_all_btn = FCButton(_("Delete All"))
-        self.del_all_btn.setToolTip(_("Delete all extensions from the list."))
-
-        hlay0 = QtWidgets.QHBoxLayout()
-        self.layout.addLayout(hlay0)
-        hlay0.addWidget(self.restore_btn)
-        hlay0.addWidget(self.del_all_btn)
-
-        # ## G-Code associations
-        self.gco_list_label = QtWidgets.QLabel("<b>%s:</b>" % _("Extensions list"))
-        self.gco_list_label.setToolTip(
-            _("List of file extensions to be\n"
-              "associated with FlatCAM.")
-        )
-        self.layout.addWidget(self.gco_list_label)
-
-        qsettings = QSettings("Open Source", "FlatCAM")
-        if qsettings.contains("textbox_font_size"):
-            tb_fsize = qsettings.value('textbox_font_size', type=int)
-        else:
-            tb_fsize = 10
-
-        self.gco_list_text = FCTextArea()
-        self.gco_list_text.setReadOnly(True)
-        # self.gco_list_text.sizeHint(custom_sizehint=150)
-        font = QtGui.QFont()
-        font.setPointSize(tb_fsize)
-        self.gco_list_text.setFont(font)
-
-        self.layout.addWidget(self.gco_list_text)
-
-        self.ext_label = QtWidgets.QLabel('%s:' % _("Extension"))
-        self.ext_label.setToolTip(_("A file extension to be added or deleted to the list."))
-        self.ext_entry = FCEntry()
-
-        hlay1 = QtWidgets.QHBoxLayout()
-        self.layout.addLayout(hlay1)
-        hlay1.addWidget(self.ext_label)
-        hlay1.addWidget(self.ext_entry)
-
-        self.add_btn = FCButton(_("Add Extension"))
-        self.add_btn.setToolTip(_("Add a file extension to the list"))
-        self.del_btn = FCButton(_("Delete Extension"))
-        self.del_btn.setToolTip(_("Delete a file extension from the list"))
-
-        hlay2 = QtWidgets.QHBoxLayout()
-        self.layout.addLayout(hlay2)
-        hlay2.addWidget(self.add_btn)
-        hlay2.addWidget(self.del_btn)
-
-        self.gco_list_btn = FCButton(_("Apply Association"))
-        self.gco_list_btn.setToolTip(_("Apply the file associations between\n"
-                                       "FlatCAM and the files with above extensions.\n"
-                                       "They will be active after next logon.\n"
-                                       "This work only in Windows."))
-        self.layout.addWidget(self.gco_list_btn)
-
-        # self.layout.addStretch()
-
-
-class FAGrbPrefGroupUI(OptionsGroupUI):
-    def __init__(self, decimals=4, parent=None):
-        # OptionsGroupUI.__init__(self, "Gerber File associations Preferences", parent=None)
-        super(FAGrbPrefGroupUI, self).__init__(self)
-
-        self.setTitle(str(_("Gerber File associations")))
-        self.decimals = decimals
-
-        self.restore_btn = FCButton(_("Restore"))
-        self.restore_btn.setToolTip(_("Restore the extension list to the default state."))
-        self.del_all_btn = FCButton(_("Delete All"))
-        self.del_all_btn.setToolTip(_("Delete all extensions from the list."))
-
-        hlay0 = QtWidgets.QHBoxLayout()
-        self.layout.addLayout(hlay0)
-        hlay0.addWidget(self.restore_btn)
-        hlay0.addWidget(self.del_all_btn)
-
-        # ## Gerber associations
-        self.grb_list_label = QtWidgets.QLabel("<b>%s:</b>" % _("Extensions list"))
-        self.grb_list_label.setToolTip(
-            _("List of file extensions to be\n"
-              "associated with FlatCAM.")
-        )
-        self.layout.addWidget(self.grb_list_label)
-
-        qsettings = QSettings("Open Source", "FlatCAM")
-        if qsettings.contains("textbox_font_size"):
-            tb_fsize = qsettings.value('textbox_font_size', type=int)
-        else:
-            tb_fsize = 10
-
-        self.grb_list_text = FCTextArea()
-        self.grb_list_text.setReadOnly(True)
-        # self.grb_list_text.sizeHint(custom_sizehint=150)
-        self.layout.addWidget(self.grb_list_text)
-        font = QtGui.QFont()
-        font.setPointSize(tb_fsize)
-        self.grb_list_text.setFont(font)
-
-        self.ext_label = QtWidgets.QLabel('%s:' % _("Extension"))
-        self.ext_label.setToolTip(_("A file extension to be added or deleted to the list."))
-        self.ext_entry = FCEntry()
-
-        hlay1 = QtWidgets.QHBoxLayout()
-        self.layout.addLayout(hlay1)
-        hlay1.addWidget(self.ext_label)
-        hlay1.addWidget(self.ext_entry)
-
-        self.add_btn = FCButton(_("Add Extension"))
-        self.add_btn.setToolTip(_("Add a file extension to the list"))
-        self.del_btn = FCButton(_("Delete Extension"))
-        self.del_btn.setToolTip(_("Delete a file extension from the list"))
-
-        hlay2 = QtWidgets.QHBoxLayout()
-        self.layout.addLayout(hlay2)
-        hlay2.addWidget(self.add_btn)
-        hlay2.addWidget(self.del_btn)
-
-        self.grb_list_btn = FCButton(_("Apply Association"))
-        self.grb_list_btn.setToolTip(_("Apply the file associations between\n"
-                                       "FlatCAM and the files with above extensions.\n"
-                                       "They will be active after next logon.\n"
-                                       "This work only in Windows."))
-
-        self.layout.addWidget(self.grb_list_btn)
-
-        # self.layout.addStretch()
-
-
-class AutoCompletePrefGroupUI(OptionsGroupUI):
-    def __init__(self, decimals=4, parent=None):
-        # OptionsGroupUI.__init__(self, "Gerber File associations Preferences", parent=None)
-        super().__init__(self, parent=parent)
-
-        self.setTitle(str(_("Autocompleter Keywords")))
-        self.decimals = decimals
-
-        self.restore_btn = FCButton(_("Restore"))
-        self.restore_btn.setToolTip(_("Restore the autocompleter keywords list to the default state."))
-        self.del_all_btn = FCButton(_("Delete All"))
-        self.del_all_btn.setToolTip(_("Delete all autocompleter keywords from the list."))
-
-        hlay0 = QtWidgets.QHBoxLayout()
-        self.layout.addLayout(hlay0)
-        hlay0.addWidget(self.restore_btn)
-        hlay0.addWidget(self.del_all_btn)
-
-        # ## Gerber associations
-        self.grb_list_label = QtWidgets.QLabel("<b>%s:</b>" % _("Keywords list"))
-        self.grb_list_label.setToolTip(
-            _("List of keywords used by\n"
-              "the autocompleter in FlatCAM.\n"
-              "The autocompleter is installed\n"
-              "in the Code Editor and for the Tcl Shell.")
-        )
-        self.layout.addWidget(self.grb_list_label)
-
-        qsettings = QSettings("Open Source", "FlatCAM")
-        if qsettings.contains("textbox_font_size"):
-            tb_fsize = qsettings.value('textbox_font_size', type=int)
-        else:
-            tb_fsize = 10
-
-        self.kw_list_text = FCTextArea()
-        self.kw_list_text.setReadOnly(True)
-        # self.grb_list_text.sizeHint(custom_sizehint=150)
-        self.layout.addWidget(self.kw_list_text)
-        font = QtGui.QFont()
-        font.setPointSize(tb_fsize)
-        self.kw_list_text.setFont(font)
-
-        self.kw_label = QtWidgets.QLabel('%s:' % _("Extension"))
-        self.kw_label.setToolTip(_("A keyword to be added or deleted to the list."))
-        self.kw_entry = FCEntry()
-
-        hlay1 = QtWidgets.QHBoxLayout()
-        self.layout.addLayout(hlay1)
-        hlay1.addWidget(self.kw_label)
-        hlay1.addWidget(self.kw_entry)
-
-        self.add_btn = FCButton(_("Add keyword"))
-        self.add_btn.setToolTip(_("Add a keyword to the list"))
-        self.del_btn = FCButton(_("Delete keyword"))
-        self.del_btn.setToolTip(_("Delete a keyword from the list"))
-
-        hlay2 = QtWidgets.QHBoxLayout()
-        self.layout.addLayout(hlay2)
-        hlay2.addWidget(self.add_btn)
-        hlay2.addWidget(self.del_btn)
-
-        # self.layout.addStretch()

+ 1 - 1
flatcamGUI/VisPyVisuals.py

@@ -462,7 +462,7 @@ class ShapeCollectionVisual(CompoundVisual):
         self.update_lock.acquire(True)
 
         # Merge shapes buffers
-        for data in self.data.values():
+        for data in list(self.data.values()):
             if data['visible'] and 'line_pts' in data:
                 try:
                     line_pts[data['layer']] += data['line_pts']

+ 19 - 0
flatcamGUI/preferences/OptionsGroupUI.py

@@ -0,0 +1,19 @@
+from PyQt5 import QtWidgets
+
+
+class OptionsGroupUI(QtWidgets.QGroupBox):
+    app = None
+
+    def __init__(self, title, parent=None):
+        # QtGui.QGroupBox.__init__(self, title, parent=parent)
+        super(OptionsGroupUI, self).__init__()
+        self.setStyleSheet("""
+        QGroupBox
+        {
+            font-size: 16px;
+            font-weight: bold;
+        }
+        """)
+
+        self.layout = QtWidgets.QVBoxLayout()
+        self.setLayout(self.layout)

+ 1135 - 0
flatcamGUI/preferences/PreferencesUIManager.py

@@ -0,0 +1,1135 @@
+import os
+from PyQt5 import QtGui, QtCore, QtWidgets
+from PyQt5.QtCore import QSettings
+from defaults import FlatCAMDefaults
+import logging
+
+import gettext
+import FlatCAMTranslation as fcTranslate
+import builtins
+
+fcTranslate.apply_language('strings')
+if '_' not in builtins.__dict__:
+    _ = gettext.gettext
+
+settings = QSettings("Open Source", "FlatCAM")
+if settings.contains("machinist"):
+    machinist_setting = settings.value('machinist', type=int)
+else:
+    machinist_setting = 0
+
+log = logging.getLogger('PreferencesUIManager')
+
+
+class PreferencesUIManager:
+
+    def __init__(self, defaults: FlatCAMDefaults, data_path: str, ui, inform):
+        """
+        Class that control the Preferences Tab
+
+        :param defaults:    a dictionary storage where all the application settings are stored
+        :param data_path:   a path to the file where all the preferences are stored for persistence
+        :param ui:          reference to the FlatCAMGUI class which constructs the UI
+        :param inform:      a pyqtSignal used to display information's in the StatusBar of the GUI
+        """
+
+        self.defaults = defaults
+        self.data_path = data_path
+        self.ui = ui
+        self.inform = inform
+        self.ignore_tab_close_event = False
+
+        # if Preferences are changed in the Edit -> Preferences tab the value will be set to True
+        self.preferences_changed_flag = False
+
+        # when adding entries here read the comments in the  method found below named:
+        # def new_object(self, kind, name, initialize, active=True, fit=True, plot=True)
+        self.defaults_form_fields = {
+            # General App
+            "decimals_inch": self.ui.general_defaults_form.general_app_group.precision_inch_entry,
+            "decimals_metric": self.ui.general_defaults_form.general_app_group.precision_metric_entry,
+            "units": self.ui.general_defaults_form.general_app_group.units_radio,
+            "global_graphic_engine": self.ui.general_defaults_form.general_app_group.ge_radio,
+            "global_app_level": self.ui.general_defaults_form.general_app_group.app_level_radio,
+            "global_portable": self.ui.general_defaults_form.general_app_group.portability_cb,
+            "global_language": self.ui.general_defaults_form.general_app_group.language_cb,
+
+            "global_systray_icon": self.ui.general_defaults_form.general_app_group.systray_cb,
+            "global_shell_at_startup": self.ui.general_defaults_form.general_app_group.shell_startup_cb,
+            "global_project_at_startup": self.ui.general_defaults_form.general_app_group.project_startup_cb,
+            "global_version_check": self.ui.general_defaults_form.general_app_group.version_check_cb,
+            "global_send_stats": self.ui.general_defaults_form.general_app_group.send_stats_cb,
+
+            "global_worker_number": self.ui.general_defaults_form.general_app_group.worker_number_sb,
+            "global_tolerance": self.ui.general_defaults_form.general_app_group.tol_entry,
+
+            "global_compression_level": self.ui.general_defaults_form.general_app_group.compress_spinner,
+            "global_save_compressed": self.ui.general_defaults_form.general_app_group.save_type_cb,
+            "global_autosave": self.ui.general_defaults_form.general_app_group.autosave_cb,
+            "global_autosave_timeout": self.ui.general_defaults_form.general_app_group.autosave_entry,
+
+            "global_tpdf_tmargin": self.ui.general_defaults_form.general_app_group.tmargin_entry,
+            "global_tpdf_bmargin": self.ui.general_defaults_form.general_app_group.bmargin_entry,
+            "global_tpdf_lmargin": self.ui.general_defaults_form.general_app_group.lmargin_entry,
+            "global_tpdf_rmargin": self.ui.general_defaults_form.general_app_group.rmargin_entry,
+
+            # General GUI Preferences
+            "global_theme": self.ui.general_defaults_form.general_gui_group.theme_radio,
+            "global_gray_icons": self.ui.general_defaults_form.general_gui_group.gray_icons_cb,
+            "global_layout": self.ui.general_defaults_form.general_gui_group.layout_combo,
+            "global_hover": self.ui.general_defaults_form.general_gui_group.hover_cb,
+            "global_selection_shape": self.ui.general_defaults_form.general_gui_group.selection_cb,
+
+            "global_sel_fill": self.ui.general_defaults_form.general_gui_group.sf_color_entry,
+            "global_sel_line": self.ui.general_defaults_form.general_gui_group.sl_color_entry,
+            "global_alt_sel_fill": self.ui.general_defaults_form.general_gui_group.alt_sf_color_entry,
+            "global_alt_sel_line": self.ui.general_defaults_form.general_gui_group.alt_sl_color_entry,
+            "global_draw_color": self.ui.general_defaults_form.general_gui_group.draw_color_entry,
+            "global_sel_draw_color": self.ui.general_defaults_form.general_gui_group.sel_draw_color_entry,
+
+            "global_proj_item_color": self.ui.general_defaults_form.general_gui_group.proj_color_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,
+
+            # General GUI Settings
+            "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_snap_max": self.ui.general_defaults_form.general_app_set_group.snap_max_dist_entry,
+            "global_workspace": self.ui.general_defaults_form.general_app_set_group.workspace_cb,
+            "global_workspaceT": self.ui.general_defaults_form.general_app_set_group.wk_cb,
+            "global_workspace_orientation": self.ui.general_defaults_form.general_app_set_group.wk_orientation_radio,
+
+            "global_cursor_type": self.ui.general_defaults_form.general_app_set_group.cursor_radio,
+            "global_cursor_size": self.ui.general_defaults_form.general_app_set_group.cursor_size_entry,
+            "global_cursor_width": self.ui.general_defaults_form.general_app_set_group.cursor_width_entry,
+            "global_cursor_color_enabled": self.ui.general_defaults_form.general_app_set_group.mouse_cursor_color_cb,
+            "global_cursor_color": self.ui.general_defaults_form.general_app_set_group.mouse_cursor_entry,
+            "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_delete_confirmation": self.ui.general_defaults_form.general_app_set_group.delete_conf_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_machinist_setting": self.ui.general_defaults_form.general_app_set_group.machinist_cb,
+
+            "global_bookmarks_limit": self.ui.general_defaults_form.general_app_set_group.bm_limit_spinner,
+            "global_activity_icon": self.ui.general_defaults_form.general_app_set_group.activity_combo,
+
+            # Gerber General
+            "gerber_plot": self.ui.gerber_defaults_form.gerber_gen_group.plot_cb,
+            "gerber_solid": self.ui.gerber_defaults_form.gerber_gen_group.solid_cb,
+            "gerber_multicolored": self.ui.gerber_defaults_form.gerber_gen_group.multicolored_cb,
+            "gerber_circle_steps": self.ui.gerber_defaults_form.gerber_gen_group.circle_steps_entry,
+            "gerber_def_units": self.ui.gerber_defaults_form.gerber_gen_group.gerber_units_radio,
+            "gerber_def_zeros": self.ui.gerber_defaults_form.gerber_gen_group.gerber_zeros_radio,
+            "gerber_clean_apertures": self.ui.gerber_defaults_form.gerber_gen_group.gerber_clean_cb,
+            "gerber_extra_buffering": self.ui.gerber_defaults_form.gerber_gen_group.gerber_extra_buffering,
+            "gerber_plot_fill": self.ui.gerber_defaults_form.gerber_gen_group.pf_color_entry,
+            "gerber_plot_line": self.ui.gerber_defaults_form.gerber_gen_group.pl_color_entry,
+
+            # Gerber Options
+            "gerber_isotooldia": self.ui.gerber_defaults_form.gerber_opt_group.iso_tool_dia_entry,
+            "gerber_isopasses": self.ui.gerber_defaults_form.gerber_opt_group.iso_width_entry,
+            "gerber_isooverlap": self.ui.gerber_defaults_form.gerber_opt_group.iso_overlap_entry,
+            "gerber_combine_passes": self.ui.gerber_defaults_form.gerber_opt_group.combine_passes_cb,
+            "gerber_iso_scope": self.ui.gerber_defaults_form.gerber_opt_group.iso_scope_radio,
+            "gerber_milling_type": self.ui.gerber_defaults_form.gerber_opt_group.milling_type_radio,
+            "gerber_noncoppermargin": self.ui.gerber_defaults_form.gerber_opt_group.noncopper_margin_entry,
+            "gerber_noncopperrounded": self.ui.gerber_defaults_form.gerber_opt_group.noncopper_rounded_cb,
+            "gerber_bboxmargin": self.ui.gerber_defaults_form.gerber_opt_group.bbmargin_entry,
+            "gerber_bboxrounded": self.ui.gerber_defaults_form.gerber_opt_group.bbrounded_cb,
+
+            # Gerber Advanced Options
+            "gerber_aperture_display": self.ui.gerber_defaults_form.gerber_adv_opt_group.aperture_table_visibility_cb,
+            # "gerber_aperture_scale_factor": self.ui.gerber_defaults_form.gerber_adv_opt_group.scale_aperture_entry,
+            # "gerber_aperture_buffer_factor": self.ui.gerber_defaults_form.gerber_adv_opt_group.buffer_aperture_entry,
+            "gerber_follow": self.ui.gerber_defaults_form.gerber_adv_opt_group.follow_cb,
+            "gerber_tool_type": self.ui.gerber_defaults_form.gerber_adv_opt_group.tool_type_radio,
+            "gerber_vtipdia": self.ui.gerber_defaults_form.gerber_adv_opt_group.tipdia_spinner,
+            "gerber_vtipangle": self.ui.gerber_defaults_form.gerber_adv_opt_group.tipangle_spinner,
+            "gerber_vcutz": self.ui.gerber_defaults_form.gerber_adv_opt_group.cutz_spinner,
+            "gerber_iso_type": self.ui.gerber_defaults_form.gerber_adv_opt_group.iso_type_radio,
+
+            "gerber_buffering": self.ui.gerber_defaults_form.gerber_adv_opt_group.buffering_radio,
+            "gerber_simplification": self.ui.gerber_defaults_form.gerber_adv_opt_group.simplify_cb,
+            "gerber_simp_tolerance": self.ui.gerber_defaults_form.gerber_adv_opt_group.simplification_tol_spinner,
+
+            # Gerber Export
+            "gerber_exp_units": self.ui.gerber_defaults_form.gerber_exp_group.gerber_units_radio,
+            "gerber_exp_integer": self.ui.gerber_defaults_form.gerber_exp_group.format_whole_entry,
+            "gerber_exp_decimals": self.ui.gerber_defaults_form.gerber_exp_group.format_dec_entry,
+            "gerber_exp_zeros": self.ui.gerber_defaults_form.gerber_exp_group.zeros_radio,
+
+            # Gerber Editor
+            "gerber_editor_sel_limit": self.ui.gerber_defaults_form.gerber_editor_group.sel_limit_entry,
+            "gerber_editor_newcode": self.ui.gerber_defaults_form.gerber_editor_group.addcode_entry,
+            "gerber_editor_newsize": self.ui.gerber_defaults_form.gerber_editor_group.addsize_entry,
+            "gerber_editor_newtype": self.ui.gerber_defaults_form.gerber_editor_group.addtype_combo,
+            "gerber_editor_newdim": self.ui.gerber_defaults_form.gerber_editor_group.adddim_entry,
+            "gerber_editor_array_size": self.ui.gerber_defaults_form.gerber_editor_group.grb_array_size_entry,
+            "gerber_editor_lin_axis": self.ui.gerber_defaults_form.gerber_editor_group.grb_axis_radio,
+            "gerber_editor_lin_pitch": self.ui.gerber_defaults_form.gerber_editor_group.grb_pitch_entry,
+            "gerber_editor_lin_angle": self.ui.gerber_defaults_form.gerber_editor_group.grb_angle_entry,
+            "gerber_editor_circ_dir": self.ui.gerber_defaults_form.gerber_editor_group.grb_circular_dir_radio,
+            "gerber_editor_circ_angle":
+                self.ui.gerber_defaults_form.gerber_editor_group.grb_circular_angle_entry,
+            "gerber_editor_scale_f": self.ui.gerber_defaults_form.gerber_editor_group.grb_scale_entry,
+            "gerber_editor_buff_f": self.ui.gerber_defaults_form.gerber_editor_group.grb_buff_entry,
+            "gerber_editor_ma_low": self.ui.gerber_defaults_form.gerber_editor_group.grb_ma_low_entry,
+            "gerber_editor_ma_high": self.ui.gerber_defaults_form.gerber_editor_group.grb_ma_high_entry,
+
+            # Excellon General
+            "excellon_plot": self.ui.excellon_defaults_form.excellon_gen_group.plot_cb,
+            "excellon_solid": self.ui.excellon_defaults_form.excellon_gen_group.solid_cb,
+            "excellon_format_upper_in":
+                self.ui.excellon_defaults_form.excellon_gen_group.excellon_format_upper_in_entry,
+            "excellon_format_lower_in":
+                self.ui.excellon_defaults_form.excellon_gen_group.excellon_format_lower_in_entry,
+            "excellon_format_upper_mm":
+                self.ui.excellon_defaults_form.excellon_gen_group.excellon_format_upper_mm_entry,
+            "excellon_format_lower_mm":
+                self.ui.excellon_defaults_form.excellon_gen_group.excellon_format_lower_mm_entry,
+            "excellon_zeros": self.ui.excellon_defaults_form.excellon_gen_group.excellon_zeros_radio,
+            "excellon_units": self.ui.excellon_defaults_form.excellon_gen_group.excellon_units_radio,
+            "excellon_update": self.ui.excellon_defaults_form.excellon_gen_group.update_excellon_cb,
+            "excellon_optimization_type": self.ui.excellon_defaults_form.excellon_gen_group.excellon_optimization_radio,
+            "excellon_search_time": self.ui.excellon_defaults_form.excellon_gen_group.optimization_time_entry,
+            "excellon_plot_fill": self.ui.excellon_defaults_form.excellon_gen_group.fill_color_entry,
+            "excellon_plot_line": self.ui.excellon_defaults_form.excellon_gen_group.line_color_entry,
+
+            # Excellon Options
+            "excellon_operation": self.ui.excellon_defaults_form.excellon_opt_group.operation_radio,
+            "excellon_milling_type": self.ui.excellon_defaults_form.excellon_opt_group.milling_type_radio,
+
+            "excellon_milling_dia": self.ui.excellon_defaults_form.excellon_opt_group.mill_dia_entry,
+
+            "excellon_cutz": self.ui.excellon_defaults_form.excellon_opt_group.cutz_entry,
+            "excellon_multidepth": self.ui.excellon_defaults_form.excellon_opt_group.mpass_cb,
+            "excellon_depthperpass": self.ui.excellon_defaults_form.excellon_opt_group.maxdepth_entry,
+            "excellon_travelz": self.ui.excellon_defaults_form.excellon_opt_group.travelz_entry,
+            "excellon_endz": self.ui.excellon_defaults_form.excellon_opt_group.endz_entry,
+            "excellon_endxy": self.ui.excellon_defaults_form.excellon_opt_group.endxy_entry,
+
+            "excellon_feedrate_z": self.ui.excellon_defaults_form.excellon_opt_group.feedrate_z_entry,
+            "excellon_spindlespeed": self.ui.excellon_defaults_form.excellon_opt_group.spindlespeed_entry,
+            "excellon_dwell": self.ui.excellon_defaults_form.excellon_opt_group.dwell_cb,
+            "excellon_dwelltime": self.ui.excellon_defaults_form.excellon_opt_group.dwelltime_entry,
+            "excellon_toolchange": self.ui.excellon_defaults_form.excellon_opt_group.toolchange_cb,
+            "excellon_toolchangez": self.ui.excellon_defaults_form.excellon_opt_group.toolchangez_entry,
+            "excellon_ppname_e": self.ui.excellon_defaults_form.excellon_opt_group.pp_excellon_name_cb,
+            "excellon_tooldia": self.ui.excellon_defaults_form.excellon_opt_group.tooldia_entry,
+            "excellon_slot_tooldia": self.ui.excellon_defaults_form.excellon_opt_group.slot_tooldia_entry,
+            "excellon_gcode_type": self.ui.excellon_defaults_form.excellon_opt_group.excellon_gcode_type_radio,
+
+            # Excellon Advanced Options
+            "excellon_offset": self.ui.excellon_defaults_form.excellon_adv_opt_group.offset_entry,
+            "excellon_toolchangexy": self.ui.excellon_defaults_form.excellon_adv_opt_group.toolchangexy_entry,
+            "excellon_startz": self.ui.excellon_defaults_form.excellon_adv_opt_group.estartz_entry,
+            "excellon_feedrate_rapid": self.ui.excellon_defaults_form.excellon_adv_opt_group.feedrate_rapid_entry,
+            "excellon_z_pdepth": self.ui.excellon_defaults_form.excellon_adv_opt_group.pdepth_entry,
+            "excellon_feedrate_probe": self.ui.excellon_defaults_form.excellon_adv_opt_group.feedrate_probe_entry,
+            "excellon_spindledir": self.ui.excellon_defaults_form.excellon_adv_opt_group.spindledir_radio,
+            "excellon_f_plunge": self.ui.excellon_defaults_form.excellon_adv_opt_group.fplunge_cb,
+            "excellon_f_retract": self.ui.excellon_defaults_form.excellon_adv_opt_group.fretract_cb,
+
+            # Excellon Export
+            "excellon_exp_units": self.ui.excellon_defaults_form.excellon_exp_group.excellon_units_radio,
+            "excellon_exp_format": self.ui.excellon_defaults_form.excellon_exp_group.format_radio,
+            "excellon_exp_integer": self.ui.excellon_defaults_form.excellon_exp_group.format_whole_entry,
+            "excellon_exp_decimals": self.ui.excellon_defaults_form.excellon_exp_group.format_dec_entry,
+            "excellon_exp_zeros": self.ui.excellon_defaults_form.excellon_exp_group.zeros_radio,
+            "excellon_exp_slot_type": self.ui.excellon_defaults_form.excellon_exp_group.slot_type_radio,
+
+            # Excellon Editor
+            "excellon_editor_sel_limit": self.ui.excellon_defaults_form.excellon_editor_group.sel_limit_entry,
+            "excellon_editor_newdia": self.ui.excellon_defaults_form.excellon_editor_group.addtool_entry,
+            "excellon_editor_array_size": self.ui.excellon_defaults_form.excellon_editor_group.drill_array_size_entry,
+            "excellon_editor_lin_dir": self.ui.excellon_defaults_form.excellon_editor_group.drill_axis_radio,
+            "excellon_editor_lin_pitch": self.ui.excellon_defaults_form.excellon_editor_group.drill_pitch_entry,
+            "excellon_editor_lin_angle": self.ui.excellon_defaults_form.excellon_editor_group.drill_angle_entry,
+            "excellon_editor_circ_dir": self.ui.excellon_defaults_form.excellon_editor_group.drill_circular_dir_radio,
+            "excellon_editor_circ_angle":
+                self.ui.excellon_defaults_form.excellon_editor_group.drill_circular_angle_entry,
+            # Excellon Slots
+            "excellon_editor_slot_direction":
+                self.ui.excellon_defaults_form.excellon_editor_group.slot_axis_radio,
+            "excellon_editor_slot_angle":
+                self.ui.excellon_defaults_form.excellon_editor_group.slot_angle_spinner,
+            "excellon_editor_slot_length":
+                self.ui.excellon_defaults_form.excellon_editor_group.slot_length_entry,
+            # Excellon Slots
+            "excellon_editor_slot_array_size":
+                self.ui.excellon_defaults_form.excellon_editor_group.slot_array_size_entry,
+            "excellon_editor_slot_lin_dir": self.ui.excellon_defaults_form.excellon_editor_group.slot_array_axis_radio,
+            "excellon_editor_slot_lin_pitch":
+                self.ui.excellon_defaults_form.excellon_editor_group.slot_array_pitch_entry,
+            "excellon_editor_slot_lin_angle":
+                self.ui.excellon_defaults_form.excellon_editor_group.slot_array_angle_entry,
+            "excellon_editor_slot_circ_dir":
+                self.ui.excellon_defaults_form.excellon_editor_group.slot_array_circular_dir_radio,
+            "excellon_editor_slot_circ_angle":
+                self.ui.excellon_defaults_form.excellon_editor_group.slot_array_circular_angle_entry,
+
+            # Geometry General
+            "geometry_plot": self.ui.geometry_defaults_form.geometry_gen_group.plot_cb,
+            "geometry_circle_steps": self.ui.geometry_defaults_form.geometry_gen_group.circle_steps_entry,
+            "geometry_cnctooldia": self.ui.geometry_defaults_form.geometry_gen_group.cnctooldia_entry,
+            "geometry_plot_line": self.ui.geometry_defaults_form.geometry_gen_group.line_color_entry,
+
+            # Geometry Options
+            "geometry_cutz": self.ui.geometry_defaults_form.geometry_opt_group.cutz_entry,
+            "geometry_travelz": self.ui.geometry_defaults_form.geometry_opt_group.travelz_entry,
+            "geometry_feedrate": self.ui.geometry_defaults_form.geometry_opt_group.cncfeedrate_entry,
+            "geometry_feedrate_z": self.ui.geometry_defaults_form.geometry_opt_group.feedrate_z_entry,
+            "geometry_spindlespeed": self.ui.geometry_defaults_form.geometry_opt_group.cncspindlespeed_entry,
+            "geometry_dwell": self.ui.geometry_defaults_form.geometry_opt_group.dwell_cb,
+            "geometry_dwelltime": self.ui.geometry_defaults_form.geometry_opt_group.dwelltime_entry,
+            "geometry_ppname_g": self.ui.geometry_defaults_form.geometry_opt_group.pp_geometry_name_cb,
+            "geometry_toolchange": self.ui.geometry_defaults_form.geometry_opt_group.toolchange_cb,
+            "geometry_toolchangez": self.ui.geometry_defaults_form.geometry_opt_group.toolchangez_entry,
+            "geometry_endz": self.ui.geometry_defaults_form.geometry_opt_group.endz_entry,
+            "geometry_endxy": self.ui.geometry_defaults_form.geometry_opt_group.endxy_entry,
+            "geometry_depthperpass": self.ui.geometry_defaults_form.geometry_opt_group.depthperpass_entry,
+            "geometry_multidepth": self.ui.geometry_defaults_form.geometry_opt_group.multidepth_cb,
+
+            # Geometry Advanced Options
+            "geometry_toolchangexy": self.ui.geometry_defaults_form.geometry_adv_opt_group.toolchangexy_entry,
+            "geometry_startz": self.ui.geometry_defaults_form.geometry_adv_opt_group.gstartz_entry,
+            "geometry_feedrate_rapid": self.ui.geometry_defaults_form.geometry_adv_opt_group.feedrate_rapid_entry,
+            "geometry_extracut": self.ui.geometry_defaults_form.geometry_adv_opt_group.extracut_cb,
+            "geometry_extracut_length": self.ui.geometry_defaults_form.geometry_adv_opt_group.e_cut_entry,
+            "geometry_z_pdepth": self.ui.geometry_defaults_form.geometry_adv_opt_group.pdepth_entry,
+            "geometry_feedrate_probe": self.ui.geometry_defaults_form.geometry_adv_opt_group.feedrate_probe_entry,
+            "geometry_spindledir": self.ui.geometry_defaults_form.geometry_adv_opt_group.spindledir_radio,
+            "geometry_f_plunge": self.ui.geometry_defaults_form.geometry_adv_opt_group.fplunge_cb,
+            "geometry_segx": self.ui.geometry_defaults_form.geometry_adv_opt_group.segx_entry,
+            "geometry_segy": self.ui.geometry_defaults_form.geometry_adv_opt_group.segy_entry,
+            "geometry_area_exclusion": self.ui.geometry_defaults_form.geometry_adv_opt_group.exclusion_cb,
+            "geometry_area_shape": self.ui.geometry_defaults_form.geometry_adv_opt_group.area_shape_radio,
+            "geometry_area_strategy": self.ui.geometry_defaults_form.geometry_adv_opt_group.strategy_radio,
+            "geometry_area_overz": self.ui.geometry_defaults_form.geometry_adv_opt_group.over_z_entry,
+
+            # Geometry Editor
+            "geometry_editor_sel_limit": self.ui.geometry_defaults_form.geometry_editor_group.sel_limit_entry,
+            "geometry_editor_milling_type": self.ui.geometry_defaults_form.geometry_editor_group.milling_type_radio,
+
+            # CNCJob General
+            "cncjob_plot": self.ui.cncjob_defaults_form.cncjob_gen_group.plot_cb,
+            "cncjob_plot_kind": self.ui.cncjob_defaults_form.cncjob_gen_group.cncplot_method_radio,
+            "cncjob_annotation": self.ui.cncjob_defaults_form.cncjob_gen_group.annotation_cb,
+
+            "cncjob_tooldia": self.ui.cncjob_defaults_form.cncjob_gen_group.tooldia_entry,
+            "cncjob_coords_type": self.ui.cncjob_defaults_form.cncjob_gen_group.coords_type_radio,
+            "cncjob_coords_decimals": self.ui.cncjob_defaults_form.cncjob_gen_group.coords_dec_entry,
+            "cncjob_fr_decimals": self.ui.cncjob_defaults_form.cncjob_gen_group.fr_dec_entry,
+            "cncjob_steps_per_circle": self.ui.cncjob_defaults_form.cncjob_gen_group.steps_per_circle_entry,
+            "cncjob_line_ending": self.ui.cncjob_defaults_form.cncjob_gen_group.line_ending_cb,
+            "cncjob_plot_line": self.ui.cncjob_defaults_form.cncjob_gen_group.line_color_entry,
+            "cncjob_plot_fill": self.ui.cncjob_defaults_form.cncjob_gen_group.fill_color_entry,
+            "cncjob_travel_line": self.ui.cncjob_defaults_form.cncjob_gen_group.tline_color_entry,
+            "cncjob_travel_fill": self.ui.cncjob_defaults_form.cncjob_gen_group.tfill_color_entry,
+
+            # CNC Job Options
+            "cncjob_prepend": self.ui.cncjob_defaults_form.cncjob_opt_group.prepend_text,
+            "cncjob_append": self.ui.cncjob_defaults_form.cncjob_opt_group.append_text,
+
+            # CNC Job Advanced Options
+            "cncjob_toolchange_macro": self.ui.cncjob_defaults_form.cncjob_adv_opt_group.toolchange_text,
+            "cncjob_toolchange_macro_enable": self.ui.cncjob_defaults_form.cncjob_adv_opt_group.toolchange_cb,
+            "cncjob_annotation_fontsize": self.ui.cncjob_defaults_form.cncjob_adv_opt_group.annotation_fontsize_sp,
+            "cncjob_annotation_fontcolor": self.ui.cncjob_defaults_form.cncjob_adv_opt_group.annotation_fontcolor_entry,
+
+            # NCC Tool
+            "tools_ncctools": self.ui.tools_defaults_form.tools_ncc_group.ncc_tool_dia_entry,
+            "tools_nccorder": self.ui.tools_defaults_form.tools_ncc_group.ncc_order_radio,
+            "tools_nccoverlap": self.ui.tools_defaults_form.tools_ncc_group.ncc_overlap_entry,
+            "tools_nccmargin": self.ui.tools_defaults_form.tools_ncc_group.ncc_margin_entry,
+            "tools_nccmethod": self.ui.tools_defaults_form.tools_ncc_group.ncc_method_combo,
+            "tools_nccconnect": self.ui.tools_defaults_form.tools_ncc_group.ncc_connect_cb,
+            "tools_ncccontour": self.ui.tools_defaults_form.tools_ncc_group.ncc_contour_cb,
+            "tools_nccrest": self.ui.tools_defaults_form.tools_ncc_group.ncc_rest_cb,
+            "tools_ncc_offset_choice": self.ui.tools_defaults_form.tools_ncc_group.ncc_choice_offset_cb,
+            "tools_ncc_offset_value": self.ui.tools_defaults_form.tools_ncc_group.ncc_offset_spinner,
+            "tools_nccref": self.ui.tools_defaults_form.tools_ncc_group.select_combo,
+            "tools_ncc_area_shape": self.ui.tools_defaults_form.tools_ncc_group.area_shape_radio,
+            "tools_ncc_plotting": self.ui.tools_defaults_form.tools_ncc_group.ncc_plotting_radio,
+            "tools_nccmilling_type": self.ui.tools_defaults_form.tools_ncc_group.milling_type_radio,
+            "tools_ncctool_type": self.ui.tools_defaults_form.tools_ncc_group.tool_type_radio,
+            "tools_ncccutz": self.ui.tools_defaults_form.tools_ncc_group.cutz_entry,
+            "tools_ncctipdia": self.ui.tools_defaults_form.tools_ncc_group.tipdia_entry,
+            "tools_ncctipangle": self.ui.tools_defaults_form.tools_ncc_group.tipangle_entry,
+            "tools_nccnewdia": self.ui.tools_defaults_form.tools_ncc_group.newdia_entry,
+
+            # CutOut Tool
+            "tools_cutouttooldia": self.ui.tools_defaults_form.tools_cutout_group.cutout_tooldia_entry,
+            "tools_cutoutkind": self.ui.tools_defaults_form.tools_cutout_group.obj_kind_combo,
+            "tools_cutoutmargin": self.ui.tools_defaults_form.tools_cutout_group.cutout_margin_entry,
+            "tools_cutout_z": self.ui.tools_defaults_form.tools_cutout_group.cutz_entry,
+            "tools_cutout_depthperpass": self.ui.tools_defaults_form.tools_cutout_group.maxdepth_entry,
+            "tools_cutout_mdepth": self.ui.tools_defaults_form.tools_cutout_group.mpass_cb,
+            "tools_cutoutgapsize": self.ui.tools_defaults_form.tools_cutout_group.cutout_gap_entry,
+            "tools_gaps_ff": self.ui.tools_defaults_form.tools_cutout_group.gaps_combo,
+            "tools_cutout_convexshape": self.ui.tools_defaults_form.tools_cutout_group.convex_box,
+
+            # Paint Area Tool
+            "tools_painttooldia": self.ui.tools_defaults_form.tools_paint_group.painttooldia_entry,
+            "tools_paintorder": self.ui.tools_defaults_form.tools_paint_group.paint_order_radio,
+            "tools_paintoverlap": self.ui.tools_defaults_form.tools_paint_group.paintoverlap_entry,
+            "tools_paintmargin": self.ui.tools_defaults_form.tools_paint_group.paintmargin_entry,
+            "tools_paintmethod": self.ui.tools_defaults_form.tools_paint_group.paintmethod_combo,
+            "tools_selectmethod": self.ui.tools_defaults_form.tools_paint_group.selectmethod_combo,
+            "tools_paint_area_shape": self.ui.tools_defaults_form.tools_paint_group.area_shape_radio,
+            "tools_pathconnect": self.ui.tools_defaults_form.tools_paint_group.pathconnect_cb,
+            "tools_paintcontour": self.ui.tools_defaults_form.tools_paint_group.contour_cb,
+            "tools_paint_plotting": self.ui.tools_defaults_form.tools_paint_group.paint_plotting_radio,
+
+            "tools_paintrest": self.ui.tools_defaults_form.tools_paint_group.rest_cb,
+            "tools_painttool_type": self.ui.tools_defaults_form.tools_paint_group.tool_type_radio,
+            "tools_paintcutz": self.ui.tools_defaults_form.tools_paint_group.cutz_entry,
+            "tools_painttipdia": self.ui.tools_defaults_form.tools_paint_group.tipdia_entry,
+            "tools_painttipangle": self.ui.tools_defaults_form.tools_paint_group.tipangle_entry,
+            "tools_paintnewdia": self.ui.tools_defaults_form.tools_paint_group.newdia_entry,
+
+            # 2-sided Tool
+            "tools_2sided_mirror_axis": self.ui.tools_defaults_form.tools_2sided_group.mirror_axis_radio,
+            "tools_2sided_axis_loc": self.ui.tools_defaults_form.tools_2sided_group.axis_location_radio,
+            "tools_2sided_drilldia": self.ui.tools_defaults_form.tools_2sided_group.drill_dia_entry,
+            "tools_2sided_allign_axis": self.ui.tools_defaults_form.tools_2sided_group.align_axis_radio,
+
+            # Film Tool
+            "tools_film_type": self.ui.tools_defaults_form.tools_film_group.film_type_radio,
+            "tools_film_boundary": self.ui.tools_defaults_form.tools_film_group.film_boundary_entry,
+            "tools_film_scale_stroke": self.ui.tools_defaults_form.tools_film_group.film_scale_stroke_entry,
+            "tools_film_color": self.ui.tools_defaults_form.tools_film_group.film_color_entry,
+            "tools_film_scale_cb": self.ui.tools_defaults_form.tools_film_group.film_scale_cb,
+            "tools_film_scale_x_entry": self.ui.tools_defaults_form.tools_film_group.film_scalex_entry,
+            "tools_film_scale_y_entry": self.ui.tools_defaults_form.tools_film_group.film_scaley_entry,
+            "tools_film_skew_cb": self.ui.tools_defaults_form.tools_film_group.film_skew_cb,
+            "tools_film_skew_x_entry": self.ui.tools_defaults_form.tools_film_group.film_skewx_entry,
+            "tools_film_skew_y_entry": self.ui.tools_defaults_form.tools_film_group.film_skewy_entry,
+            "tools_film_skew_ref_radio": self.ui.tools_defaults_form.tools_film_group.film_skew_reference,
+            "tools_film_mirror_cb": self.ui.tools_defaults_form.tools_film_group.film_mirror_cb,
+            "tools_film_mirror_axis_radio": self.ui.tools_defaults_form.tools_film_group.film_mirror_axis,
+            "tools_film_file_type_radio": self.ui.tools_defaults_form.tools_film_group.file_type_radio,
+            "tools_film_orientation": self.ui.tools_defaults_form.tools_film_group.orientation_radio,
+            "tools_film_pagesize": self.ui.tools_defaults_form.tools_film_group.pagesize_combo,
+
+            # Panelize Tool
+            "tools_panelize_spacing_columns": self.ui.tools_defaults_form.tools_panelize_group.pspacing_columns,
+            "tools_panelize_spacing_rows": self.ui.tools_defaults_form.tools_panelize_group.pspacing_rows,
+            "tools_panelize_columns": self.ui.tools_defaults_form.tools_panelize_group.pcolumns,
+            "tools_panelize_rows": self.ui.tools_defaults_form.tools_panelize_group.prows,
+            "tools_panelize_constrain": self.ui.tools_defaults_form.tools_panelize_group.pconstrain_cb,
+            "tools_panelize_constrainx": self.ui.tools_defaults_form.tools_panelize_group.px_width_entry,
+            "tools_panelize_constrainy": self.ui.tools_defaults_form.tools_panelize_group.py_height_entry,
+            "tools_panelize_panel_type": self.ui.tools_defaults_form.tools_panelize_group.panel_type_radio,
+
+            # Calculators Tool
+            "tools_calc_vshape_tip_dia": self.ui.tools_defaults_form.tools_calculators_group.tip_dia_entry,
+            "tools_calc_vshape_tip_angle": self.ui.tools_defaults_form.tools_calculators_group.tip_angle_entry,
+            "tools_calc_vshape_cut_z": self.ui.tools_defaults_form.tools_calculators_group.cut_z_entry,
+            "tools_calc_electro_length": self.ui.tools_defaults_form.tools_calculators_group.pcblength_entry,
+            "tools_calc_electro_width": self.ui.tools_defaults_form.tools_calculators_group.pcbwidth_entry,
+            "tools_calc_electro_cdensity": self.ui.tools_defaults_form.tools_calculators_group.cdensity_entry,
+            "tools_calc_electro_growth": self.ui.tools_defaults_form.tools_calculators_group.growth_entry,
+
+            # Transformations Tool
+            "tools_transform_rotate": self.ui.tools_defaults_form.tools_transform_group.rotate_entry,
+            "tools_transform_skew_x": self.ui.tools_defaults_form.tools_transform_group.skewx_entry,
+            "tools_transform_skew_y": self.ui.tools_defaults_form.tools_transform_group.skewy_entry,
+            "tools_transform_scale_x": self.ui.tools_defaults_form.tools_transform_group.scalex_entry,
+            "tools_transform_scale_y": self.ui.tools_defaults_form.tools_transform_group.scaley_entry,
+            "tools_transform_scale_link": self.ui.tools_defaults_form.tools_transform_group.link_cb,
+            "tools_transform_scale_reference": self.ui.tools_defaults_form.tools_transform_group.reference_cb,
+            "tools_transform_offset_x": self.ui.tools_defaults_form.tools_transform_group.offx_entry,
+            "tools_transform_offset_y": self.ui.tools_defaults_form.tools_transform_group.offy_entry,
+            "tools_transform_mirror_reference": self.ui.tools_defaults_form.tools_transform_group.mirror_reference_cb,
+            "tools_transform_mirror_point": self.ui.tools_defaults_form.tools_transform_group.flip_ref_entry,
+            "tools_transform_buffer_dis": self.ui.tools_defaults_form.tools_transform_group.buffer_entry,
+            "tools_transform_buffer_factor": self.ui.tools_defaults_form.tools_transform_group.buffer_factor_entry,
+            "tools_transform_buffer_corner": self.ui.tools_defaults_form.tools_transform_group.buffer_rounded_cb,
+
+            # SolderPaste Dispensing Tool
+            "tools_solderpaste_tools": self.ui.tools_defaults_form.tools_solderpaste_group.nozzle_tool_dia_entry,
+            "tools_solderpaste_new": self.ui.tools_defaults_form.tools_solderpaste_group.addtool_entry,
+            "tools_solderpaste_z_start": self.ui.tools_defaults_form.tools_solderpaste_group.z_start_entry,
+            "tools_solderpaste_z_dispense": self.ui.tools_defaults_form.tools_solderpaste_group.z_dispense_entry,
+            "tools_solderpaste_z_stop": self.ui.tools_defaults_form.tools_solderpaste_group.z_stop_entry,
+            "tools_solderpaste_z_travel": self.ui.tools_defaults_form.tools_solderpaste_group.z_travel_entry,
+            "tools_solderpaste_z_toolchange": self.ui.tools_defaults_form.tools_solderpaste_group.z_toolchange_entry,
+            "tools_solderpaste_xy_toolchange": self.ui.tools_defaults_form.tools_solderpaste_group.xy_toolchange_entry,
+            "tools_solderpaste_frxy": self.ui.tools_defaults_form.tools_solderpaste_group.frxy_entry,
+            "tools_solderpaste_frz": self.ui.tools_defaults_form.tools_solderpaste_group.frz_entry,
+            "tools_solderpaste_frz_dispense": self.ui.tools_defaults_form.tools_solderpaste_group.frz_dispense_entry,
+            "tools_solderpaste_speedfwd": self.ui.tools_defaults_form.tools_solderpaste_group.speedfwd_entry,
+            "tools_solderpaste_dwellfwd": self.ui.tools_defaults_form.tools_solderpaste_group.dwellfwd_entry,
+            "tools_solderpaste_speedrev": self.ui.tools_defaults_form.tools_solderpaste_group.speedrev_entry,
+            "tools_solderpaste_dwellrev": self.ui.tools_defaults_form.tools_solderpaste_group.dwellrev_entry,
+            "tools_solderpaste_pp": self.ui.tools_defaults_form.tools_solderpaste_group.pp_combo,
+            "tools_sub_close_paths": self.ui.tools_defaults_form.tools_sub_group.close_paths_cb,
+
+            # #######################################################################################################
+            # ########################################## TOOLS 2 ####################################################
+            # #######################################################################################################
+
+            # Optimal Tool
+            "tools_opt_precision": self.ui.tools2_defaults_form.tools2_optimal_group.precision_sp,
+
+            # Check Rules Tool
+            "tools_cr_trace_size": self.ui.tools2_defaults_form.tools2_checkrules_group.trace_size_cb,
+            "tools_cr_trace_size_val": self.ui.tools2_defaults_form.tools2_checkrules_group.trace_size_entry,
+            "tools_cr_c2c": self.ui.tools2_defaults_form.tools2_checkrules_group.clearance_copper2copper_cb,
+            "tools_cr_c2c_val": self.ui.tools2_defaults_form.tools2_checkrules_group.clearance_copper2copper_entry,
+            "tools_cr_c2o": self.ui.tools2_defaults_form.tools2_checkrules_group.clearance_copper2ol_cb,
+            "tools_cr_c2o_val": self.ui.tools2_defaults_form.tools2_checkrules_group.clearance_copper2ol_entry,
+            "tools_cr_s2s": self.ui.tools2_defaults_form.tools2_checkrules_group.clearance_silk2silk_cb,
+            "tools_cr_s2s_val": self.ui.tools2_defaults_form.tools2_checkrules_group.clearance_silk2silk_entry,
+            "tools_cr_s2sm": self.ui.tools2_defaults_form.tools2_checkrules_group.clearance_silk2sm_cb,
+            "tools_cr_s2sm_val": self.ui.tools2_defaults_form.tools2_checkrules_group.clearance_silk2sm_entry,
+            "tools_cr_s2o": self.ui.tools2_defaults_form.tools2_checkrules_group.clearance_silk2ol_cb,
+            "tools_cr_s2o_val": self.ui.tools2_defaults_form.tools2_checkrules_group.clearance_silk2ol_entry,
+            "tools_cr_sm2sm": self.ui.tools2_defaults_form.tools2_checkrules_group.clearance_sm2sm_cb,
+            "tools_cr_sm2sm_val": self.ui.tools2_defaults_form.tools2_checkrules_group.clearance_sm2sm_entry,
+            "tools_cr_ri": self.ui.tools2_defaults_form.tools2_checkrules_group.ring_integrity_cb,
+            "tools_cr_ri_val": self.ui.tools2_defaults_form.tools2_checkrules_group.ring_integrity_entry,
+            "tools_cr_h2h": self.ui.tools2_defaults_form.tools2_checkrules_group.clearance_d2d_cb,
+            "tools_cr_h2h_val": self.ui.tools2_defaults_form.tools2_checkrules_group.clearance_d2d_entry,
+            "tools_cr_dh": self.ui.tools2_defaults_form.tools2_checkrules_group.drill_size_cb,
+            "tools_cr_dh_val": self.ui.tools2_defaults_form.tools2_checkrules_group.drill_size_entry,
+
+            # QRCode Tool
+            "tools_qrcode_version": self.ui.tools2_defaults_form.tools2_qrcode_group.version_entry,
+            "tools_qrcode_error": self.ui.tools2_defaults_form.tools2_qrcode_group.error_radio,
+            "tools_qrcode_box_size": self.ui.tools2_defaults_form.tools2_qrcode_group.bsize_entry,
+            "tools_qrcode_border_size": self.ui.tools2_defaults_form.tools2_qrcode_group.border_size_entry,
+            "tools_qrcode_qrdata": self.ui.tools2_defaults_form.tools2_qrcode_group.text_data,
+            "tools_qrcode_polarity": self.ui.tools2_defaults_form.tools2_qrcode_group.pol_radio,
+            "tools_qrcode_rounded": self.ui.tools2_defaults_form.tools2_qrcode_group.bb_radio,
+            "tools_qrcode_fill_color": self.ui.tools2_defaults_form.tools2_qrcode_group.fill_color_entry,
+            "tools_qrcode_back_color": self.ui.tools2_defaults_form.tools2_qrcode_group.back_color_entry,
+            "tools_qrcode_sel_limit": self.ui.tools2_defaults_form.tools2_qrcode_group.sel_limit_entry,
+
+            # Copper Thieving Tool
+            "tools_copper_thieving_clearance": self.ui.tools2_defaults_form.tools2_cfill_group.clearance_entry,
+            "tools_copper_thieving_margin": self.ui.tools2_defaults_form.tools2_cfill_group.margin_entry,
+            "tools_copper_thieving_reference": self.ui.tools2_defaults_form.tools2_cfill_group.reference_radio,
+            "tools_copper_thieving_box_type": self.ui.tools2_defaults_form.tools2_cfill_group.bbox_type_radio,
+            "tools_copper_thieving_circle_steps": self.ui.tools2_defaults_form.tools2_cfill_group.circlesteps_entry,
+            "tools_copper_thieving_fill_type": self.ui.tools2_defaults_form.tools2_cfill_group.fill_type_radio,
+            "tools_copper_thieving_dots_dia": self.ui.tools2_defaults_form.tools2_cfill_group.dot_dia_entry,
+            "tools_copper_thieving_dots_spacing": self.ui.tools2_defaults_form.tools2_cfill_group.dot_spacing_entry,
+            "tools_copper_thieving_squares_size": self.ui.tools2_defaults_form.tools2_cfill_group.square_size_entry,
+            "tools_copper_thieving_squares_spacing":
+                self.ui.tools2_defaults_form.tools2_cfill_group.squares_spacing_entry,
+            "tools_copper_thieving_lines_size": self.ui.tools2_defaults_form.tools2_cfill_group.line_size_entry,
+            "tools_copper_thieving_lines_spacing": self.ui.tools2_defaults_form.tools2_cfill_group.lines_spacing_entry,
+            "tools_copper_thieving_rb_margin": self.ui.tools2_defaults_form.tools2_cfill_group.rb_margin_entry,
+            "tools_copper_thieving_rb_thickness": self.ui.tools2_defaults_form.tools2_cfill_group.rb_thickness_entry,
+            "tools_copper_thieving_mask_clearance": self.ui.tools2_defaults_form.tools2_cfill_group.clearance_ppm_entry,
+
+            # Fiducials Tool
+            "tools_fiducials_dia": self.ui.tools2_defaults_form.tools2_fiducials_group.dia_entry,
+            "tools_fiducials_margin": self.ui.tools2_defaults_form.tools2_fiducials_group.margin_entry,
+            "tools_fiducials_mode": self.ui.tools2_defaults_form.tools2_fiducials_group.mode_radio,
+            "tools_fiducials_second_pos": self.ui.tools2_defaults_form.tools2_fiducials_group.pos_radio,
+            "tools_fiducials_type": self.ui.tools2_defaults_form.tools2_fiducials_group.fid_type_radio,
+            "tools_fiducials_line_thickness": self.ui.tools2_defaults_form.tools2_fiducials_group.line_thickness_entry,
+
+            # Calibration Tool
+            "tools_cal_calsource": self.ui.tools2_defaults_form.tools2_cal_group.cal_source_radio,
+            "tools_cal_travelz": self.ui.tools2_defaults_form.tools2_cal_group.travelz_entry,
+            "tools_cal_verz": self.ui.tools2_defaults_form.tools2_cal_group.verz_entry,
+            "tools_cal_zeroz": self.ui.tools2_defaults_form.tools2_cal_group.zeroz_cb,
+            "tools_cal_toolchangez": self.ui.tools2_defaults_form.tools2_cal_group.toolchangez_entry,
+            "tools_cal_toolchange_xy": self.ui.tools2_defaults_form.tools2_cal_group.toolchange_xy_entry,
+            "tools_cal_sec_point": self.ui.tools2_defaults_form.tools2_cal_group.second_point_radio,
+
+            # Extract Drills Tool
+            "tools_edrills_hole_type": self.ui.tools2_defaults_form.tools2_edrills_group.hole_size_radio,
+            "tools_edrills_hole_fixed_dia": self.ui.tools2_defaults_form.tools2_edrills_group.dia_entry,
+            "tools_edrills_hole_prop_factor": self.ui.tools2_defaults_form.tools2_edrills_group.factor_entry,
+            "tools_edrills_circular_ring": self.ui.tools2_defaults_form.tools2_edrills_group.circular_ring_entry,
+            "tools_edrills_oblong_ring": self.ui.tools2_defaults_form.tools2_edrills_group.oblong_ring_entry,
+            "tools_edrills_square_ring": self.ui.tools2_defaults_form.tools2_edrills_group.square_ring_entry,
+            "tools_edrills_rectangular_ring": self.ui.tools2_defaults_form.tools2_edrills_group.rectangular_ring_entry,
+            "tools_edrills_others_ring": self.ui.tools2_defaults_form.tools2_edrills_group.other_ring_entry,
+            "tools_edrills_circular": self.ui.tools2_defaults_form.tools2_edrills_group.circular_cb,
+            "tools_edrills_oblong": self.ui.tools2_defaults_form.tools2_edrills_group.oblong_cb,
+            "tools_edrills_square": self.ui.tools2_defaults_form.tools2_edrills_group.square_cb,
+            "tools_edrills_rectangular": self.ui.tools2_defaults_form.tools2_edrills_group.rectangular_cb,
+            "tools_edrills_others": self.ui.tools2_defaults_form.tools2_edrills_group.other_cb,
+
+            # Punch Gerber Tool
+            "tools_punch_hole_type": self.ui.tools2_defaults_form.tools2_punch_group.hole_size_radio,
+            "tools_punch_hole_fixed_dia": self.ui.tools2_defaults_form.tools2_punch_group.dia_entry,
+            "tools_punch_hole_prop_factor": self.ui.tools2_defaults_form.tools2_punch_group.factor_entry,
+            "tools_punch_circular_ring": self.ui.tools2_defaults_form.tools2_punch_group.circular_ring_entry,
+            "tools_punch_oblong_ring": self.ui.tools2_defaults_form.tools2_punch_group.oblong_ring_entry,
+            "tools_punch_square_ring": self.ui.tools2_defaults_form.tools2_punch_group.square_ring_entry,
+            "tools_punch_rectangular_ring": self.ui.tools2_defaults_form.tools2_punch_group.rectangular_ring_entry,
+            "tools_punch_others_ring": self.ui.tools2_defaults_form.tools2_punch_group.other_ring_entry,
+            "tools_punch_circular": self.ui.tools2_defaults_form.tools2_punch_group.circular_cb,
+            "tools_punch_oblong": self.ui.tools2_defaults_form.tools2_punch_group.oblong_cb,
+            "tools_punch_square": self.ui.tools2_defaults_form.tools2_punch_group.square_cb,
+            "tools_punch_rectangular": self.ui.tools2_defaults_form.tools2_punch_group.rectangular_cb,
+            "tools_punch_others": self.ui.tools2_defaults_form.tools2_punch_group.other_cb,
+
+            # Invert Gerber Tool
+            "tools_invert_margin": self.ui.tools2_defaults_form.tools2_invert_group.margin_entry,
+            "tools_invert_join_style": self.ui.tools2_defaults_form.tools2_invert_group.join_radio,
+
+            # Utilities
+            # File associations
+            "fa_excellon": self.ui.util_defaults_form.fa_excellon_group.exc_list_text,
+            "fa_gcode": self.ui.util_defaults_form.fa_gcode_group.gco_list_text,
+            # "fa_geometry": self.ui.util_defaults_form.fa_geometry_group.close_paths_cb,
+            "fa_gerber": self.ui.util_defaults_form.fa_gerber_group.grb_list_text,
+            "util_autocomplete_keywords": self.ui.util_defaults_form.kw_group.kw_list_text,
+
+        }
+
+    def defaults_read_form(self):
+        """
+        Will read all the values in the Preferences GUI and update the defaults dictionary.
+
+        :return: None
+        """
+        for option in self.defaults_form_fields:
+            try:
+                self.defaults[option] = self.defaults_form_fields[option].get_value()
+            except Exception as e:
+                log.debug("App.defaults_read_form() --> %s" % str(e))
+
+    def defaults_write_form(self, factor=None, fl_units=None, source_dict=None):
+        """
+        Will set the values for all the GUI elements in Preferences GUI based on the values found in the
+        self.defaults dictionary.
+
+        :param factor: will apply a factor to the values that written in the GUI elements
+        :param fl_units: current measuring units in FlatCAM: Metric or Inch
+        :param source_dict: the repository of options, usually is the self.defaults
+        :return: None
+        """
+
+        options_storage = self.defaults if source_dict is None else source_dict
+
+        for option in options_storage:
+            if source_dict:
+                self.defaults_write_form_field(option, factor=factor, units=fl_units, defaults_dict=source_dict)
+            else:
+                self.defaults_write_form_field(option, factor=factor, units=fl_units)
+
+    def defaults_write_form_field(self, field, factor=None, units=None, defaults_dict=None):
+        """
+        Basically it is the worker in the self.defaults_write_form()
+
+        :param field: the GUI element in Preferences GUI to be updated
+        :param factor: factor to be applied to the field parameter
+        :param units: current FlatCAM measuring units
+        :param defaults_dict: the defaults storage
+        :return: None, it updates GUI elements
+        """
+
+        def_dict = self.defaults if defaults_dict is None else defaults_dict
+
+        try:
+            value = def_dict[field]
+            log.debug("value is " + str(value) + " and factor is "+str(factor))
+            if factor is not None:
+                value *= factor
+
+            form_field = self.defaults_form_fields[field]
+            if units is None:
+                form_field.set_value(value)
+            elif (units == 'IN' or units == 'MM') and (field == 'global_gridx' or field == 'global_gridy'):
+                form_field.set_value(value)
+
+        except KeyError:
+            pass
+        except AttributeError:
+            log.debug(field)
+
+    def show_preferences_gui(self):
+        """
+        Called to initialize and show the Preferences GUI
+
+        :return: None
+        """
+
+        gen_form = self.ui.general_defaults_form
+        try:
+            self.ui.general_scroll_area.takeWidget()
+        except Exception:
+            log.debug("Nothing to remove")
+        self.ui.general_scroll_area.setWidget(gen_form)
+        gen_form.show()
+
+        ger_form = self.ui.gerber_defaults_form
+        try:
+            self.ui.gerber_scroll_area.takeWidget()
+        except Exception:
+            log.debug("Nothing to remove")
+        self.ui.gerber_scroll_area.setWidget(ger_form)
+        ger_form.show()
+
+        exc_form = self.ui.excellon_defaults_form
+        try:
+            self.ui.excellon_scroll_area.takeWidget()
+        except Exception:
+            log.debug("Nothing to remove")
+        self.ui.excellon_scroll_area.setWidget(exc_form)
+        exc_form.show()
+
+        geo_form = self.ui.geometry_defaults_form
+        try:
+            self.ui.geometry_scroll_area.takeWidget()
+        except Exception:
+            log.debug("Nothing to remove")
+        self.ui.geometry_scroll_area.setWidget(geo_form)
+        geo_form.show()
+
+        cnc_form = self.ui.cncjob_defaults_form
+        try:
+            self.ui.cncjob_scroll_area.takeWidget()
+        except Exception:
+            log.debug("Nothing to remove")
+        self.ui.cncjob_scroll_area.setWidget(cnc_form)
+        cnc_form.show()
+
+        tools_form = self.ui.tools_defaults_form
+        try:
+            self.ui.tools_scroll_area.takeWidget()
+        except Exception:
+            log.debug("Nothing to remove")
+        self.ui.tools_scroll_area.setWidget(tools_form)
+        tools_form.show()
+
+        tools2_form = self.ui.tools2_defaults_form
+        try:
+            self.ui.tools2_scroll_area.takeWidget()
+        except Exception:
+            log.debug("Nothing to remove")
+        self.ui.tools2_scroll_area.setWidget(tools2_form)
+        tools2_form.show()
+
+        fa_form = self.ui.util_defaults_form
+        try:
+            self.ui.fa_scroll_area.takeWidget()
+        except Exception:
+            log.debug("Nothing to remove")
+        self.ui.fa_scroll_area.setWidget(fa_form)
+        fa_form.show()
+
+        # Initialize the color box's color in Preferences -> Global -> Colo
+        self.__init_color_pickers()
+
+        # Button handlers
+        self.ui.pref_save_button.clicked.connect(lambda: self.on_save_button(save_to_file=True))
+        self.ui.pref_apply_button.clicked.connect(lambda: self.on_save_button(save_to_file=False))
+        self.ui.pref_close_button.clicked.connect(self.on_pref_close_button)
+        self.ui.pref_defaults_button.clicked.connect(self.on_restore_defaults_preferences)
+
+        log.debug("Finished Preferences GUI form initialization.")
+
+    def __init_color_pickers(self):
+        # Init Gerber Plot Colors
+        self.ui.gerber_defaults_form.gerber_gen_group.pf_color_entry.set_value(self.defaults['gerber_plot_fill'])
+        self.ui.gerber_defaults_form.gerber_gen_group.pf_color_button.setStyleSheet(
+            "background-color:%s;"
+            "border-color: dimgray" % str(self.defaults['gerber_plot_fill'])[:7])
+        self.ui.gerber_defaults_form.gerber_gen_group.pf_color_alpha_spinner.set_value(
+            int(self.defaults['gerber_plot_fill'][7:9], 16))
+        self.ui.gerber_defaults_form.gerber_gen_group.pf_color_alpha_slider.setValue(
+            int(self.defaults['gerber_plot_fill'][7:9], 16))
+
+        self.ui.gerber_defaults_form.gerber_gen_group.pl_color_entry.set_value(self.defaults['gerber_plot_line'])
+        self.ui.gerber_defaults_form.gerber_gen_group.pl_color_button.setStyleSheet(
+            "background-color:%s;"
+            "border-color: dimgray" % str(self.defaults['gerber_plot_line'])[:7])
+
+        # Init Excellon Plot Colors
+        self.ui.excellon_defaults_form.excellon_gen_group.fill_color_entry.set_value(
+            self.defaults['excellon_plot_fill'])
+        self.ui.excellon_defaults_form.excellon_gen_group.fill_color_button.setStyleSheet(
+            "background-color:%s;"
+            "border-color: dimgray" % str(self.defaults['excellon_plot_fill'])[:7])
+        self.ui.excellon_defaults_form.excellon_gen_group.color_alpha_spinner.set_value(
+            int(self.defaults['excellon_plot_fill'][7:9], 16))
+        self.ui.excellon_defaults_form.excellon_gen_group.color_alpha_slider.setValue(
+            int(self.defaults['excellon_plot_fill'][7:9], 16))
+
+        self.ui.excellon_defaults_form.excellon_gen_group.line_color_entry.set_value(
+            self.defaults['excellon_plot_line'])
+        self.ui.excellon_defaults_form.excellon_gen_group.line_color_button.setStyleSheet(
+            "background-color:%s;"
+            "border-color: dimgray" % str(self.defaults['excellon_plot_line'])[:7])
+
+        # Init Geometry Plot Colors
+        self.ui.geometry_defaults_form.geometry_gen_group.line_color_entry.set_value(
+            self.defaults['geometry_plot_line'])
+        self.ui.geometry_defaults_form.geometry_gen_group.line_color_button.setStyleSheet(
+            "background-color:%s;"
+            "border-color: dimgray" % str(self.defaults['geometry_plot_line'])[:7])
+
+        # Init CNCJob Travel Line Colors
+        self.ui.cncjob_defaults_form.cncjob_gen_group.tfill_color_entry.set_value(
+            self.defaults['cncjob_travel_fill'])
+        self.ui.cncjob_defaults_form.cncjob_gen_group.tfill_color_button.setStyleSheet(
+            "background-color:%s;"
+            "border-color: dimgray" % str(self.defaults['cncjob_travel_fill'])[:7])
+        self.ui.cncjob_defaults_form.cncjob_gen_group.tcolor_alpha_spinner.set_value(
+            int(self.defaults['cncjob_travel_fill'][7:9], 16))
+        self.ui.cncjob_defaults_form.cncjob_gen_group.tcolor_alpha_slider.setValue(
+            int(self.defaults['cncjob_travel_fill'][7:9], 16))
+
+        self.ui.cncjob_defaults_form.cncjob_gen_group.tline_color_entry.set_value(
+            self.defaults['cncjob_travel_line'])
+        self.ui.cncjob_defaults_form.cncjob_gen_group.tline_color_button.setStyleSheet(
+            "background-color:%s;"
+            "border-color: dimgray" % str(self.defaults['cncjob_travel_line'])[:7])
+
+        # Init CNCJob Plot Colors
+        self.ui.cncjob_defaults_form.cncjob_gen_group.fill_color_entry.set_value(
+            self.defaults['cncjob_plot_fill'])
+        self.ui.cncjob_defaults_form.cncjob_gen_group.fill_color_button.setStyleSheet(
+            "background-color:%s;"
+            "border-color: dimgray" % str(self.defaults['cncjob_plot_fill'])[:7])
+
+        self.ui.cncjob_defaults_form.cncjob_gen_group.line_color_entry.set_value(
+            self.defaults['cncjob_plot_line'])
+        self.ui.cncjob_defaults_form.cncjob_gen_group.line_color_button.setStyleSheet(
+            "background-color:%s;"
+            "border-color: dimgray" % str(self.defaults['cncjob_plot_line'])[:7])
+
+        # Init Left-Right Selection colors
+        self.ui.general_defaults_form.general_gui_group.sf_color_entry.set_value(self.defaults['global_sel_fill'])
+        self.ui.general_defaults_form.general_gui_group.sf_color_button.setStyleSheet(
+            "background-color:%s;"
+            "border-color: dimgray" % str(self.defaults['global_sel_fill'])[:7])
+        self.ui.general_defaults_form.general_gui_group.sf_color_alpha_spinner.set_value(
+            int(self.defaults['global_sel_fill'][7:9], 16))
+        self.ui.general_defaults_form.general_gui_group.sf_color_alpha_slider.setValue(
+            int(self.defaults['global_sel_fill'][7:9], 16))
+
+        self.ui.general_defaults_form.general_gui_group.sl_color_entry.set_value(self.defaults['global_sel_line'])
+        self.ui.general_defaults_form.general_gui_group.sl_color_button.setStyleSheet(
+            "background-color:%s;"
+            "border-color: dimgray" % str(self.defaults['global_sel_line'])[:7])
+
+        # Init Right-Left Selection colors
+        self.ui.general_defaults_form.general_gui_group.alt_sf_color_entry.set_value(
+            self.defaults['global_alt_sel_fill'])
+        self.ui.general_defaults_form.general_gui_group.alt_sf_color_button.setStyleSheet(
+            "background-color:%s;"
+            "border-color: dimgray" % str(self.defaults['global_alt_sel_fill'])[:7])
+        self.ui.general_defaults_form.general_gui_group.alt_sf_color_alpha_spinner.set_value(
+            int(self.defaults['global_sel_fill'][7:9], 16))
+        self.ui.general_defaults_form.general_gui_group.alt_sf_color_alpha_slider.setValue(
+            int(self.defaults['global_sel_fill'][7:9], 16))
+
+        self.ui.general_defaults_form.general_gui_group.alt_sl_color_entry.set_value(
+            self.defaults['global_alt_sel_line'])
+        self.ui.general_defaults_form.general_gui_group.alt_sl_color_button.setStyleSheet(
+            "background-color:%s;"
+            "border-color: dimgray" % str(self.defaults['global_alt_sel_line'])[:7])
+
+        # Init Draw color and Selection Draw Color
+        self.ui.general_defaults_form.general_gui_group.draw_color_entry.set_value(
+            self.defaults['global_draw_color'])
+        self.ui.general_defaults_form.general_gui_group.draw_color_button.setStyleSheet(
+            "background-color:%s;"
+            "border-color: dimgray" % str(self.defaults['global_draw_color'])[:7])
+
+        self.ui.general_defaults_form.general_gui_group.sel_draw_color_entry.set_value(
+            self.defaults['global_sel_draw_color'])
+        self.ui.general_defaults_form.general_gui_group.sel_draw_color_button.setStyleSheet(
+            "background-color:%s;"
+            "border-color: dimgray" % str(self.defaults['global_sel_draw_color'])[:7])
+
+        # Init Project Items color
+        self.ui.general_defaults_form.general_gui_group.proj_color_entry.set_value(
+            self.defaults['global_proj_item_color'])
+        self.ui.general_defaults_form.general_gui_group.proj_color_button.setStyleSheet(
+            "background-color:%s;"
+            "border-color: dimgray" % str(self.defaults['global_proj_item_color'])[:7])
+
+        # Init Project Disabled Items color
+        self.ui.general_defaults_form.general_gui_group.proj_color_dis_entry.set_value(
+            self.defaults['global_proj_item_dis_color'])
+        self.ui.general_defaults_form.general_gui_group.proj_color_dis_button.setStyleSheet(
+            "background-color:%s;"
+            "border-color: dimgray" % str(self.defaults['global_proj_item_dis_color'])[:7])
+
+        # Init Project Disabled Items color
+        self.ui.general_defaults_form.general_app_set_group.mouse_cursor_entry.set_value(
+            self.defaults['global_cursor_color'])
+        self.ui.general_defaults_form.general_app_set_group.mouse_cursor_button.setStyleSheet(
+            "background-color:%s;"
+            "border-color: dimgray" % str(self.defaults['global_cursor_color'])[:7])
+
+        # Init the Annotation CNC Job color
+        self.ui.cncjob_defaults_form.cncjob_adv_opt_group.annotation_fontcolor_entry.set_value(
+            self.defaults['cncjob_annotation_fontcolor'])
+        self.ui.cncjob_defaults_form.cncjob_adv_opt_group.annotation_fontcolor_button.setStyleSheet(
+            "background-color:%s;"
+            "border-color: dimgray" % str(self.defaults['cncjob_annotation_fontcolor'])[:7])
+
+        # Init the Tool Film color
+        self.ui.tools_defaults_form.tools_film_group.film_color_entry.set_value(
+            self.defaults['tools_film_color'])
+        self.ui.tools_defaults_form.tools_film_group.film_color_button.setStyleSheet(
+            "background-color:%s;"
+            "border-color: dimgray" % str(self.defaults['tools_film_color'])[:7]
+        )
+
+        # Init the Tool QRCode colors
+        self.ui.tools2_defaults_form.tools2_qrcode_group.fill_color_entry.set_value(
+            self.defaults['tools_qrcode_fill_color'])
+        self.ui.tools2_defaults_form.tools2_qrcode_group.fill_color_button.setStyleSheet(
+            "background-color:%s;"
+            "border-color: dimgray" % str(self.defaults['tools_qrcode_fill_color'])[:7])
+
+        self.ui.tools2_defaults_form.tools2_qrcode_group.back_color_entry.set_value(
+            self.defaults['tools_qrcode_back_color'])
+        self.ui.tools2_defaults_form.tools2_qrcode_group.back_color_button.setStyleSheet(
+            "background-color:%s;"
+            "border-color: dimgray" % str(self.defaults['tools_qrcode_back_color'])[:7])
+
+    def on_save_button(self, save_to_file=True):
+        log.debug("on_save_button() --> Applying preferences to file.")
+
+        # Preferences saved, update flag
+        self.preferences_changed_flag = False
+
+        # Preferences save, update the color of the Preferences Tab text
+        for idx in range(self.ui.plot_tab_area.count()):
+            if self.ui.plot_tab_area.tabText(idx) == _("Preferences"):
+                self.ui.plot_tab_area.tabBar.setTabTextColor(idx, QtGui.QColor('black'))
+
+        # restore the default stylesheet by setting a blank one
+        self.ui.pref_apply_button.setStyleSheet("")
+
+        self.inform.emit('%s' % _("Preferences applied."))
+
+        # make sure we update the self.current_defaults dict used to undo changes to self.defaults
+        self.defaults.current_defaults.update(self.defaults)
+
+        if save_to_file:
+            self.save_defaults(silent=False)
+            # load the defaults so they are updated into the app
+            self.defaults.load(filename=os.path.join(self.data_path, 'current_defaults.FlatConfig'))
+
+        # Re-fresh project options
+        self.ui.app.on_options_app2project()
+
+        settgs = QSettings("Open Source", "FlatCAM")
+
+        # save the notebook font size
+        fsize = self.ui.general_defaults_form.general_app_set_group.notebook_font_size_spinner.get_value()
+        settgs.setValue('notebook_font_size', fsize)
+
+        # save the axis font size
+        g_fsize = self.ui.general_defaults_form.general_app_set_group.axis_font_size_spinner.get_value()
+        settgs.setValue('axis_font_size', g_fsize)
+
+        # save the textbox font size
+        tb_fsize = self.ui.general_defaults_form.general_app_set_group.textbox_font_size_spinner.get_value()
+        settgs.setValue('textbox_font_size', tb_fsize)
+
+        settgs.setValue(
+            'machinist',
+            1 if self.ui.general_defaults_form.general_app_set_group.machinist_cb.get_value() else 0
+        )
+
+        # This will write the setting to the platform specific storage.
+        del settgs
+
+        if save_to_file:
+            # close the tab and delete it
+            for idx in range(self.ui.plot_tab_area.count()):
+                if self.ui.plot_tab_area.tabText(idx) == _("Preferences"):
+                    self.ui.plot_tab_area.tabBar.setTabTextColor(idx, QtGui.QColor('black'))
+                    self.ui.plot_tab_area.closeTab(idx)
+                    break
+
+    def on_pref_close_button(self):
+        # Preferences saved, update flag
+        self.preferences_changed_flag = False
+        self.ignore_tab_close_event = True
+
+        try:
+            self.ui.general_defaults_form.general_app_group.units_radio.activated_custom.disconnect()
+        except (TypeError, AttributeError):
+            pass
+        self.defaults_write_form(source_dict=self.defaults.current_defaults)
+        self.ui.general_defaults_form.general_app_group.units_radio.activated_custom.connect(
+            lambda: self.ui.app.on_toggle_units(no_pref=False))
+        self.defaults.update(self.defaults.current_defaults)
+
+        # Preferences save, update the color of the Preferences Tab text
+        for idx in range(self.ui.plot_tab_area.count()):
+            if self.ui.plot_tab_area.tabText(idx) == _("Preferences"):
+                self.ui.plot_tab_area.tabBar.setTabTextColor(idx, QtGui.QColor('black'))
+                self.ui.plot_tab_area.closeTab(idx)
+                break
+
+        self.inform.emit('%s' % _("Preferences closed without saving."))
+        self.ignore_tab_close_event = False
+
+    def on_restore_defaults_preferences(self):
+        """
+        Loads the application's factory default settings into ``self.defaults``.
+
+        :return: None
+        """
+        log.debug("on_restore_defaults_preferences()")
+        self.defaults.reset_to_factory_defaults()
+        self.on_preferences_edited()
+        self.inform.emit('[success] %s' % _("Preferences default values are restored."))
+
+    def save_defaults(self, silent=False, data_path=None, first_time=False):
+        """
+        Saves application default options
+        ``self.defaults`` to current_defaults.FlatConfig file.
+        Save the toolbars visibility status to the preferences file (current_defaults.FlatConfig) to be
+        used at the next launch of the application.
+
+        :param silent:      Whether to display a message in status bar or not; boolean
+        :param data_path:   The path where to save the preferences file (current_defaults.FlatConfig)
+        When the application is portable it should be a mobile location.
+        :param first_time:  Boolean. If True will execute some code when the app is run first time
+        :return:            None
+        """
+        self.defaults.report_usage("save_defaults")
+
+        if data_path is None:
+            data_path = self.data_path
+
+        self.defaults.propagate_defaults()
+
+        if first_time is False:
+            self.save_toolbar_view()
+
+        # Save the options to disk
+        filename = os.path.join(data_path, "current_defaults.FlatConfig")
+        try:
+            self.defaults.write(filename=filename)
+        except Exception as e:
+            log.error("save_defaults() --> Failed to write defaults to file %s" % str(e))
+            self.inform.emit('[ERROR_NOTCL] %s %s' % (_("Failed to write defaults to file."), str(filename)))
+            return
+
+        if not silent:
+            self.inform.emit('[success] %s' % _("Preferences saved."))
+
+        # update the autosave timer
+        self.ui.app.save_project_auto_update()
+
+    def save_toolbar_view(self):
+        """
+        Will save the toolbar view state to the defaults
+
+        :return:            None
+        """
+
+        # Save the toolbar view
+        tb_status = 0
+        if self.ui.toolbarfile.isVisible():
+            tb_status += 1
+
+        if self.ui.toolbargeo.isVisible():
+            tb_status += 2
+
+        if self.ui.toolbarview.isVisible():
+            tb_status += 4
+
+        if self.ui.toolbartools.isVisible():
+            tb_status += 8
+
+        if self.ui.exc_edit_toolbar.isVisible():
+            tb_status += 16
+
+        if self.ui.geo_edit_toolbar.isVisible():
+            tb_status += 32
+
+        if self.ui.grb_edit_toolbar.isVisible():
+            tb_status += 64
+
+        if self.ui.snap_toolbar.isVisible():
+            tb_status += 128
+
+        if self.ui.toolbarshell.isVisible():
+            tb_status += 256
+
+        self.defaults["global_toolbar_view"] = tb_status
+
+    def on_preferences_edited(self):
+        """
+        Executed when a preference was changed in the Edit -> Preferences tab.
+        Will color the Preferences tab text to Red color.
+        :return:
+        """
+        if self.preferences_changed_flag is False:
+            self.inform.emit('[WARNING_NOTCL] %s' % _("Preferences edited but not saved."))
+
+            for idx in range(self.ui.plot_tab_area.count()):
+                if self.ui.plot_tab_area.tabText(idx) == _("Preferences"):
+                    self.ui.plot_tab_area.tabBar.setTabTextColor(idx, QtGui.QColor('red'))
+
+            self.ui.pref_apply_button.setStyleSheet("QPushButton {color: red;}")
+
+            self.preferences_changed_flag = True
+
+    def on_close_preferences_tab(self):
+        if self.ignore_tab_close_event:
+            return
+
+        # disconnect
+        for idx in range(self.ui.pref_tab_area.count()):
+            for tb in self.ui.pref_tab_area.widget(idx).findChildren(QtCore.QObject):
+                try:
+                    tb.textEdited.disconnect(self.on_preferences_edited)
+                except (TypeError, AttributeError):
+                    pass
+
+                try:
+                    tb.modificationChanged.disconnect(self.on_preferences_edited)
+                except (TypeError, AttributeError):
+                    pass
+
+                try:
+                    tb.toggled.disconnect(self.on_preferences_edited)
+                except (TypeError, AttributeError):
+                    pass
+
+                try:
+                    tb.valueChanged.disconnect(self.on_preferences_edited)
+                except (TypeError, AttributeError):
+                    pass
+
+                try:
+                    tb.currentIndexChanged.disconnect(self.on_preferences_edited)
+                except (TypeError, AttributeError):
+                    pass
+
+        # Prompt user to save
+        if self.preferences_changed_flag is True:
+            msgbox = QtWidgets.QMessageBox()
+            msgbox.setText(_("One or more values are changed.\n"
+                             "Do you want to save the Preferences?"))
+            msgbox.setWindowTitle(_("Save Preferences"))
+            msgbox.setWindowIcon(QtGui.QIcon(self.ui.app.resource_location + '/save_as.png'))
+
+            bt_yes = msgbox.addButton(_('Yes'), QtWidgets.QMessageBox.YesRole)
+            msgbox.addButton(_('No'), QtWidgets.QMessageBox.NoRole)
+
+            msgbox.setDefaultButton(bt_yes)
+            msgbox.exec_()
+            response = msgbox.clickedButton()
+
+            if response == bt_yes:
+                self.on_save_button(save_to_file=True)
+                self.inform.emit('[success] %s' % _("Preferences saved."))
+            else:
+                self.preferences_changed_flag = False
+                self.inform.emit('')
+                return

+ 15 - 0
flatcamGUI/preferences/__init__.py

@@ -0,0 +1,15 @@
+from flatcamGUI.GUIElements import *
+import gettext
+import FlatCAMTranslation as fcTranslate
+import builtins
+
+
+fcTranslate.apply_language('strings')
+if '_' not in builtins.__dict__:
+    _ = gettext.gettext
+
+settings = QSettings("Open Source", "FlatCAM")
+if settings.contains("machinist"):
+    machinist_setting = settings.value('machinist', type=int)
+else:
+    machinist_setting = 0

+ 208 - 0
flatcamGUI/preferences/cncjob/CNCJobAdvOptPrefGroupUI.py

@@ -0,0 +1,208 @@
+from PyQt5 import QtWidgets, QtGui, QtCore
+from PyQt5.QtCore import QSettings, Qt
+
+from flatcamGUI.GUIElements import FCTextArea, FCCheckBox, FCComboBox, FCSpinner, FCEntry
+from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
+import gettext
+import FlatCAMTranslation as fcTranslate
+import builtins
+
+fcTranslate.apply_language('strings')
+if '_' not in builtins.__dict__:
+    _ = gettext.gettext
+
+settings = QSettings("Open Source", "FlatCAM")
+if settings.contains("machinist"):
+    machinist_setting = settings.value('machinist', type=int)
+else:
+    machinist_setting = 0
+
+
+class CNCJobAdvOptPrefGroupUI(OptionsGroupUI):
+    def __init__(self, decimals=4, parent=None):
+        # OptionsGroupUI.__init__(self, "CNC Job Advanced Options Preferences", parent=None)
+        super(CNCJobAdvOptPrefGroupUI, self).__init__(self, parent=parent)
+        self.decimals = decimals
+
+        self.setTitle(str(_("CNC Job Adv. Options")))
+
+        # ## Export G-Code
+        self.export_gcode_label = QtWidgets.QLabel("<b>%s:</b>" % _("Export CNC Code"))
+        self.export_gcode_label.setToolTip(
+            _("Export and save G-Code to\n"
+              "make this object to a file.")
+        )
+        self.layout.addWidget(self.export_gcode_label)
+
+        # Prepend to G-Code
+        toolchangelabel = QtWidgets.QLabel('%s' % _('Toolchange G-Code'))
+        toolchangelabel.setToolTip(
+            _(
+                "Type here any G-Code commands you would\n"
+                "like to be executed when Toolchange event is encountered.\n"
+                "This will constitute a Custom Toolchange GCode,\n"
+                "or a Toolchange Macro.\n"
+                "The FlatCAM variables are surrounded by '%' symbol.\n\n"
+                "WARNING: it can be used only with a preprocessor file\n"
+                "that has 'toolchange_custom' in it's name and this is built\n"
+                "having as template the 'Toolchange Custom' posprocessor file."
+            )
+        )
+        self.layout.addWidget(toolchangelabel)
+
+        qsettings = QSettings("Open Source", "FlatCAM")
+        if qsettings.contains("textbox_font_size"):
+            tb_fsize = qsettings.value('textbox_font_size', type=int)
+        else:
+            tb_fsize = 10
+        font = QtGui.QFont()
+        font.setPointSize(tb_fsize)
+
+        self.toolchange_text = FCTextArea()
+        self.toolchange_text.setPlaceholderText(
+            _(
+                "Type here any G-Code commands you would "
+                "like to be executed when Toolchange event is encountered.\n"
+                "This will constitute a Custom Toolchange GCode, "
+                "or a Toolchange Macro.\n"
+                "The FlatCAM variables are surrounded by '%' symbol.\n"
+                "WARNING: it can be used only with a preprocessor file "
+                "that has 'toolchange_custom' in it's name."
+            )
+        )
+        self.layout.addWidget(self.toolchange_text)
+        self.toolchange_text.setFont(font)
+
+        hlay = QtWidgets.QHBoxLayout()
+        self.layout.addLayout(hlay)
+
+        # Toolchange Replacement GCode
+        self.toolchange_cb = FCCheckBox(label='%s' % _('Use Toolchange Macro'))
+        self.toolchange_cb.setToolTip(
+            _("Check this box if you want to use\n"
+              "a Custom Toolchange GCode (macro).")
+        )
+        hlay.addWidget(self.toolchange_cb)
+        hlay.addStretch()
+
+        hlay1 = QtWidgets.QHBoxLayout()
+        self.layout.addLayout(hlay1)
+
+        # Variable list
+        self.tc_variable_combo = FCComboBox()
+        self.tc_variable_combo.setToolTip(
+            _("A list of the FlatCAM variables that can be used\n"
+              "in the Toolchange event.\n"
+              "They have to be surrounded by the '%' symbol")
+        )
+        hlay1.addWidget(self.tc_variable_combo)
+
+        # Populate the Combo Box
+        variables = [_('Parameters'), 'tool', 'tooldia', 't_drills', 'x_toolchange', 'y_toolchange', 'z_toolchange',
+                     'z_cut', 'z_move', 'z_depthpercut', 'spindlespeed', 'dwelltime']
+        self.tc_variable_combo.addItems(variables)
+        self.tc_variable_combo.insertSeparator(1)
+
+        self.tc_variable_combo.setItemData(0, _("FlatCAM CNC parameters"), Qt.ToolTipRole)
+        fnt = QtGui.QFont()
+        fnt.setBold(True)
+        self.tc_variable_combo.setItemData(0, fnt, Qt.FontRole)
+
+        self.tc_variable_combo.setItemData(2, 'tool = %s' % _("tool number"), Qt.ToolTipRole)
+        self.tc_variable_combo.setItemData(3, 'tooldia = %s' % _("tool diameter"), Qt.ToolTipRole)
+        self.tc_variable_combo.setItemData(4, 't_drills = %s' % _("for Excellon, total number of drills"),
+                                           Qt.ToolTipRole)
+        self.tc_variable_combo.setItemData(5, 'x_toolchange = %s' % _("X coord for Toolchange"), Qt.ToolTipRole)
+        self.tc_variable_combo.setItemData(6, 'y_toolchange = %s' % _("Y coord for Toolchange"),
+                                           Qt.ToolTipRole)
+        self.tc_variable_combo.setItemData(7, 'z_toolchange = %s' % _("Z coord for Toolchange"), Qt.ToolTipRole)
+        self.tc_variable_combo.setItemData(8, 'z_cut = %s' % _("Z depth for the cut"), Qt.ToolTipRole)
+        self.tc_variable_combo.setItemData(9, 'z_move = %s' % _("Z height for travel"), Qt.ToolTipRole)
+        self.tc_variable_combo.setItemData(10, 'z_depthpercut = %s' % _("the step value for multidepth cut"),
+                                           Qt.ToolTipRole)
+        self.tc_variable_combo.setItemData(11, 'spindlesspeed = %s' % _("the value for the spindle speed"),
+                                           Qt.ToolTipRole)
+        self.tc_variable_combo.setItemData(12,
+                                           _("dwelltime = time to dwell to allow the spindle to reach it's set RPM"),
+                                           Qt.ToolTipRole)
+
+        # hlay1.addStretch()
+
+        # Insert Variable into the Toolchange G-Code Text Box
+        # self.tc_insert_buton = FCButton("Insert")
+        # self.tc_insert_buton.setToolTip(
+        #     "Insert the variable in the GCode Box\n"
+        #     "surrounded by the '%' symbol."
+        # )
+        # hlay1.addWidget(self.tc_insert_buton)
+
+        grid0 = QtWidgets.QGridLayout()
+        self.layout.addLayout(grid0)
+
+        grid0.addWidget(QtWidgets.QLabel(''), 1, 0, 1, 2)
+
+        # Annotation Font Size
+        self.annotation_fontsize_label = QtWidgets.QLabel('%s:' % _("Annotation Size"))
+        self.annotation_fontsize_label.setToolTip(
+            _("The font size of the annotation text. In pixels.")
+        )
+        grid0.addWidget(self.annotation_fontsize_label, 2, 0)
+        self.annotation_fontsize_sp = FCSpinner()
+        self.annotation_fontsize_sp.set_range(0, 9999)
+
+        grid0.addWidget(self.annotation_fontsize_sp, 2, 1)
+        grid0.addWidget(QtWidgets.QLabel(''), 2, 2)
+
+        # Annotation Font Color
+        self.annotation_color_label = QtWidgets.QLabel('%s:' % _('Annotation Color'))
+        self.annotation_color_label.setToolTip(
+            _("Set the font color for the annotation texts.")
+        )
+        self.annotation_fontcolor_entry = FCEntry()
+        self.annotation_fontcolor_button = QtWidgets.QPushButton()
+        self.annotation_fontcolor_button.setFixedSize(15, 15)
+
+        self.form_box_child = QtWidgets.QHBoxLayout()
+        self.form_box_child.setContentsMargins(0, 0, 0, 0)
+        self.form_box_child.addWidget(self.annotation_fontcolor_entry)
+        self.form_box_child.addWidget(self.annotation_fontcolor_button, alignment=Qt.AlignRight)
+        self.form_box_child.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
+
+        color_widget = QtWidgets.QWidget()
+        color_widget.setLayout(self.form_box_child)
+        grid0.addWidget(self.annotation_color_label, 3, 0)
+        grid0.addWidget(color_widget, 3, 1)
+        grid0.addWidget(QtWidgets.QLabel(''), 3, 2)
+
+        self.layout.addStretch()
+
+        self.tc_variable_combo.currentIndexChanged[str].connect(self.on_cnc_custom_parameters)
+
+        self.annotation_fontcolor_entry.editingFinished.connect(self.on_annotation_fontcolor_entry)
+        self.annotation_fontcolor_button.clicked.connect(self.on_annotation_fontcolor_button)
+
+    def on_cnc_custom_parameters(self, signal_text):
+        if signal_text == 'Parameters':
+            return
+        else:
+            self.toolchange_text.insertPlainText('%%%s%%' % signal_text)
+
+    def on_annotation_fontcolor_entry(self):
+        self.app.defaults['cncjob_annotation_fontcolor'] = self.annotation_fontcolor_entry.get_value()
+        self.annotation_fontcolor_button.setStyleSheet(
+            "background-color:%s" % str(self.app.defaults['cncjob_annotation_fontcolor']))
+
+    def on_annotation_fontcolor_button(self):
+        current_color = QtGui.QColor(self.app.defaults['cncjob_annotation_fontcolor'])
+
+        c_dialog = QtWidgets.QColorDialog()
+        annotation_color = c_dialog.getColor(initial=current_color)
+
+        if annotation_color.isValid() is False:
+            return
+
+        self.annotation_fontcolor_button.setStyleSheet("background-color:%s" % str(annotation_color.name()))
+
+        new_val_sel = str(annotation_color.name())
+        self.annotation_fontcolor_entry.set_value(new_val_sel)
+        self.app.defaults['cncjob_annotation_fontcolor'] = new_val_sel

+ 389 - 0
flatcamGUI/preferences/cncjob/CNCJobGenPrefGroupUI.py

@@ -0,0 +1,389 @@
+from PyQt5 import QtWidgets, QtCore, QtGui
+from PyQt5.QtCore import QSettings
+
+from flatcamGUI.GUIElements import FCCheckBox, RadioSet, FCSpinner, FCDoubleSpinner, FCEntry
+from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
+import gettext
+import FlatCAMTranslation as fcTranslate
+import builtins
+
+fcTranslate.apply_language('strings')
+if '_' not in builtins.__dict__:
+    _ = gettext.gettext
+
+settings = QSettings("Open Source", "FlatCAM")
+if settings.contains("machinist"):
+    machinist_setting = settings.value('machinist', type=int)
+else:
+    machinist_setting = 0
+
+
+class CNCJobGenPrefGroupUI(OptionsGroupUI):
+    def __init__(self, decimals=4, parent=None):
+        # OptionsGroupUI.__init__(self, "CNC Job General Preferences", parent=None)
+        super(CNCJobGenPrefGroupUI, self).__init__(self, parent=parent)
+
+        self.setTitle(str(_("CNC Job General")))
+        self.decimals = decimals
+
+        # ## Plot options
+        self.plot_options_label = QtWidgets.QLabel("<b>%s:</b>" % _("Plot Options"))
+        self.layout.addWidget(self.plot_options_label)
+
+        grid0 = QtWidgets.QGridLayout()
+        self.layout.addLayout(grid0)
+        grid0.setColumnStretch(0, 0)
+        grid0.setColumnStretch(1, 1)
+
+        # Plot CB
+        # self.plot_cb = QtWidgets.QCheckBox('Plot')
+        self.plot_cb = FCCheckBox(_('Plot Object'))
+        self.plot_cb.setToolTip(_("Plot (show) this object."))
+        grid0.addWidget(self.plot_cb, 0, 0, 1, 2)
+
+        # Plot Kind
+        self.cncplot_method_label = QtWidgets.QLabel('%s:' % _("Plot kind"))
+        self.cncplot_method_label.setToolTip(
+            _("This selects the kind of geometries on the canvas to plot.\n"
+              "Those can be either of type 'Travel' which means the moves\n"
+              "above the work piece or it can be of type 'Cut',\n"
+              "which means the moves that cut into the material.")
+        )
+
+        self.cncplot_method_radio = RadioSet([
+            {"label": _("All"), "value": "all"},
+            {"label": _("Travel"), "value": "travel"},
+            {"label": _("Cut"), "value": "cut"}
+        ], orientation='vertical')
+
+        grid0.addWidget(self.cncplot_method_label, 1, 0)
+        grid0.addWidget(self.cncplot_method_radio, 1, 1)
+        grid0.addWidget(QtWidgets.QLabel(''), 1, 2)
+
+        # Display Annotation
+        self.annotation_cb = FCCheckBox(_("Display Annotation"))
+        self.annotation_cb.setToolTip(
+            _("This selects if to display text annotation on the plot.\n"
+              "When checked it will display numbers in order for each end\n"
+              "of a travel line."
+              )
+        )
+
+        grid0.addWidget(self.annotation_cb, 2, 0, 1, 3)
+
+        # ###################################################################
+        # Number of circle steps for circular aperture linear approximation #
+        # ###################################################################
+        self.steps_per_circle_label = QtWidgets.QLabel('%s:' % _("Circle Steps"))
+        self.steps_per_circle_label.setToolTip(
+            _("The number of circle steps for <b>GCode</b> \n"
+              "circle and arc shapes linear approximation.")
+        )
+        grid0.addWidget(self.steps_per_circle_label, 3, 0)
+        self.steps_per_circle_entry = FCSpinner()
+        self.steps_per_circle_entry.set_range(0, 99999)
+        grid0.addWidget(self.steps_per_circle_entry, 3, 1)
+
+        # Tool dia for plot
+        tdlabel = QtWidgets.QLabel('%s:' % _('Travel dia'))
+        tdlabel.setToolTip(
+            _("The width of the travel lines to be\n"
+              "rendered in the plot.")
+        )
+        self.tooldia_entry = FCDoubleSpinner()
+        self.tooldia_entry.set_range(0, 99999)
+        self.tooldia_entry.set_precision(self.decimals)
+        self.tooldia_entry.setSingleStep(0.1)
+        self.tooldia_entry.setWrapping(True)
+
+        grid0.addWidget(tdlabel, 4, 0)
+        grid0.addWidget(self.tooldia_entry, 4, 1)
+
+        # add a space
+        grid0.addWidget(QtWidgets.QLabel('<b>%s:</b>' % _("G-code Decimals")), 5, 0, 1, 2)
+
+        # Number of decimals to use in GCODE coordinates
+        cdeclabel = QtWidgets.QLabel('%s:' % _('Coordinates'))
+        cdeclabel.setToolTip(
+            _("The number of decimals to be used for \n"
+              "the X, Y, Z coordinates in CNC code (GCODE, etc.)")
+        )
+        self.coords_dec_entry = FCSpinner()
+        self.coords_dec_entry.set_range(0, 9)
+        self.coords_dec_entry.setWrapping(True)
+
+        grid0.addWidget(cdeclabel, 6, 0)
+        grid0.addWidget(self.coords_dec_entry, 6, 1)
+
+        # Number of decimals to use in GCODE feedrate
+        frdeclabel = QtWidgets.QLabel('%s:' % _('Feedrate'))
+        frdeclabel.setToolTip(
+            _("The number of decimals to be used for \n"
+              "the Feedrate parameter in CNC code (GCODE, etc.)")
+        )
+        self.fr_dec_entry = FCSpinner()
+        self.fr_dec_entry.set_range(0, 9)
+        self.fr_dec_entry.setWrapping(True)
+
+        grid0.addWidget(frdeclabel, 7, 0)
+        grid0.addWidget(self.fr_dec_entry, 7, 1)
+
+        # The type of coordinates used in the Gcode: Absolute or Incremental
+        coords_type_label = QtWidgets.QLabel('%s:' % _('Coordinates type'))
+        coords_type_label.setToolTip(
+            _("The type of coordinates to be used in Gcode.\n"
+              "Can be:\n"
+              "- Absolute G90 -> the reference is the origin x=0, y=0\n"
+              "- Incremental G91 -> the reference is the previous position")
+        )
+        self.coords_type_radio = RadioSet([
+            {"label": _("Absolute G90"), "value": "G90"},
+            {"label": _("Incremental G91"), "value": "G91"}
+        ], orientation='vertical', stretch=False)
+        grid0.addWidget(coords_type_label, 8, 0)
+        grid0.addWidget(self.coords_type_radio, 8, 1)
+
+        # hidden for the time being, until implemented
+        coords_type_label.hide()
+        self.coords_type_radio.hide()
+
+        # Line Endings
+        self.line_ending_cb = FCCheckBox(_("Force Windows style line-ending"))
+        self.line_ending_cb.setToolTip(
+            _("When checked will force a Windows style line-ending\n"
+              "(\\r\\n) on non-Windows OS's.")
+        )
+
+        grid0.addWidget(self.line_ending_cb, 9, 0, 1, 3)
+
+        separator_line = QtWidgets.QFrame()
+        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
+        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
+        grid0.addWidget(separator_line, 12, 0, 1, 2)
+
+        # Travel Line Color
+        self.travel_color_label = QtWidgets.QLabel('<b>%s</b>' % _('Travel Line Color'))
+        grid0.addWidget(self.travel_color_label, 13, 0, 1, 2)
+
+        # Plot Line Color
+        self.tline_color_label = QtWidgets.QLabel('%s:' % _('Outline'))
+        self.tline_color_label.setToolTip(
+            _("Set the travel line color for plotted objects.")
+        )
+        self.tline_color_entry = FCEntry()
+        self.tline_color_button = QtWidgets.QPushButton()
+        self.tline_color_button.setFixedSize(15, 15)
+
+        self.form_box_child_2 = QtWidgets.QHBoxLayout()
+        self.form_box_child_2.addWidget(self.tline_color_entry)
+        self.form_box_child_2.addWidget(self.tline_color_button)
+        self.form_box_child_2.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
+
+        grid0.addWidget(self.tline_color_label, 14, 0)
+        grid0.addLayout(self.form_box_child_2, 14, 1)
+
+        # Plot Fill Color
+        self.tfill_color_label = QtWidgets.QLabel('%s:' % _('Fill'))
+        self.tfill_color_label.setToolTip(
+            _("Set the fill color for plotted objects.\n"
+              "First 6 digits are the color and the last 2\n"
+              "digits are for alpha (transparency) level.")
+        )
+        self.tfill_color_entry = FCEntry()
+        self.tfill_color_button = QtWidgets.QPushButton()
+        self.tfill_color_button.setFixedSize(15, 15)
+
+        self.form_box_child_1 = QtWidgets.QHBoxLayout()
+        self.form_box_child_1.addWidget(self.tfill_color_entry)
+        self.form_box_child_1.addWidget(self.tfill_color_button)
+        self.form_box_child_1.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
+
+        grid0.addWidget(self.tfill_color_label, 15, 0)
+        grid0.addLayout(self.form_box_child_1, 15, 1)
+
+        # Plot Fill Transparency Level
+        self.alpha_label = QtWidgets.QLabel('%s:' % _('Alpha'))
+        self.alpha_label.setToolTip(
+            _("Set the fill transparency for plotted objects.")
+        )
+        self.tcolor_alpha_slider = QtWidgets.QSlider(QtCore.Qt.Horizontal)
+        self.tcolor_alpha_slider.setMinimum(0)
+        self.tcolor_alpha_slider.setMaximum(255)
+        self.tcolor_alpha_slider.setSingleStep(1)
+
+        self.tcolor_alpha_spinner = FCSpinner()
+        self.tcolor_alpha_spinner.setMinimumWidth(70)
+        self.tcolor_alpha_spinner.set_range(0, 255)
+
+        self.form_box_child_3 = QtWidgets.QHBoxLayout()
+        self.form_box_child_3.addWidget(self.tcolor_alpha_slider)
+        self.form_box_child_3.addWidget(self.tcolor_alpha_spinner)
+
+        grid0.addWidget(self.alpha_label, 16, 0)
+        grid0.addLayout(self.form_box_child_3, 16, 1)
+
+        separator_line = QtWidgets.QFrame()
+        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
+        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
+        grid0.addWidget(separator_line, 17, 0, 1, 2)
+
+        # CNCJob Object Color
+        self.cnc_color_label = QtWidgets.QLabel('<b>%s</b>' % _('CNCJob Object Color'))
+        grid0.addWidget(self.cnc_color_label, 18, 0, 1, 2)
+
+        # Plot Line Color
+        self.line_color_label = QtWidgets.QLabel('%s:' % _('Outline'))
+        self.line_color_label.setToolTip(
+            _("Set the color for plotted objects.")
+        )
+        self.line_color_entry = FCEntry()
+        self.line_color_button = QtWidgets.QPushButton()
+        self.line_color_button.setFixedSize(15, 15)
+
+        self.form_box_child_2 = QtWidgets.QHBoxLayout()
+        self.form_box_child_2.addWidget(self.line_color_entry)
+        self.form_box_child_2.addWidget(self.line_color_button)
+        self.form_box_child_2.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
+
+        grid0.addWidget(self.line_color_label, 19, 0)
+        grid0.addLayout(self.form_box_child_2, 19, 1)
+
+        # Plot Fill Color
+        self.fill_color_label = QtWidgets.QLabel('%s:' % _('Fill'))
+        self.fill_color_label.setToolTip(
+            _("Set the fill color for plotted objects.\n"
+              "First 6 digits are the color and the last 2\n"
+              "digits are for alpha (transparency) level.")
+        )
+        self.fill_color_entry = FCEntry()
+        self.fill_color_button = QtWidgets.QPushButton()
+        self.fill_color_button.setFixedSize(15, 15)
+
+        self.form_box_child_1 = QtWidgets.QHBoxLayout()
+        self.form_box_child_1.addWidget(self.fill_color_entry)
+        self.form_box_child_1.addWidget(self.fill_color_button)
+        self.form_box_child_1.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
+
+        grid0.addWidget(self.fill_color_label, 20, 0)
+        grid0.addLayout(self.form_box_child_1, 20, 1)
+
+        self.layout.addStretch()
+
+        # Setting plot colors signals
+        self.tline_color_entry.editingFinished.connect(self.on_tline_color_entry)
+        self.tline_color_button.clicked.connect(self.on_tline_color_button)
+        self.tfill_color_entry.editingFinished.connect(self.on_tfill_color_entry)
+        self.tfill_color_button.clicked.connect(self.on_tfill_color_button)
+        self.tcolor_alpha_spinner.valueChanged.connect(self.on_tcolor_spinner)
+        self.tcolor_alpha_slider.valueChanged.connect(self.on_tcolor_slider)
+
+        self.line_color_entry.editingFinished.connect(self.on_line_color_entry)
+        self.line_color_button.clicked.connect(self.on_line_color_button)
+        self.fill_color_entry.editingFinished.connect(self.on_fill_color_entry)
+        self.fill_color_button.clicked.connect(self.on_fill_color_button)
+
+    # ------------------------------------------------------
+    # Setting travel colors handlers
+    # ------------------------------------------------------
+    def on_tfill_color_entry(self):
+        self.app.defaults['cncjob_travel_fill'] = self.tfill_color_entry.get_value()[:7] + \
+                                                  self.app.defaults['cncjob_travel_fill'][7:9]
+        self.tfill_color_button.setStyleSheet(
+            "background-color:%s" % str(self.app.defaults['cncjob_travel_fill'])[:7])
+
+    def on_tfill_color_button(self):
+        current_color = QtGui.QColor(self.app.defaults['cncjob_travel_fill'][:7])
+
+        c_dialog = QtWidgets.QColorDialog()
+        plot_fill_color = c_dialog.getColor(initial=current_color)
+
+        if plot_fill_color.isValid() is False:
+            return
+
+        self.tfill_color_button.setStyleSheet("background-color:%s" % str(plot_fill_color.name()))
+
+        new_val = str(plot_fill_color.name()) + str(self.app.defaults['cncjob_travel_fill'][7:9])
+        self.tfill_color_entry.set_value(new_val)
+        self.app.defaults['cncjob_travel_fill'] = new_val
+
+    def on_tcolor_spinner(self):
+        spinner_value = self.tcolor_alpha_spinner.value()
+        self.tcolor_alpha_slider.setValue(spinner_value)
+        self.app.defaults['cncjob_travel_fill'] = \
+            self.app.defaults['cncjob_travel_fill'][:7] + \
+            (hex(spinner_value)[2:] if int(hex(spinner_value)[2:], 16) > 0 else '00')
+        self.app.defaults['cncjob_travel_line'] = \
+            self.app.defaults['cncjob_travel_line'][:7] + \
+            (hex(spinner_value)[2:] if int(hex(spinner_value)[2:], 16) > 0 else '00')
+
+    def on_tcolor_slider(self):
+        slider_value = self.tcolor_alpha_slider.value()
+        self.tcolor_alpha_spinner.setValue(slider_value)
+
+    def on_tline_color_entry(self):
+        self.app.defaults['cncjob_travel_line'] = self.tline_color_entry.get_value()[:7] + \
+                                                  self.app.defaults['cncjob_travel_line'][7:9]
+        self.tline_color_button.setStyleSheet(
+            "background-color:%s" % str(self.app.defaults['cncjob_travel_line'])[:7])
+
+    def on_tline_color_button(self):
+        current_color = QtGui.QColor(self.app.defaults['cncjob_travel_line'][:7])
+        # print(current_color)
+
+        c_dialog = QtWidgets.QColorDialog()
+        plot_line_color = c_dialog.getColor(initial=current_color)
+
+        if plot_line_color.isValid() is False:
+            return
+
+        self.tline_color_button.setStyleSheet("background-color:%s" % str(plot_line_color.name()))
+
+        new_val_line = str(plot_line_color.name()) + str(self.app.defaults['cncjob_travel_line'][7:9])
+        self.tline_color_entry.set_value(new_val_line)
+        self.app.defaults['cncjob_travel_line'] = new_val_line
+
+    # ------------------------------------------------------
+    # Setting plot colors handlers
+    # ------------------------------------------------------
+    def on_fill_color_entry(self):
+        self.app.defaults['cncjob_plot_fill'] = self.fill_color_entry.get_value()[:7] + \
+                                                  self.app.defaults['cncjob_plot_fill'][7:9]
+        self.fill_color_button.setStyleSheet(
+            "background-color:%s" % str(self.app.defaults['cncjob_plot_fill'])[:7])
+
+    def on_fill_color_button(self):
+        current_color = QtGui.QColor(self.app.defaults['cncjob_plot_fill'][:7])
+
+        c_dialog = QtWidgets.QColorDialog()
+        plot_fill_color = c_dialog.getColor(initial=current_color)
+
+        if plot_fill_color.isValid() is False:
+            return
+
+        self.fill_color_button.setStyleSheet("background-color:%s" % str(plot_fill_color.name()))
+
+        new_val = str(plot_fill_color.name()) + str(self.app.defaults['cncjob_plot_fill'][7:9])
+        self.fill_color_entry.set_value(new_val)
+        self.app.defaults['cncjob_plot_fill'] = new_val
+
+    def on_line_color_entry(self):
+        self.app.defaults['cncjob_plot_line'] = self.line_color_entry.get_value()[:7] + \
+                                                  self.app.defaults['cncjob_plot_line'][7:9]
+        self.line_color_button.setStyleSheet(
+            "background-color:%s" % str(self.app.defaults['cncjob_plot_line'])[:7])
+
+    def on_line_color_button(self):
+        current_color = QtGui.QColor(self.app.defaults['cncjob_plot_line'][:7])
+        # print(current_color)
+
+        c_dialog = QtWidgets.QColorDialog()
+        plot_line_color = c_dialog.getColor(initial=current_color)
+
+        if plot_line_color.isValid() is False:
+            return
+
+        self.line_color_button.setStyleSheet("background-color:%s" % str(plot_line_color.name()))
+
+        new_val_line = str(plot_line_color.name()) + str(self.app.defaults['cncjob_plot_line'][7:9])
+        self.line_color_entry.set_value(new_val_line)
+        self.app.defaults['cncjob_plot_line'] = new_val_line

+ 80 - 0
flatcamGUI/preferences/cncjob/CNCJobOptPrefGroupUI.py

@@ -0,0 +1,80 @@
+from PyQt5 import QtWidgets, QtGui
+from PyQt5.QtCore import QSettings
+
+from flatcamGUI.GUIElements import FCTextArea
+from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
+
+import gettext
+import FlatCAMTranslation as fcTranslate
+import builtins
+
+fcTranslate.apply_language('strings')
+if '_' not in builtins.__dict__:
+    _ = gettext.gettext
+
+settings = QSettings("Open Source", "FlatCAM")
+if settings.contains("machinist"):
+    machinist_setting = settings.value('machinist', type=int)
+else:
+    machinist_setting = 0
+
+
+class CNCJobOptPrefGroupUI(OptionsGroupUI):
+    def __init__(self, decimals=4, parent=None):
+        # OptionsGroupUI.__init__(self, "CNC Job Options Preferences", parent=None)
+        super(CNCJobOptPrefGroupUI, self).__init__(self, parent=parent)
+
+        self.setTitle(str(_("CNC Job Options")))
+        self.decimals = decimals
+
+        # ## Export G-Code
+        self.export_gcode_label = QtWidgets.QLabel("<b>%s:</b>" % _("Export G-Code"))
+        self.export_gcode_label.setToolTip(
+            _("Export and save G-Code to\n"
+              "make this object to a file.")
+        )
+        self.layout.addWidget(self.export_gcode_label)
+
+        qsettings = QSettings("Open Source", "FlatCAM")
+        if qsettings.contains("textbox_font_size"):
+            tb_fsize = qsettings.value('textbox_font_size', type=int)
+        else:
+            tb_fsize = 10
+        font = QtGui.QFont()
+        font.setPointSize(tb_fsize)
+
+        # Prepend to G-Code
+        prependlabel = QtWidgets.QLabel('%s:' % _('Prepend to G-Code'))
+        prependlabel.setToolTip(
+            _("Type here any G-Code commands you would\n"
+              "like to add at the beginning of the G-Code file.")
+        )
+        self.layout.addWidget(prependlabel)
+
+        self.prepend_text = FCTextArea()
+        self.prepend_text.setPlaceholderText(
+            _("Type here any G-Code commands you would "
+              "like to add at the beginning of the G-Code file.")
+        )
+        self.layout.addWidget(self.prepend_text)
+        self.prepend_text.setFont(font)
+
+        # Append text to G-Code
+        appendlabel = QtWidgets.QLabel('%s:' % _('Append to G-Code'))
+        appendlabel.setToolTip(
+            _("Type here any G-Code commands you would\n"
+              "like to append to the generated file.\n"
+              "I.e.: M2 (End of program)")
+        )
+        self.layout.addWidget(appendlabel)
+
+        self.append_text = FCTextArea()
+        self.append_text.setPlaceholderText(
+            _("Type here any G-Code commands you would "
+              "like to append to the generated file.\n"
+              "I.e.: M2 (End of program)")
+        )
+        self.layout.addWidget(self.append_text)
+        self.append_text.setFont(font)
+
+        self.layout.addStretch()

+ 27 - 0
flatcamGUI/preferences/cncjob/CNCJobPreferencesUI.py

@@ -0,0 +1,27 @@
+from PyQt5 import QtWidgets
+
+from flatcamGUI.preferences.cncjob.CNCJobAdvOptPrefGroupUI import CNCJobAdvOptPrefGroupUI
+from flatcamGUI.preferences.cncjob.CNCJobOptPrefGroupUI import CNCJobOptPrefGroupUI
+from flatcamGUI.preferences.cncjob.CNCJobGenPrefGroupUI import CNCJobGenPrefGroupUI
+
+
+class CNCJobPreferencesUI(QtWidgets.QWidget):
+
+    def __init__(self, decimals, parent=None):
+        QtWidgets.QWidget.__init__(self, parent=parent)
+        self.layout = QtWidgets.QHBoxLayout()
+        self.setLayout(self.layout)
+        self.decimals = decimals
+
+        self.cncjob_gen_group = CNCJobGenPrefGroupUI(decimals=self.decimals)
+        self.cncjob_gen_group.setMinimumWidth(260)
+        self.cncjob_opt_group = CNCJobOptPrefGroupUI(decimals=self.decimals)
+        self.cncjob_opt_group.setMinimumWidth(260)
+        self.cncjob_adv_opt_group = CNCJobAdvOptPrefGroupUI(decimals=self.decimals)
+        self.cncjob_adv_opt_group.setMinimumWidth(260)
+
+        self.layout.addWidget(self.cncjob_gen_group)
+        self.layout.addWidget(self.cncjob_opt_group)
+        self.layout.addWidget(self.cncjob_adv_opt_group)
+
+        self.layout.addStretch()

+ 0 - 0
flatcamGUI/preferences/cncjob/__init__.py


+ 155 - 0
flatcamGUI/preferences/excellon/ExcellonAdvOptPrefGroupUI.py

@@ -0,0 +1,155 @@
+from PyQt5 import QtWidgets
+from PyQt5.QtCore import QSettings
+
+from flatcamGUI.GUIElements import FCDoubleSpinner, FCEntry, FloatEntry, RadioSet, FCCheckBox
+from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
+import gettext
+import FlatCAMTranslation as fcTranslate
+import builtins
+
+fcTranslate.apply_language('strings')
+if '_' not in builtins.__dict__:
+    _ = gettext.gettext
+
+settings = QSettings("Open Source", "FlatCAM")
+if settings.contains("machinist"):
+    machinist_setting = settings.value('machinist', type=int)
+else:
+    machinist_setting = 0
+
+
+class ExcellonAdvOptPrefGroupUI(OptionsGroupUI):
+
+    def __init__(self, decimals=4, parent=None):
+        # OptionsGroupUI.__init__(self, "Excellon Advanced Options", parent=parent)
+        super(ExcellonAdvOptPrefGroupUI, self).__init__(self, parent=parent)
+
+        self.setTitle(str(_("Excellon Adv. Options")))
+        self.decimals = decimals
+
+        # #######################
+        # ## ADVANCED OPTIONS ###
+        # #######################
+
+        self.exc_label = QtWidgets.QLabel('<b>%s:</b>' % _('Advanced Options'))
+        self.exc_label.setToolTip(
+            _("A list of Excellon advanced parameters.\n"
+              "Those parameters are available only for\n"
+              "Advanced App. Level.")
+        )
+        self.layout.addWidget(self.exc_label)
+
+        grid1 = QtWidgets.QGridLayout()
+        self.layout.addLayout(grid1)
+
+        # Offset Z
+        offsetlabel = QtWidgets.QLabel('%s:' % _('Offset Z'))
+        offsetlabel.setToolTip(
+            _("Some drill bits (the larger ones) need to drill deeper\n"
+              "to create the desired exit hole diameter due of the tip shape.\n"
+              "The value here can compensate the Cut Z parameter."))
+        self.offset_entry = FCDoubleSpinner()
+        self.offset_entry.set_precision(self.decimals)
+        self.offset_entry.set_range(-999.9999, 999.9999)
+
+        grid1.addWidget(offsetlabel, 0, 0)
+        grid1.addWidget(self.offset_entry, 0, 1)
+
+        # ToolChange X,Y
+        toolchange_xy_label = QtWidgets.QLabel('%s:' % _('Toolchange X,Y'))
+        toolchange_xy_label.setToolTip(
+            _("Toolchange X,Y position.")
+        )
+        self.toolchangexy_entry = FCEntry()
+
+        grid1.addWidget(toolchange_xy_label, 1, 0)
+        grid1.addWidget(self.toolchangexy_entry, 1, 1)
+
+        # Start Z
+        startzlabel = QtWidgets.QLabel('%s:' % _('Start Z'))
+        startzlabel.setToolTip(
+            _("Height of the tool just after start.\n"
+              "Delete the value if you don't need this feature.")
+        )
+        self.estartz_entry = FloatEntry()
+
+        grid1.addWidget(startzlabel, 2, 0)
+        grid1.addWidget(self.estartz_entry, 2, 1)
+
+        # Feedrate Rapids
+        fr_rapid_label = QtWidgets.QLabel('%s:' % _('Feedrate Rapids'))
+        fr_rapid_label.setToolTip(
+            _("Tool speed while drilling\n"
+              "(in units per minute).\n"
+              "This is for the rapid move G00.\n"
+              "It is useful only for Marlin,\n"
+              "ignore for any other cases.")
+        )
+        self.feedrate_rapid_entry = FCDoubleSpinner()
+        self.feedrate_rapid_entry.set_precision(self.decimals)
+        self.feedrate_rapid_entry.set_range(0, 99999.9999)
+
+        grid1.addWidget(fr_rapid_label, 3, 0)
+        grid1.addWidget(self.feedrate_rapid_entry, 3, 1)
+
+        # Probe depth
+        self.pdepth_label = QtWidgets.QLabel('%s:' % _("Probe Z depth"))
+        self.pdepth_label.setToolTip(
+            _("The maximum depth that the probe is allowed\n"
+              "to probe. Negative value, in current units.")
+        )
+        self.pdepth_entry = FCDoubleSpinner()
+        self.pdepth_entry.set_precision(self.decimals)
+        self.pdepth_entry.set_range(-99999.9999, 0.0000)
+
+        grid1.addWidget(self.pdepth_label, 4, 0)
+        grid1.addWidget(self.pdepth_entry, 4, 1)
+
+        # Probe feedrate
+        self.feedrate_probe_label = QtWidgets.QLabel('%s:' % _("Feedrate Probe"))
+        self.feedrate_probe_label.setToolTip(
+           _("The feedrate used while the probe is probing.")
+        )
+        self.feedrate_probe_entry = FCDoubleSpinner()
+        self.feedrate_probe_entry.set_precision(self.decimals)
+        self.feedrate_probe_entry.set_range(0, 99999.9999)
+
+        grid1.addWidget(self.feedrate_probe_label, 5, 0)
+        grid1.addWidget(self.feedrate_probe_entry, 5, 1)
+
+        # Spindle direction
+        spindle_dir_label = QtWidgets.QLabel('%s:' % _('Spindle direction'))
+        spindle_dir_label.setToolTip(
+            _("This sets the direction that the spindle is rotating.\n"
+              "It can be either:\n"
+              "- CW = clockwise or\n"
+              "- CCW = counter clockwise")
+        )
+
+        self.spindledir_radio = RadioSet([{'label': _('CW'), 'value': 'CW'},
+                                          {'label': _('CCW'), 'value': 'CCW'}])
+        grid1.addWidget(spindle_dir_label, 6, 0)
+        grid1.addWidget(self.spindledir_radio, 6, 1)
+
+        self.fplunge_cb = FCCheckBox('%s' % _('Fast Plunge'))
+        self.fplunge_cb.setToolTip(
+            _("By checking this, the vertical move from\n"
+              "Z_Toolchange to Z_move is done with G0,\n"
+              "meaning the fastest speed available.\n"
+              "WARNING: the move is done at Toolchange X,Y coords.")
+        )
+        grid1.addWidget(self.fplunge_cb, 7, 0, 1, 2)
+
+        self.fretract_cb = FCCheckBox('%s' % _('Fast Retract'))
+        self.fretract_cb.setToolTip(
+            _("Exit hole strategy.\n"
+              " - When uncheked, while exiting the drilled hole the drill bit\n"
+              "will travel slow, with set feedrate (G1), up to zero depth and then\n"
+              "travel as fast as possible (G0) to the Z Move (travel height).\n"
+              " - When checked the travel from Z cut (cut depth) to Z_move\n"
+              "(travel height) is done as fast as possible (G0) in one move.")
+        )
+
+        grid1.addWidget(self.fretract_cb, 8, 0, 1, 2)
+
+        self.layout.addStretch()

+ 306 - 0
flatcamGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py

@@ -0,0 +1,306 @@
+from PyQt5 import QtWidgets
+from PyQt5.QtCore import QSettings
+
+from flatcamGUI.GUIElements import FCSpinner, FCDoubleSpinner, RadioSet
+from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
+
+import gettext
+import FlatCAMTranslation as fcTranslate
+import builtins
+
+fcTranslate.apply_language('strings')
+if '_' not in builtins.__dict__:
+    _ = gettext.gettext
+
+settings = QSettings("Open Source", "FlatCAM")
+if settings.contains("machinist"):
+    machinist_setting = settings.value('machinist', type=int)
+else:
+    machinist_setting = 0
+
+
+class ExcellonEditorPrefGroupUI(OptionsGroupUI):
+    def __init__(self, decimals=4, parent=None):
+        super(ExcellonEditorPrefGroupUI, self).__init__(self, parent=parent)
+
+        self.setTitle(str(_("Excellon Editor")))
+        self.decimals = decimals
+
+        # Excellon Editor Parameters
+        self.param_label = QtWidgets.QLabel("<b>%s:</b>" % _("Parameters"))
+        self.param_label.setToolTip(
+            _("A list of Excellon Editor parameters.")
+        )
+        self.layout.addWidget(self.param_label)
+
+        grid0 = QtWidgets.QGridLayout()
+        self.layout.addLayout(grid0)
+
+        # Selection Limit
+        self.sel_limit_label = QtWidgets.QLabel('%s:' % _("Selection limit"))
+        self.sel_limit_label.setToolTip(
+            _("Set the number of selected Excellon geometry\n"
+              "items above which the utility geometry\n"
+              "becomes just a selection rectangle.\n"
+              "Increases the performance when moving a\n"
+              "large number of geometric elements.")
+        )
+        self.sel_limit_entry = FCSpinner()
+        self.sel_limit_entry.set_range(0, 99999)
+
+        grid0.addWidget(self.sel_limit_label, 0, 0)
+        grid0.addWidget(self.sel_limit_entry, 0, 1)
+
+        # New Diameter
+        self.addtool_entry_lbl = QtWidgets.QLabel('%s:' % _('New Dia'))
+        self.addtool_entry_lbl.setToolTip(
+            _("Diameter for the new tool")
+        )
+
+        self.addtool_entry = FCDoubleSpinner()
+        self.addtool_entry.set_range(0.000001, 99.9999)
+        self.addtool_entry.set_precision(self.decimals)
+
+        grid0.addWidget(self.addtool_entry_lbl, 1, 0)
+        grid0.addWidget(self.addtool_entry, 1, 1)
+
+        # Number of drill holes in a drill array
+        self.drill_array_size_label = QtWidgets.QLabel('%s:' % _('Nr of drills'))
+        self.drill_array_size_label.setToolTip(
+            _("Specify how many drills to be in the array.")
+        )
+        # self.drill_array_size_label.setMinimumWidth(100)
+
+        self.drill_array_size_entry = FCSpinner()
+        self.drill_array_size_entry.set_range(0, 9999)
+
+        grid0.addWidget(self.drill_array_size_label, 2, 0)
+        grid0.addWidget(self.drill_array_size_entry, 2, 1)
+
+        self.drill_array_linear_label = QtWidgets.QLabel('<b>%s:</b>' % _('Linear Drill Array'))
+        grid0.addWidget(self.drill_array_linear_label, 3, 0, 1, 2)
+
+        # Linear Drill Array direction
+        self.drill_axis_label = QtWidgets.QLabel('%s:' % _('Linear Direction'))
+        self.drill_axis_label.setToolTip(
+            _("Direction on which the linear array is oriented:\n"
+              "- 'X' - horizontal axis \n"
+              "- 'Y' - vertical axis or \n"
+              "- 'Angle' - a custom angle for the array inclination")
+        )
+        # self.drill_axis_label.setMinimumWidth(100)
+        self.drill_axis_radio = RadioSet([{'label': _('X'), 'value': 'X'},
+                                          {'label': _('Y'), 'value': 'Y'},
+                                          {'label': _('Angle'), 'value': 'A'}])
+
+        grid0.addWidget(self.drill_axis_label, 4, 0)
+        grid0.addWidget(self.drill_axis_radio, 4, 1)
+
+        # Linear Drill Array pitch distance
+        self.drill_pitch_label = QtWidgets.QLabel('%s:' % _('Pitch'))
+        self.drill_pitch_label.setToolTip(
+            _("Pitch = Distance between elements of the array.")
+        )
+        # self.drill_pitch_label.setMinimumWidth(100)
+        self.drill_pitch_entry = FCDoubleSpinner()
+        self.drill_pitch_entry.set_range(0, 99999.9999)
+        self.drill_pitch_entry.set_precision(self.decimals)
+
+        grid0.addWidget(self.drill_pitch_label, 5, 0)
+        grid0.addWidget(self.drill_pitch_entry, 5, 1)
+
+        # Linear Drill Array custom angle
+        self.drill_angle_label = QtWidgets.QLabel('%s:' % _('Angle'))
+        self.drill_angle_label.setToolTip(
+            _("Angle at which each element in circular array is placed.")
+        )
+        self.drill_angle_entry = FCDoubleSpinner()
+        self.drill_pitch_entry.set_range(-360, 360)
+        self.drill_pitch_entry.set_precision(self.decimals)
+        self.drill_angle_entry.setWrapping(True)
+        self.drill_angle_entry.setSingleStep(5)
+
+        grid0.addWidget(self.drill_angle_label, 6, 0)
+        grid0.addWidget(self.drill_angle_entry, 6, 1)
+
+        self.drill_array_circ_label = QtWidgets.QLabel('<b>%s:</b>' % _('Circular Drill Array'))
+        grid0.addWidget(self.drill_array_circ_label, 7, 0, 1, 2)
+
+        # Circular Drill Array direction
+        self.drill_circular_direction_label = QtWidgets.QLabel('%s:' % _('Circular Direction'))
+        self.drill_circular_direction_label.setToolTip(
+            _("Direction for circular array.\n"
+              "Can be CW = clockwise or CCW = counter clockwise.")
+        )
+
+        self.drill_circular_dir_radio = RadioSet([{'label': _('CW'), 'value': 'CW'},
+                                                  {'label': _('CCW'), 'value': 'CCW'}])
+
+        grid0.addWidget(self.drill_circular_direction_label, 8, 0)
+        grid0.addWidget(self.drill_circular_dir_radio, 8, 1)
+
+        # Circular Drill Array Angle
+        self.drill_circular_angle_label = QtWidgets.QLabel('%s:' % _('Circular Angle'))
+        self.drill_circular_angle_label.setToolTip(
+            _("Angle at which each element in circular array is placed.")
+        )
+        self.drill_circular_angle_entry = FCDoubleSpinner()
+        self.drill_circular_angle_entry.set_range(-360, 360)
+        self.drill_circular_angle_entry.set_precision(self.decimals)
+        self.drill_circular_angle_entry.setWrapping(True)
+        self.drill_circular_angle_entry.setSingleStep(5)
+
+        grid0.addWidget(self.drill_circular_angle_label, 9, 0)
+        grid0.addWidget(self.drill_circular_angle_entry, 9, 1)
+
+        # ##### SLOTS #####
+        # #################
+        self.drill_array_circ_label = QtWidgets.QLabel('<b>%s:</b>' % _('Slots'))
+        grid0.addWidget(self.drill_array_circ_label, 10, 0, 1, 2)
+
+        # Slot length
+        self.slot_length_label = QtWidgets.QLabel('%s:' % _('Length'))
+        self.slot_length_label.setToolTip(
+            _("Length = The length of the slot.")
+        )
+        self.slot_length_label.setMinimumWidth(100)
+
+        self.slot_length_entry = FCDoubleSpinner()
+        self.slot_length_entry.set_range(0, 99999)
+        self.slot_length_entry.set_precision(self.decimals)
+        self.slot_length_entry.setWrapping(True)
+        self.slot_length_entry.setSingleStep(1)
+
+        grid0.addWidget(self.slot_length_label, 11, 0)
+        grid0.addWidget(self.slot_length_entry, 11, 1)
+
+        # Slot direction
+        self.slot_axis_label = QtWidgets.QLabel('%s:' % _('Direction'))
+        self.slot_axis_label.setToolTip(
+            _("Direction on which the slot is oriented:\n"
+              "- 'X' - horizontal axis \n"
+              "- 'Y' - vertical axis or \n"
+              "- 'Angle' - a custom angle for the slot inclination")
+        )
+        self.slot_axis_label.setMinimumWidth(100)
+
+        self.slot_axis_radio = RadioSet([{'label': _('X'), 'value': 'X'},
+                                         {'label': _('Y'), 'value': 'Y'},
+                                         {'label': _('Angle'), 'value': 'A'}])
+        grid0.addWidget(self.slot_axis_label, 12, 0)
+        grid0.addWidget(self.slot_axis_radio, 12, 1)
+
+        # Slot custom angle
+        self.slot_angle_label = QtWidgets.QLabel('%s:' % _('Angle'))
+        self.slot_angle_label.setToolTip(
+            _("Angle at which the slot is placed.\n"
+              "The precision is of max 2 decimals.\n"
+              "Min value is: -359.99 degrees.\n"
+              "Max value is:  360.00 degrees.")
+        )
+        self.slot_angle_label.setMinimumWidth(100)
+
+        self.slot_angle_spinner = FCDoubleSpinner()
+        self.slot_angle_spinner.set_precision(self.decimals)
+        self.slot_angle_spinner.setWrapping(True)
+        self.slot_angle_spinner.setRange(-359.99, 360.00)
+        self.slot_angle_spinner.setSingleStep(5)
+
+        grid0.addWidget(self.slot_angle_label, 13, 0)
+        grid0.addWidget(self.slot_angle_spinner, 13, 1)
+
+        # #### SLOTS ARRAY #######
+        # ########################
+
+        self.slot_array_linear_label = QtWidgets.QLabel('<b>%s:</b>' % _('Linear Slot Array'))
+        grid0.addWidget(self.slot_array_linear_label, 14, 0, 1, 2)
+
+        # Number of slot holes in a drill array
+        self.slot_array_size_label = QtWidgets.QLabel('%s:' % _('Nr of slots'))
+        self.drill_array_size_label.setToolTip(
+            _("Specify how many slots to be in the array.")
+        )
+        # self.slot_array_size_label.setMinimumWidth(100)
+
+        self.slot_array_size_entry = FCSpinner()
+        self.slot_array_size_entry.set_range(0, 999999)
+
+        grid0.addWidget(self.slot_array_size_label, 15, 0)
+        grid0.addWidget(self.slot_array_size_entry, 15, 1)
+
+        # Linear Slot Array direction
+        self.slot_array_axis_label = QtWidgets.QLabel('%s:' % _('Linear Direction'))
+        self.slot_array_axis_label.setToolTip(
+            _("Direction on which the linear array is oriented:\n"
+              "- 'X' - horizontal axis \n"
+              "- 'Y' - vertical axis or \n"
+              "- 'Angle' - a custom angle for the array inclination")
+        )
+        # self.slot_axis_label.setMinimumWidth(100)
+        self.slot_array_axis_radio = RadioSet([{'label': _('X'), 'value': 'X'},
+                                               {'label': _('Y'), 'value': 'Y'},
+                                               {'label': _('Angle'), 'value': 'A'}])
+
+        grid0.addWidget(self.slot_array_axis_label, 16, 0)
+        grid0.addWidget(self.slot_array_axis_radio, 16, 1)
+
+        # Linear Slot Array pitch distance
+        self.slot_array_pitch_label = QtWidgets.QLabel('%s:' % _('Pitch'))
+        self.slot_array_pitch_label.setToolTip(
+            _("Pitch = Distance between elements of the array.")
+        )
+        # self.drill_pitch_label.setMinimumWidth(100)
+        self.slot_array_pitch_entry = FCDoubleSpinner()
+        self.slot_array_pitch_entry.set_precision(self.decimals)
+        self.slot_array_pitch_entry.setWrapping(True)
+        self.slot_array_pitch_entry.setRange(0, 999999)
+        self.slot_array_pitch_entry.setSingleStep(1)
+
+        grid0.addWidget(self.slot_array_pitch_label, 17, 0)
+        grid0.addWidget(self.slot_array_pitch_entry, 17, 1)
+
+        # Linear Slot Array custom angle
+        self.slot_array_angle_label = QtWidgets.QLabel('%s:' % _('Angle'))
+        self.slot_array_angle_label.setToolTip(
+            _("Angle at which each element in circular array is placed.")
+        )
+        self.slot_array_angle_entry = FCDoubleSpinner()
+        self.slot_array_angle_entry.set_precision(self.decimals)
+        self.slot_array_angle_entry.setWrapping(True)
+        self.slot_array_angle_entry.setRange(-360, 360)
+        self.slot_array_angle_entry.setSingleStep(5)
+
+        grid0.addWidget(self.slot_array_angle_label, 18, 0)
+        grid0.addWidget(self.slot_array_angle_entry, 18, 1)
+
+        self.slot_array_circ_label = QtWidgets.QLabel('<b>%s:</b>' % _('Circular Slot Array'))
+        grid0.addWidget(self.slot_array_circ_label, 19, 0, 1, 2)
+
+        # Circular Slot Array direction
+        self.slot_array_circular_direction_label = QtWidgets.QLabel('%s:' % _('Circular Direction'))
+        self.slot_array_circular_direction_label.setToolTip(
+            _("Direction for circular array.\n"
+              "Can be CW = clockwise or CCW = counter clockwise.")
+        )
+
+        self.slot_array_circular_dir_radio = RadioSet([{'label': _('CW'), 'value': 'CW'},
+                                                       {'label': _('CCW'), 'value': 'CCW'}])
+
+        grid0.addWidget(self.slot_array_circular_direction_label, 20, 0)
+        grid0.addWidget(self.slot_array_circular_dir_radio, 20, 1)
+
+        # Circular Slot Array Angle
+        self.slot_array_circular_angle_label = QtWidgets.QLabel('%s:' % _('Circular Angle'))
+        self.slot_array_circular_angle_label.setToolTip(
+            _("Angle at which each element in circular array is placed.")
+        )
+        self.slot_array_circular_angle_entry = FCDoubleSpinner()
+        self.slot_array_circular_angle_entry.set_precision(self.decimals)
+        self.slot_array_circular_angle_entry.setWrapping(True)
+        self.slot_array_circular_angle_entry.setRange(-360, 360)
+        self.slot_array_circular_angle_entry.setSingleStep(5)
+
+        grid0.addWidget(self.slot_array_circular_angle_label, 21, 0)
+        grid0.addWidget(self.slot_array_circular_angle_entry, 21, 1)
+
+        self.layout.addStretch()

+ 168 - 0
flatcamGUI/preferences/excellon/ExcellonExpPrefGroupUI.py

@@ -0,0 +1,168 @@
+from PyQt5 import QtWidgets, QtCore
+from PyQt5.QtCore import QSettings
+
+from flatcamGUI.GUIElements import RadioSet, FCSpinner
+from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
+import gettext
+import FlatCAMTranslation as fcTranslate
+import builtins
+
+fcTranslate.apply_language('strings')
+if '_' not in builtins.__dict__:
+    _ = gettext.gettext
+
+settings = QSettings("Open Source", "FlatCAM")
+if settings.contains("machinist"):
+    machinist_setting = settings.value('machinist', type=int)
+else:
+    machinist_setting = 0
+
+
+class ExcellonExpPrefGroupUI(OptionsGroupUI):
+
+    def __init__(self, decimals=4, parent=None):
+        super(ExcellonExpPrefGroupUI, self).__init__(self, parent=parent)
+
+        self.setTitle(str(_("Excellon Export")))
+        self.decimals = decimals
+
+        # Plot options
+        self.export_options_label = QtWidgets.QLabel("<b>%s:</b>" % _("Export Options"))
+        self.export_options_label.setToolTip(
+            _("The parameters set here are used in the file exported\n"
+              "when using the File -> Export -> Export Excellon menu entry.")
+        )
+        self.layout.addWidget(self.export_options_label)
+
+        form = QtWidgets.QFormLayout()
+        self.layout.addLayout(form)
+
+        # Excellon Units
+        self.excellon_units_label = QtWidgets.QLabel('%s:' % _('Units'))
+        self.excellon_units_label.setToolTip(
+            _("The units used in the Excellon file.")
+        )
+
+        self.excellon_units_radio = RadioSet([{'label': _('INCH'), 'value': 'INCH'},
+                                              {'label': _('MM'), 'value': 'METRIC'}])
+        self.excellon_units_radio.setToolTip(
+            _("The units used in the Excellon file.")
+        )
+
+        form.addRow(self.excellon_units_label, self.excellon_units_radio)
+
+        # Excellon non-decimal format
+        self.digits_label = QtWidgets.QLabel("%s:" % _("Int/Decimals"))
+        self.digits_label.setToolTip(
+            _("The NC drill files, usually named Excellon files\n"
+              "are files that can be found in different formats.\n"
+              "Here we set the format used when the provided\n"
+              "coordinates are not using period.")
+        )
+
+        hlay1 = QtWidgets.QHBoxLayout()
+
+        self.format_whole_entry = FCSpinner()
+        self.format_whole_entry.set_range(0, 9)
+        self.format_whole_entry.setMinimumWidth(30)
+        self.format_whole_entry.setToolTip(
+            _("This numbers signify the number of digits in\n"
+              "the whole part of Excellon coordinates.")
+        )
+        hlay1.addWidget(self.format_whole_entry, QtCore.Qt.AlignLeft)
+
+        excellon_separator_label = QtWidgets.QLabel(':')
+        excellon_separator_label.setFixedWidth(5)
+        hlay1.addWidget(excellon_separator_label, QtCore.Qt.AlignLeft)
+
+        self.format_dec_entry = FCSpinner()
+        self.format_dec_entry.set_range(0, 9)
+        self.format_dec_entry.setMinimumWidth(30)
+        self.format_dec_entry.setToolTip(
+            _("This numbers signify the number of digits in\n"
+              "the decimal part of Excellon coordinates.")
+        )
+        hlay1.addWidget(self.format_dec_entry, QtCore.Qt.AlignLeft)
+        hlay1.addStretch()
+
+        form.addRow(self.digits_label, hlay1)
+
+        # Select the Excellon Format
+        self.format_label = QtWidgets.QLabel("%s:" % _("Format"))
+        self.format_label.setToolTip(
+            _("Select the kind of coordinates format used.\n"
+              "Coordinates can be saved with decimal point or without.\n"
+              "When there is no decimal point, it is required to specify\n"
+              "the number of digits for integer part and the number of decimals.\n"
+              "Also it will have to be specified if LZ = leading zeros are kept\n"
+              "or TZ = trailing zeros are kept.")
+        )
+        self.format_radio = RadioSet([{'label': _('Decimal'), 'value': 'dec'},
+                                      {'label': _('No-Decimal'), 'value': 'ndec'}])
+        self.format_radio.setToolTip(
+            _("Select the kind of coordinates format used.\n"
+              "Coordinates can be saved with decimal point or without.\n"
+              "When there is no decimal point, it is required to specify\n"
+              "the number of digits for integer part and the number of decimals.\n"
+              "Also it will have to be specified if LZ = leading zeros are kept\n"
+              "or TZ = trailing zeros are kept.")
+        )
+
+        form.addRow(self.format_label, self.format_radio)
+
+        # Excellon Zeros
+        self.zeros_label = QtWidgets.QLabel('%s:' % _('Zeros'))
+        self.zeros_label.setAlignment(QtCore.Qt.AlignLeft)
+        self.zeros_label.setToolTip(
+            _("This sets the type of Excellon zeros.\n"
+              "If LZ then Leading Zeros are kept and\n"
+              "Trailing Zeros are removed.\n"
+              "If TZ is checked then Trailing Zeros are kept\n"
+              "and Leading Zeros are removed.")
+        )
+
+        self.zeros_radio = RadioSet([{'label': _('LZ'), 'value': 'LZ'},
+                                     {'label': _('TZ'), 'value': 'TZ'}])
+        self.zeros_radio.setToolTip(
+            _("This sets the default type of Excellon zeros.\n"
+              "If LZ then Leading Zeros are kept and\n"
+              "Trailing Zeros are removed.\n"
+              "If TZ is checked then Trailing Zeros are kept\n"
+              "and Leading Zeros are removed.")
+        )
+
+        form.addRow(self.zeros_label, self.zeros_radio)
+
+        # Slot type
+        self.slot_type_label = QtWidgets.QLabel('%s:' % _('Slot type'))
+        self.slot_type_label.setAlignment(QtCore.Qt.AlignLeft)
+        self.slot_type_label.setToolTip(
+            _("This sets how the slots will be exported.\n"
+              "If ROUTED then the slots will be routed\n"
+              "using M15/M16 commands.\n"
+              "If DRILLED(G85) the slots will be exported\n"
+              "using the Drilled slot command (G85).")
+        )
+
+        self.slot_type_radio = RadioSet([{'label': _('Routed'), 'value': 'routing'},
+                                         {'label': _('Drilled(G85)'), 'value': 'drilling'}])
+        self.slot_type_radio.setToolTip(
+            _("This sets how the slots will be exported.\n"
+              "If ROUTED then the slots will be routed\n"
+              "using M15/M16 commands.\n"
+              "If DRILLED(G85) the slots will be exported\n"
+              "using the Drilled slot command (G85).")
+        )
+
+        form.addRow(self.slot_type_label, self.slot_type_radio)
+
+        self.layout.addStretch()
+        self.format_radio.activated_custom.connect(self.optimization_selection)
+
+    def optimization_selection(self):
+        if self.format_radio.get_value() == 'dec':
+            self.zeros_label.setDisabled(True)
+            self.zeros_radio.setDisabled(True)
+        else:
+            self.zeros_label.setDisabled(False)
+            self.zeros_radio.setDisabled(False)

+ 415 - 0
flatcamGUI/preferences/excellon/ExcellonGenPrefGroupUI.py

@@ -0,0 +1,415 @@
+import platform
+
+from PyQt5 import QtWidgets, QtCore, QtGui
+from PyQt5.QtCore import QSettings
+
+from flatcamGUI.GUIElements import FCCheckBox, FCSpinner, RadioSet, FCEntry
+from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
+import gettext
+import FlatCAMTranslation as fcTranslate
+import builtins
+
+fcTranslate.apply_language('strings')
+if '_' not in builtins.__dict__:
+    _ = gettext.gettext
+
+settings = QSettings("Open Source", "FlatCAM")
+if settings.contains("machinist"):
+    machinist_setting = settings.value('machinist', type=int)
+else:
+    machinist_setting = 0
+
+
+class ExcellonGenPrefGroupUI(OptionsGroupUI):
+
+    def __init__(self, decimals=4, parent=None):
+        # OptionsGroupUI.__init__(self, "Excellon Options", parent=parent)
+        super(ExcellonGenPrefGroupUI, self).__init__(self, parent=parent)
+
+        self.setTitle(str(_("Excellon General")))
+        self.decimals = decimals
+
+        # Plot options
+        self.plot_options_label = QtWidgets.QLabel("<b>%s:</b>" % _("Plot Options"))
+        self.layout.addWidget(self.plot_options_label)
+
+        grid1 = QtWidgets.QGridLayout()
+        self.layout.addLayout(grid1)
+
+        self.plot_cb = FCCheckBox(label=_('Plot'))
+        self.plot_cb.setToolTip(
+            "Plot (show) this object."
+        )
+        grid1.addWidget(self.plot_cb, 0, 0)
+
+        self.solid_cb = FCCheckBox(label=_('Solid'))
+        self.solid_cb.setToolTip(
+            "Plot as solid circles."
+        )
+        grid1.addWidget(self.solid_cb, 0, 1)
+
+        separator_line = QtWidgets.QFrame()
+        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
+        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
+        grid1.addWidget(separator_line, 1, 0, 1, 2)
+
+        grid2 = QtWidgets.QGridLayout()
+        self.layout.addLayout(grid2)
+        grid2.setColumnStretch(0, 0)
+        grid2.setColumnStretch(1, 1)
+
+        # Excellon format
+        self.excellon_format_label = QtWidgets.QLabel("<b>%s:</b>" % _("Excellon Format"))
+        self.excellon_format_label.setToolTip(
+            _("The NC drill files, usually named Excellon files\n"
+              "are files that can be found in different formats.\n"
+              "Here we set the format used when the provided\n"
+              "coordinates are not using period.\n"
+              "\n"
+              "Possible presets:\n"
+              "\n"
+              "PROTEUS 3:3 MM LZ\n"
+              "DipTrace 5:2 MM TZ\n"
+              "DipTrace 4:3 MM LZ\n"
+              "\n"
+              "EAGLE 3:3 MM TZ\n"
+              "EAGLE 4:3 MM TZ\n"
+              "EAGLE 2:5 INCH TZ\n"
+              "EAGLE 3:5 INCH TZ\n"
+              "\n"
+              "ALTIUM 2:4 INCH LZ\n"
+              "Sprint Layout 2:4 INCH LZ"
+              "\n"
+              "KiCAD 3:5 INCH TZ")
+        )
+        grid2.addWidget(self.excellon_format_label, 0, 0, 1, 2)
+
+        self.excellon_format_in_label = QtWidgets.QLabel('%s:' % _("INCH"))
+        self.excellon_format_in_label.setToolTip(_("Default values for INCH are 2:4"))
+
+        hlay1 = QtWidgets.QHBoxLayout()
+        self.excellon_format_upper_in_entry = FCSpinner()
+        self.excellon_format_upper_in_entry.set_range(0, 9)
+        self.excellon_format_upper_in_entry.setMinimumWidth(30)
+        self.excellon_format_upper_in_entry.setToolTip(
+           _("This numbers signify the number of digits in\n"
+             "the whole part of Excellon coordinates.")
+        )
+        hlay1.addWidget(self.excellon_format_upper_in_entry)
+
+        excellon_separator_in_label = QtWidgets.QLabel(':')
+        excellon_separator_in_label.setFixedWidth(5)
+        hlay1.addWidget(excellon_separator_in_label)
+
+        self.excellon_format_lower_in_entry = FCSpinner()
+        self.excellon_format_lower_in_entry.set_range(0, 9)
+        self.excellon_format_lower_in_entry.setMinimumWidth(30)
+        self.excellon_format_lower_in_entry.setToolTip(
+            _("This numbers signify the number of digits in\n"
+              "the decimal part of Excellon coordinates.")
+        )
+        hlay1.addWidget(self.excellon_format_lower_in_entry)
+
+        grid2.addWidget(self.excellon_format_in_label, 1, 0)
+        grid2.addLayout(hlay1, 1, 1)
+
+        self.excellon_format_mm_label = QtWidgets.QLabel('%s:' % _("METRIC"))
+        self.excellon_format_mm_label.setToolTip(_("Default values for METRIC are 3:3"))
+
+        hlay2 = QtWidgets.QHBoxLayout()
+        self.excellon_format_upper_mm_entry = FCSpinner()
+        self.excellon_format_upper_mm_entry.set_range(0, 9)
+        self.excellon_format_upper_mm_entry.setMinimumWidth(30)
+        self.excellon_format_upper_mm_entry.setToolTip(
+            _("This numbers signify the number of digits in\n"
+              "the whole part of Excellon coordinates.")
+        )
+        hlay2.addWidget(self.excellon_format_upper_mm_entry)
+
+        excellon_separator_mm_label = QtWidgets.QLabel(':')
+        excellon_separator_mm_label.setFixedWidth(5)
+        hlay2.addWidget(excellon_separator_mm_label, QtCore.Qt.AlignLeft)
+
+        self.excellon_format_lower_mm_entry = FCSpinner()
+        self.excellon_format_lower_mm_entry.set_range(0, 9)
+        self.excellon_format_lower_mm_entry.setMinimumWidth(30)
+        self.excellon_format_lower_mm_entry.setToolTip(
+            _("This numbers signify the number of digits in\n"
+              "the decimal part of Excellon coordinates.")
+        )
+        hlay2.addWidget(self.excellon_format_lower_mm_entry)
+
+        grid2.addWidget(self.excellon_format_mm_label, 2, 0)
+        grid2.addLayout(hlay2, 2, 1)
+
+        self.excellon_zeros_label = QtWidgets.QLabel('%s:' % _('Zeros'))
+        self.excellon_zeros_label.setAlignment(QtCore.Qt.AlignLeft)
+        self.excellon_zeros_label.setToolTip(
+            _("This sets the type of Excellon zeros.\n"
+              "If LZ then Leading Zeros are kept and\n"
+              "Trailing Zeros are removed.\n"
+              "If TZ is checked then Trailing Zeros are kept\n"
+              "and Leading Zeros are removed.\n\n"
+              "This is used when there is no information\n"
+              "stored in the Excellon file.")
+        )
+        grid2.addWidget(self.excellon_zeros_label, 3, 0)
+
+        self.excellon_zeros_radio = RadioSet([{'label': _('LZ'), 'value': 'L'},
+                                              {'label': _('TZ'), 'value': 'T'}])
+
+        grid2.addWidget(self.excellon_zeros_radio, 3, 1)
+
+        self.excellon_units_label = QtWidgets.QLabel('%s:' % _('Units'))
+        self.excellon_units_label.setAlignment(QtCore.Qt.AlignLeft)
+        self.excellon_units_label.setToolTip(
+            _("This sets the default units of Excellon files.\n"
+              "If it is not detected in the parsed file the value here\n"
+              "will be used."
+              "Some Excellon files don't have an header\n"
+              "therefore this parameter will be used.")
+        )
+
+        self.excellon_units_radio = RadioSet([{'label': _('INCH'), 'value': 'INCH'},
+                                              {'label': _('MM'), 'value': 'METRIC'}])
+        self.excellon_units_radio.setToolTip(
+            _("This sets the units of Excellon files.\n"
+              "Some Excellon files don't have an header\n"
+              "therefore this parameter will be used.")
+        )
+
+        grid2.addWidget(self.excellon_units_label, 4, 0)
+        grid2.addWidget(self.excellon_units_radio, 4, 1)
+
+        self.update_excellon_cb = FCCheckBox(label=_('Update Export settings'))
+        self.update_excellon_cb.setToolTip(
+            "If checked, the Excellon Export settings will be updated with the ones above."
+        )
+        grid2.addWidget(self.update_excellon_cb, 5, 0, 1, 2)
+
+        # Adding the Excellon Format Defaults Button
+        self.excellon_defaults_button = QtWidgets.QPushButton()
+        self.excellon_defaults_button.setText(str(_("Restore Defaults")))
+        self.excellon_defaults_button.setMinimumWidth(80)
+        grid2.addWidget(self.excellon_defaults_button, 6, 0, 1, 2)
+
+        separator_line = QtWidgets.QFrame()
+        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
+        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
+        grid2.addWidget(separator_line, 7, 0, 1, 2)
+
+        self.excellon_general_label = QtWidgets.QLabel("<b>%s:</b>" % _("Excellon Optimization"))
+        grid2.addWidget(self.excellon_general_label, 8, 0, 1, 2)
+
+        self.excellon_optimization_label = QtWidgets.QLabel(_('Algorithm:'))
+        self.excellon_optimization_label.setToolTip(
+            _("This sets the optimization type for the Excellon drill path.\n"
+              "If <<MetaHeuristic>> is checked then Google OR-Tools algorithm with\n"
+              "MetaHeuristic Guided Local Path is used. Default search time is 3sec.\n"
+              "If <<Basic>> is checked then Google OR-Tools Basic algorithm is used.\n"
+              "If <<TSA>> is checked then Travelling Salesman algorithm is used for\n"
+              "drill path optimization.\n"
+              "\n"
+              "If this control is disabled, then FlatCAM works in 32bit mode and it uses\n"
+              "Travelling Salesman algorithm for path optimization.")
+        )
+
+        self.excellon_optimization_radio = RadioSet([{'label': _('MetaHeuristic'), 'value': 'M'},
+                                                     {'label': _('Basic'), 'value': 'B'},
+                                                     {'label': _('TSA'), 'value': 'T'}],
+                                                    orientation='vertical', stretch=False)
+        self.excellon_optimization_radio.setToolTip(
+            _("This sets the optimization type for the Excellon drill path.\n"
+              "If <<MetaHeuristic>> is checked then Google OR-Tools algorithm with\n"
+              "MetaHeuristic Guided Local Path is used. Default search time is 3sec.\n"
+              "If <<Basic>> is checked then Google OR-Tools Basic algorithm is used.\n"
+              "If <<TSA>> is checked then Travelling Salesman algorithm is used for\n"
+              "drill path optimization.\n"
+              "\n"
+              "If this control is disabled, then FlatCAM works in 32bit mode and it uses\n"
+              "Travelling Salesman algorithm for path optimization.")
+        )
+
+        grid2.addWidget(self.excellon_optimization_label, 9, 0)
+        grid2.addWidget(self.excellon_optimization_radio, 9, 1)
+
+        self.optimization_time_label = QtWidgets.QLabel('%s:' % _('Duration'))
+        self.optimization_time_label.setAlignment(QtCore.Qt.AlignLeft)
+        self.optimization_time_label.setToolTip(
+            _("When OR-Tools Metaheuristic (MH) is enabled there is a\n"
+              "maximum threshold for how much time is spent doing the\n"
+              "path optimization. This max duration is set here.\n"
+              "In seconds.")
+
+        )
+
+        self.optimization_time_entry = FCSpinner()
+        self.optimization_time_entry.set_range(0, 999)
+
+        grid2.addWidget(self.optimization_time_label, 10, 0)
+        grid2.addWidget(self.optimization_time_entry, 10, 1)
+
+        separator_line = QtWidgets.QFrame()
+        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
+        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
+        grid2.addWidget(separator_line, 11, 0, 1, 2)
+
+        # Excellon Object Color
+        self.gerber_color_label = QtWidgets.QLabel('<b>%s</b>' % _('Excellon Object Color'))
+        grid2.addWidget(self.gerber_color_label, 12, 0, 1, 2)
+
+        # Plot Line Color
+        self.line_color_label = QtWidgets.QLabel('%s:' % _('Outline'))
+        self.line_color_label.setToolTip(
+            _("Set the line color for plotted objects.")
+        )
+        self.line_color_entry = FCEntry()
+        self.line_color_button = QtWidgets.QPushButton()
+        self.line_color_button.setFixedSize(15, 15)
+
+        self.form_box_child_2 = QtWidgets.QHBoxLayout()
+        self.form_box_child_2.addWidget(self.line_color_entry)
+        self.form_box_child_2.addWidget(self.line_color_button)
+        self.form_box_child_2.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
+
+        grid2.addWidget(self.line_color_label, 13, 0)
+        grid2.addLayout(self.form_box_child_2, 13, 1)
+
+        # Plot Fill Color
+        self.fill_color_label = QtWidgets.QLabel('%s:' % _('Fill'))
+        self.fill_color_label.setToolTip(
+            _("Set the fill color for plotted objects.\n"
+              "First 6 digits are the color and the last 2\n"
+              "digits are for alpha (transparency) level.")
+        )
+        self.fill_color_entry = FCEntry()
+        self.fill_color_button = QtWidgets.QPushButton()
+        self.fill_color_button.setFixedSize(15, 15)
+
+        self.form_box_child_1 = QtWidgets.QHBoxLayout()
+        self.form_box_child_1.addWidget(self.fill_color_entry)
+        self.form_box_child_1.addWidget(self.fill_color_button)
+        self.form_box_child_1.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
+
+        grid2.addWidget(self.fill_color_label, 14, 0)
+        grid2.addLayout(self.form_box_child_1, 14, 1)
+
+        # Plot Fill Transparency Level
+        self.alpha_label = QtWidgets.QLabel('%s:' % _('Alpha'))
+        self.alpha_label.setToolTip(
+            _("Set the fill transparency for plotted objects.")
+        )
+        self.color_alpha_slider = QtWidgets.QSlider(QtCore.Qt.Horizontal)
+        self.color_alpha_slider.setMinimum(0)
+        self.color_alpha_slider.setMaximum(255)
+        self.color_alpha_slider.setSingleStep(1)
+
+        self.color_alpha_spinner = FCSpinner()
+        self.color_alpha_spinner.setMinimumWidth(70)
+        self.color_alpha_spinner.set_range(0, 255)
+
+        self.form_box_child_3 = QtWidgets.QHBoxLayout()
+        self.form_box_child_3.addWidget(self.color_alpha_slider)
+        self.form_box_child_3.addWidget(self.color_alpha_spinner)
+
+        grid2.addWidget(self.alpha_label, 15, 0)
+        grid2.addLayout(self.form_box_child_3, 15, 1)
+
+        self.layout.addStretch()
+
+        current_platform = platform.architecture()[0]
+        if current_platform == '64bit':
+            self.excellon_optimization_label.setDisabled(False)
+            self.excellon_optimization_radio.setDisabled(False)
+            self.optimization_time_label.setDisabled(False)
+            self.optimization_time_entry.setDisabled(False)
+            self.excellon_optimization_radio.activated_custom.connect(self.optimization_selection)
+
+        else:
+            self.excellon_optimization_label.setDisabled(True)
+            self.excellon_optimization_radio.setDisabled(True)
+            self.optimization_time_label.setDisabled(True)
+            self.optimization_time_entry.setDisabled(True)
+
+        # Setting plot colors signals
+        self.line_color_entry.editingFinished.connect(self.on_line_color_entry)
+        self.line_color_button.clicked.connect(self.on_line_color_button)
+        self.fill_color_entry.editingFinished.connect(self.on_fill_color_entry)
+        self.fill_color_button.clicked.connect(self.on_fill_color_button)
+        self.color_alpha_spinner.valueChanged.connect(self.on_color_spinner)
+        self.color_alpha_slider.valueChanged.connect(self.on_color_slider)
+
+        # Load the defaults values into the Excellon Format and Excellon Zeros fields
+        self.excellon_defaults_button.clicked.connect(self.on_excellon_defaults_button)
+
+    def optimization_selection(self):
+        if self.excellon_optimization_radio.get_value() == 'M':
+            self.optimization_time_label.setDisabled(False)
+            self.optimization_time_entry.setDisabled(False)
+        else:
+            self.optimization_time_label.setDisabled(True)
+            self.optimization_time_entry.setDisabled(True)
+
+    # Setting plot colors handlers
+    def on_fill_color_entry(self):
+        self.app.defaults['excellon_plot_fill'] = self.fill_color_entry.get_value()[:7] + \
+            self.app.defaults['excellon_plot_fill'][7:9]
+        self.fill_color_button.setStyleSheet("background-color:%s" % str(self.app.defaults['excellon_plot_fill'])[:7])
+
+    def on_fill_color_button(self):
+        current_color = QtGui.QColor(self.app.defaults['excellon_plot_fill'][:7])
+
+        c_dialog = QtWidgets.QColorDialog()
+        plot_fill_color = c_dialog.getColor(initial=current_color)
+
+        if plot_fill_color.isValid() is False:
+            return
+
+        self.fill_color_button.setStyleSheet("background-color:%s" % str(plot_fill_color.name()))
+
+        new_val = str(plot_fill_color.name()) + str(self.app.defaults['excellon_plot_fill'][7:9])
+        self.fill_color_entry.set_value(new_val)
+        self.app.defaults['excellon_plot_fill'] = new_val
+
+    def on_color_spinner(self):
+        spinner_value = self.color_alpha_spinner.value()
+        self.color_alpha_slider.setValue(spinner_value)
+        self.app.defaults['excellon_plot_fill'] = \
+            self.app.defaults['excellon_plot_fill'][:7] + \
+            (hex(spinner_value)[2:] if int(hex(spinner_value)[2:], 16) > 0 else '00')
+        self.app.defaults['excellon_plot_line'] = \
+            self.app.defaults['excellon_plot_line'][:7] + \
+            (hex(spinner_value)[2:] if int(hex(spinner_value)[2:], 16) > 0 else '00')
+
+    def on_color_slider(self):
+        slider_value = self.color_alpha_slider.value()
+        self.color_alpha_spinner.setValue(slider_value)
+
+    def on_line_color_entry(self):
+        self.app.defaults['excellon_plot_line'] = self.line_color_entry.get_value()[:7] + \
+                                                self.app.defaults['excellon_plot_line'][7:9]
+        self.line_color_button.setStyleSheet("background-color:%s" % str(self.app.defaults['excellon_plot_line'])[:7])
+
+    def on_line_color_button(self):
+        current_color = QtGui.QColor(self.app.defaults['excellon_plot_line'][:7])
+        # print(current_color)
+
+        c_dialog = QtWidgets.QColorDialog()
+        plot_line_color = c_dialog.getColor(initial=current_color)
+
+        if plot_line_color.isValid() is False:
+            return
+
+        self.line_color_button.setStyleSheet("background-color:%s" % str(plot_line_color.name()))
+
+        new_val_line = str(plot_line_color.name()) + str(self.app.defaults['excellon_plot_line'][7:9])
+        self.line_color_entry.set_value(new_val_line)
+        self.app.defaults['excellon_plot_line'] = new_val_line
+
+    def on_excellon_defaults_button(self):
+        self.app.preferencesUiManager.defaults_form_fields["excellon_format_lower_in"].set_value('4')
+        self.app.preferencesUiManager.defaults_form_fields["excellon_format_upper_in"].set_value('2')
+        self.app.preferencesUiManager.defaults_form_fields["excellon_format_lower_mm"].set_value('3')
+        self.app.preferencesUiManager.defaults_form_fields["excellon_format_upper_mm"].set_value('3')
+        self.app.preferencesUiManager.defaults_form_fields["excellon_zeros"].set_value('L')
+        self.app.preferencesUiManager.defaults_form_fields["excellon_units"].set_value('INCH')

+ 317 - 0
flatcamGUI/preferences/excellon/ExcellonOptPrefGroupUI.py

@@ -0,0 +1,317 @@
+from PyQt5 import QtWidgets
+from PyQt5.QtCore import Qt, QSettings
+
+from flatcamGUI.GUIElements import RadioSet, FCDoubleSpinner, FCCheckBox, FCEntry, FCSpinner, OptionalInputSection, \
+    FCComboBox
+from flatcamGUI.preferences import machinist_setting
+from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
+import gettext
+import FlatCAMTranslation as fcTranslate
+import builtins
+
+fcTranslate.apply_language('strings')
+if '_' not in builtins.__dict__:
+    _ = gettext.gettext
+
+settings = QSettings("Open Source", "FlatCAM")
+if settings.contains("machinist"):
+    machinist_setting = settings.value('machinist', type=int)
+else:
+    machinist_setting = 0
+
+
+class ExcellonOptPrefGroupUI(OptionsGroupUI):
+
+    def __init__(self, decimals=4, parent=None):
+        # OptionsGroupUI.__init__(self, "Excellon Options", parent=parent)
+        super(ExcellonOptPrefGroupUI, self).__init__(self, parent=parent)
+
+        self.setTitle(str(_("Excellon Options")))
+        self.decimals = decimals
+
+        # ## Create CNC Job
+        self.cncjob_label = QtWidgets.QLabel('<b>%s</b>' % _('Create CNC Job'))
+        self.cncjob_label.setToolTip(
+            _("Parameters used to create a CNC Job object\n"
+              "for this drill object.")
+        )
+        self.layout.addWidget(self.cncjob_label)
+
+        grid2 = QtWidgets.QGridLayout()
+        self.layout.addLayout(grid2)
+        grid2.setColumnStretch(0, 0)
+        grid2.setColumnStretch(1, 1)
+
+        # Operation Type
+        self.operation_label = QtWidgets.QLabel('<b>%s:</b>' % _('Operation'))
+        self.operation_label.setToolTip(
+            _("Operation type:\n"
+              "- Drilling -> will drill the drills/slots associated with this tool\n"
+              "- Milling -> will mill the drills/slots")
+        )
+        self.operation_radio = RadioSet(
+            [
+                {'label': _('Drilling'), 'value': 'drill'},
+                {'label': _("Milling"), 'value': 'mill'}
+            ]
+        )
+
+        grid2.addWidget(self.operation_label, 0, 0)
+        grid2.addWidget(self.operation_radio, 0, 1)
+
+        self.mill_type_label = QtWidgets.QLabel('%s:' % _('Milling Type'))
+        self.mill_type_label.setToolTip(
+            _("Milling type:\n"
+              "- Drills -> will mill the drills associated with this tool\n"
+              "- Slots -> will mill the slots associated with this tool\n"
+              "- Both -> will mill both drills and mills or whatever is available")
+        )
+        self.milling_type_radio = RadioSet(
+            [
+                {'label': _('Drills'), 'value': 'drills'},
+                {'label': _("Slots"), 'value': 'slots'},
+                {'label': _("Both"), 'value': 'both'},
+            ]
+        )
+
+        grid2.addWidget(self.mill_type_label, 1, 0)
+        grid2.addWidget(self.milling_type_radio, 1, 1)
+
+        self.mill_dia_label = QtWidgets.QLabel('%s:' % _('Milling Diameter'))
+        self.mill_dia_label.setToolTip(
+            _("The diameter of the tool who will do the milling")
+        )
+
+        self.mill_dia_entry = FCDoubleSpinner()
+        self.mill_dia_entry.set_precision(self.decimals)
+        self.mill_dia_entry.set_range(0.0000, 9999.9999)
+
+        grid2.addWidget(self.mill_dia_label, 2, 0)
+        grid2.addWidget(self.mill_dia_entry, 2, 1)
+
+        # Cut Z
+        cutzlabel = QtWidgets.QLabel('%s:' % _('Cut Z'))
+        cutzlabel.setToolTip(
+            _("Drill depth (negative)\n"
+              "below the copper surface.")
+        )
+
+        self.cutz_entry = FCDoubleSpinner()
+
+        if machinist_setting == 0:
+            self.cutz_entry.set_range(-9999.9999, 0.0000)
+        else:
+            self.cutz_entry.set_range(-9999.9999, 9999.9999)
+
+        self.cutz_entry.setSingleStep(0.1)
+        self.cutz_entry.set_precision(self.decimals)
+
+        grid2.addWidget(cutzlabel, 3, 0)
+        grid2.addWidget(self.cutz_entry, 3, 1)
+
+        # Multi-Depth
+        self.mpass_cb = FCCheckBox('%s:' % _("Multi-Depth"))
+        self.mpass_cb.setToolTip(
+            _(
+                "Use multiple passes to limit\n"
+                "the cut depth in each pass. Will\n"
+                "cut multiple times until Cut Z is\n"
+                "reached."
+            )
+        )
+
+        self.maxdepth_entry = FCDoubleSpinner()
+        self.maxdepth_entry.set_precision(self.decimals)
+        self.maxdepth_entry.set_range(0, 9999.9999)
+        self.maxdepth_entry.setSingleStep(0.1)
+
+        self.maxdepth_entry.setToolTip(_("Depth of each pass (positive)."))
+
+        grid2.addWidget(self.mpass_cb, 4, 0)
+        grid2.addWidget(self.maxdepth_entry, 4, 1)
+
+        # Travel Z
+        travelzlabel = QtWidgets.QLabel('%s:' % _('Travel Z'))
+        travelzlabel.setToolTip(
+            _("Tool height when travelling\n"
+              "across the XY plane.")
+        )
+
+        self.travelz_entry = FCDoubleSpinner()
+        self.travelz_entry.set_precision(self.decimals)
+
+        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(travelzlabel, 5, 0)
+        grid2.addWidget(self.travelz_entry, 5, 1)
+
+        # Tool change:
+        self.toolchange_cb = FCCheckBox('%s' % _("Tool change"))
+        self.toolchange_cb.setToolTip(
+            _("Include tool-change sequence\n"
+              "in G-Code (Pause for tool change).")
+        )
+        grid2.addWidget(self.toolchange_cb, 6, 0, 1, 2)
+
+        # Tool Change Z
+        toolchangezlabel = QtWidgets.QLabel('%s:' % _('Toolchange Z'))
+        toolchangezlabel.setToolTip(
+            _("Z-axis position (height) for\n"
+              "tool change.")
+        )
+
+        self.toolchangez_entry = FCDoubleSpinner()
+        self.toolchangez_entry.set_precision(self.decimals)
+
+        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(toolchangezlabel, 7, 0)
+        grid2.addWidget(self.toolchangez_entry, 7, 1)
+
+        # End Move Z
+        endz_label = QtWidgets.QLabel('%s:' % _('End move Z'))
+        endz_label.setToolTip(
+            _("Height of the tool after\n"
+              "the last move at the end of the job.")
+        )
+        self.endz_entry = FCDoubleSpinner()
+        self.endz_entry.set_precision(self.decimals)
+
+        if machinist_setting == 0:
+            self.endz_entry.set_range(0.0000, 9999.9999)
+        else:
+            self.endz_entry.set_range(-9999.9999, 9999.9999)
+
+        grid2.addWidget(endz_label, 8, 0)
+        grid2.addWidget(self.endz_entry, 8, 1)
+
+        # End Move X,Y
+        endmove_xy_label = QtWidgets.QLabel('%s:' % _('End move X,Y'))
+        endmove_xy_label.setToolTip(
+            _("End move X,Y position. In format (x,y).\n"
+              "If no value is entered then there is no move\n"
+              "on X,Y plane at the end of the job.")
+        )
+        self.endxy_entry = FCEntry()
+
+        grid2.addWidget(endmove_xy_label, 9, 0)
+        grid2.addWidget(self.endxy_entry, 9, 1)
+
+        # Feedrate Z
+        frlabel = QtWidgets.QLabel('%s:' % _('Feedrate Z'))
+        frlabel.setToolTip(
+            _("Tool speed while drilling\n"
+              "(in units per minute).\n"
+              "So called 'Plunge' feedrate.\n"
+              "This is for linear move G01.")
+        )
+        self.feedrate_z_entry = FCDoubleSpinner()
+        self.feedrate_z_entry.set_precision(self.decimals)
+        self.feedrate_z_entry.set_range(0, 99999.9999)
+
+        grid2.addWidget(frlabel, 10, 0)
+        grid2.addWidget(self.feedrate_z_entry, 10, 1)
+
+        # Spindle speed
+        spdlabel = QtWidgets.QLabel('%s:' % _('Spindle Speed'))
+        spdlabel.setToolTip(
+            _("Speed of the spindle\n"
+              "in RPM (optional)")
+        )
+
+        self.spindlespeed_entry = FCSpinner()
+        self.spindlespeed_entry.set_range(0, 1000000)
+        self.spindlespeed_entry.set_step(100)
+
+        grid2.addWidget(spdlabel, 11, 0)
+        grid2.addWidget(self.spindlespeed_entry, 11, 1)
+
+        # Dwell
+        self.dwell_cb = FCCheckBox('%s' % _('Enable Dwell'))
+        self.dwell_cb .setToolTip(
+            _("Pause to allow the spindle to reach its\n"
+              "speed before cutting.")
+        )
+
+        grid2.addWidget(self.dwell_cb, 12, 0, 1, 2)
+
+        # Dwell Time
+        dwelltime = QtWidgets.QLabel('%s:' % _('Duration'))
+        dwelltime.setToolTip(_("Number of time units for spindle to dwell."))
+        self.dwelltime_entry = FCDoubleSpinner()
+        self.dwelltime_entry.set_precision(self.decimals)
+        self.dwelltime_entry.set_range(0, 99999.9999)
+
+        grid2.addWidget(dwelltime, 13, 0)
+        grid2.addWidget(self.dwelltime_entry, 13, 1)
+
+        self.ois_dwell_exc = OptionalInputSection(self.dwell_cb, [self.dwelltime_entry])
+
+        # preprocessor selection
+        pp_excellon_label = QtWidgets.QLabel('%s:' % _("Preprocessor"))
+        pp_excellon_label.setToolTip(
+            _("The preprocessor JSON file that dictates\n"
+              "Gcode output.")
+        )
+
+        self.pp_excellon_name_cb = FCComboBox()
+        self.pp_excellon_name_cb.setFocusPolicy(Qt.StrongFocus)
+
+        grid2.addWidget(pp_excellon_label, 14, 0)
+        grid2.addWidget(self.pp_excellon_name_cb, 14, 1)
+
+        # ### Choose what to use for Gcode creation: Drills, Slots or Both
+        excellon_gcode_type_label = QtWidgets.QLabel('<b>%s</b>' % _('Gcode'))
+        excellon_gcode_type_label.setToolTip(
+            _("Choose what to use for GCode generation:\n"
+              "'Drills', 'Slots' or 'Both'.\n"
+              "When choosing 'Slots' or 'Both', slots will be\n"
+              "converted to drills.")
+        )
+        self.excellon_gcode_type_radio = RadioSet([{'label': 'Drills', 'value': 'drills'},
+                                                   {'label': 'Slots', 'value': 'slots'},
+                                                   {'label': 'Both', 'value': 'both'}])
+        grid2.addWidget(excellon_gcode_type_label, 15, 0)
+        grid2.addWidget(self.excellon_gcode_type_radio, 15, 1)
+
+        # until I decide to implement this feature those remain disabled
+        excellon_gcode_type_label.hide()
+        self.excellon_gcode_type_radio.setVisible(False)
+
+        # ### Milling Holes ## ##
+        self.mill_hole_label = QtWidgets.QLabel('<b>%s</b>' % _('Mill Holes'))
+        self.mill_hole_label.setToolTip(
+            _("Create Geometry for milling holes.")
+        )
+        grid2.addWidget(self.mill_hole_label, 16, 0, 1, 2)
+
+        tdlabel = QtWidgets.QLabel('%s:' % _('Drill Tool dia'))
+        tdlabel.setToolTip(
+            _("Diameter of the cutting tool.")
+        )
+        self.tooldia_entry = FCDoubleSpinner()
+        self.tooldia_entry.set_precision(self.decimals)
+        self.tooldia_entry.set_range(0, 999.9999)
+
+        grid2.addWidget(tdlabel, 18, 0)
+        grid2.addWidget(self.tooldia_entry, 18, 1)
+
+        stdlabel = QtWidgets.QLabel('%s:' % _('Slot Tool dia'))
+        stdlabel.setToolTip(
+            _("Diameter of the cutting tool\n"
+              "when milling slots.")
+        )
+        self.slot_tooldia_entry = FCDoubleSpinner()
+        self.slot_tooldia_entry.set_precision(self.decimals)
+        self.slot_tooldia_entry.set_range(0, 999.9999)
+
+        grid2.addWidget(stdlabel, 21, 0)
+        grid2.addWidget(self.slot_tooldia_entry, 21, 1)
+
+        self.layout.addStretch()

+ 53 - 0
flatcamGUI/preferences/excellon/ExcellonPreferencesUI.py

@@ -0,0 +1,53 @@
+from PyQt5 import QtWidgets
+from PyQt5.QtCore import QSettings
+
+from flatcamGUI.preferences.excellon.ExcellonEditorPrefGroupUI import ExcellonEditorPrefGroupUI
+from flatcamGUI.preferences.excellon.ExcellonExpPrefGroupUI import ExcellonExpPrefGroupUI
+from flatcamGUI.preferences.excellon.ExcellonAdvOptPrefGroupUI import ExcellonAdvOptPrefGroupUI
+from flatcamGUI.preferences.excellon.ExcellonOptPrefGroupUI import ExcellonOptPrefGroupUI
+from flatcamGUI.preferences.excellon.ExcellonGenPrefGroupUI import ExcellonGenPrefGroupUI
+
+import gettext
+import FlatCAMTranslation as fcTranslate
+import builtins
+
+fcTranslate.apply_language('strings')
+if '_' not in builtins.__dict__:
+    _ = gettext.gettext
+
+settings = QSettings("Open Source", "FlatCAM")
+if settings.contains("machinist"):
+    machinist_setting = settings.value('machinist', type=int)
+else:
+    machinist_setting = 0
+
+
+class ExcellonPreferencesUI(QtWidgets.QWidget):
+
+    def __init__(self, decimals, parent=None):
+        QtWidgets.QWidget.__init__(self, parent=parent)
+        self.layout = QtWidgets.QHBoxLayout()
+        self.setLayout(self.layout)
+        self.decimals = decimals
+
+        self.excellon_gen_group = ExcellonGenPrefGroupUI(decimals=self.decimals)
+        self.excellon_gen_group.setMinimumWidth(220)
+        self.excellon_opt_group = ExcellonOptPrefGroupUI(decimals=self.decimals)
+        self.excellon_opt_group.setMinimumWidth(290)
+        self.excellon_exp_group = ExcellonExpPrefGroupUI(decimals=self.decimals)
+        self.excellon_exp_group.setMinimumWidth(250)
+        self.excellon_adv_opt_group = ExcellonAdvOptPrefGroupUI(decimals=self.decimals)
+        self.excellon_adv_opt_group.setMinimumWidth(250)
+        self.excellon_editor_group = ExcellonEditorPrefGroupUI(decimals=self.decimals)
+        self.excellon_editor_group.setMinimumWidth(260)
+
+        self.vlay = QtWidgets.QVBoxLayout()
+        self.vlay.addWidget(self.excellon_opt_group)
+        self.vlay.addWidget(self.excellon_exp_group)
+
+        self.layout.addWidget(self.excellon_gen_group)
+        self.layout.addLayout(self.vlay)
+        self.layout.addWidget(self.excellon_adv_opt_group)
+        self.layout.addWidget(self.excellon_editor_group)
+
+        self.layout.addStretch()

+ 0 - 0
flatcamGUI/preferences/excellon/__init__.py


+ 483 - 0
flatcamGUI/preferences/general/GeneralAPPSetGroupUI.py

@@ -0,0 +1,483 @@
+from PyQt5 import QtCore, QtWidgets, QtGui
+from PyQt5.QtCore import QSettings
+
+from flatcamGUI.GUIElements import FCDoubleSpinner, FCCheckBox, FCComboBox, RadioSet, OptionalInputSection, FCSpinner, \
+    FCEntry
+from flatcamGUI.preferences import settings
+from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
+
+import gettext
+import FlatCAMTranslation as fcTranslate
+import builtins
+
+fcTranslate.apply_language('strings')
+if '_' not in builtins.__dict__:
+    _ = gettext.gettext
+
+settings = QSettings("Open Source", "FlatCAM")
+if settings.contains("machinist"):
+    machinist_setting = settings.value('machinist', type=int)
+else:
+    machinist_setting = 0
+
+
+class GeneralAPPSetGroupUI(OptionsGroupUI):
+    def __init__(self, decimals=4, parent=None):
+        super(GeneralAPPSetGroupUI, self).__init__(self, parent=parent)
+
+        self.setTitle(str(_("App Settings")))
+        self.decimals = decimals
+
+        theme_settings = QtCore.QSettings("Open Source", "FlatCAM")
+        if theme_settings.contains("theme"):
+            theme = theme_settings.value('theme', type=str)
+        else:
+            theme = 'white'
+
+        if theme == 'white':
+            self.resource_loc = 'assets/resources'
+        else:
+            self.resource_loc = 'assets/resources'
+
+        # Create a grid layout for the Application general settings
+        grid0 = QtWidgets.QGridLayout()
+        self.layout.addLayout(grid0)
+        grid0.setColumnStretch(0, 0)
+        grid0.setColumnStretch(1, 1)
+
+        # GRID Settings
+        self.grid_label = QtWidgets.QLabel('<b>%s</b>' % _('Grid Settings'))
+        grid0.addWidget(self.grid_label, 0, 0, 1, 2)
+
+        # Grid X Entry
+        self.gridx_label = QtWidgets.QLabel('%s:' % _('X value'))
+        self.gridx_label.setToolTip(
+           _("This is the Grid snap value on X axis.")
+        )
+        self.gridx_entry = FCDoubleSpinner()
+        self.gridx_entry.set_precision(self.decimals)
+        self.gridx_entry.setSingleStep(0.1)
+
+        grid0.addWidget(self.gridx_label, 1, 0)
+        grid0.addWidget(self.gridx_entry, 1, 1)
+
+        # Grid Y Entry
+        self.gridy_label = QtWidgets.QLabel('%s:' % _('Y value'))
+        self.gridy_label.setToolTip(
+            _("This is the Grid snap value on Y axis.")
+        )
+        self.gridy_entry = FCDoubleSpinner()
+        self.gridy_entry.set_precision(self.decimals)
+        self.gridy_entry.setSingleStep(0.1)
+
+        grid0.addWidget(self.gridy_label, 2, 0)
+        grid0.addWidget(self.gridy_entry, 2, 1)
+
+        # Snap Max Entry
+        self.snap_max_label = QtWidgets.QLabel('%s:' % _('Snap Max'))
+        self.snap_max_label.setToolTip(_("Max. magnet distance"))
+        self.snap_max_dist_entry = FCDoubleSpinner()
+        self.snap_max_dist_entry.set_precision(self.decimals)
+        self.snap_max_dist_entry.setSingleStep(0.1)
+
+        grid0.addWidget(self.snap_max_label, 3, 0)
+        grid0.addWidget(self.snap_max_dist_entry, 3, 1)
+
+        separator_line = QtWidgets.QFrame()
+        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
+        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
+        grid0.addWidget(separator_line, 4, 0, 1, 2)
+
+        # Workspace
+        self.workspace_label = QtWidgets.QLabel('<b>%s</b>' % _('Workspace Settings'))
+        grid0.addWidget(self.workspace_label, 5, 0, 1, 2)
+
+        self.workspace_cb = FCCheckBox('%s' % _('Active'))
+        self.workspace_cb.setToolTip(
+           _("Draw a delimiting rectangle on canvas.\n"
+             "The purpose is to illustrate the limits for our work.")
+        )
+
+        grid0.addWidget(self.workspace_cb, 6, 0, 1, 2)
+
+        self.workspace_type_lbl = QtWidgets.QLabel('%s:' % _('Size'))
+        self.workspace_type_lbl.setToolTip(
+           _("Select the type of rectangle to be used on canvas,\n"
+             "as valid workspace.")
+        )
+        self.wk_cb = FCComboBox()
+
+        grid0.addWidget(self.workspace_type_lbl, 7, 0)
+        grid0.addWidget(self.wk_cb, 7, 1)
+
+        self.pagesize = {}
+        self.pagesize.update(
+            {
+                'A0': (841, 1189),
+                'A1': (594, 841),
+                'A2': (420, 594),
+                'A3': (297, 420),
+                'A4': (210, 297),
+                'A5': (148, 210),
+                'A6': (105, 148),
+                'A7': (74, 105),
+                'A8': (52, 74),
+                'A9': (37, 52),
+                'A10': (26, 37),
+
+                'B0': (1000, 1414),
+                'B1': (707, 1000),
+                'B2': (500, 707),
+                'B3': (353, 500),
+                'B4': (250, 353),
+                'B5': (176, 250),
+                'B6': (125, 176),
+                'B7': (88, 125),
+                'B8': (62, 88),
+                'B9': (44, 62),
+                'B10': (31, 44),
+
+                'C0': (917, 1297),
+                'C1': (648, 917),
+                'C2': (458, 648),
+                'C3': (324, 458),
+                'C4': (229, 324),
+                'C5': (162, 229),
+                'C6': (114, 162),
+                'C7': (81, 114),
+                'C8': (57, 81),
+                'C9': (40, 57),
+                'C10': (28, 40),
+
+                # American paper sizes
+                'LETTER': (8.5, 11),
+                'LEGAL': (8.5, 14),
+                'ELEVENSEVENTEEN': (11, 17),
+
+                # From https://en.wikipedia.org/wiki/Paper_size
+                'JUNIOR_LEGAL': (5, 8),
+                'HALF_LETTER': (5.5, 8),
+                'GOV_LETTER': (8, 10.5),
+                'GOV_LEGAL': (8.5, 13),
+                'LEDGER': (17, 11),
+            }
+        )
+
+        page_size_list = list(self.pagesize.keys())
+
+        self.wk_cb.addItems(page_size_list)
+
+        # Page orientation
+        self.wk_orientation_label = QtWidgets.QLabel('%s:' % _("Orientation"))
+        self.wk_orientation_label.setToolTip(_("Can be:\n"
+                                               "- Portrait\n"
+                                               "- Landscape"))
+
+        self.wk_orientation_radio = RadioSet([{'label': _('Portrait'), 'value': 'p'},
+                                              {'label': _('Landscape'), 'value': 'l'},
+                                              ], stretch=False)
+
+        self.wks = OptionalInputSection(self.workspace_cb,
+                                        [
+                                            self.workspace_type_lbl,
+                                            self.wk_cb,
+                                            self.wk_orientation_label,
+                                            self.wk_orientation_radio
+                                        ])
+
+        grid0.addWidget(self.wk_orientation_label, 8, 0)
+        grid0.addWidget(self.wk_orientation_radio, 8, 1)
+
+        separator_line = QtWidgets.QFrame()
+        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
+        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
+        grid0.addWidget(separator_line, 9, 0, 1, 2)
+
+        # Font Size
+        self.font_size_label = QtWidgets.QLabel('<b>%s</b>' % _('Font Size'))
+        grid0.addWidget(self.font_size_label, 10, 0, 1, 2)
+
+        # Notebook Font Size
+        self.notebook_font_size_label = QtWidgets.QLabel('%s:' % _('Notebook'))
+        self.notebook_font_size_label.setToolTip(
+            _("This sets the font size for the elements found in the Notebook.\n"
+              "The notebook is the collapsible area in the left side of the GUI,\n"
+              "and include the Project, Selected and Tool tabs.")
+        )
+
+        self.notebook_font_size_spinner = FCSpinner()
+        self.notebook_font_size_spinner.set_range(8, 40)
+        self.notebook_font_size_spinner.setWrapping(True)
+
+        qsettings = QSettings("Open Source", "FlatCAM")
+        if qsettings.contains("notebook_font_size"):
+            self.notebook_font_size_spinner.set_value(qsettings.value('notebook_font_size', type=int))
+        else:
+            self.notebook_font_size_spinner.set_value(12)
+
+        grid0.addWidget(self.notebook_font_size_label, 11, 0)
+        grid0.addWidget(self.notebook_font_size_spinner, 11, 1)
+
+        # Axis Font Size
+        self.axis_font_size_label = QtWidgets.QLabel('%s:' % _('Axis'))
+        self.axis_font_size_label.setToolTip(
+            _("This sets the font size for canvas axis.")
+        )
+
+        self.axis_font_size_spinner = FCSpinner()
+        self.axis_font_size_spinner.set_range(0, 40)
+        self.axis_font_size_spinner.setWrapping(True)
+
+        qsettings = QSettings("Open Source", "FlatCAM")
+        if qsettings.contains("axis_font_size"):
+            self.axis_font_size_spinner.set_value(qsettings.value('axis_font_size', type=int))
+        else:
+            self.axis_font_size_spinner.set_value(8)
+
+        grid0.addWidget(self.axis_font_size_label, 12, 0)
+        grid0.addWidget(self.axis_font_size_spinner, 12, 1)
+
+        # TextBox Font Size
+        self.textbox_font_size_label = QtWidgets.QLabel('%s:' % _('Textbox'))
+        self.textbox_font_size_label.setToolTip(
+            _("This sets the font size for the Textbox GUI\n"
+              "elements that are used in FlatCAM.")
+        )
+
+        self.textbox_font_size_spinner = FCSpinner()
+        self.textbox_font_size_spinner.set_range(8, 40)
+        self.textbox_font_size_spinner.setWrapping(True)
+
+        qsettings = QSettings("Open Source", "FlatCAM")
+        if qsettings.contains("textbox_font_size"):
+            self.textbox_font_size_spinner.set_value(settings.value('textbox_font_size', type=int))
+        else:
+            self.textbox_font_size_spinner.set_value(10)
+
+        grid0.addWidget(self.textbox_font_size_label, 13, 0)
+        grid0.addWidget(self.textbox_font_size_spinner, 13, 1)
+
+        separator_line = QtWidgets.QFrame()
+        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
+        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
+        grid0.addWidget(separator_line, 14, 0, 1, 2)
+
+        # -----------------------------------------------------------
+        # -------------- MOUSE SETTINGS -----------------------------
+        # -----------------------------------------------------------
+
+        self.mouse_lbl = QtWidgets.QLabel('<b>%s</b>' % _('Mouse Settings'))
+        grid0.addWidget(self.mouse_lbl, 21, 0, 1, 2)
+
+        # Mouse Cursor Shape
+        self.cursor_lbl = QtWidgets.QLabel('%s:' % _('Cursor Shape'))
+        self.cursor_lbl.setToolTip(
+           _("Choose a mouse cursor shape.\n"
+             "- Small -> with a customizable size.\n"
+             "- Big -> Infinite lines")
+        )
+
+        self.cursor_radio = RadioSet([
+            {"label": _("Small"), "value": "small"},
+            {"label": _("Big"), "value": "big"}
+        ], orientation='horizontal', stretch=False)
+
+        grid0.addWidget(self.cursor_lbl, 22, 0)
+        grid0.addWidget(self.cursor_radio, 22, 1)
+
+        # Mouse Cursor Size
+        self.cursor_size_lbl = QtWidgets.QLabel('%s:' % _('Cursor Size'))
+        self.cursor_size_lbl.setToolTip(
+           _("Set the size of the mouse cursor, in pixels.")
+        )
+
+        self.cursor_size_entry = FCSpinner()
+        self.cursor_size_entry.set_range(10, 70)
+        self.cursor_size_entry.setWrapping(True)
+
+        grid0.addWidget(self.cursor_size_lbl, 23, 0)
+        grid0.addWidget(self.cursor_size_entry, 23, 1)
+
+        # Cursor Width
+        self.cursor_width_lbl = QtWidgets.QLabel('%s:' % _('Cursor Width'))
+        self.cursor_width_lbl.setToolTip(
+           _("Set the line width of the mouse cursor, in pixels.")
+        )
+
+        self.cursor_width_entry = FCSpinner()
+        self.cursor_width_entry.set_range(1, 10)
+        self.cursor_width_entry.setWrapping(True)
+
+        grid0.addWidget(self.cursor_width_lbl, 24, 0)
+        grid0.addWidget(self.cursor_width_entry, 24, 1)
+
+        # Cursor Color Enable
+        self.mouse_cursor_color_cb = FCCheckBox(label='%s' % _('Cursor Color'))
+        self.mouse_cursor_color_cb.setToolTip(
+            _("Check this box to color mouse cursor.")
+        )
+        grid0.addWidget(self.mouse_cursor_color_cb, 25, 0, 1, 2)
+
+        # Cursor Color
+        self.mouse_color_label = QtWidgets.QLabel('%s:' % _('Cursor Color'))
+        self.mouse_color_label.setToolTip(
+            _("Set the color of the mouse cursor.")
+        )
+        self.mouse_cursor_entry = FCEntry()
+        self.mouse_cursor_button = QtWidgets.QPushButton()
+        self.mouse_cursor_button.setFixedSize(15, 15)
+
+        self.form_box_child_1 = QtWidgets.QHBoxLayout()
+        self.form_box_child_1.addWidget(self.mouse_cursor_entry)
+        self.form_box_child_1.addWidget(self.mouse_cursor_button)
+        self.form_box_child_1.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
+
+        grid0.addWidget(self.mouse_color_label, 26, 0)
+        grid0.addLayout(self.form_box_child_1, 26, 1)
+
+        self.mois = OptionalInputSection(
+            self.mouse_cursor_color_cb,
+            [
+                self.mouse_color_label,
+                self.mouse_cursor_entry,
+                self.mouse_cursor_button
+            ]
+        )
+        # Select mouse pan button
+        self.panbuttonlabel = QtWidgets.QLabel('%s:' % _('Pan Button'))
+        self.panbuttonlabel.setToolTip(
+            _("Select the mouse button to use for panning:\n"
+              "- MMB --> Middle Mouse Button\n"
+              "- RMB --> Right Mouse Button")
+        )
+        self.pan_button_radio = RadioSet([{'label': _('MMB'), 'value': '3'},
+                                          {'label': _('RMB'), 'value': '2'}])
+
+        grid0.addWidget(self.panbuttonlabel, 27, 0)
+        grid0.addWidget(self.pan_button_radio, 27, 1)
+
+        # Multiple Selection Modifier Key
+        self.mselectlabel = QtWidgets.QLabel('%s:' % _('Multiple Selection'))
+        self.mselectlabel.setToolTip(
+            _("Select the key used for multiple selection.")
+        )
+        self.mselect_radio = RadioSet([{'label': _('CTRL'), 'value': 'Control'},
+                                       {'label': _('SHIFT'), 'value': 'Shift'}])
+
+        grid0.addWidget(self.mselectlabel, 28, 0)
+        grid0.addWidget(self.mselect_radio, 28, 1)
+
+        separator_line = QtWidgets.QFrame()
+        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
+        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
+        grid0.addWidget(separator_line, 29, 0, 1, 2)
+
+        # Delete confirmation
+        self.delete_conf_cb = FCCheckBox(_('Delete object confirmation'))
+        self.delete_conf_cb.setToolTip(
+            _("When checked the application will ask for user confirmation\n"
+              "whenever the Delete object(s) event is triggered, either by\n"
+              "menu shortcut or key shortcut.")
+        )
+        grid0.addWidget(self.delete_conf_cb, 30, 0, 1, 2)
+
+        # Open behavior
+        self.open_style_cb = FCCheckBox('%s' % _('"Open" behavior'))
+        self.open_style_cb.setToolTip(
+            _("When checked the path for the last saved file is used when saving files,\n"
+              "and the path for the last opened file is used when opening files.\n\n"
+              "When unchecked the path for opening files is the one used last: either the\n"
+              "path for saving files or the path for opening files.")
+        )
+
+        grid0.addWidget(self.open_style_cb, 31, 0, 1, 2)
+
+        # Enable/Disable ToolTips globally
+        self.toggle_tooltips_cb = FCCheckBox(label=_('Enable ToolTips'))
+        self.toggle_tooltips_cb.setToolTip(
+            _("Check this box if you want to have toolTips displayed\n"
+              "when hovering with mouse over items throughout the App.")
+        )
+
+        grid0.addWidget(self.toggle_tooltips_cb, 32, 0, 1, 2)
+
+        # 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, 33, 0, 1, 2)
+
+        # Bookmarks Limit in the Help Menu
+        self.bm_limit_spinner = FCSpinner()
+        self.bm_limit_spinner.set_range(0, 9999)
+        self.bm_limit_label = QtWidgets.QLabel('%s:' % _('Bookmarks limit'))
+        self.bm_limit_label.setToolTip(
+            _("The maximum number of bookmarks that may be installed in the menu.\n"
+              "The number of bookmarks in the bookmark manager may be greater\n"
+              "but the menu will hold only so much.")
+        )
+
+        grid0.addWidget(self.bm_limit_label, 34, 0)
+        grid0.addWidget(self.bm_limit_spinner, 34, 1)
+
+        # Activity monitor icon
+        self.activity_label = QtWidgets.QLabel('%s:' % _("Activity Icon"))
+        self.activity_label.setToolTip(
+            _("Select the GIF that show activity when FlatCAM is active.")
+        )
+        self.activity_combo = FCComboBox()
+        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)
+
+        self.layout.addStretch()
+
+        self.mouse_cursor_color_cb.stateChanged.connect(self.on_mouse_cursor_color_enable)
+
+        self.mouse_cursor_entry.editingFinished.connect(self.on_mouse_cursor_entry)
+        self.mouse_cursor_button.clicked.connect(self.on_mouse_cursor_button)
+
+    def on_mouse_cursor_color_enable(self, val):
+        if val:
+            self.app.cursor_color_3D = self.app.defaults["global_cursor_color"]
+        else:
+            theme_settings = QtCore.QSettings("Open Source", "FlatCAM")
+            if theme_settings.contains("theme"):
+                theme = theme_settings.value('theme', type=str)
+            else:
+                theme = 'white'
+
+            if theme == 'white':
+                self.app.cursor_color_3D = 'black'
+            else:
+                self.app.cursor_color_3D = 'gray'
+
+    def on_mouse_cursor_entry(self):
+        self.app.defaults['global_cursor_color'] = self.mouse_cursor_entry.get_value()
+        self.mouse_cursor_button.setStyleSheet("background-color:%s" % str(self.app.defaults['global_cursor_color']))
+
+        self.app.cursor_color_3D = self.app.defaults["global_cursor_color"]
+
+    def on_mouse_cursor_button(self):
+        current_color = QtGui.QColor(self.app.defaults['global_cursor_color'])
+
+        c_dialog = QtWidgets.QColorDialog()
+        proj_color = c_dialog.getColor(initial=current_color)
+
+        if proj_color.isValid() is False:
+            return
+
+        self.mouse_cursor_button.setStyleSheet("background-color:%s" % str(proj_color.name()))
+
+        new_val_sel = str(proj_color.name())
+        self.mouse_cursor_entry.set_value(new_val_sel)
+        self.app.defaults['global_cursor_color'] = new_val_sel
+
+        self.app.cursor_color_3D = self.app.defaults["global_cursor_color"]

+ 402 - 0
flatcamGUI/preferences/general/GeneralAppPrefGroupUI.py

@@ -0,0 +1,402 @@
+import sys
+
+from PyQt5 import QtWidgets
+from PyQt5.QtCore import QSettings
+
+from flatcamGUI.GUIElements import RadioSet, FCSpinner, FCCheckBox, FCComboBox, FCButton, OptionalInputSection, \
+    FCDoubleSpinner
+from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
+
+import gettext
+import FlatCAMTranslation as fcTranslate
+import builtins
+
+fcTranslate.apply_language('strings')
+if '_' not in builtins.__dict__:
+    _ = gettext.gettext
+
+settings = QSettings("Open Source", "FlatCAM")
+if settings.contains("machinist"):
+    machinist_setting = settings.value('machinist', type=int)
+else:
+    machinist_setting = 0
+
+
+class GeneralAppPrefGroupUI(OptionsGroupUI):
+    def __init__(self, decimals=4, parent=None):
+        super(GeneralAppPrefGroupUI, self).__init__(self, parent=parent)
+
+        self.setTitle(_("App Preferences"))
+        self.decimals = decimals
+
+        # Create a form layout for the Application general settings
+        grid0 = QtWidgets.QGridLayout()
+        self.layout.addLayout(grid0)
+        grid0.setColumnStretch(0, 0)
+        grid0.setColumnStretch(1, 1)
+
+        # Units for FlatCAM
+        self.unitslabel = QtWidgets.QLabel('<span style="color:red;"><b>%s:</b></span>' % _('Units'))
+        self.unitslabel.setToolTip(_("The default value for FlatCAM units.\n"
+                                     "Whatever is selected here is set every time\n"
+                                     "FlatCAM is started."))
+        self.units_radio = RadioSet([{'label': _('MM'), 'value': 'MM'},
+                                     {'label': _('IN'), 'value': 'IN'}])
+
+        grid0.addWidget(self.unitslabel, 0, 0)
+        grid0.addWidget(self.units_radio, 0, 1)
+
+        # Precision Metric
+        self.precision_metric_label = QtWidgets.QLabel('%s:' % _('Precision MM'))
+        self.precision_metric_label.setToolTip(
+            _("The number of decimals used throughout the application\n"
+              "when the set units are in METRIC system.\n"
+              "Any change here require an application restart.")
+        )
+        self.precision_metric_entry = FCSpinner()
+        self.precision_metric_entry.set_range(2, 16)
+        self.precision_metric_entry.setWrapping(True)
+
+        grid0.addWidget(self.precision_metric_label, 1, 0)
+        grid0.addWidget(self.precision_metric_entry, 1, 1)
+
+        # Precision Inch
+        self.precision_inch_label = QtWidgets.QLabel('%s:' % _('Precision INCH'))
+        self.precision_inch_label.setToolTip(
+            _("The number of decimals used throughout the application\n"
+              "when the set units are in INCH system.\n"
+              "Any change here require an application restart.")
+        )
+        self.precision_inch_entry = FCSpinner()
+        self.precision_inch_entry.set_range(2, 16)
+        self.precision_inch_entry.setWrapping(True)
+
+        grid0.addWidget(self.precision_inch_label, 2, 0)
+        grid0.addWidget(self.precision_inch_entry, 2, 1)
+
+        # Graphic Engine for FlatCAM
+        self.ge_label = QtWidgets.QLabel('<b>%s:</b>' % _('Graphic Engine'))
+        self.ge_label.setToolTip(_("Choose what graphic engine to use in FlatCAM.\n"
+                                   "Legacy(2D) -> reduced functionality, slow performance but enhanced compatibility.\n"
+                                   "OpenGL(3D) -> full functionality, high performance\n"
+                                   "Some graphic cards are too old and do not work in OpenGL(3D) mode, like:\n"
+                                   "Intel HD3000 or older. In this case the plot area will be black therefore\n"
+                                   "use the Legacy(2D) mode."))
+        self.ge_radio = RadioSet([{'label': _('Legacy(2D)'), 'value': '2D'},
+                                  {'label': _('OpenGL(3D)'), 'value': '3D'}],
+                                 orientation='vertical')
+
+        grid0.addWidget(self.ge_label, 3, 0)
+        grid0.addWidget(self.ge_radio, 3, 1)
+
+        separator_line = QtWidgets.QFrame()
+        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
+        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
+        grid0.addWidget(separator_line, 4, 0, 1, 2)
+
+        # Application Level for FlatCAM
+        self.app_level_label = QtWidgets.QLabel('<span style="color:red;"><b>%s:</b></span>' % _('APP. LEVEL'))
+        self.app_level_label.setToolTip(_("Choose the default level of usage for FlatCAM.\n"
+                                          "BASIC level -> reduced functionality, best for beginner's.\n"
+                                          "ADVANCED level -> full functionality.\n\n"
+                                          "The choice here will influence the parameters in\n"
+                                          "the Selected Tab for all kinds of FlatCAM objects."))
+        self.app_level_radio = RadioSet([{'label': _('Basic'), 'value': 'b'},
+                                         {'label': _('Advanced'), 'value': 'a'}])
+
+        grid0.addWidget(self.app_level_label, 5, 0)
+        grid0.addWidget(self.app_level_radio, 5, 1)
+
+        # Portability for FlatCAM
+        self.portability_cb = FCCheckBox('%s' % _('Portable app'))
+        self.portability_cb.setToolTip(_("Choose if the application should run as portable.\n\n"
+                                         "If Checked the application will run portable,\n"
+                                         "which means that the preferences files will be saved\n"
+                                         "in the application folder, in the lib\\config subfolder."))
+
+        grid0.addWidget(self.portability_cb, 6, 0, 1, 2)
+
+        separator_line = QtWidgets.QFrame()
+        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
+        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
+        grid0.addWidget(separator_line, 7, 0, 1, 2)
+
+        # Languages for FlatCAM
+        self.languagelabel = QtWidgets.QLabel('<b>%s</b>' % _('Languages'))
+        self.languagelabel.setToolTip(_("Set the language used throughout FlatCAM."))
+        self.language_cb = FCComboBox()
+
+        grid0.addWidget(self.languagelabel, 8, 0, 1, 2)
+        grid0.addWidget(self.language_cb, 9, 0, 1, 2)
+
+        self.language_apply_btn = FCButton(_("Apply Language"))
+        self.language_apply_btn.setToolTip(_("Set the language used throughout FlatCAM.\n"
+                                             "The app will restart after click."))
+
+        grid0.addWidget(self.language_apply_btn, 15, 0, 1, 2)
+
+        separator_line = QtWidgets.QFrame()
+        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
+        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
+        grid0.addWidget(separator_line, 16, 0, 1, 2)
+
+        # -----------------------------------------------------------
+        # ----------- APPLICATION STARTUP SETTINGS ------------------
+        # -----------------------------------------------------------
+
+        self.startup_label = QtWidgets.QLabel('<b>%s</b>' % _('Startup Settings'))
+        grid0.addWidget(self.startup_label, 17, 0, 1, 2)
+
+        # Splash Screen
+        self.splash_cb = FCCheckBox('%s' % _('Splash Screen'))
+        self.splash_cb.setToolTip(
+            _("Enable display of the splash screen at application startup.")
+        )
+
+        qsettings = QSettings("Open Source", "FlatCAM")
+        if qsettings.value("splash_screen"):
+            self.splash_cb.set_value(True)
+        else:
+            self.splash_cb.set_value(False)
+
+        grid0.addWidget(self.splash_cb, 18, 0, 1, 2)
+
+        # Sys Tray Icon
+        self.systray_cb = FCCheckBox('%s' % _('Sys Tray Icon'))
+        self.systray_cb.setToolTip(
+            _("Enable display of FlatCAM icon in Sys Tray.")
+        )
+        grid0.addWidget(self.systray_cb, 19, 0, 1, 2)
+
+        # Shell StartUp CB
+        self.shell_startup_cb = FCCheckBox(label='%s' % _('Show Shell'))
+        self.shell_startup_cb.setToolTip(
+            _("Check this box if you want the shell to\n"
+              "start automatically at startup.")
+        )
+
+        grid0.addWidget(self.shell_startup_cb, 20, 0, 1, 2)
+
+        # Project at StartUp CB
+        self.project_startup_cb = FCCheckBox(label='%s' % _('Show Project'))
+        self.project_startup_cb.setToolTip(
+            _("Check this box if you want the project/selected/tool tab area to\n"
+              "to be shown automatically at startup.")
+        )
+        grid0.addWidget(self.project_startup_cb, 21, 0, 1, 2)
+
+        # Version Check CB
+        self.version_check_cb = FCCheckBox(label='%s' % _('Version Check'))
+        self.version_check_cb.setToolTip(
+            _("Check this box if you want to check\n"
+              "for a new version automatically at startup.")
+        )
+
+        grid0.addWidget(self.version_check_cb, 22, 0, 1, 2)
+
+        # Send Stats CB
+        self.send_stats_cb = FCCheckBox(label='%s' % _('Send Statistics'))
+        self.send_stats_cb.setToolTip(
+            _("Check this box if you agree to send anonymous\n"
+              "stats automatically at startup, to help improve FlatCAM.")
+        )
+
+        grid0.addWidget(self.send_stats_cb, 23, 0, 1, 2)
+
+        self.ois_version_check = OptionalInputSection(self.version_check_cb, [self.send_stats_cb])
+
+        separator_line = QtWidgets.QFrame()
+        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
+        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
+        grid0.addWidget(separator_line, 24, 0, 1, 2)
+
+        # Worker Numbers
+        self.worker_number_label = QtWidgets.QLabel('%s:' % _('Workers number'))
+        self.worker_number_label.setToolTip(
+            _("The number of Qthreads made available to the App.\n"
+              "A bigger number may finish the jobs more quickly but\n"
+              "depending on your computer speed, may make the App\n"
+              "unresponsive. Can have a value between 2 and 16.\n"
+              "Default value is 2.\n"
+              "After change, it will be applied at next App start.")
+        )
+        self.worker_number_sb = FCSpinner()
+        self.worker_number_sb.set_range(2, 16)
+
+        grid0.addWidget(self.worker_number_label, 25, 0)
+        grid0.addWidget(self.worker_number_sb, 25, 1)
+
+        # Geometric tolerance
+        tol_label = QtWidgets.QLabel('%s:' % _("Geo Tolerance"))
+        tol_label.setToolTip(_(
+            "This value can counter the effect of the Circle Steps\n"
+            "parameter. Default value is 0.005.\n"
+            "A lower value will increase the detail both in image\n"
+            "and in Gcode for the circles, with a higher cost in\n"
+            "performance. Higher value will provide more\n"
+            "performance at the expense of level of detail."
+        ))
+        self.tol_entry = FCDoubleSpinner()
+        self.tol_entry.setSingleStep(0.001)
+        self.tol_entry.set_precision(6)
+
+        grid0.addWidget(tol_label, 26, 0)
+        grid0.addWidget(self.tol_entry, 26, 1)
+
+        separator_line = QtWidgets.QFrame()
+        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
+        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
+        grid0.addWidget(separator_line, 27, 0, 1, 2)
+
+        # Save Settings
+        self.save_label = QtWidgets.QLabel('<b>%s</b>' % _("Save Settings"))
+        grid0.addWidget(self.save_label, 28, 0, 1, 2)
+
+        # Save compressed project CB
+        self.save_type_cb = FCCheckBox(_('Save Compressed Project'))
+        self.save_type_cb.setToolTip(
+            _("Whether to save a compressed or uncompressed project.\n"
+              "When checked it will save a compressed FlatCAM project.")
+        )
+
+        grid0.addWidget(self.save_type_cb, 29, 0, 1, 2)
+
+        # Project LZMA Comppression Level
+        self.compress_spinner = FCSpinner()
+        self.compress_spinner.set_range(0, 9)
+        self.compress_label = QtWidgets.QLabel('%s:' % _('Compression'))
+        self.compress_label.setToolTip(
+            _("The level of compression used when saving\n"
+              "a FlatCAM project. Higher value means better compression\n"
+              "but require more RAM usage and more processing time.")
+        )
+
+        grid0.addWidget(self.compress_label, 30, 0)
+        grid0.addWidget(self.compress_spinner, 30, 1)
+
+        self.proj_ois = OptionalInputSection(self.save_type_cb, [self.compress_label, self.compress_spinner], True)
+
+        # Auto save CB
+        self.autosave_cb = FCCheckBox(_('Enable Auto Save'))
+        self.autosave_cb.setToolTip(
+            _("Check to enable the autosave feature.\n"
+              "When enabled, the application will try to save a project\n"
+              "at the set interval.")
+        )
+
+        grid0.addWidget(self.autosave_cb, 31, 0, 1, 2)
+
+        # Auto Save Timeout Interval
+        self.autosave_entry = FCSpinner()
+        self.autosave_entry.set_range(0, 9999999)
+        self.autosave_label = QtWidgets.QLabel('%s:' % _('Interval'))
+        self.autosave_label.setToolTip(
+            _("Time interval for autosaving. In milliseconds.\n"
+              "The application will try to save periodically but only\n"
+              "if the project was saved manually at least once.\n"
+              "While active, some operations may block this feature.")
+        )
+
+        grid0.addWidget(self.autosave_label, 32, 0)
+        grid0.addWidget(self.autosave_entry, 32, 1)
+
+        # self.as_ois = OptionalInputSection(self.autosave_cb, [self.autosave_label, self.autosave_entry], True)
+
+        separator_line = QtWidgets.QFrame()
+        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
+        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
+        grid0.addWidget(separator_line, 33, 0, 1, 2)
+
+        self.pdf_param_label = QtWidgets.QLabel('<B>%s:</b>' % _("Text to PDF parameters"))
+        self.pdf_param_label.setToolTip(
+            _("Used when saving text in Code Editor or in FlatCAM Document objects.")
+        )
+        grid0.addWidget(self.pdf_param_label, 34, 0, 1, 2)
+
+        # Top Margin value
+        self.tmargin_entry = FCDoubleSpinner()
+        self.tmargin_entry.set_precision(self.decimals)
+        self.tmargin_entry.set_range(0.0000, 9999.9999)
+
+        self.tmargin_label = QtWidgets.QLabel('%s:' % _("Top Margin"))
+        self.tmargin_label.setToolTip(
+            _("Distance between text body and the top of the PDF file.")
+        )
+
+        grid0.addWidget(self.tmargin_label, 35, 0)
+        grid0.addWidget(self.tmargin_entry, 35, 1)
+
+        # Bottom Margin value
+        self.bmargin_entry = FCDoubleSpinner()
+        self.bmargin_entry.set_precision(self.decimals)
+        self.bmargin_entry.set_range(0.0000, 9999.9999)
+
+        self.bmargin_label = QtWidgets.QLabel('%s:' % _("Bottom Margin"))
+        self.bmargin_label.setToolTip(
+            _("Distance between text body and the bottom of the PDF file.")
+        )
+
+        grid0.addWidget(self.bmargin_label, 36, 0)
+        grid0.addWidget(self.bmargin_entry, 36, 1)
+
+        # Left Margin value
+        self.lmargin_entry = FCDoubleSpinner()
+        self.lmargin_entry.set_precision(self.decimals)
+        self.lmargin_entry.set_range(0.0000, 9999.9999)
+
+        self.lmargin_label = QtWidgets.QLabel('%s:' % _("Left Margin"))
+        self.lmargin_label.setToolTip(
+            _("Distance between text body and the left of the PDF file.")
+        )
+
+        grid0.addWidget(self.lmargin_label, 37, 0)
+        grid0.addWidget(self.lmargin_entry, 37, 1)
+
+        # Right Margin value
+        self.rmargin_entry = FCDoubleSpinner()
+        self.rmargin_entry.set_precision(self.decimals)
+        self.rmargin_entry.set_range(0.0000, 9999.9999)
+
+        self.rmargin_label = QtWidgets.QLabel('%s:' % _("Right Margin"))
+        self.rmargin_label.setToolTip(
+            _("Distance between text body and the right of the PDF file.")
+        )
+
+        grid0.addWidget(self.rmargin_label, 38, 0)
+        grid0.addWidget(self.rmargin_entry, 38, 1)
+
+        self.layout.addStretch()
+
+        if sys.platform != 'win32':
+            self.portability_cb.hide()
+
+        # splash screen button signal
+        self.splash_cb.stateChanged.connect(self.on_splash_changed)
+
+        # Monitor the checkbox from the Application Defaults Tab and show the TCL shell or not depending on it's value
+        self.shell_startup_cb.clicked.connect(self.on_toggle_shell_from_settings)
+
+        self.language_apply_btn.clicked.connect(lambda: fcTranslate.on_language_apply_click(app=self.app, restart=True))
+
+    def on_toggle_shell_from_settings(self, state):
+        """
+        Toggle shell: if is visible close it, if it is closed then open it
+        :return: None
+        """
+
+        self.app.defaults.report_usage("on_toggle_shell_from_settings()")
+
+        if state is True:
+            if not self.app.ui.shell_dock.isVisible():
+                self.app.ui.shell_dock.show()
+        else:
+            if self.app.ui.shell_dock.isVisible():
+                self.app.ui.shell_dock.hide()
+
+    @staticmethod
+    def on_splash_changed(state):
+        qsettings = QSettings("Open Source", "FlatCAM")
+        qsettings.setValue('splash_screen', 1) if state else qsettings.setValue('splash_screen', 0)
+
+        # This will write the setting to the platform specific storage.
+        del qsettings

+ 787 - 0
flatcamGUI/preferences/general/GeneralGUIPrefGroupUI.py

@@ -0,0 +1,787 @@
+from PyQt5 import QtWidgets, QtCore, QtGui
+from PyQt5.QtCore import QSettings, Qt
+
+from flatcamGUI.GUIElements import RadioSet, FCCheckBox, FCButton, FCComboBox, FCEntry, FCSpinner
+from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
+
+import gettext
+import FlatCAMTranslation as fcTranslate
+import builtins
+
+fcTranslate.apply_language('strings')
+if '_' not in builtins.__dict__:
+    _ = gettext.gettext
+
+settings = QSettings("Open Source", "FlatCAM")
+if settings.contains("machinist"):
+    machinist_setting = settings.value('machinist', type=int)
+else:
+    machinist_setting = 0
+
+
+class GeneralGUIPrefGroupUI(OptionsGroupUI):
+    def __init__(self, decimals=4, parent=None):
+        super(GeneralGUIPrefGroupUI, self).__init__(self, parent=parent)
+
+        self.setTitle(str(_("GUI Preferences")))
+        self.decimals = decimals
+
+        # Create a grid layout for the Application general settings
+        grid0 = QtWidgets.QGridLayout()
+        self.layout.addLayout(grid0)
+        grid0.setColumnStretch(0, 0)
+        grid0.setColumnStretch(1, 1)
+
+        # Theme selection
+        self.theme_label = QtWidgets.QLabel('%s:' % _('Theme'))
+        self.theme_label.setToolTip(
+            _("Select a theme for FlatCAM.\n"
+              "It will theme the plot area.")
+        )
+
+        self.theme_radio = RadioSet([
+            {"label": _("Light"), "value": "white"},
+            {"label": _("Dark"), "value": "black"}
+        ], orientation='vertical')
+
+        grid0.addWidget(self.theme_label, 0, 0)
+        grid0.addWidget(self.theme_radio, 0, 1)
+
+        # Enable Gray Icons
+        self.gray_icons_cb = FCCheckBox('%s' % _('Use Gray Icons'))
+        self.gray_icons_cb.setToolTip(
+            _("Check this box to use a set of icons with\n"
+              "a lighter (gray) color. To be used when a\n"
+              "full dark theme is applied.")
+        )
+        grid0.addWidget(self.gray_icons_cb, 1, 0, 1, 3)
+
+        self.theme_button = FCButton(_("Apply Theme"))
+        self.theme_button.setToolTip(
+            _("Select a theme for FlatCAM.\n"
+              "It will theme the plot area.\n"
+              "The application will restart after change.")
+        )
+        grid0.addWidget(self.theme_button, 2, 0, 1, 3)
+
+        separator_line = QtWidgets.QFrame()
+        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
+        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
+        grid0.addWidget(separator_line, 3, 0, 1, 2)
+
+        # Layout selection
+        self.layout_label = QtWidgets.QLabel('%s:' % _('Layout'))
+        self.layout_label.setToolTip(
+            _("Select an layout for FlatCAM.\n"
+              "It is applied immediately.")
+        )
+        self.layout_combo = FCComboBox()
+        # don't translate the QCombo items as they are used in QSettings and identified by name
+        self.layout_combo.addItem("standard")
+        self.layout_combo.addItem("compact")
+        self.layout_combo.addItem("minimal")
+
+        grid0.addWidget(self.layout_label, 4, 0)
+        grid0.addWidget(self.layout_combo, 4, 1)
+
+        # Set the current index for layout_combo
+        qsettings = QSettings("Open Source", "FlatCAM")
+        if qsettings.contains("layout"):
+            layout = qsettings.value('layout', type=str)
+            idx = self.layout_combo.findText(layout.capitalize())
+            self.layout_combo.setCurrentIndex(idx)
+
+        # Style selection
+        self.style_label = QtWidgets.QLabel('%s:' % _('Style'))
+        self.style_label.setToolTip(
+            _("Select an style for FlatCAM.\n"
+              "It will be applied at the next app start.")
+        )
+        self.style_combo = FCComboBox()
+        self.style_combo.addItems(QtWidgets.QStyleFactory.keys())
+        # find current style
+        index = self.style_combo.findText(QtWidgets.qApp.style().objectName(), QtCore.Qt.MatchFixedString)
+        self.style_combo.setCurrentIndex(index)
+        self.style_combo.activated[str].connect(self.handle_style)
+
+        grid0.addWidget(self.style_label, 5, 0)
+        grid0.addWidget(self.style_combo, 5, 1)
+
+        # Enable High DPI Support
+        self.hdpi_cb = FCCheckBox('%s' % _('Activate HDPI Support'))
+        self.hdpi_cb.setToolTip(
+            _("Enable High DPI support for FlatCAM.\n"
+              "It will be applied at the next app start.")
+        )
+
+        qsettings = QSettings("Open Source", "FlatCAM")
+        if qsettings.contains("hdpi"):
+            self.hdpi_cb.set_value(qsettings.value('hdpi', type=int))
+        else:
+            self.hdpi_cb.set_value(False)
+        self.hdpi_cb.stateChanged.connect(self.handle_hdpi)
+
+        grid0.addWidget(self.hdpi_cb, 6, 0, 1, 3)
+
+        # Enable Hover box
+        self.hover_cb = FCCheckBox('%s' % _('Display Hover Shape'))
+        self.hover_cb.setToolTip(
+            _("Enable display of a hover shape for FlatCAM objects.\n"
+              "It is displayed whenever the mouse cursor is hovering\n"
+              "over any kind of not-selected object.")
+        )
+        grid0.addWidget(self.hover_cb, 8, 0, 1, 3)
+
+        # Enable Selection box
+        self.selection_cb = FCCheckBox('%s' % _('Display Selection Shape'))
+        self.selection_cb.setToolTip(
+            _("Enable the display of a selection shape for FlatCAM objects.\n"
+              "It is displayed whenever the mouse selects an object\n"
+              "either by clicking or dragging mouse from left to right or\n"
+              "right to left.")
+        )
+        grid0.addWidget(self.selection_cb, 9, 0, 1, 3)
+
+        separator_line = QtWidgets.QFrame()
+        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
+        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
+        grid0.addWidget(separator_line, 14, 0, 1, 2)
+
+        # Plot Selection (left - right) Color
+        self.sel_lr_label = QtWidgets.QLabel('<b>%s</b>' % _('Left-Right Selection Color'))
+        grid0.addWidget(self.sel_lr_label, 15, 0, 1, 2)
+
+        self.sl_color_label = QtWidgets.QLabel('%s:' % _('Outline'))
+        self.sl_color_label.setToolTip(
+            _("Set the line color for the 'left to right' selection box.")
+        )
+        self.sl_color_entry = FCEntry()
+        self.sl_color_button = QtWidgets.QPushButton()
+        self.sl_color_button.setFixedSize(15, 15)
+
+        self.form_box_child_4 = QtWidgets.QHBoxLayout()
+        self.form_box_child_4.addWidget(self.sl_color_entry)
+        self.form_box_child_4.addWidget(self.sl_color_button)
+        self.form_box_child_4.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
+
+        grid0.addWidget(self.sl_color_label, 16, 0)
+        grid0.addLayout(self.form_box_child_4, 16, 1)
+
+        self.sf_color_label = QtWidgets.QLabel('%s:' % _('Fill'))
+        self.sf_color_label.setToolTip(
+            _("Set the fill color for the selection box\n"
+              "in case that the selection is done from left to right.\n"
+              "First 6 digits are the color and the last 2\n"
+              "digits are for alpha (transparency) level.")
+        )
+        self.sf_color_entry = FCEntry()
+        self.sf_color_button = QtWidgets.QPushButton()
+        self.sf_color_button.setFixedSize(15, 15)
+
+        self.form_box_child_5 = QtWidgets.QHBoxLayout()
+        self.form_box_child_5.addWidget(self.sf_color_entry)
+        self.form_box_child_5.addWidget(self.sf_color_button)
+        self.form_box_child_5.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
+
+        grid0.addWidget(self.sf_color_label, 17, 0)
+        grid0.addLayout(self.form_box_child_5, 17, 1)
+
+        # Plot Selection (left - right) Fill Transparency Level
+        self.sf_alpha_label = QtWidgets.QLabel('%s:' % _('Alpha'))
+        self.sf_alpha_label.setToolTip(
+            _("Set the fill transparency for the 'left to right' selection box.")
+        )
+        self.sf_color_alpha_slider = QtWidgets.QSlider(QtCore.Qt.Horizontal)
+        self.sf_color_alpha_slider.setMinimum(0)
+        self.sf_color_alpha_slider.setMaximum(255)
+        self.sf_color_alpha_slider.setSingleStep(1)
+
+        self.sf_color_alpha_spinner = FCSpinner()
+        self.sf_color_alpha_spinner.setMinimumWidth(70)
+        self.sf_color_alpha_spinner.set_range(0, 255)
+
+        self.form_box_child_6 = QtWidgets.QHBoxLayout()
+        self.form_box_child_6.addWidget(self.sf_color_alpha_slider)
+        self.form_box_child_6.addWidget(self.sf_color_alpha_spinner)
+
+        grid0.addWidget(self.sf_alpha_label, 18, 0)
+        grid0.addLayout(self.form_box_child_6, 18, 1)
+
+        separator_line = QtWidgets.QFrame()
+        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
+        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
+        grid0.addWidget(separator_line, 19, 0, 1, 2)
+
+        # Plot Selection (left - right) Color
+        self.sel_rl_label = QtWidgets.QLabel('<b>%s</b>' % _('Right-Left Selection Color'))
+        grid0.addWidget(self.sel_rl_label, 20, 0, 1, 2)
+
+        # Plot Selection (right - left) Line Color
+        self.alt_sl_color_label = QtWidgets.QLabel('%s:' % _('Outline'))
+        self.alt_sl_color_label.setToolTip(
+            _("Set the line color for the 'right to left' selection box.")
+        )
+        self.alt_sl_color_entry = FCEntry()
+        self.alt_sl_color_button = QtWidgets.QPushButton()
+        self.alt_sl_color_button.setFixedSize(15, 15)
+
+        self.form_box_child_7 = QtWidgets.QHBoxLayout()
+        self.form_box_child_7.addWidget(self.alt_sl_color_entry)
+        self.form_box_child_7.addWidget(self.alt_sl_color_button)
+        self.form_box_child_7.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
+
+        grid0.addWidget(self.alt_sl_color_label, 21, 0)
+        grid0.addLayout(self.form_box_child_7, 21, 1)
+
+        # Plot Selection (right - left) Fill Color
+        self.alt_sf_color_label = QtWidgets.QLabel('%s:' % _('Fill'))
+        self.alt_sf_color_label.setToolTip(
+            _("Set the fill color for the selection box\n"
+              "in case that the selection is done from right to left.\n"
+              "First 6 digits are the color and the last 2\n"
+              "digits are for alpha (transparency) level.")
+        )
+        self.alt_sf_color_entry = FCEntry()
+        self.alt_sf_color_button = QtWidgets.QPushButton()
+        self.alt_sf_color_button.setFixedSize(15, 15)
+
+        self.form_box_child_8 = QtWidgets.QHBoxLayout()
+        self.form_box_child_8.addWidget(self.alt_sf_color_entry)
+        self.form_box_child_8.addWidget(self.alt_sf_color_button)
+        self.form_box_child_8.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
+
+        grid0.addWidget(self.alt_sf_color_label, 22, 0)
+        grid0.addLayout(self.form_box_child_8, 22, 1)
+
+        # Plot Selection (right - left) Fill Transparency Level
+        self.alt_sf_alpha_label = QtWidgets.QLabel('%s:' % _('Alpha'))
+        self.alt_sf_alpha_label.setToolTip(
+            _("Set the fill transparency for selection 'right to left' box.")
+        )
+        self.alt_sf_color_alpha_slider = QtWidgets.QSlider(QtCore.Qt.Horizontal)
+        self.alt_sf_color_alpha_slider.setMinimum(0)
+        self.alt_sf_color_alpha_slider.setMaximum(255)
+        self.alt_sf_color_alpha_slider.setSingleStep(1)
+
+        self.alt_sf_color_alpha_spinner = FCSpinner()
+        self.alt_sf_color_alpha_spinner.setMinimumWidth(70)
+        self.alt_sf_color_alpha_spinner.set_range(0, 255)
+
+        self.form_box_child_9 = QtWidgets.QHBoxLayout()
+        self.form_box_child_9.addWidget(self.alt_sf_color_alpha_slider)
+        self.form_box_child_9.addWidget(self.alt_sf_color_alpha_spinner)
+
+        grid0.addWidget(self.alt_sf_alpha_label, 23, 0)
+        grid0.addLayout(self.form_box_child_9, 23, 1)
+
+        separator_line = QtWidgets.QFrame()
+        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
+        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
+        grid0.addWidget(separator_line, 24, 0, 1, 2)
+
+        # ------------------------------------------------------------------
+        # ----------------------- Editor Color -----------------------------
+        # ------------------------------------------------------------------
+
+        self.editor_color_label = QtWidgets.QLabel('<b>%s</b>' % _('Editor Color'))
+        grid0.addWidget(self.editor_color_label, 25, 0, 1, 2)
+
+        # Editor Draw Color
+        self.draw_color_label = QtWidgets.QLabel('%s:' % _('Drawing'))
+        self.alt_sf_color_label.setToolTip(
+            _("Set the color for the shape.")
+        )
+        self.draw_color_entry = FCEntry()
+        self.draw_color_button = QtWidgets.QPushButton()
+        self.draw_color_button.setFixedSize(15, 15)
+
+        self.form_box_child_10 = QtWidgets.QHBoxLayout()
+        self.form_box_child_10.addWidget(self.draw_color_entry)
+        self.form_box_child_10.addWidget(self.draw_color_button)
+        self.form_box_child_10.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
+
+        grid0.addWidget(self.draw_color_label, 26, 0)
+        grid0.addLayout(self.form_box_child_10, 26, 1)
+
+        # Editor Draw Selection Color
+        self.sel_draw_color_label = QtWidgets.QLabel('%s:' % _('Selection'))
+        self.sel_draw_color_label.setToolTip(
+            _("Set the color of the shape when selected.")
+        )
+        self.sel_draw_color_entry = FCEntry()
+        self.sel_draw_color_button = QtWidgets.QPushButton()
+        self.sel_draw_color_button.setFixedSize(15, 15)
+
+        self.form_box_child_11 = QtWidgets.QHBoxLayout()
+        self.form_box_child_11.addWidget(self.sel_draw_color_entry)
+        self.form_box_child_11.addWidget(self.sel_draw_color_button)
+        self.form_box_child_11.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
+
+        grid0.addWidget(self.sel_draw_color_label, 27, 0)
+        grid0.addLayout(self.form_box_child_11, 27, 1)
+
+        separator_line = QtWidgets.QFrame()
+        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
+        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
+        grid0.addWidget(separator_line, 28, 0, 1, 2)
+
+        # ------------------------------------------------------------------
+        # ----------------------- Project Settings -----------------------------
+        # ------------------------------------------------------------------
+
+        self.proj_settings_label = QtWidgets.QLabel('<b>%s</b>' % _('Project Items Color'))
+        grid0.addWidget(self.proj_settings_label, 29, 0, 1, 2)
+
+        # Project Tab items color
+        self.proj_color_label = QtWidgets.QLabel('%s:' % _('Enabled'))
+        self.proj_color_label.setToolTip(
+            _("Set the color of the items in Project Tab Tree.")
+        )
+        self.proj_color_entry = FCEntry()
+        self.proj_color_button = QtWidgets.QPushButton()
+        self.proj_color_button.setFixedSize(15, 15)
+
+        self.form_box_child_12 = QtWidgets.QHBoxLayout()
+        self.form_box_child_12.addWidget(self.proj_color_entry)
+        self.form_box_child_12.addWidget(self.proj_color_button)
+        self.form_box_child_12.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
+
+        grid0.addWidget(self.proj_color_label, 30, 0)
+        grid0.addLayout(self.form_box_child_12, 30, 1)
+
+        self.proj_color_dis_label = QtWidgets.QLabel('%s:' % _('Disabled'))
+        self.proj_color_dis_label.setToolTip(
+            _("Set the color of the items in Project Tab Tree,\n"
+              "for the case when the items are disabled.")
+        )
+        self.proj_color_dis_entry = FCEntry()
+        self.proj_color_dis_button = QtWidgets.QPushButton()
+        self.proj_color_dis_button.setFixedSize(15, 15)
+
+        self.form_box_child_13 = QtWidgets.QHBoxLayout()
+        self.form_box_child_13.addWidget(self.proj_color_dis_entry)
+        self.form_box_child_13.addWidget(self.proj_color_dis_button)
+        self.form_box_child_13.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
+
+        grid0.addWidget(self.proj_color_dis_label, 31, 0)
+        grid0.addLayout(self.form_box_child_13, 31, 1)
+
+        # Project autohide CB
+        self.project_autohide_cb = FCCheckBox(label=_('Project AutoHide'))
+        self.project_autohide_cb.setToolTip(
+            _("Check this box if you want the project/selected/tool tab area to\n"
+              "hide automatically when there are no objects loaded and\n"
+              "to show whenever a new object is created.")
+        )
+
+        grid0.addWidget(self.project_autohide_cb, 32, 0, 1, 2)
+
+        # Just to add empty rows
+        grid0.addWidget(QtWidgets.QLabel(''), 33, 0, 1, 2)
+
+        self.layout.addStretch()
+
+        self.theme_button.clicked.connect(self.on_theme_change)
+
+        # #############################################################################
+        # ############################# GUI COLORS SIGNALS ############################
+        # #############################################################################
+
+        # Setting selection (left - right) colors signals
+        self.sf_color_entry.editingFinished.connect(self.on_sf_color_entry)
+        self.sf_color_button.clicked.connect(self.on_sf_color_button)
+        self.sf_color_alpha_spinner.valueChanged.connect(self.on_sf_color_spinner)
+        self.sf_color_alpha_slider.valueChanged.connect(self.on_sf_color_slider)
+        self.sl_color_entry.editingFinished.connect(self.on_sl_color_entry)
+        self.sl_color_button.clicked.connect(self.on_sl_color_button)
+
+        # Setting selection (right - left) colors signals
+        self.alt_sf_color_entry.editingFinished.connect(self.on_alt_sf_color_entry)
+        self.alt_sf_color_button.clicked.connect(self.on_alt_sf_color_button)
+        self.alt_sf_color_alpha_spinner.valueChanged.connect(self.on_alt_sf_color_spinner)
+        self.alt_sf_color_alpha_slider.valueChanged.connect(self.on_alt_sf_color_slider)
+        self.alt_sl_color_entry.editingFinished.connect(self.on_alt_sl_color_entry)
+        self.alt_sl_color_button.clicked.connect(self.on_alt_sl_color_button)
+
+        # Setting Editor Draw colors signals
+        self.draw_color_entry.editingFinished.connect(self.on_draw_color_entry)
+        self.draw_color_button.clicked.connect(self.on_draw_color_button)
+
+        self.sel_draw_color_entry.editingFinished.connect(self.on_sel_draw_color_entry)
+        self.sel_draw_color_button.clicked.connect(self.on_sel_draw_color_button)
+
+        self.proj_color_entry.editingFinished.connect(self.on_proj_color_entry)
+        self.proj_color_button.clicked.connect(self.on_proj_color_button)
+
+        self.proj_color_dis_entry.editingFinished.connect(self.on_proj_color_dis_entry)
+        self.proj_color_dis_button.clicked.connect(self.on_proj_color_dis_button)
+
+        self.layout_combo.activated.connect(self.on_layout)
+
+    def on_theme_change(self):
+        val = self.theme_radio.get_value()
+        qsettings = QSettings("Open Source", "FlatCAM")
+        qsettings.setValue('theme', val)
+
+        # This will write the setting to the platform specific storage.
+        del qsettings
+
+        self.app.on_app_restart()
+
+    @staticmethod
+    def handle_style(style):
+        # set current style
+        qsettings = QSettings("Open Source", "FlatCAM")
+        qsettings.setValue('style', style)
+
+        # This will write the setting to the platform specific storage.
+        del qsettings
+
+    @staticmethod
+    def handle_hdpi(state):
+        # set current HDPI
+        qsettings = QSettings("Open Source", "FlatCAM")
+        qsettings.setValue('hdpi', state)
+
+        # This will write the setting to the platform specific storage.
+        del qsettings
+
+    # Setting selection colors (left - right) handlers
+    def on_sf_color_entry(self):
+        self.app.defaults['global_sel_fill'] = self.app.defaults['global_sel_fill'][7:9]
+        self.sf_color_button.setStyleSheet("background-color:%s" % str(self.app.defaults['global_sel_fill'])[:7])
+
+    def on_sf_color_button(self):
+        current_color = QtGui.QColor(self.app.defaults['global_sel_fill'][:7])
+
+        c_dialog = QtWidgets.QColorDialog()
+        plot_fill_color = c_dialog.getColor(initial=current_color)
+
+        if plot_fill_color.isValid() is False:
+            return
+
+        self.sf_color_button.setStyleSheet("background-color:%s" % str(plot_fill_color.name()))
+
+        new_val = str(plot_fill_color.name()) + str(self.app.defaults['global_sel_fill'][7:9])
+        self.sf_color_entry.set_value(new_val)
+        self.app.defaults['global_sel_fill'] = new_val
+
+    def on_sf_color_spinner(self):
+        spinner_value = self.sf_color_alpha_spinner.value()
+        self.sf_color_alpha_slider.setValue(spinner_value)
+        self.app.defaults['global_sel_fill'] = self.app.defaults['global_sel_fill'][:7] + \
+            (hex(spinner_value)[2:] if int(hex(spinner_value)[2:], 16) > 0 else '00')
+        self.app.defaults['global_sel_line'] = self.app.defaults['global_sel_line'][:7] + \
+            (hex(spinner_value)[2:] if int(hex(spinner_value)[2:], 16) > 0 else '00')
+
+    def on_sf_color_slider(self):
+        slider_value = self.sf_color_alpha_slider.value()
+        self.sf_color_alpha_spinner.setValue(slider_value)
+
+    def on_sl_color_entry(self):
+        self.app.defaults['global_sel_line'] = self.sl_color_entry.get_value()[:7] + \
+            self.app.defaults['global_sel_line'][7:9]
+        self.sl_color_button.setStyleSheet("background-color:%s" % str(self.app.defaults['global_sel_line'])[:7])
+
+    def on_sl_color_button(self):
+        current_color = QtGui.QColor(self.app.defaults['global_sel_line'][:7])
+
+        c_dialog = QtWidgets.QColorDialog()
+        plot_line_color = c_dialog.getColor(initial=current_color)
+
+        if plot_line_color.isValid() is False:
+            return
+
+        self.sl_color_button.setStyleSheet("background-color:%s" % str(plot_line_color.name()))
+
+        new_val_line = str(plot_line_color.name()) + str(self.app.defaults['global_sel_line'][7:9])
+        self.sl_color_entry.set_value(new_val_line)
+        self.app.defaults['global_sel_line'] = new_val_line
+
+    # Setting selection colors (right - left) handlers
+    def on_alt_sf_color_entry(self):
+        self.app.defaults['global_alt_sel_fill'] = self.alt_sf_color_entry.get_value()[:7] + \
+                                                   self.app.defaults['global_alt_sel_fill'][7:9]
+        self.alt_sf_color_button.setStyleSheet(
+            "background-color:%s" % str(self.app.defaults['global_alt_sel_fill'])[:7]
+        )
+
+    def on_alt_sf_color_button(self):
+        current_color = QtGui.QColor(self.app.defaults['global_alt_sel_fill'][:7])
+
+        c_dialog = QtWidgets.QColorDialog()
+        plot_fill_color = c_dialog.getColor(initial=current_color)
+
+        if plot_fill_color.isValid() is False:
+            return
+
+        self.alt_sf_color_button.setStyleSheet("background-color:%s" % str(plot_fill_color.name()))
+
+        new_val = str(plot_fill_color.name()) + str(self.app.defaults['global_alt_sel_fill'][7:9])
+        self.alt_sf_color_entry.set_value(new_val)
+        self.app.defaults['global_alt_sel_fill'] = new_val
+
+    def on_alt_sf_color_spinner(self):
+        spinner_value = self.alt_sf_color_alpha_spinner.value()
+        self.alt_sf_color_alpha_slider.setValue(spinner_value)
+        self.app.defaults['global_alt_sel_fill'] = self.app.defaults['global_alt_sel_fill'][:7] + \
+            (hex(spinner_value)[2:] if int(hex(spinner_value)[2:], 16) > 0 else '00')
+        self.app.defaults['global_alt_sel_line'] = self.app.defaults['global_alt_sel_line'][:7] + \
+            (hex(spinner_value)[2:] if int(hex(spinner_value)[2:], 16) > 0 else '00')
+
+    def on_alt_sf_color_slider(self):
+        slider_value = self.alt_sf_color_alpha_slider.value()
+        self.alt_sf_color_alpha_spinner.setValue(slider_value)
+
+    def on_alt_sl_color_entry(self):
+        self.app.defaults['global_alt_sel_line'] = self.alt_sl_color_entry.get_value()[:7] + \
+                                                   self.app.defaults['global_alt_sel_line'][7:9]
+        self.alt_sl_color_button.setStyleSheet(
+            "background-color:%s" % str(self.app.defaults['global_alt_sel_line'])[:7]
+        )
+
+    def on_alt_sl_color_button(self):
+        current_color = QtGui.QColor(self.app.defaults['global_alt_sel_line'][:7])
+
+        c_dialog = QtWidgets.QColorDialog()
+        plot_line_color = c_dialog.getColor(initial=current_color)
+
+        if plot_line_color.isValid() is False:
+            return
+
+        self.alt_sl_color_button.setStyleSheet("background-color:%s" % str(plot_line_color.name()))
+
+        new_val_line = str(plot_line_color.name()) + str(self.app.defaults['global_alt_sel_line'][7:9])
+        self.alt_sl_color_entry.set_value(new_val_line)
+        self.app.defaults['global_alt_sel_line'] = new_val_line
+
+    # Setting Editor colors
+    def on_draw_color_entry(self):
+        self.app.defaults['global_draw_color'] = self.draw_color_entry.get_value()
+        self.draw_color_button.setStyleSheet("background-color:%s" % str(self.app.defaults['global_draw_color']))
+
+    def on_draw_color_button(self):
+        current_color = QtGui.QColor(self.app.defaults['global_draw_color'])
+
+        c_dialog = QtWidgets.QColorDialog()
+        draw_color = c_dialog.getColor(initial=current_color)
+
+        if draw_color.isValid() is False:
+            return
+
+        self.draw_color_button.setStyleSheet("background-color:%s" % str(draw_color.name()))
+
+        new_val = str(draw_color.name())
+        self.draw_color_entry.set_value(new_val)
+        self.app.defaults['global_draw_color'] = new_val
+
+    def on_sel_draw_color_entry(self):
+        self.app.defaults['global_sel_draw_color'] = self.sel_draw_color_entry.get_value()
+        self.sel_draw_color_button.setStyleSheet(
+            "background-color:%s" % str(self.app.defaults['global_sel_draw_color']))
+
+    def on_sel_draw_color_button(self):
+        current_color = QtGui.QColor(self.app.defaults['global_sel_draw_color'])
+
+        c_dialog = QtWidgets.QColorDialog()
+        sel_draw_color = c_dialog.getColor(initial=current_color)
+
+        if sel_draw_color.isValid() is False:
+            return
+
+        self.sel_draw_color_button.setStyleSheet("background-color:%s" % str(sel_draw_color.name()))
+
+        new_val_sel = str(sel_draw_color.name())
+        self.sel_draw_color_entry.set_value(new_val_sel)
+        self.app.defaults['global_sel_draw_color'] = new_val_sel
+
+    def on_proj_color_entry(self):
+        self.app.defaults['global_proj_item_color'] = self.proj_color_entry.get_value()
+        self.proj_color_button.setStyleSheet(
+            "background-color:%s" % str(self.app.defaults['global_proj_item_color']))
+
+    def on_proj_color_button(self):
+        current_color = QtGui.QColor(self.app.defaults['global_proj_item_color'])
+
+        c_dialog = QtWidgets.QColorDialog()
+        proj_color = c_dialog.getColor(initial=current_color)
+
+        if proj_color.isValid() is False:
+            return
+
+        self.proj_color_button.setStyleSheet("background-color:%s" % str(proj_color.name()))
+
+        new_val_sel = str(proj_color.name())
+        self.proj_color_entry.set_value(new_val_sel)
+        self.app.defaults['global_proj_item_color'] = new_val_sel
+
+    def on_proj_color_dis_entry(self):
+        self.app.defaults['global_proj_item_dis_color'] = self.proj_color_dis_entry.get_value()
+        self.proj_color_dis_button.setStyleSheet(
+            "background-color:%s" % str(self.app.defaults['global_proj_item_dis_color']))
+
+    def on_proj_color_dis_button(self):
+        current_color = QtGui.QColor(self.app.defaults['global_proj_item_dis_color'])
+
+        c_dialog = QtWidgets.QColorDialog()
+        proj_color = c_dialog.getColor(initial=current_color)
+
+        if proj_color.isValid() is False:
+            return
+
+        self.proj_color_dis_button.setStyleSheet("background-color:%s" % str(proj_color.name()))
+
+        new_val_sel = str(proj_color.name())
+        self.proj_color_dis_entry.set_value(new_val_sel)
+        self.app.defaults['global_proj_item_dis_color'] = new_val_sel
+
+    def on_layout(self, index=None, lay=None):
+        """
+        Set the toolbars layout (location)
+
+        :param index:
+        :param lay:     Type of layout to be set on the toolbard
+        :return:        None
+        """
+
+        self.app.defaults.report_usage("on_layout()")
+        if lay:
+            current_layout = lay
+        else:
+            current_layout = self.layout_combo.get_value()
+
+        lay_settings = QSettings("Open Source", "FlatCAM")
+        lay_settings.setValue('layout', current_layout)
+
+        # This will write the setting to the platform specific storage.
+        del lay_settings
+
+        # first remove the toolbars:
+        try:
+            self.app.ui.removeToolBar(self.app.ui.toolbarfile)
+            self.app.ui.removeToolBar(self.app.ui.toolbargeo)
+            self.app.ui.removeToolBar(self.app.ui.toolbarview)
+            self.app.ui.removeToolBar(self.app.ui.toolbarshell)
+            self.app.ui.removeToolBar(self.app.ui.toolbartools)
+            self.app.ui.removeToolBar(self.app.ui.exc_edit_toolbar)
+            self.app.ui.removeToolBar(self.app.ui.geo_edit_toolbar)
+            self.app.ui.removeToolBar(self.app.ui.grb_edit_toolbar)
+            self.app.ui.removeToolBar(self.app.ui.snap_toolbar)
+            self.app.ui.removeToolBar(self.app.ui.toolbarshell)
+        except Exception:
+            pass
+
+        if current_layout == 'compact':
+            # ## TOOLBAR INSTALLATION # ##
+            self.app.ui.toolbarfile = QtWidgets.QToolBar('File Toolbar')
+            self.app.ui.toolbarfile.setObjectName('File_TB')
+            self.app.ui.addToolBar(Qt.LeftToolBarArea, self.app.ui.toolbarfile)
+
+            self.app.ui.toolbargeo = QtWidgets.QToolBar('Edit Toolbar')
+            self.app.ui.toolbargeo.setObjectName('Edit_TB')
+            self.app.ui.addToolBar(Qt.LeftToolBarArea, self.app.ui.toolbargeo)
+
+            self.app.ui.toolbarshell = QtWidgets.QToolBar('Shell Toolbar')
+            self.app.ui.toolbarshell.setObjectName('Shell_TB')
+            self.app.ui.addToolBar(Qt.LeftToolBarArea, self.app.ui.toolbarshell)
+
+            self.app.ui.toolbartools = QtWidgets.QToolBar('Tools Toolbar')
+            self.app.ui.toolbartools.setObjectName('Tools_TB')
+            self.app.ui.addToolBar(Qt.LeftToolBarArea, self.app.ui.toolbartools)
+
+            self.app.ui.geo_edit_toolbar = QtWidgets.QToolBar('Geometry Editor Toolbar')
+            # self.app.ui.geo_edit_toolbar.setVisible(False)
+            self.app.ui.geo_edit_toolbar.setObjectName('GeoEditor_TB')
+            self.app.ui.addToolBar(Qt.RightToolBarArea, self.app.ui.geo_edit_toolbar)
+
+            self.app.ui.toolbarview = QtWidgets.QToolBar('View Toolbar')
+            self.app.ui.toolbarview.setObjectName('View_TB')
+            self.app.ui.addToolBar(Qt.RightToolBarArea, self.app.ui.toolbarview)
+
+            self.app.ui.addToolBarBreak(area=Qt.RightToolBarArea)
+
+            self.app.ui.grb_edit_toolbar = QtWidgets.QToolBar('Gerber Editor Toolbar')
+            # self.app.ui.grb_edit_toolbar.setVisible(False)
+            self.app.ui.grb_edit_toolbar.setObjectName('GrbEditor_TB')
+            self.app.ui.addToolBar(Qt.RightToolBarArea, self.app.ui.grb_edit_toolbar)
+
+            self.app.ui.exc_edit_toolbar = QtWidgets.QToolBar('Excellon Editor Toolbar')
+            self.app.ui.exc_edit_toolbar.setObjectName('ExcEditor_TB')
+            self.app.ui.addToolBar(Qt.RightToolBarArea, self.app.ui.exc_edit_toolbar)
+
+            self.app.ui.snap_toolbar = QtWidgets.QToolBar('Grid Toolbar')
+            self.app.ui.snap_toolbar.setObjectName('Snap_TB')
+            self.app.ui.snap_toolbar.setMaximumHeight(30)
+            self.app.ui.splitter_left.addWidget(self.app.ui.snap_toolbar)
+
+            self.app.ui.corner_snap_btn.setVisible(True)
+            self.app.ui.snap_magnet.setVisible(True)
+        else:
+            # ## TOOLBAR INSTALLATION # ##
+            self.app.ui.toolbarfile = QtWidgets.QToolBar('File Toolbar')
+            self.app.ui.toolbarfile.setObjectName('File_TB')
+            self.app.ui.addToolBar(self.app.ui.toolbarfile)
+
+            self.app.ui.toolbargeo = QtWidgets.QToolBar('Edit Toolbar')
+            self.app.ui.toolbargeo.setObjectName('Edit_TB')
+            self.app.ui.addToolBar(self.app.ui.toolbargeo)
+
+            self.app.ui.toolbarview = QtWidgets.QToolBar('View Toolbar')
+            self.app.ui.toolbarview.setObjectName('View_TB')
+            self.app.ui.addToolBar(self.app.ui.toolbarview)
+
+            self.app.ui.toolbarshell = QtWidgets.QToolBar('Shell Toolbar')
+            self.app.ui.toolbarshell.setObjectName('Shell_TB')
+            self.app.ui.addToolBar(self.app.ui.toolbarshell)
+
+            self.app.ui.toolbartools = QtWidgets.QToolBar('Tools Toolbar')
+            self.app.ui.toolbartools.setObjectName('Tools_TB')
+            self.app.ui.addToolBar(self.app.ui.toolbartools)
+
+            self.app.ui.exc_edit_toolbar = QtWidgets.QToolBar('Excellon Editor Toolbar')
+            # self.app.ui.exc_edit_toolbar.setVisible(False)
+            self.app.ui.exc_edit_toolbar.setObjectName('ExcEditor_TB')
+            self.app.ui.addToolBar(self.app.ui.exc_edit_toolbar)
+
+            self.app.ui.addToolBarBreak()
+
+            self.app.ui.geo_edit_toolbar = QtWidgets.QToolBar('Geometry Editor Toolbar')
+            # self.app.ui.geo_edit_toolbar.setVisible(False)
+            self.app.ui.geo_edit_toolbar.setObjectName('GeoEditor_TB')
+            self.app.ui.addToolBar(self.app.ui.geo_edit_toolbar)
+
+            self.app.ui.grb_edit_toolbar = QtWidgets.QToolBar('Gerber Editor Toolbar')
+            # self.app.ui.grb_edit_toolbar.setVisible(False)
+            self.app.ui.grb_edit_toolbar.setObjectName('GrbEditor_TB')
+            self.app.ui.addToolBar(self.app.ui.grb_edit_toolbar)
+
+            self.app.ui.snap_toolbar = QtWidgets.QToolBar('Grid Toolbar')
+            self.app.ui.snap_toolbar.setObjectName('Snap_TB')
+            # self.app.ui.snap_toolbar.setMaximumHeight(30)
+            self.app.ui.addToolBar(self.app.ui.snap_toolbar)
+
+            self.app.ui.corner_snap_btn.setVisible(False)
+            self.app.ui.snap_magnet.setVisible(False)
+
+        if current_layout == 'minimal':
+            self.app.ui.toolbarview.setVisible(False)
+            self.app.ui.toolbarshell.setVisible(False)
+            self.app.ui.snap_toolbar.setVisible(False)
+            self.app.ui.geo_edit_toolbar.setVisible(False)
+            self.app.ui.grb_edit_toolbar.setVisible(False)
+            self.app.ui.exc_edit_toolbar.setVisible(False)
+            self.app.ui.lock_toolbar(lock=True)
+
+        # add all the actions to the toolbars
+        self.app.ui.populate_toolbars()
+
+        # reconnect all the signals to the toolbar actions
+        self.app.connect_toolbar_signals()
+
+        self.app.ui.grid_snap_btn.setChecked(True)
+        self.app.ui.on_grid_snap_triggered(state=True)
+
+        self.app.ui.grid_gap_x_entry.setText(str(self.app.defaults["global_gridx"]))
+        self.app.ui.grid_gap_y_entry.setText(str(self.app.defaults["global_gridy"]))
+        self.app.ui.snap_max_dist_entry.setText(str(self.app.defaults["global_snap_max"]))
+        self.app.ui.grid_gap_link_cb.setChecked(True)

+ 43 - 0
flatcamGUI/preferences/general/GeneralPreferencesUI.py

@@ -0,0 +1,43 @@
+from PyQt5 import QtWidgets
+from PyQt5.QtCore import QSettings
+
+from flatcamGUI.preferences.general.GeneralAppPrefGroupUI import GeneralAppPrefGroupUI
+from flatcamGUI.preferences.general.GeneralAPPSetGroupUI import GeneralAPPSetGroupUI
+from flatcamGUI.preferences.general.GeneralGUIPrefGroupUI import GeneralGUIPrefGroupUI
+
+import gettext
+import FlatCAMTranslation as fcTranslate
+import builtins
+
+fcTranslate.apply_language('strings')
+if '_' not in builtins.__dict__:
+    _ = gettext.gettext
+
+settings = QSettings("Open Source", "FlatCAM")
+if settings.contains("machinist"):
+    machinist_setting = settings.value('machinist', type=int)
+else:
+    machinist_setting = 0
+
+
+class GeneralPreferencesUI(QtWidgets.QWidget):
+    def __init__(self, decimals, parent=None):
+        QtWidgets.QWidget.__init__(self, parent=parent)
+        self.layout = QtWidgets.QHBoxLayout()
+        self.setLayout(self.layout)
+        self.decimals = decimals
+
+        self.general_app_group = GeneralAppPrefGroupUI(decimals=self.decimals)
+        self.general_app_group.setMinimumWidth(250)
+
+        self.general_gui_group = GeneralGUIPrefGroupUI(decimals=self.decimals)
+        self.general_gui_group.setMinimumWidth(250)
+
+        self.general_app_set_group = GeneralAPPSetGroupUI(decimals=self.decimals)
+        self.general_app_set_group.setMinimumWidth(250)
+
+        self.layout.addWidget(self.general_app_group)
+        self.layout.addWidget(self.general_gui_group)
+        self.layout.addWidget(self.general_app_set_group)
+
+        self.layout.addStretch()

+ 0 - 0
flatcamGUI/preferences/general/__init__.py


+ 246 - 0
flatcamGUI/preferences/geometry/GeometryAdvOptPrefGroupUI.py

@@ -0,0 +1,246 @@
+from PyQt5 import QtWidgets
+from PyQt5.QtCore import QSettings
+
+from flatcamGUI.GUIElements import FCEntry, FloatEntry, FCDoubleSpinner, FCCheckBox, RadioSet, FCLabel
+from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
+
+import gettext
+import FlatCAMTranslation as fcTranslate
+import builtins
+
+fcTranslate.apply_language('strings')
+if '_' not in builtins.__dict__:
+    _ = gettext.gettext
+
+settings = QSettings("Open Source", "FlatCAM")
+if settings.contains("machinist"):
+    machinist_setting = settings.value('machinist', type=int)
+else:
+    machinist_setting = 0
+
+
+class GeometryAdvOptPrefGroupUI(OptionsGroupUI):
+    def __init__(self, decimals=4, parent=None):
+        # OptionsGroupUI.__init__(self, "Geometry Advanced Options Preferences", parent=parent)
+        super(GeometryAdvOptPrefGroupUI, self).__init__(self, parent=parent)
+
+        self.setTitle(str(_("Geometry Adv. Options")))
+        self.decimals = decimals
+
+        # ------------------------------
+        # ## Advanced Options
+        # ------------------------------
+        self.geo_label = QtWidgets.QLabel('<b>%s:</b>' % _('Advanced Options'))
+        self.geo_label.setToolTip(
+            _("A list of Geometry advanced parameters.\n"
+              "Those parameters are available only for\n"
+              "Advanced App. Level.")
+        )
+        self.layout.addWidget(self.geo_label)
+
+        grid1 = QtWidgets.QGridLayout()
+        self.layout.addLayout(grid1)
+
+        # Toolchange X,Y
+        toolchange_xy_label = QtWidgets.QLabel('%s:' % _('Toolchange X-Y'))
+        toolchange_xy_label.setToolTip(
+            _("Toolchange X,Y position.")
+        )
+        grid1.addWidget(toolchange_xy_label, 1, 0)
+        self.toolchangexy_entry = FCEntry()
+        grid1.addWidget(self.toolchangexy_entry, 1, 1)
+
+        # Start move Z
+        startzlabel = QtWidgets.QLabel('%s:' % _('Start Z'))
+        startzlabel.setToolTip(
+            _("Height of the tool just after starting the work.\n"
+              "Delete the value if you don't need this feature.")
+        )
+        grid1.addWidget(startzlabel, 2, 0)
+        self.gstartz_entry = FloatEntry()
+        grid1.addWidget(self.gstartz_entry, 2, 1)
+
+        # Feedrate rapids
+        fr_rapid_label = QtWidgets.QLabel('%s:' % _('Feedrate Rapids'))
+        fr_rapid_label.setToolTip(
+            _("Cutting speed in the XY plane\n"
+              "(in units per minute).\n"
+              "This is for the rapid move G00.\n"
+              "It is useful only for Marlin,\n"
+              "ignore for any other cases.")
+        )
+        self.feedrate_rapid_entry = FCDoubleSpinner()
+        self.feedrate_rapid_entry.set_range(0, 99999.9999)
+        self.feedrate_rapid_entry.set_precision(self.decimals)
+        self.feedrate_rapid_entry.setSingleStep(0.1)
+        self.feedrate_rapid_entry.setWrapping(True)
+
+        grid1.addWidget(fr_rapid_label, 4, 0)
+        grid1.addWidget(self.feedrate_rapid_entry, 4, 1)
+
+        # End move extra cut
+        self.extracut_cb = FCCheckBox('%s' % _('Re-cut'))
+        self.extracut_cb.setToolTip(
+            _("In order to remove possible\n"
+              "copper leftovers where first cut\n"
+              "meet with last cut, we generate an\n"
+              "extended cut over the first cut section.")
+        )
+
+        self.e_cut_entry = FCDoubleSpinner()
+        self.e_cut_entry.set_range(0, 99999)
+        self.e_cut_entry.set_precision(self.decimals)
+        self.e_cut_entry.setSingleStep(0.1)
+        self.e_cut_entry.setWrapping(True)
+        self.e_cut_entry.setToolTip(
+            _("In order to remove possible\n"
+              "copper leftovers where first cut\n"
+              "meet with last cut, we generate an\n"
+              "extended cut over the first cut section.")
+        )
+        grid1.addWidget(self.extracut_cb, 5, 0)
+        grid1.addWidget(self.e_cut_entry, 5, 1)
+
+        # Probe depth
+        self.pdepth_label = QtWidgets.QLabel('%s:' % _("Probe Z depth"))
+        self.pdepth_label.setToolTip(
+            _("The maximum depth that the probe is allowed\n"
+              "to probe. Negative value, in current units.")
+        )
+        self.pdepth_entry = FCDoubleSpinner()
+        self.pdepth_entry.set_range(-99999, 0.0000)
+        self.pdepth_entry.set_precision(self.decimals)
+        self.pdepth_entry.setSingleStep(0.1)
+        self.pdepth_entry.setWrapping(True)
+
+        grid1.addWidget(self.pdepth_label, 6, 0)
+        grid1.addWidget(self.pdepth_entry, 6, 1)
+
+        # Probe feedrate
+        self.feedrate_probe_label = QtWidgets.QLabel('%s:' % _("Feedrate Probe"))
+        self.feedrate_probe_label.setToolTip(
+            _("The feedrate used while the probe is probing.")
+        )
+        self.feedrate_probe_entry = FCDoubleSpinner()
+        self.feedrate_probe_entry.set_range(0, 99999.9999)
+        self.feedrate_probe_entry.set_precision(self.decimals)
+        self.feedrate_probe_entry.setSingleStep(0.1)
+        self.feedrate_probe_entry.setWrapping(True)
+
+        grid1.addWidget(self.feedrate_probe_label, 7, 0)
+        grid1.addWidget(self.feedrate_probe_entry, 7, 1)
+
+        # Spindle direction
+        spindle_dir_label = QtWidgets.QLabel('%s:' % _('Spindle direction'))
+        spindle_dir_label.setToolTip(
+            _("This sets the direction that the spindle is rotating.\n"
+              "It can be either:\n"
+              "- CW = clockwise or\n"
+              "- CCW = counter clockwise")
+        )
+
+        self.spindledir_radio = RadioSet([{'label': _('CW'), 'value': 'CW'},
+                                          {'label': _('CCW'), 'value': 'CCW'}])
+        grid1.addWidget(spindle_dir_label, 8, 0)
+        grid1.addWidget(self.spindledir_radio, 8, 1)
+
+        # Fast Move from Z Toolchange
+        self.fplunge_cb = FCCheckBox('%s' % _('Fast Plunge'))
+        self.fplunge_cb.setToolTip(
+            _("By checking this, the vertical move from\n"
+              "Z_Toolchange to Z_move is done with G0,\n"
+              "meaning the fastest speed available.\n"
+              "WARNING: the move is done at Toolchange X,Y coords.")
+        )
+        grid1.addWidget(self.fplunge_cb, 9, 0, 1, 2)
+
+        # Size of trace segment on X axis
+        segx_label = QtWidgets.QLabel('%s:' % _("Segment X size"))
+        segx_label.setToolTip(
+            _("The size of the trace segment on the X axis.\n"
+              "Useful for auto-leveling.\n"
+              "A value of 0 means no segmentation on the X axis.")
+        )
+        self.segx_entry = FCDoubleSpinner()
+        self.segx_entry.set_range(0, 99999)
+        self.segx_entry.set_precision(self.decimals)
+        self.segx_entry.setSingleStep(0.1)
+        self.segx_entry.setWrapping(True)
+
+        grid1.addWidget(segx_label, 10, 0)
+        grid1.addWidget(self.segx_entry, 10, 1)
+
+        # Size of trace segment on Y axis
+        segy_label = QtWidgets.QLabel('%s:' % _("Segment Y size"))
+        segy_label.setToolTip(
+            _("The size of the trace segment on the Y axis.\n"
+              "Useful for auto-leveling.\n"
+              "A value of 0 means no segmentation on the Y axis.")
+        )
+        self.segy_entry = FCDoubleSpinner()
+        self.segy_entry.set_range(0, 99999)
+        self.segy_entry.set_precision(self.decimals)
+        self.segy_entry.setSingleStep(0.1)
+        self.segy_entry.setWrapping(True)
+
+        grid1.addWidget(segy_label, 11, 0)
+        grid1.addWidget(self.segy_entry, 11, 1)
+
+        # -----------------------------
+        # --- Area Exclusion ----------
+        # -----------------------------
+        self.adv_label = QtWidgets.QLabel('<b>%s:</b>' % _('Area Exclusion'))
+        self.adv_label.setToolTip(
+            _("Area exclusion parameters.\n"
+              "Those parameters are available only for\n"
+              "Advanced App. Level.")
+        )
+        grid1.addWidget(self.adv_label, 12, 0, 1, 2)
+
+        # Exclusion Area CB
+        self.exclusion_cb = FCCheckBox('%s:' % _("Exclusion areas"))
+        self.exclusion_cb.setToolTip(
+            _(
+                "Include exclusion areas.\n"
+                "In those areas the travel of the tools\n"
+                "is forbidden."
+            )
+        )
+        grid1.addWidget(self.exclusion_cb, 13, 0, 1, 2)
+
+        # Area Selection shape
+        self.area_shape_label = QtWidgets.QLabel('%s:' % _("Shape"))
+        self.area_shape_label.setToolTip(
+            _("The kind of selection shape used for area selection.")
+        )
+
+        self.area_shape_radio = RadioSet([{'label': _("Square"), 'value': 'square'},
+                                          {'label': _("Polygon"), 'value': 'polygon'}])
+
+        grid1.addWidget(self.area_shape_label, 14, 0)
+        grid1.addWidget(self.area_shape_radio, 14, 1)
+
+        # Chose Strategy
+        self.strategy_label = FCLabel('%s:' % _("Strategy"))
+        self.strategy_label.setToolTip(_("The strategy followed when encountering an exclusion area.\n"
+                                         "Can be:\n"
+                                         "- Over -> when encountering the area, the tool will go to a set height\n"
+                                         "- Around -> will avoid the exclusion area by going around the area"))
+        self.strategy_radio = RadioSet([{'label': _('Over'), 'value': 'over'},
+                                        {'label': _('Around'), 'value': 'around'}])
+
+        grid1.addWidget(self.strategy_label, 15, 0)
+        grid1.addWidget(self.strategy_radio, 15, 1)
+
+        # Over Z
+        self.over_z_label = FCLabel('%s:' % _("Over Z"))
+        self.over_z_label.setToolTip(_("The height Z to which the tool will rise in order to avoid\n"
+                                       "an interdiction area."))
+        self.over_z_entry = FCDoubleSpinner()
+        self.over_z_entry.set_range(0.000, 9999.9999)
+        self.over_z_entry.set_precision(self.decimals)
+
+        grid1.addWidget(self.over_z_label, 18, 0)
+        grid1.addWidget(self.over_z_entry, 18, 1)
+
+        self.layout.addStretch()

+ 67 - 0
flatcamGUI/preferences/geometry/GeometryEditorPrefGroupUI.py

@@ -0,0 +1,67 @@
+from PyQt5 import QtWidgets
+from PyQt5.QtCore import QSettings
+
+from flatcamGUI.GUIElements import FCSpinner, RadioSet
+from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
+
+import gettext
+import FlatCAMTranslation as fcTranslate
+import builtins
+
+fcTranslate.apply_language('strings')
+if '_' not in builtins.__dict__:
+    _ = gettext.gettext
+
+settings = QSettings("Open Source", "FlatCAM")
+if settings.contains("machinist"):
+    machinist_setting = settings.value('machinist', type=int)
+else:
+    machinist_setting = 0
+
+
+class GeometryEditorPrefGroupUI(OptionsGroupUI):
+    def __init__(self, decimals=4, parent=None):
+        # OptionsGroupUI.__init__(self, "Gerber Adv. Options Preferences", parent=parent)
+        super(GeometryEditorPrefGroupUI, self).__init__(self, parent=parent)
+
+        self.setTitle(str(_("Geometry Editor")))
+        self.decimals = decimals
+
+        # Advanced Geometry Parameters
+        self.param_label = QtWidgets.QLabel("<b>%s:</b>" % _("Parameters"))
+        self.param_label.setToolTip(
+            _("A list of Geometry Editor parameters.")
+        )
+        self.layout.addWidget(self.param_label)
+
+        grid0 = QtWidgets.QGridLayout()
+        self.layout.addLayout(grid0)
+
+        # Selection Limit
+        self.sel_limit_label = QtWidgets.QLabel('%s:' % _("Selection limit"))
+        self.sel_limit_label.setToolTip(
+            _("Set the number of selected geometry\n"
+              "items above which the utility geometry\n"
+              "becomes just a selection rectangle.\n"
+              "Increases the performance when moving a\n"
+              "large number of geometric elements.")
+        )
+        self.sel_limit_entry = FCSpinner()
+        self.sel_limit_entry.set_range(0, 9999)
+
+        grid0.addWidget(self.sel_limit_label, 0, 0)
+        grid0.addWidget(self.sel_limit_entry, 0, 1)
+
+        # Milling Type
+        milling_type_label = QtWidgets.QLabel('%s:' % _('Milling Type'))
+        milling_type_label.setToolTip(
+            _("Milling type:\n"
+              "- climb / best for precision milling and to reduce tool usage\n"
+              "- conventional / useful when there is no backlash compensation")
+        )
+        self.milling_type_radio = RadioSet([{'label': _('Climb'), 'value': 'cl'},
+                                            {'label': _('Conventional'), 'value': 'cv'}])
+        grid0.addWidget(milling_type_label, 1, 0)
+        grid0.addWidget(self.milling_type_radio, 1, 1)
+
+        self.layout.addStretch()

+ 123 - 0
flatcamGUI/preferences/geometry/GeometryGenPrefGroupUI.py

@@ -0,0 +1,123 @@
+from PyQt5 import QtWidgets, QtCore, QtGui
+from PyQt5.QtCore import QSettings
+
+from flatcamGUI.GUIElements import FCCheckBox, FCSpinner, FCEntry
+from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
+
+import gettext
+import FlatCAMTranslation as fcTranslate
+import builtins
+
+fcTranslate.apply_language('strings')
+if '_' not in builtins.__dict__:
+    _ = gettext.gettext
+
+settings = QSettings("Open Source", "FlatCAM")
+if settings.contains("machinist"):
+    machinist_setting = settings.value('machinist', type=int)
+else:
+    machinist_setting = 0
+
+
+class GeometryGenPrefGroupUI(OptionsGroupUI):
+    def __init__(self, decimals=4, parent=None):
+        # OptionsGroupUI.__init__(self, "Geometry General Preferences", parent=parent)
+        super(GeometryGenPrefGroupUI, self).__init__(self, parent=parent)
+
+        self.setTitle(str(_("Geometry General")))
+        self.decimals = decimals
+
+        # ## Plot options
+        self.plot_options_label = QtWidgets.QLabel("<b>%s:</b>" % _("Plot Options"))
+        self.layout.addWidget(self.plot_options_label)
+
+        # Plot CB
+        self.plot_cb = FCCheckBox(label=_('Plot'))
+        self.plot_cb.setToolTip(
+            _("Plot (show) this object.")
+        )
+        self.layout.addWidget(self.plot_cb)
+
+        grid0 = QtWidgets.QGridLayout()
+        self.layout.addLayout(grid0)
+        grid0.setColumnStretch(0, 0)
+        grid0.setColumnStretch(1, 1)
+
+        # Number of circle steps for circular aperture linear approximation
+        self.circle_steps_label = QtWidgets.QLabel('%s:' % _("Circle Steps"))
+        self.circle_steps_label.setToolTip(
+            _("The number of circle steps for <b>Geometry</b> \n"
+              "circle and arc shapes linear approximation.")
+        )
+        self.circle_steps_entry = FCSpinner()
+        self.circle_steps_entry.set_range(0, 999)
+
+        grid0.addWidget(self.circle_steps_label, 1, 0)
+        grid0.addWidget(self.circle_steps_entry, 1, 1)
+
+        # Tools
+        self.tools_label = QtWidgets.QLabel("<b>%s:</b>" % _("Tools"))
+        grid0.addWidget(self.tools_label, 2, 0, 1, 2)
+
+        # Tooldia
+        tdlabel = QtWidgets.QLabel('<b><font color="green">%s:</font></b>' % _('Tools Dia'))
+        tdlabel.setToolTip(
+            _("Diameters of the tools, separated by comma.\n"
+              "The value of the diameter has to use the dot decimals separator.\n"
+              "Valid values: 0.3, 1.0")
+        )
+        self.cnctooldia_entry = FCEntry()
+
+        grid0.addWidget(tdlabel, 3, 0)
+        grid0.addWidget(self.cnctooldia_entry, 3, 1)
+
+        separator_line = QtWidgets.QFrame()
+        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
+        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
+        grid0.addWidget(separator_line, 9, 0, 1, 2)
+
+        # Geometry Object Color
+        self.gerber_color_label = QtWidgets.QLabel('<b>%s</b>' % _('Geometry Object Color'))
+        grid0.addWidget(self.gerber_color_label, 10, 0, 1, 2)
+
+        # Plot Line Color
+        self.line_color_label = QtWidgets.QLabel('%s:' % _('Outline'))
+        self.line_color_label.setToolTip(
+            _("Set the line color for plotted objects.")
+        )
+        self.line_color_entry = FCEntry()
+        self.line_color_button = QtWidgets.QPushButton()
+        self.line_color_button.setFixedSize(15, 15)
+
+        self.form_box_child_2 = QtWidgets.QHBoxLayout()
+        self.form_box_child_2.addWidget(self.line_color_entry)
+        self.form_box_child_2.addWidget(self.line_color_button)
+        self.form_box_child_2.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
+
+        grid0.addWidget(self.line_color_label, 11, 0)
+        grid0.addLayout(self.form_box_child_2, 11, 1)
+
+        self.layout.addStretch()
+
+        # Setting plot colors signals
+        self.line_color_entry.editingFinished.connect(self.on_line_color_entry)
+        self.line_color_button.clicked.connect(self.on_line_color_button)
+
+    def on_line_color_entry(self):
+        self.app.defaults['geometry_plot_line'] = self.line_color_entry.get_value()[:7] + 'FF'
+        self.line_color_button.setStyleSheet("background-color:%s" % str(self.app.defaults['geometry_plot_line'])[:7])
+
+    def on_line_color_button(self):
+        current_color = QtGui.QColor(self.app.defaults['geometry_plot_line'][:7])
+        # print(current_color)
+
+        c_dialog = QtWidgets.QColorDialog()
+        plot_line_color = c_dialog.getColor(initial=current_color)
+
+        if plot_line_color.isValid() is False:
+            return
+
+        self.line_color_button.setStyleSheet("background-color:%s" % str(plot_line_color.name()))
+
+        new_val_line = str(plot_line_color.name()) + str(self.app.defaults['geometry_plot_line'][7:9])
+        self.line_color_entry.set_value(new_val_line)

+ 265 - 0
flatcamGUI/preferences/geometry/GeometryOptPrefGroupUI.py

@@ -0,0 +1,265 @@
+from PyQt5 import QtWidgets
+from PyQt5.QtCore import Qt, QSettings
+
+from flatcamGUI.GUIElements import FCDoubleSpinner, FCCheckBox, OptionalInputSection, FCEntry, FCSpinner, FCComboBox
+from flatcamGUI.preferences import machinist_setting
+from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
+
+import gettext
+import FlatCAMTranslation as fcTranslate
+import builtins
+
+fcTranslate.apply_language('strings')
+if '_' not in builtins.__dict__:
+    _ = gettext.gettext
+
+settings = QSettings("Open Source", "FlatCAM")
+if settings.contains("machinist"):
+    machinist_setting = settings.value('machinist', type=int)
+else:
+    machinist_setting = 0
+
+
+class GeometryOptPrefGroupUI(OptionsGroupUI):
+    def __init__(self, decimals=4, parent=None):
+        # OptionsGroupUI.__init__(self, "Geometry Options Preferences", parent=parent)
+        super(GeometryOptPrefGroupUI, self).__init__(self, parent=parent)
+
+        self.setTitle(str(_("Geometry Options")))
+        self.decimals = decimals
+
+        # ------------------------------
+        # ## Create CNC Job
+        # ------------------------------
+        self.cncjob_label = QtWidgets.QLabel('<b>%s:</b>' % _('Create CNC Job'))
+        self.cncjob_label.setToolTip(
+            _("Create a CNC Job object\n"
+              "tracing the contours of this\n"
+              "Geometry object.")
+        )
+        self.layout.addWidget(self.cncjob_label)
+
+        grid1 = QtWidgets.QGridLayout()
+        self.layout.addLayout(grid1)
+        grid1.setColumnStretch(0, 0)
+        grid1.setColumnStretch(1, 1)
+
+        # Cut Z
+        cutzlabel = QtWidgets.QLabel('%s:' % _('Cut Z'))
+        cutzlabel.setToolTip(
+            _("Cutting depth (negative)\n"
+              "below the copper surface.")
+        )
+        self.cutz_entry = FCDoubleSpinner()
+
+        if machinist_setting == 0:
+            self.cutz_entry.set_range(-9999.9999, 0.0000)
+        else:
+            self.cutz_entry.set_range(-9999.9999, 9999.9999)
+
+        self.cutz_entry.set_precision(self.decimals)
+        self.cutz_entry.setSingleStep(0.1)
+        self.cutz_entry.setWrapping(True)
+
+        grid1.addWidget(cutzlabel, 0, 0)
+        grid1.addWidget(self.cutz_entry, 0, 1)
+
+        # Multidepth CheckBox
+        self.multidepth_cb = FCCheckBox(label=_('Multi-Depth'))
+        self.multidepth_cb.setToolTip(
+            _(
+                "Use multiple passes to limit\n"
+                "the cut depth in each pass. Will\n"
+                "cut multiple times until Cut Z is\n"
+                "reached."
+            )
+        )
+        grid1.addWidget(self.multidepth_cb, 1, 0)
+
+        # Depth/pass
+        dplabel = QtWidgets.QLabel('%s:' % _('Depth/Pass'))
+        dplabel.setToolTip(
+            _("The depth to cut on each pass,\n"
+              "when multidepth is enabled.\n"
+              "It has positive value although\n"
+              "it is a fraction from the depth\n"
+              "which has negative value.")
+        )
+
+        self.depthperpass_entry = FCDoubleSpinner()
+        self.depthperpass_entry.set_range(0, 99999)
+        self.depthperpass_entry.set_precision(self.decimals)
+        self.depthperpass_entry.setSingleStep(0.1)
+        self.depthperpass_entry.setWrapping(True)
+
+        grid1.addWidget(dplabel, 2, 0)
+        grid1.addWidget(self.depthperpass_entry, 2, 1)
+
+        self.ois_multidepth = OptionalInputSection(self.multidepth_cb, [self.depthperpass_entry])
+
+        # Travel Z
+        travelzlabel = QtWidgets.QLabel('%s:' % _('Travel Z'))
+        travelzlabel.setToolTip(
+            _("Height of the tool when\n"
+              "moving without cutting.")
+        )
+        self.travelz_entry = FCDoubleSpinner()
+
+        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(self.decimals)
+        self.travelz_entry.setSingleStep(0.1)
+        self.travelz_entry.setWrapping(True)
+
+        grid1.addWidget(travelzlabel, 3, 0)
+        grid1.addWidget(self.travelz_entry, 3, 1)
+
+        # Tool change:
+        self.toolchange_cb = FCCheckBox('%s' % _("Tool change"))
+        self.toolchange_cb.setToolTip(
+            _(
+                "Include tool-change sequence\n"
+                "in the Machine Code (Pause for tool change)."
+            )
+        )
+        grid1.addWidget(self.toolchange_cb, 4, 0, 1, 2)
+
+        # Toolchange Z
+        toolchangezlabel = QtWidgets.QLabel('%s:' % _('Toolchange Z'))
+        toolchangezlabel.setToolTip(
+            _(
+                "Z-axis position (height) for\n"
+                "tool change."
+            )
+        )
+        self.toolchangez_entry = FCDoubleSpinner()
+
+        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(self.decimals)
+        self.toolchangez_entry.setSingleStep(0.1)
+        self.toolchangez_entry.setWrapping(True)
+
+        grid1.addWidget(toolchangezlabel, 5, 0)
+        grid1.addWidget(self.toolchangez_entry, 5, 1)
+
+        # End move Z
+        endz_label = QtWidgets.QLabel('%s:' % _('End move Z'))
+        endz_label.setToolTip(
+            _("Height of the tool after\n"
+              "the last move at the end of the job.")
+        )
+        self.endz_entry = FCDoubleSpinner()
+
+        if machinist_setting == 0:
+            self.endz_entry.set_range(0.000, 9999.9999)
+        else:
+            self.endz_entry.set_range(-9999.9999, 9999.9999)
+
+        self.endz_entry.set_precision(self.decimals)
+        self.endz_entry.setSingleStep(0.1)
+        self.endz_entry.setWrapping(True)
+
+        grid1.addWidget(endz_label, 6, 0)
+        grid1.addWidget(self.endz_entry, 6, 1)
+
+        # End Move X,Y
+        endmove_xy_label = QtWidgets.QLabel('%s:' % _('End move X,Y'))
+        endmove_xy_label.setToolTip(
+            _("End move X,Y position. In format (x,y).\n"
+              "If no value is entered then there is no move\n"
+              "on X,Y plane at the end of the job.")
+        )
+        self.endxy_entry = FCEntry()
+
+        grid1.addWidget(endmove_xy_label, 7, 0)
+        grid1.addWidget(self.endxy_entry, 7, 1)
+
+        # Feedrate X-Y
+        frlabel = QtWidgets.QLabel('%s:' % _('Feedrate X-Y'))
+        frlabel.setToolTip(
+            _("Cutting speed in the XY\n"
+              "plane in units per minute")
+        )
+        self.cncfeedrate_entry = FCDoubleSpinner()
+        self.cncfeedrate_entry.set_range(0, 99999.9999)
+        self.cncfeedrate_entry.set_precision(self.decimals)
+        self.cncfeedrate_entry.setSingleStep(0.1)
+        self.cncfeedrate_entry.setWrapping(True)
+
+        grid1.addWidget(frlabel, 8, 0)
+        grid1.addWidget(self.cncfeedrate_entry, 8, 1)
+
+        # Feedrate Z (Plunge)
+        frz_label = QtWidgets.QLabel('%s:' % _('Feedrate Z'))
+        frz_label.setToolTip(
+            _("Cutting speed in the XY\n"
+              "plane in units per minute.\n"
+              "It is called also Plunge.")
+        )
+        self.feedrate_z_entry = FCDoubleSpinner()
+        self.feedrate_z_entry.set_range(0, 99999.9999)
+        self.feedrate_z_entry.set_precision(self.decimals)
+        self.feedrate_z_entry.setSingleStep(0.1)
+        self.feedrate_z_entry.setWrapping(True)
+
+        grid1.addWidget(frz_label, 9, 0)
+        grid1.addWidget(self.feedrate_z_entry, 9, 1)
+
+        # Spindle Speed
+        spdlabel = QtWidgets.QLabel('%s:' % _('Spindle speed'))
+        spdlabel.setToolTip(
+            _(
+                "Speed of the spindle in RPM (optional).\n"
+                "If LASER preprocessor is used,\n"
+                "this value is the power of laser."
+            )
+        )
+        self.cncspindlespeed_entry = FCSpinner()
+        self.cncspindlespeed_entry.set_range(0, 1000000)
+        self.cncspindlespeed_entry.set_step(100)
+
+        grid1.addWidget(spdlabel, 10, 0)
+        grid1.addWidget(self.cncspindlespeed_entry, 10, 1)
+
+        # Dwell
+        self.dwell_cb = FCCheckBox(label='%s' % _('Enable Dwell'))
+        self.dwell_cb.setToolTip(
+            _("Pause to allow the spindle to reach its\n"
+              "speed before cutting.")
+        )
+        dwelltime = QtWidgets.QLabel('%s:' % _('Duration'))
+        dwelltime.setToolTip(
+            _("Number of time units for spindle to dwell.")
+        )
+        self.dwelltime_entry = FCDoubleSpinner()
+        self.dwelltime_entry.set_range(0, 99999)
+        self.dwelltime_entry.set_precision(self.decimals)
+        self.dwelltime_entry.setSingleStep(0.1)
+        self.dwelltime_entry.setWrapping(True)
+
+        grid1.addWidget(self.dwell_cb, 11, 0)
+        grid1.addWidget(dwelltime, 12, 0)
+        grid1.addWidget(self.dwelltime_entry, 12, 1)
+
+        self.ois_dwell = OptionalInputSection(self.dwell_cb, [self.dwelltime_entry])
+
+        # preprocessor selection
+        pp_label = QtWidgets.QLabel('%s:' % _("Preprocessor"))
+        pp_label.setToolTip(
+            _("The Preprocessor file that dictates\n"
+              "the Machine Code (like GCode, RML, HPGL) output.")
+        )
+        self.pp_geometry_name_cb = FCComboBox()
+        self.pp_geometry_name_cb.setFocusPolicy(Qt.StrongFocus)
+
+        grid1.addWidget(pp_label, 13, 0)
+        grid1.addWidget(self.pp_geometry_name_cb, 13, 1)
+
+        self.layout.addStretch()

+ 46 - 0
flatcamGUI/preferences/geometry/GeometryPreferencesUI.py

@@ -0,0 +1,46 @@
+from PyQt5 import QtWidgets
+from PyQt5.QtCore import QSettings
+
+from flatcamGUI.preferences.geometry.GeometryEditorPrefGroupUI import GeometryEditorPrefGroupUI
+from flatcamGUI.preferences.geometry.GeometryAdvOptPrefGroupUI import GeometryAdvOptPrefGroupUI
+from flatcamGUI.preferences.geometry.GeometryOptPrefGroupUI import GeometryOptPrefGroupUI
+from flatcamGUI.preferences.geometry.GeometryGenPrefGroupUI import GeometryGenPrefGroupUI
+
+import gettext
+import FlatCAMTranslation as fcTranslate
+import builtins
+
+fcTranslate.apply_language('strings')
+if '_' not in builtins.__dict__:
+    _ = gettext.gettext
+
+settings = QSettings("Open Source", "FlatCAM")
+if settings.contains("machinist"):
+    machinist_setting = settings.value('machinist', type=int)
+else:
+    machinist_setting = 0
+
+
+class GeometryPreferencesUI(QtWidgets.QWidget):
+
+    def __init__(self, decimals, parent=None):
+        QtWidgets.QWidget.__init__(self, parent=parent)
+        self.layout = QtWidgets.QHBoxLayout()
+        self.setLayout(self.layout)
+        self.decimals = decimals
+
+        self.geometry_gen_group = GeometryGenPrefGroupUI(decimals=self.decimals)
+        self.geometry_gen_group.setMinimumWidth(220)
+        self.geometry_opt_group = GeometryOptPrefGroupUI(decimals=self.decimals)
+        self.geometry_opt_group.setMinimumWidth(300)
+        self.geometry_adv_opt_group = GeometryAdvOptPrefGroupUI(decimals=self.decimals)
+        self.geometry_adv_opt_group.setMinimumWidth(270)
+        self.geometry_editor_group = GeometryEditorPrefGroupUI(decimals=self.decimals)
+        self.geometry_editor_group.setMinimumWidth(250)
+
+        self.layout.addWidget(self.geometry_gen_group)
+        self.layout.addWidget(self.geometry_opt_group)
+        self.layout.addWidget(self.geometry_adv_opt_group)
+        self.layout.addWidget(self.geometry_editor_group)
+
+        self.layout.addStretch()

+ 0 - 0
flatcamGUI/preferences/geometry/__init__.py


+ 186 - 0
flatcamGUI/preferences/gerber/GerberAdvOptPrefGroupUI.py

@@ -0,0 +1,186 @@
+from PyQt5 import QtWidgets
+from PyQt5.QtCore import QSettings
+
+from flatcamGUI.GUIElements import FCCheckBox, RadioSet, FCDoubleSpinner, FCSpinner, OptionalInputSection
+from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
+
+import gettext
+import FlatCAMTranslation as fcTranslate
+import builtins
+
+fcTranslate.apply_language('strings')
+if '_' not in builtins.__dict__:
+    _ = gettext.gettext
+
+settings = QSettings("Open Source", "FlatCAM")
+if settings.contains("machinist"):
+    machinist_setting = settings.value('machinist', type=int)
+else:
+    machinist_setting = 0
+
+
+class GerberAdvOptPrefGroupUI(OptionsGroupUI):
+    def __init__(self, decimals=4, parent=None):
+        # OptionsGroupUI.__init__(self, "Gerber Adv. Options Preferences", parent=parent)
+        super(GerberAdvOptPrefGroupUI, self).__init__(self, parent=parent)
+
+        self.setTitle(str(_("Gerber Adv. Options")))
+        self.decimals = decimals
+
+        # ## Advanced Gerber Parameters
+        self.adv_param_label = QtWidgets.QLabel('<b>%s:</b>' % _('Advanced Options'))
+        self.adv_param_label.setToolTip(
+            _("A list of Gerber advanced parameters.\n"
+              "Those parameters are available only for\n"
+              "Advanced App. Level.")
+        )
+        self.layout.addWidget(self.adv_param_label)
+
+        grid0 = QtWidgets.QGridLayout()
+        self.layout.addLayout(grid0)
+
+        # Follow Attribute
+        self.follow_cb = FCCheckBox(label=_('"Follow"'))
+        self.follow_cb.setToolTip(
+            _("Generate a 'Follow' geometry.\n"
+              "This means that it will cut through\n"
+              "the middle of the trace.")
+        )
+        grid0.addWidget(self.follow_cb, 0, 0, 1, 2)
+
+        # Aperture Table Visibility CB
+        self.aperture_table_visibility_cb = FCCheckBox(label=_('Table Show/Hide'))
+        self.aperture_table_visibility_cb.setToolTip(
+            _("Toggle the display of the Gerber Apertures Table.\n"
+              "Also, on hide, it will delete all mark shapes\n"
+              "that are drawn on canvas.")
+
+        )
+        grid0.addWidget(self.aperture_table_visibility_cb, 1, 0, 1, 2)
+
+        separator_line = QtWidgets.QFrame()
+        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
+        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
+        grid0.addWidget(separator_line, 2, 0, 1, 2)
+
+        # Tool Type
+        self.tool_type_label = QtWidgets.QLabel('<b>%s</b>' % _('Tool Type'))
+        self.tool_type_label.setToolTip(
+            _("Choose which tool to use for Gerber isolation:\n"
+              "'Circular' or 'V-shape'.\n"
+              "When the 'V-shape' is selected then the tool\n"
+              "diameter will depend on the chosen cut depth.")
+        )
+        self.tool_type_radio = RadioSet([{'label': 'Circular', 'value': 'circular'},
+                                         {'label': 'V-Shape', 'value': 'v'}])
+
+        grid0.addWidget(self.tool_type_label, 3, 0)
+        grid0.addWidget(self.tool_type_radio, 3, 1, 1, 2)
+
+        # Tip Dia
+        self.tipdialabel = QtWidgets.QLabel('%s:' % _('V-Tip Dia'))
+        self.tipdialabel.setToolTip(
+            _("The tip diameter for V-Shape Tool")
+        )
+        self.tipdia_spinner = FCDoubleSpinner()
+        self.tipdia_spinner.set_precision(self.decimals)
+        self.tipdia_spinner.set_range(-99.9999, 99.9999)
+        self.tipdia_spinner.setSingleStep(0.1)
+        self.tipdia_spinner.setWrapping(True)
+        grid0.addWidget(self.tipdialabel, 4, 0)
+        grid0.addWidget(self.tipdia_spinner, 4, 1, 1, 2)
+
+        # Tip Angle
+        self.tipanglelabel = QtWidgets.QLabel('%s:' % _('V-Tip Angle'))
+        self.tipanglelabel.setToolTip(
+            _("The tip angle for V-Shape Tool.\n"
+              "In degree.")
+        )
+        self.tipangle_spinner = FCSpinner()
+        self.tipangle_spinner.set_range(1, 180)
+        self.tipangle_spinner.set_step(5)
+        self.tipangle_spinner.setWrapping(True)
+        grid0.addWidget(self.tipanglelabel, 5, 0)
+        grid0.addWidget(self.tipangle_spinner, 5, 1, 1, 2)
+
+        # Cut Z
+        self.cutzlabel = QtWidgets.QLabel('%s:' % _('Cut Z'))
+        self.cutzlabel.setToolTip(
+            _("Cutting depth (negative)\n"
+              "below the copper surface.")
+        )
+        self.cutz_spinner = FCDoubleSpinner()
+        self.cutz_spinner.set_precision(self.decimals)
+        self.cutz_spinner.set_range(-99.9999, 0.0000)
+        self.cutz_spinner.setSingleStep(0.1)
+        self.cutz_spinner.setWrapping(True)
+
+        grid0.addWidget(self.cutzlabel, 6, 0)
+        grid0.addWidget(self.cutz_spinner, 6, 1, 1, 2)
+
+        # Isolation Type
+        self.iso_type_label = QtWidgets.QLabel('%s:' % _('Isolation Type'))
+        self.iso_type_label.setToolTip(
+            _("Choose how the isolation will be executed:\n"
+              "- 'Full' -> complete isolation of polygons\n"
+              "- 'Ext' -> will isolate only on the outside\n"
+              "- 'Int' -> will isolate only on the inside\n"
+              "'Exterior' isolation is almost always possible\n"
+              "(with the right tool) but 'Interior'\n"
+              "isolation can be done only when there is an opening\n"
+              "inside of the polygon (e.g polygon is a 'doughnut' shape).")
+        )
+        self.iso_type_radio = RadioSet([{'label': _('Full'), 'value': 'full'},
+                                        {'label': _('Exterior'), 'value': 'ext'},
+                                        {'label': _('Interior'), 'value': 'int'}])
+
+        grid0.addWidget(self.iso_type_label, 7, 0,)
+        grid0.addWidget(self.iso_type_radio, 7, 1, 1, 2)
+
+        separator_line = QtWidgets.QFrame()
+        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
+        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
+        grid0.addWidget(separator_line, 8, 0, 1, 2)
+
+        # Buffering Type
+        buffering_label = QtWidgets.QLabel('%s:' % _('Buffering'))
+        buffering_label.setToolTip(
+            _("Buffering type:\n"
+              "- None --> best performance, fast file loading but no so good display\n"
+              "- Full --> slow file loading but good visuals. This is the default.\n"
+              "<<WARNING>>: Don't change this unless you know what you are doing !!!")
+        )
+        self.buffering_radio = RadioSet([{'label': _('None'), 'value': 'no'},
+                                         {'label': _('Full'), 'value': 'full'}])
+        grid0.addWidget(buffering_label, 9, 0)
+        grid0.addWidget(self.buffering_radio, 9, 1)
+
+        # Simplification
+        self.simplify_cb = FCCheckBox(label=_('Simplify'))
+        self.simplify_cb.setToolTip(
+            _("When checked all the Gerber polygons will be\n"
+              "loaded with simplification having a set tolerance.\n"
+              "<<WARNING>>: Don't change this unless you know what you are doing !!!")
+                                    )
+        grid0.addWidget(self.simplify_cb, 10, 0, 1, 2)
+
+        # Simplification tolerance
+        self.simplification_tol_label = QtWidgets.QLabel(_('Tolerance'))
+        self.simplification_tol_label.setToolTip(_("Tolerance for polygon simplification."))
+
+        self.simplification_tol_spinner = FCDoubleSpinner()
+        self.simplification_tol_spinner.set_precision(self.decimals + 1)
+        self.simplification_tol_spinner.setWrapping(True)
+        self.simplification_tol_spinner.setRange(0.00000, 0.01000)
+        self.simplification_tol_spinner.setSingleStep(0.0001)
+
+        grid0.addWidget(self.simplification_tol_label, 11, 0)
+        grid0.addWidget(self.simplification_tol_spinner, 11, 1)
+        self.ois_simplif = OptionalInputSection(
+            self.simplify_cb,
+            [
+                self.simplification_tol_label, self.simplification_tol_spinner
+            ],
+            logic=True)
+
+        self.layout.addStretch()

+ 247 - 0
flatcamGUI/preferences/gerber/GerberEditorPrefGroupUI.py

@@ -0,0 +1,247 @@
+from PyQt5 import QtWidgets
+from PyQt5.QtCore import QSettings
+
+from flatcamGUI.GUIElements import FCSpinner, FCDoubleSpinner, FCComboBox, FCEntry, RadioSet
+from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
+
+import gettext
+import FlatCAMTranslation as fcTranslate
+import builtins
+
+fcTranslate.apply_language('strings')
+if '_' not in builtins.__dict__:
+    _ = gettext.gettext
+
+settings = QSettings("Open Source", "FlatCAM")
+if settings.contains("machinist"):
+    machinist_setting = settings.value('machinist', type=int)
+else:
+    machinist_setting = 0
+
+
+class GerberEditorPrefGroupUI(OptionsGroupUI):
+    def __init__(self, decimals=4, parent=None):
+        # OptionsGroupUI.__init__(self, "Gerber Adv. Options Preferences", parent=parent)
+        super(GerberEditorPrefGroupUI, self).__init__(self, parent=parent)
+
+        self.setTitle(str(_("Gerber Editor")))
+        self.decimals = decimals
+
+        # Advanced Gerber Parameters
+        self.param_label = QtWidgets.QLabel("<b>%s:</b>" % _("Parameters"))
+        self.param_label.setToolTip(
+            _("A list of Gerber Editor parameters.")
+        )
+        self.layout.addWidget(self.param_label)
+
+        grid0 = QtWidgets.QGridLayout()
+        self.layout.addLayout(grid0)
+
+        # Selection Limit
+        self.sel_limit_label = QtWidgets.QLabel('%s:' % _("Selection limit"))
+        self.sel_limit_label.setToolTip(
+            _("Set the number of selected Gerber geometry\n"
+              "items above which the utility geometry\n"
+              "becomes just a selection rectangle.\n"
+              "Increases the performance when moving a\n"
+              "large number of geometric elements.")
+        )
+        self.sel_limit_entry = FCSpinner()
+        self.sel_limit_entry.set_range(0, 9999)
+
+        grid0.addWidget(self.sel_limit_label, 0, 0)
+        grid0.addWidget(self.sel_limit_entry, 0, 1)
+
+        # New aperture code
+        self.addcode_entry_lbl = QtWidgets.QLabel('%s:' % _('New Aperture code'))
+        self.addcode_entry_lbl.setToolTip(
+            _("Code for the new aperture")
+        )
+
+        self.addcode_entry = FCSpinner()
+        self.addcode_entry.set_range(10, 99)
+        self.addcode_entry.setWrapping(True)
+
+        grid0.addWidget(self.addcode_entry_lbl, 1, 0)
+        grid0.addWidget(self.addcode_entry, 1, 1)
+
+        # New aperture size
+        self.addsize_entry_lbl = QtWidgets.QLabel('%s:' % _('New Aperture size'))
+        self.addsize_entry_lbl.setToolTip(
+            _("Size for the new aperture")
+        )
+
+        self.addsize_entry = FCDoubleSpinner()
+        self.addsize_entry.set_range(0, 100)
+        self.addsize_entry.set_precision(self.decimals)
+
+        grid0.addWidget(self.addsize_entry_lbl, 2, 0)
+        grid0.addWidget(self.addsize_entry, 2, 1)
+
+        # New aperture type
+        self.addtype_combo_lbl = QtWidgets.QLabel('%s:' % _('New Aperture type'))
+        self.addtype_combo_lbl.setToolTip(
+            _("Type for the new aperture.\n"
+              "Can be 'C', 'R' or 'O'.")
+        )
+
+        self.addtype_combo = FCComboBox()
+        self.addtype_combo.addItems(['C', 'R', 'O'])
+
+        grid0.addWidget(self.addtype_combo_lbl, 3, 0)
+        grid0.addWidget(self.addtype_combo, 3, 1)
+
+        # Number of pads in a pad array
+        self.grb_array_size_label = QtWidgets.QLabel('%s:' % _('Nr of pads'))
+        self.grb_array_size_label.setToolTip(
+            _("Specify how many pads to be in the array.")
+        )
+
+        self.grb_array_size_entry = FCSpinner()
+        self.grb_array_size_entry.set_range(0, 9999)
+
+        grid0.addWidget(self.grb_array_size_label, 4, 0)
+        grid0.addWidget(self.grb_array_size_entry, 4, 1)
+
+        self.adddim_label = QtWidgets.QLabel('%s:' % _('Aperture Dimensions'))
+        self.adddim_label.setToolTip(
+            _("Diameters of the tools, separated by comma.\n"
+              "The value of the diameter has to use the dot decimals separator.\n"
+              "Valid values: 0.3, 1.0")
+        )
+        grid0.addWidget(self.adddim_label, 5, 0)
+        self.adddim_entry = FCEntry()
+        grid0.addWidget(self.adddim_entry, 5, 1)
+
+        self.grb_array_linear_label = QtWidgets.QLabel('<b>%s:</b>' % _('Linear Pad Array'))
+        grid0.addWidget(self.grb_array_linear_label, 6, 0, 1, 2)
+
+        # Linear Pad Array direction
+        self.grb_axis_label = QtWidgets.QLabel('%s:' % _('Linear Direction'))
+        self.grb_axis_label.setToolTip(
+            _("Direction on which the linear array is oriented:\n"
+              "- 'X' - horizontal axis \n"
+              "- 'Y' - vertical axis or \n"
+              "- 'Angle' - a custom angle for the array inclination")
+        )
+
+        self.grb_axis_radio = RadioSet([{'label': _('X'), 'value': 'X'},
+                                        {'label': _('Y'), 'value': 'Y'},
+                                        {'label': _('Angle'), 'value': 'A'}])
+
+        grid0.addWidget(self.grb_axis_label, 7, 0)
+        grid0.addWidget(self.grb_axis_radio, 7, 1)
+
+        # Linear Pad Array pitch distance
+        self.grb_pitch_label = QtWidgets.QLabel('%s:' % _('Pitch'))
+        self.grb_pitch_label.setToolTip(
+            _("Pitch = Distance between elements of the array.")
+        )
+        # self.drill_pitch_label.setMinimumWidth(100)
+        self.grb_pitch_entry = FCDoubleSpinner()
+        self.grb_pitch_entry.set_precision(self.decimals)
+
+        grid0.addWidget(self.grb_pitch_label, 8, 0)
+        grid0.addWidget(self.grb_pitch_entry, 8, 1)
+
+        # Linear Pad Array custom angle
+        self.grb_angle_label = QtWidgets.QLabel('%s:' % _('Angle'))
+        self.grb_angle_label.setToolTip(
+            _("Angle at which each element in circular array is placed.")
+        )
+        self.grb_angle_entry = FCDoubleSpinner()
+        self.grb_angle_entry.set_precision(self.decimals)
+        self.grb_angle_entry.set_range(-360, 360)
+        self.grb_angle_entry.setSingleStep(5)
+
+        grid0.addWidget(self.grb_angle_label, 9, 0)
+        grid0.addWidget(self.grb_angle_entry, 9, 1)
+
+        self.grb_array_circ_label = QtWidgets.QLabel('<b>%s:</b>' % _('Circular Pad Array'))
+        grid0.addWidget(self.grb_array_circ_label, 10, 0, 1, 2)
+
+        # Circular Pad Array direction
+        self.grb_circular_direction_label = QtWidgets.QLabel('%s:' % _('Circular Direction'))
+        self.grb_circular_direction_label.setToolTip(
+            _("Direction for circular array.\n"
+              "Can be CW = clockwise or CCW = counter clockwise.")
+        )
+
+        self.grb_circular_dir_radio = RadioSet([{'label': _('CW'), 'value': 'CW'},
+                                                {'label': _('CCW'), 'value': 'CCW'}])
+
+        grid0.addWidget(self.grb_circular_direction_label, 11, 0)
+        grid0.addWidget(self.grb_circular_dir_radio, 11, 1)
+
+        # Circular Pad Array Angle
+        self.grb_circular_angle_label = QtWidgets.QLabel('%s:' % _('Circular Angle'))
+        self.grb_circular_angle_label.setToolTip(
+            _("Angle at which each element in circular array is placed.")
+        )
+        self.grb_circular_angle_entry = FCDoubleSpinner()
+        self.grb_circular_angle_entry.set_precision(self.decimals)
+        self.grb_circular_angle_entry.set_range(-360, 360)
+
+        self.grb_circular_angle_entry.setSingleStep(5)
+
+        grid0.addWidget(self.grb_circular_angle_label, 12, 0)
+        grid0.addWidget(self.grb_circular_angle_entry, 12, 1)
+
+        self.grb_array_tools_b_label = QtWidgets.QLabel('<b>%s:</b>' % _('Buffer Tool'))
+        grid0.addWidget(self.grb_array_tools_b_label, 13, 0, 1, 2)
+
+        # Buffer Distance
+        self.grb_buff_label = QtWidgets.QLabel('%s:' % _('Buffer distance'))
+        self.grb_buff_label.setToolTip(
+            _("Distance at which to buffer the Gerber element.")
+        )
+        self.grb_buff_entry = FCDoubleSpinner()
+        self.grb_buff_entry.set_precision(self.decimals)
+        self.grb_buff_entry.set_range(-9999, 9999)
+
+        grid0.addWidget(self.grb_buff_label, 14, 0)
+        grid0.addWidget(self.grb_buff_entry, 14, 1)
+
+        self.grb_array_tools_s_label = QtWidgets.QLabel('<b>%s:</b>' % _('Scale Tool'))
+        grid0.addWidget(self.grb_array_tools_s_label, 15, 0, 1, 2)
+
+        # Scale Factor
+        self.grb_scale_label = QtWidgets.QLabel('%s:' % _('Scale factor'))
+        self.grb_scale_label.setToolTip(
+            _("Factor to scale the Gerber element.")
+        )
+        self.grb_scale_entry = FCDoubleSpinner()
+        self.grb_scale_entry.set_precision(self.decimals)
+        self.grb_scale_entry.set_range(0, 9999)
+
+        grid0.addWidget(self.grb_scale_label, 16, 0)
+        grid0.addWidget(self.grb_scale_entry, 16, 1)
+
+        self.grb_array_tools_ma_label = QtWidgets.QLabel('<b>%s:</b>' % _('Mark Area Tool'))
+        grid0.addWidget(self.grb_array_tools_ma_label, 17, 0, 1, 2)
+
+        # Mark area Tool low threshold
+        self.grb_ma_low_label = QtWidgets.QLabel('%s:' % _('Threshold low'))
+        self.grb_ma_low_label.setToolTip(
+            _("Threshold value under which the apertures are not marked.")
+        )
+        self.grb_ma_low_entry = FCDoubleSpinner()
+        self.grb_ma_low_entry.set_precision(self.decimals)
+        self.grb_ma_low_entry.set_range(0, 9999)
+
+        grid0.addWidget(self.grb_ma_low_label, 18, 0)
+        grid0.addWidget(self.grb_ma_low_entry, 18, 1)
+
+        # Mark area Tool high threshold
+        self.grb_ma_high_label = QtWidgets.QLabel('%s:' % _('Threshold high'))
+        self.grb_ma_high_label.setToolTip(
+            _("Threshold value over which the apertures are not marked.")
+        )
+        self.grb_ma_high_entry = FCDoubleSpinner()
+        self.grb_ma_high_entry.set_precision(self.decimals)
+        self.grb_ma_high_entry.set_range(0, 9999)
+
+        grid0.addWidget(self.grb_ma_high_label, 19, 0)
+        grid0.addWidget(self.grb_ma_high_entry, 19, 1)
+
+        self.layout.addStretch()

+ 118 - 0
flatcamGUI/preferences/gerber/GerberExpPrefGroupUI.py

@@ -0,0 +1,118 @@
+from PyQt5 import QtWidgets, QtCore
+from PyQt5.QtCore import QSettings
+
+from flatcamGUI.GUIElements import RadioSet, FCSpinner
+from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
+
+import gettext
+import FlatCAMTranslation as fcTranslate
+import builtins
+
+fcTranslate.apply_language('strings')
+if '_' not in builtins.__dict__:
+    _ = gettext.gettext
+
+settings = QSettings("Open Source", "FlatCAM")
+if settings.contains("machinist"):
+    machinist_setting = settings.value('machinist', type=int)
+else:
+    machinist_setting = 0
+
+
+class GerberExpPrefGroupUI(OptionsGroupUI):
+
+    def __init__(self, decimals=4, parent=None):
+        super(GerberExpPrefGroupUI, self).__init__(self, parent=parent)
+
+        self.setTitle(str(_("Gerber Export")))
+        self.decimals = decimals
+
+        # Plot options
+        self.export_options_label = QtWidgets.QLabel("<b>%s:</b>" % _("Export Options"))
+        self.export_options_label.setToolTip(
+            _("The parameters set here are used in the file exported\n"
+              "when using the File -> Export -> Export Gerber menu entry.")
+        )
+        self.layout.addWidget(self.export_options_label)
+
+        form = QtWidgets.QFormLayout()
+        self.layout.addLayout(form)
+
+        # Gerber Units
+        self.gerber_units_label = QtWidgets.QLabel('%s:' % _('Units'))
+        self.gerber_units_label.setToolTip(
+            _("The units used in the Gerber file.")
+        )
+
+        self.gerber_units_radio = RadioSet([{'label': _('INCH'), 'value': 'IN'},
+                                            {'label': _('MM'), 'value': 'MM'}])
+        self.gerber_units_radio.setToolTip(
+            _("The units used in the Gerber file.")
+        )
+
+        form.addRow(self.gerber_units_label, self.gerber_units_radio)
+
+        # Gerber format
+        self.digits_label = QtWidgets.QLabel("%s:" % _("Int/Decimals"))
+        self.digits_label.setToolTip(
+            _("The number of digits in the whole part of the number\n"
+              "and in the fractional part of the number.")
+        )
+
+        hlay1 = QtWidgets.QHBoxLayout()
+
+        self.format_whole_entry = FCSpinner()
+        self.format_whole_entry.set_range(0, 9)
+        self.format_whole_entry.set_step(1)
+        self.format_whole_entry.setWrapping(True)
+
+        self.format_whole_entry.setMinimumWidth(30)
+        self.format_whole_entry.setToolTip(
+            _("This numbers signify the number of digits in\n"
+              "the whole part of Gerber coordinates.")
+        )
+        hlay1.addWidget(self.format_whole_entry, QtCore.Qt.AlignLeft)
+
+        gerber_separator_label = QtWidgets.QLabel(':')
+        gerber_separator_label.setFixedWidth(5)
+        hlay1.addWidget(gerber_separator_label, QtCore.Qt.AlignLeft)
+
+        self.format_dec_entry = FCSpinner()
+        self.format_dec_entry.set_range(0, 9)
+        self.format_dec_entry.set_step(1)
+        self.format_dec_entry.setWrapping(True)
+
+        self.format_dec_entry.setMinimumWidth(30)
+        self.format_dec_entry.setToolTip(
+            _("This numbers signify the number of digits in\n"
+              "the decimal part of Gerber coordinates.")
+        )
+        hlay1.addWidget(self.format_dec_entry, QtCore.Qt.AlignLeft)
+        hlay1.addStretch()
+
+        form.addRow(self.digits_label, hlay1)
+
+        # Gerber Zeros
+        self.zeros_label = QtWidgets.QLabel('%s:' % _('Zeros'))
+        self.zeros_label.setAlignment(QtCore.Qt.AlignLeft)
+        self.zeros_label.setToolTip(
+            _("This sets the type of Gerber zeros.\n"
+              "If LZ then Leading Zeros are removed and\n"
+              "Trailing Zeros are kept.\n"
+              "If TZ is checked then Trailing Zeros are removed\n"
+              "and Leading Zeros are kept.")
+        )
+
+        self.zeros_radio = RadioSet([{'label': _('LZ'), 'value': 'L'},
+                                     {'label': _('TZ'), 'value': 'T'}])
+        self.zeros_radio.setToolTip(
+            _("This sets the type of Gerber zeros.\n"
+              "If LZ then Leading Zeros are removed and\n"
+              "Trailing Zeros are kept.\n"
+              "If TZ is checked then Trailing Zeros are removed\n"
+              "and Leading Zeros are kept.")
+        )
+
+        form.addRow(self.zeros_label, self.zeros_radio)
+
+        self.layout.addStretch()

+ 273 - 0
flatcamGUI/preferences/gerber/GerberGenPrefGroupUI.py

@@ -0,0 +1,273 @@
+from PyQt5 import QtWidgets, QtCore, QtGui
+from PyQt5.QtCore import QSettings
+
+from flatcamGUI.GUIElements import FCCheckBox, FCSpinner, RadioSet, FCEntry
+from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
+
+import gettext
+import FlatCAMTranslation as fcTranslate
+import builtins
+
+fcTranslate.apply_language('strings')
+if '_' not in builtins.__dict__:
+    _ = gettext.gettext
+
+settings = QSettings("Open Source", "FlatCAM")
+if settings.contains("machinist"):
+    machinist_setting = settings.value('machinist', type=int)
+else:
+    machinist_setting = 0
+
+
+class GerberGenPrefGroupUI(OptionsGroupUI):
+    def __init__(self, decimals=4, parent=None):
+        # OptionsGroupUI.__init__(self, "Gerber General Preferences", parent=parent)
+        super(GerberGenPrefGroupUI, self).__init__(self, parent=parent)
+
+        self.setTitle(str(_("Gerber General")))
+        self.decimals = decimals
+
+        # ## Plot options
+        self.plot_options_label = QtWidgets.QLabel("<b>%s:</b>" % _("Plot Options"))
+        self.layout.addWidget(self.plot_options_label)
+
+        grid0 = QtWidgets.QGridLayout()
+        self.layout.addLayout(grid0)
+
+        # Solid CB
+        self.solid_cb = FCCheckBox(label='%s' % _('Solid'))
+        self.solid_cb.setToolTip(
+            _("Solid color polygons.")
+        )
+        grid0.addWidget(self.solid_cb, 0, 0)
+
+        # Multicolored CB
+        self.multicolored_cb = FCCheckBox(label='%s' % _('M-Color'))
+        self.multicolored_cb.setToolTip(
+            _("Draw polygons in different colors.")
+        )
+        grid0.addWidget(self.multicolored_cb, 0, 1)
+
+        # Plot CB
+        self.plot_cb = FCCheckBox(label='%s' % _('Plot'))
+        self.plot_options_label.setToolTip(
+            _("Plot (show) this object.")
+        )
+        grid0.addWidget(self.plot_cb, 0, 2)
+
+        # Number of circle steps for circular aperture linear approximation
+        self.circle_steps_label = QtWidgets.QLabel('%s:' % _("Circle Steps"))
+        self.circle_steps_label.setToolTip(
+            _("The number of circle steps for Gerber \n"
+              "circular aperture linear approximation.")
+        )
+        self.circle_steps_entry = FCSpinner()
+        self.circle_steps_entry.set_range(0, 9999)
+
+        grid0.addWidget(self.circle_steps_label, 1, 0)
+        grid0.addWidget(self.circle_steps_entry, 1, 1, 1, 2)
+
+        grid0.addWidget(QtWidgets.QLabel(''), 2, 0, 1, 3)
+
+        # Default format for Gerber
+        self.gerber_default_label = QtWidgets.QLabel('<b>%s:</b>' % _('Default Values'))
+        self.gerber_default_label.setToolTip(
+            _("Those values will be used as fallback values\n"
+              "in case that they are not found in the Gerber file.")
+        )
+
+        grid0.addWidget(self.gerber_default_label, 3, 0, 1, 3)
+
+        # Gerber Units
+        self.gerber_units_label = QtWidgets.QLabel('%s:' % _('Units'))
+        self.gerber_units_label.setToolTip(
+            _("The units used in the Gerber file.")
+        )
+
+        self.gerber_units_radio = RadioSet([{'label': _('INCH'), 'value': 'IN'},
+                                            {'label': _('MM'), 'value': 'MM'}])
+        self.gerber_units_radio.setToolTip(
+            _("The units used in the Gerber file.")
+        )
+
+        grid0.addWidget(self.gerber_units_label, 4, 0)
+        grid0.addWidget(self.gerber_units_radio, 4, 1, 1, 2)
+
+        # Gerber Zeros
+        self.gerber_zeros_label = QtWidgets.QLabel('%s:' % _('Zeros'))
+        self.gerber_zeros_label.setAlignment(QtCore.Qt.AlignLeft)
+        self.gerber_zeros_label.setToolTip(
+            _("This sets the type of Gerber zeros.\n"
+              "If LZ then Leading Zeros are removed and\n"
+              "Trailing Zeros are kept.\n"
+              "If TZ is checked then Trailing Zeros are removed\n"
+              "and Leading Zeros are kept.")
+        )
+
+        self.gerber_zeros_radio = RadioSet([{'label': _('LZ'), 'value': 'L'},
+                                            {'label': _('TZ'), 'value': 'T'}])
+        self.gerber_zeros_radio.setToolTip(
+            _("This sets the type of Gerber zeros.\n"
+              "If LZ then Leading Zeros are removed and\n"
+              "Trailing Zeros are kept.\n"
+              "If TZ is checked then Trailing Zeros are removed\n"
+              "and Leading Zeros are kept.")
+        )
+
+        grid0.addWidget(self.gerber_zeros_label, 5, 0)
+        grid0.addWidget(self.gerber_zeros_radio, 5, 1, 1, 2)
+
+        separator_line = QtWidgets.QFrame()
+        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
+        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
+        grid0.addWidget(separator_line, 6, 0, 1, 3)
+
+        # Apertures Cleaning
+        self.gerber_clean_cb = FCCheckBox(label='%s' % _('Clean Apertures'))
+        self.gerber_clean_cb.setToolTip(
+            _("Will remove apertures that do not have geometry\n"
+              "thus lowering the number of apertures in the Gerber object.")
+        )
+        grid0.addWidget(self.gerber_clean_cb, 7, 0, 1, 3)
+
+        # Apply Extra Buffering
+        self.gerber_extra_buffering = FCCheckBox(label='%s' % _('Polarity change buffer'))
+        self.gerber_extra_buffering.setToolTip(
+            _("Will apply extra buffering for the\n"
+              "solid geometry when we have polarity changes.\n"
+              "May help loading Gerber files that otherwise\n"
+              "do not load correctly.")
+        )
+        grid0.addWidget(self.gerber_extra_buffering, 8, 0, 1, 3)
+
+        separator_line = QtWidgets.QFrame()
+        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
+        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
+        grid0.addWidget(separator_line, 9, 0, 1, 3)
+
+        # Gerber Object Color
+        self.gerber_color_label = QtWidgets.QLabel('<b>%s</b>' % _('Gerber Object Color'))
+        grid0.addWidget(self.gerber_color_label, 10, 0, 1, 3)
+
+        # Plot Line Color
+        self.pl_color_label = QtWidgets.QLabel('%s:' % _('Outline'))
+        self.pl_color_label.setToolTip(
+            _("Set the line color for plotted objects.")
+        )
+        self.pl_color_entry = FCEntry()
+        self.pl_color_button = QtWidgets.QPushButton()
+        self.pl_color_button.setFixedSize(15, 15)
+
+        self.form_box_child_2 = QtWidgets.QHBoxLayout()
+        self.form_box_child_2.addWidget(self.pl_color_entry)
+        self.form_box_child_2.addWidget(self.pl_color_button)
+        self.form_box_child_2.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
+
+        grid0.addWidget(self.pl_color_label, 11, 0)
+        grid0.addLayout(self.form_box_child_2, 11, 1, 1, 2)
+
+        # Plot Fill Color
+        self.pf_color_label = QtWidgets.QLabel('%s:' % _('Fill'))
+        self.pf_color_label.setToolTip(
+            _("Set the fill color for plotted objects.\n"
+              "First 6 digits are the color and the last 2\n"
+              "digits are for alpha (transparency) level.")
+        )
+        self.pf_color_entry = FCEntry()
+        self.pf_color_button = QtWidgets.QPushButton()
+        self.pf_color_button.setFixedSize(15, 15)
+
+        self.form_box_child_1 = QtWidgets.QHBoxLayout()
+        self.form_box_child_1.addWidget(self.pf_color_entry)
+        self.form_box_child_1.addWidget(self.pf_color_button)
+        self.form_box_child_1.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
+
+        grid0.addWidget(self.pf_color_label, 12, 0)
+        grid0.addLayout(self.form_box_child_1, 12, 1, 1, 2)
+
+        # Plot Fill Transparency Level
+        self.pf_alpha_label = QtWidgets.QLabel('%s:' % _('Alpha'))
+        self.pf_alpha_label.setToolTip(
+            _("Set the fill transparency for plotted objects.")
+        )
+        self.pf_color_alpha_slider = QtWidgets.QSlider(QtCore.Qt.Horizontal)
+        self.pf_color_alpha_slider.setMinimum(0)
+        self.pf_color_alpha_slider.setMaximum(255)
+        self.pf_color_alpha_slider.setSingleStep(1)
+
+        self.pf_color_alpha_spinner = FCSpinner()
+        self.pf_color_alpha_spinner.setMinimumWidth(70)
+        self.pf_color_alpha_spinner.set_range(0, 255)
+
+        self.form_box_child_3 = QtWidgets.QHBoxLayout()
+        self.form_box_child_3.addWidget(self.pf_color_alpha_slider)
+        self.form_box_child_3.addWidget(self.pf_color_alpha_spinner)
+
+        grid0.addWidget(self.pf_alpha_label, 13, 0)
+        grid0.addLayout(self.form_box_child_3, 13, 1, 1, 2)
+
+        self.layout.addStretch()
+
+        # Setting plot colors signals
+        self.pl_color_entry.editingFinished.connect(self.on_pl_color_entry)
+        self.pl_color_button.clicked.connect(self.on_pl_color_button)
+        self.pf_color_entry.editingFinished.connect(self.on_pf_color_entry)
+        self.pf_color_button.clicked.connect(self.on_pf_color_button)
+        self.pf_color_alpha_spinner.valueChanged.connect(self.on_pf_color_spinner)
+        self.pf_color_alpha_slider.valueChanged.connect(self.on_pf_color_slider)
+
+    # Setting plot colors handlers
+    def on_pf_color_entry(self):
+        self.app.defaults['gerber_plot_fill'] = self.pf_color_entry.get_value()[:7] + \
+            self.app.defaults['gerber_plot_fill'][7:9]
+        self.pf_color_button.setStyleSheet("background-color:%s" % str(self.app.defaults['gerber_plot_fill'])[:7])
+
+    def on_pf_color_button(self):
+        current_color = QtGui.QColor(self.app.defaults['gerber_plot_fill'][:7])
+
+        c_dialog = QtWidgets.QColorDialog()
+        plot_fill_color = c_dialog.getColor(initial=current_color)
+
+        if plot_fill_color.isValid() is False:
+            return
+
+        self.pf_color_button.setStyleSheet("background-color:%s" % str(plot_fill_color.name()))
+
+        new_val = str(plot_fill_color.name()) + str(self.app.defaults['gerber_plot_fill'][7:9])
+        self.pf_color_entry.set_value(new_val)
+        self.app.defaults['gerber_plot_fill'] = new_val
+
+    def on_pf_color_spinner(self):
+        spinner_value = self.pf_color_alpha_spinner.value()
+        self.pf_color_alpha_slider.setValue(spinner_value)
+        self.app.defaults['gerber_plot_fill'] = \
+            self.app.defaults['gerber_plot_fill'][:7] + \
+            (hex(spinner_value)[2:] if int(hex(spinner_value)[2:], 16) > 0 else '00')
+        self.app.defaults['gerber_plot_line'] = \
+            self.app.defaults['gerber_plot_line'][:7] + \
+            (hex(spinner_value)[2:] if int(hex(spinner_value)[2:], 16) > 0 else '00')
+
+    def on_pf_color_slider(self):
+        slider_value = self.pf_color_alpha_slider.value()
+        self.pf_color_alpha_spinner.setValue(slider_value)
+
+    def on_pl_color_entry(self):
+        self.app.defaults['gerber_plot_line'] = self.pl_color_entry.get_value()[:7] + \
+                                                self.app.defaults['gerber_plot_line'][7:9]
+        self.pl_color_button.setStyleSheet("background-color:%s" % str(self.app.defaults['gerber_plot_line'])[:7])
+
+    def on_pl_color_button(self):
+        current_color = QtGui.QColor(self.app.defaults['gerber_plot_line'][:7])
+        # print(current_color)
+
+        c_dialog = QtWidgets.QColorDialog()
+        plot_line_color = c_dialog.getColor(initial=current_color)
+
+        if plot_line_color.isValid() is False:
+            return
+
+        self.pl_color_button.setStyleSheet("background-color:%s" % str(plot_line_color.name()))
+
+        new_val_line = str(plot_line_color.name()) + str(self.app.defaults['gerber_plot_line'][7:9])
+        self.pl_color_entry.set_value(new_val_line)
+        self.app.defaults['gerber_plot_line'] = new_val_line

+ 187 - 0
flatcamGUI/preferences/gerber/GerberOptPrefGroupUI.py

@@ -0,0 +1,187 @@
+from PyQt5 import QtWidgets
+from PyQt5.QtCore import QSettings
+
+from flatcamGUI.GUIElements import FCDoubleSpinner, FCSpinner, RadioSet, FCCheckBox
+from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
+
+import gettext
+import FlatCAMTranslation as fcTranslate
+import builtins
+
+fcTranslate.apply_language('strings')
+if '_' not in builtins.__dict__:
+    _ = gettext.gettext
+
+settings = QSettings("Open Source", "FlatCAM")
+if settings.contains("machinist"):
+    machinist_setting = settings.value('machinist', type=int)
+else:
+    machinist_setting = 0
+
+
+class GerberOptPrefGroupUI(OptionsGroupUI):
+    def __init__(self, decimals=4, parent=None):
+        # OptionsGroupUI.__init__(self, "Gerber Options Preferences", parent=parent)
+        super(GerberOptPrefGroupUI, self).__init__(self, parent=parent)
+
+        self.decimals = decimals
+
+        self.setTitle(str(_("Gerber Options")))
+
+        # ## Isolation Routing
+        self.isolation_routing_label = QtWidgets.QLabel("<b>%s:</b>" % _("Isolation Routing"))
+        self.isolation_routing_label.setToolTip(
+            _("Create a Geometry object with\n"
+              "toolpaths to cut outside polygons.")
+        )
+        self.layout.addWidget(self.isolation_routing_label)
+
+        # Cutting Tool Diameter
+        grid0 = QtWidgets.QGridLayout()
+        self.layout.addLayout(grid0)
+
+        tdlabel = QtWidgets.QLabel('%s:' % _('Tool dia'))
+        tdlabel.setToolTip(
+            _("Diameter of the cutting tool.")
+        )
+        grid0.addWidget(tdlabel, 0, 0)
+        self.iso_tool_dia_entry = FCDoubleSpinner()
+        self.iso_tool_dia_entry.set_precision(self.decimals)
+        self.iso_tool_dia_entry.setSingleStep(0.1)
+        self.iso_tool_dia_entry.set_range(-9999, 9999)
+
+        grid0.addWidget(self.iso_tool_dia_entry, 0, 1)
+
+        # Nr of passes
+        passlabel = QtWidgets.QLabel('%s:' % _('# Passes'))
+        passlabel.setToolTip(
+            _("Width of the isolation gap in\n"
+              "number (integer) of tool widths.")
+        )
+        self.iso_width_entry = FCSpinner()
+        self.iso_width_entry.set_range(1, 999)
+
+        grid0.addWidget(passlabel, 1, 0)
+        grid0.addWidget(self.iso_width_entry, 1, 1)
+
+        # Pass overlap
+        overlabel = QtWidgets.QLabel('%s:' % _('Pass overlap'))
+        overlabel.setToolTip(
+            _("How much (percentage) of the tool width to overlap each tool pass.")
+        )
+        self.iso_overlap_entry = FCDoubleSpinner(suffix='%')
+        self.iso_overlap_entry.set_precision(self.decimals)
+        self.iso_overlap_entry.setWrapping(True)
+        self.iso_overlap_entry.setRange(0.0000, 99.9999)
+        self.iso_overlap_entry.setSingleStep(0.1)
+
+        grid0.addWidget(overlabel, 2, 0)
+        grid0.addWidget(self.iso_overlap_entry, 2, 1)
+
+        # Isolation Scope
+        self.iso_scope_label = QtWidgets.QLabel('%s:' % _('Scope'))
+        self.iso_scope_label.setToolTip(
+            _("Isolation scope. Choose what to isolate:\n"
+              "- 'All' -> Isolate all the polygons in the object\n"
+              "- 'Selection' -> Isolate a selection of polygons.")
+        )
+        self.iso_scope_radio = RadioSet([{'label': _('All'), 'value': 'all'},
+                                         {'label': _('Selection'), 'value': 'single'}])
+
+        grid0.addWidget(self.iso_scope_label, 3, 0)
+        grid0.addWidget(self.iso_scope_radio, 3, 1, 1, 2)
+
+        # Milling Type
+        milling_type_label = QtWidgets.QLabel('%s:' % _('Milling Type'))
+        milling_type_label.setToolTip(
+            _("Milling type:\n"
+              "- climb / best for precision milling and to reduce tool usage\n"
+              "- conventional / useful when there is no backlash compensation")
+        )
+        grid0.addWidget(milling_type_label, 4, 0)
+        self.milling_type_radio = RadioSet([{'label': _('Climb'), 'value': 'cl'},
+                                            {'label': _('Conventional'), 'value': 'cv'}])
+        grid0.addWidget(self.milling_type_radio, 4, 1)
+
+        # Combine passes
+        self.combine_passes_cb = FCCheckBox(label=_('Combine Passes'))
+        self.combine_passes_cb.setToolTip(
+            _("Combine all passes into one object")
+        )
+        grid0.addWidget(self.combine_passes_cb, 5, 0, 1, 2)
+
+        separator_line = QtWidgets.QFrame()
+        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
+        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
+        grid0.addWidget(separator_line, 6, 0, 1, 2)
+
+        # ## Clear non-copper regions
+        self.clearcopper_label = QtWidgets.QLabel("<b>%s:</b>" % _("Non-copper regions"))
+        self.clearcopper_label.setToolTip(
+            _("Create polygons covering the\n"
+              "areas without copper on the PCB.\n"
+              "Equivalent to the inverse of this\n"
+              "object. Can be used to remove all\n"
+              "copper from a specified region.")
+        )
+        self.layout.addWidget(self.clearcopper_label)
+
+        grid1 = QtWidgets.QGridLayout()
+        self.layout.addLayout(grid1)
+
+        # Margin
+        bmlabel = QtWidgets.QLabel('%s:' % _('Boundary Margin'))
+        bmlabel.setToolTip(
+            _("Specify the edge of the PCB\n"
+              "by drawing a box around all\n"
+              "objects with this minimum\n"
+              "distance.")
+        )
+        grid1.addWidget(bmlabel, 0, 0)
+        self.noncopper_margin_entry = FCDoubleSpinner()
+        self.noncopper_margin_entry.set_precision(self.decimals)
+        self.noncopper_margin_entry.setSingleStep(0.1)
+        self.noncopper_margin_entry.set_range(-9999, 9999)
+        grid1.addWidget(self.noncopper_margin_entry, 0, 1)
+
+        # Rounded corners
+        self.noncopper_rounded_cb = FCCheckBox(label=_("Rounded Geo"))
+        self.noncopper_rounded_cb.setToolTip(
+            _("Resulting geometry will have rounded corners.")
+        )
+        grid1.addWidget(self.noncopper_rounded_cb, 1, 0, 1, 2)
+
+        separator_line = QtWidgets.QFrame()
+        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
+        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
+        grid1.addWidget(separator_line, 2, 0, 1, 2)
+
+        # ## Bounding box
+        self.boundingbox_label = QtWidgets.QLabel('<b>%s:</b>' % _('Bounding Box'))
+        self.layout.addWidget(self.boundingbox_label)
+
+        grid2 = QtWidgets.QGridLayout()
+        self.layout.addLayout(grid2)
+
+        bbmargin = QtWidgets.QLabel('%s:' % _('Boundary Margin'))
+        bbmargin.setToolTip(
+            _("Distance of the edges of the box\n"
+              "to the nearest polygon.")
+        )
+        self.bbmargin_entry = FCDoubleSpinner()
+        self.bbmargin_entry.set_precision(self.decimals)
+        self.bbmargin_entry.setSingleStep(0.1)
+        self.bbmargin_entry.set_range(-9999, 9999)
+
+        grid2.addWidget(bbmargin, 0, 0)
+        grid2.addWidget(self.bbmargin_entry, 0, 1)
+
+        self.bbrounded_cb = FCCheckBox(label='%s' % _("Rounded Geo"))
+        self.bbrounded_cb.setToolTip(
+            _("If the bounding box is \n"
+              "to have rounded corners\n"
+              "their radius is equal to\n"
+              "the margin.")
+        )
+        grid2.addWidget(self.bbrounded_cb, 1, 0, 1, 2)
+        self.layout.addStretch()

+ 53 - 0
flatcamGUI/preferences/gerber/GerberPreferencesUI.py

@@ -0,0 +1,53 @@
+from PyQt5 import QtWidgets
+from PyQt5.QtCore import QSettings
+
+from flatcamGUI.preferences.gerber.GerberEditorPrefGroupUI import GerberEditorPrefGroupUI
+from flatcamGUI.preferences.gerber.GerberExpPrefGroupUI import GerberExpPrefGroupUI
+from flatcamGUI.preferences.gerber.GerberAdvOptPrefGroupUI import GerberAdvOptPrefGroupUI
+from flatcamGUI.preferences.gerber.GerberOptPrefGroupUI import GerberOptPrefGroupUI
+from flatcamGUI.preferences.gerber.GerberGenPrefGroupUI import GerberGenPrefGroupUI
+
+import gettext
+import FlatCAMTranslation as fcTranslate
+import builtins
+
+fcTranslate.apply_language('strings')
+if '_' not in builtins.__dict__:
+    _ = gettext.gettext
+
+settings = QSettings("Open Source", "FlatCAM")
+if settings.contains("machinist"):
+    machinist_setting = settings.value('machinist', type=int)
+else:
+    machinist_setting = 0
+
+
+class GerberPreferencesUI(QtWidgets.QWidget):
+
+    def __init__(self, decimals, parent=None):
+        QtWidgets.QWidget.__init__(self, parent=parent)
+        self.layout = QtWidgets.QHBoxLayout()
+        self.setLayout(self.layout)
+        self.decimals = decimals
+
+        self.gerber_gen_group = GerberGenPrefGroupUI(decimals=self.decimals)
+        self.gerber_gen_group.setMinimumWidth(250)
+        self.gerber_opt_group = GerberOptPrefGroupUI(decimals=self.decimals)
+        self.gerber_opt_group.setMinimumWidth(250)
+        self.gerber_exp_group = GerberExpPrefGroupUI(decimals=self.decimals)
+        self.gerber_exp_group.setMinimumWidth(230)
+        self.gerber_adv_opt_group = GerberAdvOptPrefGroupUI(decimals=self.decimals)
+        self.gerber_adv_opt_group.setMinimumWidth(200)
+        self.gerber_editor_group = GerberEditorPrefGroupUI(decimals=self.decimals)
+        self.gerber_editor_group.setMinimumWidth(200)
+
+        self.vlay = QtWidgets.QVBoxLayout()
+        self.vlay.addWidget(self.gerber_opt_group)
+        self.vlay.addWidget(self.gerber_exp_group)
+
+        self.layout.addWidget(self.gerber_gen_group)
+        self.layout.addLayout(self.vlay)
+        self.layout.addWidget(self.gerber_adv_opt_group)
+        self.layout.addWidget(self.gerber_editor_group)
+
+        self.layout.addStretch()

+ 0 - 0
flatcamGUI/preferences/gerber/__init__.py


+ 274 - 0
flatcamGUI/preferences/tools/Tools2CThievingPrefGroupUI.py

@@ -0,0 +1,274 @@
+from PyQt5 import QtWidgets
+from PyQt5.QtCore import QSettings
+
+from flatcamGUI.GUIElements import FCSpinner, FCDoubleSpinner, RadioSet
+from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
+
+import gettext
+import FlatCAMTranslation as fcTranslate
+import builtins
+
+fcTranslate.apply_language('strings')
+if '_' not in builtins.__dict__:
+    _ = gettext.gettext
+
+settings = QSettings("Open Source", "FlatCAM")
+if settings.contains("machinist"):
+    machinist_setting = settings.value('machinist', type=int)
+else:
+    machinist_setting = 0
+
+
+class Tools2CThievingPrefGroupUI(OptionsGroupUI):
+    def __init__(self, decimals=4, parent=None):
+
+        super(Tools2CThievingPrefGroupUI, self).__init__(self, parent=parent)
+
+        self.setTitle(str(_("Copper Thieving Tool Options")))
+        self.decimals = decimals
+
+        # ## Grid Layout
+        grid_lay = QtWidgets.QGridLayout()
+        self.layout.addLayout(grid_lay)
+        grid_lay.setColumnStretch(0, 0)
+        grid_lay.setColumnStretch(1, 1)
+
+        # ## Parameters
+        self.cflabel = QtWidgets.QLabel('<b>%s</b>' % _('Parameters'))
+        self.cflabel.setToolTip(
+            _("A tool to generate a Copper Thieving that can be added\n"
+              "to a selected Gerber file.")
+        )
+        grid_lay.addWidget(self.cflabel, 0, 0, 1, 2)
+
+        # CIRCLE STEPS - to be used when buffering
+        self.circle_steps_lbl = QtWidgets.QLabel('%s:' % _("Circle Steps"))
+        self.circle_steps_lbl.setToolTip(
+            _("Number of steps (lines) used to interpolate circles.")
+        )
+
+        self.circlesteps_entry = FCSpinner()
+        self.circlesteps_entry.set_range(1, 9999)
+
+        grid_lay.addWidget(self.circle_steps_lbl, 1, 0)
+        grid_lay.addWidget(self.circlesteps_entry, 1, 1)
+
+        # CLEARANCE #
+        self.clearance_label = QtWidgets.QLabel('%s:' % _("Clearance"))
+        self.clearance_label.setToolTip(
+            _("This set the distance between the copper Thieving components\n"
+              "(the polygon fill may be split in multiple polygons)\n"
+              "and the copper traces in the Gerber file.")
+        )
+        self.clearance_entry = FCDoubleSpinner()
+        self.clearance_entry.setMinimum(0.00001)
+        self.clearance_entry.set_precision(self.decimals)
+        self.clearance_entry.setSingleStep(0.1)
+
+        grid_lay.addWidget(self.clearance_label, 2, 0)
+        grid_lay.addWidget(self.clearance_entry, 2, 1)
+
+        # MARGIN #
+        self.margin_label = QtWidgets.QLabel('%s:' % _("Margin"))
+        self.margin_label.setToolTip(
+            _("Bounding box margin.")
+        )
+        self.margin_entry = FCDoubleSpinner()
+        self.margin_entry.setMinimum(0.0)
+        self.margin_entry.set_precision(self.decimals)
+        self.margin_entry.setSingleStep(0.1)
+
+        grid_lay.addWidget(self.margin_label, 3, 0)
+        grid_lay.addWidget(self.margin_entry, 3, 1)
+
+        # Reference #
+        self.reference_radio = RadioSet([
+            {'label': _('Itself'), 'value': 'itself'},
+            {"label": _("Area Selection"), "value": "area"},
+            {'label': _("Reference Object"), 'value': 'box'}
+        ], orientation='vertical', stretch=False)
+        self.reference_label = QtWidgets.QLabel(_("Reference:"))
+        self.reference_label.setToolTip(
+            _("- 'Itself' - the copper Thieving extent is based on the object extent.\n"
+              "- 'Area Selection' - left mouse click to start selection of the area to be filled.\n"
+              "- 'Reference Object' - will do copper thieving within the area specified by another object.")
+        )
+        grid_lay.addWidget(self.reference_label, 4, 0)
+        grid_lay.addWidget(self.reference_radio, 4, 1)
+
+        # Bounding Box Type #
+        self.bbox_type_radio = RadioSet([
+            {'label': _('Rectangular'), 'value': 'rect'},
+            {"label": _("Minimal"), "value": "min"}
+        ], stretch=False)
+        self.bbox_type_label = QtWidgets.QLabel(_("Box Type:"))
+        self.bbox_type_label.setToolTip(
+            _("- 'Rectangular' - the bounding box will be of rectangular shape.\n"
+              "- 'Minimal' - the bounding box will be the convex hull shape.")
+        )
+        grid_lay.addWidget(self.bbox_type_label, 5, 0)
+        grid_lay.addWidget(self.bbox_type_radio, 5, 1)
+
+        separator_line = QtWidgets.QFrame()
+        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
+        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
+        grid_lay.addWidget(separator_line, 6, 0, 1, 2)
+
+        # Fill Type
+        self.fill_type_radio = RadioSet([
+            {'label': _('Solid'), 'value': 'solid'},
+            {"label": _("Dots Grid"), "value": "dot"},
+            {"label": _("Squares Grid"), "value": "square"},
+            {"label": _("Lines Grid"), "value": "line"}
+        ], orientation='vertical', stretch=False)
+        self.fill_type_label = QtWidgets.QLabel(_("Fill Type:"))
+        self.fill_type_label.setToolTip(
+            _("- 'Solid' - copper thieving will be a solid polygon.\n"
+              "- 'Dots Grid' - the empty area will be filled with a pattern of dots.\n"
+              "- 'Squares Grid' - the empty area will be filled with a pattern of squares.\n"
+              "- 'Lines Grid' - the empty area will be filled with a pattern of lines.")
+        )
+        grid_lay.addWidget(self.fill_type_label, 7, 0)
+        grid_lay.addWidget(self.fill_type_radio, 7, 1)
+
+        self.dots_label = QtWidgets.QLabel('<b>%s</b>:' % _("Dots Grid Parameters"))
+        grid_lay.addWidget(self.dots_label, 8, 0, 1, 2)
+
+        # Dot diameter #
+        self.dotdia_label = QtWidgets.QLabel('%s:' % _("Dia"))
+        self.dotdia_label.setToolTip(
+            _("Dot diameter in Dots Grid.")
+        )
+        self.dot_dia_entry = FCDoubleSpinner()
+        self.dot_dia_entry.set_range(0.0, 9999.9999)
+        self.dot_dia_entry.set_precision(self.decimals)
+        self.dot_dia_entry.setSingleStep(0.1)
+
+        grid_lay.addWidget(self.dotdia_label, 9, 0)
+        grid_lay.addWidget(self.dot_dia_entry, 9, 1)
+
+        # Dot spacing #
+        self.dotspacing_label = QtWidgets.QLabel('%s:' % _("Spacing"))
+        self.dotspacing_label.setToolTip(
+            _("Distance between each two dots in Dots Grid.")
+        )
+        self.dot_spacing_entry = FCDoubleSpinner()
+        self.dot_spacing_entry.set_range(0.0, 9999.9999)
+        self.dot_spacing_entry.set_precision(self.decimals)
+        self.dot_spacing_entry.setSingleStep(0.1)
+
+        grid_lay.addWidget(self.dotspacing_label, 10, 0)
+        grid_lay.addWidget(self.dot_spacing_entry, 10, 1)
+
+        self.squares_label = QtWidgets.QLabel('<b>%s</b>:' % _("Squares Grid Parameters"))
+        grid_lay.addWidget(self.squares_label, 11, 0, 1, 2)
+
+        # Square Size #
+        self.square_size_label = QtWidgets.QLabel('%s:' % _("Size"))
+        self.square_size_label.setToolTip(
+            _("Square side size in Squares Grid.")
+        )
+        self.square_size_entry = FCDoubleSpinner()
+        self.square_size_entry.set_range(0.0, 9999.9999)
+        self.square_size_entry.set_precision(self.decimals)
+        self.square_size_entry.setSingleStep(0.1)
+
+        grid_lay.addWidget(self.square_size_label, 12, 0)
+        grid_lay.addWidget(self.square_size_entry, 12, 1)
+
+        # Squares spacing #
+        self.squares_spacing_label = QtWidgets.QLabel('%s:' % _("Spacing"))
+        self.squares_spacing_label.setToolTip(
+            _("Distance between each two squares in Squares Grid.")
+        )
+        self.squares_spacing_entry = FCDoubleSpinner()
+        self.squares_spacing_entry.set_range(0.0, 9999.9999)
+        self.squares_spacing_entry.set_precision(self.decimals)
+        self.squares_spacing_entry.setSingleStep(0.1)
+
+        grid_lay.addWidget(self.squares_spacing_label, 13, 0)
+        grid_lay.addWidget(self.squares_spacing_entry, 13, 1)
+
+        self.lines_label = QtWidgets.QLabel('<b>%s</b>:' % _("Lines Grid Parameters"))
+        grid_lay.addWidget(self.lines_label, 14, 0, 1, 2)
+
+        # Square Size #
+        self.line_size_label = QtWidgets.QLabel('%s:' % _("Size"))
+        self.line_size_label.setToolTip(
+            _("Line thickness size in Lines Grid.")
+        )
+        self.line_size_entry = FCDoubleSpinner()
+        self.line_size_entry.set_range(0.0, 9999.9999)
+        self.line_size_entry.set_precision(self.decimals)
+        self.line_size_entry.setSingleStep(0.1)
+
+        grid_lay.addWidget(self.line_size_label, 15, 0)
+        grid_lay.addWidget(self.line_size_entry, 15, 1)
+
+        # Lines spacing #
+        self.lines_spacing_label = QtWidgets.QLabel('%s:' % _("Spacing"))
+        self.lines_spacing_label.setToolTip(
+            _("Distance between each two lines in Lines Grid.")
+        )
+        self.lines_spacing_entry = FCDoubleSpinner()
+        self.lines_spacing_entry.set_range(0.0, 9999.9999)
+        self.lines_spacing_entry.set_precision(self.decimals)
+        self.lines_spacing_entry.setSingleStep(0.1)
+
+        grid_lay.addWidget(self.lines_spacing_label, 16, 0)
+        grid_lay.addWidget(self.lines_spacing_entry, 16, 1)
+
+        self.robber_bar_label = QtWidgets.QLabel('<b>%s</b>' % _('Robber Bar Parameters'))
+        self.robber_bar_label.setToolTip(
+            _("Parameters used for the robber bar.\n"
+              "Robber bar = copper border to help in pattern hole plating.")
+        )
+        grid_lay.addWidget(self.robber_bar_label, 17, 0, 1, 2)
+
+        # ROBBER BAR MARGIN #
+        self.rb_margin_label = QtWidgets.QLabel('%s:' % _("Margin"))
+        self.rb_margin_label.setToolTip(
+            _("Bounding box margin for robber bar.")
+        )
+        self.rb_margin_entry = FCDoubleSpinner()
+        self.rb_margin_entry.set_range(-9999.9999, 9999.9999)
+        self.rb_margin_entry.set_precision(self.decimals)
+        self.rb_margin_entry.setSingleStep(0.1)
+
+        grid_lay.addWidget(self.rb_margin_label, 18, 0)
+        grid_lay.addWidget(self.rb_margin_entry, 18, 1)
+
+        # THICKNESS #
+        self.rb_thickness_label = QtWidgets.QLabel('%s:' % _("Thickness"))
+        self.rb_thickness_label.setToolTip(
+            _("The robber bar thickness.")
+        )
+        self.rb_thickness_entry = FCDoubleSpinner()
+        self.rb_thickness_entry.set_range(0.0000, 9999.9999)
+        self.rb_thickness_entry.set_precision(self.decimals)
+        self.rb_thickness_entry.setSingleStep(0.1)
+
+        grid_lay.addWidget(self.rb_thickness_label, 19, 0)
+        grid_lay.addWidget(self.rb_thickness_entry, 19, 1)
+
+        self.patern_mask_label = QtWidgets.QLabel('<b>%s</b>' % _('Pattern Plating Mask'))
+        self.patern_mask_label.setToolTip(
+            _("Generate a mask for pattern plating.")
+        )
+        grid_lay.addWidget(self.patern_mask_label, 20, 0, 1, 2)
+
+        # Openings CLEARANCE #
+        self.clearance_ppm_label = QtWidgets.QLabel('%s:' % _("Clearance"))
+        self.clearance_ppm_label.setToolTip(
+            _("The distance between the possible copper thieving elements\n"
+              "and/or robber bar and the actual openings in the mask.")
+        )
+        self.clearance_ppm_entry = FCDoubleSpinner()
+        self.clearance_ppm_entry.set_range(-9999.9999, 9999.9999)
+        self.clearance_ppm_entry.set_precision(self.decimals)
+        self.clearance_ppm_entry.setSingleStep(0.1)
+
+        grid_lay.addWidget(self.clearance_ppm_label, 21, 0)
+        grid_lay.addWidget(self.clearance_ppm_entry, 21, 1)
+
+        self.layout.addStretch()

+ 138 - 0
flatcamGUI/preferences/tools/Tools2CalPrefGroupUI.py

@@ -0,0 +1,138 @@
+from PyQt5 import QtWidgets
+from PyQt5.QtCore import QSettings
+
+from flatcamGUI.GUIElements import RadioSet, FCDoubleSpinner, FCCheckBox, FCEntry
+from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
+
+import gettext
+import FlatCAMTranslation as fcTranslate
+import builtins
+
+fcTranslate.apply_language('strings')
+if '_' not in builtins.__dict__:
+    _ = gettext.gettext
+
+settings = QSettings("Open Source", "FlatCAM")
+if settings.contains("machinist"):
+    machinist_setting = settings.value('machinist', type=int)
+else:
+    machinist_setting = 0
+
+
+class Tools2CalPrefGroupUI(OptionsGroupUI):
+    def __init__(self, decimals=4, parent=None):
+
+        super(Tools2CalPrefGroupUI, self).__init__(self, parent=parent)
+
+        self.setTitle(str(_("Calibration Tool Options")))
+        self.decimals = decimals
+
+        # ## Grid Layout
+        grid_lay = QtWidgets.QGridLayout()
+        self.layout.addLayout(grid_lay)
+        grid_lay.setColumnStretch(0, 0)
+        grid_lay.setColumnStretch(1, 1)
+
+        self.param_label = QtWidgets.QLabel('<b>%s:</b>' % _('Parameters'))
+        self.param_label.setToolTip(
+            _("Parameters used for this tool.")
+        )
+        grid_lay.addWidget(self.param_label, 0, 0, 1, 2)
+
+        # Calibration source
+        self.cal_source_lbl = QtWidgets.QLabel("<b>%s:</b>" % _("Source Type"))
+        self.cal_source_lbl.setToolTip(_("The source of calibration points.\n"
+                                         "It can be:\n"
+                                         "- Object -> click a hole geo for Excellon or a pad for Gerber\n"
+                                         "- Free -> click freely on canvas to acquire the calibration points"))
+        self.cal_source_radio = RadioSet([{'label': _('Object'), 'value': 'object'},
+                                          {'label': _('Free'), 'value': 'free'}],
+                                         stretch=False)
+
+        grid_lay.addWidget(self.cal_source_lbl, 1, 0)
+        grid_lay.addWidget(self.cal_source_radio, 1, 1, 1, 2)
+
+        separator_line = QtWidgets.QFrame()
+        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
+        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
+        grid_lay.addWidget(separator_line, 2, 0, 1, 2)
+
+        # Travel Z entry
+        travelz_lbl = QtWidgets.QLabel('%s:' % _("Travel Z"))
+        travelz_lbl.setToolTip(
+            _("Height (Z) for travelling between the points.")
+        )
+
+        self.travelz_entry = FCDoubleSpinner()
+        self.travelz_entry.set_range(-9999.9999, 9999.9999)
+        self.travelz_entry.set_precision(self.decimals)
+        self.travelz_entry.setSingleStep(0.1)
+
+        grid_lay.addWidget(travelz_lbl, 3, 0)
+        grid_lay.addWidget(self.travelz_entry, 3, 1, 1, 2)
+
+        # Verification Z entry
+        verz_lbl = QtWidgets.QLabel('%s:' % _("Verification Z"))
+        verz_lbl.setToolTip(
+            _("Height (Z) for checking the point.")
+        )
+
+        self.verz_entry = FCDoubleSpinner()
+        self.verz_entry.set_range(-9999.9999, 9999.9999)
+        self.verz_entry.set_precision(self.decimals)
+        self.verz_entry.setSingleStep(0.1)
+
+        grid_lay.addWidget(verz_lbl, 4, 0)
+        grid_lay.addWidget(self.verz_entry, 4, 1, 1, 2)
+
+        # Zero the Z of the verification tool
+        self.zeroz_cb = FCCheckBox('%s' % _("Zero Z tool"))
+        self.zeroz_cb.setToolTip(
+            _("Include a sequence to zero the height (Z)\n"
+              "of the verification tool.")
+        )
+
+        grid_lay.addWidget(self.zeroz_cb, 5, 0, 1, 3)
+
+        # Toochange Z entry
+        toolchangez_lbl = QtWidgets.QLabel('%s:' % _("Toolchange Z"))
+        toolchangez_lbl.setToolTip(
+            _("Height (Z) for mounting the verification probe.")
+        )
+
+        self.toolchangez_entry = FCDoubleSpinner()
+        self.toolchangez_entry.set_range(0.0000, 9999.9999)
+        self.toolchangez_entry.set_precision(self.decimals)
+        self.toolchangez_entry.setSingleStep(0.1)
+
+        grid_lay.addWidget(toolchangez_lbl, 6, 0)
+        grid_lay.addWidget(self.toolchangez_entry, 6, 1, 1, 2)
+
+        # Toolchange X-Y entry
+        toolchangexy_lbl = QtWidgets.QLabel('%s:' % _('Toolchange X-Y'))
+        toolchangexy_lbl.setToolTip(
+            _("Toolchange X,Y position.\n"
+              "If no value is entered then the current\n"
+              "(x, y) point will be used,")
+        )
+
+        self.toolchange_xy_entry = FCEntry()
+
+        grid_lay.addWidget(toolchangexy_lbl, 7, 0)
+        grid_lay.addWidget(self.toolchange_xy_entry, 7, 1, 1, 2)
+
+        # Second point choice
+        second_point_lbl = QtWidgets.QLabel('%s:' % _("Second point"))
+        second_point_lbl.setToolTip(
+            _("Second point in the Gcode verification can be:\n"
+              "- top-left -> the user will align the PCB vertically\n"
+              "- bottom-right -> the user will align the PCB horizontally")
+        )
+        self.second_point_radio = RadioSet([{'label': _('Top-Left'), 'value': 'tl'},
+                                            {'label': _('Bottom-Right'), 'value': 'br'}],
+                                           orientation='vertical')
+
+        grid_lay.addWidget(second_point_lbl, 8, 0)
+        grid_lay.addWidget(self.second_point_radio, 8, 1, 1, 2)
+
+        self.layout.addStretch()

+ 231 - 0
flatcamGUI/preferences/tools/Tools2EDrillsPrefGroupUI.py

@@ -0,0 +1,231 @@
+from PyQt5 import QtWidgets
+from PyQt5.QtCore import QSettings
+
+from flatcamGUI.GUIElements import FCCheckBox, RadioSet, FCDoubleSpinner
+from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
+
+import gettext
+import FlatCAMTranslation as fcTranslate
+import builtins
+
+fcTranslate.apply_language('strings')
+if '_' not in builtins.__dict__:
+    _ = gettext.gettext
+
+settings = QSettings("Open Source", "FlatCAM")
+if settings.contains("machinist"):
+    machinist_setting = settings.value('machinist', type=int)
+else:
+    machinist_setting = 0
+
+
+class Tools2EDrillsPrefGroupUI(OptionsGroupUI):
+    def __init__(self, decimals=4, parent=None):
+
+        super(Tools2EDrillsPrefGroupUI, self).__init__(self, parent=parent)
+
+        self.setTitle(str(_("Extract Drills Options")))
+        self.decimals = decimals
+
+        # ## Grid Layout
+        grid_lay = QtWidgets.QGridLayout()
+        self.layout.addLayout(grid_lay)
+        grid_lay.setColumnStretch(0, 0)
+        grid_lay.setColumnStretch(1, 1)
+
+        self.param_label = QtWidgets.QLabel('<b>%s:</b>' % _('Parameters'))
+        self.param_label.setToolTip(
+            _("Parameters used for this tool.")
+        )
+        grid_lay.addWidget(self.param_label, 0, 0, 1, 2)
+
+        self.padt_label = QtWidgets.QLabel("<b>%s:</b>" % _("Processed Pads Type"))
+        self.padt_label.setToolTip(
+            _("The type of pads shape to be processed.\n"
+              "If the PCB has many SMD pads with rectangular pads,\n"
+              "disable the Rectangular aperture.")
+        )
+
+        grid_lay.addWidget(self.padt_label, 2, 0, 1, 2)
+
+        # Circular Aperture Selection
+        self.circular_cb = FCCheckBox('%s' % _("Circular"))
+        self.circular_cb.setToolTip(
+            _("Process Circular Pads.")
+        )
+
+        grid_lay.addWidget(self.circular_cb, 3, 0, 1, 2)
+
+        # Oblong Aperture Selection
+        self.oblong_cb = FCCheckBox('%s' % _("Oblong"))
+        self.oblong_cb.setToolTip(
+            _("Process Oblong Pads.")
+        )
+
+        grid_lay.addWidget(self.oblong_cb, 4, 0, 1, 2)
+
+        # Square Aperture Selection
+        self.square_cb = FCCheckBox('%s' % _("Square"))
+        self.square_cb.setToolTip(
+            _("Process Square Pads.")
+        )
+
+        grid_lay.addWidget(self.square_cb, 5, 0, 1, 2)
+
+        # Rectangular Aperture Selection
+        self.rectangular_cb = FCCheckBox('%s' % _("Rectangular"))
+        self.rectangular_cb.setToolTip(
+            _("Process Rectangular Pads.")
+        )
+
+        grid_lay.addWidget(self.rectangular_cb, 6, 0, 1, 2)
+
+        # Others type of Apertures Selection
+        self.other_cb = FCCheckBox('%s' % _("Others"))
+        self.other_cb.setToolTip(
+            _("Process pads not in the categories above.")
+        )
+
+        grid_lay.addWidget(self.other_cb, 7, 0, 1, 2)
+
+        separator_line = QtWidgets.QFrame()
+        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
+        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
+        grid_lay.addWidget(separator_line, 8, 0, 1, 2)
+
+        # ## Axis
+        self.hole_size_radio = RadioSet(
+            [
+                {'label': _("Fixed Diameter"), 'value': 'fixed'},
+                {'label': _("Fixed Annular Ring"), 'value': 'ring'},
+                {'label': _("Proportional"), 'value': 'prop'}
+            ],
+            orientation='vertical',
+            stretch=False)
+        self.hole_size_label = QtWidgets.QLabel('<b>%s:</b>' % _("Method"))
+        self.hole_size_label.setToolTip(
+            _("The method for processing pads. Can be:\n"
+              "- Fixed Diameter -> all holes will have a set size\n"
+              "- Fixed Annular Ring -> all holes will have a set annular ring\n"
+              "- Proportional -> each hole size will be a fraction of the pad size"))
+
+        grid_lay.addWidget(self.hole_size_label, 9, 0)
+        grid_lay.addWidget(self.hole_size_radio, 9, 1)
+
+        # grid_lay1.addWidget(QtWidgets.QLabel(''))
+
+        separator_line = QtWidgets.QFrame()
+        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
+        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
+        grid_lay.addWidget(separator_line, 10, 0, 1, 2)
+
+        # Annular Ring
+        self.fixed_label = QtWidgets.QLabel('<b>%s</b>' % _("Fixed Diameter"))
+        grid_lay.addWidget(self.fixed_label, 11, 0, 1, 2)
+
+        # Diameter value
+        self.dia_entry = FCDoubleSpinner()
+        self.dia_entry.set_precision(self.decimals)
+        self.dia_entry.set_range(0.0000, 9999.9999)
+
+        self.dia_label = QtWidgets.QLabel('%s:' % _("Value"))
+        self.dia_label.setToolTip(
+            _("Fixed hole diameter.")
+        )
+
+        grid_lay.addWidget(self.dia_label, 12, 0)
+        grid_lay.addWidget(self.dia_entry, 12, 1)
+
+        # Annular Ring value
+        self.ring_label = QtWidgets.QLabel('<b>%s</b>' % _("Fixed Annular Ring"))
+        self.ring_label.setToolTip(
+            _("The size of annular ring.\n"
+              "The copper sliver between the hole exterior\n"
+              "and the margin of the copper pad.")
+        )
+        grid_lay.addWidget(self.ring_label, 13, 0, 1, 2)
+
+        # Circular Annular Ring Value
+        self.circular_ring_label = QtWidgets.QLabel('%s:' % _("Circular"))
+        self.circular_ring_label.setToolTip(
+            _("The size of annular ring for circular pads.")
+        )
+
+        self.circular_ring_entry = FCDoubleSpinner()
+        self.circular_ring_entry.set_precision(self.decimals)
+        self.circular_ring_entry.set_range(0.0000, 9999.9999)
+
+        grid_lay.addWidget(self.circular_ring_label, 14, 0)
+        grid_lay.addWidget(self.circular_ring_entry, 14, 1)
+
+        # Oblong Annular Ring Value
+        self.oblong_ring_label = QtWidgets.QLabel('%s:' % _("Oblong"))
+        self.oblong_ring_label.setToolTip(
+            _("The size of annular ring for oblong pads.")
+        )
+
+        self.oblong_ring_entry = FCDoubleSpinner()
+        self.oblong_ring_entry.set_precision(self.decimals)
+        self.oblong_ring_entry.set_range(0.0000, 9999.9999)
+
+        grid_lay.addWidget(self.oblong_ring_label, 15, 0)
+        grid_lay.addWidget(self.oblong_ring_entry, 15, 1)
+
+        # Square Annular Ring Value
+        self.square_ring_label = QtWidgets.QLabel('%s:' % _("Square"))
+        self.square_ring_label.setToolTip(
+            _("The size of annular ring for square pads.")
+        )
+
+        self.square_ring_entry = FCDoubleSpinner()
+        self.square_ring_entry.set_precision(self.decimals)
+        self.square_ring_entry.set_range(0.0000, 9999.9999)
+
+        grid_lay.addWidget(self.square_ring_label, 16, 0)
+        grid_lay.addWidget(self.square_ring_entry, 16, 1)
+
+        # Rectangular Annular Ring Value
+        self.rectangular_ring_label = QtWidgets.QLabel('%s:' % _("Rectangular"))
+        self.rectangular_ring_label.setToolTip(
+            _("The size of annular ring for rectangular pads.")
+        )
+
+        self.rectangular_ring_entry = FCDoubleSpinner()
+        self.rectangular_ring_entry.set_precision(self.decimals)
+        self.rectangular_ring_entry.set_range(0.0000, 9999.9999)
+
+        grid_lay.addWidget(self.rectangular_ring_label, 17, 0)
+        grid_lay.addWidget(self.rectangular_ring_entry, 17, 1)
+
+        # Others Annular Ring Value
+        self.other_ring_label = QtWidgets.QLabel('%s:' % _("Others"))
+        self.other_ring_label.setToolTip(
+            _("The size of annular ring for other pads.")
+        )
+
+        self.other_ring_entry = FCDoubleSpinner()
+        self.other_ring_entry.set_precision(self.decimals)
+        self.other_ring_entry.set_range(0.0000, 9999.9999)
+
+        grid_lay.addWidget(self.other_ring_label, 18, 0)
+        grid_lay.addWidget(self.other_ring_entry, 18, 1)
+
+        self.prop_label = QtWidgets.QLabel('<b>%s</b>' % _("Proportional Diameter"))
+        grid_lay.addWidget(self.prop_label, 19, 0, 1, 2)
+
+        # Factor value
+        self.factor_entry = FCDoubleSpinner(suffix='%')
+        self.factor_entry.set_precision(self.decimals)
+        self.factor_entry.set_range(0.0000, 100.0000)
+        self.factor_entry.setSingleStep(0.1)
+
+        self.factor_label = QtWidgets.QLabel('%s:' % _("Factor"))
+        self.factor_label.setToolTip(
+            _("Proportional Diameter.\n"
+              "The hole diameter will be a fraction of the pad size.")
+        )
+
+        grid_lay.addWidget(self.factor_label, 20, 0)
+        grid_lay.addWidget(self.factor_entry, 20, 1)
+
+        self.layout.addStretch()

+ 135 - 0
flatcamGUI/preferences/tools/Tools2FiducialsPrefGroupUI.py

@@ -0,0 +1,135 @@
+from PyQt5 import QtWidgets
+from PyQt5.QtCore import QSettings
+
+from flatcamGUI.GUIElements import FCDoubleSpinner, RadioSet
+from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
+
+import gettext
+import FlatCAMTranslation as fcTranslate
+import builtins
+
+fcTranslate.apply_language('strings')
+if '_' not in builtins.__dict__:
+    _ = gettext.gettext
+
+settings = QSettings("Open Source", "FlatCAM")
+if settings.contains("machinist"):
+    machinist_setting = settings.value('machinist', type=int)
+else:
+    machinist_setting = 0
+
+
+class Tools2FiducialsPrefGroupUI(OptionsGroupUI):
+    def __init__(self, decimals=4, parent=None):
+
+        super(Tools2FiducialsPrefGroupUI, self).__init__(self, parent=parent)
+
+        self.setTitle(str(_("Fiducials Tool Options")))
+        self.decimals = decimals
+
+        # ## Grid Layout
+        grid_lay = QtWidgets.QGridLayout()
+        self.layout.addLayout(grid_lay)
+        grid_lay.setColumnStretch(0, 0)
+        grid_lay.setColumnStretch(1, 1)
+
+        self.param_label = QtWidgets.QLabel('<b>%s:</b>' % _('Parameters'))
+        self.param_label.setToolTip(
+            _("Parameters used for this tool.")
+        )
+        grid_lay.addWidget(self.param_label, 0, 0, 1, 2)
+
+        # DIAMETER #
+        self.dia_label = QtWidgets.QLabel('%s:' % _("Size"))
+        self.dia_label.setToolTip(
+            _("This set the fiducial diameter if fiducial type is circular,\n"
+              "otherwise is the size of the fiducial.\n"
+              "The soldermask opening is double than that.")
+        )
+        self.dia_entry = FCDoubleSpinner()
+        self.dia_entry.set_range(1.0000, 3.0000)
+        self.dia_entry.set_precision(self.decimals)
+        self.dia_entry.setWrapping(True)
+        self.dia_entry.setSingleStep(0.1)
+
+        grid_lay.addWidget(self.dia_label, 1, 0)
+        grid_lay.addWidget(self.dia_entry, 1, 1)
+
+        # MARGIN #
+        self.margin_label = QtWidgets.QLabel('%s:' % _("Margin"))
+        self.margin_label.setToolTip(
+            _("Bounding box margin.")
+        )
+        self.margin_entry = FCDoubleSpinner()
+        self.margin_entry.set_range(-9999.9999, 9999.9999)
+        self.margin_entry.set_precision(self.decimals)
+        self.margin_entry.setSingleStep(0.1)
+
+        grid_lay.addWidget(self.margin_label, 2, 0)
+        grid_lay.addWidget(self.margin_entry, 2, 1)
+
+        # Mode #
+        self.mode_radio = RadioSet([
+            {'label': _('Auto'), 'value': 'auto'},
+            {"label": _("Manual"), "value": "manual"}
+        ], stretch=False)
+        self.mode_label = QtWidgets.QLabel(_("Mode:"))
+        self.mode_label.setToolTip(
+            _("- 'Auto' - automatic placement of fiducials in the corners of the bounding box.\n"
+              "- 'Manual' - manual placement of fiducials.")
+        )
+        grid_lay.addWidget(self.mode_label, 3, 0)
+        grid_lay.addWidget(self.mode_radio, 3, 1)
+
+        # Position for second fiducial #
+        self.pos_radio = RadioSet([
+            {'label': _('Up'), 'value': 'up'},
+            {"label": _("Down"), "value": "down"},
+            {"label": _("None"), "value": "no"}
+        ], stretch=False)
+        self.pos_label = QtWidgets.QLabel('%s:' % _("Second fiducial"))
+        self.pos_label.setToolTip(
+            _("The position for the second fiducial.\n"
+              "- 'Up' - the order is: bottom-left, top-left, top-right.\n"
+              "- 'Down' - the order is: bottom-left, bottom-right, top-right.\n"
+              "- 'None' - there is no second fiducial. The order is: bottom-left, top-right.")
+        )
+        grid_lay.addWidget(self.pos_label, 4, 0)
+        grid_lay.addWidget(self.pos_radio, 4, 1)
+
+        separator_line = QtWidgets.QFrame()
+        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
+        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
+        grid_lay.addWidget(separator_line, 5, 0, 1, 2)
+
+        # Fiducial type #
+        self.fid_type_radio = RadioSet([
+            {'label': _('Circular'), 'value': 'circular'},
+            {"label": _("Cross"), "value": "cross"},
+            {"label": _("Chess"), "value": "chess"}
+        ], stretch=False)
+
+        self.fid_type_label = QtWidgets.QLabel('%s:' % _("Fiducial Type"))
+        self.fid_type_label.setToolTip(
+            _("The type of fiducial.\n"
+              "- 'Circular' - this is the regular fiducial.\n"
+              "- 'Cross' - cross lines fiducial.\n"
+              "- 'Chess' - chess pattern fiducial.")
+        )
+        grid_lay.addWidget(self.fid_type_label, 6, 0)
+        grid_lay.addWidget(self.fid_type_radio, 6, 1)
+
+        # Line Thickness #
+        self.line_thickness_label = QtWidgets.QLabel('%s:' % _("Line thickness"))
+        self.line_thickness_label.setToolTip(
+            _("Bounding box margin.")
+        )
+        self.line_thickness_entry = FCDoubleSpinner()
+        self.line_thickness_entry.set_range(0.00001, 9999.9999)
+        self.line_thickness_entry.set_precision(self.decimals)
+        self.line_thickness_entry.setSingleStep(0.1)
+
+        grid_lay.addWidget(self.line_thickness_label, 7, 0)
+        grid_lay.addWidget(self.line_thickness_entry, 7, 1)
+
+        self.layout.addStretch()

+ 75 - 0
flatcamGUI/preferences/tools/Tools2InvertPrefGroupUI.py

@@ -0,0 +1,75 @@
+from PyQt5 import QtWidgets
+from PyQt5.QtCore import QSettings
+
+from flatcamGUI.GUIElements import FCDoubleSpinner, RadioSet
+from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
+
+import gettext
+import FlatCAMTranslation as fcTranslate
+import builtins
+
+fcTranslate.apply_language('strings')
+if '_' not in builtins.__dict__:
+    _ = gettext.gettext
+
+settings = QSettings("Open Source", "FlatCAM")
+if settings.contains("machinist"):
+    machinist_setting = settings.value('machinist', type=int)
+else:
+    machinist_setting = 0
+
+
+class Tools2InvertPrefGroupUI(OptionsGroupUI):
+    def __init__(self, decimals=4, parent=None):
+
+        super(Tools2InvertPrefGroupUI, self).__init__(self, parent=parent)
+
+        self.setTitle(str(_("Invert Gerber Tool Options")))
+        self.decimals = decimals
+
+        # ## Subtractor Tool Parameters
+        self.sublabel = QtWidgets.QLabel("<b>%s:</b>" % _("Parameters"))
+        self.sublabel.setToolTip(
+            _("A tool to invert Gerber geometry from positive to negative\n"
+              "and in revers.")
+        )
+        self.layout.addWidget(self.sublabel)
+
+        # Grid Layout
+        grid0 = QtWidgets.QGridLayout()
+        grid0.setColumnStretch(0, 0)
+        grid0.setColumnStretch(1, 1)
+        self.layout.addLayout(grid0)
+
+        # Margin
+        self.margin_label = QtWidgets.QLabel('%s:' % _('Margin'))
+        self.margin_label.setToolTip(
+            _("Distance by which to avoid\n"
+              "the edges of the Gerber object.")
+        )
+        self.margin_entry = FCDoubleSpinner()
+        self.margin_entry.set_precision(self.decimals)
+        self.margin_entry.set_range(0.0000, 9999.9999)
+        self.margin_entry.setObjectName(_("Margin"))
+
+        grid0.addWidget(self.margin_label, 2, 0, 1, 2)
+        grid0.addWidget(self.margin_entry, 3, 0, 1, 2)
+
+        self.join_label = QtWidgets.QLabel('%s:' % _("Lines Join Style"))
+        self.join_label.setToolTip(
+            _("The way that the lines in the object outline will be joined.\n"
+              "Can be:\n"
+              "- rounded -> an arc is added between two joining lines\n"
+              "- square -> the lines meet in 90 degrees angle\n"
+              "- bevel -> the lines are joined by a third line")
+        )
+        self.join_radio = RadioSet([
+            {'label': 'Rounded', 'value': 'r'},
+            {'label': 'Square', 'value': 's'},
+            {'label': 'Bevel', 'value': 'b'}
+        ], orientation='vertical', stretch=False)
+
+        grid0.addWidget(self.join_label, 5, 0, 1, 2)
+        grid0.addWidget(self.join_radio, 7, 0, 1, 2)
+
+        self.layout.addStretch()

+ 56 - 0
flatcamGUI/preferences/tools/Tools2OptimalPrefGroupUI.py

@@ -0,0 +1,56 @@
+from PyQt5 import QtWidgets
+from PyQt5.QtCore import QSettings
+
+from flatcamGUI.GUIElements import FCSpinner
+from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
+
+import gettext
+import FlatCAMTranslation as fcTranslate
+import builtins
+
+fcTranslate.apply_language('strings')
+if '_' not in builtins.__dict__:
+    _ = gettext.gettext
+
+settings = QSettings("Open Source", "FlatCAM")
+if settings.contains("machinist"):
+    machinist_setting = settings.value('machinist', type=int)
+else:
+    machinist_setting = 0
+
+
+class Tools2OptimalPrefGroupUI(OptionsGroupUI):
+    def __init__(self, decimals=4, parent=None):
+
+        super(Tools2OptimalPrefGroupUI, self).__init__(self, parent=parent)
+
+        self.setTitle(str(_("Optimal Tool Options")))
+        self.decimals = decimals
+
+        # ## Parameters
+        self.optlabel = QtWidgets.QLabel("<b>%s:</b>" % _("Parameters"))
+        self.optlabel.setToolTip(
+            _("A tool to find the minimum distance between\n"
+              "every two Gerber geometric elements")
+        )
+        self.layout.addWidget(self.optlabel)
+
+        grid0 = QtWidgets.QGridLayout()
+        self.layout.addLayout(grid0)
+        grid0.setColumnStretch(0, 0)
+        grid0.setColumnStretch(1, 1)
+
+        self.precision_sp = FCSpinner()
+        self.precision_sp.set_range(2, 10)
+        self.precision_sp.set_step(1)
+        self.precision_sp.setWrapping(True)
+
+        self.precision_lbl = QtWidgets.QLabel('%s:' % _("Precision"))
+        self.precision_lbl.setToolTip(
+            _("Number of decimals for the distances and coordinates in this tool.")
+        )
+
+        grid0.addWidget(self.precision_lbl, 0, 0)
+        grid0.addWidget(self.precision_sp, 0, 1)
+
+        self.layout.addStretch()

+ 89 - 0
flatcamGUI/preferences/tools/Tools2PreferencesUI.py

@@ -0,0 +1,89 @@
+from PyQt5 import QtWidgets
+from PyQt5.QtCore import QSettings
+
+from flatcamGUI.preferences.tools.Tools2InvertPrefGroupUI import Tools2InvertPrefGroupUI
+from flatcamGUI.preferences.tools.Tools2PunchGerberPrefGroupUI import Tools2PunchGerberPrefGroupUI
+from flatcamGUI.preferences.tools.Tools2EDrillsPrefGroupUI import Tools2EDrillsPrefGroupUI
+from flatcamGUI.preferences.tools.Tools2CalPrefGroupUI import Tools2CalPrefGroupUI
+from flatcamGUI.preferences.tools.Tools2FiducialsPrefGroupUI import Tools2FiducialsPrefGroupUI
+from flatcamGUI.preferences.tools.Tools2CThievingPrefGroupUI import Tools2CThievingPrefGroupUI
+from flatcamGUI.preferences.tools.Tools2QRCodePrefGroupUI import Tools2QRCodePrefGroupUI
+from flatcamGUI.preferences.tools.Tools2OptimalPrefGroupUI import Tools2OptimalPrefGroupUI
+from flatcamGUI.preferences.tools.Tools2RulesCheckPrefGroupUI import Tools2RulesCheckPrefGroupUI
+
+import gettext
+import FlatCAMTranslation as fcTranslate
+import builtins
+
+fcTranslate.apply_language('strings')
+if '_' not in builtins.__dict__:
+    _ = gettext.gettext
+
+settings = QSettings("Open Source", "FlatCAM")
+if settings.contains("machinist"):
+    machinist_setting = settings.value('machinist', type=int)
+else:
+    machinist_setting = 0
+
+
+class Tools2PreferencesUI(QtWidgets.QWidget):
+
+    def __init__(self, decimals, parent=None):
+        QtWidgets.QWidget.__init__(self, parent=parent)
+        self.layout = QtWidgets.QHBoxLayout()
+        self.setLayout(self.layout)
+        self.decimals = decimals
+
+        self.tools2_checkrules_group = Tools2RulesCheckPrefGroupUI(decimals=self.decimals)
+        self.tools2_checkrules_group.setMinimumWidth(220)
+
+        self.tools2_optimal_group = Tools2OptimalPrefGroupUI(decimals=self.decimals)
+        self.tools2_optimal_group.setMinimumWidth(220)
+
+        self.tools2_qrcode_group = Tools2QRCodePrefGroupUI(decimals=self.decimals)
+        self.tools2_qrcode_group.setMinimumWidth(220)
+
+        self.tools2_cfill_group = Tools2CThievingPrefGroupUI(decimals=self.decimals)
+        self.tools2_cfill_group.setMinimumWidth(220)
+
+        self.tools2_fiducials_group = Tools2FiducialsPrefGroupUI(decimals=self.decimals)
+        self.tools2_fiducials_group.setMinimumWidth(220)
+
+        self.tools2_cal_group = Tools2CalPrefGroupUI(decimals=self.decimals)
+        self.tools2_cal_group.setMinimumWidth(220)
+
+        self.tools2_edrills_group = Tools2EDrillsPrefGroupUI(decimals=self.decimals)
+        self.tools2_edrills_group.setMinimumWidth(220)
+
+        self.tools2_punch_group = Tools2PunchGerberPrefGroupUI(decimals=self.decimals)
+        self.tools2_punch_group.setMinimumWidth(220)
+
+        self.tools2_invert_group = Tools2InvertPrefGroupUI(decimals=self.decimals)
+        self.tools2_invert_group.setMinimumWidth(220)
+
+        self.vlay = QtWidgets.QVBoxLayout()
+        self.vlay.addWidget(self.tools2_checkrules_group)
+        self.vlay.addWidget(self.tools2_optimal_group)
+
+        self.vlay1 = QtWidgets.QVBoxLayout()
+        self.vlay1.addWidget(self.tools2_qrcode_group)
+        self.vlay1.addWidget(self.tools2_fiducials_group)
+
+        self.vlay2 = QtWidgets.QVBoxLayout()
+        self.vlay2.addWidget(self.tools2_cfill_group)
+
+        self.vlay3 = QtWidgets.QVBoxLayout()
+        self.vlay3.addWidget(self.tools2_cal_group)
+        self.vlay3.addWidget(self.tools2_edrills_group)
+
+        self.vlay4 = QtWidgets.QVBoxLayout()
+        self.vlay4.addWidget(self.tools2_punch_group)
+        self.vlay4.addWidget(self.tools2_invert_group)
+
+        self.layout.addLayout(self.vlay)
+        self.layout.addLayout(self.vlay1)
+        self.layout.addLayout(self.vlay2)
+        self.layout.addLayout(self.vlay3)
+        self.layout.addLayout(self.vlay4)
+
+        self.layout.addStretch()

+ 233 - 0
flatcamGUI/preferences/tools/Tools2PunchGerberPrefGroupUI.py

@@ -0,0 +1,233 @@
+from PyQt5 import QtWidgets
+from PyQt5.QtCore import QSettings
+
+from flatcamGUI.GUIElements import FCCheckBox, RadioSet, FCDoubleSpinner
+from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
+
+import gettext
+import FlatCAMTranslation as fcTranslate
+import builtins
+
+fcTranslate.apply_language('strings')
+if '_' not in builtins.__dict__:
+    _ = gettext.gettext
+
+settings = QSettings("Open Source", "FlatCAM")
+if settings.contains("machinist"):
+    machinist_setting = settings.value('machinist', type=int)
+else:
+    machinist_setting = 0
+
+
+class Tools2PunchGerberPrefGroupUI(OptionsGroupUI):
+    def __init__(self, decimals=4, parent=None):
+
+        super(Tools2PunchGerberPrefGroupUI, self).__init__(self, parent=parent)
+
+        self.setTitle(str(_("Punch Gerber Options")))
+        self.decimals = decimals
+
+        # ## Grid Layout
+        grid_lay = QtWidgets.QGridLayout()
+        self.layout.addLayout(grid_lay)
+        grid_lay.setColumnStretch(0, 0)
+        grid_lay.setColumnStretch(1, 1)
+
+        self.param_label = QtWidgets.QLabel('<b>%s:</b>' % _('Parameters'))
+        self.param_label.setToolTip(
+            _("Parameters used for this tool.")
+        )
+        grid_lay.addWidget(self.param_label, 0, 0, 1, 2)
+
+        self.padt_label = QtWidgets.QLabel("<b>%s:</b>" % _("Processed Pads Type"))
+        self.padt_label.setToolTip(
+            _("The type of pads shape to be processed.\n"
+              "If the PCB has many SMD pads with rectangular pads,\n"
+              "disable the Rectangular aperture.")
+        )
+
+        grid_lay.addWidget(self.padt_label, 2, 0, 1, 2)
+
+        # Circular Aperture Selection
+        self.circular_cb = FCCheckBox('%s' % _("Circular"))
+        self.circular_cb.setToolTip(
+            _("Process Circular Pads.")
+        )
+
+        grid_lay.addWidget(self.circular_cb, 3, 0, 1, 2)
+
+        # Oblong Aperture Selection
+        self.oblong_cb = FCCheckBox('%s' % _("Oblong"))
+        self.oblong_cb.setToolTip(
+            _("Process Oblong Pads.")
+        )
+
+        grid_lay.addWidget(self.oblong_cb, 4, 0, 1, 2)
+
+        # Square Aperture Selection
+        self.square_cb = FCCheckBox('%s' % _("Square"))
+        self.square_cb.setToolTip(
+            _("Process Square Pads.")
+        )
+
+        grid_lay.addWidget(self.square_cb, 5, 0, 1, 2)
+
+        # Rectangular Aperture Selection
+        self.rectangular_cb = FCCheckBox('%s' % _("Rectangular"))
+        self.rectangular_cb.setToolTip(
+            _("Process Rectangular Pads.")
+        )
+
+        grid_lay.addWidget(self.rectangular_cb, 6, 0, 1, 2)
+
+        # Others type of Apertures Selection
+        self.other_cb = FCCheckBox('%s' % _("Others"))
+        self.other_cb.setToolTip(
+            _("Process pads not in the categories above.")
+        )
+
+        grid_lay.addWidget(self.other_cb, 7, 0, 1, 2)
+
+        separator_line = QtWidgets.QFrame()
+        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
+        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
+        grid_lay.addWidget(separator_line, 8, 0, 1, 2)
+
+        # ## Axis
+        self.hole_size_radio = RadioSet(
+            [
+                {'label': _("Excellon"), 'value': 'exc'},
+                {'label': _("Fixed Diameter"), 'value': 'fixed'},
+                {'label': _("Fixed Annular Ring"), 'value': 'ring'},
+                {'label': _("Proportional"), 'value': 'prop'}
+            ],
+            orientation='vertical',
+            stretch=False)
+        self.hole_size_label = QtWidgets.QLabel('<b>%s:</b>' % _("Method"))
+        self.hole_size_label.setToolTip(
+            _("The punch hole source can be:\n"
+              "- Excellon Object-> the Excellon object drills center will serve as reference.\n"
+              "- Fixed Diameter -> will try to use the pads center as reference adding fixed diameter holes.\n"
+              "- Fixed Annular Ring -> will try to keep a set annular ring.\n"
+              "- Proportional -> will make a Gerber punch hole having the diameter a percentage of the pad diameter.")
+        )
+        grid_lay.addWidget(self.hole_size_label, 9, 0)
+        grid_lay.addWidget(self.hole_size_radio, 9, 1)
+
+        # grid_lay1.addWidget(QtWidgets.QLabel(''))
+
+        separator_line = QtWidgets.QFrame()
+        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
+        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
+        grid_lay.addWidget(separator_line, 10, 0, 1, 2)
+
+        # Annular Ring
+        self.fixed_label = QtWidgets.QLabel('<b>%s</b>' % _("Fixed Diameter"))
+        grid_lay.addWidget(self.fixed_label, 11, 0, 1, 2)
+
+        # Diameter value
+        self.dia_entry = FCDoubleSpinner()
+        self.dia_entry.set_precision(self.decimals)
+        self.dia_entry.set_range(0.0000, 9999.9999)
+
+        self.dia_label = QtWidgets.QLabel('%s:' % _("Value"))
+        self.dia_label.setToolTip(
+            _("Fixed hole diameter.")
+        )
+
+        grid_lay.addWidget(self.dia_label, 12, 0)
+        grid_lay.addWidget(self.dia_entry, 12, 1)
+
+        # Annular Ring value
+        self.ring_label = QtWidgets.QLabel('<b>%s</b>' % _("Fixed Annular Ring"))
+        self.ring_label.setToolTip(
+            _("The size of annular ring.\n"
+              "The copper sliver between the hole exterior\n"
+              "and the margin of the copper pad.")
+        )
+        grid_lay.addWidget(self.ring_label, 13, 0, 1, 2)
+
+        # Circular Annular Ring Value
+        self.circular_ring_label = QtWidgets.QLabel('%s:' % _("Circular"))
+        self.circular_ring_label.setToolTip(
+            _("The size of annular ring for circular pads.")
+        )
+
+        self.circular_ring_entry = FCDoubleSpinner()
+        self.circular_ring_entry.set_precision(self.decimals)
+        self.circular_ring_entry.set_range(0.0000, 9999.9999)
+
+        grid_lay.addWidget(self.circular_ring_label, 14, 0)
+        grid_lay.addWidget(self.circular_ring_entry, 14, 1)
+
+        # Oblong Annular Ring Value
+        self.oblong_ring_label = QtWidgets.QLabel('%s:' % _("Oblong"))
+        self.oblong_ring_label.setToolTip(
+            _("The size of annular ring for oblong pads.")
+        )
+
+        self.oblong_ring_entry = FCDoubleSpinner()
+        self.oblong_ring_entry.set_precision(self.decimals)
+        self.oblong_ring_entry.set_range(0.0000, 9999.9999)
+
+        grid_lay.addWidget(self.oblong_ring_label, 15, 0)
+        grid_lay.addWidget(self.oblong_ring_entry, 15, 1)
+
+        # Square Annular Ring Value
+        self.square_ring_label = QtWidgets.QLabel('%s:' % _("Square"))
+        self.square_ring_label.setToolTip(
+            _("The size of annular ring for square pads.")
+        )
+
+        self.square_ring_entry = FCDoubleSpinner()
+        self.square_ring_entry.set_precision(self.decimals)
+        self.square_ring_entry.set_range(0.0000, 9999.9999)
+
+        grid_lay.addWidget(self.square_ring_label, 16, 0)
+        grid_lay.addWidget(self.square_ring_entry, 16, 1)
+
+        # Rectangular Annular Ring Value
+        self.rectangular_ring_label = QtWidgets.QLabel('%s:' % _("Rectangular"))
+        self.rectangular_ring_label.setToolTip(
+            _("The size of annular ring for rectangular pads.")
+        )
+
+        self.rectangular_ring_entry = FCDoubleSpinner()
+        self.rectangular_ring_entry.set_precision(self.decimals)
+        self.rectangular_ring_entry.set_range(0.0000, 9999.9999)
+
+        grid_lay.addWidget(self.rectangular_ring_label, 17, 0)
+        grid_lay.addWidget(self.rectangular_ring_entry, 17, 1)
+
+        # Others Annular Ring Value
+        self.other_ring_label = QtWidgets.QLabel('%s:' % _("Others"))
+        self.other_ring_label.setToolTip(
+            _("The size of annular ring for other pads.")
+        )
+
+        self.other_ring_entry = FCDoubleSpinner()
+        self.other_ring_entry.set_precision(self.decimals)
+        self.other_ring_entry.set_range(0.0000, 9999.9999)
+
+        grid_lay.addWidget(self.other_ring_label, 18, 0)
+        grid_lay.addWidget(self.other_ring_entry, 18, 1)
+
+        self.prop_label = QtWidgets.QLabel('<b>%s</b>' % _("Proportional Diameter"))
+        grid_lay.addWidget(self.prop_label, 19, 0, 1, 2)
+
+        # Factor value
+        self.factor_entry = FCDoubleSpinner(suffix='%')
+        self.factor_entry.set_precision(self.decimals)
+        self.factor_entry.set_range(0.0000, 100.0000)
+        self.factor_entry.setSingleStep(0.1)
+
+        self.factor_label = QtWidgets.QLabel('%s:' % _("Factor"))
+        self.factor_label.setToolTip(
+            _("Proportional Diameter.\n"
+              "The hole diameter will be a fraction of the pad size.")
+        )
+
+        grid_lay.addWidget(self.factor_label, 20, 0)
+        grid_lay.addWidget(self.factor_entry, 20, 1)
+
+        self.layout.addStretch()

+ 207 - 0
flatcamGUI/preferences/tools/Tools2QRCodePrefGroupUI.py

@@ -0,0 +1,207 @@
+from PyQt5 import QtWidgets, QtCore
+from PyQt5.QtCore import Qt, QSettings
+
+from flatcamGUI.GUIElements import FCSpinner, RadioSet, FCTextArea, FCEntry
+from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
+
+import gettext
+import FlatCAMTranslation as fcTranslate
+import builtins
+
+fcTranslate.apply_language('strings')
+if '_' not in builtins.__dict__:
+    _ = gettext.gettext
+
+settings = QSettings("Open Source", "FlatCAM")
+if settings.contains("machinist"):
+    machinist_setting = settings.value('machinist', type=int)
+else:
+    machinist_setting = 0
+
+
+class Tools2QRCodePrefGroupUI(OptionsGroupUI):
+    def __init__(self, decimals=4, parent=None):
+
+        super(Tools2QRCodePrefGroupUI, self).__init__(self, parent=parent)
+
+        self.setTitle(str(_("QRCode Tool Options")))
+        self.decimals = decimals
+
+        # ## Parameters
+        self.qrlabel = QtWidgets.QLabel("<b>%s:</b>" % _("Parameters"))
+        self.qrlabel.setToolTip(
+            _("A tool to create a QRCode that can be inserted\n"
+              "into a selected Gerber file, or it can be exported as a file.")
+        )
+        self.layout.addWidget(self.qrlabel)
+
+        # ## Grid Layout
+        grid_lay = QtWidgets.QGridLayout()
+        self.layout.addLayout(grid_lay)
+        grid_lay.setColumnStretch(0, 0)
+        grid_lay.setColumnStretch(1, 1)
+
+        # VERSION #
+        self.version_label = QtWidgets.QLabel('%s:' % _("Version"))
+        self.version_label.setToolTip(
+            _("QRCode version can have values from 1 (21x21 boxes)\n"
+              "to 40 (177x177 boxes).")
+        )
+        self.version_entry = FCSpinner()
+        self.version_entry.set_range(1, 40)
+        self.version_entry.setWrapping(True)
+
+        grid_lay.addWidget(self.version_label, 1, 0)
+        grid_lay.addWidget(self.version_entry, 1, 1)
+
+        # ERROR CORRECTION #
+        self.error_label = QtWidgets.QLabel('%s:' % _("Error correction"))
+        self.error_label.setToolTip(
+            _("Parameter that controls the error correction used for the QR Code.\n"
+              "L = maximum 7%% errors can be corrected\n"
+              "M = maximum 15%% errors can be corrected\n"
+              "Q = maximum 25%% errors can be corrected\n"
+              "H = maximum 30%% errors can be corrected.")
+        )
+        self.error_radio = RadioSet([{'label': 'L', 'value': 'L'},
+                                     {'label': 'M', 'value': 'M'},
+                                     {'label': 'Q', 'value': 'Q'},
+                                     {'label': 'H', 'value': 'H'}])
+        self.error_radio.setToolTip(
+            _("Parameter that controls the error correction used for the QR Code.\n"
+              "L = maximum 7%% errors can be corrected\n"
+              "M = maximum 15%% errors can be corrected\n"
+              "Q = maximum 25%% errors can be corrected\n"
+              "H = maximum 30%% errors can be corrected.")
+        )
+        grid_lay.addWidget(self.error_label, 2, 0)
+        grid_lay.addWidget(self.error_radio, 2, 1)
+
+        # BOX SIZE #
+        self.bsize_label = QtWidgets.QLabel('%s:' % _("Box Size"))
+        self.bsize_label.setToolTip(
+            _("Box size control the overall size of the QRcode\n"
+              "by adjusting the size of each box in the code.")
+        )
+        self.bsize_entry = FCSpinner()
+        self.bsize_entry.set_range(1, 9999)
+        self.bsize_entry.setWrapping(True)
+
+        grid_lay.addWidget(self.bsize_label, 3, 0)
+        grid_lay.addWidget(self.bsize_entry, 3, 1)
+
+        # BORDER SIZE #
+        self.border_size_label = QtWidgets.QLabel('%s:' % _("Border Size"))
+        self.border_size_label.setToolTip(
+            _("Size of the QRCode border. How many boxes thick is the border.\n"
+              "Default value is 4. The width of the clearance around the QRCode.")
+        )
+        self.border_size_entry = FCSpinner()
+        self.border_size_entry.set_range(1, 9999)
+        self.border_size_entry.setWrapping(True)
+
+        grid_lay.addWidget(self.border_size_label, 4, 0)
+        grid_lay.addWidget(self.border_size_entry, 4, 1)
+
+        # Text box
+        self.text_label = QtWidgets.QLabel('%s:' % _("QRCode Data"))
+        self.text_label.setToolTip(
+            _("QRCode Data. Alphanumeric text to be encoded in the QRCode.")
+        )
+        self.text_data = FCTextArea()
+        self.text_data.setPlaceholderText(
+            _("Add here the text to be included in the QRCode...")
+        )
+        grid_lay.addWidget(self.text_label, 5, 0)
+        grid_lay.addWidget(self.text_data, 6, 0, 1, 2)
+
+        # POLARITY CHOICE #
+        self.pol_label = QtWidgets.QLabel('%s:' % _("Polarity"))
+        self.pol_label.setToolTip(
+            _("Choose the polarity of the QRCode.\n"
+              "It can be drawn in a negative way (squares are clear)\n"
+              "or in a positive way (squares are opaque).")
+        )
+        self.pol_radio = RadioSet([{'label': _('Negative'), 'value': 'neg'},
+                                   {'label': _('Positive'), 'value': 'pos'}])
+        self.pol_radio.setToolTip(
+            _("Choose the type of QRCode to be created.\n"
+              "If added on a Silkscreen Gerber file the QRCode may\n"
+              "be added as positive. If it is added to a Copper Gerber\n"
+              "file then perhaps the QRCode can be added as negative.")
+        )
+        grid_lay.addWidget(self.pol_label, 7, 0)
+        grid_lay.addWidget(self.pol_radio, 7, 1)
+
+        # BOUNDING BOX TYPE #
+        self.bb_label = QtWidgets.QLabel('%s:' % _("Bounding Box"))
+        self.bb_label.setToolTip(
+            _("The bounding box, meaning the empty space that surrounds\n"
+              "the QRCode geometry, can have a rounded or a square shape.")
+        )
+        self.bb_radio = RadioSet([{'label': _('Rounded'), 'value': 'r'},
+                                  {'label': _('Square'), 'value': 's'}])
+        self.bb_radio.setToolTip(
+            _("The bounding box, meaning the empty space that surrounds\n"
+              "the QRCode geometry, can have a rounded or a square shape.")
+        )
+        grid_lay.addWidget(self.bb_label, 8, 0)
+        grid_lay.addWidget(self.bb_radio, 8, 1)
+
+        # FILL COLOR #
+        self.fill_color_label = QtWidgets.QLabel('%s:' % _('Fill Color'))
+        self.fill_color_label.setToolTip(
+            _("Set the QRCode fill color (squares color).")
+        )
+        self.fill_color_entry = FCEntry()
+        self.fill_color_button = QtWidgets.QPushButton()
+        self.fill_color_button.setFixedSize(15, 15)
+
+        fill_lay_child = QtWidgets.QHBoxLayout()
+        fill_lay_child.setContentsMargins(0, 0, 0, 0)
+        fill_lay_child.addWidget(self.fill_color_entry)
+        fill_lay_child.addWidget(self.fill_color_button, alignment=Qt.AlignRight)
+        fill_lay_child.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
+
+        fill_color_widget = QtWidgets.QWidget()
+        fill_color_widget.setLayout(fill_lay_child)
+
+        grid_lay.addWidget(self.fill_color_label, 9, 0)
+        grid_lay.addWidget(fill_color_widget, 9, 1)
+
+        # BACK COLOR #
+        self.back_color_label = QtWidgets.QLabel('%s:' % _('Back Color'))
+        self.back_color_label.setToolTip(
+            _("Set the QRCode background color.")
+        )
+        self.back_color_entry = FCEntry()
+        self.back_color_button = QtWidgets.QPushButton()
+        self.back_color_button.setFixedSize(15, 15)
+
+        back_lay_child = QtWidgets.QHBoxLayout()
+        back_lay_child.setContentsMargins(0, 0, 0, 0)
+        back_lay_child.addWidget(self.back_color_entry)
+        back_lay_child.addWidget(self.back_color_button, alignment=Qt.AlignRight)
+        back_lay_child.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
+
+        back_color_widget = QtWidgets.QWidget()
+        back_color_widget.setLayout(back_lay_child)
+
+        grid_lay.addWidget(self.back_color_label, 10, 0)
+        grid_lay.addWidget(back_color_widget, 10, 1)
+
+        # Selection Limit
+        self.sel_limit_label = QtWidgets.QLabel('%s:' % _("Selection limit"))
+        self.sel_limit_label.setToolTip(
+            _("Set the number of selected geometry\n"
+              "items above which the utility geometry\n"
+              "becomes just a selection rectangle.\n"
+              "Increases the performance when moving a\n"
+              "large number of geometric elements.")
+        )
+        self.sel_limit_entry = FCSpinner()
+        self.sel_limit_entry.set_range(0, 9999)
+
+        grid_lay.addWidget(self.sel_limit_label, 11, 0)
+        grid_lay.addWidget(self.sel_limit_entry, 11, 1)
+        # self.layout.addStretch()

+ 242 - 0
flatcamGUI/preferences/tools/Tools2RulesCheckPrefGroupUI.py

@@ -0,0 +1,242 @@
+from PyQt5 import QtWidgets
+from PyQt5.QtCore import QSettings
+
+from flatcamGUI.GUIElements import FCCheckBox, FCDoubleSpinner
+from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
+
+import gettext
+import FlatCAMTranslation as fcTranslate
+import builtins
+
+fcTranslate.apply_language('strings')
+if '_' not in builtins.__dict__:
+    _ = gettext.gettext
+
+settings = QSettings("Open Source", "FlatCAM")
+if settings.contains("machinist"):
+    machinist_setting = settings.value('machinist', type=int)
+else:
+    machinist_setting = 0
+
+
+class Tools2RulesCheckPrefGroupUI(OptionsGroupUI):
+    def __init__(self, decimals=4, parent=None):
+
+        super(Tools2RulesCheckPrefGroupUI, self).__init__(self, parent=parent)
+
+        self.setTitle(str(_("Check Rules Tool Options")))
+        self.decimals = decimals
+
+        self.crlabel = QtWidgets.QLabel("<b>%s:</b>" % _("Parameters"))
+        self.crlabel.setToolTip(
+            _("A tool to check if Gerber files are within a set\n"
+              "of Manufacturing Rules.")
+        )
+        self.layout.addWidget(self.crlabel)
+
+        # Form Layout
+        self.form_layout_1 = QtWidgets.QFormLayout()
+        self.layout.addLayout(self.form_layout_1)
+
+        # Trace size
+        self.trace_size_cb = FCCheckBox('%s:' % _("Trace Size"))
+        self.trace_size_cb.setToolTip(
+            _("This checks if the minimum size for traces is met.")
+        )
+        self.form_layout_1.addRow(self.trace_size_cb)
+
+        # Trace size value
+        self.trace_size_entry = FCDoubleSpinner()
+        self.trace_size_entry.set_range(0.00001, 999.99999)
+        self.trace_size_entry.set_precision(self.decimals)
+        self.trace_size_entry.setSingleStep(0.1)
+
+        self.trace_size_lbl = QtWidgets.QLabel('%s:' % _("Min value"))
+        self.trace_size_lbl.setToolTip(
+            _("Minimum acceptable trace size.")
+        )
+        self.form_layout_1.addRow(self.trace_size_lbl, self.trace_size_entry)
+
+        # Copper2copper clearance
+        self.clearance_copper2copper_cb = FCCheckBox('%s:' % _("Copper to Copper clearance"))
+        self.clearance_copper2copper_cb.setToolTip(
+            _("This checks if the minimum clearance between copper\n"
+              "features is met.")
+        )
+        self.form_layout_1.addRow(self.clearance_copper2copper_cb)
+
+        # Copper2copper clearance value
+        self.clearance_copper2copper_entry = FCDoubleSpinner()
+        self.clearance_copper2copper_entry.set_range(0.00001, 999.99999)
+        self.clearance_copper2copper_entry.set_precision(self.decimals)
+        self.clearance_copper2copper_entry.setSingleStep(0.1)
+
+        self.clearance_copper2copper_lbl = QtWidgets.QLabel('%s:' % _("Min value"))
+        self.clearance_copper2copper_lbl.setToolTip(
+            _("Minimum acceptable clearance value.")
+        )
+        self.form_layout_1.addRow(self.clearance_copper2copper_lbl, self.clearance_copper2copper_entry)
+
+        # Copper2outline clearance
+        self.clearance_copper2ol_cb = FCCheckBox('%s:' % _("Copper to Outline clearance"))
+        self.clearance_copper2ol_cb.setToolTip(
+            _("This checks if the minimum clearance between copper\n"
+              "features and the outline is met.")
+        )
+        self.form_layout_1.addRow(self.clearance_copper2ol_cb)
+
+        # Copper2outline clearance value
+        self.clearance_copper2ol_entry = FCDoubleSpinner()
+        self.clearance_copper2ol_entry.set_range(0.00001, 999.99999)
+        self.clearance_copper2ol_entry.set_precision(self.decimals)
+        self.clearance_copper2ol_entry.setSingleStep(0.1)
+
+        self.clearance_copper2ol_lbl = QtWidgets.QLabel('%s:' % _("Min value"))
+        self.clearance_copper2ol_lbl.setToolTip(
+            _("Minimum acceptable clearance value.")
+        )
+        self.form_layout_1.addRow(self.clearance_copper2ol_lbl, self.clearance_copper2ol_entry)
+
+        # Silkscreen2silkscreen clearance
+        self.clearance_silk2silk_cb = FCCheckBox('%s:' % _("Silk to Silk Clearance"))
+        self.clearance_silk2silk_cb.setToolTip(
+            _("This checks if the minimum clearance between silkscreen\n"
+              "features and silkscreen features is met.")
+        )
+        self.form_layout_1.addRow(self.clearance_silk2silk_cb)
+
+        # Copper2silkscreen clearance value
+        self.clearance_silk2silk_entry = FCDoubleSpinner()
+        self.clearance_silk2silk_entry.set_range(0.00001, 999.99999)
+        self.clearance_silk2silk_entry.set_precision(self.decimals)
+        self.clearance_silk2silk_entry.setSingleStep(0.1)
+
+        self.clearance_silk2silk_lbl = QtWidgets.QLabel('%s:' % _("Min value"))
+        self.clearance_silk2silk_lbl.setToolTip(
+            _("Minimum acceptable clearance value.")
+        )
+        self.form_layout_1.addRow(self.clearance_silk2silk_lbl, self.clearance_silk2silk_entry)
+
+        # Silkscreen2soldermask clearance
+        self.clearance_silk2sm_cb = FCCheckBox('%s:' % _("Silk to Solder Mask Clearance"))
+        self.clearance_silk2sm_cb.setToolTip(
+            _("This checks if the minimum clearance between silkscreen\n"
+              "features and soldermask features is met.")
+        )
+        self.form_layout_1.addRow(self.clearance_silk2sm_cb)
+
+        # Silkscreen2soldermask clearance value
+        self.clearance_silk2sm_entry = FCDoubleSpinner()
+        self.clearance_silk2sm_entry.set_range(0.00001, 999.99999)
+        self.clearance_silk2sm_entry.set_precision(self.decimals)
+        self.clearance_silk2sm_entry.setSingleStep(0.1)
+
+        self.clearance_silk2sm_lbl = QtWidgets.QLabel('%s:' % _("Min value"))
+        self.clearance_silk2sm_lbl.setToolTip(
+            _("Minimum acceptable clearance value.")
+        )
+        self.form_layout_1.addRow(self.clearance_silk2sm_lbl, self.clearance_silk2sm_entry)
+
+        # Silk2outline clearance
+        self.clearance_silk2ol_cb = FCCheckBox('%s:' % _("Silk to Outline Clearance"))
+        self.clearance_silk2ol_cb.setToolTip(
+            _("This checks if the minimum clearance between silk\n"
+              "features and the outline is met.")
+        )
+        self.form_layout_1.addRow(self.clearance_silk2ol_cb)
+
+        # Silk2outline clearance value
+        self.clearance_silk2ol_entry = FCDoubleSpinner()
+        self.clearance_silk2ol_entry.set_range(0.00001, 999.99999)
+        self.clearance_silk2ol_entry.set_precision(self.decimals)
+        self.clearance_silk2ol_entry.setSingleStep(0.1)
+
+        self.clearance_silk2ol_lbl = QtWidgets.QLabel('%s:' % _("Min value"))
+        self.clearance_silk2ol_lbl.setToolTip(
+            _("Minimum acceptable clearance value.")
+        )
+        self.form_layout_1.addRow(self.clearance_silk2ol_lbl, self.clearance_silk2ol_entry)
+
+        # Soldermask2soldermask clearance
+        self.clearance_sm2sm_cb = FCCheckBox('%s:' % _("Minimum Solder Mask Sliver"))
+        self.clearance_sm2sm_cb.setToolTip(
+            _("This checks if the minimum clearance between soldermask\n"
+              "features and soldermask features is met.")
+        )
+        self.form_layout_1.addRow(self.clearance_sm2sm_cb)
+
+        # Soldermask2soldermask clearance value
+        self.clearance_sm2sm_entry = FCDoubleSpinner()
+        self.clearance_sm2sm_entry.set_range(0.00001, 999.99999)
+        self.clearance_sm2sm_entry.set_precision(self.decimals)
+        self.clearance_sm2sm_entry.setSingleStep(0.1)
+
+        self.clearance_sm2sm_lbl = QtWidgets.QLabel('%s:' % _("Min value"))
+        self.clearance_sm2sm_lbl.setToolTip(
+            _("Minimum acceptable clearance value.")
+        )
+        self.form_layout_1.addRow(self.clearance_sm2sm_lbl, self.clearance_sm2sm_entry)
+
+        # Ring integrity check
+        self.ring_integrity_cb = FCCheckBox('%s:' % _("Minimum Annular Ring"))
+        self.ring_integrity_cb.setToolTip(
+            _("This checks if the minimum copper ring left by drilling\n"
+              "a hole into a pad is met.")
+        )
+        self.form_layout_1.addRow(self.ring_integrity_cb)
+
+        # Ring integrity value
+        self.ring_integrity_entry = FCDoubleSpinner()
+        self.ring_integrity_entry.set_range(0.00001, 999.99999)
+        self.ring_integrity_entry.set_precision(self.decimals)
+        self.ring_integrity_entry.setSingleStep(0.1)
+
+        self.ring_integrity_lbl = QtWidgets.QLabel('%s:' % _("Min value"))
+        self.ring_integrity_lbl.setToolTip(
+            _("Minimum acceptable ring value.")
+        )
+        self.form_layout_1.addRow(self.ring_integrity_lbl, self.ring_integrity_entry)
+
+        self.form_layout_1.addRow(QtWidgets.QLabel(""))
+
+        # Hole2Hole clearance
+        self.clearance_d2d_cb = FCCheckBox('%s:' % _("Hole to Hole Clearance"))
+        self.clearance_d2d_cb.setToolTip(
+            _("This checks if the minimum clearance between a drill hole\n"
+              "and another drill hole is met.")
+        )
+        self.form_layout_1.addRow(self.clearance_d2d_cb)
+
+        # Hole2Hole clearance value
+        self.clearance_d2d_entry = FCDoubleSpinner()
+        self.clearance_d2d_entry.set_range(0.00001, 999.99999)
+        self.clearance_d2d_entry.set_precision(self.decimals)
+        self.clearance_d2d_entry.setSingleStep(0.1)
+
+        self.clearance_d2d_lbl = QtWidgets.QLabel('%s:' % _("Min value"))
+        self.clearance_d2d_lbl.setToolTip(
+            _("Minimum acceptable drill size.")
+        )
+        self.form_layout_1.addRow(self.clearance_d2d_lbl, self.clearance_d2d_entry)
+
+        # Drill holes size check
+        self.drill_size_cb = FCCheckBox('%s:' % _("Hole Size"))
+        self.drill_size_cb.setToolTip(
+            _("This checks if the drill holes\n"
+              "sizes are above the threshold.")
+        )
+        self.form_layout_1.addRow(self.drill_size_cb)
+
+        # Drile holes value
+        self.drill_size_entry = FCDoubleSpinner()
+        self.drill_size_entry.set_range(0.00001, 999.99999)
+        self.drill_size_entry.set_precision(self.decimals)
+        self.drill_size_entry.setSingleStep(0.1)
+
+        self.drill_size_lbl = QtWidgets.QLabel('%s:' % _("Min value"))
+        self.drill_size_lbl.setToolTip(
+            _("Minimum acceptable clearance value.")
+        )
+        self.form_layout_1.addRow(self.drill_size_lbl, self.drill_size_entry)
+
+        self.layout.addStretch()

+ 92 - 0
flatcamGUI/preferences/tools/Tools2sidedPrefGroupUI.py

@@ -0,0 +1,92 @@
+from PyQt5 import QtWidgets
+from PyQt5.QtCore import QSettings
+
+from flatcamGUI.GUIElements import FCDoubleSpinner, RadioSet
+from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
+
+import gettext
+import FlatCAMTranslation as fcTranslate
+import builtins
+
+fcTranslate.apply_language('strings')
+if '_' not in builtins.__dict__:
+    _ = gettext.gettext
+
+settings = QSettings("Open Source", "FlatCAM")
+if settings.contains("machinist"):
+    machinist_setting = settings.value('machinist', type=int)
+else:
+    machinist_setting = 0
+
+
+class Tools2sidedPrefGroupUI(OptionsGroupUI):
+    def __init__(self, decimals=4, parent=None):
+        # OptionsGroupUI.__init__(self, "2sided Tool Options", parent=parent)
+        super(Tools2sidedPrefGroupUI, self).__init__(self, parent=parent)
+
+        self.setTitle(str(_("2Sided Tool Options")))
+        self.decimals = decimals
+
+        # ## Board cuttout
+        self.dblsided_label = QtWidgets.QLabel("<b>%s:</b>" % _("Parameters"))
+        self.dblsided_label.setToolTip(
+            _("A tool to help in creating a double sided\n"
+              "PCB using alignment holes.")
+        )
+        self.layout.addWidget(self.dblsided_label)
+
+        grid0 = QtWidgets.QGridLayout()
+        self.layout.addLayout(grid0)
+
+        # ## Drill diameter for alignment holes
+        self.drill_dia_entry = FCDoubleSpinner()
+        self.drill_dia_entry.set_range(0.000001, 9999.9999)
+        self.drill_dia_entry.set_precision(self.decimals)
+        self.drill_dia_entry.setSingleStep(0.1)
+
+        self.dd_label = QtWidgets.QLabel('%s:' % _("Drill dia"))
+        self.dd_label.setToolTip(
+            _("Diameter of the drill for the "
+              "alignment holes.")
+        )
+        grid0.addWidget(self.dd_label, 0, 0)
+        grid0.addWidget(self.drill_dia_entry, 0, 1)
+
+        # ## Alignment Axis
+        self.align_ax_label = QtWidgets.QLabel('%s:' % _("Align Axis"))
+        self.align_ax_label.setToolTip(
+            _("Mirror vertically (X) or horizontally (Y).")
+        )
+        self.align_axis_radio = RadioSet([{'label': 'X', 'value': 'X'},
+                                          {'label': 'Y', 'value': 'Y'}])
+
+        grid0.addWidget(self.align_ax_label, 1, 0)
+        grid0.addWidget(self.align_axis_radio, 1, 1)
+
+        # ## Axis
+        self.mirror_axis_radio = RadioSet([{'label': 'X', 'value': 'X'},
+                                           {'label': 'Y', 'value': 'Y'}])
+        self.mirax_label = QtWidgets.QLabel(_("Mirror Axis:"))
+        self.mirax_label.setToolTip(
+            _("Mirror vertically (X) or horizontally (Y).")
+        )
+
+        self.empty_lb1 = QtWidgets.QLabel("")
+        grid0.addWidget(self.empty_lb1, 2, 0)
+        grid0.addWidget(self.mirax_label, 3, 0)
+        grid0.addWidget(self.mirror_axis_radio, 3, 1)
+
+        # ## Axis Location
+        self.axis_location_radio = RadioSet([{'label': _('Point'), 'value': 'point'},
+                                             {'label': _('Box'), 'value': 'box'}])
+        self.axloc_label = QtWidgets.QLabel('%s:' % _("Axis Ref"))
+        self.axloc_label.setToolTip(
+            _("The axis should pass through a <b>point</b> or cut\n "
+              "a specified <b>box</b> (in a FlatCAM object) through \n"
+              "the center.")
+        )
+
+        grid0.addWidget(self.axloc_label, 4, 0)
+        grid0.addWidget(self.axis_location_radio, 4, 1)
+
+        self.layout.addStretch()

+ 142 - 0
flatcamGUI/preferences/tools/ToolsCalculatorsPrefGroupUI.py

@@ -0,0 +1,142 @@
+from PyQt5 import QtWidgets
+from PyQt5.QtCore import QSettings
+
+from flatcamGUI.GUIElements import FCDoubleSpinner
+from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
+
+import gettext
+import FlatCAMTranslation as fcTranslate
+import builtins
+
+fcTranslate.apply_language('strings')
+if '_' not in builtins.__dict__:
+    _ = gettext.gettext
+
+settings = QSettings("Open Source", "FlatCAM")
+if settings.contains("machinist"):
+    machinist_setting = settings.value('machinist', type=int)
+else:
+    machinist_setting = 0
+
+
+class ToolsCalculatorsPrefGroupUI(OptionsGroupUI):
+    def __init__(self, decimals=4, parent=None):
+        # OptionsGroupUI.__init__(self, "Calculators Tool Options", parent=parent)
+        super(ToolsCalculatorsPrefGroupUI, self).__init__(self, parent=parent)
+
+        self.setTitle(str(_("Calculators Tool Options")))
+        self.decimals = decimals
+
+        # ## V-shape Calculator Tool
+        self.vshape_tool_label = QtWidgets.QLabel("<b>%s:</b>" % _("V-Shape Tool Calculator"))
+        self.vshape_tool_label.setToolTip(
+            _("Calculate the tool diameter for a given V-shape tool,\n"
+              "having the tip diameter, tip angle and\n"
+              "depth-of-cut as parameters.")
+        )
+        self.layout.addWidget(self.vshape_tool_label)
+
+        grid0 = QtWidgets.QGridLayout()
+        grid0.setColumnStretch(0, 0)
+        grid0.setColumnStretch(1, 1)
+        self.layout.addLayout(grid0)
+
+        # ## Tip Diameter
+        self.tip_dia_entry = FCDoubleSpinner()
+        self.tip_dia_entry.set_range(0.000001, 9999.9999)
+        self.tip_dia_entry.set_precision(self.decimals)
+        self.tip_dia_entry.setSingleStep(0.1)
+
+        self.tip_dia_label = QtWidgets.QLabel('%s:' % _("Tip Diameter"))
+        self.tip_dia_label.setToolTip(
+            _("This is the tool tip diameter.\n"
+              "It is specified by manufacturer.")
+        )
+        grid0.addWidget(self.tip_dia_label, 0, 0)
+        grid0.addWidget(self.tip_dia_entry, 0, 1)
+
+        # ## Tip angle
+        self.tip_angle_entry = FCDoubleSpinner()
+        self.tip_angle_entry.set_range(0.0, 180.0)
+        self.tip_angle_entry.set_precision(self.decimals)
+        self.tip_angle_entry.setSingleStep(5)
+
+        self.tip_angle_label = QtWidgets.QLabel('%s:' % _("Tip Angle"))
+        self.tip_angle_label.setToolTip(
+            _("This is the angle on the tip of the tool.\n"
+              "It is specified by manufacturer.")
+        )
+        grid0.addWidget(self.tip_angle_label, 1, 0)
+        grid0.addWidget(self.tip_angle_entry, 1, 1)
+
+        # ## Depth-of-cut Cut Z
+        self.cut_z_entry = FCDoubleSpinner()
+        self.cut_z_entry.set_range(-9999.9999, 0.0000)
+        self.cut_z_entry.set_precision(self.decimals)
+        self.cut_z_entry.setSingleStep(0.01)
+
+        self.cut_z_label = QtWidgets.QLabel('%s:' % _("Cut Z"))
+        self.cut_z_label.setToolTip(
+            _("This is depth to cut into material.\n"
+              "In the CNCJob object it is the CutZ parameter.")
+        )
+        grid0.addWidget(self.cut_z_label, 2, 0)
+        grid0.addWidget(self.cut_z_entry, 2, 1)
+
+        # ## Electroplating Calculator Tool
+        self.plate_title_label = QtWidgets.QLabel("<b>%s:</b>" % _("ElectroPlating Calculator"))
+        self.plate_title_label.setToolTip(
+            _("This calculator is useful for those who plate the via/pad/drill holes,\n"
+              "using a method like graphite ink or calcium hypophosphite ink or palladium chloride.")
+        )
+        grid0.addWidget(self.plate_title_label, 3, 0, 1, 2)
+
+        # ## PCB Length
+        self.pcblength_entry = FCDoubleSpinner()
+        self.pcblength_entry.set_range(0.000001, 9999.9999)
+        self.pcblength_entry.set_precision(self.decimals)
+        self.pcblength_entry.setSingleStep(0.1)
+
+        self.pcblengthlabel = QtWidgets.QLabel('%s:' % _("Board Length"))
+
+        self.pcblengthlabel.setToolTip(_('This is the board length. In centimeters.'))
+        grid0.addWidget(self.pcblengthlabel, 4, 0)
+        grid0.addWidget(self.pcblength_entry, 4, 1)
+
+        # ## PCB Width
+        self.pcbwidth_entry = FCDoubleSpinner()
+        self.pcbwidth_entry.set_range(0.000001, 9999.9999)
+        self.pcbwidth_entry.set_precision(self.decimals)
+        self.pcbwidth_entry.setSingleStep(0.1)
+
+        self.pcbwidthlabel = QtWidgets.QLabel('%s:' % _("Board Width"))
+
+        self.pcbwidthlabel.setToolTip(_('This is the board width.In centimeters.'))
+        grid0.addWidget(self.pcbwidthlabel, 5, 0)
+        grid0.addWidget(self.pcbwidth_entry, 5, 1)
+
+        # ## Current Density
+        self.cdensity_label = QtWidgets.QLabel('%s:' % _("Current Density"))
+        self.cdensity_entry = FCDoubleSpinner()
+        self.cdensity_entry.set_range(0.000001, 9999.9999)
+        self.cdensity_entry.set_precision(self.decimals)
+        self.cdensity_entry.setSingleStep(0.1)
+
+        self.cdensity_label.setToolTip(_("Current density to pass through the board. \n"
+                                         "In Amps per Square Feet ASF."))
+        grid0.addWidget(self.cdensity_label, 6, 0)
+        grid0.addWidget(self.cdensity_entry, 6, 1)
+
+        # ## PCB Copper Growth
+        self.growth_label = QtWidgets.QLabel('%s:' % _("Copper Growth"))
+        self.growth_entry = FCDoubleSpinner()
+        self.growth_entry.set_range(0.000001, 9999.9999)
+        self.growth_entry.set_precision(self.decimals)
+        self.growth_entry.setSingleStep(0.01)
+
+        self.growth_label.setToolTip(_("How thick the copper growth is intended to be.\n"
+                                       "In microns."))
+        grid0.addWidget(self.growth_label, 7, 0)
+        grid0.addWidget(self.growth_entry, 7, 1)
+
+        self.layout.addStretch()

+ 177 - 0
flatcamGUI/preferences/tools/ToolsCutoutPrefGroupUI.py

@@ -0,0 +1,177 @@
+from PyQt5 import QtWidgets
+from PyQt5.QtCore import QSettings
+
+from flatcamGUI.GUIElements import FCDoubleSpinner, FCCheckBox, RadioSet, FCComboBox
+from flatcamGUI.preferences import machinist_setting
+from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
+
+import gettext
+import FlatCAMTranslation as fcTranslate
+import builtins
+
+fcTranslate.apply_language('strings')
+if '_' not in builtins.__dict__:
+    _ = gettext.gettext
+
+settings = QSettings("Open Source", "FlatCAM")
+if settings.contains("machinist"):
+    machinist_setting = settings.value('machinist', type=int)
+else:
+    machinist_setting = 0
+
+
+class ToolsCutoutPrefGroupUI(OptionsGroupUI):
+    def __init__(self, decimals=4, parent=None):
+        # OptionsGroupUI.__init__(self, "Cutout Tool Options", parent=parent)
+        super(ToolsCutoutPrefGroupUI, self).__init__(self, parent=parent)
+
+        self.setTitle(str(_("Cutout Tool Options")))
+        self.decimals = decimals
+
+        # ## Board cutout
+        self.board_cutout_label = QtWidgets.QLabel("<b>%s:</b>" % _("Parameters"))
+        self.board_cutout_label.setToolTip(
+            _("Create toolpaths to cut around\n"
+              "the PCB and separate it from\n"
+              "the original board.")
+        )
+        self.layout.addWidget(self.board_cutout_label)
+
+        grid0 = QtWidgets.QGridLayout()
+        self.layout.addLayout(grid0)
+
+        tdclabel = QtWidgets.QLabel('%s:' % _('Tool Diameter'))
+        tdclabel.setToolTip(
+            _("Diameter of the tool used to cutout\n"
+              "the PCB shape out of the surrounding material.")
+        )
+
+        self.cutout_tooldia_entry = FCDoubleSpinner()
+        self.cutout_tooldia_entry.set_range(0.000001, 9999.9999)
+        self.cutout_tooldia_entry.set_precision(self.decimals)
+        self.cutout_tooldia_entry.setSingleStep(0.1)
+
+        grid0.addWidget(tdclabel, 0, 0)
+        grid0.addWidget(self.cutout_tooldia_entry, 0, 1)
+
+        # Cut Z
+        cutzlabel = QtWidgets.QLabel('%s:' % _('Cut Z'))
+        cutzlabel.setToolTip(
+            _(
+                "Cutting depth (negative)\n"
+                "below the copper surface."
+            )
+        )
+        self.cutz_entry = FCDoubleSpinner()
+        self.cutz_entry.set_precision(self.decimals)
+
+        if machinist_setting == 0:
+            self.cutz_entry.setRange(-9999.9999, 0.0000)
+        else:
+            self.cutz_entry.setRange(-9999.9999, 9999.9999)
+
+        self.cutz_entry.setSingleStep(0.1)
+
+        grid0.addWidget(cutzlabel, 1, 0)
+        grid0.addWidget(self.cutz_entry, 1, 1)
+
+        # Multi-pass
+        self.mpass_cb = FCCheckBox('%s:' % _("Multi-Depth"))
+        self.mpass_cb.setToolTip(
+            _(
+                "Use multiple passes to limit\n"
+                "the cut depth in each pass. Will\n"
+                "cut multiple times until Cut Z is\n"
+                "reached."
+            )
+        )
+
+        self.maxdepth_entry = FCDoubleSpinner()
+        self.maxdepth_entry.set_precision(self.decimals)
+        self.maxdepth_entry.setRange(0, 9999.9999)
+        self.maxdepth_entry.setSingleStep(0.1)
+
+        self.maxdepth_entry.setToolTip(_("Depth of each pass (positive)."))
+
+        grid0.addWidget(self.mpass_cb, 2, 0)
+        grid0.addWidget(self.maxdepth_entry, 2, 1)
+
+        # Object kind
+        kindlabel = QtWidgets.QLabel('%s:' % _('Object kind'))
+        kindlabel.setToolTip(
+            _("Choice of what kind the object we want to cutout is.<BR>"
+              "- <B>Single</B>: contain a single PCB Gerber outline object.<BR>"
+              "- <B>Panel</B>: a panel PCB Gerber object, which is made\n"
+              "out of many individual PCB outlines.")
+        )
+
+        self.obj_kind_combo = RadioSet([
+            {"label": _("Single"), "value": "single"},
+            {"label": _("Panel"), "value": "panel"},
+        ])
+        grid0.addWidget(kindlabel, 3, 0)
+        grid0.addWidget(self.obj_kind_combo, 3, 1)
+
+        marginlabel = QtWidgets.QLabel('%s:' % _('Margin'))
+        marginlabel.setToolTip(
+            _("Margin over bounds. A positive value here\n"
+              "will make the cutout of the PCB further from\n"
+              "the actual PCB border")
+        )
+
+        self.cutout_margin_entry = FCDoubleSpinner()
+        self.cutout_margin_entry.set_range(-9999.9999, 9999.9999)
+        self.cutout_margin_entry.set_precision(self.decimals)
+        self.cutout_margin_entry.setSingleStep(0.1)
+
+        grid0.addWidget(marginlabel, 4, 0)
+        grid0.addWidget(self.cutout_margin_entry, 4, 1)
+
+        gaplabel = QtWidgets.QLabel('%s:' % _('Gap size'))
+        gaplabel.setToolTip(
+            _("The size of the bridge gaps in the cutout\n"
+              "used to keep the board connected to\n"
+              "the surrounding material (the one \n"
+              "from which the PCB is cutout).")
+        )
+
+        self.cutout_gap_entry = FCDoubleSpinner()
+        self.cutout_gap_entry.set_range(0.000001, 9999.9999)
+        self.cutout_gap_entry.set_precision(self.decimals)
+        self.cutout_gap_entry.setSingleStep(0.1)
+
+        grid0.addWidget(gaplabel, 5, 0)
+        grid0.addWidget(self.cutout_gap_entry, 5, 1)
+
+        gaps_label = QtWidgets.QLabel('%s:' % _('Gaps'))
+        gaps_label.setToolTip(
+            _("Number of gaps used for the cutout.\n"
+              "There can be maximum 8 bridges/gaps.\n"
+              "The choices are:\n"
+              "- None  - no gaps\n"
+              "- lr    - left + right\n"
+              "- tb    - top + bottom\n"
+              "- 4     - left + right +top + bottom\n"
+              "- 2lr   - 2*left + 2*right\n"
+              "- 2tb  - 2*top + 2*bottom\n"
+              "- 8     - 2*left + 2*right +2*top + 2*bottom")
+        )
+
+        self.gaps_combo = FCComboBox()
+        grid0.addWidget(gaps_label, 6, 0)
+        grid0.addWidget(self.gaps_combo, 6, 1)
+
+        gaps_items = ['None', 'LR', 'TB', '4', '2LR', '2TB', '8']
+        for it in gaps_items:
+            self.gaps_combo.addItem(it)
+            self.gaps_combo.setStyleSheet('background-color: rgb(255,255,255)')
+
+        # Surrounding convex box shape
+        self.convex_box = FCCheckBox('%s' % _("Convex Shape"))
+        self.convex_box.setToolTip(
+            _("Create a convex shape surrounding the entire PCB.\n"
+              "Used only if the source object type is Gerber.")
+        )
+        grid0.addWidget(self.convex_box, 7, 0, 1, 2)
+
+        self.layout.addStretch()

+ 316 - 0
flatcamGUI/preferences/tools/ToolsFilmPrefGroupUI.py

@@ -0,0 +1,316 @@
+from PyQt5 import QtWidgets, QtCore
+from PyQt5.QtCore import Qt, QSettings
+
+from flatcamGUI.GUIElements import RadioSet, FCEntry, FCDoubleSpinner, FCCheckBox, FCComboBox
+from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
+
+import gettext
+import FlatCAMTranslation as fcTranslate
+import builtins
+
+fcTranslate.apply_language('strings')
+if '_' not in builtins.__dict__:
+    _ = gettext.gettext
+
+settings = QSettings("Open Source", "FlatCAM")
+if settings.contains("machinist"):
+    machinist_setting = settings.value('machinist', type=int)
+else:
+    machinist_setting = 0
+
+
+class ToolsFilmPrefGroupUI(OptionsGroupUI):
+    def __init__(self, decimals=4, parent=None):
+        # OptionsGroupUI.__init__(self, "Cutout Tool Options", parent=parent)
+        super(ToolsFilmPrefGroupUI, self).__init__(self, parent=parent)
+
+        self.setTitle(str(_("Film Tool Options")))
+        self.decimals = decimals
+
+        # ## Parameters
+        self.film_label = QtWidgets.QLabel("<b>%s:</b>" % _("Parameters"))
+        self.film_label.setToolTip(
+            _("Create a PCB film from a Gerber or Geometry\n"
+              "FlatCAM object.\n"
+              "The file is saved in SVG format.")
+        )
+        self.layout.addWidget(self.film_label)
+
+        grid0 = QtWidgets.QGridLayout()
+        self.layout.addLayout(grid0)
+
+        self.film_type_radio = RadioSet([{'label': 'Pos', 'value': 'pos'},
+                                         {'label': 'Neg', 'value': 'neg'}])
+        ftypelbl = QtWidgets.QLabel('%s:' % _('Film Type'))
+        ftypelbl.setToolTip(
+            _("Generate a Positive black film or a Negative film.\n"
+              "Positive means that it will print the features\n"
+              "with black on a white canvas.\n"
+              "Negative means that it will print the features\n"
+              "with white on a black canvas.\n"
+              "The Film format is SVG.")
+        )
+        grid0.addWidget(ftypelbl, 0, 0)
+        grid0.addWidget(self.film_type_radio, 0, 1)
+
+        # Film Color
+        self.film_color_label = QtWidgets.QLabel('%s:' % _('Film Color'))
+        self.film_color_label.setToolTip(
+            _("Set the film color when positive film is selected.")
+        )
+        self.film_color_entry = FCEntry()
+        self.film_color_button = QtWidgets.QPushButton()
+        self.film_color_button.setFixedSize(15, 15)
+
+        self.form_box_child = QtWidgets.QHBoxLayout()
+        self.form_box_child.setContentsMargins(0, 0, 0, 0)
+        self.form_box_child.addWidget(self.film_color_entry)
+        self.form_box_child.addWidget(self.film_color_button, alignment=Qt.AlignRight)
+        self.form_box_child.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
+
+        film_color_widget = QtWidgets.QWidget()
+        film_color_widget.setLayout(self.form_box_child)
+        grid0.addWidget(self.film_color_label, 1, 0)
+        grid0.addWidget(film_color_widget, 1, 1)
+
+        # Film Border
+        self.film_boundary_entry = FCDoubleSpinner()
+        self.film_boundary_entry.set_precision(self.decimals)
+        self.film_boundary_entry.set_range(0, 9999.9999)
+        self.film_boundary_entry.setSingleStep(0.1)
+
+        self.film_boundary_label = QtWidgets.QLabel('%s:' % _("Border"))
+        self.film_boundary_label.setToolTip(
+            _("Specify a border around the object.\n"
+              "Only for negative film.\n"
+              "It helps if we use as a Box Object the same \n"
+              "object as in Film Object. It will create a thick\n"
+              "black bar around the actual print allowing for a\n"
+              "better delimitation of the outline features which are of\n"
+              "white color like the rest and which may confound with the\n"
+              "surroundings if not for this border.")
+        )
+        grid0.addWidget(self.film_boundary_label, 2, 0)
+        grid0.addWidget(self.film_boundary_entry, 2, 1)
+
+        self.film_scale_stroke_entry = FCDoubleSpinner()
+        self.film_scale_stroke_entry.set_precision(self.decimals)
+        self.film_scale_stroke_entry.set_range(0, 9999.9999)
+        self.film_scale_stroke_entry.setSingleStep(0.1)
+
+        self.film_scale_stroke_label = QtWidgets.QLabel('%s:' % _("Scale Stroke"))
+        self.film_scale_stroke_label.setToolTip(
+            _("Scale the line stroke thickness of each feature in the SVG file.\n"
+              "It means that the line that envelope each SVG feature will be thicker or thinner,\n"
+              "therefore the fine features may be more affected by this parameter.")
+        )
+        grid0.addWidget(self.film_scale_stroke_label, 3, 0)
+        grid0.addWidget(self.film_scale_stroke_entry, 3, 1)
+
+        self.film_adj_label = QtWidgets.QLabel('<b>%s</b>' % _("Film Adjustments"))
+        self.film_adj_label.setToolTip(
+            _("Sometime the printers will distort the print shape, especially the Laser types.\n"
+              "This section provide the tools to compensate for the print distortions.")
+        )
+
+        grid0.addWidget(self.film_adj_label, 4, 0, 1, 2)
+
+        # Scale Geometry
+        self.film_scale_cb = FCCheckBox('%s' % _("Scale Film geometry"))
+        self.film_scale_cb.setToolTip(
+            _("A value greater than 1 will stretch the film\n"
+              "while a value less than 1 will jolt it.")
+        )
+        self.film_scale_cb.setStyleSheet(
+            """
+            QCheckBox {font-weight: bold; color: black}
+            """
+        )
+        grid0.addWidget(self.film_scale_cb, 5, 0, 1, 2)
+
+        self.film_scalex_label = QtWidgets.QLabel('%s:' % _("X factor"))
+        self.film_scalex_entry = FCDoubleSpinner()
+        self.film_scalex_entry.set_range(-999.9999, 999.9999)
+        self.film_scalex_entry.set_precision(self.decimals)
+        self.film_scalex_entry.setSingleStep(0.01)
+
+        grid0.addWidget(self.film_scalex_label, 6, 0)
+        grid0.addWidget(self.film_scalex_entry, 6, 1)
+
+        self.film_scaley_label = QtWidgets.QLabel('%s:' % _("Y factor"))
+        self.film_scaley_entry = FCDoubleSpinner()
+        self.film_scaley_entry.set_range(-999.9999, 999.9999)
+        self.film_scaley_entry.set_precision(self.decimals)
+        self.film_scaley_entry.setSingleStep(0.01)
+
+        grid0.addWidget(self.film_scaley_label, 7, 0)
+        grid0.addWidget(self.film_scaley_entry, 7, 1)
+
+        # Skew Geometry
+        self.film_skew_cb = FCCheckBox('%s' % _("Skew Film geometry"))
+        self.film_skew_cb.setToolTip(
+            _("Positive values will skew to the right\n"
+              "while negative values will skew to the left.")
+        )
+        self.film_skew_cb.setStyleSheet(
+            """
+            QCheckBox {font-weight: bold; color: black}
+            """
+        )
+        grid0.addWidget(self.film_skew_cb, 8, 0, 1, 2)
+
+        self.film_skewx_label = QtWidgets.QLabel('%s:' % _("X angle"))
+        self.film_skewx_entry = FCDoubleSpinner()
+        self.film_skewx_entry.set_range(-999.9999, 999.9999)
+        self.film_skewx_entry.set_precision(self.decimals)
+        self.film_skewx_entry.setSingleStep(0.01)
+
+        grid0.addWidget(self.film_skewx_label, 9, 0)
+        grid0.addWidget(self.film_skewx_entry, 9, 1)
+
+        self.film_skewy_label = QtWidgets.QLabel('%s:' % _("Y angle"))
+        self.film_skewy_entry = FCDoubleSpinner()
+        self.film_skewy_entry.set_range(-999.9999, 999.9999)
+        self.film_skewy_entry.set_precision(self.decimals)
+        self.film_skewy_entry.setSingleStep(0.01)
+
+        grid0.addWidget(self.film_skewy_label, 10, 0)
+        grid0.addWidget(self.film_skewy_entry, 10, 1)
+
+        self.film_skew_ref_label = QtWidgets.QLabel('%s:' % _("Reference"))
+        self.film_skew_ref_label.setToolTip(
+            _("The reference point to be used as origin for the skew.\n"
+              "It can be one of the four points of the geometry bounding box.")
+        )
+        self.film_skew_reference = RadioSet([{'label': _('Bottom Left'), 'value': 'bottomleft'},
+                                             {'label': _('Top Left'), 'value': 'topleft'},
+                                             {'label': _('Bottom Right'), 'value': 'bottomright'},
+                                             {'label': _('Top right'), 'value': 'topright'}],
+                                            orientation='vertical',
+                                            stretch=False)
+
+        grid0.addWidget(self.film_skew_ref_label, 11, 0)
+        grid0.addWidget(self.film_skew_reference, 11, 1)
+
+        # Mirror Geometry
+        self.film_mirror_cb = FCCheckBox('%s' % _("Mirror Film geometry"))
+        self.film_mirror_cb.setToolTip(
+            _("Mirror the film geometry on the selected axis or on both.")
+        )
+        self.film_mirror_cb.setStyleSheet(
+            """
+            QCheckBox {font-weight: bold; color: black}
+            """
+        )
+        grid0.addWidget(self.film_mirror_cb, 12, 0, 1, 2)
+
+        self.film_mirror_axis = RadioSet([{'label': _('None'), 'value': 'none'},
+                                          {'label': _('X'), 'value': 'x'},
+                                          {'label': _('Y'), 'value': 'y'},
+                                          {'label': _('Both'), 'value': 'both'}],
+                                         stretch=False)
+        self.film_mirror_axis_label = QtWidgets.QLabel('%s:' % _("Mirror axis"))
+
+        grid0.addWidget(self.film_mirror_axis_label, 13, 0)
+        grid0.addWidget(self.film_mirror_axis, 13, 1)
+
+        separator_line3 = QtWidgets.QFrame()
+        separator_line3.setFrameShape(QtWidgets.QFrame.HLine)
+        separator_line3.setFrameShadow(QtWidgets.QFrame.Sunken)
+        grid0.addWidget(separator_line3, 14, 0, 1, 2)
+
+        self.file_type_radio = RadioSet([{'label': _('SVG'), 'value': 'svg'},
+                                         {'label': _('PNG'), 'value': 'png'},
+                                         {'label': _('PDF'), 'value': 'pdf'}
+                                         ], stretch=False)
+
+        self.file_type_label = QtWidgets.QLabel(_("Film Type:"))
+        self.file_type_label.setToolTip(
+            _("The file type of the saved film. Can be:\n"
+              "- 'SVG' -> open-source vectorial format\n"
+              "- 'PNG' -> raster image\n"
+              "- 'PDF' -> portable document format")
+        )
+        grid0.addWidget(self.file_type_label, 15, 0)
+        grid0.addWidget(self.file_type_radio, 15, 1)
+
+        # Page orientation
+        self.orientation_label = QtWidgets.QLabel('%s:' % _("Page Orientation"))
+        self.orientation_label.setToolTip(_("Can be:\n"
+                                            "- Portrait\n"
+                                            "- Landscape"))
+
+        self.orientation_radio = RadioSet([{'label': _('Portrait'), 'value': 'p'},
+                                           {'label': _('Landscape'), 'value': 'l'},
+                                           ], stretch=False)
+
+        grid0.addWidget(self.orientation_label, 16, 0)
+        grid0.addWidget(self.orientation_radio, 16, 1)
+
+        # Page Size
+        self.pagesize_label = QtWidgets.QLabel('%s:' % _("Page Size"))
+        self.pagesize_label.setToolTip(_("A selection of standard ISO 216 page sizes."))
+
+        self.pagesize_combo = FCComboBox()
+
+        self.pagesize = {}
+        self.pagesize.update(
+            {
+                'Bounds': None,
+                'A0': (841, 1189),
+                'A1': (594, 841),
+                'A2': (420, 594),
+                'A3': (297, 420),
+                'A4': (210, 297),
+                'A5': (148, 210),
+                'A6': (105, 148),
+                'A7': (74, 105),
+                'A8': (52, 74),
+                'A9': (37, 52),
+                'A10': (26, 37),
+
+                'B0': (1000, 1414),
+                'B1': (707, 1000),
+                'B2': (500, 707),
+                'B3': (353, 500),
+                'B4': (250, 353),
+                'B5': (176, 250),
+                'B6': (125, 176),
+                'B7': (88, 125),
+                'B8': (62, 88),
+                'B9': (44, 62),
+                'B10': (31, 44),
+
+                'C0': (917, 1297),
+                'C1': (648, 917),
+                'C2': (458, 648),
+                'C3': (324, 458),
+                'C4': (229, 324),
+                'C5': (162, 229),
+                'C6': (114, 162),
+                'C7': (81, 114),
+                'C8': (57, 81),
+                'C9': (40, 57),
+                'C10': (28, 40),
+
+                # American paper sizes
+                'LETTER': (8.5, 11),
+                'LEGAL': (8.5, 14),
+                'ELEVENSEVENTEEN': (11, 17),
+
+                # From https://en.wikipedia.org/wiki/Paper_size
+                'JUNIOR_LEGAL': (5, 8),
+                'HALF_LETTER': (5.5, 8),
+                'GOV_LETTER': (8, 10.5),
+                'GOV_LEGAL': (8.5, 13),
+                'LEDGER': (17, 11),
+            }
+        )
+
+        page_size_list = list(self.pagesize.keys())
+        self.pagesize_combo.addItems(page_size_list)
+
+        grid0.addWidget(self.pagesize_label, 17, 0)
+        grid0.addWidget(self.pagesize_combo, 17, 1)
+
+        self.layout.addStretch()

+ 349 - 0
flatcamGUI/preferences/tools/ToolsNCCPrefGroupUI.py

@@ -0,0 +1,349 @@
+from PyQt5 import QtWidgets
+from PyQt5.QtCore import QSettings
+
+from flatcamGUI.GUIElements import FCEntry, RadioSet, FCDoubleSpinner, FCComboBox, FCCheckBox
+from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
+
+import gettext
+import FlatCAMTranslation as fcTranslate
+import builtins
+
+fcTranslate.apply_language('strings')
+if '_' not in builtins.__dict__:
+    _ = gettext.gettext
+
+settings = QSettings("Open Source", "FlatCAM")
+if settings.contains("machinist"):
+    machinist_setting = settings.value('machinist', type=int)
+else:
+    machinist_setting = 0
+
+
+class ToolsNCCPrefGroupUI(OptionsGroupUI):
+    def __init__(self, decimals=4, parent=None):
+        # OptionsGroupUI.__init__(self, "NCC Tool Options", parent=parent)
+        super(ToolsNCCPrefGroupUI, self).__init__(self, parent=parent)
+
+        self.setTitle(str(_("NCC Tool Options")))
+        self.decimals = decimals
+
+        # ## Clear non-copper regions
+        self.clearcopper_label = QtWidgets.QLabel("<b>%s:</b>" % _("Parameters"))
+        self.clearcopper_label.setToolTip(
+            _("Create a Geometry object with\n"
+              "toolpaths to cut all non-copper regions.")
+        )
+        self.layout.addWidget(self.clearcopper_label)
+
+        grid0 = QtWidgets.QGridLayout()
+        self.layout.addLayout(grid0)
+
+        ncctdlabel = QtWidgets.QLabel('<b><font color="green">%s:</font></b>' % _('Tools Dia'))
+        ncctdlabel.setToolTip(
+            _("Diameters of the tools, separated by comma.\n"
+              "The value of the diameter has to use the dot decimals separator.\n"
+              "Valid values: 0.3, 1.0")
+        )
+        grid0.addWidget(ncctdlabel, 0, 0)
+        self.ncc_tool_dia_entry = FCEntry(border_color='#0069A9')
+        self.ncc_tool_dia_entry.setPlaceholderText(_("Comma separated values"))
+        grid0.addWidget(self.ncc_tool_dia_entry, 0, 1)
+
+        # Tool Type Radio Button
+        self.tool_type_label = QtWidgets.QLabel('%s:' % _('Tool Type'))
+        self.tool_type_label.setToolTip(
+            _("Default tool type:\n"
+              "- 'V-shape'\n"
+              "- Circular")
+        )
+
+        self.tool_type_radio = RadioSet([{'label': _('V-shape'), 'value': 'V'},
+                                         {'label': _('Circular'), 'value': 'C1'}])
+        self.tool_type_radio.setToolTip(
+            _("Default tool type:\n"
+              "- 'V-shape'\n"
+              "- Circular")
+        )
+
+        grid0.addWidget(self.tool_type_label, 1, 0)
+        grid0.addWidget(self.tool_type_radio, 1, 1)
+
+        # Tip Dia
+        self.tipdialabel = QtWidgets.QLabel('%s:' % _('V-Tip Dia'))
+        self.tipdialabel.setToolTip(
+            _("The tip diameter for V-Shape Tool"))
+        self.tipdia_entry = FCDoubleSpinner()
+        self.tipdia_entry.set_precision(self.decimals)
+        self.tipdia_entry.set_range(0, 1000)
+        self.tipdia_entry.setSingleStep(0.1)
+
+        grid0.addWidget(self.tipdialabel, 2, 0)
+        grid0.addWidget(self.tipdia_entry, 2, 1)
+
+        # Tip Angle
+        self.tipanglelabel = QtWidgets.QLabel('%s:' % _('V-Tip Angle'))
+        self.tipanglelabel.setToolTip(
+            _("The tip angle for V-Shape Tool.\n"
+              "In degree."))
+        self.tipangle_entry = FCDoubleSpinner()
+        self.tipangle_entry.set_precision(self.decimals)
+        self.tipangle_entry.set_range(1, 180)
+        self.tipangle_entry.setSingleStep(5)
+        self.tipangle_entry.setWrapping(True)
+
+        grid0.addWidget(self.tipanglelabel, 3, 0)
+        grid0.addWidget(self.tipangle_entry, 3, 1)
+
+        # Cut Z entry
+        cutzlabel = QtWidgets.QLabel('%s:' % _('Cut Z'))
+        cutzlabel.setToolTip(
+           _("Depth of cut into material. Negative value.\n"
+             "In FlatCAM units.")
+        )
+        self.cutz_entry = FCDoubleSpinner()
+        self.cutz_entry.set_precision(self.decimals)
+        self.cutz_entry.set_range(-9999.9999, 0.0000)
+        self.cutz_entry.setSingleStep(0.1)
+
+        self.cutz_entry.setToolTip(
+           _("Depth of cut into material. Negative value.\n"
+             "In FlatCAM units.")
+        )
+
+        grid0.addWidget(cutzlabel, 4, 0)
+        grid0.addWidget(self.cutz_entry, 4, 1)
+
+        # New Diameter
+        self.newdialabel = QtWidgets.QLabel('%s:' % _('New Dia'))
+        self.newdialabel.setToolTip(
+            _("Diameter for the new tool to add in the Tool Table.\n"
+              "If the tool is V-shape type then this value is automatically\n"
+              "calculated from the other parameters.")
+        )
+        self.newdia_entry = FCDoubleSpinner()
+        self.newdia_entry.set_precision(self.decimals)
+        self.newdia_entry.set_range(0.0001, 9999.9999)
+        self.newdia_entry.setSingleStep(0.1)
+
+        grid0.addWidget(self.newdialabel, 5, 0)
+        grid0.addWidget(self.newdia_entry, 5, 1)
+
+        separator_line = QtWidgets.QFrame()
+        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
+        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
+        grid0.addWidget(separator_line, 6, 0, 1, 2)
+
+        # Milling Type Radio Button
+        self.milling_type_label = QtWidgets.QLabel('%s:' % _('Milling Type'))
+        self.milling_type_label.setToolTip(
+            _("Milling type when the selected tool is of type: 'iso_op':\n"
+              "- climb / best for precision milling and to reduce tool usage\n"
+              "- conventional / useful when there is no backlash compensation")
+        )
+
+        self.milling_type_radio = RadioSet([{'label': _('Climb'), 'value': 'cl'},
+                                            {'label': _('Conventional'), 'value': 'cv'}])
+        self.milling_type_radio.setToolTip(
+            _("Milling type when the selected tool is of type: 'iso_op':\n"
+              "- climb / best for precision milling and to reduce tool usage\n"
+              "- conventional / useful when there is no backlash compensation")
+        )
+
+        grid0.addWidget(self.milling_type_label, 7, 0)
+        grid0.addWidget(self.milling_type_radio, 7, 1)
+
+        # Tool order Radio Button
+        self.ncc_order_label = QtWidgets.QLabel('%s:' % _('Tool order'))
+        self.ncc_order_label.setToolTip(_("This set the way that the tools in the tools table are used.\n"
+                                          "'No' --> means that the used order is the one in the tool table\n"
+                                          "'Forward' --> means that the tools will be ordered from small to big\n"
+                                          "'Reverse' --> means that the tools will ordered from big to small\n\n"
+                                          "WARNING: using rest machining will automatically set the order\n"
+                                          "in reverse and disable this control."))
+
+        self.ncc_order_radio = RadioSet([{'label': _('No'), 'value': 'no'},
+                                         {'label': _('Forward'), 'value': 'fwd'},
+                                         {'label': _('Reverse'), 'value': 'rev'}])
+        self.ncc_order_radio.setToolTip(_("This set the way that the tools in the tools table are used.\n"
+                                          "'No' --> means that the used order is the one in the tool table\n"
+                                          "'Forward' --> means that the tools will be ordered from small to big\n"
+                                          "'Reverse' --> means that the tools will ordered from big to small\n\n"
+                                          "WARNING: using rest machining will automatically set the order\n"
+                                          "in reverse and disable this control."))
+        grid0.addWidget(self.ncc_order_label, 8, 0)
+        grid0.addWidget(self.ncc_order_radio, 8, 1)
+
+        separator_line = QtWidgets.QFrame()
+        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
+        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
+        grid0.addWidget(separator_line, 9, 0, 1, 2)
+
+        # Overlap Entry
+        nccoverlabel = QtWidgets.QLabel('%s:' % _('Overlap'))
+        nccoverlabel.setToolTip(
+           _("How much (percentage) of the tool width to overlap each tool pass.\n"
+             "Adjust the value starting with lower values\n"
+             "and increasing it if areas that should be cleared are still \n"
+             "not cleared.\n"
+             "Lower values = faster processing, faster execution on CNC.\n"
+             "Higher values = slow processing and slow execution on CNC\n"
+             "due of too many paths.")
+        )
+        self.ncc_overlap_entry = FCDoubleSpinner(suffix='%')
+        self.ncc_overlap_entry.set_precision(self.decimals)
+        self.ncc_overlap_entry.setWrapping(True)
+        self.ncc_overlap_entry.setRange(0.0000, 99.9999)
+        self.ncc_overlap_entry.setSingleStep(0.1)
+
+        grid0.addWidget(nccoverlabel, 10, 0)
+        grid0.addWidget(self.ncc_overlap_entry, 10, 1)
+
+        # Margin entry
+        nccmarginlabel = QtWidgets.QLabel('%s:' % _('Margin'))
+        nccmarginlabel.setToolTip(
+            _("Bounding box margin.")
+        )
+        self.ncc_margin_entry = FCDoubleSpinner()
+        self.ncc_margin_entry.set_precision(self.decimals)
+        self.ncc_margin_entry.set_range(-10000, 10000)
+        self.ncc_margin_entry.setSingleStep(0.1)
+
+        grid0.addWidget(nccmarginlabel, 11, 0)
+        grid0.addWidget(self.ncc_margin_entry, 11, 1)
+
+        # Method
+        methodlabel = QtWidgets.QLabel('%s:' % _('Method'))
+        methodlabel.setToolTip(
+            _("Algorithm for copper clearing:\n"
+              "- Standard: Fixed step inwards.\n"
+              "- Seed-based: Outwards from seed.\n"
+              "- Line-based: Parallel lines.")
+        )
+
+        # self.ncc_method_radio = RadioSet([
+        #     {"label": _("Standard"), "value": "standard"},
+        #     {"label": _("Seed-based"), "value": "seed"},
+        #     {"label": _("Straight lines"), "value": "lines"}
+        # ], orientation='vertical', stretch=False)
+        self.ncc_method_combo = FCComboBox()
+        self.ncc_method_combo.addItems(
+            [_("Standard"), _("Seed"), _("Lines")]
+        )
+
+        grid0.addWidget(methodlabel, 12, 0)
+        grid0.addWidget(self.ncc_method_combo, 12, 1)
+
+        # Connect lines
+        self.ncc_connect_cb = FCCheckBox('%s' % _("Connect"))
+        self.ncc_connect_cb.setToolTip(
+            _("Draw lines between resulting\n"
+              "segments to minimize tool lifts.")
+        )
+
+        grid0.addWidget(self.ncc_connect_cb, 13, 0)
+
+        # Contour Checkbox
+        self.ncc_contour_cb = FCCheckBox('%s' % _("Contour"))
+        self.ncc_contour_cb.setToolTip(
+           _("Cut around the perimeter of the polygon\n"
+             "to trim rough edges.")
+        )
+
+        grid0.addWidget(self.ncc_contour_cb, 13, 1)
+
+        # ## NCC Offset choice
+        self.ncc_choice_offset_cb = FCCheckBox('%s' % _("Offset"))
+        self.ncc_choice_offset_cb.setToolTip(
+            _("If used, it will add an offset to the copper features.\n"
+              "The copper clearing will finish to a distance\n"
+              "from the copper features.\n"
+              "The value can be between 0 and 10 FlatCAM units.")
+        )
+
+        grid0.addWidget(self.ncc_choice_offset_cb, 14, 0, 1, 2)
+
+        # ## NCC Offset value
+        self.ncc_offset_label = QtWidgets.QLabel('%s:' % _("Offset value"))
+        self.ncc_offset_label.setToolTip(
+            _("If used, it will add an offset to the copper features.\n"
+              "The copper clearing will finish to a distance\n"
+              "from the copper features.\n"
+              "The value can be between 0.0 and 9999.9 FlatCAM units.")
+        )
+        self.ncc_offset_spinner = FCDoubleSpinner()
+        self.ncc_offset_spinner.set_range(0.00, 9999.9999)
+        self.ncc_offset_spinner.set_precision(self.decimals)
+        self.ncc_offset_spinner.setWrapping(True)
+        self.ncc_offset_spinner.setSingleStep(0.1)
+
+        grid0.addWidget(self.ncc_offset_label, 15, 0)
+        grid0.addWidget(self.ncc_offset_spinner, 15, 1)
+
+        separator_line = QtWidgets.QFrame()
+        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
+        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
+        grid0.addWidget(separator_line, 16, 0, 1, 2)
+
+        # Rest machining CheckBox
+        self.ncc_rest_cb = FCCheckBox('%s' % _("Rest Machining"))
+        self.ncc_rest_cb.setToolTip(
+            _("If checked, use 'rest machining'.\n"
+              "Basically it will clear copper outside PCB features,\n"
+              "using the biggest tool and continue with the next tools,\n"
+              "from bigger to smaller, to clear areas of copper that\n"
+              "could not be cleared by previous tool, until there is\n"
+              "no more copper to clear or there are no more tools.\n"
+              "If not checked, use the standard algorithm.")
+        )
+
+        grid0.addWidget(self.ncc_rest_cb, 17, 0, 1, 2)
+
+        # ## Reference
+        # self.reference_radio = RadioSet([{'label': _('Itself'), 'value': 'itself'},
+        #                                  {"label": _("Area Selection"), "value": "area"},
+        #                                  {'label': _('Reference Object'), 'value': 'box'}],
+        #                                 orientation='vertical',
+        #                                 stretch=None)
+        self.select_combo = FCComboBox()
+        self.select_combo.addItems(
+            [_("Itself"), _("Area Selection"), _("Reference Object")]
+        )
+        select_label = QtWidgets.QLabel('%s:' % _("Selection"))
+        select_label.setToolTip(
+            _("Selection of area to be processed.\n"
+              "- 'Itself' - the processing extent is based on the object that is processed.\n "
+              "- 'Area Selection' - left mouse click to start selection of the area to be processed.\n"
+              "- 'Reference Object' - will process the area specified by another object.")
+        )
+
+        grid0.addWidget(select_label, 18, 0)
+        grid0.addWidget(self.select_combo, 18, 1)
+
+        self.area_shape_label = QtWidgets.QLabel('%s:' % _("Shape"))
+        self.area_shape_label.setToolTip(
+            _("The kind of selection shape used for area selection.")
+        )
+
+        self.area_shape_radio = RadioSet([{'label': _("Square"), 'value': 'square'},
+                                          {'label': _("Polygon"), 'value': 'polygon'}])
+
+        grid0.addWidget(self.area_shape_label, 19, 0)
+        grid0.addWidget(self.area_shape_radio, 19, 1)
+
+        separator_line = QtWidgets.QFrame()
+        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
+        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
+        grid0.addWidget(separator_line, 20, 0, 1, 2)
+
+        # ## Plotting type
+        self.ncc_plotting_radio = RadioSet([{'label': _('Normal'), 'value': 'normal'},
+                                            {"label": _("Progressive"), "value": "progressive"}])
+        plotting_label = QtWidgets.QLabel('%s:' % _("NCC Plotting"))
+        plotting_label.setToolTip(
+            _("- 'Normal' -  normal plotting, done at the end of the NCC job\n"
+              "- 'Progressive' - after each shape is generated it will be plotted.")
+        )
+        grid0.addWidget(plotting_label, 21, 0)
+        grid0.addWidget(self.ncc_plotting_radio, 21, 1)
+
+        self.layout.addStretch()

+ 313 - 0
flatcamGUI/preferences/tools/ToolsPaintPrefGroupUI.py

@@ -0,0 +1,313 @@
+from PyQt5 import QtWidgets
+from PyQt5.QtCore import QSettings
+
+from flatcamGUI.GUIElements import FCEntry, RadioSet, FCDoubleSpinner, FCComboBox, FCCheckBox
+from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
+
+import gettext
+import FlatCAMTranslation as fcTranslate
+import builtins
+
+fcTranslate.apply_language('strings')
+if '_' not in builtins.__dict__:
+    _ = gettext.gettext
+
+settings = QSettings("Open Source", "FlatCAM")
+if settings.contains("machinist"):
+    machinist_setting = settings.value('machinist', type=int)
+else:
+    machinist_setting = 0
+
+
+class ToolsPaintPrefGroupUI(OptionsGroupUI):
+    def __init__(self, decimals=4, parent=None):
+        # OptionsGroupUI.__init__(self, "Paint Area Tool Options", parent=parent)
+        super(ToolsPaintPrefGroupUI, self).__init__(self, parent=parent)
+
+        self.setTitle(str(_("Paint Tool Options")))
+        self.decimals = decimals
+
+        # ------------------------------
+        # ## Paint area
+        # ------------------------------
+        self.paint_label = QtWidgets.QLabel(_('<b>Parameters:</b>'))
+        self.paint_label.setToolTip(
+            _("Creates tool paths to cover the\n"
+              "whole area of a polygon (remove\n"
+              "all copper). You will be asked\n"
+              "to click on the desired polygon.")
+        )
+        self.layout.addWidget(self.paint_label)
+
+        grid0 = QtWidgets.QGridLayout()
+        grid0.setColumnStretch(0, 0)
+        grid0.setColumnStretch(1, 1)
+        self.layout.addLayout(grid0)
+
+        # Tool dia
+        ptdlabel = QtWidgets.QLabel('<b><font color="green">%s:</font></b>' % _('Tools Dia'))
+        ptdlabel.setToolTip(
+            _("Diameters of the tools, separated by comma.\n"
+              "The value of the diameter has to use the dot decimals separator.\n"
+              "Valid values: 0.3, 1.0")
+        )
+        grid0.addWidget(ptdlabel, 0, 0)
+
+        self.painttooldia_entry = FCEntry(border_color='#0069A9')
+        self.painttooldia_entry.setPlaceholderText(_("Comma separated values"))
+
+        grid0.addWidget(self.painttooldia_entry, 0, 1)
+
+        # Tool Type Radio Button
+        self.tool_type_label = QtWidgets.QLabel('%s:' % _('Tool Type'))
+        self.tool_type_label.setToolTip(
+            _("Default tool type:\n"
+              "- 'V-shape'\n"
+              "- Circular")
+        )
+
+        self.tool_type_radio = RadioSet([{'label': _('V-shape'), 'value': 'V'},
+                                         {'label': _('Circular'), 'value': 'C1'}])
+
+        self.tool_type_radio.setObjectName(_("Tool Type"))
+
+        grid0.addWidget(self.tool_type_label, 1, 0)
+        grid0.addWidget(self.tool_type_radio, 1, 1)
+
+        # Tip Dia
+        self.tipdialabel = QtWidgets.QLabel('%s:' % _('V-Tip Dia'))
+        self.tipdialabel.setToolTip(
+            _("The tip diameter for V-Shape Tool"))
+        self.tipdia_entry = FCDoubleSpinner()
+        self.tipdia_entry.set_precision(self.decimals)
+        self.tipdia_entry.set_range(0.0000, 9999.9999)
+        self.tipdia_entry.setSingleStep(0.1)
+        self.tipdia_entry.setObjectName(_("V-Tip Dia"))
+
+        grid0.addWidget(self.tipdialabel, 2, 0)
+        grid0.addWidget(self.tipdia_entry, 2, 1)
+
+        # Tip Angle
+        self.tipanglelabel = QtWidgets.QLabel('%s:' % _('V-Tip Angle'))
+        self.tipanglelabel.setToolTip(
+            _("The tip angle for V-Shape Tool.\n"
+              "In degree."))
+        self.tipangle_entry = FCDoubleSpinner()
+        self.tipangle_entry.set_precision(self.decimals)
+        self.tipangle_entry.set_range(1.0000, 180.0000)
+        self.tipangle_entry.setSingleStep(5)
+        self.tipangle_entry.setObjectName(_("V-Tip Angle"))
+
+        grid0.addWidget(self.tipanglelabel, 3, 0)
+        grid0.addWidget(self.tipangle_entry, 3, 1)
+
+        # Cut Z entry
+        cutzlabel = QtWidgets.QLabel('%s:' % _('Cut Z'))
+        cutzlabel.setToolTip(
+            _("Depth of cut into material. Negative value.\n"
+              "In FlatCAM units.")
+        )
+        self.cutz_entry = FCDoubleSpinner()
+        self.cutz_entry.set_precision(self.decimals)
+        self.cutz_entry.set_range(-99999.9999, 0.0000)
+        self.cutz_entry.setObjectName(_("Cut Z"))
+
+        self.cutz_entry.setToolTip(
+            _("Depth of cut into material. Negative value.\n"
+              "In FlatCAM units.")
+        )
+        grid0.addWidget(cutzlabel, 4, 0)
+        grid0.addWidget(self.cutz_entry, 4, 1)
+
+        # ### Tool Diameter ####
+        self.newdialabel = QtWidgets.QLabel('%s:' % _('New Dia'))
+        self.newdialabel.setToolTip(
+            _("Diameter for the new tool to add in the Tool Table.\n"
+              "If the tool is V-shape type then this value is automatically\n"
+              "calculated from the other parameters.")
+        )
+        self.newdia_entry = FCDoubleSpinner()
+        self.newdia_entry.set_precision(self.decimals)
+        self.newdia_entry.set_range(0.000, 9999.9999)
+        self.newdia_entry.setObjectName(_("Tool Dia"))
+
+        grid0.addWidget(self.newdialabel, 5, 0)
+        grid0.addWidget(self.newdia_entry, 5, 1)
+
+        separator_line = QtWidgets.QFrame()
+        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
+        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
+        grid0.addWidget(separator_line, 6, 0, 1, 2)
+
+        self.paint_order_label = QtWidgets.QLabel('%s:' % _('Tool order'))
+        self.paint_order_label.setToolTip(_("This set the way that the tools in the tools table are used.\n"
+                                            "'No' --> means that the used order is the one in the tool table\n"
+                                            "'Forward' --> means that the tools will be ordered from small to big\n"
+                                            "'Reverse' --> means that the tools will ordered from big to small\n\n"
+                                            "WARNING: using rest machining will automatically set the order\n"
+                                            "in reverse and disable this control."))
+
+        self.paint_order_radio = RadioSet([{'label': _('No'), 'value': 'no'},
+                                           {'label': _('Forward'), 'value': 'fwd'},
+                                           {'label': _('Reverse'), 'value': 'rev'}])
+
+        grid0.addWidget(self.paint_order_label, 7, 0)
+        grid0.addWidget(self.paint_order_radio, 7, 1)
+
+        separator_line = QtWidgets.QFrame()
+        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
+        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
+        grid0.addWidget(separator_line, 8, 0, 1, 2)
+
+        # Overlap
+        ovlabel = QtWidgets.QLabel('%s:' % _('Overlap'))
+        ovlabel.setToolTip(
+            _("How much (percentage) of the tool width to overlap each tool pass.\n"
+              "Adjust the value starting with lower values\n"
+              "and increasing it if areas that should be painted are still \n"
+              "not painted.\n"
+              "Lower values = faster processing, faster execution on CNC.\n"
+              "Higher values = slow processing and slow execution on CNC\n"
+              "due of too many paths.")
+        )
+        self.paintoverlap_entry = FCDoubleSpinner(suffix='%')
+        self.paintoverlap_entry.set_precision(self.decimals)
+        self.paintoverlap_entry.setWrapping(True)
+        self.paintoverlap_entry.setRange(0.0000, 99.9999)
+        self.paintoverlap_entry.setSingleStep(0.1)
+
+        grid0.addWidget(ovlabel, 9, 0)
+        grid0.addWidget(self.paintoverlap_entry, 9, 1)
+
+        # Margin
+        marginlabel = QtWidgets.QLabel('%s:' % _('Margin'))
+        marginlabel.setToolTip(
+            _("Distance by which to avoid\n"
+              "the edges of the polygon to\n"
+              "be painted.")
+        )
+        self.paintmargin_entry = FCDoubleSpinner()
+        self.paintmargin_entry.set_range(-9999.9999, 9999.9999)
+        self.paintmargin_entry.set_precision(self.decimals)
+        self.paintmargin_entry.setSingleStep(0.1)
+
+        grid0.addWidget(marginlabel, 10, 0)
+        grid0.addWidget(self.paintmargin_entry, 10, 1)
+
+        # Method
+        methodlabel = QtWidgets.QLabel('%s:' % _('Method'))
+        methodlabel.setToolTip(
+            _("Algorithm for painting:\n"
+              "- Standard: Fixed step inwards.\n"
+              "- Seed-based: Outwards from seed.\n"
+              "- Line-based: Parallel lines.\n"
+              "- Laser-lines: Active only for Gerber objects.\n"
+              "Will create lines that follow the traces.\n"
+              "- Combo: In case of failure a new method will be picked from the above\n"
+              "in the order specified.")
+        )
+
+        # self.paintmethod_combo = RadioSet([
+        #     {"label": _("Standard"), "value": "standard"},
+        #     {"label": _("Seed-based"), "value": "seed"},
+        #     {"label": _("Straight lines"), "value": "lines"}
+        # ], orientation='vertical', stretch=False)
+        self.paintmethod_combo = FCComboBox()
+        self.paintmethod_combo.addItems(
+            [_("Standard"), _("Seed"), _("Lines"), _("Laser_lines"), _("Combo")]
+        )
+
+        grid0.addWidget(methodlabel, 11, 0)
+        grid0.addWidget(self.paintmethod_combo, 11, 1)
+
+        # Connect lines
+        self.pathconnect_cb = FCCheckBox('%s' % _("Connect"))
+        self.pathconnect_cb.setToolTip(
+            _("Draw lines between resulting\n"
+              "segments to minimize tool lifts.")
+        )
+        grid0.addWidget(self.pathconnect_cb, 12, 0)
+
+        # Paint contour
+        self.contour_cb = FCCheckBox('%s' % _("Contour"))
+        self.contour_cb.setToolTip(
+            _("Cut around the perimeter of the polygon\n"
+              "to trim rough edges.")
+        )
+        grid0.addWidget(self.contour_cb, 12, 1)
+
+        separator_line = QtWidgets.QFrame()
+        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
+        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
+        grid0.addWidget(separator_line, 13, 0, 1, 2)
+
+        self.rest_cb = FCCheckBox('%s' % _("Rest Machining"))
+        self.rest_cb.setObjectName(_("Rest Machining"))
+        self.rest_cb.setToolTip(
+            _("If checked, use 'rest machining'.\n"
+              "Basically it will clear copper outside PCB features,\n"
+              "using the biggest tool and continue with the next tools,\n"
+              "from bigger to smaller, to clear areas of copper that\n"
+              "could not be cleared by previous tool, until there is\n"
+              "no more copper to clear or there are no more tools.\n\n"
+              "If not checked, use the standard algorithm.")
+        )
+        grid0.addWidget(self.rest_cb, 14, 0, 1, 2)
+
+        # Polygon selection
+        selectlabel = QtWidgets.QLabel('%s:' % _('Selection'))
+        selectlabel.setToolTip(
+            _("Selection of area to be processed.\n"
+              "- 'Polygon Selection' - left mouse click to add/remove polygons to be processed.\n"
+              "- 'Area Selection' - left mouse click to start selection of the area to be processed.\n"
+              "Keeping a modifier key pressed (CTRL or SHIFT) will allow to add multiple areas.\n"
+              "- 'All Polygons' - the process will start after click.\n"
+              "- 'Reference Object' - will process the area specified by another object.")
+        )
+
+        # self.selectmethod_combo = RadioSet(
+        #     [
+        #         {"label": _("Polygon Selection"), "value": "single"},
+        #         {"label": _("Area Selection"), "value": "area"},
+        #         {"label": _("All Polygons"), "value": "all"},
+        #         {"label": _("Reference Object"), "value": "ref"}
+        #     ],
+        #     orientation='vertical',
+        #     stretch=None
+        # )
+        self.selectmethod_combo = FCComboBox()
+        self.selectmethod_combo.addItems(
+            [_("Polygon Selection"), _("Area Selection"), _("All Polygons"), _("Reference Object")]
+        )
+
+        grid0.addWidget(selectlabel, 15, 0)
+        grid0.addWidget(self.selectmethod_combo, 15, 1)
+
+        self.area_shape_label = QtWidgets.QLabel('%s:' % _("Shape"))
+        self.area_shape_label.setToolTip(
+            _("The kind of selection shape used for area selection.")
+        )
+
+        self.area_shape_radio = RadioSet([{'label': _("Square"), 'value': 'square'},
+                                          {'label': _("Polygon"), 'value': 'polygon'}])
+
+        grid0.addWidget(self.area_shape_label, 18, 0)
+        grid0.addWidget(self.area_shape_radio, 18, 1)
+
+        separator_line = QtWidgets.QFrame()
+        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
+        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
+        grid0.addWidget(separator_line, 19, 0, 1, 2)
+
+        # ## Plotting type
+        self.paint_plotting_radio = RadioSet([{'label': _('Normal'), 'value': 'normal'},
+                                              {"label": _("Progressive"), "value": "progressive"}])
+        plotting_label = QtWidgets.QLabel('%s:' % _("Paint Plotting"))
+        plotting_label.setToolTip(
+            _("- 'Normal' -  normal plotting, done at the end of the Paint job\n"
+              "- 'Progressive' - after each shape is generated it will be plotted.")
+        )
+        grid0.addWidget(plotting_label, 20, 0)
+        grid0.addWidget(self.paint_plotting_radio, 20, 1)
+
+        self.layout.addStretch()

+ 146 - 0
flatcamGUI/preferences/tools/ToolsPanelizePrefGroupUI.py

@@ -0,0 +1,146 @@
+from PyQt5 import QtWidgets
+from PyQt5.QtCore import QSettings
+
+from flatcamGUI.GUIElements import FCDoubleSpinner, FCSpinner, RadioSet, FCCheckBox
+from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
+
+import gettext
+import FlatCAMTranslation as fcTranslate
+import builtins
+
+fcTranslate.apply_language('strings')
+if '_' not in builtins.__dict__:
+    _ = gettext.gettext
+
+settings = QSettings("Open Source", "FlatCAM")
+if settings.contains("machinist"):
+    machinist_setting = settings.value('machinist', type=int)
+else:
+    machinist_setting = 0
+
+
+class ToolsPanelizePrefGroupUI(OptionsGroupUI):
+    def __init__(self, decimals=4, parent=None):
+        # OptionsGroupUI.__init__(self, "Cutout Tool Options", parent=parent)
+        super(ToolsPanelizePrefGroupUI, self).__init__(self, parent=parent)
+
+        self.setTitle(str(_("Panelize Tool Options")))
+        self.decimals = decimals
+
+        # ## Board cuttout
+        self.panelize_label = QtWidgets.QLabel("<b>%s:</b>" % _("Parameters"))
+        self.panelize_label.setToolTip(
+            _("Create an object that contains an array of (x, y) elements,\n"
+              "each element is a copy of the source object spaced\n"
+              "at a X distance, Y distance of each other.")
+        )
+        self.layout.addWidget(self.panelize_label)
+
+        grid0 = QtWidgets.QGridLayout()
+        self.layout.addLayout(grid0)
+        grid0.setColumnStretch(0, 0)
+        grid0.setColumnStretch(1, 1)
+
+        # ## Spacing Columns
+        self.pspacing_columns = FCDoubleSpinner()
+        self.pspacing_columns.set_range(0.000001, 9999.9999)
+        self.pspacing_columns.set_precision(self.decimals)
+        self.pspacing_columns.setSingleStep(0.1)
+
+        self.spacing_columns_label = QtWidgets.QLabel('%s:' % _("Spacing cols"))
+        self.spacing_columns_label.setToolTip(
+            _("Spacing between columns of the desired panel.\n"
+              "In current units.")
+        )
+        grid0.addWidget(self.spacing_columns_label, 0, 0)
+        grid0.addWidget(self.pspacing_columns, 0, 1)
+
+        # ## Spacing Rows
+        self.pspacing_rows = FCDoubleSpinner()
+        self.pspacing_rows.set_range(0.000001, 9999.9999)
+        self.pspacing_rows.set_precision(self.decimals)
+        self.pspacing_rows.setSingleStep(0.1)
+
+        self.spacing_rows_label = QtWidgets.QLabel('%s:' % _("Spacing rows"))
+        self.spacing_rows_label.setToolTip(
+            _("Spacing between rows of the desired panel.\n"
+              "In current units.")
+        )
+        grid0.addWidget(self.spacing_rows_label, 1, 0)
+        grid0.addWidget(self.pspacing_rows, 1, 1)
+
+        # ## Columns
+        self.pcolumns = FCSpinner()
+        self.pcolumns.set_range(1, 1000)
+        self.pcolumns.set_step(1)
+
+        self.columns_label = QtWidgets.QLabel('%s:' % _("Columns"))
+        self.columns_label.setToolTip(
+            _("Number of columns of the desired panel")
+        )
+        grid0.addWidget(self.columns_label, 2, 0)
+        grid0.addWidget(self.pcolumns, 2, 1)
+
+        # ## Rows
+        self.prows = FCSpinner()
+        self.prows.set_range(1, 1000)
+        self.prows.set_step(1)
+
+        self.rows_label = QtWidgets.QLabel('%s:' % _("Rows"))
+        self.rows_label.setToolTip(
+            _("Number of rows of the desired panel")
+        )
+        grid0.addWidget(self.rows_label, 3, 0)
+        grid0.addWidget(self.prows, 3, 1)
+
+        # ## Type of resulting Panel object
+        self.panel_type_radio = RadioSet([{'label': _('Gerber'), 'value': 'gerber'},
+                                          {'label': _('Geo'), 'value': 'geometry'}])
+        self.panel_type_label = QtWidgets.QLabel('%s:' % _("Panel Type"))
+        self.panel_type_label.setToolTip(
+           _("Choose the type of object for the panel object:\n"
+             "- Gerber\n"
+             "- Geometry")
+        )
+
+        grid0.addWidget(self.panel_type_label, 4, 0)
+        grid0.addWidget(self.panel_type_radio, 4, 1)
+
+        # ## Constrains
+        self.pconstrain_cb = FCCheckBox('%s:' % _("Constrain within"))
+        self.pconstrain_cb.setToolTip(
+            _("Area define by DX and DY within to constrain the panel.\n"
+              "DX and DY values are in current units.\n"
+              "Regardless of how many columns and rows are desired,\n"
+              "the final panel will have as many columns and rows as\n"
+              "they fit completely within selected area.")
+        )
+        grid0.addWidget(self.pconstrain_cb, 5, 0, 1, 2)
+
+        self.px_width_entry = FCDoubleSpinner()
+        self.px_width_entry.set_range(0.000001, 9999.9999)
+        self.px_width_entry.set_precision(self.decimals)
+        self.px_width_entry.setSingleStep(0.1)
+
+        self.x_width_lbl = QtWidgets.QLabel('%s:' % _("Width (DX)"))
+        self.x_width_lbl.setToolTip(
+            _("The width (DX) within which the panel must fit.\n"
+              "In current units.")
+        )
+        grid0.addWidget(self.x_width_lbl, 6, 0)
+        grid0.addWidget(self.px_width_entry, 6, 1)
+
+        self.py_height_entry = FCDoubleSpinner()
+        self.py_height_entry.set_range(0.000001, 9999.9999)
+        self.py_height_entry.set_precision(self.decimals)
+        self.py_height_entry.setSingleStep(0.1)
+
+        self.y_height_lbl = QtWidgets.QLabel('%s:' % _("Height (DY)"))
+        self.y_height_lbl.setToolTip(
+            _("The height (DY)within which the panel must fit.\n"
+              "In current units.")
+        )
+        grid0.addWidget(self.y_height_lbl, 7, 0)
+        grid0.addWidget(self.py_height_entry, 7, 1)
+
+        self.layout.addStretch()

+ 94 - 0
flatcamGUI/preferences/tools/ToolsPreferencesUI.py

@@ -0,0 +1,94 @@
+from PyQt5 import QtWidgets
+from PyQt5.QtCore import QSettings
+
+from flatcamGUI.preferences.tools.ToolsSubPrefGroupUI import ToolsSubPrefGroupUI
+from flatcamGUI.preferences.tools.ToolsSolderpastePrefGroupUI import ToolsSolderpastePrefGroupUI
+from flatcamGUI.preferences.tools.ToolsTransformPrefGroupUI import ToolsTransformPrefGroupUI
+from flatcamGUI.preferences.tools.ToolsCalculatorsPrefGroupUI import ToolsCalculatorsPrefGroupUI
+from flatcamGUI.preferences.tools.ToolsPanelizePrefGroupUI import ToolsPanelizePrefGroupUI
+from flatcamGUI.preferences.tools.ToolsFilmPrefGroupUI import ToolsFilmPrefGroupUI
+from flatcamGUI.preferences.tools.ToolsPaintPrefGroupUI import ToolsPaintPrefGroupUI
+from flatcamGUI.preferences.tools.Tools2sidedPrefGroupUI import Tools2sidedPrefGroupUI
+from flatcamGUI.preferences.tools.ToolsCutoutPrefGroupUI import ToolsCutoutPrefGroupUI
+from flatcamGUI.preferences.tools.ToolsNCCPrefGroupUI import ToolsNCCPrefGroupUI
+
+import gettext
+import FlatCAMTranslation as fcTranslate
+import builtins
+
+fcTranslate.apply_language('strings')
+if '_' not in builtins.__dict__:
+    _ = gettext.gettext
+
+settings = QSettings("Open Source", "FlatCAM")
+if settings.contains("machinist"):
+    machinist_setting = settings.value('machinist', type=int)
+else:
+    machinist_setting = 0
+
+
+class ToolsPreferencesUI(QtWidgets.QWidget):
+
+    def __init__(self, decimals, parent=None):
+        QtWidgets.QWidget.__init__(self, parent=parent)
+        self.layout = QtWidgets.QHBoxLayout()
+        self.setLayout(self.layout)
+        self.decimals = decimals
+
+        self.tools_ncc_group = ToolsNCCPrefGroupUI(decimals=self.decimals)
+        self.tools_ncc_group.setMinimumWidth(220)
+
+        self.tools_paint_group = ToolsPaintPrefGroupUI(decimals=self.decimals)
+        self.tools_paint_group.setMinimumWidth(220)
+
+        self.tools_cutout_group = ToolsCutoutPrefGroupUI(decimals=self.decimals)
+        self.tools_cutout_group.setMinimumWidth(220)
+
+        self.tools_2sided_group = Tools2sidedPrefGroupUI(decimals=self.decimals)
+        self.tools_2sided_group.setMinimumWidth(220)
+
+        self.tools_film_group = ToolsFilmPrefGroupUI(decimals=self.decimals)
+        self.tools_film_group.setMinimumWidth(220)
+
+        self.tools_panelize_group = ToolsPanelizePrefGroupUI(decimals=self.decimals)
+        self.tools_panelize_group.setMinimumWidth(220)
+
+        self.tools_calculators_group = ToolsCalculatorsPrefGroupUI(decimals=self.decimals)
+        self.tools_calculators_group.setMinimumWidth(220)
+
+        self.tools_transform_group = ToolsTransformPrefGroupUI(decimals=self.decimals)
+        self.tools_transform_group.setMinimumWidth(200)
+
+        self.tools_solderpaste_group = ToolsSolderpastePrefGroupUI(decimals=self.decimals)
+        self.tools_solderpaste_group.setMinimumWidth(200)
+
+        self.tools_sub_group = ToolsSubPrefGroupUI(decimals=self.decimals)
+        self.tools_sub_group.setMinimumWidth(200)
+
+        self.vlay = QtWidgets.QVBoxLayout()
+        self.vlay.addWidget(self.tools_ncc_group)
+        self.vlay.addWidget(self.tools_cutout_group)
+
+        self.vlay1 = QtWidgets.QVBoxLayout()
+        self.vlay1.addWidget(self.tools_paint_group)
+        self.vlay1.addWidget(self.tools_panelize_group)
+
+        self.vlay2 = QtWidgets.QVBoxLayout()
+        self.vlay2.addWidget(self.tools_transform_group)
+        self.vlay2.addWidget(self.tools_2sided_group)
+        self.vlay2.addWidget(self.tools_sub_group)
+
+        self.vlay3 = QtWidgets.QVBoxLayout()
+        self.vlay3.addWidget(self.tools_film_group)
+        self.vlay3.addWidget(self.tools_calculators_group)
+
+        self.vlay4 = QtWidgets.QVBoxLayout()
+        self.vlay4.addWidget(self.tools_solderpaste_group)
+
+        self.layout.addLayout(self.vlay)
+        self.layout.addLayout(self.vlay1)
+        self.layout.addLayout(self.vlay2)
+        self.layout.addLayout(self.vlay3)
+        self.layout.addLayout(self.vlay4)
+
+        self.layout.addStretch()

+ 246 - 0
flatcamGUI/preferences/tools/ToolsSolderpastePrefGroupUI.py

@@ -0,0 +1,246 @@
+from PyQt5 import QtWidgets
+from PyQt5.QtCore import QSettings
+
+from flatcamGUI.GUIElements import FCEntry, FCDoubleSpinner, FCSpinner, FCComboBox
+from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
+
+import gettext
+import FlatCAMTranslation as fcTranslate
+import builtins
+
+fcTranslate.apply_language('strings')
+if '_' not in builtins.__dict__:
+    _ = gettext.gettext
+
+settings = QSettings("Open Source", "FlatCAM")
+if settings.contains("machinist"):
+    machinist_setting = settings.value('machinist', type=int)
+else:
+    machinist_setting = 0
+
+
+class ToolsSolderpastePrefGroupUI(OptionsGroupUI):
+    def __init__(self, decimals=4, parent=None):
+
+        super(ToolsSolderpastePrefGroupUI, self).__init__(self, parent=parent)
+
+        self.setTitle(str(_("SolderPaste Tool Options")))
+        self.decimals = decimals
+
+        # ## Solder Paste Dispensing
+        self.solderpastelabel = QtWidgets.QLabel("<b>%s:</b>" % _("Parameters"))
+        self.solderpastelabel.setToolTip(
+            _("A tool to create GCode for dispensing\n"
+              "solder paste onto a PCB.")
+        )
+        self.layout.addWidget(self.solderpastelabel)
+
+        grid0 = QtWidgets.QGridLayout()
+        self.layout.addLayout(grid0)
+
+        # Nozzle Tool Diameters
+        nozzletdlabel = QtWidgets.QLabel('<b><font color="green">%s:</font></b>' % _('Tools Dia'))
+        nozzletdlabel.setToolTip(
+            _("Diameters of the tools, separated by comma.\n"
+              "The value of the diameter has to use the dot decimals separator.\n"
+              "Valid values: 0.3, 1.0")
+        )
+        self.nozzle_tool_dia_entry = FCEntry()
+
+        grid0.addWidget(nozzletdlabel, 0, 0)
+        grid0.addWidget(self.nozzle_tool_dia_entry, 0, 1)
+
+        # New Nozzle Tool Dia
+        self.addtool_entry_lbl = QtWidgets.QLabel('<b>%s:</b>' % _('New Nozzle Dia'))
+        self.addtool_entry_lbl.setToolTip(
+            _("Diameter for the new Nozzle tool to add in the Tool Table")
+        )
+        self.addtool_entry = FCDoubleSpinner()
+        self.addtool_entry.set_precision(self.decimals)
+        self.addtool_entry.set_range(0.0000001, 9999.9999)
+        self.addtool_entry.setSingleStep(0.1)
+
+        grid0.addWidget(self.addtool_entry_lbl, 1, 0)
+        grid0.addWidget(self.addtool_entry, 1, 1)
+
+        # Z dispense start
+        self.z_start_entry = FCDoubleSpinner()
+        self.z_start_entry.set_precision(self.decimals)
+        self.z_start_entry.set_range(0.0000001, 9999.9999)
+        self.z_start_entry.setSingleStep(0.1)
+
+        self.z_start_label = QtWidgets.QLabel('%s:' % _("Z Dispense Start"))
+        self.z_start_label.setToolTip(
+            _("The height (Z) when solder paste dispensing starts.")
+        )
+        grid0.addWidget(self.z_start_label, 2, 0)
+        grid0.addWidget(self.z_start_entry, 2, 1)
+
+        # Z dispense
+        self.z_dispense_entry = FCDoubleSpinner()
+        self.z_dispense_entry.set_precision(self.decimals)
+        self.z_dispense_entry.set_range(0.0000001, 9999.9999)
+        self.z_dispense_entry.setSingleStep(0.1)
+
+        self.z_dispense_label = QtWidgets.QLabel('%s:' % _("Z Dispense"))
+        self.z_dispense_label.setToolTip(
+            _("The height (Z) when doing solder paste dispensing.")
+        )
+        grid0.addWidget(self.z_dispense_label, 3, 0)
+        grid0.addWidget(self.z_dispense_entry, 3, 1)
+
+        # Z dispense stop
+        self.z_stop_entry = FCDoubleSpinner()
+        self.z_stop_entry.set_precision(self.decimals)
+        self.z_stop_entry.set_range(0.0000001, 9999.9999)
+        self.z_stop_entry.setSingleStep(0.1)
+
+        self.z_stop_label = QtWidgets.QLabel('%s:' % _("Z Dispense Stop"))
+        self.z_stop_label.setToolTip(
+            _("The height (Z) when solder paste dispensing stops.")
+        )
+        grid0.addWidget(self.z_stop_label, 4, 0)
+        grid0.addWidget(self.z_stop_entry, 4, 1)
+
+        # Z travel
+        self.z_travel_entry = FCDoubleSpinner()
+        self.z_travel_entry.set_precision(self.decimals)
+        self.z_travel_entry.set_range(0.0000001, 9999.9999)
+        self.z_travel_entry.setSingleStep(0.1)
+
+        self.z_travel_label = QtWidgets.QLabel('%s:' % _("Z Travel"))
+        self.z_travel_label.setToolTip(
+            _("The height (Z) for travel between pads\n"
+              "(without dispensing solder paste).")
+        )
+        grid0.addWidget(self.z_travel_label, 5, 0)
+        grid0.addWidget(self.z_travel_entry, 5, 1)
+
+        # Z toolchange location
+        self.z_toolchange_entry = FCDoubleSpinner()
+        self.z_toolchange_entry.set_precision(self.decimals)
+        self.z_toolchange_entry.set_range(0.0000001, 9999.9999)
+        self.z_toolchange_entry.setSingleStep(0.1)
+
+        self.z_toolchange_label = QtWidgets.QLabel('%s:' % _("Z Toolchange"))
+        self.z_toolchange_label.setToolTip(
+            _("The height (Z) for tool (nozzle) change.")
+        )
+        grid0.addWidget(self.z_toolchange_label, 6, 0)
+        grid0.addWidget(self.z_toolchange_entry, 6, 1)
+
+        # X,Y Toolchange location
+        self.xy_toolchange_entry = FCEntry()
+        self.xy_toolchange_label = QtWidgets.QLabel('%s:' % _("Toolchange X-Y"))
+        self.xy_toolchange_label.setToolTip(
+            _("The X,Y location for tool (nozzle) change.\n"
+              "The format is (x, y) where x and y are real numbers.")
+        )
+        grid0.addWidget(self.xy_toolchange_label, 7, 0)
+        grid0.addWidget(self.xy_toolchange_entry, 7, 1)
+
+        # Feedrate X-Y
+        self.frxy_entry = FCDoubleSpinner()
+        self.frxy_entry.set_precision(self.decimals)
+        self.frxy_entry.set_range(0.0000001, 99999.9999)
+        self.frxy_entry.setSingleStep(0.1)
+
+        self.frxy_label = QtWidgets.QLabel('%s:' % _("Feedrate X-Y"))
+        self.frxy_label.setToolTip(
+            _("Feedrate (speed) while moving on the X-Y plane.")
+        )
+        grid0.addWidget(self.frxy_label, 8, 0)
+        grid0.addWidget(self.frxy_entry, 8, 1)
+
+        # Feedrate Z
+        self.frz_entry = FCDoubleSpinner()
+        self.frz_entry.set_precision(self.decimals)
+        self.frz_entry.set_range(0.0000001, 99999.9999)
+        self.frz_entry.setSingleStep(0.1)
+
+        self.frz_label = QtWidgets.QLabel('%s:' % _("Feedrate Z"))
+        self.frz_label.setToolTip(
+            _("Feedrate (speed) while moving vertically\n"
+              "(on Z plane).")
+        )
+        grid0.addWidget(self.frz_label, 9, 0)
+        grid0.addWidget(self.frz_entry, 9, 1)
+
+        # Feedrate Z Dispense
+        self.frz_dispense_entry = FCDoubleSpinner()
+        self.frz_dispense_entry.set_precision(self.decimals)
+        self.frz_dispense_entry.set_range(0.0000001, 99999.9999)
+        self.frz_dispense_entry.setSingleStep(0.1)
+
+        self.frz_dispense_label = QtWidgets.QLabel('%s:' % _("Feedrate Z Dispense"))
+        self.frz_dispense_label.setToolTip(
+            _("Feedrate (speed) while moving up vertically\n"
+              "to Dispense position (on Z plane).")
+        )
+        grid0.addWidget(self.frz_dispense_label, 10, 0)
+        grid0.addWidget(self.frz_dispense_entry, 10, 1)
+
+        # Spindle Speed Forward
+        self.speedfwd_entry = FCSpinner()
+        self.speedfwd_entry.set_range(0, 99999)
+        self.speedfwd_entry.set_step(1000)
+
+        self.speedfwd_label = QtWidgets.QLabel('%s:' % _("Spindle Speed FWD"))
+        self.speedfwd_label.setToolTip(
+            _("The dispenser speed while pushing solder paste\n"
+              "through the dispenser nozzle.")
+        )
+        grid0.addWidget(self.speedfwd_label, 11, 0)
+        grid0.addWidget(self.speedfwd_entry, 11, 1)
+
+        # Dwell Forward
+        self.dwellfwd_entry = FCDoubleSpinner()
+        self.dwellfwd_entry.set_precision(self.decimals)
+        self.dwellfwd_entry.set_range(0.0000001, 9999.9999)
+        self.dwellfwd_entry.setSingleStep(0.1)
+
+        self.dwellfwd_label = QtWidgets.QLabel('%s:' % _("Dwell FWD"))
+        self.dwellfwd_label.setToolTip(
+            _("Pause after solder dispensing.")
+        )
+        grid0.addWidget(self.dwellfwd_label, 12, 0)
+        grid0.addWidget(self.dwellfwd_entry, 12, 1)
+
+        # Spindle Speed Reverse
+        self.speedrev_entry = FCSpinner()
+        self.speedrev_entry.set_range(0, 999999)
+        self.speedrev_entry.set_step(1000)
+
+        self.speedrev_label = QtWidgets.QLabel('%s:' % _("Spindle Speed REV"))
+        self.speedrev_label.setToolTip(
+            _("The dispenser speed while retracting solder paste\n"
+              "through the dispenser nozzle.")
+        )
+        grid0.addWidget(self.speedrev_label, 13, 0)
+        grid0.addWidget(self.speedrev_entry, 13, 1)
+
+        # Dwell Reverse
+        self.dwellrev_entry = FCDoubleSpinner()
+        self.dwellrev_entry.set_precision(self.decimals)
+        self.dwellrev_entry.set_range(0.0000001, 9999.9999)
+        self.dwellrev_entry.setSingleStep(0.1)
+
+        self.dwellrev_label = QtWidgets.QLabel('%s:' % _("Dwell REV"))
+        self.dwellrev_label.setToolTip(
+            _("Pause after solder paste dispenser retracted,\n"
+              "to allow pressure equilibrium.")
+        )
+        grid0.addWidget(self.dwellrev_label, 14, 0)
+        grid0.addWidget(self.dwellrev_entry, 14, 1)
+
+        # Preprocessors
+        pp_label = QtWidgets.QLabel('%s:' % _('Preprocessor'))
+        pp_label.setToolTip(
+            _("Files that control the GCode generation.")
+        )
+
+        self.pp_combo = FCComboBox()
+        grid0.addWidget(pp_label, 15, 0)
+        grid0.addWidget(self.pp_combo, 15, 1)
+
+        self.layout.addStretch()

+ 42 - 0
flatcamGUI/preferences/tools/ToolsSubPrefGroupUI.py

@@ -0,0 +1,42 @@
+from PyQt5 import QtWidgets
+from PyQt5.QtCore import QSettings
+
+from flatcamGUI.GUIElements import FCCheckBox
+from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
+
+import gettext
+import FlatCAMTranslation as fcTranslate
+import builtins
+
+fcTranslate.apply_language('strings')
+if '_' not in builtins.__dict__:
+    _ = gettext.gettext
+
+settings = QSettings("Open Source", "FlatCAM")
+if settings.contains("machinist"):
+    machinist_setting = settings.value('machinist', type=int)
+else:
+    machinist_setting = 0
+
+
+class ToolsSubPrefGroupUI(OptionsGroupUI):
+    def __init__(self, decimals=4, parent=None):
+
+        super(ToolsSubPrefGroupUI, self).__init__(self, parent=parent)
+
+        self.setTitle(str(_("Substractor Tool Options")))
+        self.decimals = decimals
+
+        # ## Subtractor Tool Parameters
+        self.sublabel = QtWidgets.QLabel("<b>%s:</b>" % _("Parameters"))
+        self.sublabel.setToolTip(
+            _("A tool to substract one Gerber or Geometry object\n"
+              "from another of the same type.")
+        )
+        self.layout.addWidget(self.sublabel)
+
+        self.close_paths_cb = FCCheckBox(_("Close paths"))
+        self.close_paths_cb.setToolTip(_("Checking this will close the paths cut by the Geometry substractor object."))
+        self.layout.addWidget(self.close_paths_cb)
+
+        self.layout.addStretch()

+ 249 - 0
flatcamGUI/preferences/tools/ToolsTransformPrefGroupUI.py

@@ -0,0 +1,249 @@
+from PyQt5 import QtWidgets
+from PyQt5.QtCore import QSettings
+
+from flatcamGUI.GUIElements import FCDoubleSpinner, FCCheckBox, EvalEntry2
+from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
+
+import gettext
+import FlatCAMTranslation as fcTranslate
+import builtins
+
+fcTranslate.apply_language('strings')
+if '_' not in builtins.__dict__:
+    _ = gettext.gettext
+
+settings = QSettings("Open Source", "FlatCAM")
+if settings.contains("machinist"):
+    machinist_setting = settings.value('machinist', type=int)
+else:
+    machinist_setting = 0
+
+
+class ToolsTransformPrefGroupUI(OptionsGroupUI):
+    def __init__(self, decimals=4, parent=None):
+
+        super(ToolsTransformPrefGroupUI, self).__init__(self, parent=parent)
+
+        self.setTitle(str(_("Transform Tool Options")))
+        self.decimals = decimals
+
+        # ## Transformations
+        self.transform_label = QtWidgets.QLabel("<b>%s:</b>" % _("Parameters"))
+        self.transform_label.setToolTip(
+            _("Various transformations that can be applied\n"
+              "on a FlatCAM object.")
+        )
+        self.layout.addWidget(self.transform_label)
+
+        grid0 = QtWidgets.QGridLayout()
+        self.layout.addLayout(grid0)
+        grid0.setColumnStretch(0, 0)
+        grid0.setColumnStretch(1, 1)
+
+        # ## Rotate Angle
+
+        rotate_title_lbl = QtWidgets.QLabel('<b>%s</b>' % _("Rotate"))
+        grid0.addWidget(rotate_title_lbl, 0, 0, 1, 2)
+
+        self.rotate_entry = FCDoubleSpinner()
+        self.rotate_entry.set_range(-360.0, 360.0)
+        self.rotate_entry.set_precision(self.decimals)
+        self.rotate_entry.setSingleStep(15)
+
+        self.rotate_label = QtWidgets.QLabel('%s:' % _("Angle"))
+        self.rotate_label.setToolTip(
+            _("Angle for Rotation action, in degrees.\n"
+              "Float number between -360 and 359.\n"
+              "Positive numbers for CW motion.\n"
+              "Negative numbers for CCW motion.")
+        )
+        grid0.addWidget(self.rotate_label, 1, 0)
+        grid0.addWidget(self.rotate_entry, 1, 1)
+
+        # ## Skew/Shear Angle on X axis
+        skew_title_lbl = QtWidgets.QLabel('<b>%s</b>' % _("Skew"))
+        grid0.addWidget(skew_title_lbl, 2, 0, 1, 2)
+
+        self.skewx_entry = FCDoubleSpinner()
+        self.skewx_entry.set_range(-360.0, 360.0)
+        self.skewx_entry.set_precision(self.decimals)
+        self.skewx_entry.setSingleStep(0.1)
+
+        self.skewx_label = QtWidgets.QLabel('%s:' % _("X angle"))
+        self.skewx_label.setToolTip(
+            _("Angle for Skew action, in degrees.\n"
+              "Float number between -360 and 359.")
+        )
+        grid0.addWidget(self.skewx_label, 3, 0)
+        grid0.addWidget(self.skewx_entry, 3, 1)
+
+        # ## Skew/Shear Angle on Y axis
+        self.skewy_entry = FCDoubleSpinner()
+        self.skewy_entry.set_range(-360.0, 360.0)
+        self.skewy_entry.set_precision(self.decimals)
+        self.skewy_entry.setSingleStep(0.1)
+
+        self.skewy_label = QtWidgets.QLabel('%s:' % _("Y angle"))
+        self.skewy_label.setToolTip(
+            _("Angle for Skew action, in degrees.\n"
+              "Float number between -360 and 359.")
+        )
+        grid0.addWidget(self.skewy_label, 4, 0)
+        grid0.addWidget(self.skewy_entry, 4, 1)
+
+        # ## Scale
+        scale_title_lbl = QtWidgets.QLabel('<b>%s</b>' % _("Scale"))
+        grid0.addWidget(scale_title_lbl, 5, 0, 1, 2)
+
+        self.scalex_entry = FCDoubleSpinner()
+        self.scalex_entry.set_range(0, 9999.9999)
+        self.scalex_entry.set_precision(self.decimals)
+        self.scalex_entry.setSingleStep(0.1)
+
+        self.scalex_label = QtWidgets.QLabel('%s:' % _("X factor"))
+        self.scalex_label.setToolTip(
+            _("Factor for scaling on X axis.")
+        )
+        grid0.addWidget(self.scalex_label, 6, 0)
+        grid0.addWidget(self.scalex_entry, 6, 1)
+
+        # ## Scale factor on X axis
+        self.scaley_entry = FCDoubleSpinner()
+        self.scaley_entry.set_range(0, 9999.9999)
+        self.scaley_entry.set_precision(self.decimals)
+        self.scaley_entry.setSingleStep(0.1)
+
+        self.scaley_label = QtWidgets.QLabel('%s:' % _("Y factor"))
+        self.scaley_label.setToolTip(
+            _("Factor for scaling on Y axis.")
+        )
+        grid0.addWidget(self.scaley_label, 7, 0)
+        grid0.addWidget(self.scaley_entry, 7, 1)
+
+        # ## Link Scale factors
+        self.link_cb = FCCheckBox(_("Link"))
+        self.link_cb.setToolTip(
+            _("Scale the selected object(s)\n"
+              "using the Scale_X factor for both axis.")
+        )
+        grid0.addWidget(self.link_cb, 8, 0)
+
+        # ## Scale Reference
+        self.reference_cb = FCCheckBox('%s' % _("Scale Reference"))
+        self.reference_cb.setToolTip(
+            _("Scale the selected object(s)\n"
+              "using the origin reference when checked,\n"
+              "and the center of the biggest bounding box\n"
+              "of the selected objects when unchecked.")
+        )
+        grid0.addWidget(self.reference_cb, 8, 1)
+
+        # ## Offset
+        offset_title_lbl = QtWidgets.QLabel('<b>%s</b>' % _("Offset"))
+        grid0.addWidget(offset_title_lbl, 9, 0, 1, 2)
+
+        self.offx_entry = FCDoubleSpinner()
+        self.offx_entry.set_range(-9999.9999, 9999.9999)
+        self.offx_entry.set_precision(self.decimals)
+        self.offx_entry.setSingleStep(0.1)
+
+        self.offx_label = QtWidgets.QLabel('%s:' % _("X val"))
+        self.offx_label.setToolTip(
+           _("Distance to offset on X axis. In current units.")
+        )
+        grid0.addWidget(self.offx_label, 10, 0)
+        grid0.addWidget(self.offx_entry, 10, 1)
+
+        # ## Offset distance on Y axis
+        self.offy_entry = FCDoubleSpinner()
+        self.offy_entry.set_range(-9999.9999, 9999.9999)
+        self.offy_entry.set_precision(self.decimals)
+        self.offy_entry.setSingleStep(0.1)
+
+        self.offy_label = QtWidgets.QLabel('%s:' % _("Y val"))
+        self.offy_label.setToolTip(
+            _("Distance to offset on Y axis. In current units.")
+        )
+        grid0.addWidget(self.offy_label, 11, 0)
+        grid0.addWidget(self.offy_entry, 11, 1)
+
+        # ## Mirror
+        mirror_title_lbl = QtWidgets.QLabel('<b>%s</b>' % _("Mirror"))
+        grid0.addWidget(mirror_title_lbl, 12, 0, 1, 2)
+
+        # ## Mirror (Flip) Reference Point
+        self.mirror_reference_cb = FCCheckBox('%s' % _("Mirror Reference"))
+        self.mirror_reference_cb.setToolTip(
+            _("Flip the selected object(s)\n"
+              "around the point in Point Entry Field.\n"
+              "\n"
+              "The point coordinates can be captured by\n"
+              "left click on canvas together with pressing\n"
+              "SHIFT key. \n"
+              "Then click Add button to insert coordinates.\n"
+              "Or enter the coords in format (x, y) in the\n"
+              "Point Entry field and click Flip on X(Y)"))
+        grid0.addWidget(self.mirror_reference_cb, 13, 0, 1, 2)
+
+        self.flip_ref_label = QtWidgets.QLabel('%s' % _("Mirror Reference point"))
+        self.flip_ref_label.setToolTip(
+            _("Coordinates in format (x, y) used as reference for mirroring.\n"
+              "The 'x' in (x, y) will be used when using Flip on X and\n"
+              "the 'y' in (x, y) will be used when using Flip on Y and")
+        )
+        self.flip_ref_entry = EvalEntry2("(0, 0)")
+
+        grid0.addWidget(self.flip_ref_label, 14, 0, 1, 2)
+        grid0.addWidget(self.flip_ref_entry, 15, 0, 1, 2)
+
+        # ## Buffer
+        buffer_title_lbl = QtWidgets.QLabel('<b>%s</b>' % _("Buffer"))
+        grid0.addWidget(buffer_title_lbl, 16, 0, 1, 2)
+
+        self.buffer_label = QtWidgets.QLabel('%s:' % _("Distance"))
+        self.buffer_label.setToolTip(
+            _("A positive value will create the effect of dilation,\n"
+              "while a negative value will create the effect of erosion.\n"
+              "Each geometry element of the object will be increased\n"
+              "or decreased with the 'distance'.")
+        )
+
+        self.buffer_entry = FCDoubleSpinner()
+        self.buffer_entry.set_precision(self.decimals)
+        self.buffer_entry.setSingleStep(0.1)
+        self.buffer_entry.setWrapping(True)
+        self.buffer_entry.set_range(-9999.9999, 9999.9999)
+
+        grid0.addWidget(self.buffer_label, 17, 0)
+        grid0.addWidget(self.buffer_entry, 17, 1)
+
+        self.buffer_factor_label = QtWidgets.QLabel('%s:' % _("Value"))
+        self.buffer_factor_label.setToolTip(
+            _("A positive value will create the effect of dilation,\n"
+              "while a negative value will create the effect of erosion.\n"
+              "Each geometry element of the object will be increased\n"
+              "or decreased to fit the 'Value'. Value is a percentage\n"
+              "of the initial dimension.")
+        )
+
+        self.buffer_factor_entry = FCDoubleSpinner(suffix='%')
+        self.buffer_factor_entry.set_range(-100.0000, 1000.0000)
+        self.buffer_factor_entry.set_precision(self.decimals)
+        self.buffer_factor_entry.setWrapping(True)
+        self.buffer_factor_entry.setSingleStep(1)
+
+        grid0.addWidget(self.buffer_factor_label, 18, 0)
+        grid0.addWidget(self.buffer_factor_entry, 18, 1)
+
+        self.buffer_rounded_cb = FCCheckBox()
+        self.buffer_rounded_cb.setText('%s' % _("Rounded"))
+        self.buffer_rounded_cb.setToolTip(
+            _("If checked then the buffer will surround the buffered shape,\n"
+              "every corner will be rounded.\n"
+              "If not checked then the buffer will follow the exact geometry\n"
+              "of the buffered shape.")
+        )
+
+        grid0.addWidget(self.buffer_rounded_cb, 19, 0, 1, 2)
+
+        self.layout.addStretch()

+ 0 - 0
flatcamGUI/preferences/tools/__init__.py


+ 83 - 0
flatcamGUI/preferences/utilities/AutoCompletePrefGroupUI.py

@@ -0,0 +1,83 @@
+from PyQt5 import QtWidgets, QtGui
+from PyQt5.QtCore import QSettings
+
+from flatcamGUI.GUIElements import FCButton, FCTextArea, FCEntry
+from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
+
+import gettext
+import FlatCAMTranslation as fcTranslate
+import builtins
+
+fcTranslate.apply_language('strings')
+if '_' not in builtins.__dict__:
+    _ = gettext.gettext
+
+settings = QSettings("Open Source", "FlatCAM")
+if settings.contains("machinist"):
+    machinist_setting = settings.value('machinist', type=int)
+else:
+    machinist_setting = 0
+
+
+class AutoCompletePrefGroupUI(OptionsGroupUI):
+    def __init__(self, decimals=4, parent=None):
+        # OptionsGroupUI.__init__(self, "Gerber File associations Preferences", parent=None)
+        super().__init__(self, parent=parent)
+
+        self.setTitle(str(_("Autocompleter Keywords")))
+        self.decimals = decimals
+
+        self.restore_btn = FCButton(_("Restore"))
+        self.restore_btn.setToolTip(_("Restore the autocompleter keywords list to the default state."))
+        self.del_all_btn = FCButton(_("Delete All"))
+        self.del_all_btn.setToolTip(_("Delete all autocompleter keywords from the list."))
+
+        hlay0 = QtWidgets.QHBoxLayout()
+        self.layout.addLayout(hlay0)
+        hlay0.addWidget(self.restore_btn)
+        hlay0.addWidget(self.del_all_btn)
+
+        # ## Gerber associations
+        self.grb_list_label = QtWidgets.QLabel("<b>%s:</b>" % _("Keywords list"))
+        self.grb_list_label.setToolTip(
+            _("List of keywords used by\n"
+              "the autocompleter in FlatCAM.\n"
+              "The autocompleter is installed\n"
+              "in the Code Editor and for the Tcl Shell.")
+        )
+        self.layout.addWidget(self.grb_list_label)
+
+        qsettings = QSettings("Open Source", "FlatCAM")
+        if qsettings.contains("textbox_font_size"):
+            tb_fsize = qsettings.value('textbox_font_size', type=int)
+        else:
+            tb_fsize = 10
+
+        self.kw_list_text = FCTextArea()
+        self.kw_list_text.setReadOnly(True)
+        # self.grb_list_text.sizeHint(custom_sizehint=150)
+        self.layout.addWidget(self.kw_list_text)
+        font = QtGui.QFont()
+        font.setPointSize(tb_fsize)
+        self.kw_list_text.setFont(font)
+
+        self.kw_label = QtWidgets.QLabel('%s:' % _("Extension"))
+        self.kw_label.setToolTip(_("A keyword to be added or deleted to the list."))
+        self.kw_entry = FCEntry()
+
+        hlay1 = QtWidgets.QHBoxLayout()
+        self.layout.addLayout(hlay1)
+        hlay1.addWidget(self.kw_label)
+        hlay1.addWidget(self.kw_entry)
+
+        self.add_btn = FCButton(_("Add keyword"))
+        self.add_btn.setToolTip(_("Add a keyword to the list"))
+        self.del_btn = FCButton(_("Delete keyword"))
+        self.del_btn.setToolTip(_("Delete a keyword from the list"))
+
+        hlay2 = QtWidgets.QHBoxLayout()
+        self.layout.addLayout(hlay2)
+        hlay2.addWidget(self.add_btn)
+        hlay2.addWidget(self.del_btn)
+
+        # self.layout.addStretch()

+ 102 - 0
flatcamGUI/preferences/utilities/FAExcPrefGroupUI.py

@@ -0,0 +1,102 @@
+from PyQt5 import QtWidgets, QtGui
+from PyQt5.QtCore import QSettings
+
+from flatcamGUI.GUIElements import VerticalScrollArea, FCButton, FCTextArea, FCEntry
+from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
+
+import gettext
+import FlatCAMTranslation as fcTranslate
+import builtins
+
+fcTranslate.apply_language('strings')
+if '_' not in builtins.__dict__:
+    _ = gettext.gettext
+
+settings = QSettings("Open Source", "FlatCAM")
+if settings.contains("machinist"):
+    machinist_setting = settings.value('machinist', type=int)
+else:
+    machinist_setting = 0
+
+
+class FAExcPrefGroupUI(OptionsGroupUI):
+    def __init__(self, decimals=4, parent=None):
+        # OptionsGroupUI.__init__(self, "Excellon File associations Preferences", parent=None)
+        super().__init__(self, parent=parent)
+
+        self.setTitle(str(_("Excellon File associations")))
+        self.decimals = decimals
+
+        self.layout.setContentsMargins(2, 2, 2, 2)
+
+        self.vertical_lay = QtWidgets.QVBoxLayout()
+        scroll_widget = QtWidgets.QWidget()
+
+        scroll = VerticalScrollArea()
+        scroll.setWidget(scroll_widget)
+        scroll.setWidgetResizable(True)
+        scroll.setFrameShape(QtWidgets.QFrame.NoFrame)
+
+        self.restore_btn = FCButton(_("Restore"))
+        self.restore_btn.setToolTip(_("Restore the extension list to the default state."))
+        self.del_all_btn = FCButton(_("Delete All"))
+        self.del_all_btn.setToolTip(_("Delete all extensions from the list."))
+
+        hlay0 = QtWidgets.QHBoxLayout()
+        hlay0.addWidget(self.restore_btn)
+        hlay0.addWidget(self.del_all_btn)
+        self.vertical_lay.addLayout(hlay0)
+
+        # # ## Excellon associations
+        list_label = QtWidgets.QLabel("<b>%s:</b>" % _("Extensions list"))
+        list_label.setToolTip(
+            _("List of file extensions to be\n"
+              "associated with FlatCAM.")
+        )
+        self.vertical_lay.addWidget(list_label)
+
+        qsettings = QSettings("Open Source", "FlatCAM")
+        if qsettings.contains("textbox_font_size"):
+            tb_fsize = qsettings.value('textbox_font_size', type=int)
+        else:
+            tb_fsize = 10
+
+        self.exc_list_text = FCTextArea()
+        self.exc_list_text.setReadOnly(True)
+        # self.exc_list_text.sizeHint(custom_sizehint=150)
+        font = QtGui.QFont()
+        font.setPointSize(tb_fsize)
+        self.exc_list_text.setFont(font)
+
+        self.vertical_lay.addWidget(self.exc_list_text)
+
+        self.ext_label = QtWidgets.QLabel('%s:' % _("Extension"))
+        self.ext_label.setToolTip(_("A file extension to be added or deleted to the list."))
+        self.ext_entry = FCEntry()
+
+        hlay1 = QtWidgets.QHBoxLayout()
+        self.vertical_lay.addLayout(hlay1)
+        hlay1.addWidget(self.ext_label)
+        hlay1.addWidget(self.ext_entry)
+
+        self.add_btn = FCButton(_("Add Extension"))
+        self.add_btn.setToolTip(_("Add a file extension to the list"))
+        self.del_btn = FCButton(_("Delete Extension"))
+        self.del_btn.setToolTip(_("Delete a file extension from the list"))
+
+        hlay2 = QtWidgets.QHBoxLayout()
+        self.vertical_lay.addLayout(hlay2)
+        hlay2.addWidget(self.add_btn)
+        hlay2.addWidget(self.del_btn)
+
+        self.exc_list_btn = FCButton(_("Apply Association"))
+        self.exc_list_btn.setToolTip(_("Apply the file associations between\n"
+                                       "FlatCAM and the files with above extensions.\n"
+                                       "They will be active after next logon.\n"
+                                       "This work only in Windows."))
+        self.vertical_lay.addWidget(self.exc_list_btn)
+
+        scroll_widget.setLayout(self.vertical_lay)
+        self.layout.addWidget(scroll)
+
+        # self.vertical_lay.addStretch()

+ 89 - 0
flatcamGUI/preferences/utilities/FAGcoPrefGroupUI.py

@@ -0,0 +1,89 @@
+from PyQt5 import QtWidgets, QtGui
+from PyQt5.QtCore import QSettings
+
+from flatcamGUI.GUIElements import FCButton, FCTextArea, FCEntry
+from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
+
+import gettext
+import FlatCAMTranslation as fcTranslate
+import builtins
+
+fcTranslate.apply_language('strings')
+if '_' not in builtins.__dict__:
+    _ = gettext.gettext
+
+settings = QSettings("Open Source", "FlatCAM")
+if settings.contains("machinist"):
+    machinist_setting = settings.value('machinist', type=int)
+else:
+    machinist_setting = 0
+
+
+class FAGcoPrefGroupUI(OptionsGroupUI):
+    def __init__(self, decimals=4, parent=None):
+        # OptionsGroupUI.__init__(self, "Gcode File associations Preferences", parent=None)
+        super(FAGcoPrefGroupUI, self).__init__(self, parent=parent)
+
+        self.setTitle(str(_("GCode File associations")))
+        self.decimals = decimals
+
+        self.restore_btn = FCButton(_("Restore"))
+        self.restore_btn.setToolTip(_("Restore the extension list to the default state."))
+        self.del_all_btn = FCButton(_("Delete All"))
+        self.del_all_btn.setToolTip(_("Delete all extensions from the list."))
+
+        hlay0 = QtWidgets.QHBoxLayout()
+        self.layout.addLayout(hlay0)
+        hlay0.addWidget(self.restore_btn)
+        hlay0.addWidget(self.del_all_btn)
+
+        # ## G-Code associations
+        self.gco_list_label = QtWidgets.QLabel("<b>%s:</b>" % _("Extensions list"))
+        self.gco_list_label.setToolTip(
+            _("List of file extensions to be\n"
+              "associated with FlatCAM.")
+        )
+        self.layout.addWidget(self.gco_list_label)
+
+        qsettings = QSettings("Open Source", "FlatCAM")
+        if qsettings.contains("textbox_font_size"):
+            tb_fsize = qsettings.value('textbox_font_size', type=int)
+        else:
+            tb_fsize = 10
+
+        self.gco_list_text = FCTextArea()
+        self.gco_list_text.setReadOnly(True)
+        # self.gco_list_text.sizeHint(custom_sizehint=150)
+        font = QtGui.QFont()
+        font.setPointSize(tb_fsize)
+        self.gco_list_text.setFont(font)
+
+        self.layout.addWidget(self.gco_list_text)
+
+        self.ext_label = QtWidgets.QLabel('%s:' % _("Extension"))
+        self.ext_label.setToolTip(_("A file extension to be added or deleted to the list."))
+        self.ext_entry = FCEntry()
+
+        hlay1 = QtWidgets.QHBoxLayout()
+        self.layout.addLayout(hlay1)
+        hlay1.addWidget(self.ext_label)
+        hlay1.addWidget(self.ext_entry)
+
+        self.add_btn = FCButton(_("Add Extension"))
+        self.add_btn.setToolTip(_("Add a file extension to the list"))
+        self.del_btn = FCButton(_("Delete Extension"))
+        self.del_btn.setToolTip(_("Delete a file extension from the list"))
+
+        hlay2 = QtWidgets.QHBoxLayout()
+        self.layout.addLayout(hlay2)
+        hlay2.addWidget(self.add_btn)
+        hlay2.addWidget(self.del_btn)
+
+        self.gco_list_btn = FCButton(_("Apply Association"))
+        self.gco_list_btn.setToolTip(_("Apply the file associations between\n"
+                                       "FlatCAM and the files with above extensions.\n"
+                                       "They will be active after next logon.\n"
+                                       "This work only in Windows."))
+        self.layout.addWidget(self.gco_list_btn)
+
+        # self.layout.addStretch()

+ 89 - 0
flatcamGUI/preferences/utilities/FAGrbPrefGroupUI.py

@@ -0,0 +1,89 @@
+from PyQt5 import QtWidgets, QtGui
+from PyQt5.QtCore import QSettings
+
+from flatcamGUI.GUIElements import FCButton, FCTextArea, FCEntry
+from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
+
+import gettext
+import FlatCAMTranslation as fcTranslate
+import builtins
+
+fcTranslate.apply_language('strings')
+if '_' not in builtins.__dict__:
+    _ = gettext.gettext
+
+settings = QSettings("Open Source", "FlatCAM")
+if settings.contains("machinist"):
+    machinist_setting = settings.value('machinist', type=int)
+else:
+    machinist_setting = 0
+
+
+class FAGrbPrefGroupUI(OptionsGroupUI):
+    def __init__(self, decimals=4, parent=None):
+        # OptionsGroupUI.__init__(self, "Gerber File associations Preferences", parent=None)
+        super(FAGrbPrefGroupUI, self).__init__(self, parent=parent)
+
+        self.setTitle(str(_("Gerber File associations")))
+        self.decimals = decimals
+
+        self.restore_btn = FCButton(_("Restore"))
+        self.restore_btn.setToolTip(_("Restore the extension list to the default state."))
+        self.del_all_btn = FCButton(_("Delete All"))
+        self.del_all_btn.setToolTip(_("Delete all extensions from the list."))
+
+        hlay0 = QtWidgets.QHBoxLayout()
+        self.layout.addLayout(hlay0)
+        hlay0.addWidget(self.restore_btn)
+        hlay0.addWidget(self.del_all_btn)
+
+        # ## Gerber associations
+        self.grb_list_label = QtWidgets.QLabel("<b>%s:</b>" % _("Extensions list"))
+        self.grb_list_label.setToolTip(
+            _("List of file extensions to be\n"
+              "associated with FlatCAM.")
+        )
+        self.layout.addWidget(self.grb_list_label)
+
+        qsettings = QSettings("Open Source", "FlatCAM")
+        if qsettings.contains("textbox_font_size"):
+            tb_fsize = qsettings.value('textbox_font_size', type=int)
+        else:
+            tb_fsize = 10
+
+        self.grb_list_text = FCTextArea()
+        self.grb_list_text.setReadOnly(True)
+        # self.grb_list_text.sizeHint(custom_sizehint=150)
+        self.layout.addWidget(self.grb_list_text)
+        font = QtGui.QFont()
+        font.setPointSize(tb_fsize)
+        self.grb_list_text.setFont(font)
+
+        self.ext_label = QtWidgets.QLabel('%s:' % _("Extension"))
+        self.ext_label.setToolTip(_("A file extension to be added or deleted to the list."))
+        self.ext_entry = FCEntry()
+
+        hlay1 = QtWidgets.QHBoxLayout()
+        self.layout.addLayout(hlay1)
+        hlay1.addWidget(self.ext_label)
+        hlay1.addWidget(self.ext_entry)
+
+        self.add_btn = FCButton(_("Add Extension"))
+        self.add_btn.setToolTip(_("Add a file extension to the list"))
+        self.del_btn = FCButton(_("Delete Extension"))
+        self.del_btn.setToolTip(_("Delete a file extension from the list"))
+
+        hlay2 = QtWidgets.QHBoxLayout()
+        self.layout.addLayout(hlay2)
+        hlay2.addWidget(self.add_btn)
+        hlay2.addWidget(self.del_btn)
+
+        self.grb_list_btn = FCButton(_("Apply Association"))
+        self.grb_list_btn.setToolTip(_("Apply the file associations between\n"
+                                       "FlatCAM and the files with above extensions.\n"
+                                       "They will be active after next logon.\n"
+                                       "This work only in Windows."))
+
+        self.layout.addWidget(self.grb_list_btn)
+
+        # self.layout.addStretch()

+ 37 - 0
flatcamGUI/preferences/utilities/UtilPreferencesUI.py

@@ -0,0 +1,37 @@
+from PyQt5 import QtWidgets
+
+from flatcamGUI.preferences.utilities.AutoCompletePrefGroupUI import AutoCompletePrefGroupUI
+from flatcamGUI.preferences.utilities.FAGrbPrefGroupUI import FAGrbPrefGroupUI
+from flatcamGUI.preferences.utilities.FAGcoPrefGroupUI import FAGcoPrefGroupUI
+from flatcamGUI.preferences.utilities.FAExcPrefGroupUI import FAExcPrefGroupUI
+
+
+class UtilPreferencesUI(QtWidgets.QWidget):
+
+    def __init__(self, decimals, parent=None):
+        QtWidgets.QWidget.__init__(self, parent=parent)
+        self.layout = QtWidgets.QHBoxLayout()
+        self.setLayout(self.layout)
+        self.decimals = decimals
+
+        self.vlay = QtWidgets.QVBoxLayout()
+        self.fa_excellon_group = FAExcPrefGroupUI(decimals=self.decimals)
+        self.fa_excellon_group.setMinimumWidth(260)
+
+        self.fa_gcode_group = FAGcoPrefGroupUI(decimals=self.decimals)
+        self.fa_gcode_group.setMinimumWidth(260)
+
+        self.vlay.addWidget(self.fa_excellon_group)
+        self.vlay.addWidget(self.fa_gcode_group)
+
+        self.fa_gerber_group = FAGrbPrefGroupUI(decimals=self.decimals)
+        self.fa_gerber_group.setMinimumWidth(260)
+
+        self.kw_group = AutoCompletePrefGroupUI(decimals=self.decimals)
+        self.kw_group.setMinimumWidth(260)
+
+        self.layout.addLayout(self.vlay)
+        self.layout.addWidget(self.fa_gerber_group)
+        self.layout.addWidget(self.kw_group)
+
+        self.layout.addStretch()

+ 0 - 0
flatcamGUI/preferences/utilities/__init__.py


+ 1 - 1
flatcamObjects/FlatCAMCNCJob.py

@@ -364,7 +364,7 @@ class CNCJobObject(FlatCAMObj, CNCjob):
         self.units_found = self.app.defaults['units']
 
         # this signal has to be connected to it's slot before the defaults are populated
-        # the decision done in the slot has to override the default value set bellow
+        # the decision done in the slot has to override the default value set below
         self.ui.toolchange_cb.toggled.connect(self.on_toolchange_custom_clicked)
 
         self.form_fields.update({

+ 268 - 1
flatcamObjects/FlatCAMGeometry.py

@@ -16,6 +16,7 @@ import shapely.affinity as affinity
 from camlib import Geometry
 
 from flatcamObjects.FlatCAMObj import *
+import FlatCAMTool
 
 import ezdxf
 import math
@@ -68,6 +69,10 @@ class GeometryObject(FlatCAMObj, Geometry):
             "extracut_length": 0.1,
             "endz": 2.0,
             "endxy": '',
+            "area_exclusion": False,
+            "area_shape": "polygon",
+            "area_strategy": "over",
+            "area_overz": 1.0,
 
             "startz": None,
             "toolchange": False,
@@ -146,6 +151,18 @@ class GeometryObject(FlatCAMObj, Geometry):
 
         self.param_fields = {}
 
+        # Event signals disconnect id holders
+        self.mr = None
+        self.mm = None
+        self.kp = None
+
+        # variables to be used in area exclusion
+        self.cursor_pos = (0, 0)
+        self.exclusion_areas_list = []
+        self.first_click = False
+        self.points = []
+        self.poly_drawn = False
+
         # Attributes to be included in serialization
         # Always append to it because it carries contents
         # from predecessors.
@@ -344,7 +361,11 @@ class GeometryObject(FlatCAMObj, Geometry):
             "toolchangez": self.ui.toolchangez_entry,
             "endz": self.ui.endz_entry,
             "endxy": self.ui.endxy_entry,
-            "cnctooldia": self.ui.addtool_entry
+            "cnctooldia": self.ui.addtool_entry,
+            "area_exclusion": self.ui.exclusion_cb,
+            "area_shape":self.ui.area_shape_radio,
+            "area_strategy": self.ui.strategy_radio,
+            "area_overz": self.ui.over_z_entry,
         })
 
         self.param_fields.update({
@@ -402,6 +423,10 @@ class GeometryObject(FlatCAMObj, Geometry):
             "toolchangez": None,
             "endz": None,
             "endxy": '',
+            "area_exclusion": None,
+            "area_shape": None,
+            "area_strategy": None,
+            "area_overz": None,
             "spindlespeed": 0,
             "toolchangexy": None,
             "startz": None
@@ -522,6 +547,9 @@ class GeometryObject(FlatCAMObj, Geometry):
         self.ui.apply_param_to_all.clicked.connect(self.on_apply_param_to_all_clicked)
         self.ui.cutz_entry.returnPressed.connect(self.on_cut_z_changed)
 
+        self.ui.add_area_button.clicked.connect(self.on_add_area_click)
+        self.ui.delete_area_button.clicked.connect(self.on_clear_area_click)
+
     def on_cut_z_changed(self):
         self.old_cutz = self.ui.cutz_entry.get_value()
 
@@ -1114,6 +1142,26 @@ class GeometryObject(FlatCAMObj, Geometry):
             self.ui.tipanglelabel.show()
             self.ui.tipangle_entry.show()
             self.ui.cutz_entry.setDisabled(True)
+            self.ui.cutzlabel.setToolTip(
+                _("Disabled because the tool is V-shape.\n"
+                  "For V-shape tools the depth of cut is\n"
+                  "calculated from other parameters like:\n"
+                  "- 'V-tip Angle' -> angle at the tip of the tool\n"
+                  "- 'V-tip Dia' -> diameter at the tip of the tool \n"
+                  "- Tool Dia -> 'Dia' column found in the Tool Table\n"
+                  "NB: a value of zero means that Tool Dia = 'V-tip Dia'"
+                )
+            )
+            self.ui.cutz_entry.setToolTip(
+                _("Disabled because the tool is V-shape.\n"
+                  "For V-shape tools the depth of cut is\n"
+                  "calculated from other parameters like:\n"
+                  "- 'V-tip Angle' -> angle at the tip of the tool\n"
+                  "- 'V-tip Dia' -> diameter at the tip of the tool \n"
+                  "- Tool Dia -> 'Dia' column found in the Tool Table\n"
+                  "NB: a value of zero means that Tool Dia = 'V-tip Dia'"
+                  )
+            )
 
             self.update_cutz()
         else:
@@ -1122,6 +1170,12 @@ class GeometryObject(FlatCAMObj, Geometry):
             self.ui.tipanglelabel.hide()
             self.ui.tipangle_entry.hide()
             self.ui.cutz_entry.setDisabled(False)
+            self.ui.cutzlabel.setToolTip(
+                _("Cutting depth (negative)\n"
+                  "below the copper surface."
+                )
+            )
+            self.ui.cutz_entry.setToolTip('')
 
     def update_cutz(self):
         vdia = float(self.ui.tipdia_entry.get_value())
@@ -2519,6 +2573,219 @@ class GeometryObject(FlatCAMObj, Geometry):
             self.ui.plot_cb.setChecked(True)
         self.ui_connect()
 
+    def on_add_area_click(self):
+        self.app.inform.emit('[WARNING_NOTCL] %s' % _("Click the start point of the area."))
+        self.app.call_source = 'geometry'
+
+        if self.app.is_legacy is False:
+            self.app.plotcanvas.graph_event_disconnect('mouse_press', self.app.on_mouse_click_over_plot)
+            self.app.plotcanvas.graph_event_disconnect('mouse_move', self.app.on_mouse_move_over_plot)
+            self.app.plotcanvas.graph_event_disconnect('mouse_release', self.app.on_mouse_click_release_over_plot)
+        else:
+            self.app.plotcanvas.graph_event_disconnect(self.app.mp)
+            self.app.plotcanvas.graph_event_disconnect(self.app.mm)
+            self.app.plotcanvas.graph_event_disconnect(self.app.mr)
+
+        self.mr = self.app.plotcanvas.graph_event_connect('mouse_release', self.on_mouse_release)
+        self.mm = self.app.plotcanvas.graph_event_connect('mouse_move', self.on_mouse_move)
+        # self.kp = self.app.plotcanvas.graph_event_connect('key_press', self.on_key_press)
+
+    # To be called after clicking on the plot.
+    def on_mouse_release(self, event):
+        if self.app.is_legacy is False:
+            event_pos = event.pos
+            # event_is_dragging = event.is_dragging
+            right_button = 2
+        else:
+            event_pos = (event.xdata, event.ydata)
+            # event_is_dragging = self.app.plotcanvas.is_dragging
+            right_button = 3
+
+        event_pos = self.app.plotcanvas.translate_coords(event_pos)
+        if self.app.grid_status():
+            curr_pos = self.app.geo_editor.snap(event_pos[0], event_pos[1])
+        else:
+            curr_pos = (event_pos[0], event_pos[1])
+
+        x1, y1 = curr_pos[0], curr_pos[1]
+
+        shape_type = self.ui.area_shape_radio.get_value()
+
+        # do clear area only for left mouse clicks
+        if event.button == 1:
+            if shape_type == "square":
+                if self.first_click is False:
+                    self.first_click = True
+                    self.app.inform.emit('[WARNING_NOTCL] %s' % _("Click the end point of the area."))
+
+                    self.cursor_pos = self.app.plotcanvas.translate_coords(event_pos)
+                    if self.app.grid_status():
+                        self.cursor_pos = self.app.geo_editor.snap(event_pos[0], event_pos[1])
+                else:
+                    self.app.inform.emit(_("Zone added. Click to start adding next zone or right click to finish."))
+                    self.app.delete_selection_shape()
+
+                    x0, y0 = self.cursor_pos[0], self.cursor_pos[1]
+
+                    pt1 = (x0, y0)
+                    pt2 = (x1, y0)
+                    pt3 = (x1, y1)
+                    pt4 = (x0, y1)
+
+                    new_rectangle = Polygon([pt1, pt2, pt3, pt4])
+                    self.exclusion_areas_list.append(new_rectangle)
+
+                    # add a temporary shape on canvas
+                    FlatCAMTool.FlatCAMTool.draw_tool_selection_shape(self, old_coords=(x0, y0), coords=(x1, y1))
+
+                    self.first_click = False
+                    return
+            else:
+                self.points.append((x1, y1))
+
+                if len(self.points) > 1:
+                    self.poly_drawn = True
+                    self.app.inform.emit(_("Click on next Point or click right mouse button to complete ..."))
+
+                return ""
+        elif event.button == right_button and self.mouse_is_dragging is False:
+
+            shape_type = self.ui.area_shape_radio.get_value()
+
+            if shape_type == "square":
+                self.first_click = False
+            else:
+                # if we finish to add a polygon
+                if self.poly_drawn is True:
+                    try:
+                        # try to add the point where we last clicked if it is not already in the self.points
+                        last_pt = (x1, y1)
+                        if last_pt != self.points[-1]:
+                            self.points.append(last_pt)
+                    except IndexError:
+                        pass
+
+                    # we need to add a Polygon and a Polygon can be made only from at least 3 points
+                    if len(self.points) > 2:
+                        FlatCAMTool.FlatCAMTool.delete_moving_selection_shape(self)
+                        pol = Polygon(self.points)
+                        # do not add invalid polygons even if they are drawn by utility geometry
+                        if pol.is_valid:
+                            self.exclusion_areas_list.append(pol)
+                            FlatCAMTool.FlatCAMTool.draw_selection_shape_polygon(self, points=self.points)
+                            self.app.inform.emit(
+                                _("Zone added. Click to start adding next zone or right click to finish."))
+
+                    self.points = []
+                    self.poly_drawn = False
+                    return
+
+            FlatCAMTool.FlatCAMTool.delete_tool_selection_shape(self)
+
+            if self.app.is_legacy is False:
+                self.app.plotcanvas.graph_event_disconnect('mouse_release', self.on_mouse_release)
+                self.app.plotcanvas.graph_event_disconnect('mouse_move', self.on_mouse_move)
+                # self.app.plotcanvas.graph_event_disconnect('key_press', self.on_key_press)
+            else:
+                self.app.plotcanvas.graph_event_disconnect(self.mr)
+                self.app.plotcanvas.graph_event_disconnect(self.mm)
+                # self.app.plotcanvas.graph_event_disconnect(self.kp)
+
+            self.app.mp = self.app.plotcanvas.graph_event_connect('mouse_press',
+                                                                  self.app.on_mouse_click_over_plot)
+            self.app.mm = self.app.plotcanvas.graph_event_connect('mouse_move',
+                                                                  self.app.on_mouse_move_over_plot)
+            self.app.mr = self.app.plotcanvas.graph_event_connect('mouse_release',
+                                                                  self.app.on_mouse_click_release_over_plot)
+
+            self.app.call_source = 'app'
+
+            if len(self.exclusion_areas_list) == 0:
+                return
+
+    def area_disconnect(self):
+        if self.app.is_legacy is False:
+            self.app.plotcanvas.graph_event_disconnect('mouse_release', self.on_mouse_release)
+            self.app.plotcanvas.graph_event_disconnect('mouse_move', self.on_mouse_move)
+        else:
+            self.app.plotcanvas.graph_event_disconnect(self.mr)
+            self.app.plotcanvas.graph_event_disconnect(self.mm)
+            self.app.plotcanvas.graph_event_disconnect(self.kp)
+
+        self.app.mp = self.app.plotcanvas.graph_event_connect('mouse_press',
+                                                              self.app.on_mouse_click_over_plot)
+        self.app.mm = self.app.plotcanvas.graph_event_connect('mouse_move',
+                                                              self.app.on_mouse_move_over_plot)
+        self.app.mr = self.app.plotcanvas.graph_event_connect('mouse_release',
+                                                              self.app.on_mouse_click_release_over_plot)
+        self.points = []
+        self.poly_drawn = False
+        self.exclusion_areas_list = []
+
+        FlatCAMTool.FlatCAMTool.delete_moving_selection_shape(self)
+        FlatCAMTool.FlatCAMTool.delete_tool_selection_shape(self)
+
+        self.app.call_source = "app"
+        self.app.inform.emit("[WARNING_NOTCL] %s" % _("Cancelled. Area exclusion drawing was interrupted."))
+
+    # called on mouse move
+    def on_mouse_move(self, event):
+        shape_type = self.ui.area_shape_radio.get_value()
+
+        if self.app.is_legacy is False:
+            event_pos = event.pos
+            event_is_dragging = event.is_dragging
+            # right_button = 2
+        else:
+            event_pos = (event.xdata, event.ydata)
+            event_is_dragging = self.app.plotcanvas.is_dragging
+            # right_button = 3
+
+        curr_pos = self.app.plotcanvas.translate_coords(event_pos)
+
+        # detect mouse dragging motion
+        if event_is_dragging is True:
+            self.mouse_is_dragging = True
+        else:
+            self.mouse_is_dragging = False
+
+        # update the cursor position
+        if self.app.grid_status():
+            # Update cursor
+            curr_pos = self.app.geo_editor.snap(curr_pos[0], curr_pos[1])
+
+            self.app.app_cursor.set_data(np.asarray([(curr_pos[0], curr_pos[1])]),
+                                         symbol='++', edge_color=self.app.cursor_color_3D,
+                                         edge_width=self.app.defaults["global_cursor_width"],
+                                         size=self.app.defaults["global_cursor_size"])
+
+        # update the positions on status bar
+        self.app.ui.position_label.setText("&nbsp;&nbsp;&nbsp;&nbsp;<b>X</b>: %.4f&nbsp;&nbsp;   "
+                                           "<b>Y</b>: %.4f" % (curr_pos[0], curr_pos[1]))
+        if self.cursor_pos is None:
+            self.cursor_pos = (0, 0)
+
+        self.app.dx = curr_pos[0] - float(self.cursor_pos[0])
+        self.app.dy = curr_pos[1] - float(self.cursor_pos[1])
+        self.app.ui.rel_position_label.setText("<b>Dx</b>: %.4f&nbsp;&nbsp;  <b>Dy</b>: "
+                                               "%.4f&nbsp;&nbsp;&nbsp;&nbsp;" % (self.app.dx, self.app.dy))
+
+        # draw the utility geometry
+        if shape_type == "square":
+            if self.first_click:
+                self.app.delete_selection_shape()
+                self.app.draw_moving_selection_shape(old_coords=(self.cursor_pos[0], self.cursor_pos[1]),
+                                                     coords=(curr_pos[0], curr_pos[1]))
+        else:
+            FlatCAMTool.FlatCAMTool.delete_moving_selection_shape(self)
+            FlatCAMTool.FlatCAMTool.draw_moving_selection_shape_poly(
+                self, points=self.points, data=(curr_pos[0], curr_pos[1]))
+
+    def on_clear_area_click(self):
+        self.exclusion_areas_list = []
+        FlatCAMTool.FlatCAMTool.delete_moving_selection_shape(self)
+        self.app.delete_selection_shape()
+
     @staticmethod
     def merge(geo_list, geo_final, multigeo=None):
         """

+ 0 - 2
flatcamObjects/FlatCAMGerber.py

@@ -1460,8 +1460,6 @@ class GerberObject(FlatCAMObj, Gerber):
         self.ui_disconnect()
         try:
             cw = self.sender()
-            assert isinstance(cw, FCCheckBox),\
-                "Expected a cellWidget but got %s" % type(cw)
             cw_index = self.ui.apertures_table.indexAt(cw.pos())
             cw_row = cw_index.row()
         except AttributeError:

+ 33 - 6
flatcamObjects/FlatCAMScript.py

@@ -50,15 +50,14 @@ class ScriptObject(FlatCAMObj):
 
         self.units = ''
 
+        self.script_editor_tab = None
+
         self.ser_attrs = ['options', 'kind', 'source_file']
         self.source_file = ''
         self.script_code = ''
 
         self.units_found = self.app.defaults['units']
 
-        # self.script_editor_tab = TextEditor(app=self.app, plain_text=True)
-        self.script_editor_tab = TextEditor(app=self.app, plain_text=True)
-
     def set_ui(self, ui):
         """
         Sets the Object UI in Selected Tab for the FlatCAM Script type of object.
@@ -87,6 +86,8 @@ class ScriptObject(FlatCAMObj):
                 '<span style="color:red;"><b>Advanced</b></span>'
             ))
 
+        self.script_editor_tab = TextEditor(app=self.app, plain_text=True, parent=self.app.ui)
+
         # tab_here = False
         # # try to not add too many times a tab that it is already installed
         # for idx in range(self.app.ui.plot_tab_area.count()):
@@ -99,8 +100,8 @@ class ScriptObject(FlatCAMObj):
         #     self.app.ui.plot_tab_area.addTab(self.script_editor_tab, '%s' % _("Script Editor"))
         #     self.script_editor_tab.setObjectName(self.options['name'])
 
-        self.app.ui.plot_tab_area.addTab(self.script_editor_tab, '%s' % _("Script Editor"))
-        self.script_editor_tab.setObjectName(self.options['name'])
+        # self.app.ui.plot_tab_area.addTab(self.script_editor_tab, '%s' % _("Script Editor"))
+        # self.script_editor_tab.setObjectName(self.options['name'])
 
         # first clear previous text in text editor (if any)
         # self.script_editor_tab.code_editor.clear()
@@ -111,7 +112,7 @@ class ScriptObject(FlatCAMObj):
 
         self.script_editor_tab.buttonRun.show()
 
-        # Switch plot_area to CNCJob tab
+        # Switch plot_area to Script Editor tab
         self.app.ui.plot_tab_area.setCurrentWidget(self.script_editor_tab)
 
         flt = "FlatCAM Scripts (*.FlatScript);;All Files (*.*)"
@@ -150,6 +151,32 @@ class ScriptObject(FlatCAMObj):
     def build_ui(self):
         FlatCAMObj.build_ui(self)
 
+        tab_here = False
+        # try to not add too many times a tab that it is already installed
+        for idx in range(self.app.ui.plot_tab_area.count()):
+            if self.app.ui.plot_tab_area.widget(idx).objectName() == self.options['name']:
+                tab_here = True
+                break
+
+        # add the tab if it is not already added
+        if tab_here is False:
+            self.app.ui.plot_tab_area.addTab(self.script_editor_tab, '%s' % _("Script Editor"))
+            self.script_editor_tab.setObjectName(self.options['name'])
+            self.app.ui.plot_tab_area.setCurrentWidget(self.script_editor_tab)
+
+    def parse_file(self, filename):
+        """
+        Will set an attribute of the object, self.source_file, with the parsed data.
+
+        :param filename:    Tcl Script file to parse
+        :return:            None
+        """
+        with open(filename, "r") as opened_script:
+            script_content = opened_script.readlines()
+            script_content = ''.join(script_content)
+
+        self.source_file = script_content
+
     def handle_run_code(self):
         # trying to run a Tcl command without having the Shell open will create some warnings because the Tcl Shell
         # tries to print on a hidden widget, therefore show the dock if hidden

+ 1 - 1
flatcamParsers/ParseExcellon.py

@@ -426,7 +426,7 @@ class Excellon(Geometry):
                                     # it's possible that tool definition has only tool number and no diameter info
                                     # (those could be in another file like PCB Wizard do)
                                     # then match.group(2) = None and float(None) will create the exception
-                                    # the bellow construction is so each tool will have a slightly different diameter
+                                    # the below construction is so each tool will have a slightly different diameter
                                     # starting with a default value, to allow Excellon editing after that
                                     self.diameterless = True
                                     self.app.inform.emit('[WARNING] %s%s %s' %

+ 4 - 0
flatcamParsers/ParseHPGL2.py

@@ -73,6 +73,10 @@ class HPGL2:
             "toolchangez": self.app.defaults["geometry_toolchangez"],
             "endz": self.app.defaults["geometry_endz"],
             "endxy": self.app.defaults["geometry_endxy"],
+            "area_exclusion": self.app.defaults["geometry_area_exclusion"],
+            "area_shape": self.app.defaults["geometry_area_shape"],
+            "area_strategy": self.app.defaults["geometry_area_strategy"],
+            "area_overz": self.app.defaults["geometry_area_overz"],
 
             "spindlespeed": self.app.defaults["geometry_spindlespeed"],
             "toolchangexy": self.app.defaults["geometry_toolchangexy"],

+ 1 - 1
flatcamTools/ToolCalculators.py

@@ -156,7 +156,7 @@ class ToolCalculator(FlatCAMTool):
         plate_title_label = QtWidgets.QLabel("<font size=3><b>%s</b></font>" % self.eplateName)
         plate_title_label.setToolTip(
             _("This calculator is useful for those who plate the via/pad/drill holes,\n"
-              "using a method like grahite ink or calcium hypophosphite ink or palladium chloride.")
+              "using a method like graphite ink or calcium hypophosphite ink or palladium chloride.")
         )
         self.layout.addWidget(plate_title_label)
 

+ 114 - 21
flatcamTools/ToolCutOut.py

@@ -241,10 +241,6 @@ class CutOut(FlatCAMTool):
         )
         grid0.addWidget(title_param_label, 18, 0, 1, 2)
 
-        # Form Layout
-        form_layout_2 = QtWidgets.QFormLayout()
-        grid0.addLayout(form_layout_2, 19, 0, 1, 2)
-
         # Gaps
         gaps_label = QtWidgets.QLabel('%s:' % _('Gaps'))
         gaps_label.setToolTip(
@@ -266,7 +262,8 @@ class CutOut(FlatCAMTool):
         for it in gaps_items:
             self.gaps.addItem(it)
             self.gaps.setStyleSheet('background-color: rgb(255,255,255)')
-        form_layout_2.addRow(gaps_label, self.gaps)
+        grid0.addWidget(gaps_label, 19, 0)
+        grid0.addWidget(self.gaps, 19, 1)
 
         # Buttons
         self.ff_cutout_object_btn = FCButton(_("Generate Freeform Geometry"))
@@ -296,12 +293,12 @@ class CutOut(FlatCAMTool):
                             font-weight: bold;
                         }
                         """)
-        self.layout.addWidget(self.rect_cutout_object_btn)
+        grid0.addWidget(self.rect_cutout_object_btn, 21, 0, 1, 2)
 
         separator_line = QtWidgets.QFrame()
         separator_line.setFrameShape(QtWidgets.QFrame.HLine)
         separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
-        grid0.addWidget(separator_line, 21, 0, 1, 2)
+        grid0.addWidget(separator_line, 22, 0, 1, 2)
 
         # Title5
         title_manual_label = QtWidgets.QLabel("<font size=4><b>%s</b></font>" % _('B. Manual Bridge Gaps'))
@@ -310,11 +307,7 @@ class CutOut(FlatCAMTool):
               "This is done by mouse clicking on the perimeter of the\n"
               "Geometry object that is used as a cutout object. ")
         )
-        grid0.addWidget(title_manual_label, 22, 0, 1, 2)
-
-        # Form Layout
-        form_layout_3 = QtWidgets.QFormLayout()
-        grid0.addLayout(form_layout_3, 23, 0, 1, 2)
+        grid0.addWidget(title_manual_label, 23, 0, 1, 2)
 
         # Manual Geo Object
         self.man_object_combo = FCComboBox()
@@ -329,10 +322,8 @@ class CutOut(FlatCAMTool):
         )
         # self.man_object_label.setMinimumWidth(60)
 
-        form_layout_3.addRow(self.man_object_label)
-        form_layout_3.addRow(self.man_object_combo)
-
-        # form_layout_3.addRow(e_lab_0)
+        grid0.addWidget(self.man_object_label, 25, 0, 1, 2)
+        grid0.addWidget(self.man_object_combo, 26, 0, 1, 2)
 
         self.man_geo_creation_btn = FCButton(_("Generate Manual Geometry"))
         self.man_geo_creation_btn.setToolTip(
@@ -347,7 +338,7 @@ class CutOut(FlatCAMTool):
                             font-weight: bold;
                         }
                         """)
-        grid0.addWidget(self.man_geo_creation_btn, 24, 0, 1, 2)
+        grid0.addWidget(self.man_geo_creation_btn, 28, 0, 1, 2)
 
         self.man_gaps_creation_btn = FCButton(_("Manual Add Bridge Gaps"))
         self.man_gaps_creation_btn.setToolTip(
@@ -363,7 +354,7 @@ class CutOut(FlatCAMTool):
                             font-weight: bold;
                         }
                         """)
-        grid0.addWidget(self.man_gaps_creation_btn, 27, 0, 1, 2)
+        grid0.addWidget(self.man_gaps_creation_btn, 30, 0, 1, 2)
 
         self.layout.addStretch()
 
@@ -403,6 +394,9 @@ class CutOut(FlatCAMTool):
         self.x_pos = None
         self.y_pos = None
 
+        # store the default data for the resulting Geometry Object
+        self.default_data = {}
+
         # Signals
         self.ff_cutout_object_btn.clicked.connect(self.on_freeform_cutout)
         self.rect_cutout_object_btn.clicked.connect(self.on_rectangular_cutout)
@@ -463,12 +457,58 @@ class CutOut(FlatCAMTool):
         self.convex_box.set_value(self.app.defaults['tools_cutout_convexshape'])
         self.type_obj_radio.set_value('grb')
 
+        self.default_data.update({
+            "plot":             True,
+            "cutz":             float(self.app.defaults["geometry_cutz"]),
+            "multidepth":       self.app.defaults["geometry_multidepth"],
+            "depthperpass":     float(self.app.defaults["geometry_depthperpass"]),
+            "vtipdia":          float(self.app.defaults["geometry_vtipdia"]),
+            "vtipangle":        float(self.app.defaults["geometry_vtipangle"]),
+            "travelz":          float(self.app.defaults["geometry_travelz"]),
+            "feedrate":         float(self.app.defaults["geometry_feedrate"]),
+            "feedrate_z":       float(self.app.defaults["geometry_feedrate_z"]),
+            "feedrate_rapid":   float(self.app.defaults["geometry_feedrate_rapid"]),
+            "spindlespeed":     self.app.defaults["geometry_spindlespeed"],
+            "dwell":            self.app.defaults["geometry_dwell"],
+            "dwelltime":        float(self.app.defaults["geometry_dwelltime"]),
+            "ppname_g":         self.app.defaults["geometry_ppname_g"],
+            "extracut":         self.app.defaults["geometry_extracut"],
+            "extracut_length":  float(self.app.defaults["geometry_extracut_length"]),
+            "toolchange":       self.app.defaults["geometry_toolchange"],
+            "toolchangexy":     self.app.defaults["geometry_toolchangexy"],
+            "toolchangez":      float(self.app.defaults["geometry_toolchangez"]),
+            "startz":           self.app.defaults["geometry_startz"],
+            "endz":             float(self.app.defaults["geometry_endz"]),
+            "area_exclusion":   self.app.defaults["geometry_area_exclusion"],
+            "area_shape":       self.app.defaults["geometry_area_shape"],
+            "area_strategy":    self.app.defaults["geometry_area_strategy"],
+            "area_overz":       float(self.app.defaults["geometry_area_overz"]),
+
+            # NCC
+            "tools_nccoperation":       self.app.defaults["tools_nccoperation"],
+            "tools_nccmilling_type":    self.app.defaults["tools_nccmilling_type"],
+            "tools_nccoverlap":         float(self.app.defaults["tools_nccoverlap"]),
+            "tools_nccmargin":          float(self.app.defaults["tools_nccmargin"]),
+            "tools_nccmethod":          self.app.defaults["tools_nccmethod"],
+            "tools_nccconnect":         self.app.defaults["tools_nccconnect"],
+            "tools_ncccontour":         self.app.defaults["tools_ncccontour"],
+            "tools_ncc_offset_choice":  self.app.defaults["tools_ncc_offset_choice"],
+            "tools_ncc_offset_value":   float(self.app.defaults["tools_ncc_offset_value"]),
+
+            # Paint
+            "tools_paintoverlap":       float(self.app.defaults["tools_paintoverlap"]),
+            "tools_paintmargin":        float(self.app.defaults["tools_paintmargin"]),
+            "tools_paintmethod":        self.app.defaults["tools_paintmethod"],
+            "tools_pathconnect":        self.app.defaults["tools_pathconnect"],
+            "tools_paintcontour":       self.app.defaults["tools_paintcontour"],
+        })
+
     def on_freeform_cutout(self):
+        log.debug("Cutout.on_freeform_cutout() was launched ...")
 
         # def subtract_rectangle(obj_, x0, y0, x1, y1):
         #     pts = [(x0, y0), (x1, y0), (x1, y1), (x0, y1)]
         #     obj_.subtract_polygon(pts)
-
         name = self.obj_combo.currentText()
 
         # Get source object.
@@ -631,8 +671,8 @@ class CutOut(FlatCAMTool):
 
                     solid_geo += cutout_handler(geom=geom_struct)
 
-            geo_obj.solid_geometry = deepcopy(solid_geo)
             xmin, ymin, xmax, ymax = recursive_bounds(geo_obj.solid_geometry)
+            geo_obj.solid_geometry = deepcopy(solid_geo)
             geo_obj.options['xmin'] = xmin
             geo_obj.options['ymin'] = ymin
             geo_obj.options['xmax'] = xmax
@@ -642,6 +682,23 @@ class CutOut(FlatCAMTool):
             geo_obj.options['multidepth'] = self.mpass_cb.get_value()
             geo_obj.options['depthperpass'] = self.maxdepth_entry.get_value()
 
+            geo_obj.tools.update({
+                1: {
+                    'tooldia': str(dia),
+                    'offset': 'Path',
+                    'offset_value': 0.0,
+                    'type': _('Rough'),
+                    'tool_type': 'C1',
+                    'data': self.default_data,
+                    'solid_geometry': geo_obj.solid_geometry
+                }
+            })
+            geo_obj.multigeo = True
+            geo_obj.tools[1]['data']['name'] = outname
+            geo_obj.tools[1]['data']['cutz'] = self.cutz_entry.get_value()
+            geo_obj.tools[1]['data']['multidepth'] = self.mpass_cb.get_value()
+            geo_obj.tools[1]['data']['depthperpass'] = self.maxdepth_entry.get_value()
+
         outname = cutout_obj.options["name"] + "_cutout"
         self.app.new_object('geometry', outname, geo_init)
 
@@ -651,6 +708,7 @@ class CutOut(FlatCAMTool):
         self.app.should_we_save = True
 
     def on_rectangular_cutout(self):
+        log.debug("Cutout.on_rectangular_cutout() was launched ...")
 
         # def subtract_rectangle(obj_, x0, y0, x1, y1):
         #     pts = [(x0, y0), (x1, y0), (x1, y1), (x0, y1)]
@@ -767,6 +825,7 @@ class CutOut(FlatCAMTool):
                 return proc_geometry
 
             if kind == 'single':
+                # fuse the lines
                 object_geo = unary_union(object_geo)
 
                 xmin, ymin, xmax, ymax = object_geo.bounds
@@ -813,11 +872,28 @@ class CutOut(FlatCAMTool):
                                          _("Rectangular cutout with negative margin is not possible."))
                     return "fail"
 
-            geo_obj.solid_geometry = deepcopy(solid_geo)
             geo_obj.options['cnctooldia'] = str(dia)
             geo_obj.options['cutz'] = self.cutz_entry.get_value()
             geo_obj.options['multidepth'] = self.mpass_cb.get_value()
             geo_obj.options['depthperpass'] = self.maxdepth_entry.get_value()
+            geo_obj.solid_geometry = deepcopy(solid_geo)
+
+            geo_obj.tools.update({
+                1: {
+                    'tooldia': str(dia),
+                    'offset': 'Path',
+                    'offset_value': 0.0,
+                    'type': _('Rough'),
+                    'tool_type': 'C1',
+                    'data': self.default_data,
+                    'solid_geometry': geo_obj.solid_geometry
+                }
+            })
+            geo_obj.multigeo = True
+            geo_obj.tools[1]['data']['name'] = outname
+            geo_obj.tools[1]['data']['cutz'] = self.cutz_entry.get_value()
+            geo_obj.tools[1]['data']['multidepth'] = self.mpass_cb.get_value()
+            geo_obj.tools[1]['data']['depthperpass'] = self.maxdepth_entry.get_value()
 
         outname = cutout_obj.options["name"] + "_cutout"
         ret = self.app.new_object('geometry', outname, geo_init)
@@ -962,6 +1038,23 @@ class CutOut(FlatCAMTool):
             geo_obj.options['multidepth'] = self.mpass_cb.get_value()
             geo_obj.options['depthperpass'] = self.maxdepth_entry.get_value()
 
+            geo_obj.tools.update({
+                1: {
+                    'tooldia': str(dia),
+                    'offset': 'Path',
+                    'offset_value': 0.0,
+                    'type': _('Rough'),
+                    'tool_type': 'C1',
+                    'data': self.default_data,
+                    'solid_geometry': geo_obj.solid_geometry
+                }
+            })
+            geo_obj.multigeo = True
+            geo_obj.tools[1]['data']['name'] = outname
+            geo_obj.tools[1]['data']['cutz'] = self.cutz_entry.get_value()
+            geo_obj.tools[1]['data']['multidepth'] = self.mpass_cb.get_value()
+            geo_obj.tools[1]['data']['depthperpass'] = self.maxdepth_entry.get_value()
+
         outname = cutout_obj.options["name"] + "_cutout"
         self.app.new_object('geometry', outname, geo_init)
 

+ 1 - 1
flatcamTools/ToolFilm.py

@@ -128,7 +128,7 @@ class Film(FlatCAMTool):
 
         self.tf_box_combo_label = QtWidgets.QLabel('%s:' % _("Box Object"))
         self.tf_box_combo_label.setToolTip(
-            _("The actual object that is used a container for the\n "
+            _("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.")

+ 7 - 2
flatcamTools/ToolNCC.py

@@ -175,7 +175,7 @@ class NonCopperClear(FlatCAMTool, Gerber):
         self.ncc_order_label.setToolTip(_("This set the way that the tools in the tools table are used.\n"
                                           "'No' --> means that the used order is the one in the tool table\n"
                                           "'Forward' --> means that the tools will be ordered from small to big\n"
-                                          "'Reverse' --> menas that the tools will ordered from big to small\n\n"
+                                          "'Reverse' --> means that the tools will ordered from big to small\n\n"
                                           "WARNING: using rest machining will automatically set the order\n"
                                           "in reverse and disable this control."))
 
@@ -185,7 +185,7 @@ class NonCopperClear(FlatCAMTool, Gerber):
         self.ncc_order_radio.setToolTip(_("This set the way that the tools in the tools table are used.\n"
                                           "'No' --> means that the used order is the one in the tool table\n"
                                           "'Forward' --> means that the tools will be ordered from small to big\n"
-                                          "'Reverse' --> menas that the tools will ordered from big to small\n\n"
+                                          "'Reverse' --> means that the tools will ordered from big to small\n\n"
                                           "WARNING: using rest machining will automatically set the order\n"
                                           "in reverse and disable this control."))
 
@@ -1039,6 +1039,11 @@ class NonCopperClear(FlatCAMTool, Gerber):
             "toolchangexy": self.app.defaults["geometry_toolchangexy"],
             "startz": self.app.defaults["geometry_startz"],
 
+            "area_exclusion": self.app.defaults["geometry_area_exclusion"],
+            "area_shape": self.app.defaults["geometry_area_shape"],
+            "area_strategy": self.app.defaults["geometry_area_strategy"],
+            "area_overz": float(self.app.defaults["geometry_area_overz"]),
+
             "tools_nccoperation": self.app.defaults["tools_nccoperation"],
             "tools_nccmargin": self.app.defaults["tools_nccmargin"],
             "tools_nccmethod": self.app.defaults["tools_nccmethod"],

+ 7 - 2
flatcamTools/ToolPaint.py

@@ -159,7 +159,7 @@ class ToolPaint(FlatCAMTool, Gerber):
         self.order_label.setToolTip(_("This set the way that the tools in the tools table are used.\n"
                                       "'No' --> means that the used order is the one in the tool table\n"
                                       "'Forward' --> means that the tools will be ordered from small to big\n"
-                                      "'Reverse' --> menas that the tools will ordered from big to small\n\n"
+                                      "'Reverse' --> means that the tools will ordered from big to small\n\n"
                                       "WARNING: using rest machining will automatically set the order\n"
                                       "in reverse and disable this control."))
 
@@ -169,7 +169,7 @@ class ToolPaint(FlatCAMTool, Gerber):
         self.order_radio.setToolTip(_("This set the way that the tools in the tools table are used.\n"
                                       "'No' --> means that the used order is the one in the tool table\n"
                                       "'Forward' --> means that the tools will be ordered from small to big\n"
-                                      "'Reverse' --> menas that the tools will ordered from big to small\n\n"
+                                      "'Reverse' --> means that the tools will ordered from big to small\n\n"
                                       "WARNING: using rest machining will automatically set the order\n"
                                       "in reverse and disable this control."))
 
@@ -1013,6 +1013,11 @@ class ToolPaint(FlatCAMTool, Gerber):
             "toolchangexy": self.app.defaults["geometry_toolchangexy"],
             "startz": self.app.defaults["geometry_startz"],
 
+            "area_exclusion": self.app.defaults["geometry_area_exclusion"],
+            "area_shape": self.app.defaults["geometry_area_shape"],
+            "area_strategy": self.app.defaults["geometry_area_strategy"],
+            "area_overz": float(self.app.defaults["geometry_area_overz"]),
+
             "tooldia": self.app.defaults["tools_painttooldia"],
             "tools_paintmargin": self.app.defaults["tools_paintmargin"],
             "tools_paintmethod": self.app.defaults["tools_paintmethod"],

Algúns arquivos non se mostraron porque demasiados arquivos cambiaron neste cambio