Kaynağa Gözat

- fixed a bug in Tools Database: due of not disconnecting the signals it created a race that was concluded into a RuntimeError exception (an dict changed size during iteration)
- Drilling Tool - working in adding tools auto-load from Tools DB
- some updates to the Excellon Object options
- Drilling Tool - manual add from Tools DB is working

Marius Stanciu 5 yıl önce
ebeveyn
işleme
22d05935e8

+ 7 - 0
CHANGELOG.md

@@ -7,6 +7,13 @@ CHANGELOG for FlatCAM beta
 
 =================================================
 
+13.07.2020
+
+- fixed a bug in Tools Database: due of not disconnecting the signals it created a race that was concluded into a RuntimeError exception (an dict changed size during iteration)
+- Drilling Tool - working in adding tools auto-load from Tools DB
+- some updates to the Excellon Object options
+- Drilling Tool - manual add from Tools DB is working
+
 12.07.2020
 
 - when creating a new FlatCAM object, the options will be updated with FlatCAM tools properties that relate to them

+ 81 - 5
appDatabase.py

@@ -1146,6 +1146,43 @@ class ToolsDB2UI:
         self.grid_tool.addWidget(self.tool_object_label, 2, 0)
         self.grid_tool.addWidget(self.object_type_combo, 2, 1)
 
+        # Tool Tolerance
+        self.tol_label = QtWidgets.QLabel("<b>%s:</b>" % _("Tolerance"))
+        self.tol_label.setToolTip(
+            _("Tool tolerance. If there is a tool in the Excellon object with\n"
+              "the value within the limits then this tool from DB will be used.\n"
+              "This behavior is enabled in the Drilling Tool.")
+        )
+        self.grid_tool.addWidget(self.tol_label, 4, 0, 1, 2)
+
+        # Tolerance Min Limit
+        self.min_limit_label = QtWidgets.QLabel('%s:' % _("Min"))
+        self.min_limit_label.setToolTip(
+            _("Set the tool tolerance minimum.")
+        )
+        self.tol_min_entry = FCDoubleSpinner(callback=self.confirmation_message)
+        self.tol_min_entry.set_precision(self.decimals)
+        self.tol_min_entry.set_range(0, 9999.9999)
+        self.tol_min_entry.setSingleStep(0.1)
+        self.tol_min_entry.setObjectName("gdb_tol_min")
+
+        self.grid_tool.addWidget(self.min_limit_label, 6, 0)
+        self.grid_tool.addWidget(self.tol_min_entry, 6, 1)
+
+        # Tolerance Min Limit
+        self.max_limit_label = QtWidgets.QLabel('%s:' % _("Max"))
+        self.max_limit_label.setToolTip(
+            _("Set the tool tolerance maximum.")
+        )
+        self.tol_max_entry = FCDoubleSpinner(callback=self.confirmation_message)
+        self.tol_max_entry.set_precision(self.decimals)
+        self.tol_max_entry.set_range(0, 9999.9999)
+        self.tol_max_entry.setSingleStep(0.1)
+        self.tol_max_entry.setObjectName("gdb_tol_max")
+
+        self.grid_tool.addWidget(self.max_limit_label, 7, 0)
+        self.grid_tool.addWidget(self.tol_max_entry, 7, 1)
+
         # ###########################################################################
         # ############### BASIC GEOMETRY UI form ####################################
         # ###########################################################################
@@ -1872,10 +1909,6 @@ class ToolsDB2UI:
         self.grid5.addWidget(self.feedrate_rapid_label, 16, 0)
         self.grid5.addWidget(self.feedrate_rapid_entry, 16, 1)
 
-        # default values is to hide
-        self.feedrate_rapid_label.hide()
-        self.feedrate_rapid_entry.hide()
-
         # Spindlespeed
         self.spindle_label = QtWidgets.QLabel('%s:' % _('Spindle speed'))
         self.spindle_label.setToolTip(
@@ -2117,6 +2150,8 @@ class ToolsDB2(QtWidgets.QWidget):
 
         self.form_fields = {
             "object_type":      self.ui.object_type_combo,
+            "tol_min":          self.ui.tol_min_entry,
+            "tol_max":          self.ui.tol_max_entry,
             # Basic
             "name":             self.ui.name_entry,
             "tooldia":          self.ui.dia_entry,
@@ -2187,6 +2222,8 @@ class ToolsDB2(QtWidgets.QWidget):
 
         self.name2option = {
             "gdb_object_type":      "object_type",
+            "gdb_tol_min":          "tol_min",
+            "gdb_tol_max":          "tol_max",
 
             # Basic
             "gdb_name":             "name",
@@ -2319,8 +2356,10 @@ class ToolsDB2(QtWidgets.QWidget):
             self.on_tool_requested_from_app()
 
     def on_list_selection_change(self, current, previous):
+        self.ui_disconnect()
         self.current_toolid = int(current.text(0))
         self.storage_to_form(self.db_tool_dict[current.text(0)])
+        self.ui_connect()
 
     def on_list_item_edited(self, item, column):
         if column == 0:
@@ -2504,6 +2543,8 @@ class ToolsDB2(QtWidgets.QWidget):
             "endz":             float(self.app.defaults["geometry_endz"]),
 
             "object_type":      _("General"),
+            "tol_min":          0.0,
+            "tol_max":          0.0,
 
             # NCC
             "tools_nccoperation":       self.app.defaults["tools_nccoperation"],
@@ -2750,7 +2791,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("")
+                self.ui.save_db_btn.setStyleSheet("")
 
                 # Save Tools DB in a file
                 try:
@@ -2927,6 +2968,11 @@ class ToolsDB2(QtWidgets.QWidget):
         else:
             if wdg_name == "gdb_object_type":
                 self.db_tool_dict[tool_id]['data']['object_type'] = val
+            elif wdg_name == "gdb_tol_min":
+                self.db_tool_dict[tool_id]['data']['tol_min'] = val
+            elif wdg_name == "gdb_tol_max":
+                self.db_tool_dict[tool_id]['data']['tol_max'] = val
+
             elif wdg_name == "gdb_cutz":
                 self.db_tool_dict[tool_id]['data']['cutz'] = val
             elif wdg_name == "gdb_multidepth":
@@ -3002,6 +3048,36 @@ class ToolsDB2(QtWidgets.QWidget):
             elif wdg_name == "gdb_i_iso_type":
                 self.db_tool_dict[tool_id]['data']['tools_iso_isotype'] = val
 
+            # Drilling Tool
+            elif wdg_name == "gdb_e_cutz":
+                self.db_tool_dict[tool_id]['data']['tools_drill_cutz'] = val
+            elif wdg_name == "gdb_e_multidepth":
+                self.db_tool_dict[tool_id]['data']['tools_drill_multidepth'] = val
+            elif wdg_name == "gdb_e_depthperpass":
+                self.db_tool_dict[tool_id]['data']['tools_drill_depthperpass'] = val
+            elif wdg_name == "gdb_e_travelz":
+                self.db_tool_dict[tool_id]['data']['tools_drill_travelz'] = val
+
+            elif wdg_name == "gdb_e_feedratez":
+                self.db_tool_dict[tool_id]['data']['tools_drill_feedrate_z'] = val
+            elif wdg_name == "gdb_e_fr_rapid":
+                self.db_tool_dict[tool_id]['data']['tools_drill_feedrate_rapid'] = val
+            elif wdg_name == "gdb_e_spindlespeed":
+                self.db_tool_dict[tool_id]['data']['tools_drill_spindlespeed'] = val
+            elif wdg_name == "gdb_e_dwell":
+                self.db_tool_dict[tool_id]['data']['tools_drill_dwell'] = val
+            elif wdg_name == "gdb_e_dwelltime":
+                self.db_tool_dict[tool_id]['data']['tools_drill_dwelltime'] = val
+
+            elif wdg_name == "gdb_e_offset":
+                self.db_tool_dict[tool_id]['data']['tools_drill_offset'] = val
+            elif wdg_name == "gdb_e_drill_slots":
+                self.db_tool_dict[tool_id]['data']['tools_drill_drill_slots'] = val
+            elif wdg_name == "gdb_e_drill_slots_over":
+                self.db_tool_dict[tool_id]['data']['tools_drill_drill_overlap'] = val
+            elif wdg_name == "gdb_e_drill_last_drill":
+                self.db_tool_dict[tool_id]['data']['tools_drill_last_drill'] = val
+
         self.callback_app()
 
     def on_tool_requested_from_app(self):

+ 16 - 2
appGUI/preferences/general/GeneralAppSettingsGroupUI.py

@@ -98,12 +98,26 @@ class GeneralAppSettingsGroupUI(OptionsGroupUI2):
         self.workspace_type_label = self.option_dict()["global_workspaceT"].label_widget
         self.workspace_orientation_field = self.option_dict()["global_workspace_orientation"].get_field()
         self.workspace_orientation_label = self.option_dict()["global_workspace_orientation"].label_widget
-        self.wks = OptionalInputSection(self.workspace_enabled_field, [self.workspace_type_label, self.workspace_type_field, self.workspace_orientation_label, self.workspace_orientation_field])
+        self.wks = OptionalInputSection(
+            self.workspace_enabled_field,
+            [
+                self.workspace_type_label,
+                self.workspace_type_field,
+                self.workspace_orientation_label,
+                self.workspace_orientation_field
+            ]
+        )
 
         self.mouse_cursor_color_enabled_field = self.option_dict()["global_cursor_color_enabled"].get_field()
         self.mouse_cursor_color_field = self.option_dict()["global_cursor_color"].get_field()
         self.mouse_cursor_color_label = self.option_dict()["global_cursor_color"].label_widget
-        self.mois = OptionalInputSection(self.mouse_cursor_color_enabled_field, [self.mouse_cursor_color_label, self.mouse_cursor_color_field])
+        self.mois = OptionalInputSection(
+            self.mouse_cursor_color_enabled_field,
+            [
+                self.mouse_cursor_color_label,
+                self.mouse_cursor_color_field
+            ]
+        )
         self.mouse_cursor_color_enabled_field.stateChanged.connect(self.on_mouse_cursor_color_enable)
         self.mouse_cursor_color_field.entry.editingFinished.connect(self.on_mouse_cursor_entry)
 

+ 110 - 130
appObjects/FlatCAMExcellon.py

@@ -53,39 +53,22 @@ class ExcellonObject(FlatCAMObj, Excellon):
             "plot": True,
             "solid": False,
             "multicolored": False,
+            "merge_fuse_tools": True,
 
-            "operation": "drill",
-            "milling_type": "drills",
-
-            "milling_dia": 0.04,
-
-            "cutz": -0.1,
-            "multidepth": False,
-            "depthperpass": 0.7,
-            "travelz": 0.1,
-            "feedrate": self.app.defaults["geometry_feedrate"],
-            "feedrate_z": 5.0,
-            "feedrate_rapid": 5.0,
             "tooldia": 0.1,
+            "milling_dia": 0.04,
             "slot_tooldia": 0.1,
-            "toolchange": False,
-            "toolchangez": 1.0,
-            "toolchangexy": "0.0, 0.0",
-            "extracut": self.app.defaults["geometry_extracut"],
-            "extracut_length": self.app.defaults["geometry_extracut_length"],
-            "endz": 2.0,
-            "endxy": '',
-
-            "startz": None,
-            "offset": 0.0,
-            "spindlespeed": 0,
-            "dwell": True,
-            "dwelltime": 1000,
-            "ppname_e": 'default',
-            "ppname_g": self.app.defaults["geometry_ppname_g"],
-            "z_pdepth": -0.02,
-            "feedrate_probe": 3.0,
+
+            "format_upper_in": 2,
+            "format_lower_in": 4,
+            "format_upper_mm": 3,
+            "lower_mm": 3,
+            "zeros": "T",
+            "units": "INCH",
+            "update": True,
+
             "optimization_type": "B",
+            "search_time": 3
         })
 
         # TODO: Document this.
@@ -99,14 +82,6 @@ class ExcellonObject(FlatCAMObj, Excellon):
         # default set of data to be added to each tool in self.tools as self.tools[tool]['data'] = self.default_data
         self.default_data = {}
 
-        # fill in self.default_data values from self.options
-        for opt_key, opt_val in self.app.options.items():
-            if opt_key.find('excellon_') == 0:
-                self.default_data[opt_key] = deepcopy(opt_val)
-        for opt_key, opt_val in self.app.options.items():
-            if opt_key.find('geometry_') == 0:
-                self.default_data[opt_key] = deepcopy(opt_val)
-
         # variable to store the total amount of drills per job
         self.tot_drill_cnt = 0
         self.tool_row = 0
@@ -133,99 +108,6 @@ class ExcellonObject(FlatCAMObj, Excellon):
         # from predecessors.
         self.ser_attrs += ['options', 'kind', 'fill_color', 'outline_color', 'alpha_level']
 
-    @staticmethod
-    def merge(exc_list, exc_final, decimals=None, fuse_tools=True):
-        """
-        Merge Excellon objects found in exc_list parameter into exc_final object.
-        Options are always copied from source .
-
-        Tools are disregarded, what is taken in consideration is the unique drill diameters found as values in the
-        exc_list tools dict's. In the reconstruction section for each unique tool diameter it will be created a
-        tool_name to be used in the final Excellon object, exc_final.
-
-        If only one object is in exc_list parameter then this function will copy that object in the exc_final
-
-        :param exc_list:    List or one object of ExcellonObject Objects to join.
-        :type exc_list:     list
-        :param exc_final:   Destination ExcellonObject object.
-        :type exc_final:    class
-        :param decimals:    The number of decimals to be used for diameters
-        :type decimals:     int
-        :param fuse_tools:  If True will try to fuse tools of the same diameter for the Excellon objects
-        :type fuse_tools:   bool
-        :return:            None
-        """
-
-        if exc_final.tools is None:
-            exc_final.tools = {}
-
-        if decimals is None:
-            decimals = 4
-        decimals_exc = decimals
-
-        try:
-            flattened_list = list(itertools.chain(*exc_list))
-        except TypeError:
-            flattened_list = exc_list
-
-        new_tools = {}
-        total_geo = []
-        toolid = 0
-        for exc in flattened_list:
-            # copy options of the current excellon obj to the final excellon obj
-            # only the last object options will survive
-            for option in exc.options:
-                if option != 'name':
-                    try:
-                        exc_final.options[option] = exc.options[option]
-                    except Exception:
-                        exc.app.log.warning("Failed to copy option.", option)
-
-            for tool in exc.tools:
-                toolid += 1
-                new_tools[toolid] = exc.tools[tool]
-
-            exc_final.tools = deepcopy(new_tools)
-            # add the zeros and units to the exc_final object
-            exc_final.zeros = exc.zeros
-            exc_final.units = exc.units
-            total_geo += exc.solid_geometry
-
-        exc_final.solid_geometry = total_geo
-
-        fused_tools_dict = {}
-        if exc_final.tools and fuse_tools:
-            toolid = 0
-            for tool, tool_dict in exc_final.tools.items():
-                current_tooldia = float('%.*f' % (decimals_exc, tool_dict['tooldia']))
-                toolid += 1
-
-                # calculate all diameters in fused_tools_dict
-                all_dia = []
-                if fused_tools_dict:
-                    for f_tool in fused_tools_dict:
-                        all_dia.append(float('%.*f' % (decimals_exc, fused_tools_dict[f_tool]['tooldia'])))
-
-                if current_tooldia in all_dia:
-                    # find tool for current_tooldia in fuse_tools
-                    t = None
-                    for f_tool in fused_tools_dict:
-                        if fused_tools_dict[f_tool]['tooldia'] == current_tooldia:
-                            t = f_tool
-                            break
-                    if t:
-                        fused_tools_dict[t]['drills'] += tool_dict['drills']
-                        fused_tools_dict[t]['slots'] += tool_dict['slots']
-                        fused_tools_dict[t]['solid_geometry'] += tool_dict['solid_geometry']
-                else:
-                    fused_tools_dict[toolid] = tool_dict
-                    fused_tools_dict[toolid]['tooldia'] = current_tooldia
-
-            exc_final.tools = fused_tools_dict
-
-        # create the geometry for the exc_final object
-        exc_final.create_geometry()
-
     def set_ui(self, ui):
         """
         Configures the user interface for this object.
@@ -246,6 +128,11 @@ class ExcellonObject(FlatCAMObj, Excellon):
             if opt_key.find('tools_drill_') == 0:
                 self.options[opt_key] = deepcopy(opt_val)
 
+        # fill in self.default_data values from self.options
+        for opt_key, opt_val in self.app.options.items():
+            if opt_key.find('excellon_') == 0 or opt_key.find('tools_drill_') == 0:
+                self.default_data[opt_key] = deepcopy(opt_val)
+
         self.form_fields.update({
             "plot":             self.ui.plot_cb,
             "solid":            self.ui.solid_cb,
@@ -1310,3 +1197,96 @@ class ExcellonObject(FlatCAMObj, Excellon):
             self.shapes.redraw()
         except (ObjectDeleted, AttributeError):
             self.shapes.clear(update=True)
+
+    @staticmethod
+    def merge(exc_list, exc_final, decimals=None, fuse_tools=True):
+        """
+        Merge Excellon objects found in exc_list parameter into exc_final object.
+        Options are always copied from source .
+
+        Tools are disregarded, what is taken in consideration is the unique drill diameters found as values in the
+        exc_list tools dict's. In the reconstruction section for each unique tool diameter it will be created a
+        tool_name to be used in the final Excellon object, exc_final.
+
+        If only one object is in exc_list parameter then this function will copy that object in the exc_final
+
+        :param exc_list:    List or one object of ExcellonObject Objects to join.
+        :type exc_list:     list
+        :param exc_final:   Destination ExcellonObject object.
+        :type exc_final:    class
+        :param decimals:    The number of decimals to be used for diameters
+        :type decimals:     int
+        :param fuse_tools:  If True will try to fuse tools of the same diameter for the Excellon objects
+        :type fuse_tools:   bool
+        :return:            None
+        """
+
+        if exc_final.tools is None:
+            exc_final.tools = {}
+
+        if decimals is None:
+            decimals = 4
+        decimals_exc = decimals
+
+        try:
+            flattened_list = list(itertools.chain(*exc_list))
+        except TypeError:
+            flattened_list = exc_list
+
+        new_tools = {}
+        total_geo = []
+        toolid = 0
+        for exc in flattened_list:
+            # copy options of the current excellon obj to the final excellon obj
+            # only the last object options will survive
+            for option in exc.options:
+                if option != 'name':
+                    try:
+                        exc_final.options[option] = exc.options[option]
+                    except Exception:
+                        exc.app.log.warning("Failed to copy option.", option)
+
+            for tool in exc.tools:
+                toolid += 1
+                new_tools[toolid] = exc.tools[tool]
+
+            exc_final.tools = deepcopy(new_tools)
+            # add the zeros and units to the exc_final object
+            exc_final.zeros = exc.zeros
+            exc_final.units = exc.units
+            total_geo += exc.solid_geometry
+
+        exc_final.solid_geometry = total_geo
+
+        fused_tools_dict = {}
+        if exc_final.tools and fuse_tools:
+            toolid = 0
+            for tool, tool_dict in exc_final.tools.items():
+                current_tooldia = float('%.*f' % (decimals_exc, tool_dict['tooldia']))
+                toolid += 1
+
+                # calculate all diameters in fused_tools_dict
+                all_dia = []
+                if fused_tools_dict:
+                    for f_tool in fused_tools_dict:
+                        all_dia.append(float('%.*f' % (decimals_exc, fused_tools_dict[f_tool]['tooldia'])))
+
+                if current_tooldia in all_dia:
+                    # find tool for current_tooldia in fuse_tools
+                    t = None
+                    for f_tool in fused_tools_dict:
+                        if fused_tools_dict[f_tool]['tooldia'] == current_tooldia:
+                            t = f_tool
+                            break
+                    if t:
+                        fused_tools_dict[t]['drills'] += tool_dict['drills']
+                        fused_tools_dict[t]['slots'] += tool_dict['slots']
+                        fused_tools_dict[t]['solid_geometry'] += tool_dict['solid_geometry']
+                else:
+                    fused_tools_dict[toolid] = tool_dict
+                    fused_tools_dict[toolid]['tooldia'] = current_tooldia
+
+            exc_final.tools = fused_tools_dict
+
+        # create the geometry for the exc_final object
+        exc_final.create_geometry()

+ 115 - 37
appTools/ToolDrilling.py

@@ -16,7 +16,10 @@ from copy import deepcopy
 
 import numpy as np
 
-from shapely.geometry import Point, LineString
+from shapely.geometry import LineString
+
+import json
+import sys
 
 from matplotlib.backend_bases import KeyEvent as mpl_key_event
 
@@ -122,42 +125,45 @@ class ToolDrilling(AppTool, Excellon):
         self.area_sel_disconnect_flag = False
         self.poly_sel_disconnect_flag = False
 
+        # Tools Database
+        self.tools_db_dict = None
+
         self.form_fields = {
-            "cutz":             self.t_ui.cutz_entry,
-            "multidepth":       self.t_ui.mpass_cb,
-            "depthperpass":     self.t_ui.maxdepth_entry,
-            "travelz":          self.t_ui.travelz_entry,
-            "feedrate_z":       self.t_ui.feedrate_z_entry,
-            "feedrate_rapid":   self.t_ui.feedrate_rapid_entry,
-
-            "spindlespeed":     self.t_ui.spindlespeed_entry,
-            "dwell":            self.t_ui.dwell_cb,
-            "dwelltime":        self.t_ui.dwelltime_entry,
-
-            "offset":           self.t_ui.offset_entry,
-
-            "drill_slots":      self.t_ui.drill_slots_cb,
-            "drill_overlap":    self.t_ui.drill_overlap_entry,
-            "last_drill":       self.t_ui.last_drill_cb
+            "tools_drill_cutz":             self.t_ui.cutz_entry,
+            "tools_drill_multidepth":       self.t_ui.mpass_cb,
+            "tools_drill_depthperpass":     self.t_ui.maxdepth_entry,
+            "tools_drill_travelz":          self.t_ui.travelz_entry,
+            "tools_drill_feedrate_z":       self.t_ui.feedrate_z_entry,
+            "tools_drill_feedrate_rapid":   self.t_ui.feedrate_rapid_entry,
+
+            "tools_drill_spindlespeed":     self.t_ui.spindlespeed_entry,
+            "tools_drill_dwell":            self.t_ui.dwell_cb,
+            "tools_drill_dwelltime":        self.t_ui.dwelltime_entry,
+
+            "tools_drill_offset":           self.t_ui.offset_entry,
+
+            "tools_drill_drill_slots":      self.t_ui.drill_slots_cb,
+            "tools_drill_drill_overlap":    self.t_ui.drill_overlap_entry,
+            "tools_drill_last_drill":       self.t_ui.last_drill_cb
         }
 
         self.name2option = {
-            "e_cutz":                   "cutz",
-            "e_multidepth":             "multidepth",
-            "e_depthperpass":           "depthperpass",
-            "e_travelz":                "travelz",
-            "e_feedratez":              "feedrate_z",
-            "e_fr_rapid":               "feedrate_rapid",
-
-            "e_spindlespeed":           "spindlespeed",
-            "e_dwell":                  "dwell",
-            "e_dwelltime":              "dwelltime",
-
-            "e_offset":                 "offset",
-
-            "e_drill_slots":            "drill_slots",
-            "e_drill_slots_overlap":    "drill_overlap",
-            "e_drill_last_drill":       "last_drill",
+            "e_cutz":                   "tools_drill_cutz",
+            "e_multidepth":             "tools_drill_multidepth",
+            "e_depthperpass":           "tools_drill_depthperpass",
+            "e_travelz":                "tools_drill_travelz",
+            "e_feedratez":              "tools_drill_feedrate_z",
+            "e_fr_rapid":               "tools_drill_feedrate_rapid",
+
+            "e_spindlespeed":           "tools_drill_spindlespeed",
+            "e_dwell":                  "tools_drill_dwell",
+            "e_dwelltime":              "tools_drill_dwelltime",
+
+            "e_offset":                 "tools_drill_offset",
+
+            "e_drill_slots":            "tools_drill_drill_slots",
+            "e_drill_slots_overlap":    "tools_drill_drill_overlap",
+            "e_drill_last_drill":       "tools_drill_last_drill",
         }
 
         self.poly_drawn = False
@@ -205,6 +211,8 @@ class ToolDrilling(AppTool, Excellon):
         # ############################ SIGNALS ########################################
         # #############################################################################
 
+        self.t_ui.manual_load_db_btn.clicked.connect(self.on_tool_db_load)
+
         self.t_ui.apply_param_to_all.clicked.connect(self.on_apply_param_to_all_clicked)
         self.t_ui.generate_cnc_button.clicked.connect(self.on_cnc_button_click)
         self.t_ui.tools_table.drag_drop_sig.connect(self.rebuild_ui)
@@ -344,7 +352,7 @@ class ToolDrilling(AppTool, Excellon):
 
         # fill in self.default_data values from self.options
         for opt_key, opt_val in self.app.options.items():
-            if opt_key.find('excellon_') == 0:
+            if opt_key.find('excellon_') == 0 or opt_key.find('tools_drill_') == 0:
                 self.default_data[opt_key] = deepcopy(opt_val)
 
         self.first_click = False
@@ -403,6 +411,7 @@ class ToolDrilling(AppTool, Excellon):
         self.t_ui.drill_overlap_label.hide()
         self.t_ui.drill_overlap_entry.hide()
         self.t_ui.last_drill_cb.hide()
+
         # if the app mode is Basic then disable this feature
         if app_mode == 'b':
             self.t_ui.drill_slots_cb.set_value(False)
@@ -697,12 +706,16 @@ class ToolDrilling(AppTool, Excellon):
             self.t_ui.exc_param_frame.setDisabled(True)
             self.set_tool_ui()
         else:
-            self.excellon_tools = self.excellon_obj.tools
             self.app.collection.set_active(self.obj_name)
             self.t_ui.exc_param_frame.setDisabled(False)
-            self.excellon_tools = self.excellon_obj.tools
 
-            self.build_tool_ui()
+            if self.t_ui.autoload_db_cb.get_value():
+                self.excellon_tools = self.excellon_obj.tools
+                self.on_tool_db_load()
+            else:
+                # self.on_tool_db_load() already build once the tool UI, no need to do it twice
+                self.excellon_tools = self.excellon_obj.tools
+                self.build_tool_ui()
 
         sel_rows = set()
         table_items = self.t_ui.tools_table.selectedItems()
@@ -795,6 +808,71 @@ class ToolDrilling(AppTool, Excellon):
         except (TypeError, ValueError):
             pass
 
+    def on_tool_db_load(self):
+
+        filename = self.app.data_path + '\\geo_tools_db.FlatDB'
+
+        # 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."))
+            return
+
+        try:
+            self.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."))
+            return
+
+        self.replace_tools()
+
+    def replace_tools(self):
+        log.debug("ToolDrilling.replace_tools()")
+
+        if self.excellon_obj:
+            new_tools_dict = deepcopy(self.excellon_tools)
+
+            for orig_tool, orig_tool_val in self.excellon_tools.items():
+                orig_tooldia = orig_tool_val['tooldia']
+
+                # look in database tools
+                for db_tool, db_tool_val in self.tools_db_dict.items():
+                    db_tooldia = db_tool_val['tooldia']
+                    low_limit = float(db_tool_val['data']['tol_min'])
+                    high_limit = float(db_tool_val['data']['tol_max'])
+
+                    # if we find a tool with the same diameter in the Tools DB just update it's data
+                    if orig_tooldia == db_tooldia:
+                        for d in db_tool_val['data']:
+                            if d.find('tools_drill') == 0:
+                                new_tools_dict[orig_tool]['data'][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[orig_tool]['data'][d] = db_tool_val['data'][d]
+                    # search for a tool that has a tolerance that the tool fits in
+                    elif high_limit >= orig_tooldia >= low_limit:
+                        new_tools_dict[orig_tool]['tooldia'] = db_tooldia
+                        for d in db_tool_val['data']:
+                            if d.find('tools_drill') == 0:
+                                new_tools_dict[orig_tool]['data'][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[orig_tool]['data'][d] = db_tool_val['data'][d]
+
+            self.excellon_tools = new_tools_dict
+            for td in self.excellon_tools:
+                print(td, self.excellon_tools[td])
+            self.build_tool_ui()
+
     def on_toggle_all_rows(self):
         """
         will toggle the selection of all rows in Tools table

+ 3 - 7
app_Main.py

@@ -8935,22 +8935,18 @@ class App(QtCore.QObject):
 
         # How the object should be initialized
         def obj_init(excellon_obj, app_obj):
-
             try:
                 ret = excellon_obj.parse_file(filename=filename)
                 if ret == "fail":
                     log.debug("Excellon parsing failed.")
-                    self.inform.emit('[ERROR_NOTCL] %s' %
-                                     _("This is not Excellon file."))
+                    self.inform.emit('[ERROR_NOTCL] %s' % _("This is not Excellon file."))
                     return "fail"
             except IOError:
-                app_obj.inform.emit('[ERROR_NOTCL] %s: %s' %
-                                    (_("Cannot open file"), filename))
+                app_obj.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Cannot open file"), filename))
                 log.debug("Could not open Excellon object.")
                 return "fail"
             except Exception:
-                msg = '[ERROR_NOTCL] %s' % \
-                      _("An internal error has occurred. See shell.\n")
+                msg = '[ERROR_NOTCL] %s' % _("An internal error has occurred. See shell.\n")
                 msg += traceback.format_exc()
                 app_obj.inform.emit(msg)
                 return "fail"