From b058f651085e1075fdea4926a43950646cf84040 Mon Sep 17 00:00:00 2001 From: Marius Stanciu Date: Sat, 19 Oct 2019 00:29:29 +0300 Subject: [PATCH] - finished the Objects menu by adding the ability of actions to be checked so they will show the selected status of the objects and by adding to actions to (de)select all objects - fixed and optimized the click selection on canvas - fixed Gerber parsing for very simple Gerber files that have only one Polygon but many LPC zones --- FlatCAMApp.py | 208 +++++++++++++++++----------------- ObjectCollection.py | 10 ++ README.md | 3 + flatcamGUI/FlatCAMGUI.py | 6 + flatcamParsers/ParseGerber.py | 2 +- share/deselect_all32.png | Bin 0 -> 690 bytes 6 files changed, 127 insertions(+), 102 deletions(-) create mode 100644 share/deselect_all32.png diff --git a/FlatCAMApp.py b/FlatCAMApp.py index 228bc2ac..1000ebf1 100644 --- a/FlatCAMApp.py +++ b/FlatCAMApp.py @@ -7935,9 +7935,12 @@ class App(QtCore.QObject): def add_act(name): obj_for_icon = self.collection.get_by_name(name) add_action = QtWidgets.QAction(parent=self.ui.menuobjects) + add_action.setCheckable(True) add_action.setText(name) add_action.setIcon(QtGui.QIcon(icon_files[obj_for_icon.kind])) - add_action.triggered.connect(lambda: self.collection.set_exclusive_active(name)) + add_action.triggered.connect( + lambda: self.collection.set_active(name) if add_action.isChecked() is True else + self.collection.set_inactive(name)) self.ui.menuobjects.addAction(add_action) for name in gerber_list: @@ -7963,6 +7966,17 @@ class App(QtCore.QObject): for name in doc_list: add_act(name) + self.ui.menuobjects.addSeparator() + self.ui.menuobjects_selall = self.ui.menuobjects.addAction( + QtGui.QIcon('share/select_all.png'), + _('Select All') + ) + self.ui.menuobjects_unselall = self.ui.menuobjects.addAction( + QtGui.QIcon('share/deselect_all32.png'), + _('Deselect All') + ) + self.ui.menuobjects_selall.triggered.connect(lambda: self.on_objects_selection(True)) + self.ui.menuobjects_unselall.triggered.connect(lambda: self.on_objects_selection(False)) elif state == 'delete': for act in self.ui.menuobjects.actions(): @@ -7979,7 +7993,9 @@ class App(QtCore.QObject): add_action = QtWidgets.QAction(parent=self.ui.menuobjects) add_action.setText(obj.options['name']) add_action.setIcon(QtGui.QIcon(icon_files[obj.kind])) - add_action.triggered.connect(lambda: self.collection.set_exclusive_active(obj.options['name'])) + add_action.triggered.connect( + lambda: self.collection.set_active(obj.options['name']) if add_action.isChecked() is True else + self.collection.set_inactive(obj.options['name'])) self.ui.menuobjects.insertAction(act, add_action) @@ -7997,6 +8013,39 @@ class App(QtCore.QObject): pass self.ui.menuobjects.clear() + self.ui.menuobjects.addSeparator() + self.ui.menuobjects_selall = self.ui.menuobjects.addAction( + QtGui.QIcon('share/select_all.png'), + _('Select All') + ) + self.ui.menuobjects_unselall = self.ui.menuobjects.addAction( + QtGui.QIcon('share/deselect_all32.png'), + _('Deselect All') + ) + self.ui.menuobjects_selall.triggered.connect(lambda: self.on_objects_selection(True)) + self.ui.menuobjects_unselall.triggered.connect(lambda: self.on_objects_selection(False)) + + def on_objects_selection(self, on_off): + obj_list = self.collection.get_names() + + if on_off is True: + self.collection.set_all_active() + for act in self.ui.menuobjects.actions(): + try: + act.setChecked(True) + except: + pass + if obj_list: + self.inform.emit('[selected] %s' % _("All objects are selected.")) + else: + self.collection.set_all_inactive() + for act in self.ui.menuobjects.actions(): + try: + act.setChecked(False) + except: + pass + self.inform.emit('%s' % _("Objects selection is cleared.")) + def grid_status(self): if self.ui.grid_snap_btn.isChecked(): return True @@ -8439,93 +8488,40 @@ class App(QtCore.QObject): objects_under_the_click_list.append(obj.options['name']) try: - # If there is no element in the overlapped objects list then make everyone inactive - # because we selected "nothing" - self.collection.set_all_inactive() - - # delete the possible selection box around a possible selected object - self.delete_selection_shape() - - if not objects_under_the_click_list: - - # and as a convenience move the focus to the Project tab because Selected tab is now empty but - # only when working on App - if self.call_source == 'app': - if self.click_noproject is False: - self.ui.notebook.setCurrentWidget(self.ui.project_tab) - else: - # restore auto open the Project Tab - self.click_noproject = False - - # delete any text in the status bar, implicitly the last object name that was selected - self.inform.emit("") - else: - self.call_source = 'app' - else: + if objects_under_the_click_list: + curr_sel_obj = self.collection.get_active() # case when there is only an object under the click and we toggle it if len(objects_under_the_click_list) == 1: - if self.collection.get_active() is None: + if curr_sel_obj is None: self.collection.set_active(objects_under_the_click_list[0]) - # create the selection box around the selected object curr_sel_obj = self.collection.get_active() + + # create the selection box around the selected object if self.defaults['global_selection_shape'] is True: self.draw_selection_shape(curr_sel_obj) - # self.inform.emit('[selected] %s: %s selected' % - # (str(curr_sel_obj.kind).capitalize(), str(curr_sel_obj.options['name']))) - if curr_sel_obj.kind == 'gerber': - self.inform.emit( - _('[selected]{name} selected').format( - color='green', name=str(curr_sel_obj.options['name']))) - elif curr_sel_obj.kind == 'excellon': - self.inform.emit( - _('[selected]{name} selected').format( - color='brown', name=str(curr_sel_obj.options['name']))) - elif curr_sel_obj.kind == 'cncjob': - self.inform.emit( - _('[selected]{name} selected').format( - color='blue', name=str(curr_sel_obj.options['name']))) - elif curr_sel_obj.kind == 'geometry': - self.inform.emit( - _('[selected]{name} selected').format( - color='red', name=str(curr_sel_obj.options['name']))) - elif self.collection.get_active().options['name'] not in objects_under_the_click_list: - self.collection.set_all_inactive() + self.on_objects_selection(False) self.delete_selection_shape() + self.collection.set_active(objects_under_the_click_list[0]) - # create the selection box around the selected object curr_sel_obj = self.collection.get_active() + + # create the selection box around the selected object if self.defaults['global_selection_shape'] is True: self.draw_selection_shape(curr_sel_obj) - # self.inform.emit('[selected] %s: %s selected' % - # (str(curr_sel_obj.kind).capitalize(), str(curr_sel_obj.options['name']))) - if curr_sel_obj.kind == 'gerber': - self.inform.emit( - _('[selected]{name} selected').format( - color='green', name=str(curr_sel_obj.options['name']))) - elif curr_sel_obj.kind == 'excellon': - self.inform.emit( - _('[selected]{name} selected').format( - color='brown', name=str(curr_sel_obj.options['name']))) - elif curr_sel_obj.kind == 'cncjob': - self.inform.emit( - _('[selected]{name} selected').format( - color='blue', name=str(curr_sel_obj.options['name']))) - elif curr_sel_obj.kind == 'geometry': - self.inform.emit( - _('[selected]{name} selected').format( - color='red', name=str(curr_sel_obj.options['name']))) + self.selected_message(curr_sel_obj=curr_sel_obj) else: - self.collection.set_all_inactive() + self.on_objects_selection(False) self.delete_selection_shape() - if self.call_source == 'app': - # delete any text in the status bar, implicitly the last object name that was selected - self.inform.emit("") - else: + + if self.call_source != 'app': self.call_source = 'app' + + self.selected_message(curr_sel_obj=curr_sel_obj) + else: # If there is no selected object # make active the first element of the overlapped objects list @@ -8540,9 +8536,9 @@ class App(QtCore.QObject): name_sel_obj = objects_under_the_click_list[0] self.collection.set_active(name_sel_obj) else: - name_sel_obj_idx = objects_under_the_click_list.index(name_sel_obj) + sel_idx = objects_under_the_click_list.index(name_sel_obj) self.collection.set_all_inactive() - self.collection.set_active(objects_under_the_click_list[(name_sel_obj_idx + 1) % + self.collection.set_active(objects_under_the_click_list[(sel_idx + 1) % len(objects_under_the_click_list)]) curr_sel_obj = self.collection.get_active() @@ -8552,30 +8548,44 @@ class App(QtCore.QObject): if self.defaults['global_selection_shape'] is True: self.draw_selection_shape(curr_sel_obj) - # self.inform.emit('[selected] %s: %s selected' % - # (str(curr_sel_obj.kind).capitalize(), str(curr_sel_obj.options['name']))) - if curr_sel_obj.kind == 'gerber': - self.inform.emit(_('[selected]{name} selected').format( - color='green', name=str(curr_sel_obj.options['name']))) - elif curr_sel_obj.kind == 'excellon': - self.inform.emit(_('[selected]{name} selected').format( - color='brown', name=str(curr_sel_obj.options['name']))) - elif curr_sel_obj.kind == 'cncjob': - self.inform.emit(_('[selected]{name} selected').format( - color='blue', name=str(curr_sel_obj.options['name']))) - elif curr_sel_obj.kind == 'geometry': - self.inform.emit(_('[selected]{name} selected').format( - color='red', name=str(curr_sel_obj.options['name']))) + self.selected_message(curr_sel_obj=curr_sel_obj) - # for obj in self.collection.get_list(): - # obj.plot() - # curr_sel_obj.plot(color=self.FC_dark_blue, face_color=self.FC_light_blue) + else: + # deselect everything + self.on_objects_selection(False) + # delete the possible selection box around a possible selected object + self.delete_selection_shape() - # TODO: on selected objects change the object colors and do not draw the selection box - # self.plotcanvas.update() # this updates the canvas + # and as a convenience move the focus to the Project tab because Selected tab is now empty but + # only when working on App + if self.call_source == 'app': + if self.click_noproject is False: + self.ui.notebook.setCurrentWidget(self.ui.project_tab) + else: + # restore auto open the Project Tab + self.click_noproject = False + + # delete any text in the status bar, implicitly the last object name that was selected + # self.inform.emit("") + else: + self.call_source = 'app' except Exception as e: - log.error("[ERROR] Something went bad. %s" % str(e)) - return + log.error("[ERROR] Something went bad in App.select_objects(). %s" % str(e)) + + def selected_message(self, curr_sel_obj): + if curr_sel_obj: + if curr_sel_obj.kind == 'gerber': + self.inform.emit(_('[selected]{name} selected').format( + color='green', name=str(curr_sel_obj.options['name']))) + elif curr_sel_obj.kind == 'excellon': + self.inform.emit(_('[selected]{name} selected').format( + color='brown', name=str(curr_sel_obj.options['name']))) + elif curr_sel_obj.kind == 'cncjob': + self.inform.emit(_('[selected]{name} selected').format( + color='blue', name=str(curr_sel_obj.options['name']))) + elif curr_sel_obj.kind == 'geometry': + self.inform.emit(_('[selected]{name} selected').format( + color='red', name=str(curr_sel_obj.options['name']))) def delete_hover_shape(self): self.hover_shapes.clear() @@ -8634,6 +8644,9 @@ class App(QtCore.QObject): :return: """ + if sel_obj is None: + return + pt1 = (float(sel_obj.options['xmin']), float(sel_obj.options['ymin'])) pt2 = (float(sel_obj.options['xmax']), float(sel_obj.options['ymin'])) pt3 = (float(sel_obj.options['xmax']), float(sel_obj.options['ymax'])) @@ -8647,13 +8660,6 @@ class App(QtCore.QObject): sel_rect = sel_rect.buffer(-0.00393) sel_rect = sel_rect.buffer(0.00787) - # if color: - # face = Color(color, alpha=0.2) - # outline = Color(color, alpha=0.8) - # else: - # face = Color(self.defaults['global_sel_fill'], alpha=0.2) - # outline = Color(self.defaults['global_sel_line'], alpha=0.8) - if color: face = color[:-2] + str(hex(int(0.2 * 255)))[2:] outline = color[:-2] + str(hex(int(0.8 * 255)))[2:] diff --git a/ObjectCollection.py b/ObjectCollection.py index 570779e2..c3534f0f 100644 --- a/ObjectCollection.py +++ b/ObjectCollection.py @@ -724,6 +724,16 @@ class ObjectCollection(QtCore.QAbstractItemModel): log.error("[ERROR] Cause: %s" % str(e)) raise + def set_all_active(self): + """ + Select all objects from the project list. This triggers the + list_selection_changed event and call on_list_selection_changed. + + :return: None + """ + for name in self.get_names(): + self.set_active(name) + def set_exclusive_active(self, name): """ Make the object with the name in parameters the only selected object diff --git a/README.md b/README.md index d68f1c53..627b25b5 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,9 @@ CAD program, and create G-Code for Isolation routing. - added an translator email address - finished the update on German translation. Part of it was corrected by Jens Karstedt - finished the update of the Romanian translation. +- finished the Objects menu by adding the ability of actions to be checked so they will show the selected status of the objects and by adding to actions to (de)select all objects +- fixed and optimized the click selection on canvas +- fixed Gerber parsing for very simple Gerber files that have only one Polygon but many LPC zones 16.10.2019 diff --git a/flatcamGUI/FlatCAMGUI.py b/flatcamGUI/FlatCAMGUI.py index 052a5524..07de5b99 100644 --- a/flatcamGUI/FlatCAMGUI.py +++ b/flatcamGUI/FlatCAMGUI.py @@ -420,6 +420,12 @@ class FlatCAMGUI(QtWidgets.QMainWindow): # ########################## Objects # ################################### # ######################################################################## self.menuobjects = self.menu.addMenu(_('Objects')) + self.menuobjects.addSeparator() + self.menuobjects_selall = self.menuobjects.addAction(QtGui.QIcon('share/select_all.png'), _('Select All')) + self.menuobjects_unselall = self.menuobjects.addAction( + QtGui.QIcon('share/deselect_all32.png'), + _('Deselect All') + ) # ######################################################################## # ########################## Tool # ###################################### diff --git a/flatcamParsers/ParseGerber.py b/flatcamParsers/ParseGerber.py index 64f7aa8c..911f3b42 100644 --- a/flatcamParsers/ParseGerber.py +++ b/flatcamParsers/ParseGerber.py @@ -1384,7 +1384,7 @@ class Gerber(Geometry): # this treats the case when we are storing geometry as solids - if len(poly_buffer) == 0: + if len(poly_buffer) == 0 and len(self.solid_geometry) == 0: log.error("Object is not Gerber file or empty. Aborting Object creation.") return 'fail' diff --git a/share/deselect_all32.png b/share/deselect_all32.png new file mode 100644 index 0000000000000000000000000000000000000000..f1e02f52e77b39d2bed73f9eeb0cfe87882e22b7 GIT binary patch literal 690 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE0wix1Z>k4UEa{HEjtmSN`?>!lvVtU&J%W50 z7^>757#dm_7=8hT8eT9klo~KFyh>nTu$sZZAYL$MSD+10f+@+{-GzZ+Rj;xUkjGiz z5n0T@z;^_M8K-LVNdpDhOFVsD*&lNW35Z#4mcKQPfq}8X)5S5w=?4$(8cQh7u^|a-yL-^VCPKE{_839f4jrP$on3% zOpp1nc5T_294vjZ*uUF3xKUq5r%z+|7ipsd%kLK!OqkX2d`W(SvPDRPj6TO9(Z%of zeBu{xFfwcM4ZV0?@?sh<%dbF>gwpHwt9p39g}!b~-Pg<)$Yi-KH2YYvpmU?m_LK&9 z8KEn8x+J)sggjiQxnXHn6z8s1Rl~QZYxXNOAJ*@% zQt~09d(}Ul(qdm0-QgzeSS6UE$#_#DC7xy1(Vhd3GIx~z_Aofv^lMew%;tN+nJrAg z_ZlxN{%c+L&G%C3#q#@$5{mzD|0{m=S8PTOFy