| |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| __title__ = "FreeCAD Panel" |
| __author__ = "Yorik van Havre" |
| __url__ = "https://www.freecad.org" |
|
|
| |
| |
| |
| |
| |
| |
| |
|
|
| import math |
|
|
| import FreeCAD |
| import ArchCommands |
| import ArchComponent |
| import Draft |
| import DraftVecUtils |
| import Part |
|
|
| from FreeCAD import Vector |
| from draftutils import params |
|
|
| if FreeCAD.GuiUp: |
| from PySide import QtCore, QtGui |
| from PySide.QtCore import QT_TRANSLATE_NOOP |
| import FreeCADGui |
| from draftutils.translate import translate |
| else: |
| |
| def translate(ctxt, txt): |
| return txt |
|
|
| def QT_TRANSLATE_NOOP(ctxt, txt): |
| return txt |
|
|
| |
|
|
|
|
| class _Panel(ArchComponent.Component): |
| "The Panel object" |
|
|
| def __init__(self, obj): |
|
|
| ArchComponent.Component.__init__(self, obj) |
| self.Type = "Panel" |
| self.setProperties(obj) |
| obj.IfcType = "Plate" |
|
|
| def setProperties(self, obj): |
|
|
| pl = obj.PropertiesList |
| if not "Length" in pl: |
| obj.addProperty( |
| "App::PropertyLength", |
| "Length", |
| "Panel", |
| QT_TRANSLATE_NOOP( |
| "App::Property", "The length of this element, if not based on a profile" |
| ), |
| locked=True, |
| ) |
| if not "Width" in pl: |
| obj.addProperty( |
| "App::PropertyLength", |
| "Width", |
| "Panel", |
| QT_TRANSLATE_NOOP( |
| "App::Property", "The width of this element, if not based on a profile" |
| ), |
| locked=True, |
| ) |
| if not "Thickness" in pl: |
| obj.addProperty( |
| "App::PropertyLength", |
| "Thickness", |
| "Panel", |
| QT_TRANSLATE_NOOP( |
| "App::Property", "The thickness or extrusion depth of this element" |
| ), |
| locked=True, |
| ) |
| if not "Sheets" in pl: |
| obj.addProperty( |
| "App::PropertyInteger", |
| "Sheets", |
| "Panel", |
| QT_TRANSLATE_NOOP("App::Property", "The number of sheets to use"), |
| locked=True, |
| ) |
| obj.Sheets = 1 |
| if not "Offset" in pl: |
| obj.addProperty( |
| "App::PropertyDistance", |
| "Offset", |
| "Panel", |
| QT_TRANSLATE_NOOP( |
| "App::Property", "The offset between this panel and its baseline" |
| ), |
| locked=True, |
| ) |
| if not "WaveLength" in pl: |
| obj.addProperty( |
| "App::PropertyLength", |
| "WaveLength", |
| "Panel", |
| QT_TRANSLATE_NOOP("App::Property", "The length of waves for corrugated elements"), |
| locked=True, |
| ) |
| if not "WaveHeight" in pl: |
| obj.addProperty( |
| "App::PropertyLength", |
| "WaveHeight", |
| "Panel", |
| QT_TRANSLATE_NOOP("App::Property", "The height of waves for corrugated elements"), |
| locked=True, |
| ) |
| if not "WaveOffset" in pl: |
| obj.addProperty( |
| "App::PropertyDistance", |
| "WaveOffset", |
| "Panel", |
| QT_TRANSLATE_NOOP( |
| "App::Property", "The horizontal offset of waves for corrugated elements" |
| ), |
| locked=True, |
| ) |
| if not "WaveDirection" in pl: |
| obj.addProperty( |
| "App::PropertyAngle", |
| "WaveDirection", |
| "Panel", |
| QT_TRANSLATE_NOOP( |
| "App::Property", "The direction of waves for corrugated elements" |
| ), |
| locked=True, |
| ) |
| if not "WaveType" in pl: |
| obj.addProperty( |
| "App::PropertyEnumeration", |
| "WaveType", |
| "Panel", |
| QT_TRANSLATE_NOOP("App::Property", "The type of waves for corrugated elements"), |
| locked=True, |
| ) |
| obj.WaveType = ["Curved", "Trapezoidal", "Spikes"] |
| if not "WaveBottom" in pl: |
| obj.addProperty( |
| "App::PropertyBool", |
| "WaveBottom", |
| "Panel", |
| QT_TRANSLATE_NOOP( |
| "App::Property", "If the wave also affects the bottom side or not" |
| ), |
| locked=True, |
| ) |
| if not "Area" in pl: |
| obj.addProperty( |
| "App::PropertyArea", |
| "Area", |
| "Panel", |
| QT_TRANSLATE_NOOP("App::Property", "The area of this panel"), |
| locked=True, |
| ) |
| if not "FaceMaker" in pl: |
| obj.addProperty( |
| "App::PropertyEnumeration", |
| "FaceMaker", |
| "Panel", |
| QT_TRANSLATE_NOOP( |
| "App::Property", "The facemaker type to use to build the profile of this object" |
| ), |
| locked=True, |
| ) |
| obj.FaceMaker = ["None", "Simple", "Cheese", "Bullseye"] |
| if not "Normal" in pl: |
| obj.addProperty( |
| "App::PropertyVector", |
| "Normal", |
| "Panel", |
| QT_TRANSLATE_NOOP( |
| "App::Property", |
| "The normal extrusion direction of this object (keep (0,0,0) for automatic normal)", |
| ), |
| locked=True, |
| ) |
| obj.setEditorMode("VerticalArea", 2) |
| obj.setEditorMode("HorizontalArea", 2) |
|
|
| def onDocumentRestored(self, obj): |
|
|
| ArchComponent.Component.onDocumentRestored(self, obj) |
| self.setProperties(obj) |
|
|
| def loads(self, state): |
|
|
| self.Type = "Panel" |
|
|
| def execute(self, obj): |
| "creates the panel shape" |
|
|
| if self.clone(obj): |
| return |
|
|
| layers = [] |
| length = 0 |
| width = 0 |
| thickness = 0 |
|
|
| |
| if obj.Base: |
| if hasattr(obj.Base, "Shape"): |
| if obj.Base.Shape.isNull(): |
| return |
| elif obj.Base.isDerivedFrom("Mesh::Feature"): |
| if not obj.Base.Mesh.isSolid(): |
| return |
| else: |
| if obj.Length.Value: |
| length = obj.Length.Value |
| else: |
| return |
| if obj.Width.Value: |
| width = obj.Width.Value |
| else: |
| return |
| if obj.Thickness.Value: |
| thickness = obj.Thickness.Value |
| else: |
| if not obj.Base: |
| return |
| elif hasattr(obj.Base, "Shape"): |
| if not obj.Base.Shape.Solids: |
| return |
| if hasattr(obj, "Material"): |
| if obj.Material: |
| if hasattr(obj.Material, "Materials"): |
| varwidth = 0 |
| thicknesses = [t for t in obj.Material.Thicknesses if t >= 0] |
| restwidth = thickness - sum(thicknesses) |
| if restwidth > 0: |
| varwidth = [t for t in thicknesses if t == 0] |
| if varwidth: |
| varwidth = restwidth / len(varwidth) |
| for t in obj.Material.Thicknesses: |
| if t: |
| layers.append(t) |
| elif varwidth: |
| layers.append(varwidth) |
| |
| pl = obj.Placement |
| base = None |
| normal = None |
| if hasattr(obj, "Normal"): |
| if obj.Normal.Length > 0: |
| normal = Vector(obj.Normal) |
| normal.normalize() |
| normal.multiply(thickness) |
| baseprofile = None |
| if obj.Base: |
| base = obj.Base.Shape.copy() |
| if not base.Solids: |
| |
| if base.Faces: |
| baseprofile = base |
| if not normal: |
| normal = baseprofile.Faces[0].normalAt(0, 0).multiply(thickness) |
| if layers: |
| layeroffset = 0 |
| shps = [] |
| for l in layers: |
| if l >= 0: |
| n = Vector(normal).normalize().multiply(abs(l)) |
| b = base.extrude(n) |
| if layeroffset: |
| o = Vector(normal).normalize().multiply(layeroffset) |
| b.translate(o) |
| shps.append(b) |
| layeroffset += abs(l) |
| base = Part.makeCompound(shps) |
| else: |
| base = base.extrude(normal) |
| elif base.Wires: |
| fm = False |
| if hasattr(obj, "FaceMaker"): |
| if obj.FaceMaker != "None": |
| try: |
| baseprofile = Part.makeFace( |
| base.Wires, "Part::FaceMaker" + str(obj.FaceMaker) |
| ) |
| fm = True |
| except Exception: |
| FreeCAD.Console.PrintError( |
| translate("Arch", "Facemaker returned an error") + "\n" |
| ) |
| return |
| if not fm: |
| closed = True |
| for w in base.Wires: |
| if not w.isClosed(): |
| closed = False |
| if closed: |
| baseprofile = ArchCommands.makeFace(base.Wires) |
| if not normal: |
| normal = baseprofile.normalAt(0, 0).multiply(thickness) |
| if layers: |
| layeroffset = 0 |
| shps = [] |
| for l in layers: |
| if l >= 0: |
| n = Vector(normal).normalize().multiply(abs(l)) |
| b = baseprofile.extrude(n) |
| if layeroffset: |
| o = Vector(normal).normalize().multiply(layeroffset) |
| b.translate(o) |
| shps.append(b) |
| layeroffset += abs(l) |
| base = Part.makeCompound(shps) |
| else: |
| base = baseprofile.extrude(normal) |
| elif obj.Base.isDerivedFrom("Mesh::Feature"): |
| if obj.Base.Mesh.isSolid(): |
| if obj.Base.Mesh.countComponents() == 1: |
| sh = ArchCommands.getShapeFromMesh(obj.Base.Mesh) |
| if sh.isClosed() and sh.isValid() and sh.Solids: |
| base = sh |
| else: |
| if layers: |
| shps = [] |
| layeroffset = 0 |
| for l in layers: |
| if l >= 0: |
| if normal: |
| n = Vector(normal).normalize().multiply(l) |
| else: |
| n = Vector(0, 0, 1).multiply(abs(l)) |
| l2 = length / 2 or 0.5 |
| w2 = width / 2 or 0.5 |
| v1 = Vector(-l2, -w2, layeroffset) |
| v2 = Vector(l2, -w2, layeroffset) |
| v3 = Vector(l2, w2, layeroffset) |
| v4 = Vector(-l2, w2, layeroffset) |
| base = Part.makePolygon([v1, v2, v3, v4, v1]) |
| baseprofile = Part.Face(base) |
| base = baseprofile.extrude(n) |
| shps.append(base) |
| layeroffset += abs(l) |
| base = Part.makeCompound(shps) |
| else: |
| if not normal: |
| normal = Vector(0, 0, 1).multiply(thickness) |
| l2 = length / 2 or 0.5 |
| w2 = width / 2 or 0.5 |
| v1 = Vector(-l2, -w2, 0) |
| v2 = Vector(l2, -w2, 0) |
| v3 = Vector(l2, w2, 0) |
| v4 = Vector(-l2, w2, 0) |
| base = Part.makePolygon([v1, v2, v3, v4, v1]) |
| baseprofile = Part.Face(base) |
| base = baseprofile.extrude(normal) |
|
|
| if hasattr(obj, "Area"): |
| if baseprofile: |
| obj.Area = baseprofile.Area |
|
|
| if hasattr(obj, "WaveLength"): |
| if baseprofile and obj.WaveLength.Value and obj.WaveHeight.Value: |
| |
| bb = baseprofile.BoundBox |
| bb.enlarge(bb.DiagonalLength) |
| downsegment = None |
| if hasattr(obj, "WaveBottom"): |
| if not obj.WaveBottom: |
| if obj.WaveType == "Curved": |
| if obj.Thickness.Value > obj.WaveHeight.Value: |
| downsegment = obj.Thickness.Value |
| else: |
| downsegment = obj.WaveHeight.Value + obj.Thickness.Value |
| else: |
| downsegment = obj.Thickness.Value |
| p1 = Vector(0, 0, 0) |
| p5 = Vector(obj.WaveLength.Value * 2, 0, 0) |
| if obj.WaveType == "Curved": |
| p2 = Vector(obj.WaveLength.Value / 2, 0, obj.WaveHeight.Value) |
| p3 = Vector(obj.WaveLength.Value, 0, 0) |
| e1 = Part.Arc(p1, p2, p3).toShape() |
| p4 = Vector(obj.WaveLength.Value * 1.5, 0, -obj.WaveHeight.Value) |
| e2 = Part.Arc(p3, p4, p5).toShape() |
| upsegment = Part.Wire([e1, e2]) |
| if not downsegment: |
| if obj.Thickness.Value < e1.Curve.Radius: |
| c3 = e1.Curve.copy() |
| c3.Radius = e1.Curve.Radius - obj.Thickness.Value |
| e3 = Part.Arc(c3, e1.FirstParameter, e1.LastParameter).toShape() |
| c4 = e2.Curve.copy() |
| c4.Radius = e2.Curve.Radius + obj.Thickness.Value |
| e4 = Part.Arc(c4, e2.FirstParameter, e2.LastParameter).toShape() |
| downsegment = Part.Wire([e3, e4]) |
| else: |
| r = e2.Curve.Radius + obj.Thickness.Value |
| z = math.sqrt(r**2 - obj.WaveLength.Value**2) |
| p6 = e2.Curve.Center.add(Vector(-obj.WaveLength, 0, -z)) |
| p7 = e2.Curve.Center.add(Vector(0, 0, -r)) |
| p8 = e2.Curve.Center.add(Vector(obj.WaveLength, 0, -z)) |
| downsegment = Part.Arc(p6, p7, p8).toShape() |
|
|
| elif obj.WaveType == "Trapezoidal": |
| p2 = Vector(obj.WaveLength.Value / 4, 0, obj.WaveHeight.Value) |
| p3 = Vector(obj.WaveLength.Value, 0, obj.WaveHeight.Value) |
| p4 = Vector(obj.WaveLength.Value * 1.25, 0, 0) |
| upsegment = Part.makePolygon([p1, p2, p3, p4, p5]) |
| if not downsegment: |
| a = ((p1.sub(p2)).getAngle(p3.sub(p2))) / 2 |
| tx = obj.Thickness.Value / math.tan(a) |
| d1 = Vector(tx, 0, -obj.Thickness.Value) |
| d2 = Vector(-tx, 0, -obj.Thickness.Value) |
| p6 = p1.add(d1) |
| if tx >= p3.sub(p2).Length / 2: |
| d3 = p2.sub(p1) |
| d3.normalize() |
| d3.multiply((0.625 * obj.WaveLength.Value) / d3.x) |
| d4 = Vector(d3.x, 0, -d3.z) |
| p7 = p6.add(d3) |
| p8 = p7.add(d4) |
| p9 = p5.add(d1) |
| downsegment = Part.makePolygon([p6, p7, p8, p9]) |
| elif tx <= 0.625 * obj.WaveLength.Value: |
| p7 = p2.add(d1) |
| p8 = p3.add(d2) |
| p9 = p4.add(d2) |
| p10 = p5.add(d1) |
| downsegment = Part.makePolygon([p6, p7, p8, p9, p10]) |
| else: |
| downsegment = obj.Thickness.Value |
|
|
| else: |
| p2 = Vector(obj.WaveHeight.Value, 0, obj.WaveHeight.Value) |
| p3 = Vector(obj.WaveHeight.Value * 2, 0, 0) |
| upsegment = Part.makePolygon([p1, p2, p3, p5]) |
| if not downsegment: |
| downsegment = obj.Thickness.Value |
|
|
| upsegment.translate(Vector(bb.getPoint(0).x, bb.getPoint(0).y, bb.Center.z)) |
| if isinstance(downsegment, Part.Shape): |
| downsegment.translate(Vector(bb.getPoint(0).x, bb.getPoint(0).y, bb.Center.z)) |
| if hasattr(obj, "WaveOffset"): |
| if obj.WaveOffset.Value: |
| upsegment.translate(Vector(obj.WaveOffset.Value, 0, 0)) |
| if isinstance(downsegment, Part.Shape): |
| downsegment.translate(Vector(obj.WaveOffset.Value, 0, 0)) |
|
|
| upedges = [] |
| downedges = [] |
| for i in range(int(bb.XLength / (obj.WaveLength.Value * 2))): |
| w1 = upsegment.copy() |
| w1.translate(Vector(obj.WaveLength.Value * 2 * i, 0, 0)) |
| upedges.extend(w1.Edges) |
| if isinstance(downsegment, Part.Shape): |
| w2 = downsegment.copy() |
| w2.translate(Vector(obj.WaveLength.Value * 2 * i, 0, 0)) |
| downedges.extend(w2.Edges) |
| upwire = Part.Wire(upedges) |
| FreeCAD.upwire = upwire |
| if isinstance(downsegment, Part.Shape): |
| downwire = Part.Wire(downedges) |
| FreeCAD.downwire = downwire |
| e1 = Part.LineSegment( |
| upwire.Vertexes[0].Point, downwire.Vertexes[0].Point |
| ).toShape() |
| e2 = Part.LineSegment( |
| upwire.Vertexes[-1].Point, downwire.Vertexes[-1].Point |
| ).toShape() |
| basewire = Part.Wire(upwire.Edges + [e1, e2] + downwire.Edges) |
| else: |
| z = obj.Thickness.Value |
| if obj.WaveType == "Curved": |
| z += obj.WaveHeight.Value |
| p1 = upwire.Vertexes[0].Point |
| p2 = p1.add(Vector(0, 0, -z)) |
| p3 = Vector(upwire.Vertexes[-1].Point.x, upwire.Vertexes[-1].Point.y, p2.z) |
| p4 = upwire.Vertexes[-1].Point |
| w = Part.makePolygon([p1, p2, p3, p4]) |
| basewire = Part.Wire(upwire.Edges + w.Edges) |
|
|
| FreeCAD.basewire = basewire |
| if not basewire.isClosed(): |
| print("Error closing base wire - check FreeCAD.basewire") |
| return |
|
|
| baseface = Part.Face(basewire) |
| base = baseface.extrude(Vector(0, bb.YLength, 0)) |
| if normal.cross(FreeCAD.Vector(0, 0, 1)).Length > 1e-6: |
| rot = FreeCAD.Rotation(FreeCAD.Vector(0, -1, 0), FreeCAD.Vector(*normal[:2], 0)) |
| base.rotate(bb.Center, rot.Axis, math.degrees(rot.Angle)) |
| rot = FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1), normal) |
| base.rotate(bb.Center, rot.Axis, math.degrees(rot.Angle)) |
| if obj.WaveDirection.Value: |
| base.rotate(bb.Center, normal, obj.WaveDirection.Value) |
| n1 = normal.negative().normalize().multiply(obj.WaveHeight.Value * 2) |
| self.vol = baseprofile.copy() |
| self.vol.translate(n1) |
| self.vol = self.vol.extrude(n1.negative().multiply(2)) |
| base = self.vol.common(base) |
| base = base.removeSplitter() |
| if not base: |
| FreeCAD.Console.PrintError( |
| translate("Arch", "Error computing shape of") + " " + obj.Label + "\n" |
| ) |
| return False |
|
|
| if base and (obj.Sheets > 1) and normal and thickness: |
| bases = [base] |
| for i in range(1, obj.Sheets): |
| n = FreeCAD.Vector(normal).normalize().multiply(i * thickness) |
| b = base.copy() |
| b.translate(n) |
| bases.append(b) |
| base = Part.makeCompound(bases) |
|
|
| if base and normal and hasattr(obj, "Offset"): |
| if obj.Offset.Value: |
| v = DraftVecUtils.scaleTo(normal, obj.Offset.Value) |
| base.translate(v) |
|
|
| |
| base = self.processSubShapes(obj, base, pl) |
|
|
| |
| if base: |
| if not base.isNull(): |
| if base.isValid() and base.Solids: |
| if len(base.Solids) == 1: |
| if base.Volume < 0: |
| base.reverse() |
| if base.Volume < 0: |
| FreeCAD.Console.PrintError( |
| translate("Arch", "Could not compute a shape") |
| ) |
| return |
| base = base.removeSplitter() |
| obj.Shape = base |
| if not pl.isNull(): |
| obj.Placement = pl |
|
|
|
|
| class _ViewProviderPanel(ArchComponent.ViewProviderComponent): |
| "A View Provider for the Panel object" |
|
|
| def __init__(self, vobj): |
|
|
| ArchComponent.ViewProviderComponent.__init__(self, vobj) |
| vobj.ShapeColor = ArchCommands.getDefaultColor("Panel") |
|
|
| def getIcon(self): |
|
|
| |
| if hasattr(self, "Object"): |
| if hasattr(self.Object, "CloneOf"): |
| if self.Object.CloneOf: |
| return ":/icons/Arch_Panel_Clone.svg" |
| return ":/icons/Arch_Panel_Tree.svg" |
|
|
| def updateData(self, obj, prop): |
|
|
| if prop in ["Placement", "Shape", "Material"]: |
| if hasattr(obj, "Material"): |
| if obj.Material: |
| if hasattr(obj.Material, "Materials"): |
| activematerials = [ |
| obj.Material.Materials[i] |
| for i in range(len(obj.Material.Materials)) |
| if obj.Material.Thicknesses[i] >= 0 |
| ] |
| if len(activematerials) == len(obj.Shape.Solids): |
| cols = [] |
| for i, mat in enumerate(activematerials): |
| c = obj.ViewObject.ShapeColor |
| c = (c[0], c[1], c[2], 1.0 - obj.ViewObject.Transparency / 100.0) |
| if "DiffuseColor" in mat.Material: |
| if "(" in mat.Material["DiffuseColor"]: |
| c = tuple( |
| [ |
| float(f) |
| for f in mat.Material["DiffuseColor"] |
| .strip("()") |
| .split(",") |
| ] |
| ) |
| if "Transparency" in mat.Material: |
| c = ( |
| c[0], |
| c[1], |
| c[2], |
| 1.0 - float(mat.Material["Transparency"]), |
| ) |
| cols.extend([c for j in range(len(obj.Shape.Solids[i].Faces))]) |
| if obj.ViewObject.DiffuseColor != cols: |
| obj.ViewObject.DiffuseColor = cols |
| ArchComponent.ViewProviderComponent.updateData(self, obj, prop) |
|
|
|
|
| class PanelCut(Draft.DraftObject): |
| "A flat, 2D view of an Arch Panel" |
|
|
| def __init__(self, obj): |
| Draft.DraftObject.__init__(self, obj) |
| obj.Proxy = self |
|
|
| |
| |
| ArchComponent.ViewProviderComponent.setProperties(self, obj) |
|
|
| self.setProperties(obj) |
|
|
| def setProperties(self, obj): |
|
|
| pl = obj.PropertiesList |
| if not "Source" in pl: |
| obj.addProperty( |
| "App::PropertyLink", |
| "Source", |
| "PanelCut", |
| QT_TRANSLATE_NOOP("App::Property", "The linked object"), |
| locked=True, |
| ) |
| if not "TagText" in pl: |
| obj.addProperty( |
| "App::PropertyString", |
| "TagText", |
| "PanelCut", |
| QT_TRANSLATE_NOOP( |
| "App::Property", |
| "The text to display. Can be %tag%, %label% or %description% to display the panel tag or label", |
| ), |
| locked=True, |
| ) |
| obj.TagText = "%tag%" |
| if not "TagSize" in pl: |
| obj.addProperty( |
| "App::PropertyLength", |
| "TagSize", |
| "PanelCut", |
| QT_TRANSLATE_NOOP("App::Property", "The size of the tag text"), |
| locked=True, |
| ) |
| obj.TagSize = 10 |
| if not "TagPosition" in pl: |
| obj.addProperty( |
| "App::PropertyVector", |
| "TagPosition", |
| "PanelCut", |
| QT_TRANSLATE_NOOP( |
| "App::Property", |
| "The position of the tag text. Keep (0,0,0) for center position", |
| ), |
| locked=True, |
| ) |
| if not "TagRotation" in pl: |
| obj.addProperty( |
| "App::PropertyAngle", |
| "TagRotation", |
| "PanelCut", |
| QT_TRANSLATE_NOOP("App::Property", "The rotation of the tag text"), |
| locked=True, |
| ) |
| if not "FontFile" in pl: |
| obj.addProperty( |
| "App::PropertyFile", |
| "FontFile", |
| "PanelCut", |
| QT_TRANSLATE_NOOP("App::Property", "The font of the tag text"), |
| locked=True, |
| ) |
| obj.FontFile = params.get_param("ShapeStringFontFile") |
| if not "MakeFace" in pl: |
| obj.addProperty( |
| "App::PropertyBool", |
| "MakeFace", |
| "PanelCut", |
| QT_TRANSLATE_NOOP( |
| "App::Property", "If True, the object is rendered as a face, if possible." |
| ), |
| locked=True, |
| ) |
| if not "AllowedAngles" in pl: |
| obj.addProperty( |
| "App::PropertyFloatList", |
| "AllowedAngles", |
| "PanelCut", |
| QT_TRANSLATE_NOOP( |
| "App::Property", |
| "The allowed angles this object can be rotated to when placed on sheets", |
| ), |
| locked=True, |
| ) |
| self.Type = "PanelCut" |
| if not "CutOffset" in pl: |
| obj.addProperty( |
| "App::PropertyDistance", |
| "CutOffset", |
| "PanelCut", |
| QT_TRANSLATE_NOOP( |
| "App::Property", "An offset value to move the cut plane from the center point" |
| ), |
| locked=True, |
| ) |
|
|
| def onDocumentRestored(self, obj): |
|
|
| self.setProperties(obj) |
|
|
| def execute(self, obj): |
|
|
| pl = obj.Placement |
| if obj.Source: |
| base = None |
| n = None |
| if Draft.getType(obj.Source) == "Panel": |
| import DraftGeomUtils |
| import Part |
|
|
| baseobj = None |
| if obj.Source.CloneOf: |
| baseobj = obj.Source.CloneOf.Base |
| if obj.Source.Base: |
| baseobj = obj.Source.Base |
| if baseobj: |
| if hasattr(baseobj, "Shape"): |
| if baseobj.Shape.Solids: |
| center = baseobj.Shape.BoundBox.Center |
| diag = baseobj.Shape.BoundBox.DiagonalLength |
| if obj.Source.Normal.Length: |
| n = obj.Source.Normal |
| elif baseobj.isDerivedFrom("Part::Extrusion"): |
| n = baseobj.Dir |
| if not n: |
| n = Vector(0, 0, 1) |
| if hasattr(obj, "CutOffset") and obj.CutOffset.Value: |
| l = obj.CutOffset.Value |
| d = Vector(n) |
| d.multiply(l) |
| center = center.add(d) |
| plane = Part.makePlane(diag, diag, center, n) |
| plane.translate(center.sub(plane.BoundBox.Center)) |
| wires = [] |
| for sol in baseobj.Shape.Solids: |
| s = sol.section(plane) |
| wires.extend(DraftGeomUtils.findWires(s.Edges)) |
| if wires: |
| base = self.buildCut(obj, wires) |
| else: |
| base = self.buildCut(obj, baseobj.Shape.Wires) |
| for w in base.Wires: |
| n = DraftGeomUtils.getNormal(w) |
| if n: |
| break |
| if not n: |
| n = Vector(0, 0, 1) |
| if base and n: |
| base.translate(base.BoundBox.Center.negative()) |
| r = FreeCAD.Rotation(n, Vector(0, 0, 1)) |
| base.rotate(Vector(0, 0, 0), r.Axis, math.degrees(r.Angle)) |
| elif baseobj.isDerivedFrom("Mesh::Feature"): |
| return |
| else: |
| l2 = obj.Source.Length / 2 |
| w2 = obj.Source.Width / 2 |
| v1 = Vector(-l2, -w2, 0) |
| v2 = Vector(l2, -w2, 0) |
| v3 = Vector(l2, w2, 0) |
| v4 = Vector(-l2, w2, 0) |
| base = Part.makePolygon([v1, v2, v3, v4, v1]) |
| if base: |
| self.outline = base |
| if obj.FontFile and obj.TagText and obj.TagSize.Value: |
| if obj.TagPosition.Length == 0: |
| pos = base.BoundBox.Center |
| else: |
| pos = obj.TagPosition |
| if obj.TagText == "%tag%": |
| string = obj.Source.Tag |
| elif obj.TagText == "%label%": |
| string = obj.Source.Label |
| elif obj.TagText == "%description%": |
| string = obj.Source.Description |
| else: |
| string = obj.TagText |
| chars = [] |
| for char in Part.makeWireString(string, obj.FontFile, obj.TagSize.Value, 0): |
| chars.extend(char) |
| textshape = Part.Compound(chars) |
| textshape.translate(pos.sub(textshape.BoundBox.Center)) |
| textshape.rotate( |
| textshape.BoundBox.Center, Vector(0, 0, 1), obj.TagRotation.Value |
| ) |
| self.tag = textshape |
| base = Part.Compound([base, textshape]) |
| else: |
| base = Part.Compound([base]) |
| obj.Shape = base |
| obj.Placement = pl |
|
|
| def buildCut(self, obj, wires): |
| """buildCut(obj,wires): builds the object shape""" |
|
|
| import Part |
|
|
| if hasattr(obj, "MakeFace"): |
| if obj.MakeFace: |
| face = None |
| if len(wires) > 1: |
| d = 0 |
| ow = None |
| for w in wires: |
| if w.BoundBox.DiagonalLength > d: |
| d = w.BoundBox.DiagonalLength |
| ow = w |
| if ow: |
| face = Part.Face(ow) |
| for w in wires: |
| if w.hashCode() != ow.hashCode(): |
| wface = Part.Face(w) |
| face = face.cut(wface) |
| else: |
| face = Part.Face(wires[0]) |
| if face: |
| return face |
| return Part.makeCompound(wires) |
|
|
| def getWires(self, obj): |
| """getWires(obj): returns a tuple containing 3 shapes |
| that define the panel outline, the panel holes, and |
| tags (engravings): (outline,holes,tags). Any of these can |
| be None if nonexistent""" |
|
|
| tag = None |
| outl = None |
| inl = None |
| if not hasattr(self, "outline"): |
| self.execute(obj) |
| if not hasattr(self, "outline"): |
| return None |
| outl = self.outline.copy() |
| if hasattr(self, "tag"): |
| tag = self.tag.copy() |
| if tag: |
| tag.Placement = obj.Placement.multiply(tag.Placement) |
|
|
| outl = self.outline.copy() |
| outl.Placement = obj.Placement.multiply(outl.Placement) |
| if len(outl.Wires) > 1: |
| |
| d = 0 |
| ow = None |
| for w in outl.Wires: |
| if w.BoundBox.DiagonalLength > d: |
| d = w.BoundBox.DiagonalLength |
| ow = w |
| if ow: |
| inl = Part.Compound([w for w in outl.Wires if w.hashCode() != ow.hashCode()]) |
| outl = Part.Compound([ow]) |
| else: |
| inl = None |
| outl = Part.Compound([outl.Wires[0]]) |
| return (outl, inl, tag) |
|
|
|
|
| class ViewProviderPanelCut(Draft.ViewProviderDraft): |
| "a view provider for the panel cut object" |
|
|
| def __init__(self, vobj): |
|
|
| Draft.ViewProviderDraft.__init__(self, vobj) |
| self.setProperties(vobj) |
|
|
| def setProperties(self, vobj): |
|
|
| pl = vobj.PropertiesList |
| if not "Margin" in pl: |
| vobj.addProperty( |
| "App::PropertyLength", |
| "Margin", |
| "Arch", |
| QT_TRANSLATE_NOOP("App::Property", "A margin inside the boundary"), |
| locked=True, |
| ) |
| if not "ShowMargin" in pl: |
| vobj.addProperty( |
| "App::PropertyBool", |
| "ShowMargin", |
| "Arch", |
| QT_TRANSLATE_NOOP("App::Property", "Turns the display of the margin on/off"), |
| locked=True, |
| ) |
|
|
| def onDocumentRestored(self, vobj): |
|
|
| self.setProperties(vobj) |
|
|
| def attach(self, vobj): |
|
|
| Draft.ViewProviderDraft.attach(self, vobj) |
| from pivy import coin |
|
|
| self.coords = coin.SoCoordinate3() |
| self.lineset = coin.SoLineSet() |
| self.lineset.numVertices.setValue(-1) |
| lineStyle = coin.SoDrawStyle() |
| lineStyle.linePattern = 0x0F0F |
| self.color = coin.SoBaseColor() |
| self.switch = coin.SoSwitch() |
| sep = coin.SoSeparator() |
| self.switch.whichChild = -1 |
| sep.addChild(self.color) |
| sep.addChild(lineStyle) |
| sep.addChild(self.coords) |
| sep.addChild(self.lineset) |
| self.switch.addChild(sep) |
| vobj.Annotation.addChild(self.switch) |
| self.onChanged(vobj, "ShowMargin") |
| self.onChanged(vobj, "LineColor") |
|
|
| def onChanged(self, vobj, prop): |
|
|
| if prop in ["Margin", "ShowMargin"]: |
| if hasattr(vobj, "Margin") and hasattr(vobj, "ShowMargin"): |
| if (vobj.Margin.Value > 0) and vobj.Object.Shape and vobj.ShowMargin: |
| self.lineset.numVertices.setValue(-1) |
| if vobj.Object.Shape.Wires: |
| d = 0 |
| dw = None |
| for w in vobj.Object.Shape.Wires: |
| if w.BoundBox.DiagonalLength > d: |
| d = w.BoundBox.DiagonalLength |
| dw = w |
| if dw: |
| ow = dw.makeOffset2D(vobj.Margin.Value) |
| verts = [] |
| for v in ow.OrderedVertexes: |
| v = vobj.Object.Placement.inverse().multVec(v.Point) |
| verts.append((v.x, v.y, v.z)) |
| if dw.isClosed(): |
| verts.append(verts[0]) |
| self.coords.point.setValues(verts) |
| self.lineset.numVertices.setValue(len(verts)) |
| self.switch.whichChild = 0 |
| else: |
| self.switch.whichChild = -1 |
| elif prop == "LineColor": |
| if hasattr(vobj, "LineColor"): |
| c = vobj.LineColor |
| self.color.rgb.setValue(c[0], c[1], c[2]) |
| Draft.ViewProviderDraft.onChanged(self, vobj, prop) |
|
|
| def updateData(self, obj, prop): |
|
|
| if prop in ["Shape"]: |
| self.onChanged(obj.ViewObject, "Margin") |
| Draft.ViewProviderDraft.updateData(self, obj, prop) |
|
|
| def doubleClicked(self, vobj): |
|
|
| |
| FreeCADGui.runCommand("Std_TransformManip") |
| return True |
|
|
|
|
| class PanelSheet(Draft.DraftObject): |
| "A collection of Panel cuts under a sheet" |
|
|
| def __init__(self, obj): |
|
|
| Draft.DraftObject.__init__(self, obj) |
| obj.Proxy = self |
| self.setProperties(obj) |
|
|
| def setProperties(self, obj): |
|
|
| pl = obj.PropertiesList |
| if not "Group" in pl: |
| obj.addProperty( |
| "App::PropertyLinkList", |
| "Group", |
| "PanelSheet", |
| QT_TRANSLATE_NOOP("App::Property", "The linked Panel cuts"), |
| locked=True, |
| ) |
| if not "TagText" in pl: |
| obj.addProperty( |
| "App::PropertyString", |
| "TagText", |
| "PanelSheet", |
| QT_TRANSLATE_NOOP("App::Property", "The tag text to display"), |
| locked=True, |
| ) |
| if not "TagSize" in pl: |
| obj.addProperty( |
| "App::PropertyLength", |
| "TagSize", |
| "PanelSheet", |
| QT_TRANSLATE_NOOP("App::Property", "The size of the tag text"), |
| locked=True, |
| ) |
| obj.TagSize = 10 |
| if not "TagPosition" in pl: |
| obj.addProperty( |
| "App::PropertyVector", |
| "TagPosition", |
| "PanelSheet", |
| QT_TRANSLATE_NOOP( |
| "App::Property", |
| "The position of the tag text. Keep (0,0,0) for center position", |
| ), |
| locked=True, |
| ) |
| if not "TagRotation" in pl: |
| obj.addProperty( |
| "App::PropertyAngle", |
| "TagRotation", |
| "PanelSheet", |
| QT_TRANSLATE_NOOP("App::Property", "The rotation of the tag text"), |
| locked=True, |
| ) |
| if not "FontFile" in pl: |
| obj.addProperty( |
| "App::PropertyFile", |
| "FontFile", |
| "PanelSheet", |
| QT_TRANSLATE_NOOP("App::Property", "The font of the tag text"), |
| locked=True, |
| ) |
| obj.FontFile = params.get_param("ShapeStringFontFile") |
| if not "Width" in pl: |
| obj.addProperty( |
| "App::PropertyLength", |
| "Width", |
| "PanelSheet", |
| QT_TRANSLATE_NOOP("App::Property", "The width of the sheet"), |
| locked=True, |
| ) |
| obj.Width = params.get_param_arch("PanelLength") |
| if not "Height" in pl: |
| obj.addProperty( |
| "App::PropertyLength", |
| "Height", |
| "PanelSheet", |
| QT_TRANSLATE_NOOP("App::Property", "The height of the sheet"), |
| locked=True, |
| ) |
| obj.Height = params.get_param_arch("PanelWidth") |
| if not "FillRatio" in pl: |
| obj.addProperty( |
| "App::PropertyPercent", |
| "FillRatio", |
| "PanelSheet", |
| QT_TRANSLATE_NOOP("App::Property", "The fill ratio of this sheet"), |
| locked=True, |
| ) |
| obj.setEditorMode("FillRatio", 2) |
| if not "MakeFace" in pl: |
| obj.addProperty( |
| "App::PropertyBool", |
| "MakeFace", |
| "PanelSheet", |
| QT_TRANSLATE_NOOP( |
| "App::Property", "If True, the object is rendered as a face, if possible." |
| ), |
| locked=True, |
| ) |
| if not "GrainDirection" in pl: |
| obj.addProperty( |
| "App::PropertyAngle", |
| "GrainDirection", |
| "PanelSheet", |
| QT_TRANSLATE_NOOP( |
| "App::Property", "Specifies an angle for the wood grain (Clockwise, 0 is North)" |
| ), |
| locked=True, |
| ) |
| if not "Scale" in pl: |
| obj.addProperty( |
| "App::PropertyFloat", |
| "Scale", |
| "PanelSheet", |
| QT_TRANSLATE_NOOP( |
| "App::Property", "Specifies the scale applied to each panel view." |
| ), |
| locked=True, |
| ) |
| obj.Scale = 1.0 |
| if not "Rotations" in pl: |
| obj.addProperty( |
| "App::PropertyFloatList", |
| "Rotations", |
| "PanelSheet", |
| QT_TRANSLATE_NOOP("App::Property", "A list of possible rotations for the nester"), |
| locked=True, |
| ) |
| self.Type = "PanelSheet" |
|
|
| def onDocumentRestored(self, obj): |
|
|
| self.setProperties(obj) |
|
|
| def execute(self, obj): |
|
|
| import Part |
|
|
| self.sheettag = None |
| self.sheetborder = None |
| pl = obj.Placement |
| if obj.Width.Value and obj.Height.Value: |
| l2 = obj.Width.Value / 2 |
| w2 = obj.Height.Value / 2 |
| v1 = Vector(-l2, -w2, 0) |
| v2 = Vector(l2, -w2, 0) |
| v3 = Vector(l2, w2, 0) |
| v4 = Vector(-l2, w2, 0) |
| base = Part.makePolygon([v1, v2, v3, v4, v1]) |
| if hasattr(obj, "MakeFace"): |
| if obj.MakeFace: |
| base = Part.Face(base) |
| self.sheetborder = base |
| wires = [] |
| area = obj.Width.Value * obj.Height.Value |
| subarea = 0 |
| for v in obj.Group: |
| if hasattr(v, "Shape"): |
| wires.extend(v.Shape.Wires) |
| if Draft.getType(v) == "PanelCut": |
| if v.Source: |
| subarea += v.Source.Area.Value |
| else: |
| for w in v.Shape.Wires: |
| if w.isClosed(): |
| f = Part.Face(w) |
| subarea += f.Area |
| if wires: |
| base = Part.Compound([base] + wires) |
| if obj.FontFile and obj.TagText and obj.TagSize.Value: |
| chars = [] |
| for char in Part.makeWireString(obj.TagText, obj.FontFile, obj.TagSize.Value, 0): |
| chars.extend(char) |
| textshape = Part.Compound(chars) |
| textshape.translate(obj.TagPosition) |
| textshape.rotate(textshape.BoundBox.Center, Vector(0, 0, 1), obj.TagRotation.Value) |
| self.sheettag = textshape |
| base = Part.Compound([base, textshape]) |
| base.scale(obj.Scale, FreeCAD.Vector()) |
| obj.Shape = base |
| obj.Placement = pl |
| obj.FillRatio = int((subarea / area) * 100) |
|
|
| def getOutlines(self, obj, transform=False): |
| """getOutlines(obj,transform=False): returns a list of compounds whose wires define the |
| outlines of the panels in this sheet. If transform is True, the placement of |
| the sheet will be added to each wire""" |
|
|
| outp = [] |
| for p in obj.Group: |
| ispanel = False |
| if hasattr(p, "Proxy"): |
| if hasattr(p.Proxy, "getWires"): |
| ispanel = True |
| w = p.Proxy.getWires(p) |
| if w[0]: |
| w = w[0] |
| w.scale(obj.Scale, FreeCAD.Vector()) |
| if transform: |
| w.Placement = obj.Placement.multiply(w.Placement) |
| outp.append(w) |
| if not ispanel: |
| if hasattr(p, "Shape"): |
| for w in p.Shape.Wires: |
| w.scale(obj.Scale, FreeCAD.Vector()) |
| if transform: |
| w.Placement = obj.Placement.multiply(w.Placement) |
| outp.append(w) |
| return outp |
|
|
| def getHoles(self, obj, transform=False): |
| """getHoles(obj,transform=False): returns a list of compound whose wires define the |
| holes contained in the panels in this sheet. If transform is True, the placement of |
| the sheet will be added to each wire""" |
|
|
| outp = [] |
| for p in obj.Group: |
| if hasattr(p, "Proxy"): |
| if hasattr(p.Proxy, "getWires"): |
| w = p.Proxy.getWires(p) |
| if w[1]: |
| w = w[1] |
| w.scale(obj.Scale, FreeCAD.Vector()) |
| if transform: |
| w.Placement = obj.Placement.multiply(w.Placement) |
| outp.append(w) |
| return outp |
|
|
| def getTags(self, obj, transform=False): |
| """getTags(obj,transform=False): returns a list of compounds whose wires define the |
| tags (engravings) contained in the panels in this sheet and the sheet intself. |
| If transform is True, the placement of the sheet will be added to each wire. |
| Warning, the wires returned by this function may not be closed, |
| depending on the font""" |
|
|
| outp = [] |
| for p in obj.Group: |
| if hasattr(p, "Proxy"): |
| if hasattr(p.Proxy, "getWires"): |
| w = p.Proxy.getWires(p) |
| if w[2]: |
| w = w[2] |
| w.scale(obj.Scale, FreeCAD.Vector()) |
| if transform: |
| w.Placement = obj.Placement.multiply(w.Placement) |
| outp.append(w) |
| if self.sheettag is not None: |
| w = self.sheettag.copy() |
| w.scale(obj.Scale, FreeCAD.Vector()) |
| if transform: |
| w.Placement = obj.Placement.multiply(w.Placement) |
| outp.append(w) |
|
|
| return outp |
|
|
|
|
| class ViewProviderPanelSheet(Draft.ViewProviderDraft): |
| "a view provider for the panel sheet object" |
|
|
| def __init__(self, vobj): |
|
|
| Draft.ViewProviderDraft.__init__(self, vobj) |
| self.setProperties(vobj) |
| vobj.PatternSize = 0.0035 |
|
|
| def setProperties(self, vobj): |
|
|
| pl = vobj.PropertiesList |
| if not "Margin" in pl: |
| vobj.addProperty( |
| "App::PropertyLength", |
| "Margin", |
| "PanelSheet", |
| QT_TRANSLATE_NOOP("App::Property", "A margin inside the boundary"), |
| locked=True, |
| ) |
| if not "ShowMargin" in pl: |
| vobj.addProperty( |
| "App::PropertyBool", |
| "ShowMargin", |
| "PanelSheet", |
| QT_TRANSLATE_NOOP("App::Property", "Turns the display of the margin on/off"), |
| locked=True, |
| ) |
| if not "ShowGrain" in pl: |
| vobj.addProperty( |
| "App::PropertyBool", |
| "ShowGrain", |
| "PanelSheet", |
| QT_TRANSLATE_NOOP( |
| "App::Property", "Turns the display of the wood grain texture on/off" |
| ), |
| locked=True, |
| ) |
|
|
| def onDocumentRestored(self, vobj): |
|
|
| self.setProperties(vobj) |
|
|
| def getIcon(self): |
|
|
| return ":/icons/Arch_Panel_Sheet_Tree.svg" |
|
|
| def setEdit(self, vobj, mode): |
| if mode == 1 or mode == 2: |
| return None |
|
|
| taskd = SheetTaskPanel(vobj.Object) |
| taskd.update() |
| FreeCADGui.Control.showDialog(taskd) |
| return True |
|
|
| def unsetEdit(self, vobj, mode): |
| if mode == 1 or mode == 2: |
| return None |
|
|
| FreeCADGui.Control.closeDialog() |
| return True |
|
|
| def attach(self, vobj): |
|
|
| Draft.ViewProviderDraft.attach(self, vobj) |
| from pivy import coin |
|
|
| self.coords = coin.SoCoordinate3() |
| self.lineset = coin.SoLineSet() |
| self.lineset.numVertices.setValue(-1) |
| lineStyle = coin.SoDrawStyle() |
| lineStyle.linePattern = 0x0F0F |
| self.color = coin.SoBaseColor() |
| self.switch = coin.SoSwitch() |
| sep = coin.SoSeparator() |
| self.switch.whichChild = -1 |
| sep.addChild(self.color) |
| sep.addChild(lineStyle) |
| sep.addChild(self.coords) |
| sep.addChild(self.lineset) |
| self.switch.addChild(sep) |
| vobj.Annotation.addChild(self.switch) |
| self.onChanged(vobj, "ShowMargin") |
| self.onChanged(vobj, "LineColor") |
|
|
| def onChanged(self, vobj, prop): |
|
|
| if prop in ["Margin", "ShowMargin"]: |
| if hasattr(vobj, "Margin") and hasattr(vobj, "ShowMargin"): |
| if ( |
| (vobj.Margin.Value > 0) |
| and (vobj.Margin.Value < vobj.Object.Width.Value / 2) |
| and (vobj.Margin.Value < vobj.Object.Height.Value / 2) |
| ): |
| l2 = vobj.Object.Width.Value / 2 |
| w2 = vobj.Object.Height.Value / 2 |
| v = vobj.Margin.Value |
| v1 = (-l2 + v, -w2 + v, 0) |
| v2 = (l2 - v, -w2 + v, 0) |
| v3 = (l2 - v, w2 - v, 0) |
| v4 = (-l2 + v, w2 - v, 0) |
| self.coords.point.setValues([v1, v2, v3, v4, v1]) |
| self.lineset.numVertices.setValue(5) |
| if vobj.ShowMargin: |
| self.switch.whichChild = 0 |
| else: |
| self.switch.whichChild = -1 |
| elif prop == "LineColor": |
| if hasattr(vobj, "LineColor"): |
| c = vobj.LineColor |
| self.color.rgb.setValue(c[0], c[1], c[2]) |
| elif prop == "ShowGrain": |
| if hasattr(vobj, "ShowGrain"): |
| if vobj.ShowGrain: |
| vobj.Pattern = "woodgrain" |
| else: |
| vobj.Pattern = "None" |
| Draft.ViewProviderDraft.onChanged(self, vobj, prop) |
|
|
| def updateData(self, obj, prop): |
|
|
| if prop in ["Width", "Height"]: |
| self.onChanged(obj.ViewObject, "Margin") |
| elif prop == "GrainDirection": |
| if hasattr(self, "texcoords"): |
| if self.texcoords: |
| s = FreeCAD.Vector(self.texcoords.directionS.getValue().getValue()).Length |
| vS = DraftVecUtils.rotate( |
| FreeCAD.Vector(s, 0, 0), -math.radians(obj.GrainDirection.Value) |
| ) |
| vT = DraftVecUtils.rotate( |
| FreeCAD.Vector(0, s, 0), -math.radians(obj.GrainDirection.Value) |
| ) |
| self.texcoords.directionS.setValue(vS.x, vS.y, vS.z) |
| self.texcoords.directionT.setValue(vT.x, vT.y, vT.z) |
| Draft.ViewProviderDraft.updateData(self, obj, prop) |
|
|
|
|
| class SheetTaskPanel(ArchComponent.ComponentTaskPanel): |
|
|
| def __init__(self, obj): |
|
|
| ArchComponent.ComponentTaskPanel.__init__(self) |
| self.obj = obj |
| self.optwid = QtGui.QWidget() |
| self.optwid.setWindowTitle(QtGui.QApplication.translate("Arch", "Tools", None)) |
| lay = QtGui.QVBoxLayout(self.optwid) |
| self.editButton = QtGui.QPushButton(self.optwid) |
| self.editButton.setIcon(QtGui.QIcon(":/icons/Draft_Edit.svg")) |
| self.editButton.setText(QtGui.QApplication.translate("Arch", "Edit views positions", None)) |
| lay.addWidget(self.editButton) |
| QtCore.QObject.connect(self.editButton, QtCore.SIGNAL("clicked()"), self.editNodes) |
| self.form = [self.form, self.optwid] |
|
|
| def editNodes(self): |
|
|
| FreeCADGui.Control.closeDialog() |
| FreeCADGui.runCommand("Draft_Edit") |
|
|