| | |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | __title__ = "BOPTools.SplitFeatures module" |
| | __author__ = "DeepSOIC" |
| | __url__ = "https://www.freecad.org" |
| | __doc__ = "Shape splitting document objects (features)." |
| |
|
| | from . import SplitAPI |
| | import FreeCAD |
| | import Part |
| |
|
| | if FreeCAD.GuiUp: |
| | import FreeCADGui |
| | from PySide import QtCore, QtGui |
| |
|
| | |
| | |
| | |
| | try: |
| | _fromUtf8 = QtCore.QString.fromUtf8 |
| | except Exception: |
| |
|
| | def _fromUtf8(s): |
| | return s |
| |
|
| | translate = FreeCAD.Qt.translate |
| | |
| |
|
| |
|
| | def getIconPath(icon_dot_svg): |
| | return icon_dot_svg |
| |
|
| |
|
| | |
| |
|
| | |
| |
|
| |
|
| | def makeBooleanFragments(name): |
| | """makeBooleanFragments(name): makes an BooleanFragments object.""" |
| | obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", name) |
| | FeatureBooleanFragments(obj) |
| | if FreeCAD.GuiUp: |
| | ViewProviderBooleanFragments(obj.ViewObject) |
| | return obj |
| |
|
| |
|
| | class FeatureBooleanFragments: |
| | """The BooleanFragments feature object.""" |
| |
|
| | def __init__(self, obj): |
| | obj.addProperty( |
| | "App::PropertyLinkList", |
| | "Objects", |
| | "BooleanFragments", |
| | "Object to compute intersections between.", |
| | locked=True, |
| | ) |
| | obj.addProperty( |
| | "App::PropertyEnumeration", |
| | "Mode", |
| | "BooleanFragments", |
| | "- Standard: wires, shells, compsolids remain in one piece.\n" |
| | "- Split: wires, shells, compsolids are split.\n" |
| | "- CompSolid: make compsolid from solid fragments.", |
| | locked=True, |
| | ) |
| | obj.Mode = ["Standard", "Split", "CompSolid"] |
| | obj.addProperty( |
| | "App::PropertyLength", |
| | "Tolerance", |
| | "BooleanFragments", |
| | "Tolerance when intersecting (fuzzy value). " |
| | "In addition to tolerances of the shapes.", |
| | locked=True, |
| | ) |
| |
|
| | obj.Proxy = self |
| | self.Type = "FeatureBooleanFragments" |
| |
|
| | def execute(self, selfobj): |
| | shapes = [obj.Shape for obj in selfobj.Objects] |
| | if len(shapes) == 1 and shapes[0].ShapeType == "Compound": |
| | shapes = shapes[0].childShapes() |
| | if len(shapes) < 2: |
| | raise ValueError( |
| | "At least two shapes are needed for computing boolean fragments. Got only {num}.".format( |
| | num=len(shapes) |
| | ) |
| | ) |
| | selfobj.Shape = SplitAPI.booleanFragments(shapes, selfobj.Mode, selfobj.Tolerance) |
| |
|
| |
|
| | class ViewProviderBooleanFragments: |
| | """A View Provider for the Part BooleanFragments feature.""" |
| |
|
| | def __init__(self, vobj): |
| | vobj.Proxy = self |
| |
|
| | def getIcon(self): |
| | return ":/icons/booleans/Part_BooleanFragments.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] |
| |
|
| |
|
| | def cmdCreateBooleanFragmentsFeature(name, mode): |
| | """cmdCreateBooleanFragmentsFeature(name, mode): implementation of GUI command to create |
| | BooleanFragments feature (GFA). Mode can be "Standard", "Split", or "CompSolid".""" |
| | sel = FreeCADGui.Selection.getSelectionEx() |
| | FreeCAD.ActiveDocument.openTransaction("Create Boolean Fragments") |
| | FreeCADGui.addModule("BOPTools.SplitFeatures") |
| | FreeCADGui.doCommand( |
| | "j = BOPTools.SplitFeatures.makeBooleanFragments(name='{name}')".format(name=name) |
| | ) |
| | FreeCADGui.doCommand( |
| | "j.Objects = {sel}".format( |
| | sel="[" + ", ".join(["App.ActiveDocument." + so.Object.Name for so in sel]) + "]" |
| | ) |
| | ) |
| | FreeCADGui.doCommand("j.Mode = {mode}".format(mode=repr(mode))) |
| |
|
| | 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_SplitFeatures", "Computing the result failed with an error:") |
| | error_text2 = translate( |
| | "Part_SplitFeatures", |
| | "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_SplitFeatures", "Bad Selection", None)) |
| | btnAbort = mb.addButton(QtGui.QMessageBox.StandardButton.Abort) |
| | btnOK = mb.addButton( |
| | translate("Part_SplitFeatures", "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() |
| |
|
| |
|
| | class CommandBooleanFragments: |
| | """Command to create BooleanFragments feature.""" |
| |
|
| | def GetResources(self): |
| | return { |
| | "Pixmap": getIconPath("Part_BooleanFragments.svg"), |
| | "MenuText": QtCore.QT_TRANSLATE_NOOP("Part_BooleanFragments", "Boolean Fragments"), |
| | "Accel": "", |
| | "ToolTip": QtCore.QT_TRANSLATE_NOOP( |
| | "Part_BooleanFragments", |
| | "Creates a boolean union which is sliced at the intersections of the selected shapes", |
| | ), |
| | } |
| |
|
| | def Activated(self): |
| | if len(FreeCADGui.Selection.getSelectionEx()) >= 1: |
| | cmdCreateBooleanFragmentsFeature(name="BooleanFragments", mode="Standard") |
| | else: |
| | mb = QtGui.QMessageBox() |
| | mb.setIcon(mb.Icon.Warning) |
| | mb.setText( |
| | translate( |
| | "Part_SplitFeatures", |
| | "Select at least two objects, or one or more compounds. " |
| | "If only one compound is selected, the compounded shapes will be intersected between each other " |
| | "(otherwise, compounds with self-intersections are invalid).", |
| | None, |
| | ) |
| | ) |
| | mb.setWindowTitle(translate("Part_SplitFeatures", "Bad Selection", None)) |
| | mb.exec_() |
| |
|
| | def IsActive(self): |
| | if FreeCAD.ActiveDocument: |
| | return True |
| | else: |
| | return False |
| |
|
| |
|
| | |
| |
|
| | |
| |
|
| |
|
| | def makeSlice(name): |
| | """makeSlice(name): makes an Slice object.""" |
| | obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", name) |
| | FeatureSlice(obj) |
| | if FreeCAD.GuiUp: |
| | ViewProviderSlice(obj.ViewObject) |
| | return obj |
| |
|
| |
|
| | class FeatureSlice: |
| | """The Slice feature object.""" |
| |
|
| | def __init__(self, obj): |
| | obj.addProperty("App::PropertyLink", "Base", "Slice", "Object to be sliced.", locked=True) |
| | obj.addProperty( |
| | "App::PropertyLinkList", "Tools", "Slice", "Objects that slice.", locked=True |
| | ) |
| | obj.addProperty( |
| | "App::PropertyEnumeration", |
| | "Mode", |
| | "Slice", |
| | "- Standard: wires, shells, compsolids remain in one piece.\n" |
| | "- Split: wires, shells, compsolids are split.\n" |
| | "- CompSolid: make compsolid from solid fragments.", |
| | locked=True, |
| | ) |
| | obj.Mode = ["Standard", "Split", "CompSolid"] |
| | obj.addProperty( |
| | "App::PropertyLength", |
| | "Tolerance", |
| | "Slice", |
| | "Tolerance when intersecting (fuzzy value). " |
| | "In addition to tolerances of the shapes.", |
| | locked=True, |
| | ) |
| |
|
| | obj.Proxy = self |
| | self.Type = "FeatureSlice" |
| |
|
| | def execute(self, selfobj): |
| | if len(selfobj.Tools) < 1: |
| | raise ValueError("No slicing objects supplied!") |
| |
|
| | |
| | def get_shape(obj): |
| | """get shape from a part object or compound from a group.""" |
| | if hasattr(obj, "Shape"): |
| | return obj.Shape |
| | elif hasattr(obj, "Group"): |
| | shapes = [] |
| | for child in obj.Group: |
| | if hasattr(child, "Shape"): |
| | shapes.append(child.Shape) |
| | if shapes: |
| | return Part.makeCompound(shapes) |
| | return None |
| |
|
| | |
| | base_shape = get_shape(selfobj.Base) |
| | if base_shape is None or base_shape.isNull(): |
| | raise ValueError("Base object has no valid shape!") |
| |
|
| | |
| | tool_shapes = [] |
| | for tool in selfobj.Tools: |
| | shape = get_shape(tool) |
| | if shape is not None and not shape.isNull(): |
| | tool_shapes.append(shape) |
| |
|
| | if len(tool_shapes) < 1: |
| | raise ValueError("No valid tool shapes available") |
| |
|
| | selfobj.Shape = SplitAPI.slice( |
| | base_shape, |
| | tool_shapes, |
| | selfobj.Mode, |
| | selfobj.Tolerance, |
| | ) |
| |
|
| |
|
| | class ViewProviderSlice: |
| | """A View Provider for the Part Slice feature.""" |
| |
|
| | def __init__(self, vobj): |
| | vobj.Proxy = self |
| |
|
| | def getIcon(self): |
| | return ":/icons/booleans/Part_Slice.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.Base] + self.Object.Tools |
| |
|
| | 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 cmdCreateSliceFeature(name, mode, transaction=True): |
| | """cmdCreateSliceFeature(name, mode): implementation of GUI command to create |
| | Slice feature. Mode can be "Standard", "Split", or "CompSolid".""" |
| | sel = FreeCADGui.Selection.getSelectionEx() |
| | if transaction: |
| | FreeCAD.ActiveDocument.openTransaction("Create Slice") |
| | FreeCADGui.addModule("BOPTools.SplitFeatures") |
| | FreeCADGui.doCommand("f = BOPTools.SplitFeatures.makeSlice(name='{name}')".format(name=name)) |
| | FreeCADGui.doCommand( |
| | "f.Base = {sel}[0]\n" |
| | "f.Tools = {sel}[1:]".format( |
| | sel="[" + ", ".join(["App.ActiveDocument." + so.Object.Name for so in sel]) + "]" |
| | ) |
| | ) |
| | FreeCADGui.doCommand("f.Mode = {mode}".format(mode=repr(mode))) |
| |
|
| | try: |
| | FreeCADGui.doCommand("f.Proxy.execute(f)") |
| | FreeCADGui.doCommand("f.purgeTouched()") |
| | except Exception as err: |
| | mb = QtGui.QMessageBox() |
| | mb.setIcon(mb.Icon.Warning) |
| | error_text1 = translate("Part_SplitFeatures", "Computing the result failed with an error:") |
| | error_text2 = translate( |
| | "Part_SplitFeatures", |
| | "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_SplitFeatures", "Bad Selection", None)) |
| | btnAbort = mb.addButton(QtGui.QMessageBox.StandardButton.Abort) |
| | btnOK = mb.addButton( |
| | translate("Part_SplitFeatures", "Continue", None), |
| | QtGui.QMessageBox.ButtonRole.ActionRole, |
| | ) |
| | mb.setDefaultButton(btnOK) |
| |
|
| | mb.exec_() |
| |
|
| | if mb.clickedButton() is btnAbort: |
| | if transaction: |
| | FreeCAD.ActiveDocument.abortTransaction() |
| | return False |
| |
|
| | FreeCADGui.doCommand( |
| | "for obj in f.ViewObject.Proxy.claimChildren():\n" " obj.ViewObject.hide()" |
| | ) |
| |
|
| | if transaction: |
| | FreeCAD.ActiveDocument.commitTransaction() |
| | return True |
| |
|
| |
|
| | def cmdSliceApart(): |
| | FreeCAD.ActiveDocument.openTransaction("Slice apart") |
| | made = cmdCreateSliceFeature(name="Slice", mode="Split", transaction=False) |
| |
|
| | if made: |
| | FreeCADGui.addModule("CompoundTools.Explode") |
| | FreeCADGui.doCommand("CompoundTools.Explode.explodeCompound(f)") |
| | FreeCADGui.doCommand("f.ViewObject.hide()") |
| | FreeCAD.ActiveDocument.commitTransaction() |
| | FreeCADGui.doCommand("App.ActiveDocument.recompute()") |
| | else: |
| | FreeCAD.ActiveDocument.abortTransaction() |
| |
|
| |
|
| | class CommandSlice: |
| | """Command to create Slice feature.""" |
| |
|
| | def GetResources(self): |
| | return { |
| | "Pixmap": getIconPath("Part_Slice.svg"), |
| | "MenuText": QtCore.QT_TRANSLATE_NOOP("Part_Slice", "Slice to Compound"), |
| | "Accel": "", |
| | "ToolTip": QtCore.QT_TRANSLATE_NOOP( |
| | "Part_Slice", |
| | "Slices the selected object by using other objects as cutting tools and storing the results in one compound", |
| | ), |
| | } |
| |
|
| | def Activated(self): |
| | if len(FreeCADGui.Selection.getSelectionEx()) > 1: |
| | cmdCreateSliceFeature(name="Slice", mode="Split") |
| | else: |
| | mb = QtGui.QMessageBox() |
| | mb.setIcon(mb.Icon.Warning) |
| | mb.setText( |
| | translate( |
| | "Part_SplitFeatures", |
| | "Select at least two objects. " |
| | "The first one is the object to be sliced; " |
| | "the rest are objects to slice with.", |
| | None, |
| | ) |
| | ) |
| | mb.setWindowTitle(translate("Part_SplitFeatures", "Bad Selection", None)) |
| | mb.exec_() |
| |
|
| | def IsActive(self): |
| | if FreeCAD.ActiveDocument: |
| | return True |
| | else: |
| | return False |
| |
|
| |
|
| | class CommandSliceApart: |
| | """Command to create exploded Slice feature.""" |
| |
|
| | def GetResources(self): |
| | return { |
| | "Pixmap": getIconPath("Part_SliceApart.svg"), |
| | "MenuText": QtCore.QT_TRANSLATE_NOOP("Part_SliceApart", "Slice Apart"), |
| | "Accel": "", |
| | "ToolTip": QtCore.QT_TRANSLATE_NOOP( |
| | "Part_SliceApart", |
| | "Slices the selected object by other objects, and splits it apart, creating a compound filter for each slide", |
| | ), |
| | } |
| |
|
| | def Activated(self): |
| | if len(FreeCADGui.Selection.getSelectionEx()) > 1: |
| | cmdSliceApart() |
| | else: |
| | mb = QtGui.QMessageBox() |
| | mb.setIcon(mb.Icon.Warning) |
| | mb.setText( |
| | translate( |
| | "Part_SplitFeatures", |
| | "Select at least two objects. " |
| | "The first one is the object to be sliced; " |
| | "the rest are objects to slice with.", |
| | None, |
| | ) |
| | ) |
| | mb.setWindowTitle(translate("Part_SplitFeatures", "Bad Selection", None)) |
| | mb.exec_() |
| |
|
| | def IsActive(self): |
| | if FreeCAD.ActiveDocument: |
| | return True |
| | else: |
| | return False |
| |
|
| |
|
| | |
| |
|
| | |
| |
|
| |
|
| | def makeXOR(name): |
| | """makeXOR(name): makes an XOR object.""" |
| | obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", name) |
| | FeatureXOR(obj) |
| | if FreeCAD.GuiUp: |
| | ViewProviderXOR(obj.ViewObject) |
| | return obj |
| |
|
| |
|
| | class FeatureXOR: |
| | """The XOR feature object.""" |
| |
|
| | def __init__(self, obj): |
| | obj.addProperty( |
| | "App::PropertyLinkList", |
| | "Objects", |
| | "XOR", |
| | "Object to compute intersections between.", |
| | locked=True, |
| | ) |
| | obj.addProperty( |
| | "App::PropertyLength", |
| | "Tolerance", |
| | "XOR", |
| | "Tolerance when intersecting (fuzzy value). " |
| | "In addition to tolerances of the shapes.", |
| | locked=True, |
| | ) |
| |
|
| | obj.Proxy = self |
| | self.Type = "FeatureXOR" |
| |
|
| | def execute(self, selfobj): |
| | shapes = [obj.Shape for obj in selfobj.Objects] |
| | if len(shapes) == 1 and shapes[0].ShapeType == "Compound": |
| | shapes = shapes[0].childShapes() |
| | if len(shapes) < 2: |
| | raise ValueError( |
| | "At least two shapes are needed for computing XOR. Got only {num}.".format( |
| | num=len(shapes) |
| | ) |
| | ) |
| | selfobj.Shape = SplitAPI.xor(shapes, selfobj.Tolerance) |
| |
|
| |
|
| | class ViewProviderXOR: |
| | """A View Provider for the Part XOR feature.""" |
| |
|
| | def __init__(self, vobj): |
| | vobj.Proxy = self |
| |
|
| | def getIcon(self): |
| | return ":/icons/booleans/Part_XOR.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] |
| |
|
| |
|
| | def cmdCreateXORFeature(name): |
| | """cmdCreateXORFeature(name): implementation of GUI command to create |
| | XOR feature (GFA). Mode can be "Standard", "Split", or "CompSolid".""" |
| | sel = FreeCADGui.Selection.getSelectionEx() |
| | FreeCAD.ActiveDocument.openTransaction("Create Boolean XOR") |
| | FreeCADGui.addModule("BOPTools.SplitFeatures") |
| | FreeCADGui.doCommand("j = BOPTools.SplitFeatures.makeXOR(name='{name}')".format(name=name)) |
| | 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_SplitFeatures", "Computing the result failed with an error:") |
| | error_text2 = translate( |
| | "Part_SplitFeatures", |
| | "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_SplitFeatures", "Bad Selection", None)) |
| | btnAbort = mb.addButton(QtGui.QMessageBox.StandardButton.Abort) |
| | btnOK = mb.addButton( |
| | translate("Part_SplitFeatures", "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() |
| |
|
| |
|
| | class CommandXOR: |
| | """Command to create XOR feature.""" |
| |
|
| | def GetResources(self): |
| | return { |
| | "Pixmap": getIconPath("Part_XOR.svg"), |
| | "MenuText": QtCore.QT_TRANSLATE_NOOP("Part_XOR", "Boolean XOR"), |
| | "Accel": "", |
| | "ToolTip": QtCore.QT_TRANSLATE_NOOP( |
| | "Part_XOR", |
| | "Performs an 'exclusive OR' boolean operation with two or more selected objects,\n" |
| | "or with the shapes inside a compound.\n" |
| | "Overlapping volumes of the shapes will be removed.", |
| | ), |
| | } |
| |
|
| | def Activated(self): |
| | if len(FreeCADGui.Selection.getSelectionEx()) >= 1: |
| | cmdCreateXORFeature(name="XOR") |
| | else: |
| | mb = QtGui.QMessageBox() |
| | mb.setIcon(mb.Icon.Warning) |
| | mb.setText( |
| | translate( |
| | "Part_SplitFeatures", |
| | "Select at least two objects, or one or more compounds. " |
| | "If only one compound is selected, the compounded shapes will be intersected between each other " |
| | "(otherwise, compounds with self-intersections are invalid).", |
| | None, |
| | ) |
| | ) |
| | mb.setWindowTitle(translate("Part_SplitFeatures", "Bad Selection", None)) |
| | mb.exec_() |
| |
|
| | def IsActive(self): |
| | if FreeCAD.ActiveDocument: |
| | return True |
| | else: |
| | return False |
| |
|
| |
|
| | |
| |
|
| |
|
| | def addCommands(): |
| | FreeCADGui.addCommand("Part_BooleanFragments", CommandBooleanFragments()) |
| | FreeCADGui.addCommand("Part_Slice", CommandSlice()) |
| | FreeCADGui.addCommand("Part_SliceApart", CommandSliceApart()) |
| | FreeCADGui.addCommand("Part_XOR", CommandXOR()) |
| |
|