diff --git a/FlatCAMApp.py b/FlatCAMApp.py index db4c9ad1..1a228bf9 100644 --- a/FlatCAMApp.py +++ b/FlatCAMApp.py @@ -3444,6 +3444,13 @@ class App(QtCore.QObject): self.inform.emit('[WARNING] %s' % _("Object empty after edit.")) log.debug("App.editor2object() --> Geometry --> %s" % str(e)) + + # restore GUI to the Selected TAB + # Remove anything else in the GUI + self.ui.tool_scroll_area.takeWidget() + # Switch notebook to Selected page + self.ui.notebook.setCurrentWidget(self.ui.selected_tab) + elif isinstance(edited_obj, FlatCAMGerber): obj_type = "Gerber" if cleanup is None: @@ -3494,8 +3501,7 @@ class App(QtCore.QObject): _("Select a Gerber, Geometry or Excellon Object to update.")) return - self.inform.emit('[selected] %s %s' % - (obj_type, _("is updated, returning to App..."))) + self.inform.emit('[selected] %s %s' % (obj_type, _("is updated, returning to App..."))) elif response == bt_no: # clean the Tools Tab self.ui.tool_scroll_area.takeWidget() @@ -3514,6 +3520,7 @@ class App(QtCore.QObject): _("Select a Gerber, Geometry or Excellon Object to update.")) return edited_obj.set_ui(edited_obj.ui_type(decimals=self.decimals)) + edited_obj.build_ui() self.ui.notebook.setCurrentWidget(self.ui.selected_tab) elif response == bt_cancel: return diff --git a/README.md b/README.md index ed74004a..e3dfaf54 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,8 @@ CAD program, and create G-Code for Isolation routing. - more work in Punch Gerber Tool - the Jump To popup window will now autoselect the LineEdit therefore no more need for an extra click after launching the function - made some structural changes in Properties Tool -- started t omake some changs in Geometry Editor +- started to make some changes in Geometry Editor +- finished adding in Geometry Editor a TreeWidget with the geometry shapes found in the edited object 24.02.2020 diff --git a/camlib.py b/camlib.py index 063478b1..d234dc90 100644 --- a/camlib.py +++ b/camlib.py @@ -5972,7 +5972,7 @@ class FlatCAMRTreeStorage(FlatCAMRTree): self.objects.append(obj) idx = len(self.objects) - 1 - # Note: Shapely objects are not hashable any more, althought + # Note: Shapely objects are not hashable any more, although # there seem to be plans to re-introduce the feature in # version 2.0. For now, we will index using the object's id, # but it's important to remember that shapely geometry is diff --git a/flatcamEditors/FlatCAMGeoEditor.py b/flatcamEditors/FlatCAMGeoEditor.py index 340c61bc..7a5a63c6 100644 --- a/flatcamEditors/FlatCAMGeoEditor.py +++ b/flatcamEditors/FlatCAMGeoEditor.py @@ -18,7 +18,7 @@ from camlib import distance, arc, three_point_circle, Geometry, FlatCAMRTreeStor from FlatCAMTool import FlatCAMTool from flatcamGUI.ObjectUI import RadioSet from flatcamGUI.GUIElements import OptionalInputSection, FCCheckBox, FCEntry, FCComboBox, FCTextAreaRich, \ - FCTable, FCDoubleSpinner, FCButton, EvalEntry2, FCInputDialog + FCTable, FCDoubleSpinner, FCButton, EvalEntry2, FCInputDialog, FCTree from flatcamParsers.ParseFont import * import FlatCAMApp @@ -2494,8 +2494,29 @@ class FCSelect(DrawTool): self.draw_app.selected = [] self.draw_app.selected.append(obj_to_add) except Exception as e: - log.error("[ERROR] Something went bad. %s" % str(e)) - raise + log.error("[ERROR] FlatCAMGeoEditor.FCSelect.click_release() -> Something went bad. %s" % str(e)) + + # if selection is done on canvas update the Tree in Selected Tab with the selection + try: + self.draw_app.tw.itemSelectionChanged.disconnect(self.draw_app.on_tree_selection_change) + except (AttributeError, TypeError): + pass + + self.draw_app.tw.selectionModel().clearSelection() + for sel_shape in self.draw_app.selected: + iterator = QtWidgets.QTreeWidgetItemIterator(self.draw_app.tw) + while iterator.value(): + item = iterator.value() + try: + if int(item.text(1)) == id(sel_shape): + item.setSelected(True) + except ValueError: + pass + + iterator += 1 + + self.draw_app.tw.itemSelectionChanged.connect(self.draw_app.on_tree_selection_change) + return "" def clean_up(self): @@ -3126,6 +3147,9 @@ class FCTransform(FCShapeTool): # ############################################### class FlatCAMGeoEditor(QtCore.QObject): + # will emit the name of the object that was just selected + item_selected = QtCore.pyqtSignal(str) + transform_complete = QtCore.pyqtSignal() draw_shape_idx = -1 @@ -3140,6 +3164,47 @@ class FlatCAMGeoEditor(QtCore.QObject): self.canvas = app.plotcanvas self.decimals = app.decimals + self.geo_edit_widget = QtWidgets.QWidget() + # ## Box for custom widgets + # This gets populated in offspring implementations. + layout = QtWidgets.QVBoxLayout() + self.geo_edit_widget.setLayout(layout) + + # add a frame and inside add a vertical box layout. Inside this vbox layout I add all the Drills widgets + # this way I can hide/show the frame + self.geo_frame = QtWidgets.QFrame() + self.geo_frame.setContentsMargins(0, 0, 0, 0) + layout.addWidget(self.geo_frame) + self.tools_box = QtWidgets.QVBoxLayout() + self.tools_box.setContentsMargins(0, 0, 0, 0) + self.geo_frame.setLayout(self.tools_box) + + # ## Page Title box (spacing between children) + self.title_box = QtWidgets.QHBoxLayout() + self.tools_box.addLayout(self.title_box) + + # ## Page Title icon + pixmap = QtGui.QPixmap(self.app.resource_location + '/flatcam_icon32.png') + self.icon = QtWidgets.QLabel() + self.icon.setPixmap(pixmap) + self.title_box.addWidget(self.icon, stretch=0) + + # ## Title label + self.title_label = QtWidgets.QLabel("%s" % _('Geometry Editor')) + self.title_label.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter) + self.title_box.addWidget(self.title_label, stretch=1) + self.title_box.addWidget(QtWidgets.QLabel('')) + + self.tw = FCTree(extended_sel=True) + self.tools_box.addWidget(self.tw) + + self.geo_font = QtGui.QFont() + self.geo_font.setBold(True) + + parent = self.tw.invisibleRootItem() + self.geo_parent = self.tw.addParent( + parent, _('Geometry Elements'), expanded=True, color=QtGui.QColor("#000000"), font=self.geo_font) + # ## Toolbar events and properties self.tools = { "select": {"button": self.app.ui.geo_select_btn, @@ -3346,6 +3411,13 @@ class FlatCAMGeoEditor(QtCore.QObject): self.units = self.app.defaults['units'].upper() self.decimals = self.app.decimals + # Remove anything else in the GUI Selected Tab + self.app.ui.selected_scroll_area.takeWidget() + # Put ourselves in the GUI Selected Tab + self.app.ui.selected_scroll_area.setWidget(self.geo_edit_widget) + # Switch notebook to Selected page + self.app.ui.notebook.setCurrentWidget(self.app.ui.selected_tab) + def build_ui(self, first_run=None): # try: @@ -3353,8 +3425,56 @@ class FlatCAMGeoEditor(QtCore.QObject): # self.apertures_table.itemChanged.disconnect() # except (TypeError, AttributeError): # pass + + iterator = QtWidgets.QTreeWidgetItemIterator(self.geo_parent) + to_delete = list() + while iterator.value(): + item = iterator.value() + to_delete.append(item) + iterator += 1 + for it in to_delete: + self.geo_parent.removeChild(it) + + for elem in self.storage.get_objects(): + geo_type = type(elem.geo) + title = None + if geo_type is LinearRing: + title = _('ID Ring') + elif geo_type is LineString: + title = _('ID Line') + elif geo_type is Polygon: + title = _('ID Polygon') + elif geo_type is MultiLineString: + title = _('ID Multi-Line') + elif geo_type is MultiPolygon: + title = _('ID Multi-Polygon') + + self.tw.addChild( + self.geo_parent, + [ + '%s:' % title, + str(id(elem)) + ], + True, + font=self.geo_font, + font_items=1 + ) + + def on_geo_elem_selected(self): pass + def on_tree_selection_change(self): + self.selected = list() + selected_tree_items = self.tw.selectedItems() + for sel in selected_tree_items: + for obj_shape in self.storage.get_objects(): + try: + if id(obj_shape) == int(sel.text(1)): + self.selected.append(obj_shape) + except ValueError: + pass + self.replot() + def activate(self): # adjust the status of the menu entries related to the editor self.app.ui.menueditedit.setDisabled(True) @@ -3403,12 +3523,22 @@ class FlatCAMGeoEditor(QtCore.QObject): # Tell the App that the editor is active self.editor_active = True + + self.item_selected.connect(self.on_geo_elem_selected) + + # ## GUI Events + self.tw.itemSelectionChanged.connect(self.on_tree_selection_change) + # self.tw.keyPressed.connect(self.app.ui.keyPressEvent) + # self.tw.customContextMenuRequested.connect(self.on_menu_request) + + self.geo_frame.show() + log.debug("Finished activating the Geometry Editor...") def deactivate(self): try: QtGui.QGuiApplication.restoreOverrideCursor() - except Exception as e: + except Exception: pass # adjust the status of the menu entries related to the editor @@ -3472,6 +3602,19 @@ class FlatCAMGeoEditor(QtCore.QObject): self.app.ui.e_editor_cmenu.menuAction().setVisible(False) self.app.ui.g_editor_cmenu.menuAction().setVisible(False) + try: + self.item_selected.disconnect() + except (AttributeError, TypeError): + pass + + try: + # ## GUI Events + self.tw.itemSelectionChanged.disconnect(self.on_tree_selection_change) + # self.tw.keyPressed.connect(self.app.ui.keyPressEvent) + # self.tw.customContextMenuRequested.connect(self.on_menu_request) + except (AttributeError, TypeError): + pass + # try: # # re-enable all the widgets in the Selected Tab that were disabled after entering in Edit Geometry Mode # sel_tab_widget_list = self.app.ui.selected_tab.findChildren(QtWidgets.QWidget) @@ -3483,6 +3626,10 @@ class FlatCAMGeoEditor(QtCore.QObject): # Show original geometry if self.fcgeometry: self.fcgeometry.visible = True + + # hide the UI + self.geo_frame.hide() + log.debug("Finished deactivating the Geometry Editor...") def connect_canvas_event_handlers(self): @@ -3684,6 +3831,7 @@ class FlatCAMGeoEditor(QtCore.QObject): self.utility.append(shape) else: self.storage.insert(shape) # TODO: Check performance + self.build_ui() def delete_utility_geometry(self): # for_deletion = [shape for shape in self.shape_buffer if shape.utility] @@ -3724,6 +3872,8 @@ class FlatCAMGeoEditor(QtCore.QObject): self.deactivate() self.activate() + self.set_ui() + # Hide original geometry self.fcgeometry = fcgeometry fcgeometry.visible = False @@ -3845,7 +3995,7 @@ class FlatCAMGeoEditor(QtCore.QObject): self.pos = self.canvas.translate_coords(event_pos) - if self.app.grid_status() == True: + if self.app.grid_status(): self.pos = self.app.geo_editor.snap(self.pos[0], self.pos[1]) else: self.pos = (self.pos[0], self.pos[1]) @@ -3925,7 +4075,7 @@ class FlatCAMGeoEditor(QtCore.QObject): return # ### Snap coordinates ### - if self.app.grid_status() == True: + if self.app.grid_status(): x, y = self.snap(x, y) # Update cursor @@ -3939,7 +4089,7 @@ class FlatCAMGeoEditor(QtCore.QObject): # update the position label in the infobar since the APP mouse event handlers are disconnected self.app.ui.position_label.setText("    X: %.4f   " - "Y: %.4f" % (x, y)) + "Y: %.4f" % (x, y)) if self.pos is None: self.pos = (0, 0) @@ -3948,7 +4098,7 @@ class FlatCAMGeoEditor(QtCore.QObject): # update the reference position label in the infobar since the APP mouse event handlers are disconnected self.app.ui.rel_position_label.setText("Dx: %.4f   Dy: " - "%.4f    " % (dx, dy)) + "%.4f    " % (dx, dy)) if event.button == 1 and event_is_dragging and isinstance(self.active_tool, FCEraser): pass @@ -3961,8 +4111,8 @@ class FlatCAMGeoEditor(QtCore.QObject): self.app.delete_selection_shape() if dx < 0: self.app.draw_moving_selection_shape((self.pos[0], self.pos[1]), (x, y), - color=self.app.defaults["global_alt_sel_line"], - face_color=self.app.defaults['global_alt_sel_fill']) + color=self.app.defaults["global_alt_sel_line"], + face_color=self.app.defaults['global_alt_sel_fill']) self.app.selection_type = False else: self.app.draw_moving_selection_shape((self.pos[0], self.pos[1]), (x, y)) @@ -4011,19 +4161,18 @@ class FlatCAMGeoEditor(QtCore.QObject): # self.app.inform.emit(msg) self.replot() elif event.button == right_button: # right click - if self.app.ui.popMenu.mouse_is_panning == False: + if self.app.ui.popMenu.mouse_is_panning is False: if self.in_action is False: try: QtGui.QGuiApplication.restoreOverrideCursor() - except Exception as e: + except Exception: pass if self.active_tool.complete is False and not isinstance(self.active_tool, FCSelect): self.active_tool.complete = True self.in_action = False self.delete_utility_geometry() - self.app.inform.emit('[success] %s' % - _("Done.")) + self.app.inform.emit('[success] %s' % _("Done.")) self.select_tool('select') else: self.app.cursor = QtGui.QCursor() @@ -4037,8 +4186,7 @@ class FlatCAMGeoEditor(QtCore.QObject): self.active_tool.make() if self.active_tool.complete: self.on_shape_complete() - self.app.inform.emit('[success] %s' % - _("Done.")) + self.app.inform.emit('[success] %s' % _("Done.")) self.select_tool(self.active_tool.name) except Exception as e: log.warning("FLatCAMGeoEditor.on_geo_click_release() --> Error: %s" % str(e)) @@ -4082,6 +4230,27 @@ class FlatCAMGeoEditor(QtCore.QObject): self.selected = [] self.selected = sel_objects_list + # if selection is done on canvas update the Tree in Selected Tab with the selection + try: + self.tw.itemSelectionChanged.disconnect(self.on_tree_selection_change) + except (AttributeError, TypeError): + pass + + self.tw.selectionModel().clearSelection() + for sel_shape in self.selected: + iterator = QtWidgets.QTreeWidgetItemIterator(self.tw) + while iterator.value(): + item = iterator.value() + try: + if int(item.text(1)) == id(sel_shape): + item.setSelected(True) + except ValueError: + pass + + iterator += 1 + + self.tw.itemSelectionChanged.connect(self.on_tree_selection_change) + self.replot() def draw_utility_geometry(self, geo): @@ -4131,6 +4300,7 @@ class FlatCAMGeoEditor(QtCore.QObject): for shape in tempref: self.delete_shape(shape) self.selected = [] + self.build_ui() def delete_shape(self, shape): diff --git a/flatcamGUI/GUIElements.py b/flatcamGUI/GUIElements.py index a136e16c..b9f17d9e 100644 --- a/flatcamGUI/GUIElements.py +++ b/flatcamGUI/GUIElements.py @@ -154,7 +154,7 @@ class RadioSet(QtWidgets.QWidget): class FCTree(QtWidgets.QTreeWidget): - def __init__(self, parent=None, columns=2, header_hidden=True): + def __init__(self, parent=None, columns=2, header_hidden=True, extended_sel=False): super(FCTree, self).__init__(parent) self.setColumnCount(columns) @@ -162,6 +162,9 @@ class FCTree(QtWidgets.QTreeWidget): self.header().setSectionResizeMode(QtWidgets.QHeaderView.ResizeToContents) self.setSizePolicy(QtWidgets.QSizePolicy.Ignored, QtWidgets.QSizePolicy.Expanding) + if extended_sel: + self.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection) + def addParent(self, parent, title, expanded=False, color=None, font=None): item = QtWidgets.QTreeWidgetItem(parent, [title]) item.setChildIndicatorPolicy(QtWidgets.QTreeWidgetItem.ShowIndicator)