diff --git a/CHANGELOG.md b/CHANGELOG.md index b6ec1a50..252b9ec3 100644 --- a/CHANGELOG.md +++ b/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 diff --git a/appDatabase.py b/appDatabase.py index c9161beb..873a73db 100644 --- a/appDatabase.py +++ b/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()): diff --git a/appGUI/MainGUI.py b/appGUI/MainGUI.py index 7017a5f2..ed789f2f 100644 --- a/appGUI/MainGUI.py +++ b/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)"), diff --git a/appGUI/ObjectUI.py b/appGUI/ObjectUI.py index d7b4070b..6e3382c9 100644 --- a/appGUI/ObjectUI.py +++ b/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) diff --git a/appObjects/FlatCAMGeometry.py b/appObjects/FlatCAMGeometry.py index b6c1fbb9..39e60550 100644 --- a/appObjects/FlatCAMGeometry.py +++ b/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 diff --git a/appTools/ToolCutOut.py b/appTools/ToolCutOut.py index 063f2671..d31227a9 100644 --- a/appTools/ToolCutOut.py +++ b/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) diff --git a/appTools/ToolDrilling.py b/appTools/ToolDrilling.py index 0ae5aea2..c2f6dde7 100644 --- a/appTools/ToolDrilling.py +++ b/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, diff --git a/appTools/ToolIsolation.py b/appTools/ToolIsolation.py index 6ae016b6..acbaade9 100644 --- a/appTools/ToolIsolation.py +++ b/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')) diff --git a/appTools/ToolMilling.py b/appTools/ToolMilling.py index 8dd3df90..fd1e569d 100644 --- a/appTools/ToolMilling.py +++ b/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, diff --git a/appTools/ToolNCC.py b/appTools/ToolNCC.py index a690aec5..70d3c107 100644 --- a/appTools/ToolNCC.py +++ b/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 diff --git a/appTools/ToolPaint.py b/appTools/ToolPaint.py index 3c600dab..2c278efa 100644 --- a/appTools/ToolPaint.py +++ b/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 diff --git a/app_Main.py b/app_Main.py index 497f3ed6..a4519882 100644 --- a/app_Main.py +++ b/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... **************** ")