소스 검색

- modified the Tools Database and Preferences with the new parameters from CutOut Tool
- changes in Tool Cutout: now on Cutout Tool start the app will look into Tools Database and search for a tool with same diameter (or within the set tolerance) as the one from Preferences and load it if found or load a default tool if not
- Tool Cutout - this Tool can now load tools from Tools Database through buttons in the Cutout Tool

Marius Stanciu 5 년 전
부모
커밋
6c9c367540

+ 3 - 0
CHANGELOG.md

@@ -14,6 +14,9 @@ CHANGELOG for FlatCAM beta
 - NCC Tool - upgraded the UI and added the functionality that now adding a new tool is done by first searching the Tools DB for a suitable tool and if fails then it adds an default tool
 - NCC Tool - on start will attempt to search in the Tools DB for the default tools and if found will load them from the DB
 - fixes in NCC, Paint and Isolation Tool due of recent changes
+- modified the Tools Database and Preferences with the new parameters from CutOut Tool
+- changes in Tool Cutout: now on Cutout Tool start the app will look into Tools Database and search for a tool with same diameter (or within the set tolerance) as the one from Preferences and load it if found or load a default tool if not
+- Tool Cutout - this Tool can now load tools from Tools Database through buttons in the Cutout Tool
 
 27.08.2020
 

+ 162 - 71
appDatabase.py

@@ -1,6 +1,6 @@
 from PyQt5 import QtGui, QtCore, QtWidgets
 from appGUI.GUIElements import FCTable, FCEntry, FCButton, FCDoubleSpinner, FCComboBox, FCCheckBox, FCSpinner, \
-    FCTree, RadioSet, FCFileSaveDialog
+    FCTree, RadioSet, FCFileSaveDialog, FCLabel
 from camlib import to_dict
 
 import sys
@@ -74,7 +74,7 @@ class ToolsDB(QtWidgets.QWidget):
         new_vlay = QtWidgets.QVBoxLayout()
         layout.addLayout(new_vlay)
 
-        # new_tool_lbl = QtWidgets.QLabel('<b>%s</b>' % _("New Tool"))
+        # new_tool_lbl = FCLabel('<b>%s</b>' % _("New Tool"))
         # new_vlay.addWidget(new_tool_lbl, alignment=QtCore.Qt.AlignBottom)
 
         self.buttons_frame = QtWidgets.QFrame()
@@ -1121,7 +1121,7 @@ class ToolsDB2UI:
         self.description_vlay.addStretch()
 
         # Tool Name
-        self.name_label = QtWidgets.QLabel('<span style="color:red;"><b>%s:</b></span>' % _('Name'))
+        self.name_label = FCLabel('<span style="color:red;"><b>%s:</b></span>' % _('Name'))
         self.name_label.setToolTip(
             _("Tool name.\n"
               "This is not used in the app, it's function\n"
@@ -1134,7 +1134,7 @@ class ToolsDB2UI:
         self.grid_tool.addWidget(self.name_entry, 0, 1)
 
         # Tool Dia
-        self.dia_label = QtWidgets.QLabel('%s:' % _('Diameter'))
+        self.dia_label = FCLabel('%s:' % _('Diameter'))
         self.dia_label.setToolTip(
             _("Tool Diameter."))
 
@@ -1147,7 +1147,7 @@ class ToolsDB2UI:
         self.grid_tool.addWidget(self.dia_entry, 1, 1)
 
         # Tool Tolerance
-        self.tol_label = QtWidgets.QLabel("<b>%s:</b>" % _("Diameter Tolerance"))
+        self.tol_label = FCLabel("<b>%s:</b>" % _("Diameter Tolerance"))
         self.tol_label.setToolTip(
             _("Tool tolerance. If there is a tool in the targeted tools table with\n"
               "the value within the limits then this tool from DB will be used.")
@@ -1155,7 +1155,7 @@ class ToolsDB2UI:
         self.grid_tool.addWidget(self.tol_label, 2, 0, 1, 2)
 
         # Tolerance Min Limit
-        self.min_limit_label = QtWidgets.QLabel('%s:' % _("Min"))
+        self.min_limit_label = FCLabel('%s:' % _("Min"))
         self.min_limit_label.setToolTip(
             _("Set the tool tolerance minimum.")
         )
@@ -1169,7 +1169,7 @@ class ToolsDB2UI:
         self.grid_tool.addWidget(self.tol_min_entry, 4, 1)
 
         # Tolerance Min Limit
-        self.max_limit_label = QtWidgets.QLabel('%s:' % _("Max"))
+        self.max_limit_label = FCLabel('%s:' % _("Max"))
         self.max_limit_label.setToolTip(
             _("Set the tool tolerance maximum.")
         )
@@ -1183,7 +1183,7 @@ class ToolsDB2UI:
         self.grid_tool.addWidget(self.tol_max_entry, 6, 1)
 
         # Tool Object Type
-        self.tool_op_label = QtWidgets.QLabel('<b>%s:</b>' % _('Operation'))
+        self.tool_op_label = FCLabel('<b>%s:</b>' % _('Operation'))
         self.tool_op_label.setToolTip(
             _("The kind of Application Tool where this tool is to be used."))
 
@@ -1205,7 +1205,7 @@ class ToolsDB2UI:
         self.milling_vlay.addStretch()
 
         # Tool Shape
-        self.shape_label = QtWidgets.QLabel('%s:' % _('Shape'))
+        self.shape_label = FCLabel('%s:' % _('Shape'))
         self.shape_label.setToolTip(
             _("Tool Shape. \n"
               "Can be:\n"
@@ -1221,7 +1221,7 @@ class ToolsDB2UI:
         self.grid0.addWidget(self.shape_combo, 2, 1)
 
         # V-Dia
-        self.vdia_label = QtWidgets.QLabel('%s:' % _("V-Dia"))
+        self.vdia_label = FCLabel('%s:' % _("V-Dia"))
         self.vdia_label.setToolTip(
             _("V-Dia.\n"
               "Diameter of the tip for V-Shape Tools."))
@@ -1235,7 +1235,7 @@ class ToolsDB2UI:
         self.grid0.addWidget(self.vdia_entry, 4, 1)
 
         # V-Angle
-        self.vangle_label = QtWidgets.QLabel('%s:' % _("V-Angle"))
+        self.vangle_label = FCLabel('%s:' % _("V-Angle"))
         self.vangle_label.setToolTip(
             _("V-Agle.\n"
               "Angle at the tip for the V-Shape Tools."))
@@ -1254,7 +1254,7 @@ class ToolsDB2UI:
         self.grid0.addWidget(separator_line, 8, 0, 1, 2)
 
         # Tool Type
-        self.type_label = QtWidgets.QLabel('%s:' % _("Tool Type"))
+        self.type_label = FCLabel('%s:' % _("Tool Type"))
         self.type_label.setToolTip(
             _("Tool Type.\n"
               "Can be:\n"
@@ -1270,7 +1270,7 @@ class ToolsDB2UI:
         self.grid0.addWidget(self.type_combo, 10, 1)
 
         # Tool Offset
-        self.tooloffset_label = QtWidgets.QLabel('%s:' % _('Tool Offset'))
+        self.tooloffset_label = FCLabel('%s:' % _('Tool Offset'))
         self.tooloffset_label.setToolTip(
             _("Tool Offset.\n"
               "Can be of a few types:\n"
@@ -1287,7 +1287,7 @@ class ToolsDB2UI:
         self.grid0.addWidget(self.tooloffset_combo, 12, 1)
 
         # Custom Offset
-        self.custom_offset_label = QtWidgets.QLabel('%s:' % _("Custom Offset"))
+        self.custom_offset_label = FCLabel('%s:' % _("Custom Offset"))
         self.custom_offset_label.setToolTip(
             _("Custom Offset.\n"
               "A value to be used as offset from the current path."))
@@ -1306,7 +1306,7 @@ class ToolsDB2UI:
         self.grid0.addWidget(separator_line, 16, 0, 1, 2)
 
         # Cut Z
-        self.cutz_label = QtWidgets.QLabel('%s:' % _("Cut Z"))
+        self.cutz_label = FCLabel('%s:' % _("Cut Z"))
         self.cutz_label.setToolTip(
             _("Cutting Depth.\n"
               "The depth at which to cut into material."))
@@ -1320,7 +1320,7 @@ class ToolsDB2UI:
         self.grid0.addWidget(self.cutz_entry, 18, 1)
 
         # Multi Depth
-        self.multidepth_label = QtWidgets.QLabel('%s:' % _("MultiDepth"))
+        self.multidepth_label = FCLabel('%s:' % _("MultiDepth"))
         self.multidepth_label.setToolTip(
             _("Multi Depth.\n"
               "Selecting this will allow cutting in multiple passes,\n"
@@ -1333,7 +1333,7 @@ class ToolsDB2UI:
         self.grid0.addWidget(self.multidepth_cb, 20, 1)
 
         # Depth Per Pass
-        self.dpp_label = QtWidgets.QLabel('%s:' % _("DPP"))
+        self.dpp_label = FCLabel('%s:' % _("DPP"))
         self.dpp_label.setToolTip(
             _("DPP. Depth per Pass.\n"
               "The value used to cut into material on each pass."))
@@ -1347,7 +1347,7 @@ class ToolsDB2UI:
         self.grid0.addWidget(self.multidepth_entry, 22, 1)
 
         # Travel Z
-        self.travelz_label = QtWidgets.QLabel('%s:' % _("Travel Z"))
+        self.travelz_label = FCLabel('%s:' % _("Travel Z"))
         self.travelz_label.setToolTip(
             _("Clearance Height.\n"
               "Height at which the milling bit will travel between cuts,\n"
@@ -1362,7 +1362,7 @@ class ToolsDB2UI:
         self.grid0.addWidget(self.travelz_entry, 24, 1)
 
         # Extra Cut
-        self.ecut_label = QtWidgets.QLabel('%s:' % _("ExtraCut"))
+        self.ecut_label = FCLabel('%s:' % _("ExtraCut"))
         self.ecut_label.setToolTip(
             _("Extra Cut.\n"
               "If checked, after a isolation is finished an extra cut\n"
@@ -1377,7 +1377,7 @@ class ToolsDB2UI:
         self.grid0.addWidget(self.ecut_cb, 26, 1)
 
         # Extra Cut Length
-        self.ecut_length_label = QtWidgets.QLabel('%s:' % _("E-Cut Length"))
+        self.ecut_length_label = FCLabel('%s:' % _("E-Cut Length"))
         self.ecut_length_label.setToolTip(
             _("Extra Cut length.\n"
               "If checked, after a isolation is finished an extra cut\n"
@@ -1400,7 +1400,7 @@ class ToolsDB2UI:
         self.grid0.addWidget(separator_line, 30, 0, 1, 2)
 
         # Feedrate X-Y
-        self.frxy_label = QtWidgets.QLabel('%s:' % _("Feedrate X-Y"))
+        self.frxy_label = FCLabel('%s:' % _("Feedrate X-Y"))
         self.frxy_label.setToolTip(
             _("Feedrate X-Y. Feedrate\n"
               "The speed on XY plane used while cutting into material."))
@@ -1414,7 +1414,7 @@ class ToolsDB2UI:
         self.grid0.addWidget(self.frxy_entry, 32, 1)
 
         # Feedrate Z
-        self.frz_label = QtWidgets.QLabel('%s:' % _("Feedrate Z"))
+        self.frz_label = FCLabel('%s:' % _("Feedrate Z"))
         self.frz_label.setToolTip(
             _("Feedrate Z\n"
               "The speed on Z plane."))
@@ -1428,7 +1428,7 @@ class ToolsDB2UI:
         self.grid0.addWidget(self.frz_entry, 34, 1)
 
         # Feedrate Rapids
-        self.frapids_label = QtWidgets.QLabel('%s:' % _("FR Rapids"))
+        self.frapids_label = FCLabel('%s:' % _("FR Rapids"))
         self.frapids_label.setToolTip(
             _("FR Rapids. Feedrate Rapids\n"
               "Speed used while moving as fast as possible.\n"
@@ -1449,7 +1449,7 @@ class ToolsDB2UI:
         self.grid0.addWidget(separator_line, 38, 0, 1, 2)
 
         # Spindle Spped
-        self.spindle_label = QtWidgets.QLabel('%s:' % _("Spindle Speed"))
+        self.spindle_label = FCLabel('%s:' % _("Spindle Speed"))
         self.spindle_label.setToolTip(
             _("Spindle Speed.\n"
               "If it's left empty it will not be used.\n"
@@ -1464,7 +1464,7 @@ class ToolsDB2UI:
         self.grid0.addWidget(self.spindle_entry, 40, 1)
 
         # Dwell
-        self.dwell_label = QtWidgets.QLabel('%s:' % _("Dwell"))
+        self.dwell_label = FCLabel('%s:' % _("Dwell"))
         self.dwell_label.setToolTip(
             _("Dwell.\n"
               "Check this if a delay is needed to allow\n"
@@ -1477,7 +1477,7 @@ class ToolsDB2UI:
         self.grid0.addWidget(self.dwell_cb, 42, 1)
 
         # Dwell Time
-        self.dwelltime_label = QtWidgets.QLabel('%s:' % _("Dwelltime"))
+        self.dwelltime_label = FCLabel('%s:' % _("Dwelltime"))
         self.dwelltime_label.setToolTip(
             _("Dwell Time.\n"
               "A delay used to allow the motor spindle reach its set speed."))
@@ -1501,7 +1501,7 @@ class ToolsDB2UI:
         self.ncc_vlay.addStretch()
 
         # Operation
-        op_label = QtWidgets.QLabel('%s:' % _('Operation'))
+        op_label = FCLabel('%s:' % _('Operation'))
         op_label.setToolTip(
             _("The 'Operation' can be:\n"
               "- Isolation -> will ensure that the non-copper clearing is always complete.\n"
@@ -1519,7 +1519,7 @@ class ToolsDB2UI:
         self.grid2.addWidget(self.op_radio, 13, 1)
 
         # Milling Type Radio Button
-        self.milling_type_label = QtWidgets.QLabel('%s:' % _('Milling Type'))
+        self.milling_type_label = FCLabel('%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"
@@ -1539,7 +1539,7 @@ class ToolsDB2UI:
         self.grid2.addWidget(self.milling_type_radio, 14, 1)
 
         # Overlap Entry
-        nccoverlabel = QtWidgets.QLabel('%s:' % _('Overlap'))
+        nccoverlabel = FCLabel('%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"
@@ -1560,7 +1560,7 @@ class ToolsDB2UI:
         self.grid2.addWidget(self.ncc_overlap_entry, 15, 1)
 
         # Margin
-        nccmarginlabel = QtWidgets.QLabel('%s:' % _('Margin'))
+        nccmarginlabel = FCLabel('%s:' % _('Margin'))
         nccmarginlabel.setToolTip(
             _("Bounding box margin.")
         )
@@ -1573,7 +1573,7 @@ class ToolsDB2UI:
         self.grid2.addWidget(self.ncc_margin_entry, 16, 1)
 
         # Method
-        methodlabel = QtWidgets.QLabel('%s:' % _('Method'))
+        methodlabel = FCLabel('%s:' % _('Method'))
         methodlabel.setToolTip(
             _("Algorithm for copper clearing:\n"
               "- Standard: Fixed step inwards.\n"
@@ -1648,7 +1648,7 @@ class ToolsDB2UI:
         self.paint_vlay.addStretch()
 
         # Overlap
-        ovlabel = QtWidgets.QLabel('%s:' % _('Overlap'))
+        ovlabel = FCLabel('%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"
@@ -1669,7 +1669,7 @@ class ToolsDB2UI:
         self.grid3.addWidget(self.paintoverlap_entry, 1, 1)
 
         # Margin
-        marginlabel = QtWidgets.QLabel('%s:' % _('Offset'))
+        marginlabel = FCLabel('%s:' % _('Offset'))
         marginlabel.setToolTip(
             _("Distance by which to avoid\n"
               "the edges of the polygon to\n"
@@ -1684,7 +1684,7 @@ class ToolsDB2UI:
         self.grid3.addWidget(self.paint_offset_entry, 2, 1)
 
         # Method
-        methodlabel = QtWidgets.QLabel('%s:' % _('Method'))
+        methodlabel = FCLabel('%s:' % _('Method'))
         methodlabel.setToolTip(
             _("Algorithm for painting:\n"
               "- Standard: Fixed step inwards.\n"
@@ -1737,7 +1737,7 @@ class ToolsDB2UI:
         self.iso_vlay.addStretch()
 
         # Passes
-        passlabel = QtWidgets.QLabel('%s:' % _('Passes'))
+        passlabel = FCLabel('%s:' % _('Passes'))
         passlabel.setToolTip(
             _("Width of the isolation gap in\n"
               "number (integer) of tool widths.")
@@ -1750,7 +1750,7 @@ class ToolsDB2UI:
         self.grid4.addWidget(self.passes_entry, 0, 1)
 
         # Overlap Entry
-        overlabel = QtWidgets.QLabel('%s:' % _('Overlap'))
+        overlabel = FCLabel('%s:' % _('Overlap'))
         overlabel.setToolTip(
             _("How much (percentage) of the tool width to overlap each tool pass.")
         )
@@ -1765,7 +1765,7 @@ class ToolsDB2UI:
         self.grid4.addWidget(self.iso_overlap_entry, 2, 1)
 
         # Milling Type Radio Button
-        self.milling_type_label = QtWidgets.QLabel('%s:' % _('Milling Type'))
+        self.milling_type_label = FCLabel('%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"
@@ -1785,7 +1785,7 @@ class ToolsDB2UI:
         self.grid4.addWidget(self.milling_type_radio, 4, 1)
 
         # Follow
-        self.follow_label = QtWidgets.QLabel('%s:' % _('Follow'))
+        self.follow_label = FCLabel('%s:' % _('Follow'))
         self.follow_label.setToolTip(
             _("Generate a 'Follow' geometry.\n"
               "This means that it will cut through\n"
@@ -1802,7 +1802,7 @@ class ToolsDB2UI:
         self.grid4.addWidget(self.follow_cb, 6, 1)
 
         # Isolation Type
-        self.iso_type_label = QtWidgets.QLabel('%s:' % _('Isolation Type'))
+        self.iso_type_label = FCLabel('%s:' % _('Isolation Type'))
         self.iso_type_label.setToolTip(
             _("Choose how the isolation will be executed:\n"
               "- 'Full' -> complete isolation of polygons\n"
@@ -1831,7 +1831,7 @@ class ToolsDB2UI:
         self.drill_vlay.addStretch()
 
         # Cut Z
-        self.cutzlabel = QtWidgets.QLabel('%s:' % _('Cut Z'))
+        self.cutzlabel = FCLabel('%s:' % _('Cut Z'))
         self.cutzlabel.setToolTip(
             _("Drill depth (negative)\n"
               "below the copper surface.")
@@ -1852,7 +1852,7 @@ class ToolsDB2UI:
         self.grid5.addWidget(self.cutz_drill_entry, 4, 1)
 
         # Tool Offset
-        self.tool_offset_label = QtWidgets.QLabel('%s:' % _('Offset Z'))
+        self.tool_offset_label = FCLabel('%s:' % _('Offset Z'))
         self.tool_offset_label.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"
@@ -1868,7 +1868,7 @@ class ToolsDB2UI:
         self.grid5.addWidget(self.offset_drill_entry, 6, 1)
 
         # Multi-Depth
-        self.multidepth_drill_label = QtWidgets.QLabel('%s:' % _("MultiDepth"))
+        self.multidepth_drill_label = FCLabel('%s:' % _("MultiDepth"))
         self.multidepth_drill_label.setToolTip(
             _(
                 "Use multiple passes to limit\n"
@@ -1884,7 +1884,7 @@ class ToolsDB2UI:
         self.grid5.addWidget(self.mpass_drill_cb, 7, 1)
 
         # Depth Per Pass
-        self.dpp_drill_label = QtWidgets.QLabel('%s:' % _("DPP"))
+        self.dpp_drill_label = FCLabel('%s:' % _("DPP"))
         self.dpp_drill_label.setToolTip(
             _("DPP. Depth per Pass.\n"
               "The value used to cut into material on each pass."))
@@ -1900,7 +1900,7 @@ class ToolsDB2UI:
         self.grid5.addWidget(self.maxdepth_drill_entry, 8, 1)
 
         # Travel Z (z_move)
-        self.travelzlabel = QtWidgets.QLabel('%s:' % _('Travel Z'))
+        self.travelzlabel = FCLabel('%s:' % _('Travel Z'))
         self.travelzlabel.setToolTip(
             _("Tool height when travelling\n"
               "across the XY plane.")
@@ -1926,7 +1926,7 @@ class ToolsDB2UI:
         self.grid5.addWidget(separator_line, 12, 0, 1, 2)
 
         # Excellon Feedrate Z
-        self.frzlabel = QtWidgets.QLabel('%s:' % _('Feedrate Z'))
+        self.frzlabel = FCLabel('%s:' % _('Feedrate Z'))
         self.frzlabel.setToolTip(
             _("Tool speed while drilling\n"
               "(in units per minute).\n"
@@ -1943,7 +1943,7 @@ class ToolsDB2UI:
         self.grid5.addWidget(self.feedrate_z_drill_entry, 14, 1)
 
         # Excellon Rapid Feedrate
-        self.feedrate_rapid_label = QtWidgets.QLabel('%s:' % _('Feedrate Rapids'))
+        self.feedrate_rapid_label = FCLabel('%s:' % _('Feedrate Rapids'))
         self.feedrate_rapid_label.setToolTip(
             _("Tool speed while drilling\n"
               "(in units per minute).\n"
@@ -1966,7 +1966,7 @@ class ToolsDB2UI:
         self.grid5.addWidget(separator_line, 18, 0, 1, 2)
 
         # Spindlespeed
-        self.spindle_label = QtWidgets.QLabel('%s:' % _('Spindle speed'))
+        self.spindle_label = FCLabel('%s:' % _('Spindle speed'))
         self.spindle_label.setToolTip(
             _("Speed of the spindle\n"
               "in RPM (optional)")
@@ -1981,7 +1981,7 @@ class ToolsDB2UI:
         self.grid5.addWidget(self.spindlespeed_drill_entry, 20, 1)
 
         # Dwell
-        self.dwell_drill_label = QtWidgets.QLabel('%s:' % _("Dwell"))
+        self.dwell_drill_label = FCLabel('%s:' % _("Dwell"))
         self.dwell_drill_label.setToolTip(
             _("Dwell.\n"
               "Check this if a delay is needed to allow\n"
@@ -1994,7 +1994,7 @@ class ToolsDB2UI:
         self.grid5.addWidget(self.dwell_drill_cb, 21, 1)
 
         # Dwelltime
-        self.dwelltime_drill_lbl = QtWidgets.QLabel('%s:' % _('Dwelltime'))
+        self.dwelltime_drill_lbl = FCLabel('%s:' % _('Dwelltime'))
         self.dwelltime_drill_lbl.setToolTip(
             _("Dwell Time.\n"
               "A delay used to allow the motor spindle reach its set speed."))
@@ -2013,7 +2013,7 @@ class ToolsDB2UI:
         self.grid5.addWidget(separator_line, 24, 0, 1, 2)
 
         # Drill slots
-        self.drill_slots_drill_lbl = QtWidgets.QLabel('%s:' % _('Drill slots'))
+        self.drill_slots_drill_lbl = FCLabel('%s:' % _('Drill slots'))
         self.drill_slots_drill_lbl.setToolTip(
             _("If the selected tool has slots then they will be drilled.")
         )
@@ -2024,7 +2024,7 @@ class ToolsDB2UI:
         self.grid5.addWidget(self.drill_slots_drill_cb, 26, 1)
 
         # Drill Overlap
-        self.drill_overlap_label = QtWidgets.QLabel('%s:' % _('Overlap'))
+        self.drill_overlap_label = FCLabel('%s:' % _('Overlap'))
         self.drill_overlap_label.setToolTip(
             _("How much (percentage) of the tool diameter to overlap previous drill hole.")
         )
@@ -2040,7 +2040,7 @@ class ToolsDB2UI:
         self.grid5.addWidget(self.drill_overlap_drill_entry, 28, 1)
 
         # Last drill in slot
-        self.last_drill_drill_lbl = QtWidgets.QLabel('%s:' % _('Last drill'))
+        self.last_drill_drill_lbl = FCLabel('%s:' % _('Last drill'))
         self.last_drill_drill_lbl.setToolTip(
             _("If the slot length is not completely covered by drill holes,\n"
               "add a drill hole on the slot end point.")
@@ -2068,7 +2068,7 @@ class ToolsDB2UI:
         self.cutout_margin_entry.set_precision(self.decimals)
         self.cutout_margin_entry.setObjectName('gdb_ct_margin')
 
-        self.cutout_margin_label = QtWidgets.QLabel('%s:' % _("Margin"))
+        self.cutout_margin_label = FCLabel('%s:' % _("Margin"))
         self.cutout_margin_label.setToolTip(
             _("Margin over bounds. A positive value here\n"
               "will make the cutout of the PCB further from\n"
@@ -2082,7 +2082,7 @@ class ToolsDB2UI:
         self.cutout_gapsize.set_precision(self.decimals)
         self.cutout_gapsize.setObjectName('gdb_ct_gapsize')
 
-        self.cutout_gapsize_label = QtWidgets.QLabel('%s:' % _("Gap size"))
+        self.cutout_gapsize_label = FCLabel('%s:' % _("Gap size"))
         self.cutout_gapsize_label.setToolTip(
             _("The size of the bridge gaps in the cutout\n"
               "used to keep the board connected to\n"
@@ -2092,6 +2092,73 @@ class ToolsDB2UI:
         self.grid6.addWidget(self.cutout_gapsize_label, 13, 0)
         self.grid6.addWidget(self.cutout_gapsize, 13, 1)
 
+        # Gap Type
+        self.gaptype_label = FCLabel('%s:' % _("Gap type"))
+        self.gaptype_label.setToolTip(
+            _("The type of gap:\n"
+              "- Bridge -> the cutout will be interrupted by bridges\n"
+              "- Thin -> same as 'bridge' but it will be thinner by partially milling the gap\n"
+              "- M-Bites -> 'Mouse Bites' - same as 'bridge' but covered with drill holes")
+        )
+
+        self.gaptype_radio = RadioSet(
+            [
+                {'label': _('Bridge'), 'value': 'b'},
+                {'label': _('Thin'), 'value': 'bt'},
+                {'label': "M-Bites", 'value': 'mb'}
+            ],
+            stretch=True
+        )
+        self.gaptype_radio.setObjectName('gdb_ct_gap_type')
+
+        self.grid6.addWidget(self.gaptype_label, 15, 0)
+        self.grid6.addWidget(self.gaptype_radio, 15, 1)
+
+        # Thin gaps Depth
+        self.thin_depth_label = FCLabel('%s:' % _("Depth"))
+        self.thin_depth_label.setToolTip(
+            _("The depth until the milling is done\n"
+              "in order to thin the gaps.")
+        )
+        self.thin_depth_entry = FCDoubleSpinner(callback=self.confirmation_message)
+        self.thin_depth_entry.set_precision(self.decimals)
+        self.thin_depth_entry.setObjectName('gdb_ct_gap_depth')
+
+        if self.machinist_setting == 0:
+            self.thin_depth_entry.setRange(-9999.9999, -0.00001)
+        else:
+            self.thin_depth_entry.setRange(-9999.9999, 9999.9999)
+        self.thin_depth_entry.setSingleStep(0.1)
+
+        self.grid6.addWidget(self.thin_depth_label, 17, 0)
+        self.grid6.addWidget(self.thin_depth_entry, 17, 1)
+
+        # Mouse Bites Tool Diameter
+        self.mb_dia_label = FCLabel('%s:' % _("Tool Diameter"))
+        self.mb_dia_label.setToolTip(
+            _("The drill hole diameter when doing mpuse bites.")
+        )
+        self.mb_dia_entry = FCDoubleSpinner(callback=self.confirmation_message)
+        self.mb_dia_entry.set_precision(self.decimals)
+        self.mb_dia_entry.setRange(0, 100.0000)
+        self.mb_dia_entry.setObjectName('gdb_ct_mb_dia')
+
+        self.grid6.addWidget(self.mb_dia_label, 19, 0)
+        self.grid6.addWidget(self.mb_dia_entry, 19, 1)
+
+        # Mouse Bites Holes Spacing
+        self.mb_spacing_label = FCLabel('%s:' % _("Spacing"))
+        self.mb_spacing_label.setToolTip(
+            _("The spacing between drill holes when doing mouse bites.")
+        )
+        self.mb_spacing_entry = FCDoubleSpinner(callback=self.confirmation_message)
+        self.mb_spacing_entry.set_precision(self.decimals)
+        self.mb_spacing_entry.setRange(0, 100.0000)
+        self.mb_spacing_entry.setObjectName('gdb_ct_mb_spacing')
+
+        self.grid6.addWidget(self.mb_spacing_label, 21, 0)
+        self.grid6.addWidget(self.mb_spacing_entry, 21, 1)
+        
         # How gaps wil be rendered:
         # lr    - left + right
         # tb    - top + bottom
@@ -2102,17 +2169,17 @@ class ToolsDB2UI:
 
         # Surrounding convex box shape
         self.cutout_convex_box = FCCheckBox('%s' % _("Convex Shape"))
-        # self.convex_box_label = QtWidgets.QLabel('%s' % _("Convex Sh."))
+        # self.convex_box_label = FCLabel('%s' % _("Convex Sh."))
         self.cutout_convex_box.setToolTip(
             _("Create a convex shape surrounding the entire PCB.\n"
               "Used only if the source object type is Gerber.")
         )
         self.cutout_convex_box.setObjectName('gdb_ct_convex')
 
-        self.grid6.addWidget(self.cutout_convex_box, 15, 0, 1, 2)
+        self.grid6.addWidget(self.cutout_convex_box, 23, 0, 1, 2)
 
         # Gaps
-        self.cutout_gaps_label = QtWidgets.QLabel('%s:' % _('Gaps'))
+        self.cutout_gaps_label = FCLabel('%s:' % _('Gaps'))
         self.cutout_gaps_label.setToolTip(
             _("Number of gaps used for the Automatic cutout.\n"
               "There can be maximum 8 bridges/gaps.\n"
@@ -2131,8 +2198,8 @@ class ToolsDB2UI:
         self.cutout_gaps.addItems(gaps_items)
         self.cutout_gaps.setObjectName('gdb_ct_gaps')
 
-        self.grid6.addWidget(self.cutout_gaps_label, 19, 0)
-        self.grid6.addWidget(self.cutout_gaps, 19, 1)
+        self.grid6.addWidget(self.cutout_gaps_label, 25, 0)
+        self.grid6.addWidget(self.cutout_gaps, 25, 1)
 
         # ####################################################################
         # ####################################################################
@@ -2363,11 +2430,16 @@ class ToolsDB2(QtWidgets.QWidget):
             "tools_drill_last_drill":       self.ui.last_drill_drill_cb,
 
             # Cutout
-            "tools_cutoutmargin":           self.ui.cutout_margin_entry,
-            "tools_cutoutgapsize":          self.ui.cutout_gapsize,
-            "tools_gaps_ff":                self.ui.cutout_gaps,
+            "tools_cutout_margin":           self.ui.cutout_margin_entry,
+            "tools_cutout_gapsize":          self.ui.cutout_gapsize,
+            "tools_cutout_gaps_ff":         self.ui.cutout_gaps,
             "tools_cutout_convexshape":     self.ui.cutout_convex_box,
 
+            "tools_cutout_gap_type":        self.ui.gaptype_radio,
+            "tools_cutout_gap_depth":       self.ui.thin_depth_entry,
+            "tools_cutout_mb_dia":          self.ui.mb_dia_entry,
+            "tools_cutout_mb_spacing":      self.ui.mb_spacing_entry,
+
         }
 
         self.name2option = {
@@ -2442,11 +2514,16 @@ class ToolsDB2(QtWidgets.QWidget):
             "gdb_e_drill_last_drill":   "tools_drill_last_drill",
 
             # Cutout
-            "gdb_ct_margin":            "tools_cutoutmargin",
-            "gdb_ct_gapsize":           "tools_cutoutgapsize",
-            "gdb_ct_gaps":              "tools_gaps_ff",
+            "gdb_ct_margin":            "tools_cutout_margin",
+            "gdb_ct_gapsize":           "tools_cutout_gapsize",
+            "gdb_ct_gaps":              "tools_cutout_gaps_ff",
             "gdb_ct_convex":            "tools_cutout_convexshape",
 
+            "gdb_ct_gap_type":          "tools_cutout_gap_type",
+            "gdb_ct_gap_depth":         "tools_cutout_gap_depth",
+            "gdb_ct_mb_dia":            "tools_cutout_mb_dia",
+            "gdb_ct_mb_spacing":        "tools_cutout_mb_spacing"
+
         }
 
         self.current_toolid = None
@@ -2782,10 +2859,15 @@ class ToolsDB2(QtWidgets.QWidget):
             "tools_drill_last_drill":       self.app.defaults["tools_drill_last_drill"],
 
             # Cutout
-            "tools_cutoutmargin":           float(self.app.defaults["tools_cutoutmargin"]),
-            "tools_cutoutgapsize":          float(self.app.defaults["tools_cutoutgapsize"]),
-            "tools_gaps_ff":                self.app.defaults["tools_gaps_ff"],
+            "tools_cutout_margin":          float(self.app.defaults["tools_cutout_margin"]),
+            "tools_cutout_gapsize":         float(self.app.defaults["tools_cutout_gapsize"]),
+            "tools_cutout_gaps_ff":         self.app.defaults["tools_cutout_gaps_ff"],
             "tools_cutout_convexshape":     self.app.defaults["tools_cutout_convexshape"],
+
+            "tools_cutout_gap_type":        self.app.defaults["tools_cutout_gap_type"],
+            "tools_cutout_gap_depth":       float(self.app.defaults["tools_cutout_gap_depth"]),
+            "tools_cutout_mb_dia":          float(self.app.defaults["tools_cutout_mb_dia"]),
+            "tools_cutout_mb_spacing":      float(self.app.defaults["tools_cutout_mb_spacing"])
         })
 
         temp = []
@@ -3341,14 +3423,23 @@ class ToolsDB2(QtWidgets.QWidget):
 
             # Cutout Tool
             elif wdg_name == "gdb_ct_margin":
-                self.db_tool_dict[tool_id]['data']['tools_cutoutmargin'] = val
+                self.db_tool_dict[tool_id]['data']['tools_cutout_margin'] = val
             elif wdg_name == "gdb_ct_gapsize":
-                self.db_tool_dict[tool_id]['data']['tools_cutoutgapsize'] = val
+                self.db_tool_dict[tool_id]['data']['tools_cutout_gapsize'] = val
             elif wdg_name == "gdb_ct_gaps":
-                self.db_tool_dict[tool_id]['data']['tools_gaps_ff'] = val
+                self.db_tool_dict[tool_id]['data']['tools_cutout_gaps_ff'] = val
             elif wdg_name == "gdb_ct_convex":
                 self.db_tool_dict[tool_id]['data']['tools_cutout_convexshape'] = val
 
+            elif wdg_name == "gdb_ct_gap_type":
+                self.db_tool_dict[tool_id]['data']['tools_cutout_gap_type'] = val
+            elif wdg_name == "gdb_ct_gap_depth":
+                self.db_tool_dict[tool_id]['data']['tools_cutout_gap_depth'] = val
+            elif wdg_name == "gdb_ct_mb_dia":
+                self.db_tool_dict[tool_id]['data']['tools_cutout_mb_dia'] = val
+            elif wdg_name == "gdb_ct_mb_spacing":
+                self.db_tool_dict[tool_id]['data']['tools_cutout_mb_spacing'] = val
+
         self.callback_app()
 
     def on_tool_requested_from_app(self):

+ 29 - 24
appGUI/preferences/PreferencesUIManager.py

@@ -402,40 +402,45 @@ class PreferencesUIManager:
             "tools_ncc_plotting":       self.ui.tools_defaults_form.tools_ncc_group.plotting_radio,
 
             # 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_tooldia":          self.ui.tools_defaults_form.tools_cutout_group.cutout_tooldia_entry,
+            "tools_cutout_kind":             self.ui.tools_defaults_form.tools_cutout_group.obj_kind_combo,
+            "tools_cutout_margin":          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_gapsize":         self.ui.tools_defaults_form.tools_cutout_group.cutout_gap_entry,
+            "tools_cutout_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,
             "tools_cutout_big_cursor":      self.ui.tools_defaults_form.tools_cutout_group.big_cursor_cb,
 
+            "tools_cutout_gap_type":        self.ui.tools_defaults_form.tools_cutout_group.gaptype_radio,
+            "tools_cutout_gap_depth":       self.ui.tools_defaults_form.tools_cutout_group.thin_depth_entry,
+            "tools_cutout_mb_dia":          self.ui.tools_defaults_form.tools_cutout_group.mb_dia_entry,
+            "tools_cutout_mb_spacing":      self.ui.tools_defaults_form.tools_cutout_group.mb_spacing_entry,
+
             # 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_paintoffset": 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,
+            "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_paintoffset":        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_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

+ 76 - 13
appGUI/preferences/tools/ToolsCutoutPrefGroupUI.py

@@ -1,7 +1,7 @@
 from PyQt5 import QtWidgets
 from PyQt5.QtCore import QSettings
 
-from appGUI.GUIElements import FCDoubleSpinner, FCCheckBox, RadioSet, FCComboBox
+from appGUI.GUIElements import FCDoubleSpinner, FCCheckBox, RadioSet, FCComboBox, FCLabel
 from appGUI.preferences import machinist_setting
 from appGUI.preferences.OptionsGroupUI import OptionsGroupUI
 
@@ -29,7 +29,7 @@ class ToolsCutoutPrefGroupUI(OptionsGroupUI):
         self.decimals = decimals
 
         # ## Board cutout
-        self.board_cutout_label = QtWidgets.QLabel("<b>%s:</b>" % _("Parameters"))
+        self.board_cutout_label = FCLabel("<b>%s:</b>" % _("Parameters"))
         self.board_cutout_label.setToolTip(
             _("Create toolpaths to cut around\n"
               "the PCB and separate it from\n"
@@ -40,7 +40,7 @@ class ToolsCutoutPrefGroupUI(OptionsGroupUI):
         grid0 = QtWidgets.QGridLayout()
         self.layout.addLayout(grid0)
 
-        tdclabel = QtWidgets.QLabel('%s:' % _('Tool Diameter'))
+        tdclabel = FCLabel('%s:' % _('Tool Diameter'))
         tdclabel.setToolTip(
             _("Diameter of the tool used to cutout\n"
               "the PCB shape out of the surrounding material.")
@@ -55,7 +55,7 @@ class ToolsCutoutPrefGroupUI(OptionsGroupUI):
         grid0.addWidget(self.cutout_tooldia_entry, 0, 1)
 
         # Cut Z
-        cutzlabel = QtWidgets.QLabel('%s:' % _('Cut Z'))
+        cutzlabel = FCLabel('%s:' % _('Cut Z'))
         cutzlabel.setToolTip(
             _(
                 "Cutting depth (negative)\n"
@@ -97,7 +97,7 @@ class ToolsCutoutPrefGroupUI(OptionsGroupUI):
         grid0.addWidget(self.maxdepth_entry, 2, 1)
 
         # Object kind
-        kindlabel = QtWidgets.QLabel('%s:' % _('Object kind'))
+        kindlabel = FCLabel('%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>"
@@ -112,7 +112,7 @@ class ToolsCutoutPrefGroupUI(OptionsGroupUI):
         grid0.addWidget(kindlabel, 3, 0)
         grid0.addWidget(self.obj_kind_combo, 3, 1)
 
-        marginlabel = QtWidgets.QLabel('%s:' % _('Margin'))
+        marginlabel = FCLabel('%s:' % _('Margin'))
         marginlabel.setToolTip(
             _("Margin over bounds. A positive value here\n"
               "will make the cutout of the PCB further from\n"
@@ -126,8 +126,9 @@ class ToolsCutoutPrefGroupUI(OptionsGroupUI):
 
         grid0.addWidget(marginlabel, 4, 0)
         grid0.addWidget(self.cutout_margin_entry, 4, 1)
-
-        gaplabel = QtWidgets.QLabel('%s:' % _('Gap size'))
+        
+        # Gap Size
+        gaplabel = FCLabel('%s:' % _('Gap size'))
         gaplabel.setToolTip(
             _("The size of the bridge gaps in the cutout\n"
               "used to keep the board connected to\n"
@@ -142,8 +143,70 @@ class ToolsCutoutPrefGroupUI(OptionsGroupUI):
 
         grid0.addWidget(gaplabel, 5, 0)
         grid0.addWidget(self.cutout_gap_entry, 5, 1)
+        
+        # Gap Type
+        self.gaptype_label = FCLabel('%s:' % _("Gap type"))
+        self.gaptype_label.setToolTip(
+            _("The type of gap:\n"
+              "- Bridge -> the cutout will be interrupted by bridges\n"
+              "- Thin -> same as 'bridge' but it will be thinner by partially milling the gap\n"
+              "- M-Bites -> 'Mouse Bites' - same as 'bridge' but covered with drill holes")
+        )
+
+        self.gaptype_radio = RadioSet(
+            [
+                {'label': _('Bridge'),      'value': 'b'},
+                {'label': _('Thin'),        'value': 'bt'},
+                {'label': "M-Bites",        'value': 'mb'}
+            ],
+            stretch=True
+        )
+
+        grid0.addWidget(self.gaptype_label, 7, 0)
+        grid0.addWidget(self.gaptype_radio, 7, 1)
+
+        # Thin gaps Depth
+        self.thin_depth_label = FCLabel('%s:' % _("Depth"))
+        self.thin_depth_label.setToolTip(
+            _("The depth until the milling is done\n"
+              "in order to thin the gaps.")
+        )
+        self.thin_depth_entry = FCDoubleSpinner()
+        self.thin_depth_entry.set_precision(self.decimals)
+        if machinist_setting == 0:
+            self.thin_depth_entry.setRange(-9999.9999, -0.00001)
+        else:
+            self.thin_depth_entry.setRange(-9999.9999, 9999.9999)
+        self.thin_depth_entry.setSingleStep(0.1)
 
-        gaps_label = QtWidgets.QLabel('%s:' % _('Gaps'))
+        grid0.addWidget(self.thin_depth_label, 9, 0)
+        grid0.addWidget(self.thin_depth_entry, 9, 1)
+
+        # Mouse Bites Tool Diameter
+        self.mb_dia_label = FCLabel('%s:' % _("Tool Diameter"))
+        self.mb_dia_label.setToolTip(
+            _("The drill hole diameter when doing mpuse bites.")
+        )
+        self.mb_dia_entry = FCDoubleSpinner()
+        self.mb_dia_entry.set_precision(self.decimals)
+        self.mb_dia_entry.setRange(0, 100.0000)
+
+        grid0.addWidget(self.mb_dia_label, 11, 0)
+        grid0.addWidget(self.mb_dia_entry, 11, 1)
+
+        # Mouse Bites Holes Spacing
+        self.mb_spacing_label = FCLabel('%s:' % _("Spacing"))
+        self.mb_spacing_label.setToolTip(
+            _("The spacing between drill holes when doing mouse bites.")
+        )
+        self.mb_spacing_entry = FCDoubleSpinner()
+        self.mb_spacing_entry.set_precision(self.decimals)
+        self.mb_spacing_entry.setRange(0, 100.0000)
+
+        grid0.addWidget(self.mb_spacing_label, 13, 0)
+        grid0.addWidget(self.mb_spacing_entry, 13, 1)
+        
+        gaps_label = FCLabel('%s:' % _('Gaps'))
         gaps_label.setToolTip(
             _("Number of gaps used for the cutout.\n"
               "There can be maximum 8 bridges/gaps.\n"
@@ -158,8 +221,8 @@ class ToolsCutoutPrefGroupUI(OptionsGroupUI):
         )
 
         self.gaps_combo = FCComboBox()
-        grid0.addWidget(gaps_label, 6, 0)
-        grid0.addWidget(self.gaps_combo, 6, 1)
+        grid0.addWidget(gaps_label, 15, 0)
+        grid0.addWidget(self.gaps_combo, 15, 1)
 
         gaps_items = ['None', 'LR', 'TB', '4', '2LR', '2TB', '8']
         for it in gaps_items:
@@ -172,11 +235,11 @@ class ToolsCutoutPrefGroupUI(OptionsGroupUI):
             _("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)
+        grid0.addWidget(self.convex_box, 17, 0, 1, 2)
 
         self.big_cursor_cb = FCCheckBox('%s' % _("Big cursor"))
         self.big_cursor_cb.setToolTip(
             _("Use a big cursor when adding manual gaps."))
-        grid0.addWidget(self.big_cursor_cb, 8, 0, 1, 2)
+        grid0.addWidget(self.big_cursor_cb, 19, 0, 1, 2)
 
         self.layout.addStretch()

+ 415 - 135
appTools/ToolCutOut.py

@@ -21,6 +21,9 @@ from copy import deepcopy
 import math
 import logging
 import gettext
+import sys
+import simplejson as json
+
 import appTranslation as fcTranslate
 import builtins
 
@@ -90,10 +93,17 @@ class CutOut(AppTool):
         # store original geometry for manual cutout
         self.manual_solid_geo = None
 
+        # here store the tool data for the Cutout Tool
+        self.cut_tool_dict = {}
+
         # Signals
         self.ui.ff_cutout_object_btn.clicked.connect(self.on_freeform_cutout)
         self.ui.rect_cutout_object_btn.clicked.connect(self.on_rectangular_cutout)
 
+        # adding tools
+        self.ui.add_newtool_button.clicked.connect(lambda: self.on_tool_add())
+        self.ui.addtool_from_db_btn.clicked.connect(self.on_tool_add_from_db_clicked)
+
         self.ui.type_obj_radio.activated_custom.connect(self.on_type_obj_changed)
         self.ui.man_geo_creation_btn.clicked.connect(self.on_manual_geo)
         self.ui.man_gaps_creation_btn.clicked.connect(self.on_manual_gap_click)
@@ -145,20 +155,6 @@ class CutOut(AppTool):
     def set_tool_ui(self):
         self.reset_fields()
 
-        self.ui.dia.set_value(float(self.app.defaults["tools_cutouttooldia"]))
-        self.ui.obj_kind_combo.set_value(self.app.defaults["tools_cutoutkind"])
-        self.ui.margin.set_value(float(self.app.defaults["tools_cutoutmargin"]))
-        self.ui.cutz_entry.set_value(float(self.app.defaults["tools_cutout_z"]))
-        self.ui.mpass_cb.set_value(float(self.app.defaults["tools_cutout_mdepth"]))
-        self.ui.maxdepth_entry.set_value(float(self.app.defaults["tools_cutout_depthperpass"]))
-
-        self.ui.gapsize.set_value(float(self.app.defaults["tools_cutoutgapsize"]))
-        self.ui.gaps.set_value(self.app.defaults["tools_gaps_ff"])
-        self.ui.convex_box_cb.set_value(self.app.defaults['tools_cutout_convexshape'])
-        self.ui.big_cursor_cb.set_value(self.app.defaults['tools_cutout_big_cursor'])
-
-        self.ui.gaptype_radio.set_value('b')
-
         # use the current selected object and make it visible in the object combobox
         sel_list = self.app.collection.get_selected()
         if len(sel_list) == 1:
@@ -188,13 +184,210 @@ class CutOut(AppTool):
             else:
                 self.on_type_obj_changed(val='geo')
 
-        # self.type_obj_radio.set_value('grb')
+        self.ui.dia.set_value(float(self.app.defaults["tools_cutout_tooldia"]))
+
+        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"]),
+            "spindledir":       self.app.defaults["geometry_spindledir"],
+            "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"]),
+            "optimization_type":    self.app.defaults["geometry_optimization_type"],
+
+            # Cutout
+            "tools_cutout_tooldia":         self.app.defaults["tools_cutout_tooldia"],
+            "tools_cutout_kind":            self.app.defaults["tools_cutout_kind"],
+            "tools_cutout_margin":          float(self.app.defaults["tools_cutout_margin"]),
+            "tools_cutout_z":               float(self.app.defaults["tools_cutout_z"]),
+            "tools_cutout_depthperpass":    float(self.app.defaults["tools_cutout_depthperpass"]),
+            "tools_cutout_mdepth":          self.app.defaults["tools_cutout_mdepth"],
+            "tools_cutout_gapsize":         float(self.app.defaults["tools_cutout_gapsize"]),
+            "tools_cutout_gaps_ff":         self.app.defaults["tools_cutout_gaps_ff"],
+            "tools_cutout_convexshape":     self.app.defaults["tools_cutout_convexshape"],
+
+            "tools_cutout_big_cursor":      self.app.defaults["tools_cutout_big_cursor"],
+            "tools_cutout_gap_type":        self.app.defaults["tools_cutout_gap_type"],
+            "tools_cutout_gap_depth":       float(self.app.defaults["tools_cutout_gap_depth"]),
+            "tools_cutout_mb_dia":          float(self.app.defaults["tools_cutout_mb_dia"]),
+            "tools_cutout_mb_spacing":      float(self.app.defaults["tools_cutout_mb_spacing"]),
+
+        })
+        tool_dia = float(self.app.defaults["tools_cutout_tooldia"])
+        self.on_tool_add(custom_dia=tool_dia)
+
+    def update_ui(self, tool_dict):
+        self.ui.obj_kind_combo.set_value(self.default_data["tools_cutout_kind"])
+        self.ui.big_cursor_cb.set_value(self.default_data['tools_cutout_big_cursor'])
+
+        # Entries that may be updated from database
+        self.ui.margin.set_value(float(tool_dict["tools_cutout_margin"]))
+        self.ui.gapsize.set_value(float(tool_dict["tools_cutout_gapsize"]))
+        self.ui.gaptype_radio.set_value(tool_dict["tools_cutout_gap_type"])
+        self.ui.thin_depth_entry.set_value(float(tool_dict["tools_cutout_gap_depth"]))
+        self.ui.mb_dia_entry.set_value(float(tool_dict["tools_cutout_mb_dia"]))
+        self.ui.mb_spacing_entry.set_value(float(tool_dict["tools_cutout_mb_spacing"]))
+        self.ui.convex_box_cb.set_value(tool_dict['tools_cutout_convexshape'])
+        self.ui.gaps.set_value(tool_dict["tools_cutout_gaps_ff"])
+
+        self.ui.cutz_entry.set_value(float(tool_dict["tools_cutout_z"]))
+        self.ui.mpass_cb.set_value(float(tool_dict["tools_cutout_mdepth"]))
+        self.ui.maxdepth_entry.set_value(float(tool_dict["tools_cutout_depthperpass"]))
+
+    def on_tool_add(self, custom_dia=None):
+        self.blockSignals(True)
+
+        filename = self.app.data_path + '\\tools_db.FlatDB'
+
+        new_tools_dict = deepcopy(self.default_data)
+        updated_tooldia = None
+
+        # determine the new tool diameter
+        if custom_dia is None:
+            tool_dia = self.ui.dia.get_value()
+        else:
+            tool_dia = custom_dia
+
+        if tool_dia is None or tool_dia == 0:
+            self.app.inform.emit('[WARNING_NOTCL] %s' % _("Please enter a tool diameter with non-zero value, "
+                                                          "in Float format."))
+            self.blockSignals(False)
+            return
+
+        truncated_tooldia = self.app.dec_format(tool_dia, self.decimals)
+
+        # load the database tools from the file
+        try:
+            with open(filename) as f:
+                tools = f.read()
+        except IOError:
+            self.app.log.error("Could not load tools DB file.")
+            self.app.inform.emit('[ERROR] %s' % _("Could not load Tools DB file."))
+            self.blockSignals(False)
+            self.on_tool_default_add(dia=tool_dia)
+            return
+
+        try:
+            # store here the tools from Tools Database when searching in Tools Database
+            tools_db_dict = json.loads(tools)
+        except Exception:
+            e = sys.exc_info()[0]
+            self.app.log.error(str(e))
+            self.app.inform.emit('[ERROR] %s' % _("Failed to parse Tools DB file."))
+            self.blockSignals(False)
+            self.on_tool_default_add(dia=tool_dia)
+            return
+
+        tool_found = 0
+
+        offset = 'Path'
+        offset_val = 0.0
+        typ = "Rough"
+        tool_type = 'V'
+        # look in database tools
+        for db_tool, db_tool_val in tools_db_dict.items():
+            offset = db_tool_val['offset']
+            offset_val = db_tool_val['offset_value']
+            typ = db_tool_val['type']
+            tool_type = db_tool_val['tool_type']
+
+            db_tooldia = db_tool_val['tooldia']
+            low_limit = float(db_tool_val['data']['tol_min'])
+            high_limit = float(db_tool_val['data']['tol_max'])
+
+            # we need only tool marked for Cutout Tool
+            if db_tool_val['data']['tool_target'] != _('Cutout'):
+                continue
+
+            # if we find a tool with the same diameter in the Tools DB just update it's data
+            if truncated_tooldia == db_tooldia:
+                tool_found += 1
+                for d in db_tool_val['data']:
+                    if d.find('tools_cutout') == 0:
+                        new_tools_dict[d] = db_tool_val['data'][d]
+                    elif d.find('tools_') == 0:
+                        # don't need data for other App Tools; this tests after 'tools_drill_'
+                        continue
+                    else:
+                        new_tools_dict[d] = db_tool_val['data'][d]
+            # search for a tool that has a tolerance that the tool fits in
+            elif high_limit >= truncated_tooldia >= low_limit:
+                tool_found += 1
+                updated_tooldia = db_tooldia
+                for d in db_tool_val['data']:
+                    if d.find('tools_cutout') == 0:
+                        new_tools_dict[d] = db_tool_val['data'][d]
+                    elif d.find('tools_') == 0:
+                        # don't need data for other App Tools; this tests after 'tools_drill_'
+                        continue
+                    else:
+                        new_tools_dict[d] = db_tool_val['data'][d]
+
+        # test we found a suitable tool in Tools Database or if multiple ones
+        if tool_found == 0:
+            self.app.inform.emit('[WARNING_NOTCL] %s' % _("Tool not in Tools Database. Adding a default tool."))
+            self.on_tool_default_add()
+            self.blockSignals(False)
+            return
+
+        if tool_found > 1:
+            self.app.inform.emit(
+                '[WARNING_NOTCL] %s' % _("Cancelled.\n"
+                                         "Multiple tools for one tool diameter found in Tools Database."))
+            self.blockSignals(False)
+            return
+
+        # FIXME when the Geometry UI milling functionality will be transferred in the Milling Tool this needs changes
+        new_tools_dict["tools_cutout_z"] = deepcopy(new_tools_dict["cutz"])
+        new_tools_dict["tools_cutout_mdepth"] = deepcopy(new_tools_dict["multidepth"])
+        new_tools_dict["tools_cutout_depthperpass"] = deepcopy(new_tools_dict["depthperpass"])
+
+        new_tdia = deepcopy(updated_tooldia) if updated_tooldia is not None else deepcopy(truncated_tooldia)
+        self.cut_tool_dict.update({
+                'tooldia':          new_tdia,
+                'offset':           deepcopy(offset),
+                'offset_value':     deepcopy(offset_val),
+                'type':             deepcopy(typ),
+                'tool_type':        deepcopy(tool_type),
+                'data':             deepcopy(new_tools_dict),
+                'solid_geometry':   []
+            })
+
+        self.update_ui(new_tools_dict)
+
+        self.blockSignals(False)
+        self.app.inform.emit('[success] %s' % _("Updated tool from Tools Database."))
 
+    def on_tool_default_add(self, dia=None, muted=None):
         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"]),
@@ -219,32 +412,115 @@ class CutOut(AppTool):
             "area_overz":       float(self.app.defaults["geometry_area_overz"]),
             "optimization_type":    self.app.defaults["geometry_optimization_type"],
 
-            # 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_paintoffset":        float(self.app.defaults["tools_paintoffset"]),
-            "tools_paintmethod":        self.app.defaults["tools_paintmethod"],
-            "tools_pathconnect":        self.app.defaults["tools_pathconnect"],
-            "tools_paintcontour":       self.app.defaults["tools_paintcontour"],
-
-            # Isolation Tool
-            "tools_iso_passes":         self.app.defaults["tools_iso_passes"],
-            "tools_iso_overlap":        self.app.defaults["tools_iso_overlap"],
-            "tools_iso_milling_type":   self.app.defaults["tools_iso_milling_type"],
-            "tools_iso_follow":         self.app.defaults["tools_iso_follow"],
-            "tools_iso_isotype":        self.app.defaults["tools_iso_isotype"],
+            # Cutout
+            "tools_cutout_tooldia":         self.app.defaults["tools_cutout_tooldia"],
+            "tools_cutout_kind":            self.app.defaults["tools_cutout_kind"],
+            "tools_cutout_margin":          float(self.app.defaults["tools_cutout_margin"]),
+            "tools_cutout_z":               float(self.app.defaults["tools_cutout_z"]),
+            "tools_cutout_depthperpass":    float(self.app.defaults["tools_cutout_depthperpass"]),
+            "tools_cutout_mdepth":          self.app.defaults["tools_cutout_mdepth"],
+            "tools_cutout_gapsize":         float(self.app.defaults["tools_cutout_gapsize"]),
+            "tools_cutout_gaps_ff":         self.app.defaults["tools_cutout_gaps_ff"],
+            "tools_cutout_convexshape":     self.app.defaults["tools_cutout_convexshape"],
+
+            "tools_cutout_big_cursor":      self.app.defaults["tools_cutout_big_cursor"],
+            "tools_cutout_gap_type":        self.app.defaults["tools_cutout_gap_type"],
+            "tools_cutout_gap_depth":       float(self.app.defaults["tools_cutout_gap_depth"]),
+            "tools_cutout_mb_dia":          float(self.app.defaults["tools_cutout_mb_dia"]),
+            "tools_cutout_mb_spacing":      float(self.app.defaults["tools_cutout_mb_spacing"]),
+
         })
 
+        self.cut_tool_dict.update({
+                'tooldia':          str(self.app.defaults["tools_cutout_tooldia"]),
+                'offset':           'Path',
+                'offset_value':     0.0,
+                'type':             _('Rough'),
+                'tool_type':        'C1',
+                'data':             deepcopy(self.default_data),
+                'solid_geometry':   []
+            })
+
+        self.update_ui(self.default_data)
+
+        if muted is None:
+            self.app.inform.emit('[success] %s' % _("Default tool added."))
+
+    def on_cutout_tool_add_from_db_executed(self, tool):
+        """
+        Here add the tool from DB  in the selected geometry object
+        :return:
+        """
+
+        if tool['data']['tool_target'] != _("Cutout"):
+            self.app.inform.emit('[ERROR_NOTCL] %s' % _("Selected tool can't be used here. Pick another."))
+            return
+        tool_from_db = deepcopy(self.default_data)
+        tool_from_db.update(tool)
+
+        # FIXME when the Geometry UI milling functionality will be transferred in the Milling Tool this needs changes
+        tool_from_db['data']["tools_cutout_tooldia"] = deepcopy(tool["tooldia"])
+        tool_from_db['data']["tools_cutout_z"] = deepcopy(tool_from_db['data']["cutz"])
+        tool_from_db['data']["tools_cutout_mdepth"] = deepcopy(tool_from_db['data']["multidepth"])
+        tool_from_db['data']["tools_cutout_depthperpass"] = deepcopy(tool_from_db['data']["depthperpass"])
+
+        self.cut_tool_dict.update(tool_from_db)
+        self.cut_tool_dict['solid_geometry'] = []
+
+        self.update_ui(tool_from_db['data'])
+        self.ui.dia.set_value(float(tool_from_db['data']["tools_cutout_tooldia"]))
+
+        for idx in range(self.app.ui.plot_tab_area.count()):
+            if self.app.ui.plot_tab_area.tabText(idx) == _("Tools Database"):
+                wdg = self.app.ui.plot_tab_area.widget(idx)
+                wdg.deleteLater()
+                self.app.ui.plot_tab_area.removeTab(idx)
+
+        self.app.inform.emit('[success] %s' % _("Tool updated from Tools Database."))
+
+    def on_tool_from_db_inserted(self, tool):
+        """
+        Called from the Tools DB object through a App method when adding a tool from Tools Database
+        :param tool: a dict with the tool data
+        :return: None
+        """
+
+        tooldia = float(tool['tooldia'])
+
+        truncated_tooldia = self.app.dec_format(tooldia, self.decimals)
+        self.cutout_tools.update({
+            1: {
+                'tooldia':          truncated_tooldia,
+                'offset':           tool['offset'],
+                'offset_value':     tool['offset_value'],
+                'type':             tool['type'],
+                'tool_type':        tool['tool_type'],
+                'data':             deepcopy(tool['data']),
+                'solid_geometry':   []
+            }
+        })
+        self.cutout_tools[1]['data']['name'] = '_cutout'
+
+        return 1
+
+    def on_tool_add_from_db_clicked(self):
+        """
+        Called when the user wants to add a new tool from Tools Database. It will create the Tools Database object
+        and display the Tools Database tab in the form needed for the Tool adding
+        :return: None
+        """
+
+        # if the Tools Database is already opened focus on it
+        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.setCurrentWidget(self.app.tools_db_tab)
+                break
+        self.app.on_tools_database(source='cutout')
+        self.app.tools_db_tab.ok_to_add = True
+        self.app.tools_db_tab.ui.buttons_frame.hide()
+        self.app.tools_db_tab.ui.add_tool_from_db.show()
+        self.app.tools_db_tab.ui.cancel_tool_from_db.show()
+
     def on_freeform_cutout(self):
         log.debug("Cutout.on_freeform_cutout() was launched ...")
 
@@ -480,18 +756,14 @@ class CutOut(AppTool):
             geo_obj.options['multidepth'] = self.ui.mpass_cb.get_value()
             geo_obj.options['depthperpass'] = self.ui.maxdepth_entry.get_value()
 
+            geo_obj.multigeo = True
+
             geo_obj.tools.update({
-                1: {
-                    'tooldia':          str(dia),
-                    'offset':           'Path',
-                    'offset_value':     0.0,
-                    'type':             _('Rough'),
-                    'tool_type':        'C1',
-                    'data':             deepcopy(self.default_data),
-                    'solid_geometry':   geo_obj.solid_geometry
-                }
+                1: self.cut_tool_dict
             })
-            geo_obj.multigeo = True
+            geo_obj.tools[1]['tooldia'] = str(dia)
+            geo_obj.tools[1]['solid_geometry'] = geo_obj.solid_geometry
+
             geo_obj.tools[1]['data']['name'] = outname
             geo_obj.tools[1]['data']['cutz'] = self.ui.cutz_entry.get_value()
             geo_obj.tools[1]['data']['multidepth'] = self.ui.mpass_cb.get_value()
@@ -499,16 +771,11 @@ class CutOut(AppTool):
 
             if gaps_solid_geo is not None:
                 geo_obj.tools.update({
-                    9999: {
-                        'tooldia': str(dia),
-                        'offset': 'Path',
-                        'offset_value': 0.0,
-                        'type': _('Rough'),
-                        'tool_type': 'C1',
-                        'data': deepcopy(self.default_data),
-                        'solid_geometry': gaps_solid_geo
-                    }
+                    9999: self.cut_tool_dict
                 })
+                geo_obj.tools[9999]['tooldia'] = str(dia)
+                geo_obj.tools[9999]['solid_geometry'] = gaps_solid_geo
+
                 geo_obj.tools[9999]['data']['name'] = outname
                 geo_obj.tools[9999]['data']['cutz'] = self.ui.thin_depth_entry.get_value()
                 geo_obj.tools[9999]['data']['multidepth'] = self.ui.mpass_cb.get_value()
@@ -735,18 +1002,14 @@ class CutOut(AppTool):
             solid_geo = linemerge(solid_geo)
             geo_obj.solid_geometry = deepcopy(solid_geo)
 
+            geo_obj.multigeo = True
+
             geo_obj.tools.update({
-                1: {
-                    'tooldia':          str(dia),
-                    'offset':           'Path',
-                    'offset_value':     0.0,
-                    'type':             _('Rough'),
-                    'tool_type':        'C1',
-                    'data':             deepcopy(self.default_data),
-                    'solid_geometry':   geo_obj.solid_geometry
-                }
+                1: self.cut_tool_dict
             })
-            geo_obj.multigeo = True
+            geo_obj.tools[1]['tooldia'] = str(dia)
+            geo_obj.tools[1]['solid_geometry'] = geo_obj.solid_geometry
+
             geo_obj.tools[1]['data']['name'] = outname
             geo_obj.tools[1]['data']['cutz'] = self.ui.cutz_entry.get_value()
             geo_obj.tools[1]['data']['multidepth'] = self.ui.mpass_cb.get_value()
@@ -754,16 +1017,11 @@ class CutOut(AppTool):
 
             if gaps_solid_geo is not None:
                 geo_obj.tools.update({
-                    9999: {
-                        'tooldia': str(dia),
-                        'offset': 'Path',
-                        'offset_value': 0.0,
-                        'type': _('Rough'),
-                        'tool_type': 'C1',
-                        'data': deepcopy(self.default_data),
-                        'solid_geometry': gaps_solid_geo
-                    }
+                    9999: self.cut_tool_dict
                 })
+                geo_obj.tools[9999]['tooldia'] = str(dia)
+                geo_obj.tools[9999]['solid_geometry'] = gaps_solid_geo
+
                 geo_obj.tools[9999]['data']['name'] = outname
                 geo_obj.tools[9999]['data']['cutz'] = self.ui.thin_depth_entry.get_value()
                 geo_obj.tools[9999]['data']['multidepth'] = self.ui.mpass_cb.get_value()
@@ -856,7 +1114,7 @@ class CutOut(AppTool):
         cut_poly = self.cutting_geo(pos=(snapped_pos[0], snapped_pos[1]))
 
         gaps_solid_geo = None
-        if self.ui.gaptype_radio.get_value() == 'bt' and self.ui.thin_depth_entry.get_value() > 0:
+        if self.ui.gaptype_radio.get_value() == 'bt' and self.ui.thin_depth_entry.get_value() != 0:
             gaps_solid_geo = self.intersect_geo(self.manual_solid_geo, cut_poly)
 
         # first subtract geometry for the total solid_geometry
@@ -864,10 +1122,11 @@ class CutOut(AppTool):
         new_solid_geometry = linemerge(new_solid_geometry)
         self.man_cutout_obj.solid_geometry = new_solid_geometry
 
-        # then do it or each tool in the manual cutout Geometry object
+        # then do it on each tool in the manual cutout Geometry object
         try:
-            self.man_cutout_obj.tools[1]['solid_geometry'] = new_solid_geometry
             self.man_cutout_obj.multigeo = True
+
+            self.man_cutout_obj.tools[1]['solid_geometry'] = new_solid_geometry
             self.man_cutout_obj.tools[1]['data']['name'] = self.man_cutout_obj.options['name'] + '_cutout'
             self.man_cutout_obj.tools[1]['data']['cutz'] = self.ui.cutz_entry.get_value()
             self.man_cutout_obj.tools[1]['data']['multidepth'] = self.ui.mpass_cb.get_value()
@@ -880,16 +1139,11 @@ class CutOut(AppTool):
         if gaps_solid_geo:
             if 9999 not in self.man_cutout_obj.tools:
                 self.man_cutout_obj.tools.update({
-                    9999: {
-                        'tooldia': str(dia),
-                        'offset': 'Path',
-                        'offset_value': 0.0,
-                        'type': _('Rough'),
-                        'tool_type': 'C1',
-                        'data': deepcopy(self.default_data),
-                        'solid_geometry': [gaps_solid_geo]
-                    }
+                    9999: self.cut_tool_dict
                 })
+                self.man_cutout_obj.tools[9999]['tooldia'] = str(dia)
+                self.man_cutout_obj.tools[9999]['solid_geometry'] = [gaps_solid_geo]
+
                 self.man_cutout_obj.tools[9999]['data']['name'] = self.man_cutout_obj.options['name'] + '_cutout'
                 self.man_cutout_obj.tools[9999]['data']['cutz'] = self.ui.thin_depth_entry.get_value()
                 self.man_cutout_obj.tools[9999]['data']['multidepth'] = self.ui.mpass_cb.get_value()
@@ -899,7 +1153,7 @@ class CutOut(AppTool):
                 self.man_cutout_obj.tools[9999]['solid_geometry'].append(gaps_solid_geo)
 
         self.man_cutout_obj.plot(plot_tool=1)
-        self.app.inform.emit('[success] %s' % _("Added manual Bridge Gap."))
+        self.app.inform.emit('%s' % _("Added manual Bridge Gap. Left click to add another or right click to finish."))
 
         self.app.should_we_save = True
 
@@ -975,18 +1229,14 @@ class CutOut(AppTool):
             geo_obj.options['multidepth'] = self.ui.mpass_cb.get_value()
             geo_obj.options['depthperpass'] = self.ui.maxdepth_entry.get_value()
 
+            geo_obj.multigeo = True
+
             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
-                }
+                1: self.cut_tool_dict
             })
-            geo_obj.multigeo = True
+            geo_obj.tools[1]['tooldia'] = str(dia)
+            geo_obj.tools[1]['solid_geometry'] = geo_obj.solid_geometry
+
             geo_obj.tools[1]['data']['name'] = outname
             geo_obj.tools[1]['data']['cutz'] = self.ui.cutz_entry.get_value()
             geo_obj.tools[1]['data']['multidepth'] = self.ui.mpass_cb.get_value()
@@ -1075,6 +1325,8 @@ class CutOut(AppTool):
             # plot the final object
             self.man_cutout_obj.plot()
 
+            self.app.inform.emit('[success] %s' % _("Finished manual adding of gaps."))
+
     def on_mouse_move(self, event):
 
         self.app.on_mouse_move_over_plot(event=event)
@@ -1507,23 +1759,55 @@ class CutoutUI:
         separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
         grid0.addWidget(separator_line, 10, 0, 1, 2)
 
-        grid0.addWidget(QtWidgets.QLabel(''), 12, 0, 1, 2)
-
-        self.param_label = QtWidgets.QLabel('<b>%s:</b>' % _("Tool Parameters"))
-        grid0.addWidget(self.param_label, 14, 0, 1, 2)
+        self.tool_sel_label = FCLabel('<b>%s</b>' % _('Cutout Tool'))
+        grid0.addWidget(self.tool_sel_label, 12, 0, 1, 2)
 
         # Tool Diameter
         self.dia = FCDoubleSpinner(callback=self.confirmation_message)
         self.dia.set_precision(self.decimals)
         self.dia.set_range(0.0000, 9999.9999)
 
-        self.dia_label = QtWidgets.QLabel('%s:' % _("Tool Diameter"))
+        self.dia_label = QtWidgets.QLabel('%s:' % _("Tool Dia"))
         self.dia_label.setToolTip(
             _("Diameter of the tool used to cutout\n"
               "the PCB shape out of the surrounding material.")
         )
-        grid0.addWidget(self.dia_label, 16, 0)
-        grid0.addWidget(self.dia, 16, 1)
+        grid0.addWidget(self.dia_label, 14, 0)
+        grid0.addWidget(self.dia, 14, 1)
+
+        hlay = QtWidgets.QHBoxLayout()
+
+        # Search and Add new Tool
+        self.add_newtool_button = FCButton(_('Search and Add'))
+        self.add_newtool_button.setIcon(QtGui.QIcon(self.app.resource_location + '/plus16.png'))
+        self.add_newtool_button.setToolTip(
+            _("Add a new tool to the Tool Table\n"
+              "with the diameter specified above.\n"
+              "This is done by a background search\n"
+              "in the Tools Database. If nothing is found\n"
+              "in the Tools DB then a default tool is added.")
+        )
+        hlay.addWidget(self.add_newtool_button)
+
+        # Pick from DB new Tool
+        self.addtool_from_db_btn = FCButton(_('Pick from DB'))
+        self.addtool_from_db_btn.setIcon(QtGui.QIcon(self.app.resource_location + '/search_db32.png'))
+        self.addtool_from_db_btn.setToolTip(
+            _("Add a new tool to the Tool Table\n"
+              "from the Tool Database.\n"
+              "Tool database administration in Menu: Options -> Tools Database")
+        )
+        hlay.addWidget(self.addtool_from_db_btn)
+
+        grid0.addLayout(hlay, 16, 0, 1, 2)
+
+        separator_line = QtWidgets.QFrame()
+        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
+        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
+        grid0.addWidget(separator_line, 18, 0, 1, 2)
+
+        self.param_label = QtWidgets.QLabel('<b>%s:</b>' % _("Tool Parameters"))
+        grid0.addWidget(self.param_label, 20, 0, 1, 2)
 
         # Cut Z
         cutzlabel = QtWidgets.QLabel('%s:' % _('Cut Z'))
@@ -1543,8 +1827,8 @@ class CutoutUI:
 
         self.cutz_entry.setSingleStep(0.1)
 
-        grid0.addWidget(cutzlabel, 18, 0)
-        grid0.addWidget(self.cutz_entry, 18, 1)
+        grid0.addWidget(cutzlabel, 22, 0)
+        grid0.addWidget(self.cutz_entry, 22, 1)
 
         # Multi-pass
         self.mpass_cb = FCCheckBox('%s:' % _("Multi-Depth"))
@@ -1568,8 +1852,8 @@ class CutoutUI:
             )
         )
 
-        grid0.addWidget(self.mpass_cb, 20, 0)
-        grid0.addWidget(self.maxdepth_entry, 20, 1)
+        grid0.addWidget(self.mpass_cb, 24, 0)
+        grid0.addWidget(self.maxdepth_entry, 24, 1)
 
         self.ois_mpass_geo = OptionalInputSection(self.mpass_cb, [self.maxdepth_entry])
 
@@ -1585,8 +1869,8 @@ class CutoutUI:
               "will make the cutout of the PCB further from\n"
               "the actual PCB border")
         )
-        grid0.addWidget(self.margin_label, 22, 0)
-        grid0.addWidget(self.margin, 22, 1)
+        grid0.addWidget(self.margin_label, 26, 0)
+        grid0.addWidget(self.margin, 26, 1)
 
         # Gapsize
         self.gapsize_label = QtWidgets.QLabel('%s:' % _("Gap size"))
@@ -1600,10 +1884,10 @@ class CutoutUI:
         self.gapsize = FCDoubleSpinner(callback=self.confirmation_message)
         self.gapsize.set_precision(self.decimals)
 
-        grid0.addWidget(self.gapsize_label, 24, 0)
-        grid0.addWidget(self.gapsize, 24, 1)
+        grid0.addWidget(self.gapsize_label, 28, 0)
+        grid0.addWidget(self.gapsize, 28, 1)
 
-        # Gapsize
+        # Gap Type
         self.gaptype_label = FCLabel('%s:' % _("Gap type"))
         self.gaptype_label.setToolTip(
             _("The type of gap:\n"
@@ -1621,8 +1905,8 @@ class CutoutUI:
             stretch=True
         )
 
-        grid0.addWidget(self.gaptype_label, 26, 0)
-        grid0.addWidget(self.gaptype_radio, 26, 1)
+        grid0.addWidget(self.gaptype_label, 30, 0)
+        grid0.addWidget(self.gaptype_radio, 30, 1)
 
         # Thin gaps Depth
         self.thin_depth_label = FCLabel('%s:' % _("Depth"))
@@ -1638,8 +1922,8 @@ class CutoutUI:
             self.thin_depth_entry.setRange(-9999.9999, 9999.9999)
         self.thin_depth_entry.setSingleStep(0.1)
 
-        grid0.addWidget(self.thin_depth_label, 28, 0)
-        grid0.addWidget(self.thin_depth_entry, 28, 1)
+        grid0.addWidget(self.thin_depth_label, 32, 0)
+        grid0.addWidget(self.thin_depth_entry, 32, 1)
 
         # Mouse Bites Tool Diameter
         self.mb_dia_label = FCLabel('%s:' % _("Tool Diameter"))
@@ -1650,8 +1934,8 @@ class CutoutUI:
         self.mb_dia_entry.set_precision(self.decimals)
         self.mb_dia_entry.setRange(0, 100.0000)
 
-        grid0.addWidget(self.mb_dia_label, 30, 0)
-        grid0.addWidget(self.mb_dia_entry, 30, 1)
+        grid0.addWidget(self.mb_dia_label, 34, 0)
+        grid0.addWidget(self.mb_dia_entry, 34, 1)
 
         # Mouse Bites Holes Spacing
         self.mb_spacing_label = FCLabel('%s:' % _("Spacing"))
@@ -1662,22 +1946,20 @@ class CutoutUI:
         self.mb_spacing_entry.set_precision(self.decimals)
         self.mb_spacing_entry.setRange(0, 100.0000)
 
-        grid0.addWidget(self.mb_spacing_label, 32, 0)
-        grid0.addWidget(self.mb_spacing_entry, 32, 1)
+        grid0.addWidget(self.mb_spacing_label, 36, 0)
+        grid0.addWidget(self.mb_spacing_entry, 36, 1)
 
         separator_line = QtWidgets.QFrame()
         separator_line.setFrameShape(QtWidgets.QFrame.HLine)
         separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
-        grid0.addWidget(separator_line, 34, 0, 1, 2)
-
-        grid0.addWidget(QtWidgets.QLabel(''), 36, 0, 1, 2)
+        grid0.addWidget(separator_line, 38, 0, 1, 2)
 
         # Title2
         title_param_label = QtWidgets.QLabel("<b>%s %s</b>:" % (_('Automatic'), _("Bridge Gaps")))
         title_param_label.setToolTip(
             _("This section handle creation of automatic bridge gaps.")
         )
-        grid0.addWidget(title_param_label, 38, 0, 1, 2)
+        grid0.addWidget(title_param_label, 40, 0, 1, 2)
 
         # Gaps
         # How gaps wil be rendered:
@@ -1707,8 +1989,8 @@ class CutoutUI:
         for it in gaps_items:
             self.gaps.addItem(it)
             # self.gaps.setStyleSheet('background-color: rgb(255,255,255)')
-        grid0.addWidget(gaps_label, 40, 0)
-        grid0.addWidget(self.gaps, 40, 1)
+        grid0.addWidget(gaps_label, 42, 0)
+        grid0.addWidget(self.gaps, 42, 1)
 
         # Buttons
         self.ff_cutout_object_btn = FCButton(_("Generate Geometry"))
@@ -1724,7 +2006,7 @@ class CutoutUI:
                                     font-weight: bold;
                                 }
                                 """)
-        grid0.addWidget(self.ff_cutout_object_btn, 42, 0, 1, 2)
+        grid0.addWidget(self.ff_cutout_object_btn, 44, 0, 1, 2)
 
         self.rect_cutout_object_btn = FCButton(_("Generate Geometry"))
         self.rect_cutout_object_btn.setIcon(QtGui.QIcon(self.app.resource_location + '/rectangle32.png'))
@@ -1740,14 +2022,12 @@ class CutoutUI:
                                     font-weight: bold;
                                 }
                                 """)
-        grid0.addWidget(self.rect_cutout_object_btn, 44, 0, 1, 2)
+        grid0.addWidget(self.rect_cutout_object_btn, 46, 0, 1, 2)
 
         separator_line = QtWidgets.QFrame()
         separator_line.setFrameShape(QtWidgets.QFrame.HLine)
         separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
-        grid0.addWidget(separator_line, 46, 0, 1, 2)
-
-        grid0.addWidget(QtWidgets.QLabel(''), 48, 0, 1, 2)
+        grid0.addWidget(separator_line, 48, 0, 1, 2)
 
         # MANUAL BRIDGE GAPS
         title_manual_label = QtWidgets.QLabel("<b>%s %s</b>:" % (_('Manual'), _("Bridge Gaps")))

+ 1 - 1
appTools/ToolNCC.py

@@ -3855,7 +3855,7 @@ class NccUI:
         self.grid3.addWidget(self.tool_sel_label, 0, 0, 1, 2)
 
         # ### Tool Diameter ####
-        self.new_tooldia_lbl = FCLabel('<b>%s:</b>' % _('Tool Dia'))
+        self.new_tooldia_lbl = FCLabel('%s:' % _('Tool Dia'))
         self.new_tooldia_lbl.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"

+ 10 - 2
app_Main.py

@@ -4259,8 +4259,8 @@ class App(QtCore.QObject):
             "tools_nccnewdia",
 
             # Cutout Tool
-            "tools_cutouttooldia", 'tools_cutoutmargin', "tools_cutout_z", "tools_cutout_depthperpass",
-            'tools_cutoutgapsize',
+            "tools_cutout_tooldia", 'tools_cutout_margin', "tools_cutout_z", "tools_cutout_depthperpass",
+            'tools_cutout_gapsize', 'tools_cutout_gap_depth', 'tools_cutout_mb_dia', 'tools_cutout_mb_spacing',
 
             # Paint Tool
             "tools_painttooldia", 'tools_paintoffset', "tools_paintcutz", "tools_painttipdia", "tools_paintnewdia",
@@ -5705,6 +5705,14 @@ class App(QtCore.QObject):
                 callback_on_edited=self.on_tools_db_edited,
                 callback_on_tool_request=self.isolation_tool.on_iso_tool_add_from_db_executed
             )
+        elif source == 'cutout':
+            self.tools_db_tab = ToolsDB2(
+                app=self,
+                parent=self.ui,
+                callback_on_edited=self.on_tools_db_edited,
+                callback_on_tool_request=self.cutout_tool.on_cutout_tool_add_from_db_executed
+            )
+
         # add the tab if it was closed
         try:
             self.ui.plot_tab_area.addTab(self.tools_db_tab, _("Tools Database"))

+ 9 - 5
defaults.py

@@ -476,16 +476,20 @@ class FlatCAMDefaults:
         "tools_ncc_plotting": 'normal',
 
         # Cutout Tool
-        "tools_cutouttooldia": 2.4,
-        "tools_cutoutkind": "single",
-        "tools_cutoutmargin": 0.1,
+        "tools_cutout_tooldia": 2.4,
+        "tools_cutout_kind": "single",
+        "tools_cutout_margin": 0.1,
         "tools_cutout_z": -1.8,
         "tools_cutout_depthperpass": 0.6,
         "tools_cutout_mdepth": True,
-        "tools_cutoutgapsize": 4,
-        "tools_gaps_ff": "4",
+        "tools_cutout_gapsize": 4,
+        "tools_cutout_gaps_ff": "4",
         "tools_cutout_convexshape": False,
         "tools_cutout_big_cursor": True,
+        "tools_cutout_gap_type": 'b',
+        "tools_cutout_gap_depth": -1.0,
+        "tools_cutout_mb_dia": 0.6,
+        "tools_cutout_mb_spacing": 0.3,
 
         # Paint Tool
         "tools_painttooldia": 0.3,

+ 4 - 4
tclCommands/TclCommandCutout.py

@@ -73,22 +73,22 @@ class TclCommandCutout(TclCommand):
         if 'margin' in args:
             margin_par = float(args['margin'])
         else:
-            margin_par = float(self.app.defaults["tools_cutoutmargin"])
+            margin_par = float(self.app.defaults["tools_cutout_margin"])
 
         if 'dia' in args:
             dia_par = float(args['dia'])
         else:
-            dia_par = float(self.app.defaults["tools_cutouttooldia"])
+            dia_par = float(self.app.defaults["tools_cutout_tooldia"])
 
         if 'gaps' in args:
             gaps_par = args['gaps']
         else:
-            gaps_par = str(self.app.defaults["tools_gaps_ff"])
+            gaps_par = str(self.app.defaults["tools_cutout_gaps_ff"])
 
         if 'gapsize' in args:
             gapsize_par = float(args['gapsize'])
         else:
-            gapsize_par = float(self.app.defaults["tools_cutoutgapsize"])
+            gapsize_par = float(self.app.defaults["tools_cutout_gapsize"])
 
         if 'outname' in args:
             outname = args['outname']

+ 4 - 4
tclCommands/TclCommandGeoCutout.py

@@ -150,22 +150,22 @@ class TclCommandGeoCutout(TclCommandSignaled):
         if 'margin' in args:
             margin = float(args['margin'])
         else:
-            margin = float(self.app.defaults["tools_cutoutmargin"])
+            margin = float(self.app.defaults["tools_cutout_margin"])
 
         if 'dia' in args:
             dia = float(args['dia'])
         else:
-            dia = float(self.app.defaults["tools_cutouttooldia"])
+            dia = float(self.app.defaults["tools_cutout_tooldia"])
 
         if 'gaps' in args:
             gaps = args['gaps']
         else:
-            gaps = str(self.app.defaults["tools_gaps_ff"])
+            gaps = str(self.app.defaults["tools_cutout_gaps_ff"])
 
         if 'gapsize' in args:
             gapsize = float(args['gapsize'])
         else:
-            gapsize = float(self.app.defaults["tools_cutoutgapsize"])
+            gapsize = float(self.app.defaults["tools_cutout_gapsize"])
 
         if 'outname' in args:
             outname = args['outname']