From 42dde995dba6c0d8583dedd0dc59b4c2cc1ecb41 Mon Sep 17 00:00:00 2001 From: Eric Price Date: Sat, 12 Oct 2024 19:47:15 +0200 Subject: [PATCH] Added new Feature to force-set Tolerance on a part --- src/Mod/Part/BOPTools/ToleranceFeatures.py | 210 +++++++++++++++++++++ src/Mod/Part/BOPTools/__init__.py | 2 + src/Mod/Part/CMakeLists.txt | 1 + src/Mod/Part/Gui/Workbench.cpp | 3 +- 4 files changed, 215 insertions(+), 1 deletion(-) create mode 100644 src/Mod/Part/BOPTools/ToleranceFeatures.py diff --git a/src/Mod/Part/BOPTools/ToleranceFeatures.py b/src/Mod/Part/BOPTools/ToleranceFeatures.py new file mode 100644 index 0000000000..e55e66efa7 --- /dev/null +++ b/src/Mod/Part/BOPTools/ToleranceFeatures.py @@ -0,0 +1,210 @@ +#/*************************************************************************** +# * Copyright (c) 2024 Eric Price (CorvusCorax) * +# * * +# * * +# * This file is part of FreeCAD. * +# * * +# * FreeCAD is free software: you can redistribute it and/or modify it * +# * under the terms of the GNU Lesser General Public License as * +# * published by the Free Software Foundation, either version 2.1 of the * +# * License, or (at your option) any later version. * +# * * +# * FreeCAD is distributed in the hope that it will be useful, but * +# * WITHOUT ANY WARRANTY; without even the implied warranty of * +# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * +# * Lesser General Public License for more details. * +# * * +# * You should have received a copy of the GNU Lesser General Public * +# * License along with FreeCAD. If not, see * +# * . * +# ***************************************************************************/ + +__title__ = "BOPTools.ToleranceFeatures module" +__author__ = "CorvusCorax" +__url__ = "https://www.freecad.org" +__doc__ = "Implementation of document objects (features) to adjust/manipulate tolerances." + +import FreeCAD +import Part + +if FreeCAD.GuiUp: + import FreeCADGui + from PySide import QtCore, QtGui +# -------------------------- common stuff ------------------------------------- + +# -------------------------- translation-related code ------------------------- + + try: + _fromUtf8 = QtCore.QString.fromUtf8 + except Exception: + def _fromUtf8(s): + return s + translate = FreeCAD.Qt.translate +# --------------------------/translation-related code ------------------------- + + +def getParamRefine(): + return FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Part/Boolean").GetBool("RefineModel") + + +def cmdCreateToleranceSetFeature(name, minTolerance=1e-7): + """cmdCreateToleranceSetFeature(name, minTolerance): generalized implementation of GUI commands.""" + sel = FreeCADGui.Selection.getSelectionEx() + + FreeCAD.ActiveDocument.openTransaction("Create ToleranceSet") + FreeCADGui.addModule("BOPTools.ToleranceFeatures") + FreeCADGui.doCommand("j = BOPTools.ToleranceFeatures.makeToleranceSet(name='{name}')".format(name=name)) + FreeCADGui.doCommand("j.minTolerance = {minTolerance}".format(minTolerance=minTolerance)) + FreeCADGui.doCommand("j.Objects = {sel}".format( + sel= "[" + ", ".join(["App.ActiveDocument."+so.Object.Name for so in sel]) + "]" + )) + + try: + FreeCADGui.doCommand("j.Proxy.execute(j)") + FreeCADGui.doCommand("j.purgeTouched()") + except Exception as err: + mb = QtGui.QMessageBox() + mb.setIcon(mb.Icon.Warning) + error_text1 = translate("Part_ToleranceFeatures", "Computing the result failed with an error:") + error_text2 = translate("Part_ToleranceFeatures", "Click 'Continue' to create the feature anyway, or 'Abort' to cancel.") + mb.setText(error_text1 + "\n\n" + str(err) + "\n\n" + error_text2) + mb.setWindowTitle(translate("Part_ToleranceFeatures","Bad selection", None)) + btnAbort = mb.addButton(QtGui.QMessageBox.StandardButton.Abort) + btnOK = mb.addButton(translate("Part_ToleranceFeatures","Continue",None), + QtGui.QMessageBox.ButtonRole.ActionRole) + mb.setDefaultButton(btnOK) + mb.exec_() + + if mb.clickedButton() is btnAbort: + FreeCAD.ActiveDocument.abortTransaction() + return + + FreeCADGui.doCommand("for obj in j.ViewObject.Proxy.claimChildren():\n" + " obj.ViewObject.hide()") + + FreeCAD.ActiveDocument.commitTransaction() + + +def getIconPath(icon_dot_svg): + return icon_dot_svg + +# -------------------------- /common stuff ------------------------------------ + +# -------------------------- Connect ------------------------------------------ + +def makeToleranceSet(name): + '''makeToleranceSet(name): makes an ToleranceSet object.''' + obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython",name) + FeatureToleranceSet(obj) + if FreeCAD.GuiUp: + ViewProviderToleranceSet(obj.ViewObject) + return obj + + +class FeatureToleranceSet: + """The PartToleranceSetFeature object.""" + + def __init__(self,obj): + obj.addProperty("App::PropertyLinkList","Objects","ToleranceSet","Objects to have tolerance adjusted.") + obj.addProperty("App::PropertyBool","Refine","ToleranceSet", + "True = refine resulting shape. False = output as is.") + obj.addProperty("App::PropertyFloat","minTolerance","ToleranceSet", "1e-7") + obj.Refine = getParamRefine() + + obj.Proxy = self + self.Type = "FeatureToleranceSet" + + def execute(self,selfobj): + shapes = [] + for obj in selfobj.Objects: + sh = obj.Shape.copy(True,False) + sh.limitTolerance(selfobj.minTolerance) + if selfobj.Refine: + sh = sh.removeSplitter() + shapes.append(sh) + + if len(shapes)>1: + rst = Part.makeCompound(shapes) + else: + rst = shapes[0] + selfobj.Shape = rst + + +class ViewProviderToleranceSet: + """A View Provider for the Part ToleranceSet feature.""" + + def __init__(self,vobj): + vobj.Proxy = self + + def getIcon(self): + return getIconPath("preferences-part_design.svg") + + def attach(self, vobj): + self.ViewObject = vobj + self.Object = vobj.Object + + def dumps(self): + return None + + def loads(self,state): + return None + + def claimChildren(self): + return self.Object.Objects + + def onDelete(self, feature, subelements): + try: + for obj in self.claimChildren(): + obj.ViewObject.show() + except Exception as err: + FreeCAD.Console.PrintError("Error in onDelete: " + str(err)) + return True + + def canDragObjects(self): + return True + def canDropObjects(self): + return True + def canDragObject(self, dragged_object): + return True + def canDropObject(self, incoming_object): + return hasattr(incoming_object, 'Shape') + def dragObject(self, selfvp, dragged_object): + objs = self.Object.Objects + objs.remove(dragged_object) + self.Object.Objects = objs + def dropObject(self, selfvp, incoming_object): + self.Object.Objects = self.Object.Objects + [incoming_object] + + +class CommandToleranceSet: + """Command to create ToleranceSet feature.""" + + def GetResources(self): + return {'Pixmap': getIconPath("preferences-part_design.svg"), + 'MenuText': QtCore.QT_TRANSLATE_NOOP("Part_ToleranceSet","Set Tolerance"), + 'Accel': "", + 'ToolTip': QtCore.QT_TRANSLATE_NOOP("Part_ToleranceSet", + "Set Tolerance for selected objects.")} + + def Activated(self): + if len(FreeCADGui.Selection.getSelectionEx()) >= 1: + cmdCreateToleranceSetFeature(name="Tolerance") + else: + mb = QtGui.QMessageBox() + mb.setIcon(mb.Icon.Warning) + mb.setText(translate("Part_ToleranceSet", + "Select at least one object or compounds", None)) + mb.setWindowTitle(translate("Part_ToleranceSet","Bad selection", None)) + mb.exec_() + + def IsActive(self): + if FreeCAD.ActiveDocument: + return True + else: + return False + +# -------------------------- /Connect ----------------------------------------- + + +def addCommands(): + FreeCADGui.addCommand('Part_ToleranceSet', CommandToleranceSet()) diff --git a/src/Mod/Part/BOPTools/__init__.py b/src/Mod/Part/BOPTools/__init__.py index c0df731f8f..06129e6638 100644 --- a/src/Mod/Part/BOPTools/__init__.py +++ b/src/Mod/Part/BOPTools/__init__.py @@ -50,6 +50,7 @@ def importAll(): from . import Utils from . import SplitAPI from . import SplitFeatures + from . import ToleranceFeatures def reloadAll(): "reloadAll(): reloads all modules of BOPTools package. Useful for debugging." @@ -63,4 +64,5 @@ def addCommands(): "addCommands(): add all GUI commands of BOPTools package to FreeCAD command manager." JoinFeatures.addCommands() SplitFeatures.addCommands() + ToleranceFeatures.addCommands() diff --git a/src/Mod/Part/CMakeLists.txt b/src/Mod/Part/CMakeLists.txt index 17cfd699a8..6e916a0380 100644 --- a/src/Mod/Part/CMakeLists.txt +++ b/src/Mod/Part/CMakeLists.txt @@ -51,6 +51,7 @@ set(BOPTools_Scripts BOPTools/ShapeMerge.py BOPTools/SplitAPI.py BOPTools/SplitFeatures.py + BOPTools/ToleranceFeatures.py BOPTools/Utils.py ) diff --git a/src/Mod/Part/Gui/Workbench.cpp b/src/Mod/Part/Gui/Workbench.cpp index a43de94bd5..588ce8824e 100644 --- a/src/Mod/Part/Gui/Workbench.cpp +++ b/src/Mod/Part/Gui/Workbench.cpp @@ -115,7 +115,8 @@ Gui::MenuItem* Workbench::setupMenuBar() const compound->setCommand("Compound"); *compound << "Part_Compound" << "Part_ExplodeCompound" - << "Part_CompoundFilter"; + << "Part_CompoundFilter" + << "Part_ToleranceSet"; Gui::MenuItem* part = new Gui::MenuItem; root->insertItem(item, part);