Sfoglia il codice sorgente

- fixed issues in the Tools Database due of recent changes in how the data structure is created
- made sure that the right tools go only to the intended use, in Tools Database otherwise an error status message is created and Tools DB is closed on adding a wrong tool
- fixed the usage for Tools Database in Unix-like OS's
- done some modest refactoring
- fixed the Search and Add feature in Geometry Object UI

Marius Stanciu 5 anni fa
parent
commit
38abfa4f31

+ 5 - 0
CHANGELOG.md

@@ -18,6 +18,11 @@ CHANGELOG for FlatCAM beta
 - fixed the sizePolicy for the FCComboBox widgets in the Preferences that holds the preprocessors
 - fixed issue with how the preamble / postamble GCode were inserted into the final GCode
 - fixed a small issue in GCode Editor where the signals for the buttons were attached again at each launch of the GCode Editor
+- fixed issues in the Tools Database due of recent changes in how the data structure is created
+- made sure that the right tools go only to the intended use, in Tools Database otherwise an error status message is created and Tools DB is closed on adding a wrong tool
+- fixed the usage for Tools Database in Unix-like OS's
+- done some modest refactoring
+- fixed the Search and Add feature in Geometry Object UI
 
 28.10.2020
 

+ 26 - 24
appDatabase.py

@@ -1,6 +1,6 @@
 from PyQt5 import QtGui, QtCore, QtWidgets
 from appGUI.GUIElements import FCEntry, FCButton, FCDoubleSpinner, FCComboBox, FCCheckBox, FCSpinner, \
-    FCTree, RadioSet, FCFileSaveDialog, FCLabel
+    FCTree, RadioSet, FCFileSaveDialog, FCLabel, FCComboBox2
 from camlib import to_dict
 
 import sys
@@ -25,6 +25,10 @@ class ToolsDB2UI:
         self.app = app
         self.decimals = self.app.decimals
 
+        self.offset_item_options = ["Path", "In", "Out", "Custom"]
+        self.type_item_options = [_("Iso"), _("Rough"), _("Finish")]
+        self.tool_type_item_options = ["C1", "C2", "C3", "C4", "B", "V"]
+
         settings = QtCore.QSettings("Open Source", "FlatCAM")
         if settings.contains("machinist"):
             self.machinist_setting = settings.value('machinist', type=int)
@@ -269,7 +273,7 @@ class ToolsDB2UI:
         self.tool_op_label.setToolTip(
             _("The kind of Application Tool where this tool is to be used."))
 
-        self.tool_op_combo = FCComboBox()
+        self.tool_op_combo = FCComboBox2()
         self.tool_op_combo.addItems(
             [_("General"), _("Milling"), _("Drilling"), _('Isolation'), _('Paint'), _('NCC'), _('Cutout')])
         self.tool_op_combo.setObjectName('gdb_tool_target')
@@ -296,7 +300,7 @@ class ToolsDB2UI:
               "V = v-shape milling tool"))
 
         self.mill_shape_combo = FCComboBox()
-        self.mill_shape_combo.addItems(["C1", "C2", "C3", "C4", "B", "V"])
+        self.mill_shape_combo.addItems(self.tool_type_item_options)
         self.mill_shape_combo.setObjectName('gdb_shape')
 
         self.grid0.addWidget(self.shape_label, 2, 0)
@@ -345,7 +349,7 @@ class ToolsDB2UI:
               "Finish = finishing cut, high feedrate"))
 
         self.mill_type_combo = FCComboBox()
-        self.mill_type_combo.addItems(["Iso", "Rough", "Finish"])
+        self.mill_type_combo.addItems(self.type_item_options)
         self.mill_type_combo.setObjectName('gdb_type')
 
         self.grid0.addWidget(self.type_label, 10, 0)
@@ -362,7 +366,7 @@ class ToolsDB2UI:
               "Custom = custom offset using the Custom Offset value"))
 
         self.mill_tooloffset_combo = FCComboBox()
-        self.mill_tooloffset_combo.addItems(["Path", "In", "Out", "Custom"])
+        self.mill_tooloffset_combo.addItems(self.offset_item_options)
         self.mill_tooloffset_combo.setObjectName('gdb_tool_offset')
 
         self.grid0.addWidget(self.tooloffset_label, 12, 0)
@@ -663,7 +667,7 @@ class ToolsDB2UI:
               "- Line-based: Parallel lines.")
         )
 
-        self.ncc_method_combo = FCComboBox()
+        self.ncc_method_combo = FCComboBox2()
         self.ncc_method_combo.addItems(
             [_("Standard"), _("Seed"), _("Lines"), _("Combo")]
         )
@@ -778,7 +782,7 @@ class ToolsDB2UI:
               "in the order specified.")
         )
 
-        self.paint_method_combo = FCComboBox()
+        self.paint_method_combo = FCComboBox2()
         self.paint_method_combo.addItems(
             [_("Standard"), _("Seed"), _("Lines"), _("Laser_lines"), _("Combo")]
         )
@@ -876,8 +880,8 @@ class ToolsDB2UI:
 
         self.iso_follow_cb = FCCheckBox()
         self.iso_follow_cb.setToolTip(_("Generate a 'Follow' geometry.\n"
-                                    "This means that it will cut through\n"
-                                    "the middle of the trace."))
+                                        "This means that it will cut through\n"
+                                        "the middle of the trace."))
         self.iso_follow_cb.setObjectName("gdb_i_follow")
 
         self.grid4.addWidget(self.follow_label, 6, 0)
@@ -1402,9 +1406,7 @@ class ToolsDB2(QtWidgets.QWidget):
 
         self.on_tool_request = callback_on_tool_request
 
-        self.offset_item_options = ["Path", "In", "Out", "Custom"]
-        self.type_item_options = ["Iso", "Rough", "Finish"]
-        self.tool_type_item_options = ["C1", "C2", "C3", "C4", "B", "V"]
+        self.tools_db_changed_flag = False
 
         '''
         dict to hold all the tools in the Tools DB
@@ -1717,7 +1719,7 @@ class ToolsDB2(QtWidgets.QWidget):
         self.ui_connect()
 
     def setup_db_ui(self):
-        filename = self.app.data_path + '\\tools_db.FlatDB'
+        filename = self.app.tools_database_path()
 
         # load the database tools from the file
         try:
@@ -1809,7 +1811,7 @@ class ToolsDB2(QtWidgets.QWidget):
 
         self.ui.tool_description_box.setEnabled(True)
         if self.db_tool_dict:
-            if tool_target == _("General"):
+            if tool_target == 0:    # _("General")
                 self.ui.milling_box.setEnabled(True)
                 self.ui.ncc_box.setEnabled(True)
                 self.ui.paint_box.setEnabled(True)
@@ -1831,33 +1833,33 @@ class ToolsDB2(QtWidgets.QWidget):
                 self.ui.drill_box.hide()
                 self.ui.cutout_box.hide()
 
-                if tool_target == _("Milling"):
+                if tool_target == 1:    # _("Milling")
                     self.ui.milling_box.setEnabled(True)
                     self.ui.milling_box.show()
 
-                if tool_target == _("Drilling"):
+                if tool_target == 2:    # _("Drilling")
                     self.ui.drill_box.setEnabled(True)
                     self.ui.drill_box.show()
 
-                if tool_target == _("Isolation"):
+                if tool_target == 3:    # _("Isolation")
                     self.ui.iso_box.setEnabled(True)
                     self.ui.iso_box.show()
                     self.ui.milling_box.setEnabled(True)
                     self.ui.milling_box.show()
 
-                if tool_target == _("Paint"):
+                if tool_target == 4:    # _("Paint")
                     self.ui.paint_box.setEnabled(True)
                     self.ui.paint_box.show()
                     self.ui.milling_box.setEnabled(True)
                     self.ui.milling_box.show()
 
-                if tool_target == _("NCC"):
+                if tool_target == 5:    # _("NCC")
                     self.ui.ncc_box.setEnabled(True)
                     self.ui.ncc_box.show()
                     self.ui.milling_box.setEnabled(True)
                     self.ui.milling_box.show()
 
-                if tool_target == _("Cutout"):
+                if tool_target == 6:    # _("Cutout")
                     self.ui.cutout_box.setEnabled(True)
                     self.ui.cutout_box.show()
                     self.ui.milling_box.setEnabled(True)
@@ -1872,7 +1874,7 @@ class ToolsDB2(QtWidgets.QWidget):
         default_data = {}
         default_data.update({
             "plot":             True,
-            "tool_target": _("General"),
+            "tool_target": 0,   # _("General")
             "tol_min": 0.0,
             "tol_max": 0.0,
 
@@ -2148,7 +2150,7 @@ class ToolsDB2(QtWidgets.QWidget):
     def on_save_tools_db(self, silent=False):
         self.app.log.debug("ToolsDB.on_save_button() --> Saving Tools Database to file.")
 
-        filename = self.app.data_path + "/tools_db.FlatDB"
+        filename = self.app.tools_database_path()
 
         # Preferences save, update the color of the Tools DB Tab text
         for idx in range(self.app_ui.plot_tab_area.count()):
@@ -2884,7 +2886,7 @@ class ToolsDB2(QtWidgets.QWidget):
 #               "A position on Z plane to move immediately after job stop."))
 #
 #     def setup_db_ui(self):
-#         filename = self.app.data_path + '/tools_db.FlatDB'
+#         filename = self.app.tools_database_path()
 #
 #         # load the database tools from the file
 #         try:
@@ -3321,7 +3323,7 @@ class ToolsDB2(QtWidgets.QWidget):
 #     def on_save_tools_db(self, silent=False):
 #         self.app.log.debug("ToolsDB.on_save_button() --> Saving Tools Database to file.")
 #
-#         filename = self.app.data_path + "/tools_db.FlatDB"
+#         filename = self.app.tools_database_path()
 #
 #         # Preferences save, update the color of the Tools DB Tab text
 #         for idx in range(self.app_ui.plot_tab_area.count()):

+ 38 - 26
appGUI/MainGUI.py

@@ -1885,6 +1885,17 @@ class MainGUI(QtWidgets.QMainWindow):
         self.infobar.setContextMenuPolicy(QtCore.Qt.ActionsContextMenu)
         self.build_infobar_context_menu()
 
+    def set_ui_title(self, name):
+        """
+        Sets the title of the main window.
+
+        :param name: String that store the project path and project name
+        :return: None
+        """
+        title = 'FlatCAM %s %s - %s - [%s]    %s' % (
+            self.app.version, ('BETA' if self.app.beta else ''), platform.architecture()[0], self.app.engine, name)
+        self.setWindowTitle(title)
+
     def save_geometry(self, x, y, width, height, notebook_width):
         """
         Will save the application geometry and positions in the defaults dicitionary to be restored at the next
@@ -2089,6 +2100,7 @@ class MainGUI(QtWidgets.QMainWindow):
         resource_loc = self.app.resource_location
 
         response = None
+        bt_yes = None
         if forced_clear is False:
             msgbox = QtWidgets.QMessageBox()
             msgbox.setText(_("Are you sure you want to delete the GUI Settings? \n"))
@@ -4449,32 +4461,32 @@ class ShortcutsTab(QtWidgets.QWidget):
 
                     # ALT section
                     _('Alt+A'), _("Align Objects Tool"),
-                    _('Alt+C'),_("Calculators Tool"),
-                    _('Alt+D'),_("2-Sided PCB Tool"),
-                    _('Alt+E'),_("Extract Drills Tool"),
-                    _('Alt+F'),_("Fiducials Tool"),
-                    _('Alt+G'),_("Invert Gerber Tool"),
-                    _('Alt+H'),_("Punch Gerber Tool"),
-                    _('Alt+I'),_("Isolation Tool"),
-                    _('Alt+J'),_("Copper Thieving Tool"),
-                    _('Alt+K'),_("Solder Paste Dispensing Tool"),
-                    _('Alt+L'),_("Film PCB Tool"),
-                    _('Alt+M'),_("Corner Markers Tool"),
-                    _('Alt+N'),_("Non-Copper Clearing Tool"),
-                    _('Alt+O'),_("Optimal Tool"),
-                    _('Alt+P'),_("Paint Area Tool"),
-                    _('Alt+Q'),_("QRCode Tool"),
-                    _('Alt+R'),_("Rules Check Tool"),
-                    _('Alt+S'),_("View File Source"),
-                    _('Alt+T'),_("Transformations Tool"),
-                    _('Alt+W'),_("Subtract Tool"),
-                    _('Alt+X'),_("Cutout PCB Tool"),
-                    _('Alt+Z'),_("Panelize PCB"),
-                    _('Alt+1'),_("Enable all"),
-                    _('Alt+2'),_("Disable all"),
-                    _('Alt+3'),_("Enable Non-selected Objects"),
-                    _('Alt+4'),_("Disable Non-selected Objects"),
-                    _('Alt+F10'),_("Toggle Full Screen"),
+                    _('Alt+C'), _("Calculators Tool"),
+                    _('Alt+D'), _("2-Sided PCB Tool"),
+                    _('Alt+E'), _("Extract Drills Tool"),
+                    _('Alt+F'), _("Fiducials Tool"),
+                    _('Alt+G'), _("Invert Gerber Tool"),
+                    _('Alt+H'), _("Punch Gerber Tool"),
+                    _('Alt+I'), _("Isolation Tool"),
+                    _('Alt+J'), _("Copper Thieving Tool"),
+                    _('Alt+K'), _("Solder Paste Dispensing Tool"),
+                    _('Alt+L'), _("Film PCB Tool"),
+                    _('Alt+M'), _("Corner Markers Tool"),
+                    _('Alt+N'), _("Non-Copper Clearing Tool"),
+                    _('Alt+O'), _("Optimal Tool"),
+                    _('Alt+P'), _("Paint Area Tool"),
+                    _('Alt+Q'), _("QRCode Tool"),
+                    _('Alt+R'), _("Rules Check Tool"),
+                    _('Alt+S'), _("View File Source"),
+                    _('Alt+T'), _("Transformations Tool"),
+                    _('Alt+W'), _("Subtract Tool"),
+                    _('Alt+X'), _("Cutout PCB Tool"),
+                    _('Alt+Z'), _("Panelize PCB"),
+                    _('Alt+1'), _("Enable all"),
+                    _('Alt+2'), _("Disable all"),
+                    _('Alt+3'), _("Enable Non-selected Objects"),
+                    _('Alt+4'), _("Disable Non-selected Objects"),
+                    _('Alt+F10'), _("Toggle Full Screen"),
 
                     # CTRL + ALT section
                     _('Ctrl+Alt+X'), _("Abort current task (gracefully)"),

+ 4 - 4
appGUI/ObjectUI.py

@@ -1093,9 +1093,9 @@ class GeometryObjectUI(ObjectUI):
 
         bhlay = QtWidgets.QHBoxLayout()
 
-        self.addtool_btn = QtWidgets.QPushButton(_('Search and Add'))
-        self.addtool_btn.setIcon(QtGui.QIcon(self.app.resource_location + '/plus16.png'))
-        self.addtool_btn.setToolTip(
+        self.search_and_add_btn = QtWidgets.QPushButton(_('Search and Add'))
+        self.search_and_add_btn.setIcon(QtGui.QIcon(self.app.resource_location + '/plus16.png'))
+        self.search_and_add_btn.setToolTip(
             _("Add a new tool to the Tool Table\n"
               "with the diameter specified above.")
         )
@@ -1109,7 +1109,7 @@ class GeometryObjectUI(ObjectUI):
               "Menu: Options -> Tools Database")
         )
 
-        bhlay.addWidget(self.addtool_btn)
+        bhlay.addWidget(self.search_and_add_btn)
         bhlay.addWidget(self.addtool_from_db_btn)
 
         grid1.addLayout(bhlay, 5, 0, 1, 2)

+ 145 - 11
appObjects/FlatCAMGeometry.py

@@ -25,6 +25,8 @@ import traceback
 from collections import defaultdict
 from functools import reduce
 
+import simplejson as json
+
 import gettext
 import appTranslation as fcTranslate
 import builtins
@@ -585,7 +587,7 @@ class GeometryObject(FlatCAMObj, Geometry):
             # self.ui.geo_tools_table.setColumnHidden(4, True)
             self.ui.addtool_entry_lbl.hide()
             self.ui.addtool_entry.hide()
-            self.ui.addtool_btn.hide()
+            self.ui.search_and_add_btn.hide()
             self.ui.deltool_btn.hide()
             # self.ui.endz_label.hide()
             # self.ui.endz_entry.hide()
@@ -745,10 +747,9 @@ class GeometryObject(FlatCAMObj, Geometry):
                 self.ui.geo_tools_table.cellWidget(row, col).currentIndexChanged.connect(
                     self.on_tooltable_cellwidget_change)
 
-        # I use lambda's because the connected functions have parameters that could be used in certain scenarios
-        self.ui.addtool_btn.clicked.connect(lambda: self.on_tool_add())
+        self.ui.search_and_add_btn.clicked.connect(self.on_tool_add)
 
-        self.ui.deltool_btn.clicked.connect(lambda: self.on_tool_delete())
+        self.ui.deltool_btn.clicked.connect(self.on_tool_delete)
 
         self.ui.geo_tools_table.clicked.connect(self.on_row_selection_change)
         self.ui.geo_tools_table.horizontalHeader().sectionClicked.connect(self.on_toggle_all_rows)
@@ -808,7 +809,7 @@ class GeometryObject(FlatCAMObj, Geometry):
                     pass
 
         try:
-            self.ui.addtool_btn.clicked.disconnect()
+            self.ui.search_and_add_btn.clicked.disconnect()
         except (TypeError, AttributeError):
             pass
 
@@ -1015,18 +1016,150 @@ class GeometryObject(FlatCAMObj, Geometry):
         self.ui_connect()
 
     def on_tool_add(self, dia=None, new_geo=None):
+        log.debug("GeometryObject.on_add_tool()")
+
         self.ui_disconnect()
 
-        self.units = self.app.defaults['units'].upper()
+        filename = self.app.tools_database_path()
 
-        tooldia = dia if dia is not None else float(self.ui.addtool_entry.get_value())
+        tool_dia = dia if dia is not None else self.ui.addtool_entry.get_value()
+        # construct a list of all 'tooluid' in the self.iso_tools
+        tool_uid_list = [int(tooluid_key) for tooluid_key in self.tools]
+
+        # find maximum from the temp_uid, add 1 and this is the new 'tooluid'
+        max_uid = 0 if not tool_uid_list else max(tool_uid_list)
+        tooluid = int(max_uid) + 1
+
+        new_tools_dict = deepcopy(self.default_data)
+        updated_tooldia = None
+
+        # determine the new tool diameter
+        if tool_dia is None or tool_dia == 0:
+            self.build_ui()
+            self.app.inform.emit('[WARNING_NOTCL] %s' % _("Please enter a tool diameter with non-zero value, "
+                                                          "in Float format."))
+            self.ui_connect()
+            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.ui_connect()
+            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.ui_connect()
+            self.on_tool_default_add(dia=tool_dia)
+            return
+
+        tool_found = 0
+
+        offset = 'Path'
+        offset_val = 0.0
+        typ = _("Rough")
+        tool_type = 'C1'
+        # 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 Milling Tool (Geometry Object)
+            if db_tool_val['data']['tool_target'] != 1:     # _('Milling')
+                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_mill_') == 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_mill_'
+                        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_iso') == 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(dia=tool_dia, new_geo=new_geo)
+            self.ui_connect()
+            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.ui_connect()
+            return
+
+        new_tdia = deepcopy(updated_tooldia) if updated_tooldia is not None else deepcopy(truncated_tooldia)
+        self.tools.update({
+            tooluid: {
+                '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.solid_geometry
+            }
+        })
+        self.ui_connect()
+        self.build_ui()
+
+        # select the tool just added
+        for row in range(self.ui.geo_tools_table.rowCount()):
+            if int(self.ui.geo_tools_table.item(row, 5).text()) == tooluid:
+                self.ui.geo_tools_table.selectRow(row)
+                break
+
+        # update the UI form
+        self.update_ui()
+
+        self.app.inform.emit('[success] %s' % _("New tool added to Tool Table from Tools Database."))
+
+    def on_tool_default_add(self, dia=None, new_geo=None, muted=None):
+        self.ui_disconnect()
+
+        tooldia = dia if dia is not None else self.ui.addtool_entry.get_value()
         tool_uid_list = [int(tooluid_key) for tooluid_key in self.tools]
 
         # find maximum from the temp_uid, add 1 and this is the new 'tooluid'
         max_uid = max(tool_uid_list) if tool_uid_list else 0
-        self.tooluid = max_uid + 1
+        self.tooluid = int(max_uid) + 1
 
-        tooldia = float('%.*f' % (self.decimals, tooldia))
+        tooldia = self.app.dec_format(tooldia, self.decimals)
 
         # here we actually add the new tool; if there is no tool in the tool table we add a tool with default data
         # otherwise we add a tool with data copied from last tool
@@ -1080,7 +1213,8 @@ class GeometryObject(FlatCAMObj, Geometry):
             pass
         self.ser_attrs.append('tools')
 
-        self.app.inform.emit('[success] %s' % _("Tool added in Tool Table."))
+        if muted is None:
+            self.app.inform.emit('[success] %s' % _("Tool added in Tool Table."))
         self.ui_connect()
         self.build_ui()
 
@@ -2782,7 +2916,7 @@ class GeometryObject(FlatCAMObj, Geometry):
             tooldia = float('%.*f' % (self.decimals, tooldia))
 
             self.ui.addtool_entry.set_value(tooldia)
-        self.ui.addtool_entry.returnPressed.connect(self.on_tool_add)
+        self.ui.addtool_entry.returnPressed.connect(self.on_tool_default_add)
 
         return factor
 

+ 8 - 3
appTools/ToolCutOut.py

@@ -266,7 +266,7 @@ class CutOut(AppTool):
     def on_tool_add(self, custom_dia=None):
         self.blockSignals(True)
 
-        filename = self.app.data_path + '\\tools_db.FlatDB'
+        filename = self.app.tools_database_path()
 
         new_tools_dict = deepcopy(self.default_data)
         updated_tooldia = None
@@ -311,7 +311,7 @@ class CutOut(AppTool):
 
         offset = 'Path'
         offset_val = 0.0
-        typ = "Rough"
+        typ = _("Rough")
         tool_type = 'V'
         # look in database tools
         for db_tool, db_tool_val in tools_db_dict.items():
@@ -462,7 +462,12 @@ class CutOut(AppTool):
         :return:
         """
 
-        if tool['data']['tool_target'] != _("Cutout"):
+        if tool['data']['tool_target'] not in [0, 6]:   # [General, Cutout Tool]
+            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('[ERROR_NOTCL] %s' % _("Selected tool can't be used here. Pick another."))
             return
         tool_from_db = deepcopy(self.default_data)

+ 2 - 2
appTools/ToolDrilling.py

@@ -884,7 +884,7 @@ class ToolDrilling(AppTool, Excellon):
 
     def on_tool_db_load(self):
 
-        filename = self.app.data_path + '\\tools_db.FlatDB'
+        filename = self.app.tools_database_path()
 
         # load the database tools from the file
         try:
@@ -1213,7 +1213,7 @@ class ToolDrilling(AppTool, Excellon):
         # if the sender is in the column with index 2 then we update the tool_type key
         if cw_col == 2:
             tt = cw.currentText()
-            typ = 'Iso' if tt == 'V' else "Rough"
+            typ = 'Iso' if tt == 'V' else _("Rough")
 
             self.excellon_tools[current_uid].update({
                 'type': typ,

+ 22 - 24
appTools/ToolIsolation.py

@@ -208,7 +208,7 @@ class ToolIsolation(AppTool, Gerber):
         self.ui.apply_param_to_all.clicked.connect(self.on_apply_param_to_all_clicked)
 
         # adding Tools
-        self.ui.add_newtool_button.clicked.connect(lambda: self.on_tool_add())
+        self.ui.search_and_add_btn.clicked.connect(lambda: self.on_tool_add())
         self.ui.addtool_from_db_btn.clicked.connect(self.on_tool_add_from_db_clicked)
 
         self.ui.generate_iso_button.clicked.connect(self.on_iso_button_click)
@@ -899,7 +899,7 @@ class ToolIsolation(AppTool, Gerber):
         # if the sender is in the column with index 2 then we update the tool_type key
         if cw_col == 2:
             tt = cw.currentText()
-            typ = 'Iso' if tt == 'V' else "Rough"
+            typ = 'Iso' if tt == 'V' else _("Rough")
 
             self.iso_tools[currenuid].update({
                 'type': typ,
@@ -1034,29 +1034,26 @@ class ToolIsolation(AppTool, Gerber):
     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
+        filename = self.app.tools_database_path()
 
+        tool_dia = custom_dia if custom_dia is not None else self.ui.new_tooldia_entry.get_value()
         # construct a list of all 'tooluid' in the self.iso_tools
         tool_uid_list = [int(tooluid_key) for tooluid_key in self.iso_tools]
 
         # find maximum from the temp_uid, add 1 and this is the new 'tooluid'
         max_uid = 0 if not tool_uid_list else max(tool_uid_list)
-        tooluid = int(max_uid + 1)
+        tooluid = int(max_uid) + 1
+
+        new_tools_dict = deepcopy(self.default_data)
+        updated_tooldia = None
 
         tool_dias = []
         for k, v in self.iso_tools.items():
             for tool_v in v.keys():
                 if tool_v == 'tooldia':
-                    tool_dias.append(self.app.dec_format(v[tool_v], self.decimals))
+                    tool_dias.append(self.app.dec_format(v['tooldia'], self.decimals))
 
         # determine the new tool diameter
-        if custom_dia is None:
-            tool_dia = self.ui.new_tooldia_entry.get_value()
-        else:
-            tool_dia = custom_dia
         if tool_dia is None or tool_dia == 0:
             self.build_ui()
             self.app.inform.emit('[WARNING_NOTCL] %s' % _("Please enter a tool diameter with non-zero value, "
@@ -1097,7 +1094,7 @@ class ToolIsolation(AppTool, Gerber):
 
         offset = 'Path'
         offset_val = 0.0
-        typ = "Rough"
+        typ = _("Rough")
         tool_type = 'V'
         # look in database tools
         for db_tool, db_tool_val in tools_db_dict.items():
@@ -1111,7 +1108,7 @@ class ToolIsolation(AppTool, Gerber):
             high_limit = float(db_tool_val['data']['tol_max'])
 
             # we need only tool marked for Isolation Tool
-            if db_tool_val['data']['tool_target'] != _('Isolation'):
+            if db_tool_val['data']['tool_target'] != 3:     # _('Isolation')
                 continue
 
             # if we find a tool with the same diameter in the Tools DB just update it's data
@@ -1186,12 +1183,8 @@ class ToolIsolation(AppTool, Gerber):
 
     def on_tool_default_add(self, dia=None, muted=None):
         self.blockSignals(True)
-        self.units = self.app.defaults['units'].upper()
 
-        if dia:
-            tool_dia = dia
-        else:
-            tool_dia = self.ui.new_tooldia_entry.get_value()
+        tool_dia = dia if dia is not None else self.ui.new_tooldia_entry.get_value()
 
         if tool_dia is None or tool_dia == 0:
             self.build_ui()
@@ -2586,7 +2579,12 @@ class ToolIsolation(AppTool, Gerber):
         """
         tool_from_db = deepcopy(tool)
 
-        if tool['data']['tool_target'] != _("Isolation"):
+        if tool['data']['tool_target'] not in [0, 3]:   # [General, Isolation]
+            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('[ERROR_NOTCL] %s' % _("Selected tool can't be used here. Pick another."))
             return
 
@@ -3134,16 +3132,16 @@ class IsoUI:
 
         bhlay = QtWidgets.QHBoxLayout()
 
-        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(
+        self.search_and_add_btn = FCButton(_('Search and Add'))
+        self.search_and_add_btn.setIcon(QtGui.QIcon(self.app.resource_location + '/plus16.png'))
+        self.search_and_add_btn.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.")
         )
-        bhlay.addWidget(self.add_newtool_button)
+        bhlay.addWidget(self.search_and_add_btn)
 
         self.addtool_from_db_btn = FCButton(_('Pick from DB'))
         self.addtool_from_db_btn.setIcon(QtGui.QIcon(self.app.resource_location + '/search_db32.png'))

+ 1 - 1
appTools/ToolMilling.py

@@ -980,7 +980,7 @@ class ToolMilling(AppTool, Excellon):
         # if the sender is in the column with index 2 then we update the tool_type key
         if cw_col == 2:
             tt = cw.currentText()
-            typ = 'Iso' if tt == 'V' else "Rough"
+            typ = 'Iso' if tt == 'V' else _("Rough")
 
             self.iso_tools[current_uid].update({
                 'type': typ,

+ 9 - 4
appTools/ToolNCC.py

@@ -836,7 +836,7 @@ class NonCopperClear(AppTool, Gerber):
         # if the sender is in the column with index 2 then we update the tool_type key
         if cw_col == 2:
             tt = cw.currentText()
-            typ = 'Iso' if tt == 'V' else "Rough"
+            typ = 'Iso' if tt == 'V' else _("Rough")
 
             self.ncc_tools[current_uid].update({
                 'type': typ,
@@ -976,7 +976,7 @@ class NonCopperClear(AppTool, Gerber):
     def on_tool_add(self, custom_dia=None):
         self.blockSignals(True)
 
-        filename = self.app.data_path + '\\tools_db.FlatDB'
+        filename = self.app.tools_database_path()
 
         new_tools_dict = deepcopy(self.default_data)
         updated_tooldia = None
@@ -1042,7 +1042,7 @@ class NonCopperClear(AppTool, Gerber):
 
         offset = 'Path'
         offset_val = 0.0
-        typ = "Rough"
+        typ = _("Rough")
         tool_type = 'V'
         # look in database tools
         for db_tool, db_tool_val in tools_db_dict.items():
@@ -3721,7 +3721,12 @@ class NonCopperClear(AppTool, Gerber):
         """
         tool_from_db = deepcopy(tool)
 
-        if tool['data']['tool_target'] != _("NCC"):
+        if tool['data']['tool_target'] not in [0, 5]:   # [General, NCC]
+            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('[ERROR_NOTCL] %s' % _("Selected tool can't be used here. Pick another."))
             return
 

+ 9 - 4
appTools/ToolPaint.py

@@ -411,7 +411,7 @@ class ToolPaint(AppTool, Gerber):
         # if the sender is in the column with index 2 then we update the tool_type key
         if cw_col == 2:
             tt = cw.currentText()
-            typ = 'Iso' if tt == 'V' else "Rough"
+            typ = 'Iso' if tt == 'V' else _("Rough")
 
             self.paint_tools[current_uid].update({
                 'type': typ,
@@ -666,7 +666,7 @@ class ToolPaint(AppTool, Gerber):
     def on_tool_add(self, custom_dia=None):
         self.blockSignals(True)
 
-        filename = self.app.data_path + '\\tools_db.FlatDB'
+        filename = self.app.tools_database_path()
 
         new_tools_dict = deepcopy(self.default_data)
         updated_tooldia = None
@@ -729,7 +729,7 @@ class ToolPaint(AppTool, Gerber):
 
         offset = 'Path'
         offset_val = 0.0
-        typ = "Rough"
+        typ = _("Rough")
         tool_type = 'V'
         # look in database tools
         for db_tool, db_tool_val in tools_db_dict.items():
@@ -2626,7 +2626,12 @@ class ToolPaint(AppTool, Gerber):
         """
         tool_from_db = deepcopy(tool)
 
-        if tool['data']['tool_target'] != _("Paint"):
+        if tool['data']['tool_target'] not in [0, 4]:   # [General, Paint]
+            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('[ERROR_NOTCL] %s' % _("Selected tool can't be used here. Pick another."))
             return
 

+ 58 - 32
app_Main.py

@@ -359,54 +359,59 @@ class App(QtCore.QObject):
         if not os.path.exists(self.data_path):
             os.makedirs(self.data_path)
             self.log.debug('Created data folder: ' + self.data_path)
+
             os.makedirs(os.path.join(self.data_path, 'preprocessors'))
             self.log.debug('Created data preprocessors folder: ' + os.path.join(self.data_path, 'preprocessors'))
 
-        self.preprocessorpaths = os.path.join(self.data_path, 'preprocessors')
+        self.preprocessorpaths = self.preprocessors_path()
         if not os.path.exists(self.preprocessorpaths):
             os.makedirs(self.preprocessorpaths)
             self.log.debug('Created preprocessors folder: ' + self.preprocessorpaths)
 
         # create tools_db.FlatDB file if there is none
+        db_path = self.tools_database_path()
         try:
-            f = open(self.data_path + '/tools_db.FlatDB')
+            f = open(db_path)
             f.close()
         except IOError:
             self.log.debug('Creating empty tools_db.FlatDB')
-            f = open(self.data_path + '/tools_db.FlatDB', 'w')
+            f = open(db_path, 'w')
             json.dump({}, f)
             f.close()
 
         # create current_defaults.FlatConfig file if there is none
+        def_path = self.defaults_path()
         try:
-            f = open(self.data_path + '/current_defaults.FlatConfig')
+            f = open(def_path)
             f.close()
         except IOError:
             self.log.debug('Creating empty current_defaults.FlatConfig')
-            f = open(self.data_path + '/current_defaults.FlatConfig', 'w')
+            f = open(def_path, 'w')
             json.dump({}, f)
             f.close()
 
         # the factory defaults are written only once at the first launch of the application after installation
-        FlatCAMDefaults.save_factory_defaults(os.path.join(self.data_path, "factory_defaults.FlatConfig"), self.version)
+        FlatCAMDefaults.save_factory_defaults(self.factory_defaults_path(), self.version)
 
         # create a recent files json file if there is none
+        rec_f_path = self.recent_files_path()
         try:
-            f = open(self.data_path + '/recent.json')
+            f = open(rec_f_path)
             f.close()
         except IOError:
             self.log.debug('Creating empty recent.json')
-            f = open(self.data_path + '/recent.json', 'w')
+            f = open(rec_f_path, 'w')
             json.dump([], f)
             f.close()
 
         # create a recent projects json file if there is none
+        rec_proj_path = self.recent_projects_path()
         try:
-            fp = open(self.data_path + '/recent_projects.json')
+            fp = open(rec_proj_path)
             fp.close()
         except IOError:
             self.log.debug('Creating empty recent_projects.json')
-            fp = open(self.data_path + '/recent_projects.json', 'w')
+            fp = open(rec_proj_path, 'w')
             json.dump([], fp)
             fp.close()
 
@@ -1275,7 +1280,7 @@ class App(QtCore.QObject):
 
         self.log.debug("Finished adding FlatCAM Editor's.")
 
-        self.set_ui_title(name=_("New Project - Not saved"))
+        self.ui.set_ui_title(name=_("New Project - Not saved"))
 
         current_platform = platform.architecture()[0]
         if current_platform != '64bit':
@@ -1865,20 +1870,23 @@ class App(QtCore.QObject):
         #     else:
         #         sys.exit(2)
 
-    def set_ui_title(self, name):
-        """
-        Sets the title of the main window.
+    def tools_database_path(self):
+        return os.path.join(self.data_path, 'tools_db.FlatDB')
 
-        :param name: String that store the project path and project name
-        :return: None
-        """
-        self.ui.setWindowTitle('FlatCAM %s %s - %s - [%s]    %s' %
-                               (self.version,
-                                ('BETA' if self.beta else ''),
-                                platform.architecture()[0],
-                                self.engine,
-                                name)
-                               )
+    def defaults_path(self):
+        return os.path.join(self.data_path, 'current_defaults.FlatConfig')
+
+    def factory_defaults_path(self):
+        return os.path.join(self.data_path, 'factory_defaults.FlatConfig')
+
+    def recent_files_path(self):
+        return os.path.join(self.data_path, 'recent.json')
+
+    def recent_projects_path(self):
+        return os.path.join(self.data_path, 'recent_projects.json')
+
+    def preprocessors_path(self):
+        return os.path.join(self.data_path, 'preprocessors')
 
     def on_app_restart(self):
 
@@ -5651,7 +5659,7 @@ class App(QtCore.QObject):
 
         :return:
         """
-        filename = self.data_path + '\\tools_db.FlatDB'
+        filename = self.tools_database_path()
 
         # load the database tools from the file
         try:
@@ -5731,9 +5739,18 @@ class App(QtCore.QObject):
         :return:
         """
         tool_from_db = deepcopy(tool)
-
         obj = self.collection.get_active()
         if obj.kind == 'geometry':
+            if tool['data']['tool_target'] not in [0, 1]:    # General, Milling Type
+                # close the tab and delete it
+                for idx in range(self.ui.plot_tab_area.count()):
+                    if self.ui.plot_tab_area.tabText(idx) == _("Tools Database"):
+                        wdg = self.ui.plot_tab_area.widget(idx)
+                        wdg.deleteLater()
+                        self.ui.plot_tab_area.removeTab(idx)
+                self.inform.emit('[ERROR_NOTCL] %s' % _("Selected tool can't be used here. Pick another."))
+                return
+
             obj.on_tool_from_db_inserted(tool=tool_from_db)
 
             # close the tab and delete it
@@ -5744,6 +5761,15 @@ class App(QtCore.QObject):
                     self.ui.plot_tab_area.removeTab(idx)
             self.inform.emit('[success] %s' % _("Tool from DB added in Tool Table."))
         elif obj.kind == 'gerber':
+            if tool['data']['tool_target'] not in [0, 3]:    # General, Isolation Type
+                # close the tab and delete it
+                for idx in range(self.ui.plot_tab_area.count()):
+                    if self.ui.plot_tab_area.tabText(idx) == _("Tools Database"):
+                        wdg = self.ui.plot_tab_area.widget(idx)
+                        wdg.deleteLater()
+                        self.ui.plot_tab_area.removeTab(idx)
+                self.inform.emit('[ERROR_NOTCL] %s' % _("Selected tool can't be used here. Pick another."))
+                return
             self.isolation_tool.on_tool_from_db_inserted(tool=tool_from_db)
 
             # close the tab and delete it
@@ -8894,7 +8920,7 @@ class MenuFileHandlers(QtCore.QObject):
         # take the focus of the Notebook on Project Tab.
         self.app.ui.notebook.setCurrentWidget(self.app.ui.project_tab)
 
-        self.app.set_ui_title(name=_("New Project - Not saved"))
+        self.app.ui.set_ui_title(name=_("New Project - Not saved"))
 
     def on_filenewscript(self, silent=False):
         """
@@ -9055,7 +9081,7 @@ class MenuFileHandlers(QtCore.QObject):
                 self.app.file_opened.emit("project", self.project_filename)
             self.app.file_saved.emit("project", self.project_filename)
 
-        self.app.set_ui_title(name=self.app.project_filename)
+        self.app.ui.set_ui_title(name=self.app.project_filename)
 
         self.app.should_we_save = False
 
@@ -9107,7 +9133,7 @@ class MenuFileHandlers(QtCore.QObject):
         if not make_copy:
             self.app.project_filename = filename
 
-        self.app.set_ui_title(name=self.app.project_filename)
+        self.app.ui.set_ui_title(name=self.app.project_filename)
         self.app.should_we_save = False
 
     def on_file_save_objects_pdf(self, use_thread=True):
@@ -10316,7 +10342,7 @@ class MenuFileHandlers(QtCore.QObject):
         # for some reason, setting ui_title does not work when this method is called from Tcl Shell
         # it's because the TclCommand is run in another thread (it inherit TclCommandSignaled)
         if cli is None:
-            self.app.set_ui_title(name=_("Loading Project ... Please Wait ..."))
+            self.app.ui.set_ui_title(name=_("Loading Project ... Please Wait ..."))
 
         if run_from_arg:
             self.splash.showMessage('%s: %ssec\n%s' % (_("Canvas initialization started.\n"
@@ -10398,7 +10424,7 @@ class MenuFileHandlers(QtCore.QObject):
             # for some reason, setting ui_title does not work when this method is called from Tcl Shell
             # it's because the TclCommand is run in another thread (it inherit TclCommandSignaled)
             if cli is None:
-                self.app.set_ui_title(name="{} {}: {}".format(
+                self.app.ui.set_ui_title(name="{} {}: {}".format(
                     _("Loading Project ... restoring"), obj['kind'].upper(), obj['options']['name']))
 
             self.app.app_obj.new_object(obj['kind'], obj['options']['name'], obj_init, plot=plot)
@@ -10414,7 +10440,7 @@ class MenuFileHandlers(QtCore.QObject):
         # for some reason, setting ui_title does not work when this method is called from Tcl Shell
         # it's because the TclCommand is run in another thread (it inherit TclCommandSignaled)
         if cli is None:
-            self.app.set_ui_title(name=self.app.project_filename)
+            self.app.ui.set_ui_title(name=self.app.project_filename)
 
         self.app.log.debug(" **************** Finished PROJECT loading... **************** ")