#*************************************************************************** #* Copyright (c) 2011 Yorik van Havre * #* * #* This program is free software; you can redistribute it and/or modify * #* it under the terms of the GNU Lesser General Public License (LGPL) * #* as published by the Free Software Foundation; either version 2 of * #* the License, or (at your option) any later version. * #* for detail see the LICENCE text file. * #* * #* This program 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 Library General Public License for more details. * #* * #* You should have received a copy of the GNU Library General Public * #* License along with this program; if not, write to the Free Software * #* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * #* USA * #* * #*************************************************************************** import os import FreeCAD import ArchCommands import ArchComponent import Draft import DraftVecUtils import ArchWindowPresets from FreeCAD import Units from FreeCAD import Vector from draftutils import params from draftutils.messages import _wrn if FreeCAD.GuiUp: import FreeCADGui from PySide import QtCore, QtGui from draftutils.translate import translate from PySide.QtCore import QT_TRANSLATE_NOOP import draftguitools.gui_trackers as DraftTrackers else: # \cond def translate(ctxt,txt): return txt def QT_TRANSLATE_NOOP(ctxt,txt): return txt # \endcond ## @package ArchWindow # \ingroup ARCH # \brief The Window object and tools # # This module provides tools to build Window objects. # Windows are Arch objects obtained by extruding a series # of wires, and that can be inserted into other Arch objects, # by defining a volume that gets subtracted from them. __title__ = "FreeCAD Window" __author__ = "Yorik van Havre" __url__ = "https://www.freecad.org" # presets WindowPartTypes = ["Frame","Solid panel","Glass panel","Louvre"] WindowOpeningModes = ["None","Arc 90","Arc 90 inv","Arc 45","Arc 45 inv","Arc 180", "Arc 180 inv","Triangle","Triangle inv","Sliding","Sliding inv"] WindowPresets = ArchWindowPresets.WindowPresets def recolorize(attr): # names is [docname,objname] """Recolorizes an object or a [documentname,objectname] list This basically calls the Proxy.colorize(obj) methods of objects that have one.""" if isinstance(attr,list): if attr[0] in FreeCAD.listDocuments(): doc = FreeCAD.getDocument(attr[0]) obj = doc.getObject(attr[1]) if obj: if obj.ViewObject: if obj.ViewObject.Proxy: obj.ViewObject.Proxy.colorize(obj) elif hasattr(attr,"ViewObject") and attr.ViewObject: obj = attr if hasattr(obj.ViewObject,"Proxy") and hasattr(obj.ViewObject.Proxy,"colorize"): obj.ViewObject.Proxy.colorize(obj) class _Window(ArchComponent.Component): "The Window object" def __init__(self,obj): ArchComponent.Component.__init__(self,obj) self.setProperties(obj) obj.IfcType = "Window" obj.MoveWithHost = True # Add features in the SketchArch External Add-on self.addSketchArchFeatures(obj) def addSketchArchFeatures(self,obj,linkObj=None,mode=None): ''' To add features in the SketchArch External Add-on (https://github.com/paullee0/FreeCAD_SketchArch) - import ArchSketchObject module, and - set properties that are common to ArchObjects (including Links) and ArchSketch to support the additional features To install SketchArch External Add-on, see https://github.com/paullee0/FreeCAD_SketchArch#iv-install ''' try: import ArchSketchObject ArchSketchObject.ArchSketch.setPropertiesLinkCommon(self, obj, linkObj, mode) except: pass def setProperties(self,obj): lp = obj.PropertiesList if not "Hosts" in lp: obj.addProperty("App::PropertyLinkList","Hosts","Window",QT_TRANSLATE_NOOP("App::Property","The objects that host this window")) if not "WindowParts" in lp: obj.addProperty("App::PropertyStringList","WindowParts","Window",QT_TRANSLATE_NOOP("App::Property","The components of this window")) obj.setEditorMode("WindowParts",2) if not "HoleDepth" in lp: obj.addProperty("App::PropertyLength","HoleDepth","Window",QT_TRANSLATE_NOOP("App::Property","The depth of the hole that this window makes in its host object. If 0, the value will be calculated automatically.")) if not "Subvolume" in lp: obj.addProperty("App::PropertyLink","Subvolume","Window",QT_TRANSLATE_NOOP("App::Property","An optional object that defines a volume to be subtracted from hosts of this window")) if not "Width" in lp: obj.addProperty("App::PropertyLength","Width","Window",QT_TRANSLATE_NOOP("App::Property","The width of this window")) if not "Height" in lp: obj.addProperty("App::PropertyLength","Height","Window",QT_TRANSLATE_NOOP("App::Property","The height of this window")) if not "Normal" in lp: obj.addProperty("App::PropertyVector","Normal","Window",QT_TRANSLATE_NOOP("App::Property","The normal direction of this window")) if not "Preset" in lp: obj.addProperty("App::PropertyInteger","Preset","Window",QT_TRANSLATE_NOOP("App::Property","The preset number this window is based on")) obj.setEditorMode("Preset",2) if not "Frame" in lp: obj.addProperty("App::PropertyLength","Frame","Window",QT_TRANSLATE_NOOP("App::Property","The frame size of this window")) if not "Offset" in lp: obj.addProperty("App::PropertyLength","Offset","Window",QT_TRANSLATE_NOOP("App::Property","The offset size of this window")) if not "Area" in lp: obj.addProperty("App::PropertyArea","Area","Window",QT_TRANSLATE_NOOP("App::Property","The area of this window")) if not "LouvreWidth" in lp: obj.addProperty("App::PropertyLength","LouvreWidth","Window",QT_TRANSLATE_NOOP("App::Property","The width of louvre elements")) if not "LouvreSpacing" in lp: obj.addProperty("App::PropertyLength","LouvreSpacing","Window",QT_TRANSLATE_NOOP("App::Property","The space between louvre elements")) if not "Opening" in lp: obj.addProperty("App::PropertyPercent","Opening","Window",QT_TRANSLATE_NOOP("App::Property","Opens the subcomponents that have a hinge defined")) if not "HoleWire" in lp: obj.addProperty("App::PropertyInteger","HoleWire","Window",QT_TRANSLATE_NOOP("App::Property","The number of the wire that defines the hole. If 0, the value will be calculated automatically")) if not "SymbolPlan" in lp: obj.addProperty("App::PropertyBool","SymbolPlan","Window",QT_TRANSLATE_NOOP("App::Property","Shows plan opening symbols if available")) if not "SymbolElevation" in lp: obj.addProperty("App::PropertyBool","SymbolElevation","Window",QT_TRANSLATE_NOOP("App::Property","Show elevation opening symbols if available")) obj.setEditorMode("VerticalArea",2) obj.setEditorMode("HorizontalArea",2) obj.setEditorMode("PerimeterLength",2) self.Type = "Window" def onDocumentRestored(self,obj): ArchComponent.Component.onDocumentRestored(self,obj) self.setProperties(obj) # Add features in the SketchArch External Add-on self.addSketchArchFeatures(obj, mode='ODR') def onBeforeChange(self,obj,prop): if prop in ["Base","WindowParts","Placement","HoleDepth","Height","Width","Hosts"]: setattr(self,prop,getattr(obj,prop)) if prop in ["Height","Width"] and obj.CloneOf is None: self.TouchOnShapeChange = True # touch hosts after next "Shape" change def onChanged(self,obj,prop): self.hideSubobjects(obj,prop) if not "Restore" in obj.State: if prop in ["Base","WindowParts","Placement","HoleDepth","Height","Width","Hosts","Shape"]: # anti-recursive loops, bc the base sketch will touch the Placement all the time touchhosts = False if prop == "Shape": if hasattr(self,"TouchOnShapeChange") and self.TouchOnShapeChange: self.TouchOnShapeChange = False touchhosts = True elif hasattr(self,prop) and getattr(self,prop) != getattr(obj,prop): touchhosts = True if touchhosts: hosts = self.Hosts if hasattr(self, "Hosts") else [] hosts += obj.Hosts if hasattr(obj, "Hosts") else [] for host in set(hosts): # use set to remove duplicates # mark host to recompute so it can detect this object host.touch() if prop in ["Width","Height","Frame"]: if obj.Base: if hasattr(obj.Base,"Constraints") and (prop in [c.Name for c in obj.Base.Constraints]): val = getattr(obj,prop).Value if val > 0: obj.Base.setDatum(prop,val) else: ArchComponent.Component.onChanged(self,obj,prop) def buildShapes(self,obj): import Part import DraftGeomUtils import math self.sshapes = [] self.vshapes = [] shapes = [] rotdata = None for i in range(int(len(obj.WindowParts)/5)): wires = [] hinge = None omode = None ssymbols = [] vsymbols = [] wstr = obj.WindowParts[(i*5)+2].split(',') for s in wstr: if "Wire" in s: j = int(s[4:]) if obj.Base.Shape.Wires: if len(obj.Base.Shape.Wires) >= j: wires.append(obj.Base.Shape.Wires[j]) elif "Edge" in s: hinge = int(s[4:])-1 elif "Mode" in s: omode = int(s[4:]) if omode >= len(WindowOpeningModes): # Ignore modes not listed in WindowOpeningModes omode = None if wires: max_length = 0 for w in wires: if w.BoundBox.DiagonalLength > max_length: max_length = w.BoundBox.DiagonalLength ext = w wires.remove(ext) shape = Part.Face(ext) norm = shape.normalAt(0,0) if hasattr(obj,"Normal"): if obj.Normal: if not DraftVecUtils.isNull(obj.Normal): norm = obj.Normal if hinge and omode: opening = None if hasattr(obj,"Opening"): if obj.Opening: opening = obj.Opening/100.0 e = obj.Base.Shape.Edges[hinge] ev1 = e.Vertexes[0].Point ev2 = e.Vertexes[-1].Point # choose the one with lowest z to draw the symbol if ev2.z < ev1.z: ev1,ev2 = ev2,ev1 # find the point most distant from the hinge p = None d = 0 for v in shape.Vertexes: dist = v.Point.distanceToLine(ev1,ev2.sub(ev1)) if dist > d: d = dist p = v.Point if p: # bring that point to the level of ev1 if needed chord = p.sub(ev1) enorm = ev2.sub(ev1) proj = DraftVecUtils.project(chord,enorm) v1 = ev1 if proj.Length > 0: #chord = p.sub(ev1.add(proj)) #p = v1.add(chord) p = p.sub(proj) chord = p.sub(ev1) # calculate symbols v4 = p.add(DraftVecUtils.scale(enorm,0.5)) if omode == 1: # Arc 90 v2 = v1.add(DraftVecUtils.rotate(chord,math.pi/4,enorm)) v3 = v1.add(DraftVecUtils.rotate(chord,math.pi/2,enorm)) ssymbols.append(Part.Arc(p,v2,v3).toShape()) ssymbols.append(Part.LineSegment(v3,v1).toShape()) vsymbols.append(Part.LineSegment(v1,v4).toShape()) vsymbols.append(Part.LineSegment(v4,ev2).toShape()) if opening: rotdata = [v1,ev2.sub(ev1),90*opening] elif omode == 2: # Arc -90 v2 = v1.add(DraftVecUtils.rotate(chord,-math.pi/4,enorm)) v3 = v1.add(DraftVecUtils.rotate(chord,-math.pi/2,enorm)) ssymbols.append(Part.Arc(p,v2,v3).toShape()) ssymbols.append(Part.LineSegment(v3,v1).toShape()) vsymbols.append(Part.LineSegment(v1,v4).toShape()) vsymbols.append(Part.LineSegment(v4,ev2).toShape()) if opening: rotdata = [v1,ev2.sub(ev1),-90*opening] elif omode == 3: # Arc 45 v2 = v1.add(DraftVecUtils.rotate(chord,math.pi/8,enorm)) v3 = v1.add(DraftVecUtils.rotate(chord,math.pi/4,enorm)) ssymbols.append(Part.Arc(p,v2,v3).toShape()) ssymbols.append(Part.LineSegment(v3,v1).toShape()) vsymbols.append(Part.LineSegment(v1,v4).toShape()) vsymbols.append(Part.LineSegment(v4,ev2).toShape()) if opening: rotdata = [v1,ev2.sub(ev1),45*opening] elif omode == 4: # Arc -45 v2 = v1.add(DraftVecUtils.rotate(chord,-math.pi/8,enorm)) v3 = v1.add(DraftVecUtils.rotate(chord,-math.pi/4,enorm)) ssymbols.append(Part.Arc(p,v2,v3).toShape()) ssymbols.append(Part.LineSegment(v3,v1).toShape()) vsymbols.append(Part.LineSegment(v1,v4).toShape()) vsymbols.append(Part.LineSegment(v4,ev2).toShape()) if opening: rotdata = [v1,ev2.sub(ev1),-45*opening] elif omode == 5: # Arc 180 v2 = v1.add(DraftVecUtils.rotate(chord,math.pi/2,enorm)) v3 = v1.add(DraftVecUtils.rotate(chord,math.pi,enorm)) ssymbols.append(Part.Arc(p,v2,v3).toShape()) ssymbols.append(Part.LineSegment(v3,v1).toShape()) vsymbols.append(Part.LineSegment(v1,v4).toShape()) vsymbols.append(Part.LineSegment(v4,ev2).toShape()) if opening: rotdata = [v1,ev2.sub(ev1),180*opening] elif omode == 6: # Arc -180 v2 = v1.add(DraftVecUtils.rotate(chord,-math.pi/2,enorm)) v3 = v1.add(DraftVecUtils.rotate(chord,-math.pi,enorm)) ssymbols.append(Part.Arc(p,v2,v3).toShape()) ssymbols.append(Part.LineSegment(v3,v1).toShape()) vsymbols.append(Part.LineSegment(v1,v4).toShape()) vsymbols.append(Part.LineSegment(v4,ev2).toShape()) if opening: rotdata = [ev1,ev2.sub(ev1),-180*opening] elif omode == 7: # tri v2 = v1.add(DraftVecUtils.rotate(chord,math.pi/2,enorm)) ssymbols.append(Part.LineSegment(p,v2).toShape()) ssymbols.append(Part.LineSegment(v2,v1).toShape()) vsymbols.append(Part.LineSegment(v1,v4).toShape()) vsymbols.append(Part.LineSegment(v4,ev2).toShape()) if opening: rotdata = [v1,ev2.sub(ev1),90*opening] elif omode == 8: # -tri v2 = v1.add(DraftVecUtils.rotate(chord,-math.pi/2,enorm)) ssymbols.append(Part.LineSegment(p,v2).toShape()) ssymbols.append(Part.LineSegment(v2,v1).toShape()) vsymbols.append(Part.LineSegment(v1,v4).toShape()) vsymbols.append(Part.LineSegment(v4,ev2).toShape()) if opening: rotdata = [v1,ev2.sub(ev1),-90*opening] elif omode == 9: # sliding pass elif omode == 10: # -sliding pass exv = FreeCAD.Vector() zov = FreeCAD.Vector() V = 0 thk = obj.WindowParts[(i*5)+3] if "+V" in thk: thk = thk[:-2] V = obj.Frame.Value thk = float(thk) + V if thk: exv = DraftVecUtils.scaleTo(norm,thk) shape = shape.extrude(exv) for w in wires: f = Part.Face(w) f = f.extrude(exv) shape = shape.cut(f) if obj.WindowParts[(i*5)+4]: V = 0 zof = obj.WindowParts[(i*5)+4] if "+V" in zof: zof = zof[:-2] V = obj.Offset.Value zof = float(zof) + V if zof: zov = DraftVecUtils.scaleTo(norm,zof) shape.translate(zov) if hinge and omode and 0 < omode < 9: if DraftVecUtils.angle(chord, norm, enorm) < 0: if omode%2 == 0: zov = zov.add(exv) else: if omode%2 == 1: zov = zov.add(exv) for symb in ssymbols: symb.translate(zov) for symb in vsymbols: symb.translate(zov) if rotdata: rotdata[0] = rotdata[0].add(zov) if obj.WindowParts[(i*5)+1] == "Louvre": if hasattr(obj,"LouvreWidth"): if obj.LouvreWidth and obj.LouvreSpacing: bb = shape.BoundBox bb.enlarge(10) step = obj.LouvreWidth.Value+obj.LouvreSpacing.Value if step < bb.ZLength: box = Part.makeBox(bb.XLength,bb.YLength,obj.LouvreSpacing.Value) boxes = [] for i in range(int(bb.ZLength/step)+1): b = box.copy() b.translate(FreeCAD.Vector(bb.XMin,bb.YMin,bb.ZMin+i*step)) boxes.append(b) self.boxes = Part.makeCompound(boxes) #rot = obj.Base.Placement.Rotation #self.boxes.rotate(self.boxes.BoundBox.Center,rot.Axis,math.degrees(rot.Angle)) self.boxes.translate(shape.BoundBox.Center.sub(self.boxes.BoundBox.Center)) shape = shape.cut(self.boxes) if rotdata: shape.rotate(rotdata[0],rotdata[1],rotdata[2]) shapes.append(shape) self.sshapes.extend(ssymbols) self.vshapes.extend(vsymbols) return shapes def execute(self,obj): if self.clone(obj): clonedProxy = obj.CloneOf.Proxy if not (hasattr(clonedProxy, "sshapes") and hasattr(clonedProxy, "vshapes")): clonedProxy.buildShapes(obj.CloneOf) self.sshapes = clonedProxy.sshapes self.vshapes = clonedProxy.vshapes if hasattr(clonedProxy, "boxes"): self.boxes = clonedProxy.boxes return import Part import DraftGeomUtils import math pl = obj.Placement base = None self.sshapes = [] self.vshapes = [] if obj.Base: if hasattr(obj,'Shape'): if hasattr(obj,"WindowParts"): if obj.WindowParts and (len(obj.WindowParts)%5 == 0): shapes = self.buildShapes(obj) if shapes: base = Part.makeCompound(shapes) elif not obj.WindowParts: if obj.Base.Shape.Solids: base = obj.Base.Shape.copy() # obj placement is already added by applyShape() below #if not DraftGeomUtils.isNull(pl): # base.Placement = base.Placement.multiply(pl) else: print("Arch: Bad formatting of window parts definitions") base = self.processSubShapes(obj,base) if base: if not base.isNull(): b = [] if self.sshapes: if hasattr(obj,"SymbolPlan"): if obj.SymbolPlan: b.extend(self.sshapes) else: b.extend(self.sshapes) if self.vshapes: if hasattr(obj,"SymbolElevation"): if obj.SymbolElevation: b.extend(self.vshapes) else: b.extend(self.vshapes) if b: base = Part.makeCompound([base]+b) #base = Part.makeCompound([base]+self.sshapes+self.vshapes) self.applyShape(obj,base,pl,allowinvalid=True,allownosolid=True) obj.Placement = pl else: obj.Shape = Part.Shape() if hasattr(obj,"Area"): obj.Area = obj.Width.Value * obj.Height.Value self.executeSketchArchFeatures(obj) def executeSketchArchFeatures(self, obj, linkObj=None, index=None, linkElement=None): ''' To execute features in the SketchArch External Add-on (https://github.com/paullee0/FreeCAD_SketchArch) - import ArchSketchObject module, and - execute features that are common to ArchObjects (including Links) and ArchSketch To install SketchArch External Add-on, see https://github.com/paullee0/FreeCAD_SketchArch#iv-install ''' # To execute features in SketchArch External Add-on try: import ArchSketchObject # Why needed ? Should have try: addSketchArchFeatures() before ! Need 'per method' ? # Execute SketchArch Feature - Intuitive Automatic Placement for Arch Windows/Doors, Equipment etc. # see https://forum.freecad.org/viewtopic.php?f=23&t=50802 ArchSketchObject.updateAttachmentOffset(obj, linkObj) except: pass def appLinkExecute(self, obj, linkObj, index, linkElement): ''' Default Link Execute method() - See https://forum.freecad.org/viewtopic.php?f=22&t=42184&start=10#p361124 @realthunder added support to Links to run Linked Scripted Object's methods() ''' # Add features in the SketchArch External Add-on self.addSketchArchFeatures(obj, linkObj) # Execute features in the SketchArch External Add-on self.executeSketchArchFeatures(obj, linkObj) def getSubVolume(self,obj,plac=None): "returns a subvolume for cutting in a base object" # check if we have a custom subvolume if hasattr(obj,"Subvolume"): if obj.Subvolume: if hasattr(obj.Subvolume,'Shape'): if not obj.Subvolume.Shape.isNull(): sh = obj.Subvolume.Shape.copy() pl = FreeCAD.Placement(sh.Placement) pl = obj.Placement.multiply(pl) if plac: pl = plac.multiply(pl) sh.Placement = pl return sh # getting extrusion depth base = None if obj.Base: base = obj.Base width = 0 if hasattr(obj,"HoleDepth"): # the code have not checked whether this is a clone and use the original's HoleDepth; if HoleDepth is set in this object, even it is a clone, the original's HoleDepth is overridden if obj.HoleDepth.Value: width = obj.HoleDepth.Value if not width: if base: b = base.Shape.BoundBox width = max(b.XLength,b.YLength,b.ZLength) if not width: if Draft.isClone(obj,"Window"): # check whether this is a clone and use the original's HoleDepth or Shape's Boundbox if hasattr(obj,"CloneOf"): orig = obj.CloneOf else: orig = obj.Objects[0] if orig.Base: base = orig.Base if hasattr(orig,"HoleDepth"): if orig.HoleDepth.Value: width = orig.HoleDepth.Value if not width: if base: b = base.Shape.BoundBox width = max(b.XLength,b.YLength,b.ZLength) if not width: width = 1.1112 # some weird value to have little chance to overlap with an existing face if not base: if Draft.isClone(obj,"Window"): # if this object has not base, check whether this is a clone and use the original's base if hasattr(obj,"CloneOf"): orig = obj.CloneOf else: orig = obj.Objects[0] # not sure what is this exactly if orig.Base: base = orig.Base else: return None # finding which wire to use to drill the hole f = None if hasattr(obj,"HoleWire"): # the code have not checked whether this is a clone and use the original's HoleWire; if HoleWire is set in this object, even it is a clone, the original's BoundBox/HoleWire is overridden if obj.HoleWire > 0: if obj.HoleWire <= len(base.Shape.Wires): f = base.Shape.Wires[obj.HoleWire-1] if not f: if Draft.isClone(obj,"Window"): # check original HoleWire then if orig.HoleWire > 0: if orig.HoleWire <= len(base.Shape.Wires): f = base.Shape.Wires[obj.HoleWire-1] if not f: # finding biggest wire in the base shape max_length = 0 for w in base.Shape.Wires: if w.BoundBox.DiagonalLength > max_length: max_length = w.BoundBox.DiagonalLength f = w if f: import Part f = Part.Face(f) norm = f.normalAt(0,0) if hasattr(obj,"Normal"): if obj.Normal: if not DraftVecUtils.isNull(obj.Normal): norm = obj.Normal v1 = DraftVecUtils.scaleTo(norm,width) f.translate(v1) v2 = v1.negative() v2 = Vector(v1).multiply(-2) f = f.extrude(v2) if plac: f.Placement = plac else: f.Placement = obj.Placement return f return None def computeAreas(self,obj): return class _ViewProviderWindow(ArchComponent.ViewProviderComponent): "A View Provider for the Window object" def __init__(self,vobj): ArchComponent.ViewProviderComponent.__init__(self,vobj) def getIcon(self): import Arch_rc if hasattr(self,"Object"): if hasattr(self.Object,"CloneOf"): if self.Object.CloneOf: return ":/icons/Arch_Window_Clone.svg" return ":/icons/Arch_Window_Tree.svg" def updateData(self,obj,prop): if prop == "Shape": if obj.Base: if obj.Base.isDerivedFrom("Part::Compound"): if obj.ViewObject.DiffuseColor != obj.Base.ViewObject.DiffuseColor: if len(obj.Base.ViewObject.DiffuseColor) > 1: obj.ViewObject.DiffuseColor = obj.Base.ViewObject.DiffuseColor obj.ViewObject.update() self.colorize(obj) elif prop == "CloneOf": if hasattr(obj,"CloneOf") and obj.CloneOf: mat = None if hasattr(obj,"Material"): if obj.Material: mat = obj.Material if not mat: if obj.ViewObject.DiffuseColor != obj.CloneOf.ViewObject.DiffuseColor: if len(obj.CloneOf.ViewObject.DiffuseColor) > 1: obj.ViewObject.DiffuseColor = obj.CloneOf.ViewObject.DiffuseColor obj.ViewObject.update() def onDelete(self,vobj,subelements): for o in vobj.Object.Hosts: o.touch() return True def onChanged(self,vobj,prop): if prop == "ShapeAppearance": self.colorize(vobj.Object) ArchComponent.ViewProviderComponent.onChanged(self,vobj,prop) def colorize(self,obj): def _shapeAppearanceMaterialIsSame(sapp_mat1, sapp_mat2): for prop in ( "AmbientColor", "DiffuseColor", "EmissiveColor", "Shininess", "SpecularColor", "Transparency", ): if getattr(sapp_mat1, prop) != getattr(sapp_mat2, prop): return False return True def _shapeAppearanceIsSame(sapp1, sapp2): if len(sapp1) != len(sapp2): return False for sapp_mat1, sapp_mat2 in zip(sapp1, sapp2): if not _shapeAppearanceMaterialIsSame(sapp_mat1, sapp_mat2): return False return True if not obj.Shape: return if not obj.Shape.Solids: return # setting different part colors if hasattr(obj, "CloneOf") and obj.CloneOf: obj, clone = obj.CloneOf, obj base_sapp_mat = clone.ViewObject.ShapeAppearance[0] arch_mat = getattr(clone, "Material", None) else: clone = None base_sapp_mat = obj.ViewObject.ShapeAppearance[0] arch_mat = getattr(obj, "Material", None) solids = obj.Shape.copy().Solids sapp = [] for i in range(len(solids)): color = None if obj.WindowParts and len(obj.WindowParts) > i*5: # WindowParts-based window name = obj.WindowParts[(i*5)] mtype = obj.WindowParts[(i*5)+1] color = self.getSolidMaterial(obj,arch_mat,name,mtype) elif obj.Base and hasattr(obj.Base,"Shape"): # Type-based window: obj.Base furnishes the window solids sol1 = self.getSolidSignature(solids[i]) # here we look for all the ways to retrieve a name for each # solid. Currently we look for similar solids in the if hasattr(obj.Base,"Group"): for child in obj.Base.Group: if hasattr(child,"Shape") and child.Shape and child.Shape.Solids: sol2 = self.getSolidSignature(child.Shape) if sol1 == sol2: color = self.getSolidMaterial(obj,arch_mat,child.Label) break if color is None: typeidx = (i*5)+1 if typeidx < len(obj.WindowParts): typ = obj.WindowParts[typeidx] if typ == WindowPartTypes[2]: # "Glass panel" color = ArchCommands.getDefaultColor("WindowGlass") if color is None: sapp_mat = base_sapp_mat else: sapp_mat = FreeCAD.Material() # ShapeAppearance material with default v0.21 properties. sapp_mat.DiffuseColor = color[:3] + (0.0, ) sapp_mat.Transparency = color[3] sapp.extend((sapp_mat, ) * len(solids[i].Faces)) if clone is not None: obj = clone if not _shapeAppearanceIsSame(obj.ViewObject.ShapeAppearance, sapp): obj.ViewObject.ShapeAppearance = sapp def getSolidSignature(self,solid): """Returns a tuple defining as uniquely as possible a solid""" return (solid.ShapeType,round(solid.Volume,3),round(solid.Area,3),round(solid.Length,3)) def getSolidMaterial(self,obj,arch_mat,name,mtype=None): color = None if arch_mat is not None and hasattr(arch_mat,"Materials") and arch_mat.Names: mat = None if name in arch_mat.Names: mat = arch_mat.Materials[arch_mat.Names.index(name)] elif mtype is not None and (mtype in arch_mat.Names): mat = arch_mat.Materials[arch_mat.Names.index(mtype)] if mat: if 'DiffuseColor' in mat.Material: if "(" in mat.Material['DiffuseColor']: color = tuple([float(f) for f in mat.Material['DiffuseColor'].strip("()").split(",")]) if color and ('Transparency' in mat.Material): t = float(mat.Material['Transparency'])/100.0 color = color[:3] + (t, ) return color def getHingeEdgeIndices(self): """returns a list of hinge edge indices (0-based)""" # WindowParts example: # ["OuterFrame", "Frame", "Wire0,Wire1", "100.0+V", "0.00+V", # "InnerFrame", "Frame", "Wire2,Wire3,Edge8,Mode1", "100.0", "100.0+V", # "InnerGlass", "Glass panel", "Wire3", "10.0", "150.0+V"] idxs = [] parts = self.Object.WindowParts for i in range(len(parts) // 5): for s in parts[(i * 5) + 2].split(","): if "Edge" in s: idxs.append(int(s[4:]) - 1) # Edge indices in string are 1-based. return idxs def setEdit(self, vobj, mode): if mode != 0: return None taskd = _ArchWindowTaskPanel() taskd.obj = self.Object self.sets = [vobj.DisplayMode,vobj.Transparency] vobj.DisplayMode = "Shaded" vobj.Transparency = 80 if self.Object.Base: self.Object.Base.ViewObject.show() taskd.update() FreeCADGui.Control.showDialog(taskd) return True def unsetEdit(self, vobj, mode): if mode != 0: return None vobj.DisplayMode = self.sets[0] vobj.Transparency = self.sets[1] vobj.DiffuseColor = vobj.DiffuseColor # reset face colors if self.Object.Base: self.Object.Base.ViewObject.hide() FreeCADGui.Control.closeDialog() return True def setupContextMenu(self, vobj, menu): hingeIdxs = self.getHingeEdgeIndices() super().contextMenuAddEdit(menu) if len(hingeIdxs) > 0: actionInvertOpening = QtGui.QAction(QtGui.QIcon(":/icons/Arch_Window_Tree.svg"), translate("Arch", "Invert opening direction"), menu) QtCore.QObject.connect(actionInvertOpening, QtCore.SIGNAL("triggered()"), self.invertOpening) menu.addAction(actionInvertOpening) if len(hingeIdxs) == 1: actionInvertHinge = QtGui.QAction(QtGui.QIcon(":/icons/Arch_Window_Tree.svg"), translate("Arch", "Invert hinge position"), menu) QtCore.QObject.connect(actionInvertHinge, QtCore.SIGNAL("triggered()"), self.invertHinge) menu.addAction(actionInvertHinge) super().contextMenuAddToggleSubcomponents(menu) def invertOpening(self): """swaps the opening modes found in this window""" pairs = [["Mode"+str(i),"Mode"+str(i+1)] for i in range(1,len(WindowOpeningModes),2)] self.invertPairs(pairs) def invertHinge(self): """swaps the hinge edge of a single hinge edge window""" idxs = self.getHingeEdgeIndices() if len(idxs) != 1: return idx = idxs[0] end = 0 for wire in self.Object.Base.Shape.Wires: sta = end end += len(wire.Edges) if sta <= idx < end: new = idx + 2 # A rectangular wire is assumed. if not (sta <= new < end): new = idx - 2 break pairs = [["Edge" + str(idx + 1), "Edge" + str(new + 1)]] self.invertPairs(pairs) # Also invert opening direction, so the door still opens towards # the same side of the wall self.invertOpening() def invertPairs(self,pairs): """scans the WindowParts of this window and swaps the two elements of each pair, if found""" if hasattr(self,"Object"): windowparts = self.Object.WindowParts nparts = [] for part in windowparts: for pair in pairs: if pair[0] in part: part = part.replace(pair[0],pair[1]) break elif pair[1] in part: part = part.replace(pair[1],pair[0]) break nparts.append(part) if nparts != self.Object.WindowParts: self.Object.WindowParts = nparts FreeCAD.ActiveDocument.recompute() else: FreeCAD.Console.PrintWarning(translate("Arch","This window has no defined opening")+"\n") class _ArchWindowTaskPanel: '''The TaskPanel for Arch Windows''' def __init__(self): self.obj = None self.baseform = QtGui.QWidget() self.baseform.setObjectName("TaskPanel") self.grid = QtGui.QGridLayout(self.baseform) self.grid.setObjectName("grid") self.title = QtGui.QLabel(self.baseform) self.grid.addWidget(self.title, 0, 0, 1, 7) self.basepanel = ArchComponent.ComponentTaskPanel() self.form = [self.baseform,self.basepanel.baseform] # base object self.tree = QtGui.QTreeWidget(self.baseform) self.grid.addWidget(self.tree, 1, 0, 1, 7) self.tree.setColumnCount(1) self.tree.setMaximumSize(QtCore.QSize(500,24)) self.tree.header().hide() # hole self.holeLabel = QtGui.QLabel(self.baseform) self.grid.addWidget(self.holeLabel, 2, 0, 1, 1) self.holeNumber = QtGui.QLineEdit(self.baseform) self.grid.addWidget(self.holeNumber, 2, 2, 1, 3) self.holeButton = QtGui.QPushButton(self.baseform) self.grid.addWidget(self.holeButton, 2, 6, 1, 1) self.holeButton.setEnabled(True) # trees self.wiretree = QtGui.QTreeWidget(self.baseform) self.grid.addWidget(self.wiretree, 3, 0, 1, 3) self.wiretree.setColumnCount(1) self.wiretree.setSelectionMode(QtGui.QAbstractItemView.MultiSelection) self.comptree = QtGui.QTreeWidget(self.baseform) self.grid.addWidget(self.comptree, 3, 4, 1, 3) self.comptree.setColumnCount(1) # buttons self.addButton = QtGui.QPushButton(self.baseform) self.addButton.setObjectName("addButton") self.addButton.setIcon(QtGui.QIcon(":/icons/Arch_Add.svg")) self.grid.addWidget(self.addButton, 4, 0, 1, 1) self.addButton.setMaximumSize(QtCore.QSize(70,40)) self.editButton = QtGui.QPushButton(self.baseform) self.editButton.setObjectName("editButton") self.editButton.setIcon(QtGui.QIcon(":/icons/Draft_Edit.svg")) self.grid.addWidget(self.editButton, 4, 2, 1, 3) self.editButton.setMaximumSize(QtCore.QSize(60,40)) self.editButton.setEnabled(False) self.delButton = QtGui.QPushButton(self.baseform) self.delButton.setObjectName("delButton") self.delButton.setIcon(QtGui.QIcon(":/icons/Arch_Remove.svg")) self.grid.addWidget(self.delButton, 4, 6, 1, 1) self.delButton.setMaximumSize(QtCore.QSize(70,40)) self.delButton.setEnabled(False) # invert buttons self.invertOpeningButton = QtGui.QPushButton(self.baseform) self.invertOpeningButton.setIcon(QtGui.QIcon(":/icons/Arch_Window_Tree.svg")) self.invertOpeningButton.clicked.connect(self.invertOpening) self.grid.addWidget(self.invertOpeningButton, 5, 0, 1, 7) self.invertOpeningButton.setEnabled(False) self.invertHingeButton = QtGui.QPushButton(self.baseform) self.invertHingeButton.setIcon(QtGui.QIcon(":/icons/Arch_Window_Tree.svg")) self.invertHingeButton.clicked.connect(self.invertHinge) self.grid.addWidget(self.invertHingeButton, 6, 0, 1, 7) self.invertHingeButton.setEnabled(False) # add new ui = FreeCADGui.UiLoader() self.newtitle = QtGui.QLabel(self.baseform) self.new1 = QtGui.QLabel(self.baseform) self.new2 = QtGui.QLabel(self.baseform) self.new3 = QtGui.QLabel(self.baseform) self.new4 = QtGui.QLabel(self.baseform) self.new5 = QtGui.QLabel(self.baseform) self.new6 = QtGui.QLabel(self.baseform) self.new7 = QtGui.QLabel(self.baseform) self.field1 = QtGui.QLineEdit(self.baseform) self.field2 = QtGui.QComboBox(self.baseform) self.field3 = QtGui.QLineEdit(self.baseform) self.field4 = ui.createWidget("Gui::InputField") self.field5 = ui.createWidget("Gui::InputField") self.field6 = QtGui.QPushButton(self.baseform) self.field7 = QtGui.QComboBox(self.baseform) self.addp4 = QtGui.QCheckBox(self.baseform) self.addp5 = QtGui.QCheckBox(self.baseform) self.createButton = QtGui.QPushButton(self.baseform) self.createButton.setObjectName("createButton") self.createButton.setIcon(QtGui.QIcon(":/icons/Arch_Add.svg")) self.grid.addWidget(self.newtitle, 7, 0, 1, 7) self.grid.addWidget(self.new1, 8, 0, 1, 1) self.grid.addWidget(self.field1, 8, 2, 1, 5) self.grid.addWidget(self.new2, 9, 0, 1, 1) self.grid.addWidget(self.field2, 9, 2, 1, 5) self.grid.addWidget(self.new3, 10, 0, 1, 1) self.grid.addWidget(self.field3, 10, 2, 1, 5) self.grid.addWidget(self.new4, 11, 0, 1, 1) self.grid.addWidget(self.field4, 11, 2, 1, 4) self.grid.addWidget(self.addp4, 11, 6, 1, 1) self.grid.addWidget(self.new5, 12, 0, 1, 1) self.grid.addWidget(self.field5, 12, 2, 1, 4) self.grid.addWidget(self.addp5, 12, 6, 1, 1) self.grid.addWidget(self.new6, 13, 0, 1, 1) self.grid.addWidget(self.field6, 13, 2, 1, 5) self.grid.addWidget(self.new7, 14, 0, 1, 1) self.grid.addWidget(self.field7, 14, 2, 1, 5) self.grid.addWidget(self.createButton, 15, 0, 1, 7) self.newtitle.setVisible(False) self.new1.setVisible(False) self.new2.setVisible(False) self.new3.setVisible(False) self.new4.setVisible(False) self.new5.setVisible(False) self.new6.setVisible(False) self.new7.setVisible(False) self.field1.setVisible(False) self.field2.setVisible(False) for t in WindowPartTypes: self.field2.addItem("") self.field3.setVisible(False) self.field3.setReadOnly(True) self.field4.setVisible(False) self.field5.setVisible(False) self.field6.setVisible(False) self.field7.setVisible(False) self.addp4.setVisible(False) self.addp5.setVisible(False) for t in WindowOpeningModes: self.field7.addItem("") self.createButton.setVisible(False) QtCore.QObject.connect(self.holeButton, QtCore.SIGNAL("clicked()"), self.selectHole) QtCore.QObject.connect(self.holeNumber, QtCore.SIGNAL("textEdited(QString)"), self.setHoleNumber) QtCore.QObject.connect(self.addButton, QtCore.SIGNAL("clicked()"), self.addElement) QtCore.QObject.connect(self.delButton, QtCore.SIGNAL("clicked()"), self.removeElement) QtCore.QObject.connect(self.editButton, QtCore.SIGNAL("clicked()"), self.editElement) QtCore.QObject.connect(self.createButton, QtCore.SIGNAL("clicked()"), self.create) QtCore.QObject.connect(self.comptree, QtCore.SIGNAL("itemClicked(QTreeWidgetItem*,int)"), self.check) QtCore.QObject.connect(self.wiretree, QtCore.SIGNAL("itemClicked(QTreeWidgetItem*,int)"), self.select) QtCore.QObject.connect(self.field6, QtCore.SIGNAL("clicked()"), self.addEdge) self.update() FreeCADGui.Selection.clearSelection() def isAllowedAlterSelection(self): return True def isAllowedAlterView(self): return True def getStandardButtons(self): return QtGui.QDialogButtonBox.Close def check(self,wid,col): self.editButton.setEnabled(True) self.delButton.setEnabled(True) def select(self,wid,col): FreeCADGui.Selection.clearSelection() ws = '' for it in self.wiretree.selectedItems(): if ws: ws += "," ws += str(it.text(0)) w = int(str(it.text(0)[4:])) if self.obj: if self.obj.Base: edges = self.obj.Base.Shape.Wires[w].Edges for e in edges: for i in range(len(self.obj.Base.Shape.Edges)): if e.hashCode() == self.obj.Base.Shape.Edges[i].hashCode(): FreeCADGui.Selection.addSelection(self.obj.Base,"Edge"+str(i+1)) self.field3.setText(ws) def selectHole(self): "takes a selected edge to determine current Hole Wire" s = FreeCADGui.Selection.getSelectionEx() if s and self.obj: if s[0].SubElementNames: if "Edge" in s[0].SubElementNames[0]: for i,w in enumerate(self.obj.Base.Shape.Wires): for e in w.Edges: if e.hashCode() == s[0].SubObjects[0].hashCode(): self.holeNumber.setText(str(i+1)) self.setHoleNumber(str(i+1)) break def setHoleNumber(self,val): "sets the HoleWire obj property" if val.isdigit(): val = int(val) if self.obj: if not hasattr(self.obj,"HoleWire"): self.obj.addProperty("App::PropertyInteger","HoleWire","Arch",QT_TRANSLATE_NOOP("App::Property","The number of the wire that defines the hole. A value of 0 means automatic")) self.obj.HoleWire = val def getIcon(self,obj): if hasattr(obj.ViewObject,"Proxy"): if hasattr(obj.ViewObject.Proxy,"getIcon"): return QtGui.QIcon(obj.ViewObject.Proxy.getIcon()) elif obj.isDerivedFrom("Sketcher::SketchObject"): return QtGui.QIcon(":/icons/Sketcher_Sketch.svg") elif hasattr(obj.ViewObject, "Icon"): return QtGui.QIcon(obj.ViewObject.Icon) return QtGui.QIcon(":/icons/Part_3D_object.svg") def update(self): 'fills the tree widgets' self.tree.clear() self.wiretree.clear() self.comptree.clear() if self.obj: if self.obj.Base: item = QtGui.QTreeWidgetItem(self.tree) item.setText(0,self.obj.Base.Name) item.setIcon(0,self.getIcon(self.obj.Base)) if hasattr(self.obj.Base,'Shape'): i = 0 for w in self.obj.Base.Shape.Wires: if w.isClosed(): item = QtGui.QTreeWidgetItem(self.wiretree) item.setText(0,"Wire" + str(i)) item.setIcon(0,QtGui.QIcon(":/icons/Draft_Draft.svg")) i += 1 if self.obj.WindowParts: for p in range(0,len(self.obj.WindowParts),5): item = QtGui.QTreeWidgetItem(self.comptree) item.setText(0,self.obj.WindowParts[p]) item.setIcon(0, QtGui.QIcon(":/icons/Part_3D_object.svg")) if hasattr(self.obj,"HoleWire"): self.holeNumber.setText(str(self.obj.HoleWire)) else: self.holeNumber.setText("0") self.retranslateUi(self.baseform) self.basepanel.obj = self.obj self.basepanel.update() for wp in self.obj.WindowParts: if ("Edge" in wp) and ("Mode" in wp): self.invertOpeningButton.setEnabled(True) self.invertHingeButton.setEnabled(True) break def addElement(self): 'opens the component creation dialog' self.field1.setText('') self.field3.setText('') self.field4.setText('') self.field5.setText('') self.field6.setText(QtGui.QApplication.translate("Arch", "Get selected edge", None)) self.field7.setCurrentIndex(0) self.addp4.setChecked(False) self.addp5.setChecked(False) self.newtitle.setVisible(True) self.new1.setVisible(True) self.new2.setVisible(True) self.new3.setVisible(True) self.new4.setVisible(True) self.new5.setVisible(True) self.new6.setVisible(True) self.new7.setVisible(True) self.field1.setVisible(True) self.field2.setVisible(True) self.field3.setVisible(True) self.field4.setVisible(True) self.field5.setVisible(True) self.field6.setVisible(True) self.field7.setVisible(True) self.addp4.setVisible(True) self.addp5.setVisible(True) self.createButton.setVisible(True) self.addButton.setEnabled(False) self.editButton.setEnabled(False) self.delButton.setEnabled(False) def removeElement(self): for it in self.comptree.selectedItems(): comp = str(it.text(0)) if self.obj: p = self.obj.WindowParts if comp in self.obj.WindowParts: ind = self.obj.WindowParts.index(comp) for i in range(5): p.pop(ind) self.obj.WindowParts = p self.update() self.editButton.setEnabled(False) self.delButton.setEnabled(False) def editElement(self): for it in self.comptree.selectedItems(): self.addElement() comp = str(it.text(0)) if self.obj: if comp in self.obj.WindowParts: ind = self.obj.WindowParts.index(comp) self.field6.setText(QtGui.QApplication.translate("Arch", "Get selected edge", None)) self.field7.setCurrentIndex(0) for i in range(5): f = getattr(self,"field"+str(i+1)) t = self.obj.WindowParts[ind+i] if i == 1: # special behaviour for types if t in WindowPartTypes: f.setCurrentIndex(WindowPartTypes.index(t)) else: f.setCurrentIndex(0) elif i == 2: wires = [] for l in t.split(","): if "Wire" in l: wires.append(l) elif "Edge" in l: self.field6.setText(l) elif "Mode" in l: if int(l[4:]) < len(WindowOpeningModes): self.field7.setCurrentIndex(int(l[4:])) else: # Ignore modes not listed in WindowOpeningModes self.field7.setCurrentIndex(0) if wires: f.setText(",".join(wires)) elif i in [3,4]: if "+V" in t: t = t[:-2] if i == 3: self.addp4.setChecked(True) else: self.addp5.setChecked(True) else: if i == 3: self.addp4.setChecked(False) else: self.addp5.setChecked(False) f.setProperty("text",FreeCAD.Units.Quantity(float(t),FreeCAD.Units.Length).UserString) else: f.setText(t) def create(self): 'adds a new component' # testing if fields are ok ok = True ar = [] for i in range(5): if i == 1: # type (1) n = getattr(self,"field"+str(i+1)).currentIndex() if n in range(len(WindowPartTypes)): t = WindowPartTypes[n] else: # if type was not specified or is invalid, we set a default t = WindowPartTypes[0] else: # name (0) t = str(getattr(self,"field"+str(i+1)).property("text")) if t in WindowPartTypes: t = t + "_" # avoiding part names similar to types if t == "": if not(i in [1,5]): ok = False else: if i > 2: # thickness (3), offset (4) try: q = FreeCAD.Units.Quantity(t) t = str(q.Value) if i == 3: if self.addp4.isChecked(): t += "+V" if i == 4: if self.addp5.isChecked(): t += "+V" except (ValueError,TypeError): ok = False elif i == 2: # check additional opening parameters hinge = self.field6.property("text") n = self.field7.currentIndex() if (hinge.startswith("Edge")) and (n > 0): # remove accelerator added by Qt hinge = hinge.replace("&","") t += "," + hinge + ",Mode" + str(n) ar.append(t) if ok: if self.obj: parts = self.obj.WindowParts if ar[0] in parts: b = parts.index(ar[0]) for i in range(5): parts[b+i] = ar[i] else: parts.extend(ar) self.obj.WindowParts = parts self.update() else: FreeCAD.Console.PrintWarning(translate("Arch", "Unable to create component")+"\n") self.newtitle.setVisible(False) self.new1.setVisible(False) self.new2.setVisible(False) self.new3.setVisible(False) self.new4.setVisible(False) self.new5.setVisible(False) self.new6.setVisible(False) self.new7.setVisible(False) self.field1.setVisible(False) self.field2.setVisible(False) self.field3.setVisible(False) self.field4.setVisible(False) self.field5.setVisible(False) self.field6.setVisible(False) self.field7.setVisible(False) self.addp4.setVisible(False) self.addp5.setVisible(False) self.createButton.setVisible(False) self.addButton.setEnabled(True) def addEdge(self): for sel in FreeCADGui.Selection.getSelectionEx(): for sub in sel.SubElementNames: if "Edge" in sub: self.field6.setText(sub) return def reject(self): FreeCAD.ActiveDocument.recompute() FreeCADGui.ActiveDocument.resetEdit() return True def retranslateUi(self, TaskPanel): TaskPanel.setWindowTitle(QtGui.QApplication.translate("Arch", "Window elements", None)) self.holeLabel.setText(QtGui.QApplication.translate("Arch", "Hole wire", None)) self.holeNumber.setToolTip(QtGui.QApplication.translate("Arch", "The number of the wire that defines a hole in the host object. A value of zero will automatically adopt the largest wire", None)) self.holeButton.setText(QtGui.QApplication.translate("Arch", "Pick selected", None)) self.delButton.setText(QtGui.QApplication.translate("Arch", "Remove", None)) self.addButton.setText(QtGui.QApplication.translate("Arch", "Add", None)) self.editButton.setText(QtGui.QApplication.translate("Arch", "Edit", None)) self.createButton.setText(QtGui.QApplication.translate("Arch", "Create/update component", None)) self.title.setText(QtGui.QApplication.translate("Arch", "Base 2D object", None)) self.wiretree.setHeaderLabels([QtGui.QApplication.translate("Arch", "Wires", None)]) self.comptree.setHeaderLabels([QtGui.QApplication.translate("Arch", "Components", None)]) self.newtitle.setText(QtGui.QApplication.translate("Arch", "Create new component", None)) self.new1.setText(QtGui.QApplication.translate("Arch", "Name", None)) self.new2.setText(QtGui.QApplication.translate("Arch", "Type", None)) self.new3.setText(QtGui.QApplication.translate("Arch", "Wires", None)) self.new4.setText(QtGui.QApplication.translate("Arch", "Thickness", None)) self.new5.setText(QtGui.QApplication.translate("Arch", "Offset", None)) self.new6.setText(QtGui.QApplication.translate("Arch", "Hinge", None)) self.new7.setText(QtGui.QApplication.translate("Arch", "Opening mode", None)) self.addp4.setText(QtGui.QApplication.translate("Arch", "+ default", None)) self.addp4.setToolTip(QtGui.QApplication.translate("Arch", "If this is checked, the default Frame value of this window will be added to the value entered here", None)) self.addp5.setText(QtGui.QApplication.translate("Arch", "+ default", None)) self.addp5.setToolTip(QtGui.QApplication.translate("Arch", "If this is checked, the default Offset value of this window will be added to the value entered here", None)) self.field6.setText(QtGui.QApplication.translate("Arch", "Get selected edge", None)) self.field6.setToolTip(QtGui.QApplication.translate("Arch", "Press to retrieve the selected edge", None)) self.invertOpeningButton.setText(QtGui.QApplication.translate("Arch", "Invert opening direction", None)) self.invertHingeButton.setText(QtGui.QApplication.translate("Arch", "Invert hinge position", None)) for i in range(len(WindowPartTypes)): self.field2.setItemText(i, QtGui.QApplication.translate("Arch", WindowPartTypes[i], None)) for i in range(len(WindowOpeningModes)): self.field7.setItemText(i, QtGui.QApplication.translate("Arch", WindowOpeningModes[i], None)) def invertOpening(self): if self.obj: self.obj.ViewObject.Proxy.invertOpening() def invertHinge(self): if self.obj: self.obj.ViewObject.Proxy.invertHinge()