diff --git a/CHANGELOG.md b/CHANGELOG.md index 569665a4..54b6a247 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,8 @@ CHANGELOG for FlatCAM beta - fixed bug that inversed mouse cursor movement versus the real movement on Y axis when Grid lines are Off - updated the language strings - PEP8 changes and PyCharm suggestions +- in Geometry UI and in Drilling Tool added ability to edit existing Exclusion Areas and change the Strategy and OverZ parameters for each of them +- in Geometry UI and in Drilling Tool added a Delete menu entry in the Exclusion Area Table context menu 11.11.2020 diff --git a/appCommon/Common.py b/appCommon/Common.py index baf60c71..4a4dc53d 100644 --- a/appCommon/Common.py +++ b/appCommon/Common.py @@ -379,10 +379,11 @@ class ExclusionAreas(QtCore.QObject): # "overz": float < - self.over_z_button # } new_el = { + "idx": len(self.exclusion_areas_storage) + 1, "obj_type": self.obj_type, - "shape": new_rectangle, + "shape": new_rectangle, "strategy": self.strategy_button.get_value(), - "overz": self.over_z_button.get_value() + "overz": self.over_z_button.get_value() } self.exclusion_areas_storage.append(new_el) @@ -442,10 +443,11 @@ class ExclusionAreas(QtCore.QObject): } """ new_el = { + "idx": len(self.exclusion_areas_storage) + 1, "obj_type": self.obj_type, - "shape": pol, + "shape": pol, "strategy": self.strategy_button.get_value(), - "overz": self.over_z_button.get_value() + "overz": self.over_z_button.get_value() } self.exclusion_areas_storage.append(new_el) diff --git a/appGUI/GUIElements.py b/appGUI/GUIElements.py index 46e479ce..aeb9fd0d 100644 --- a/appGUI/GUIElements.py +++ b/appGUI/GUIElements.py @@ -2167,8 +2167,8 @@ class FCComboBox(QtWidgets.QComboBox): class FCComboBox2(FCComboBox): - def __init__(self, parent=None, callback=None): - super(FCComboBox2, self).__init__(parent=parent, callback=callback) + def __init__(self, parent=None, callback=None, policy=True): + super(FCComboBox2, self).__init__(parent=parent, callback=callback, policy=policy) def get_value(self): return int(self.currentIndex()) diff --git a/appObjects/FlatCAMGeometry.py b/appObjects/FlatCAMGeometry.py index 654c67d4..d2483f7c 100644 --- a/appObjects/FlatCAMGeometry.py +++ b/appObjects/FlatCAMGeometry.py @@ -330,20 +330,18 @@ class GeometryObject(FlatCAMObj, Geometry): self.set_tool_offset_visibility(selected_row) - # ----------------------------- - # Build Exclusion Areas section - # ----------------------------- + # ############################################################################################################# + # ################################### Build Exclusion Areas section ########################################### + # ############################################################################################################# + self.ui_disconnect() + e_len = len(self.app.exc_areas.exclusion_areas_storage) self.ui.exclusion_table.setRowCount(e_len) - area_id = 0 - for area in range(e_len): - area_id += 1 - area_dict = self.app.exc_areas.exclusion_areas_storage[area] - area_id_item = QtWidgets.QTableWidgetItem('%d' % int(area_id)) + area_id_item = QtWidgets.QTableWidgetItem('%d' % int(area_dict["idx"])) area_id_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) self.ui.exclusion_table.setItem(area, 0, area_id_item) # Area id @@ -351,14 +349,28 @@ class GeometryObject(FlatCAMObj, Geometry): object_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) self.ui.exclusion_table.setItem(area, 1, object_item) # Origin Object - strategy_item = QtWidgets.QTableWidgetItem('%s' % area_dict["strategy"]) - strategy_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) - self.ui.exclusion_table.setItem(area, 2, strategy_item) # Strategy + # strategy_item = QtWidgets.QTableWidgetItem('%s' % area_dict["strategy"]) + # strategy_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) + strategy_item = FCComboBox2(policy=False) + strategy_item.addItems([_("Around"), _("Over")]) + idx = 0 if area_dict["strategy"] == 'around' else 1 + # protection against having this translated or loading a project with translated values + if idx == -1: + strategy_item.setCurrentIndex(0) + else: + strategy_item.setCurrentIndex(idx) + self.ui.exclusion_table.setCellWidget(area, 2, strategy_item) # Strategy overz_item = QtWidgets.QTableWidgetItem('%s' % area_dict["overz"]) overz_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) self.ui.exclusion_table.setItem(area, 3, overz_item) # Over Z + # make the Overz column editable + for row in range(e_len): + self.ui.exclusion_table.item(row, 3).setFlags(QtCore.Qt.ItemIsSelectable | + QtCore.Qt.ItemIsEditable | + QtCore.Qt.ItemIsEnabled) + self.ui.exclusion_table.resizeColumnsToContents() self.ui.exclusion_table.resizeRowsToContents() @@ -575,6 +587,9 @@ class GeometryObject(FlatCAMObj, Geometry): log.debug("Expected a GeometryObjectUI, got %s" % type(self.ui)) return + # ############################################################################################################# + # ############################### TOOLS TABLE context menu #################################################### + # ############################################################################################################# self.ui.geo_tools_table.setupContextMenu() self.ui.geo_tools_table.addContextMenu( _("Pick from DB"), self.on_tool_add_from_db_clicked, @@ -586,6 +601,14 @@ class GeometryObject(FlatCAMObj, Geometry): _("Delete"), lambda: self.on_tool_delete(clicked_signal=None, all_tools=None), icon=QtGui.QIcon(self.app.resource_location + "/trash16.png")) + # ############################################################################################################# + # ############################## EXCLUSION TABLE context menu ################################################# + # ############################################################################################################# + self.ui.exclusion_table.setupContextMenu() + self.ui.exclusion_table.addContextMenu( + _("Delete"), self.on_delete_sel_areas, icon=QtGui.QIcon(self.app.resource_location + "/trash16.png") + ) + # Show/Hide Advanced Options if self.app.defaults["global_app_level"] == 'b': self.ui.level.setText('%s' % _('Basic')) @@ -779,6 +802,12 @@ class GeometryObject(FlatCAMObj, Geometry): self.ui.plot_cb.stateChanged.connect(self.on_plot_cb_click) + # Exclusion Table widgets connect + for row in range(self.ui.exclusion_table.rowCount()): + self.ui.exclusion_table.cellWidget(row, 2).currentIndexChanged.connect(self.on_exclusion_table_strategy) + + self.ui.exclusion_table.itemChanged.connect(self.on_exclusion_table_overz) + # common parameters update self.ui.toolchangeg_cb.stateChanged.connect(self.update_common_param_in_storage) self.ui.toolchangez_entry.editingFinished.connect(self.update_common_param_in_storage) @@ -818,6 +847,7 @@ class GeometryObject(FlatCAMObj, Geometry): except TypeError: pass + # disconnect FCCombobox widgets in the Tool Table for row in range(self.ui.geo_tools_table.rowCount()): for col in [2, 3, 4]: try: @@ -882,12 +912,26 @@ class GeometryObject(FlatCAMObj, Geometry): self.ui.pp_geometry_name_cb.currentIndexChanged.disconnect(self.update_common_param_in_storage) except (TypeError, AttributeError): pass + + try: + self.ui.polish_cb.stateChanged.disconnect(self.update_common_param_in_storage) + except (TypeError, AttributeError): + pass + try: self.ui.exclusion_cb.stateChanged.disconnect(self.update_common_param_in_storage) except (TypeError, AttributeError): pass + + # Exclusion Table widgets disconnect + for row in range(self.ui.exclusion_table.rowCount()): + try: + self.ui.exclusion_table.cellWidget(row, 2).currentIndexChanged.disconnect() + except (TypeError, AttributeError): + pass + try: - self.ui.polish_cb.stateChanged.disconnect(self.update_common_param_in_storage) + self.ui.exclusion_table.itemChanged.disconnect() except (TypeError, AttributeError): pass @@ -3063,6 +3107,57 @@ class GeometryObject(FlatCAMObj, Geometry): self.ui.exclusion_table.selectAll() self.on_draw_sel_shape() + def on_exclusion_table_overz(self, current_item): + self.ui_disconnect() + + current_row = current_item.row() + try: + d = float(self.ui.exclusion_table.item(current_row, 3).text()) + except ValueError: + # try to convert comma to decimal point. if it's still not working error message and return + try: + d = float(self.ui.exclusion_table.item(current_row, 3).text().replace(',', '.')) + except ValueError: + self.app.inform.emit('[ERROR_NOTCL] %s' % _("Wrong value format entered, use a number.")) + return + except AttributeError: + self.ui_connect() + return + + overz = self.app.dec_format(d, self.decimals) + idx = int(self.ui.exclusion_table.item(current_row, 0).text()) + + for area_dict in self.app.exc_areas.exclusion_areas_storage: + if area_dict['idx'] == idx: + area_dict['overz'] = overz + + self.app.inform.emit('[success] %s' % _("Value edited in Exclusion Table.")) + self.ui_connect() + self.builduiSig.emit() + + def on_exclusion_table_strategy(self): + cw = self.sender() + cw_index = self.ui.exclusion_table.indexAt(cw.pos()) + cw_row = cw_index.row() + idx = int(self.ui.exclusion_table.item(cw_row, 0).text()) + + for area_dict in self.app.exc_areas.exclusion_areas_storage: + if area_dict['idx'] == idx: + strategy = self.ui.exclusion_table.cellWidget(cw_row, 2).currentIndex() + area_dict['strategy'] = "around" if strategy == 0 else 'overz' + + self.app.inform.emit('[success] %s' % _("Value edited in Exclusion Table.")) + self.ui_connect() + self.builduiSig.emit() + + def area_disconnect(self): + try: + self.app.exc_areas.area_disconnect() + if not self.app.exc_areas.exclusion_areas_storage: + self.app.exc_areas.clear_shapes() + except Exception as err: + log.debug('GeometryObject.area_disconnect() -> %s' % str(err)) + def plot_element(self, element, color=None, visible=None): if color is None: diff --git a/appTools/ToolDrilling.py b/appTools/ToolDrilling.py index 0faaebe3..a42ab93a 100644 --- a/appTools/ToolDrilling.py +++ b/appTools/ToolDrilling.py @@ -10,7 +10,7 @@ from PyQt5 import QtWidgets, QtCore, QtGui from appTool import AppTool from appGUI.GUIElements import FCCheckBox, FCDoubleSpinner, RadioSet, FCTable, FCButton, \ FCComboBox, OptionalInputSection, FCSpinner, NumericalEvalEntry, OptionalHideInputSection, FCLabel, \ - NumericalEvalTupleEntry + NumericalEvalTupleEntry, FCComboBox2 from appParsers.ParseExcellon import Excellon from copy import deepcopy @@ -45,6 +45,7 @@ else: class ToolDrilling(AppTool, Excellon): + builduiSig = QtCore.pyqtSignal() def __init__(self, app): self.app = app @@ -204,6 +205,14 @@ class ToolDrilling(AppTool, Excellon): "e_area_shape": "tools_drill_area_shape", } + # ############################################################################################################# + # ############################## EXCLUSION TABLE context menu ################################################# + # ############################################################################################################# + self.ui.exclusion_table.setupContextMenu() + self.ui.exclusion_table.addContextMenu( + _("Delete"), self.on_delete_sel_areas, icon=QtGui.QIcon(self.app.resource_location + "/trash16.png") + ) + self.poly_drawn = False self.connect_signals_at_init() @@ -248,6 +257,7 @@ class ToolDrilling(AppTool, Excellon): # ############################################################################# # ############################ SIGNALS ######################################## # ############################################################################# + self.builduiSig.connect(self.build_tool_ui) self.ui.search_load_db_btn.clicked.connect(self.on_tool_db_load) @@ -650,7 +660,11 @@ class ToolDrilling(AppTool, Excellon): # all the tools are selected by default self.ui.tools_table.selectAll() - # Build Exclusion Areas section + # ############################################################################################################# + # ################################### Build Exclusion Areas section ########################################### + # ############################################################################################################# + self.ui_disconnect() + e_len = len(self.app.exc_areas.exclusion_areas_storage) self.ui.exclusion_table.setRowCount(e_len) @@ -669,14 +683,28 @@ class ToolDrilling(AppTool, Excellon): object_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) self.ui.exclusion_table.setItem(area, 1, object_item) # Origin Object - strategy_item = QtWidgets.QTableWidgetItem('%s' % area_dict["strategy"]) - strategy_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) - self.ui.exclusion_table.setItem(area, 2, strategy_item) # Strategy + # strategy_item = QtWidgets.QTableWidgetItem('%s' % area_dict["strategy"]) + # strategy_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) + strategy_item = FCComboBox2(policy=False) + strategy_item.addItems([_("Around"), _("Over")]) + idx = 0 if area_dict["strategy"] == 'around' else 1 + # protection against having this translated or loading a project with translated values + if idx == -1: + strategy_item.setCurrentIndex(0) + else: + strategy_item.setCurrentIndex(idx) + self.ui.exclusion_table.setCellWidget(area, 2, strategy_item) # Strategy overz_item = QtWidgets.QTableWidgetItem('%s' % area_dict["overz"]) overz_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) self.ui.exclusion_table.setItem(area, 3, overz_item) # Over Z + # make the Overz column editable + for row in range(e_len): + self.ui.exclusion_table.item(row, 3).setFlags(QtCore.Qt.ItemIsSelectable | + QtCore.Qt.ItemIsEditable | + QtCore.Qt.ItemIsEnabled) + self.ui.exclusion_table.resizeColumnsToContents() self.ui.exclusion_table.resizeRowsToContents() @@ -810,6 +838,12 @@ class ToolDrilling(AppTool, Excellon): self.ui.order_radio.activated_custom[str].connect(self.on_order_changed) + # Exclusion Table widgets connect + for row in range(self.ui.exclusion_table.rowCount()): + self.ui.exclusion_table.cellWidget(row, 2).currentIndexChanged.connect(self.on_exclusion_table_strategy) + + self.ui.exclusion_table.itemChanged.connect(self.on_exclusion_table_overz) + def ui_disconnect(self): # rows selected try: @@ -891,6 +925,18 @@ class ToolDrilling(AppTool, Excellon): except (TypeError, ValueError): pass + # Exclusion Table widgets disconnect + for row in range(self.ui.exclusion_table.rowCount()): + try: + self.ui.exclusion_table.cellWidget(row, 2).currentIndexChanged.disconnect() + except (TypeError, AttributeError): + pass + + try: + self.ui.exclusion_table.itemChanged.disconnect() + except (TypeError, AttributeError): + pass + def on_tool_db_load(self): filename = self.app.tools_database_path() @@ -1472,6 +1518,49 @@ class ToolDrilling(AppTool, Excellon): self.ui.exclusion_table.selectAll() self.draw_sel_shape() + def on_exclusion_table_overz(self, current_item): + self.ui_disconnect() + + current_row = current_item.row() + try: + d = float(self.ui.exclusion_table.item(current_row, 3).text()) + except ValueError: + # try to convert comma to decimal point. if it's still not working error message and return + try: + d = float(self.ui.exclusion_table.item(current_row, 3).text().replace(',', '.')) + except ValueError: + self.app.inform.emit('[ERROR_NOTCL] %s' % _("Wrong value format entered, use a number.")) + return + except AttributeError: + self.ui_connect() + return + + overz = self.app.dec_format(d, self.decimals) + idx = int(self.ui.exclusion_table.item(current_row, 0).text()) + + for area_dict in self.app.exc_areas.exclusion_areas_storage: + if area_dict['idx'] == idx: + area_dict['overz'] = overz + + self.app.inform.emit('[success] %s' % _("Value edited in Exclusion Table.")) + self.ui_connect() + self.builduiSig.emit() + + def on_exclusion_table_strategy(self): + cw = self.sender() + cw_index = self.ui.exclusion_table.indexAt(cw.pos()) + cw_row = cw_index.row() + idx = int(self.ui.exclusion_table.item(cw_row, 0).text()) + + for area_dict in self.app.exc_areas.exclusion_areas_storage: + if area_dict['idx'] == idx: + strategy = self.ui.exclusion_table.cellWidget(cw_row, 2).currentIndex() + area_dict['strategy'] = "around" if strategy == 0 else 'overz' + + self.app.inform.emit('[success] %s' % _("Value edited in Exclusion Table.")) + self.ui_connect() + self.builduiSig.emit() + @staticmethod def process_slot_as_drills(slot, overlap, add_last_pt=False): diff --git a/app_Main.py b/app_Main.py index d9058d87..c5ca8d40 100644 --- a/app_Main.py +++ b/app_Main.py @@ -5340,25 +5340,25 @@ class App(QtCore.QObject): app_obj.debug("on_copy_command() --> no excellon tools") return 'fail' - def initialize_script(obj_init, app_obj): - obj_init.source_file = deepcopy(obj.source_file) + def initialize_script(new_obj, app_obj): + new_obj.source_file = deepcopy(obj.source_file) - def initialize_document(obj_init, app_obj): - obj_init.source_file = deepcopy(obj.source_file) + def initialize_document(new_obj, app_obj): + new_obj.source_file = deepcopy(obj.source_file) for obj in self.collection.get_selected(): obj_name = obj.options["name"] try: - if isinstance(obj, ExcellonObject): + if obj.kind == 'excellon': self.app_obj.new_object("excellon", str(obj_name) + "_copy", initialize_excellon) - elif isinstance(obj, GerberObject): + elif obj.kind == 'gerber': self.app_obj.new_object("gerber", str(obj_name) + "_copy", initialize) - elif isinstance(obj, GeometryObject): + elif obj.kind == 'geometry': self.app_obj.new_object("geometry", str(obj_name) + "_copy", initialize) - elif isinstance(obj, ScriptObject): + elif obj.kind == 'script': self.app_obj.new_object("script", str(obj_name) + "_copy", initialize_script) - elif isinstance(obj, DocumentObject): + elif obj.kind == 'document': self.app_obj.new_object("document", str(obj_name) + "_copy", initialize_document) except Exception as e: return "Operation failed: %s" % str(e)