From b108f6678050c24d2754815dfc66e0f791f55732 Mon Sep 17 00:00:00 2001 From: carlopav Date: Mon, 24 May 2021 23:10:55 +0200 Subject: [PATCH 1/7] Draft: Edit, Remove all the empty pass functions Remove unused functions. Since GuiTools objects inherit from the GuiTools class, they do not need those placeholders. --- .../draftguitools/gui_edit_arch_objects.py | 37 ------------------- .../draftguitools/gui_edit_draft_objects.py | 24 ------------ .../draftguitools/gui_edit_part_objects.py | 30 --------------- .../gui_edit_sketcher_objects.py | 7 ---- 4 files changed, 98 deletions(-) diff --git a/src/Mod/Draft/draftguitools/gui_edit_arch_objects.py b/src/Mod/Draft/draftguitools/gui_edit_arch_objects.py index 7986f06c1c..18264309ec 100644 --- a/src/Mod/Draft/draftguitools/gui_edit_arch_objects.py +++ b/src/Mod/Draft/draftguitools/gui_edit_arch_objects.py @@ -73,12 +73,6 @@ class ArchWallGuiTools(GuiTools): if vz.Length > 0: obj.Height = vz.Length - def get_edit_point_context_menu(self, obj, node_idx): - pass - - def evaluate_context_menu_action(self, edit_command, obj, node_idx, action): - pass - class ArchWindowGuiTools(GuiTools): @@ -112,12 +106,6 @@ class ArchWindowGuiTools(GuiTools): for obj in obj.Hosts: obj.recompute() - def get_edit_point_context_menu(self, obj, node_idx): - pass - - def evaluate_context_menu_action(self, edit_command, obj, node_idx, action): - pass - class ArchStructureGuiTools(GuiTools): @@ -138,12 +126,6 @@ class ArchStructureGuiTools(GuiTools): nodes[node_idx] = v obj.Nodes = nodes - def get_edit_point_context_menu(self, obj, node_idx): - pass - - def evaluate_context_menu_action(self, edit_command, obj, node_idx, action): - pass - def get_object_style(self, obj): return (obj.ViewObject.DisplayMode, obj.ViewObject.NodeSize, @@ -177,12 +159,6 @@ class ArchSpaceGuiTools(GuiTools): if node_idx == 0: obj.ViewObject.TextPosition = v - def get_edit_point_context_menu(self, obj, node_idx): - pass - - def evaluate_context_menu_action(self, edit_command, obj, node_idx, action): - pass - class ArchPanelCutGuiTools(GuiTools): @@ -202,12 +178,6 @@ class ArchPanelCutGuiTools(GuiTools): if node_idx == 0: obj.TagPosition = v - def get_edit_point_context_menu(self, obj, node_idx): - pass - - def evaluate_context_menu_action(self, edit_command, obj, node_idx, action): - pass - class ArchPanelSheetGuiTools(GuiTools): @@ -227,11 +197,4 @@ class ArchPanelSheetGuiTools(GuiTools): else: obj.Group[node_idx-1].Placement.Base = v - def get_edit_point_context_menu(self, obj, node_idx): - pass - - def evaluate_context_menu_action(self, edit_command, obj, node_idx, action): - pass - - ## @} diff --git a/src/Mod/Draft/draftguitools/gui_edit_draft_objects.py b/src/Mod/Draft/draftguitools/gui_edit_draft_objects.py index f09c902e46..b8e986e057 100644 --- a/src/Mod/Draft/draftguitools/gui_edit_draft_objects.py +++ b/src/Mod/Draft/draftguitools/gui_edit_draft_objects.py @@ -176,12 +176,6 @@ class DraftRectangleGuiTools(GuiTools): elif node_idx == 2: obj.Height = DraftVecUtils.project(v, App.Vector(0,1,0)).Length - def get_edit_point_context_menu(self, obj, node_idx): - pass - - def evaluate_context_menu_action(self, edit_command, obj, node_idx, action): - pass - class DraftCircleGuiTools(GuiTools): @@ -414,12 +408,6 @@ class DraftEllipseGuiTools(GuiTools): else: obj.MinorRadius = obj.MajorRadius - def get_edit_point_context_menu(self, obj, node_idx): - pass - - def evaluate_context_menu_action(self, edit_command, obj, node_idx, action): - pass - class DraftPolygonGuiTools(GuiTools): @@ -444,12 +432,6 @@ class DraftPolygonGuiTools(GuiTools): obj.Radius = v.Length obj.recompute() - def get_edit_point_context_menu(self, obj, node_idx): - pass - - def evaluate_context_menu_action(self, edit_command, obj, node_idx, action): - pass - class DraftDimensionGuiTools(GuiTools): @@ -475,12 +457,6 @@ class DraftDimensionGuiTools(GuiTools): elif node_idx == 3: obj.ViewObject.TextPosition = v - def get_edit_point_context_menu(self, obj, node_idx): - pass - - def evaluate_context_menu_action(self, edit_command, obj, node_idx, action): - pass - class DraftBezCurveGuiTools(GuiTools): diff --git a/src/Mod/Draft/draftguitools/gui_edit_part_objects.py b/src/Mod/Draft/draftguitools/gui_edit_part_objects.py index 456aea26bb..2a1d7e647d 100644 --- a/src/Mod/Draft/draftguitools/gui_edit_part_objects.py +++ b/src/Mod/Draft/draftguitools/gui_edit_part_objects.py @@ -57,12 +57,6 @@ class PartLineGuiTools(GuiTools): obj.Y2 = v.y obj.Z2 = v.z - def get_edit_point_context_menu(self, obj, node_idx): - pass - - def evaluate_context_menu_action(self, edit_command, obj, node_idx, action): - pass - class PartBoxGuiTools(GuiTools): @@ -90,12 +84,6 @@ class PartBoxGuiTools(GuiTools): _vector = DraftVecUtils.project(v, App.Vector(0, 0, 1)) obj.Height = _vector.Length - def get_edit_point_context_menu(self, obj, node_idx): - pass - - def evaluate_context_menu_action(self, edit_command, obj, node_idx, action): - pass - class PartCylinderGuiTools(GuiTools): @@ -119,12 +107,6 @@ class PartCylinderGuiTools(GuiTools): _vector = DraftVecUtils.project(v, App.Vector(0, 0, 1)) obj.Height = _vector.Length - def get_edit_point_context_menu(self, obj, node_idx): - pass - - def evaluate_context_menu_action(self, edit_command, obj, node_idx, action): - pass - class PartConeGuiTools(GuiTools): @@ -151,12 +133,6 @@ class PartConeGuiTools(GuiTools): _vector = DraftVecUtils.project(v, App.Vector(0, 0, 1)) obj.Height = _vector.Length - def get_edit_point_context_menu(self, obj, node_idx): - pass - - def evaluate_context_menu_action(self, edit_command, obj, node_idx, action): - pass - class PartSphereGuiTools(GuiTools): @@ -176,10 +152,4 @@ class PartSphereGuiTools(GuiTools): if v.Length > 0.0: obj.Radius = v.Length # TODO: Perhaps better to project on the face? - def get_edit_point_context_menu(self, obj, node_idx): - pass - - def evaluate_context_menu_action(self, edit_command, obj, node_idx, action): - pass - ## @} diff --git a/src/Mod/Draft/draftguitools/gui_edit_sketcher_objects.py b/src/Mod/Draft/draftguitools/gui_edit_sketcher_objects.py index bd11f40159..e62b8d7792 100644 --- a/src/Mod/Draft/draftguitools/gui_edit_sketcher_objects.py +++ b/src/Mod/Draft/draftguitools/gui_edit_sketcher_objects.py @@ -75,11 +75,4 @@ class SketcherSketchObjectGuiTools(GuiTools): obj.movePoint(0, 2, v) obj.recompute() - def get_edit_point_context_menu(self, obj, node_idx): - pass - - def evaluate_context_menu_action(self, edit_command, obj, node_idx, action): - pass - - ## @} From bd02136f9d9fd06440628fbcfedf05a99c30f703 Mon Sep 17 00:00:00 2001 From: carlopav Date: Mon, 24 May 2021 23:26:26 +0200 Subject: [PATCH 2/7] Draft: Edit, change startEditing method to accept (obj, node_idx) This enable to start editing also without an user click event, by just calling the method and specifying the object and the edit point index. It is used for alternative edit mode (alt_edit_mode) by arc context menu --- src/Mod/Draft/draftguitools/gui_edit.py | 36 ++++++++++--------- .../draftguitools/gui_edit_draft_objects.py | 2 +- 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/src/Mod/Draft/draftguitools/gui_edit.py b/src/Mod/Draft/draftguitools/gui_edit.py index 936b892876..cfb0323349 100644 --- a/src/Mod/Draft/draftguitools/gui_edit.py +++ b/src/Mod/Draft/draftguitools/gui_edit.py @@ -457,7 +457,16 @@ class Edit(gui_base_original.Modifier): ):#left click if not event.wasAltDown(): if self.editing is None: - self.startEditing(event) + + pos = event.getPosition() + node = self.getEditNode(pos) + node_idx = self.getEditNodeIndex(node) + if node_idx is None: + return + doc = App.getDocument(str(node.documentName.getValue())) + obj = doc.getObject(str(node.objectName.getValue())) + + self.startEditing(obj, node_idx) else: self.endEditing(self.obj, self.editing) elif event.wasAltDown(): # left click with ctrl down @@ -486,32 +495,25 @@ class Edit(gui_base_original.Modifier): self.overNode.setColor(COLORS["default"]) self.overNode = None - def startEditing(self, event): + def startEditing(self, obj, node_idx): """Start editing selected EditNode.""" - pos = event.getPosition() - node = self.getEditNode(pos) - ep = self.getEditNodeIndex(node) - if ep is None: + self.obj = obj # this is still needed to handle preview + if obj is None: return - doc = App.getDocument(str(node.documentName.getValue())) - self.obj = doc.getObject(str(node.objectName.getValue())) - if self.obj is None: - return - - App.Console.PrintMessage(self.obj.Name + App.Console.PrintMessage(obj.Name + ": editing node number " - + str(ep) + "\n") + + str(node_idx) + "\n") self.ui.lineUi() self.ui.isRelative.show() - self.editing = ep - self.trackers[self.obj.Name][self.editing].off() + self.editing = node_idx + self.trackers[obj.Name][node_idx].off() self.finalizeGhost() - self.initGhost(self.obj) + self.initGhost(obj) - self.node.append(self.trackers[self.obj.Name][self.editing].get()) + self.node.append(self.trackers[obj.Name][node_idx].get()) Gui.Snapper.setSelectMode(False) self.hideTrackers() diff --git a/src/Mod/Draft/draftguitools/gui_edit_draft_objects.py b/src/Mod/Draft/draftguitools/gui_edit_draft_objects.py index b8e986e057..c3abdebc05 100644 --- a/src/Mod/Draft/draftguitools/gui_edit_draft_objects.py +++ b/src/Mod/Draft/draftguitools/gui_edit_draft_objects.py @@ -290,7 +290,7 @@ class DraftCircleGuiTools(GuiTools): if action in ("move arc", "set radius", "set first angle", "set last angle"): edit_command.alt_edit_mode = 1 - edit_command.startEditing(edit_command.event) + edit_command.startEditing(obj, node_idx) def init_preview_object(self, obj): return trackers.arcTracker() From cc87ab4913c6d957af515ab55a2b2c16baf55c4b Mon Sep 17 00:00:00 2001 From: carlopav Date: Mon, 24 May 2021 23:38:43 +0200 Subject: [PATCH 3/7] Draft: Edit, Split out addPoint in multiple methods in the GuiTools Now Draft_Wire, Draft_BSpline, Draft_Bezcurve have their own methods to add points. --- src/Mod/Draft/draftguitools/gui_edit.py | 106 +------------- .../draftguitools/gui_edit_draft_objects.py | 138 +++++++++++++++++- 2 files changed, 139 insertions(+), 105 deletions(-) diff --git a/src/Mod/Draft/draftguitools/gui_edit.py b/src/Mod/Draft/draftguitools/gui_edit.py index cfb0323349..c3846a243d 100644 --- a/src/Mod/Draft/draftguitools/gui_edit.py +++ b/src/Mod/Draft/draftguitools/gui_edit.py @@ -672,109 +672,6 @@ class Edit(gui_base_original.Modifier): except Exception: return - # ------------------------------------------------------------------------- - # EDIT OBJECT TOOLS : Add/Delete Vertexes - # ------------------------------------------------------------------------- - - def addPoint(self, event): - """Add point to obj and reset trackers. - """ - pos = event.getPosition() - # self.setSelectState(obj, True) - selobjs = Gui.ActiveDocument.ActiveView.getObjectsInfo((pos[0],pos[1])) - if not selobjs: - return - for info in selobjs: - if not info: - return - for o in self.edited_objects: - if o.Name != info["Object"]: - continue - obj = o - break - if utils.get_type(obj) == "Wire" and 'Edge' in info["Component"]: - pt = App.Vector(info["x"], info["y"], info["z"]) - self.addPointToWire(obj, pt, int(info["Component"][4:])) - elif utils.get_type(obj) in ["BSpline", "BezCurve"]: #to fix double vertex created - # pt = self.point - if "x" in info:# prefer "real" 3D location over working-plane-driven one if possible - pt = App.Vector(info["x"], info["y"], info["z"]) - else: - continue - self.addPointToCurve(pt, obj, info) - obj.recompute() - self.resetTrackers(obj) - return - - def addPointToWire(self, obj, newPoint, edgeIndex): - newPoints = [] - if hasattr(obj, "ChamferSize") and hasattr(obj, "FilletRadius"): - if obj.ChamferSize > 0 and obj.FilletRadius > 0: - edgeIndex = (edgeIndex + 3) / 4 - elif obj.ChamferSize > 0 or obj.FilletRadius > 0: - edgeIndex = (edgeIndex + 1) / 2 - - for index, point in enumerate(obj.Points): - if index == edgeIndex: - newPoints.append(self.localize_vector(obj, newPoint)) - newPoints.append(point) - if obj.Closed and edgeIndex == len(obj.Points): - # last segment when object is closed - newPoints.append(self.localize_vector(obj, newPoint)) - obj.Points = newPoints - - def addPointToCurve(self, point, obj, info=None): - import Part - if utils.get_type(obj) not in ["BSpline", "BezCurve"]: - return - pts = obj.Points - if utils.get_type(obj) == "BezCurve": - if not info['Component'].startswith('Edge'): - return # clicked control point - edgeindex = int(info['Component'].lstrip('Edge')) - 1 - wire = obj.Shape.Wires[0] - bz = wire.Edges[edgeindex].Curve - param = bz.parameter(point) - seg1 = wire.Edges[edgeindex].copy().Curve - seg2 = wire.Edges[edgeindex].copy().Curve - seg1.segment(seg1.FirstParameter, param) - seg2.segment(param, seg2.LastParameter) - if edgeindex == len(wire.Edges): - # we hit the last segment, we need to fix the degree - degree=wire.Edges[0].Curve.Degree - seg1.increase(degree) - seg2.increase(degree) - edges = wire.Edges[0:edgeindex] + [Part.Edge(seg1),Part.Edge(seg2)] \ - + wire.Edges[edgeindex + 1:] - pts = edges[0].Curve.getPoles()[0:1] - for edge in edges: - pts.extend(edge.Curve.getPoles()[1:]) - if obj.Closed: - pts.pop() - c = obj.Continuity - # assume we have a tangent continuity for an arbitrarily split - # segment, unless it's linear - cont = 1 if (obj.Degree >= 2) else 0 - obj.Continuity = c[0:edgeindex] + [cont] + c[edgeindex:] - else: - if (utils.get_type(obj) in ["BSpline"]): - if (obj.Closed == True): - curve = obj.Shape.Edges[0].Curve - else: - curve = obj.Shape.Curve - uNewPoint = curve.parameter(point) - uPoints = [] - for p in obj.Points: - uPoints.append(curve.parameter(p)) - for i in range(len(uPoints) - 1): - if ( uNewPoint > uPoints[i] ) and ( uNewPoint < uPoints[i+1] ): - pts.insert(i + 1, self.localize_vector(obj, point)) - break - # DNC: fix: add points to last segment if curve is closed - if obj.Closed and (uNewPoint > uPoints[-1]): - pts.append(self.localize_vector(obj, point)) - obj.Points = pts - # ------------------------------------------------------------------------ # DRAFT EDIT Context menu # ------------------------------------------------------------------------ @@ -833,7 +730,8 @@ class Edit(gui_base_original.Modifier): actions = obj_gui_tools.evaluate_context_menu_action(self, obj, idx, action_label) elif action_label == "add point": - self.addPoint(self.event) + if obj and obj_gui_tools: + obj_gui_tools.add_point(self.event) elif action_label == "invert arc": pos = self.event.getPosition() diff --git a/src/Mod/Draft/draftguitools/gui_edit_draft_objects.py b/src/Mod/Draft/draftguitools/gui_edit_draft_objects.py index c3abdebc05..8189501462 100644 --- a/src/Mod/Draft/draftguitools/gui_edit_draft_objects.py +++ b/src/Mod/Draft/draftguitools/gui_edit_draft_objects.py @@ -46,6 +46,7 @@ __url__ = "https://www.freecadweb.org" # @{ import math import FreeCAD as App +import FreeCADGui as Gui import DraftVecUtils from draftutils.translate import translate @@ -123,7 +124,45 @@ class DraftWireGuiTools(GuiTools): if obj.Closed: pointList.append(pointList[0]) edit_command.ghost.updateFromPointlist(pointList) - + + def add_point(self, edit_command, obj, pos): + """Add point to obj. + """ + # self.setSelectState(obj, True) + selobjs = Gui.ActiveDocument.ActiveView.getObjectsInfo((pos[0],pos[1])) + if not selobjs: + return + for info in selobjs: + if not info: + return + if obj.Name != info["Object"]: + return + if not 'Edge' in info["Component"]: + return + newPoint = App.Vector(info["x"], info["y"], info["z"]) + edgeIndex = int(info["Component"][4:]) + + newPoints = [] + if hasattr(obj, "ChamferSize") and hasattr(obj, "FilletRadius"): + # TODO: If Draft_Wire fails to calculate one of the fillets or chamfers + # this algo fails to identify the correct edge + if obj.ChamferSize > 0 and obj.FilletRadius > 0: + edgeIndex = (edgeIndex + 3) / 4 + elif obj.ChamferSize > 0 or obj.FilletRadius > 0: + edgeIndex = (edgeIndex + 1) / 2 + + for index, point in enumerate(obj.Points): + if index == edgeIndex: + newPoints.append(edit_command.localize_vector(obj, newPoint)) + newPoints.append(point) + if obj.Closed and edgeIndex == len(obj.Points): + # last segment when object is closed + newPoints.append(edit_command.localize_vector(obj, newPoint)) + obj.Points = newPoints + + obj.recompute() + return + def delete_point(self, obj, node_idx): if len(obj.Points) <= 2: _msg = translate("draft", "Active object must have more than two points/nodes") @@ -149,6 +188,44 @@ class DraftBSplineGuiTools(DraftWireGuiTools): pointList.append(pointList[0]) edit_command.ghost.update(pointList) + def add_point(self, edit_command, obj, pos): + """Add point to obj. + """ + # self.setSelectState(obj, True) + selobjs = Gui.ActiveDocument.ActiveView.getObjectsInfo((pos[0],pos[1])) + if not selobjs: + return + for info in selobjs: + if not info: + return + if obj.Name != info["Object"]: + return + if "x" in info:# prefer "real" 3D location over working-plane-driven one if possible + point = App.Vector(info["x"], info["y"], info["z"]) + else: + continue + + pts = obj.Points + if (obj.Closed == True): + curve = obj.Shape.Edges[0].Curve + else: + curve = obj.Shape.Curve + uNewPoint = curve.parameter(point) + uPoints = [] + for p in obj.Points: + uPoints.append(curve.parameter(p)) + for i in range(len(uPoints) - 1): + if ( uNewPoint > uPoints[i] ) and ( uNewPoint < uPoints[i+1] ): + pts.insert(i + 1, edit_command.localize_vector(obj, point)) + break + # DNC: fix: add points to last segment if curve is closed + if obj.Closed and (uNewPoint > uPoints[-1]): + pts.append(edit_command.localize_vector(obj, point)) + obj.Points = pts + + obj.recompute() + return + class DraftRectangleGuiTools(GuiTools): @@ -690,4 +767,63 @@ class DraftBezCurveGuiTools(GuiTools): obj.Points = pts obj.Continuity = newcont + def add_point(self, edit_command, obj, pos): + """Add point to obj and reset trackers. + """ + # self.setSelectState(obj, True) + selobjs = Gui.ActiveDocument.ActiveView.getObjectsInfo((pos[0],pos[1])) + if not selobjs: + return + for info in selobjs: + if not info: + return + for o in edit_command.edited_objects: + if o.Name != info["Object"]: + continue + obj = o + break + if utils.get_type(obj) == "BezCurve": #to fix double vertex created + # pt = self.point + if "x" in info:# prefer "real" 3D location over working-plane-driven one if possible + pt = App.Vector(info["x"], info["y"], info["z"]) + else: + continue + self.addPointToCurve(pt, obj, info) + obj.recompute() + return + + def addPointToCurve(self, point, obj, info=None): + import Part + + pts = obj.Points + if not info['Component'].startswith('Edge'): + return # clicked control point + edgeindex = int(info['Component'].lstrip('Edge')) - 1 + wire = obj.Shape.Wires[0] + bz = wire.Edges[edgeindex].Curve + param = bz.parameter(point) + seg1 = wire.Edges[edgeindex].copy().Curve + seg2 = wire.Edges[edgeindex].copy().Curve + seg1.segment(seg1.FirstParameter, param) + seg2.segment(param, seg2.LastParameter) + if edgeindex == len(wire.Edges): + # we hit the last segment, we need to fix the degree + degree=wire.Edges[0].Curve.Degree + seg1.increase(degree) + seg2.increase(degree) + edges = wire.Edges[0:edgeindex] + [Part.Edge(seg1),Part.Edge(seg2)] \ + + wire.Edges[edgeindex + 1:] + pts = edges[0].Curve.getPoles()[0:1] + for edge in edges: + pts.extend(edge.Curve.getPoles()[1:]) + if obj.Closed: + pts.pop() + c = obj.Continuity + # assume we have a tangent continuity for an arbitrarily split + # segment, unless it's linear + cont = 1 if (obj.Degree >= 2) else 0 + obj.Continuity = c[0:edgeindex] + [cont] + c[edgeindex:] + + obj.Points = pts + ## @} From 28ced10d50bd9c9aae2bb9062c1b3fce86c65421 Mon Sep 17 00:00:00 2001 From: carlopav Date: Mon, 24 May 2021 23:44:48 +0200 Subject: [PATCH 4/7] Draft: Edit, rewriting context menu system get_edit_point_context_menu(self, edit_command, obj, node_idx) get_edit_obj_context_menu(self, edit_command, obj, position) that are called depending if the user is over an editpoint or over another part of the object. --- src/Mod/Draft/draftguitools/gui_edit.py | 47 ++----- .../draftguitools/gui_edit_base_object.py | 29 ++++- .../draftguitools/gui_edit_draft_objects.py | 122 ++++++++++++------ 3 files changed, 118 insertions(+), 80 deletions(-) diff --git a/src/Mod/Draft/draftguitools/gui_edit.py b/src/Mod/Draft/draftguitools/gui_edit.py index c3846a243d..db2bce71a4 100644 --- a/src/Mod/Draft/draftguitools/gui_edit.py +++ b/src/Mod/Draft/draftguitools/gui_edit.py @@ -678,7 +678,6 @@ class Edit(gui_base_original.Modifier): def display_tracker_menu(self, event): self.tracker_menu = QtGui.QMenu() - self.event = event actions = None if self.overNode: @@ -689,22 +688,23 @@ class Edit(gui_base_original.Modifier): obj_gui_tools = self.get_obj_gui_tools(obj) if obj_gui_tools: - actions = obj_gui_tools.get_edit_point_context_menu(obj, ep) + actions = obj_gui_tools.get_edit_point_context_menu(self, obj, ep) else: # try if user is over an edited object - pos = self.event.getPosition() + pos = event.getPosition() obj = self.get_selected_obj_at_position(pos) - if utils.get_type(obj) in ["Line", "Wire", "BSpline", "BezCurve"]: - actions = ["add point"] - elif utils.get_type(obj) in ["Circle"] and obj.FirstAngle != obj.LastAngle: - actions = ["invert arc"] + + obj_gui_tools = self.get_obj_gui_tools(obj) + if obj_gui_tools: + actions = obj_gui_tools.get_edit_obj_context_menu(self, obj, pos) if actions is None: return - for a in actions: - self.tracker_menu.addAction(a) + for (label, callback) in actions: + action = self.tracker_menu.addAction(label) + action.setData(callback) self.tracker_menu.popup(Gui.getMainWindow().cursor().pos()) @@ -713,32 +713,9 @@ class Edit(gui_base_original.Modifier): self.evaluate_menu_action) - def evaluate_menu_action(self, labelname): - action_label = str(labelname.text()) - - doc = None - obj = None - idx = None - - if self.overNode: - doc = self.overNode.get_doc_name() - obj = App.getDocument(doc).getObject(self.overNode.get_obj_name()) - idx = self.overNode.get_subelement_index() - - obj_gui_tools = self.get_obj_gui_tools(obj) - if obj and obj_gui_tools: - actions = obj_gui_tools.evaluate_context_menu_action(self, obj, idx, action_label) - - elif action_label == "add point": - if obj and obj_gui_tools: - obj_gui_tools.add_point(self.event) - - elif action_label == "invert arc": - pos = self.event.getPosition() - obj = self.get_selected_obj_at_position(pos) - obj_gui_tools.arcInvert(obj) - - del self.event + def evaluate_menu_action(self, action): + callback = action.data() + callback() # ------------------------------------------------------------------------- diff --git a/src/Mod/Draft/draftguitools/gui_edit_base_object.py b/src/Mod/Draft/draftguitools/gui_edit_base_object.py index 7eb3511371..40efc832b4 100644 --- a/src/Mod/Draft/draftguitools/gui_edit_base_object.py +++ b/src/Mod/Draft/draftguitools/gui_edit_base_object.py @@ -61,14 +61,33 @@ class GuiTools: """ pass - def get_edit_point_context_menu(self, obj, node_idx): - """ Return a list of Draft_Edit context menu actions. + def get_edit_point_context_menu(self, edit_command, obj, node_idx): + """ Get the context menu associated to edit points (user is over an editpoint) + + Return a list of tuples containig menu labels and associated functions: + return [ + ("action label", lambda: self.handle_action_label(edit_command, obj, node_idx)), + ] + + Parameters: + edit_command: running Draft_Edit command + obj: the edited object + node_idx: number of the edited node """ pass - - def evaluate_context_menu_action(self, edit_command, obj, node_idx, action): - """ Do something when a Draft_Edit context menu action is triggered over a node. + def get_edit_obj_context_menu(self, edit_command, obj, position): + """ Get the context menu associated to edited object (user is over the object) + + Return a list of tuples containig menu labels and associated functions: + return [ + ("action label", lambda: self.handle_action_label(edit_command, obj, position)), + ] + + Parameters: + edit_command: running Draft_Edit command + obj: the edited object + position: position of the cursor on the screen (x, y) """ pass diff --git a/src/Mod/Draft/draftguitools/gui_edit_draft_objects.py b/src/Mod/Draft/draftguitools/gui_edit_draft_objects.py index 8189501462..d5e8f8fb90 100644 --- a/src/Mod/Draft/draftguitools/gui_edit_draft_objects.py +++ b/src/Mod/Draft/draftguitools/gui_edit_draft_objects.py @@ -104,15 +104,23 @@ class DraftWireGuiTools(GuiTools): pts[node_idx] = v obj.Points = pts + def get_edit_point_context_menu(self, edit_command, obj, node_idx): + return [ + ("delete point", lambda: self.handle_delete_point(edit_command, obj, node_idx)), + ] - def get_edit_point_context_menu(self, obj, node_idx): - actions = ["delete point"] - return actions - - def evaluate_context_menu_action(self, edit_command, obj, node_idx, action): - if action == "delete point": - self.delete_point(obj, node_idx) - edit_command.resetTrackers(obj) + def get_edit_obj_context_menu(self, edit_command, obj, position): + return [ + ("add point", lambda: self.handle_add_point(edit_command, obj, position)), + ] + + def handle_delete_point(self, edit_command, obj, node_idx): + self.delete_point(obj, node_idx) + edit_command.resetTrackers(obj) + + def handle_add_point(self, edit_command, obj, pos): + self.add_point(edit_command, obj, pos) + edit_command.resetTrackers(obj) def init_preview_object(self, obj): return trackers.wireTracker(obj.Shape) @@ -348,26 +356,50 @@ class DraftCircleGuiTools(GuiTools): obj.Radius = v.Length - def get_edit_point_context_menu(self, obj, node_idx): + def get_edit_point_context_menu(self, edit_command, obj, node_idx): actions = None if obj.FirstAngle != obj.LastAngle: if node_idx == 0: # user is over arc start point - actions = ["move arc"] + return [ + ("move arc", lambda: self.handle_move_arc(edit_command, obj, node_idx)), + ] elif node_idx == 1: # user is over arc start point - actions = ["set first angle"] + return [ + ("mset first angle", lambda: self.handle_set_first_angle(edit_command, obj, node_idx)), + ] elif node_idx == 2: # user is over arc end point - actions = ["set last angle"] + return [ + ("set last angle", lambda: self.handle_set_last_angle(edit_command, obj, node_idx)), + ] elif node_idx == 3: # user is over arc mid point - actions = ["set radius"] - if actions: - return actions + return [ + ("set radius", lambda: self.handle_set_radius(edit_command, obj, node_idx)), + ] + def handle_move_arc(self, edit_command, obj, node_idx): + edit_command.alt_edit_mode = 1 + edit_command.startEditing(obj, node_idx) - def evaluate_context_menu_action(self, edit_command, obj, node_idx, action): - if action in ("move arc", "set radius", - "set first angle", "set last angle"): - edit_command.alt_edit_mode = 1 - edit_command.startEditing(obj, node_idx) + def handle_set_first_angle(self, edit_command, obj, node_idx): + edit_command.alt_edit_mode = 1 + edit_command.startEditing(obj, node_idx) + + def handle_set_last_angle(self, edit_command, obj, node_idx): + edit_command.alt_edit_mode = 1 + edit_command.startEditing(obj, node_idx) + + def handle_set_radius(self, edit_command, obj, node_idx): + edit_command.alt_edit_mode = 1 + edit_command.startEditing(obj, node_idx) + + def get_edit_obj_context_menu(self, edit_command, obj, position): + return [ + ("invert arc", lambda: self.handle_invert_arc(edit_command, obj, position)), + ] + + def handle_invert_arc(self, edit_command, obj, position): + self.arcInvert(obj) + edit_command.resetTrackers(obj) def init_preview_object(self, obj): return trackers.arcTracker() @@ -574,28 +606,38 @@ class DraftBezCurveGuiTools(GuiTools): obj.Points = pts - def get_edit_point_context_menu(self, obj, node_idx): - if utils.get_type(obj) in ["Line", "Wire", "BSpline"]: - actions = ["delete point"] - elif utils.get_type(obj) in ["BezCurve"]: - actions = ["make sharp", "make tangent", - "make symmetric", "delete point"] - return actions + def get_edit_point_context_menu(self, edit_command, obj, node_idx): + return [ + ("make sharp", lambda: self.handle_make_sharp(edit_command, obj, node_idx)), + ("make tangent", lambda: self.handle_make_tangent(edit_command, obj, node_idx)), + ("make symmetric", lambda: self.handle_make_symmetric(edit_command, obj, node_idx)), + ("delete point", lambda: self.handle_delete_point(edit_command, obj, node_idx)), + ] + def get_edit_obj_context_menu(self, edit_command, obj, position): + return [ + ("add point", lambda: self.handle_add_point(edit_command, obj, position)), + ] - def evaluate_context_menu_action(self, edit_command, obj, node_idx, action): - if action == "delete point": - self.delete_point(obj, node_idx) - edit_command.resetTrackers(obj) - # Bezier curve menu - elif action in ["make sharp", "make tangent", "make symmetric"]: - if action == "make sharp": - self.smoothBezPoint(obj, node_idx, 'Sharp') - elif action == "make tangent": - self.smoothBezPoint(obj, node_idx, 'Tangent') - elif action == "make symmetric": - self.smoothBezPoint(obj, node_idx, 'Symmetric') - edit_command.resetTrackers(obj) + def handle_make_sharp(self, edit_command, obj, node_idx): + self.smoothBezPoint(obj, node_idx, 'Sharp') + edit_command.resetTrackers(obj) + + def handle_make_tangent(self, edit_command, obj, node_idx): + self.smoothBezPoint(obj, node_idx, 'Tangent') + edit_command.resetTrackers(obj) + + def handle_make_symmetric(self, edit_command, obj, node_idx): + self.smoothBezPoint(obj, node_idx, 'Symmetric') + edit_command.resetTrackers(obj) + + def handle_delete_point(self, edit_command, obj, node_idx): + self.delete_point(obj, node_idx) + edit_command.resetTrackers(obj) + + def handle_add_point(self, edit_command, obj, pos): + self.add_point(edit_command, obj, pos) + edit_command.resetTrackers(obj) def init_preview_object(self, obj): From 0ea4c5d2fe7c6bd056721030634b0f63464aa99e Mon Sep 17 00:00:00 2001 From: carlopav Date: Mon, 24 May 2021 23:45:51 +0200 Subject: [PATCH 5/7] Draft: Edit, Added reverse wire option to context menu Now Draft Edit can reverse the order of the points of a Draft_Wire --- src/Mod/Draft/draftguitools/gui_edit_draft_objects.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/Mod/Draft/draftguitools/gui_edit_draft_objects.py b/src/Mod/Draft/draftguitools/gui_edit_draft_objects.py index d5e8f8fb90..797b02ff0e 100644 --- a/src/Mod/Draft/draftguitools/gui_edit_draft_objects.py +++ b/src/Mod/Draft/draftguitools/gui_edit_draft_objects.py @@ -112,6 +112,7 @@ class DraftWireGuiTools(GuiTools): def get_edit_obj_context_menu(self, edit_command, obj, position): return [ ("add point", lambda: self.handle_add_point(edit_command, obj, position)), + ("reverse wire", lambda: self.handle_reverse_wire(edit_command, obj, position)), ] def handle_delete_point(self, edit_command, obj, node_idx): @@ -122,6 +123,10 @@ class DraftWireGuiTools(GuiTools): self.add_point(edit_command, obj, pos) edit_command.resetTrackers(obj) + def handle_reverse_wire(self, edit_command, obj, pos): + self.reverse_wire(obj) + edit_command.resetTrackers(obj) + def init_preview_object(self, obj): return trackers.wireTracker(obj.Shape) @@ -183,6 +188,9 @@ class DraftWireGuiTools(GuiTools): obj.recompute() + def reverse_wire(self, obj): + obj.Points = reversed(obj.Points) + obj.recompute() class DraftBSplineGuiTools(DraftWireGuiTools): From 5841b737e14acb35a130d5c594472c41aae879ac Mon Sep 17 00:00:00 2001 From: carlopav Date: Tue, 25 May 2021 00:04:38 +0200 Subject: [PATCH 6/7] Draft: Edit, moved resetTrackers for context menu actions to main module The wrapper allows to call resetTrackers in the main module after the callback to the GuiTools is executed. This is the last commit, many thanks to @matthijskooijman for having menthored me :) I think it's helpful to have @matthijskooijman explanation on this use of the wrapper: This defines a new wrapper function, that calls the original callback and then calls resetTrackers. Note that this creates a new function for every loop iteration, so each of these wrapper functions captures potentially different callback, self and obj values so things work as expected. Note I did something weird with the callback value there: Since functions like these capture a variable, not its value at the time of function definition, and loop variables like label and callback are a single variable shared between all loop iterations, capturing callback directly ends up with all wrappers calling the last callback (i.e. they all capture the same variable and by the time the wrappers are called, that variable will contain the last of the callbacks). This is commonly solved by using a default value in the function definition, since such a default value uses the value of the (in this case) callback variable, not capturing the variable. --- src/Mod/Draft/draftguitools/gui_edit.py | 6 +- .../draftguitools/gui_edit_draft_objects.py | 55 +++---------------- 2 files changed, 14 insertions(+), 47 deletions(-) diff --git a/src/Mod/Draft/draftguitools/gui_edit.py b/src/Mod/Draft/draftguitools/gui_edit.py index db2bce71a4..90e8f30220 100644 --- a/src/Mod/Draft/draftguitools/gui_edit.py +++ b/src/Mod/Draft/draftguitools/gui_edit.py @@ -703,8 +703,12 @@ class Edit(gui_base_original.Modifier): return for (label, callback) in actions: + def wrapper(callback=callback): + callback() + self.resetTrackers(obj) + action = self.tracker_menu.addAction(label) - action.setData(callback) + action.setData(wrapper) self.tracker_menu.popup(Gui.getMainWindow().cursor().pos()) diff --git a/src/Mod/Draft/draftguitools/gui_edit_draft_objects.py b/src/Mod/Draft/draftguitools/gui_edit_draft_objects.py index 797b02ff0e..854ebf1da6 100644 --- a/src/Mod/Draft/draftguitools/gui_edit_draft_objects.py +++ b/src/Mod/Draft/draftguitools/gui_edit_draft_objects.py @@ -106,27 +106,15 @@ class DraftWireGuiTools(GuiTools): def get_edit_point_context_menu(self, edit_command, obj, node_idx): return [ - ("delete point", lambda: self.handle_delete_point(edit_command, obj, node_idx)), + ("delete point", lambda: self.delete_point(obj, node_idx)), ] def get_edit_obj_context_menu(self, edit_command, obj, position): return [ - ("add point", lambda: self.handle_add_point(edit_command, obj, position)), - ("reverse wire", lambda: self.handle_reverse_wire(edit_command, obj, position)), + ("add point", lambda: self.add_point(edit_command, obj, position)), + ("reverse wire", lambda: self.reverse_wire(obj)), ] - def handle_delete_point(self, edit_command, obj, node_idx): - self.delete_point(obj, node_idx) - edit_command.resetTrackers(obj) - - def handle_add_point(self, edit_command, obj, pos): - self.add_point(edit_command, obj, pos) - edit_command.resetTrackers(obj) - - def handle_reverse_wire(self, edit_command, obj, pos): - self.reverse_wire(obj) - edit_command.resetTrackers(obj) - def init_preview_object(self, obj): return trackers.wireTracker(obj.Shape) @@ -402,13 +390,9 @@ class DraftCircleGuiTools(GuiTools): def get_edit_obj_context_menu(self, edit_command, obj, position): return [ - ("invert arc", lambda: self.handle_invert_arc(edit_command, obj, position)), + ("invert arc", lambda: self.arcInvert(obj)), ] - def handle_invert_arc(self, edit_command, obj, position): - self.arcInvert(obj) - edit_command.resetTrackers(obj) - def init_preview_object(self, obj): return trackers.arcTracker() @@ -616,38 +600,17 @@ class DraftBezCurveGuiTools(GuiTools): def get_edit_point_context_menu(self, edit_command, obj, node_idx): return [ - ("make sharp", lambda: self.handle_make_sharp(edit_command, obj, node_idx)), - ("make tangent", lambda: self.handle_make_tangent(edit_command, obj, node_idx)), - ("make symmetric", lambda: self.handle_make_symmetric(edit_command, obj, node_idx)), - ("delete point", lambda: self.handle_delete_point(edit_command, obj, node_idx)), + ("make sharp", lambda: self.smoothBezPoint(obj, node_idx, 'Sharp')), + ("make tangent", lambda: self.smoothBezPoint(obj, node_idx, 'Tangent')), + ("make symmetric", lambda: self.smoothBezPoint(obj, node_idx, 'Symmetric')), + ("delete point", lambda: self.delete_point(obj, node_idx)), ] def get_edit_obj_context_menu(self, edit_command, obj, position): return [ - ("add point", lambda: self.handle_add_point(edit_command, obj, position)), + ("add point", lambda: self.add_point(edit_command, obj, position)), ] - def handle_make_sharp(self, edit_command, obj, node_idx): - self.smoothBezPoint(obj, node_idx, 'Sharp') - edit_command.resetTrackers(obj) - - def handle_make_tangent(self, edit_command, obj, node_idx): - self.smoothBezPoint(obj, node_idx, 'Tangent') - edit_command.resetTrackers(obj) - - def handle_make_symmetric(self, edit_command, obj, node_idx): - self.smoothBezPoint(obj, node_idx, 'Symmetric') - edit_command.resetTrackers(obj) - - def handle_delete_point(self, edit_command, obj, node_idx): - self.delete_point(obj, node_idx) - edit_command.resetTrackers(obj) - - def handle_add_point(self, edit_command, obj, pos): - self.add_point(edit_command, obj, pos) - edit_command.resetTrackers(obj) - - def init_preview_object(self, obj): return trackers.bezcurveTracker() From 267291567fcb127fb97a68aa7a2cf51457d2db96 Mon Sep 17 00:00:00 2001 From: carlopav Date: Tue, 25 May 2021 01:03:20 +0200 Subject: [PATCH 7/7] Draft: Edit, cleanup of new GuiTools.addPoint methods In new splitted addPoint methods, getObjectsInfo and the consequent checks have been removed and moved to main DraftEdit module in a new get_specific_object_info method. This method returns the info for the selected object at a given position and the 3d Vector of the point clicked on the object. --- src/Mod/Draft/draftguitools/gui_edit.py | 14 ++ .../draftguitools/gui_edit_draft_objects.py | 130 +++++++----------- 2 files changed, 62 insertions(+), 82 deletions(-) diff --git a/src/Mod/Draft/draftguitools/gui_edit.py b/src/Mod/Draft/draftguitools/gui_edit.py index 90e8f30220..d334fac5e8 100644 --- a/src/Mod/Draft/draftguitools/gui_edit.py +++ b/src/Mod/Draft/draftguitools/gui_edit.py @@ -843,6 +843,20 @@ class Edit(gui_base_original.Modifier): obj_gui_tools.restore_object_style(obj, self.objs_formats[obj.Name]) + def get_specific_object_info(self, obj, pos): + """Return info of a specific object at a given position. + """ + selobjs = Gui.ActiveDocument.ActiveView.getObjectsInfo((pos[0],pos[1])) + if not selobjs: + return + for info in selobjs: + if not info: + continue + if obj.Name == info["Object"] and "x" in info: + # prefer "real" 3D location over working-plane-driven one if possible + pt = App.Vector(info["x"], info["y"], info["z"]) + return info, pt + def get_selected_obj_at_position(self, pos): """Return object at given position. diff --git a/src/Mod/Draft/draftguitools/gui_edit_draft_objects.py b/src/Mod/Draft/draftguitools/gui_edit_draft_objects.py index 854ebf1da6..bce3f5aaf3 100644 --- a/src/Mod/Draft/draftguitools/gui_edit_draft_objects.py +++ b/src/Mod/Draft/draftguitools/gui_edit_draft_objects.py @@ -129,40 +129,34 @@ class DraftWireGuiTools(GuiTools): def add_point(self, edit_command, obj, pos): """Add point to obj. """ - # self.setSelectState(obj, True) - selobjs = Gui.ActiveDocument.ActiveView.getObjectsInfo((pos[0],pos[1])) - if not selobjs: + info, newPoint = edit_command.get_specific_object_info(obj,pos) + + if not info: return - for info in selobjs: - if not info: - return - if obj.Name != info["Object"]: - return - if not 'Edge' in info["Component"]: - return - newPoint = App.Vector(info["x"], info["y"], info["z"]) - edgeIndex = int(info["Component"][4:]) + if not 'Edge' in info["Component"]: + return + edgeIndex = int(info["Component"][4:]) - newPoints = [] - if hasattr(obj, "ChamferSize") and hasattr(obj, "FilletRadius"): - # TODO: If Draft_Wire fails to calculate one of the fillets or chamfers - # this algo fails to identify the correct edge - if obj.ChamferSize > 0 and obj.FilletRadius > 0: - edgeIndex = (edgeIndex + 3) / 4 - elif obj.ChamferSize > 0 or obj.FilletRadius > 0: - edgeIndex = (edgeIndex + 1) / 2 + newPoints = [] + if hasattr(obj, "ChamferSize") and hasattr(obj, "FilletRadius"): + # TODO: If Draft_Wire fails to calculate one of the fillets or chamfers + # this algo fails to identify the correct edge + if obj.ChamferSize > 0 and obj.FilletRadius > 0: + edgeIndex = (edgeIndex + 3) / 4 + elif obj.ChamferSize > 0 or obj.FilletRadius > 0: + edgeIndex = (edgeIndex + 1) / 2 - for index, point in enumerate(obj.Points): - if index == edgeIndex: - newPoints.append(edit_command.localize_vector(obj, newPoint)) - newPoints.append(point) - if obj.Closed and edgeIndex == len(obj.Points): - # last segment when object is closed + for index, point in enumerate(obj.Points): + if index == edgeIndex: newPoints.append(edit_command.localize_vector(obj, newPoint)) - obj.Points = newPoints + newPoints.append(point) + if obj.Closed and edgeIndex == len(obj.Points): + # last segment when object is closed + newPoints.append(edit_command.localize_vector(obj, newPoint)) + obj.Points = newPoints - obj.recompute() - return + obj.recompute() + return def delete_point(self, obj, node_idx): if len(obj.Points) <= 2: @@ -195,40 +189,29 @@ class DraftBSplineGuiTools(DraftWireGuiTools): def add_point(self, edit_command, obj, pos): """Add point to obj. """ - # self.setSelectState(obj, True) - selobjs = Gui.ActiveDocument.ActiveView.getObjectsInfo((pos[0],pos[1])) - if not selobjs: + info, pt = edit_command.get_specific_object_info(obj,pos) + if not info or (pt is None): return - for info in selobjs: - if not info: - return - if obj.Name != info["Object"]: - return - if "x" in info:# prefer "real" 3D location over working-plane-driven one if possible - point = App.Vector(info["x"], info["y"], info["z"]) - else: - continue - pts = obj.Points - if (obj.Closed == True): - curve = obj.Shape.Edges[0].Curve - else: - curve = obj.Shape.Curve - uNewPoint = curve.parameter(point) - uPoints = [] - for p in obj.Points: - uPoints.append(curve.parameter(p)) - for i in range(len(uPoints) - 1): - if ( uNewPoint > uPoints[i] ) and ( uNewPoint < uPoints[i+1] ): - pts.insert(i + 1, edit_command.localize_vector(obj, point)) - break - # DNC: fix: add points to last segment if curve is closed - if obj.Closed and (uNewPoint > uPoints[-1]): - pts.append(edit_command.localize_vector(obj, point)) - obj.Points = pts + pts = obj.Points + if (obj.Closed == True): + curve = obj.Shape.Edges[0].Curve + else: + curve = obj.Shape.Curve + uNewPoint = curve.parameter(pt) + uPoints = [] + for p in obj.Points: + uPoints.append(curve.parameter(p)) + for i in range(len(uPoints) - 1): + if ( uNewPoint > uPoints[i] ) and ( uNewPoint < uPoints[i+1] ): + pts.insert(i + 1, edit_command.localize_vector(obj, pt)) + break + # DNC: fix: add points to last segment if curve is closed + if obj.Closed and (uNewPoint > uPoints[-1]): + pts.append(edit_command.localize_vector(obj, pt)) + obj.Points = pts obj.recompute() - return class DraftRectangleGuiTools(GuiTools): @@ -783,29 +766,10 @@ class DraftBezCurveGuiTools(GuiTools): def add_point(self, edit_command, obj, pos): """Add point to obj and reset trackers. """ - # self.setSelectState(obj, True) - selobjs = Gui.ActiveDocument.ActiveView.getObjectsInfo((pos[0],pos[1])) - if not selobjs: + info, pt = edit_command.get_specific_object_info(obj,pos) + if not info or (pt is None): return - for info in selobjs: - if not info: - return - for o in edit_command.edited_objects: - if o.Name != info["Object"]: - continue - obj = o - break - if utils.get_type(obj) == "BezCurve": #to fix double vertex created - # pt = self.point - if "x" in info:# prefer "real" 3D location over working-plane-driven one if possible - pt = App.Vector(info["x"], info["y"], info["z"]) - else: - continue - self.addPointToCurve(pt, obj, info) - obj.recompute() - return - - def addPointToCurve(self, point, obj, info=None): + import Part pts = obj.Points @@ -814,7 +778,7 @@ class DraftBezCurveGuiTools(GuiTools): edgeindex = int(info['Component'].lstrip('Edge')) - 1 wire = obj.Shape.Wires[0] bz = wire.Edges[edgeindex].Curve - param = bz.parameter(point) + param = bz.parameter(pt) seg1 = wire.Edges[edgeindex].copy().Curve seg2 = wire.Edges[edgeindex].copy().Curve seg1.segment(seg1.FirstParameter, param) @@ -839,4 +803,6 @@ class DraftBezCurveGuiTools(GuiTools): obj.Points = pts + obj.recompute() + ## @}