# /********************************************************************** # * * # * Copyright (c) 2021 Javier BraƱa * # * * # * 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 ArchComponent import FreeCAD import Part if FreeCAD.GuiUp: import FreeCADGui from PySide import QtCore, QtGui, QtSvg from PySide.QtCore import QT_TRANSLATE_NOOP import PySide.QtGui as QtGui import draftguitools.gui_trackers as DraftTrackers else: # \cond def translate(ctxt, txt): return txt def QT_TRANSLATE_NOOP(ctxt, txt): return txt # \endcond try: _fromUtf8 = QtCore.QString.fromUtf8 except AttributeError: def _fromUtf8(s): return s import os import PVPlantResources from PVPlantResources import DirIcons as DirIcons def makePVPlantFence(): obj = FreeCAD.ActiveDocument.addObject('Part::FeaturePython', 'FenceGate') FenceGate(obj) if FreeCAD.GuiUp: ViewProviderGate(obj.ViewObject) return obj class FenceGate(ArchComponent.Component): def __init__(self, obj): ArchComponent.Component.__init__(self, obj) self.setProperties(obj) def setProperties(self, obj): pl = obj.PropertiesList if not ("GateType" in pl): obj.addProperty("App::PropertyEnumeration", "GateType", "Gate", QT_TRANSLATE_NOOP("App::Property", "The facemaker type to use to build the profile of this object") ).GateType = ["Single", "Double"] if not ("GateWidth" in pl): obj.addProperty("App::PropertyDistance", "GateWidth", "Gate", "Base wire" ).GateWidth = 3000 if not ("GateHeight" in pl): obj.addProperty("App::PropertyDistance", "GateHeight", "Gate", "Base wire" ).GateHeight = 2000 if not ("PostWidth" in pl): obj.addProperty("App::PropertyDistance", "PostWidth", "Gate", "Base wire" ).PostWidth = 60 if not ("PostHeight" in pl): obj.addProperty("App::PropertyDistance", "PostHeight", "Gate", "Base wire" ).PostHeight = 2000 if not "PostDepth" in pl: obj.addProperty("App::PropertyLength", "PostDepth", "Fence", "A single fence post").PostDepth = 700 self.Type = "PVPlatFenceGate" #obj.Type = self.Type obj.Proxy = self self.obj = obj def __getstate__(self): if hasattr(self, 'sectionFaceNumbers'): return (self.sectionFaceNumbers) return None def __setstate__(self, state): if state is not None and isinstance(state, tuple): self.sectionFaceNumbers = state[0] return None def onDocumentRestored(self, obj): """Method run when the document is restored.""" self.setProperties(obj) def onChanged(self, obj, prop): '''Do something when a property has changed''' def execute(self, obj): tubewidth = 50 gap = 20 posts = Part.makeCompound([]) gates = Part.makeCompound([]) pl = obj.Placement post = Part.makeBox(obj.PostWidth.Value, obj.PostWidth.Value, obj.PostHeight.Value + obj.PostDepth.Value) gate = Part.makeBox(obj.GateWidth.Value, tubewidth, obj.GateHeight.Value) cut = Part.makeBox(obj.GateWidth.Value - tubewidth * 2, tubewidth, obj.GateHeight.Value - tubewidth * 2) cut.Placement.Base += FreeCAD.Vector(tubewidth, 0, tubewidth) gate = gate.cut(cut) if obj.GateType == "Single": post.Placement.Base = FreeCAD.Vector(-(obj.PostWidth.Value + gap + obj.GateWidth.Value / 2), -obj.PostWidth.Value / 2, -obj.PostDepth.Value) posts.add(post) post = post.copy() post.Placement.Base.x = -(post.Placement.Base.x + obj.PostWidth.Value) posts.add(post) gate.Placement.Base += FreeCAD.Vector(-obj.GateWidth.Value / 2, obj.PostWidth.Value / 2 - tubewidth, 0) gates.add(gate) else: post.Placement.Base = FreeCAD.Vector(-(obj.PostWidth.Value + gap * 1.5 + obj.GateWidth.Value), -obj.PostWidth.Value / 2, -obj.PostDepth.Value) posts.add(post) post = post.copy() post.Placement.Base.x = -(post.Placement.Base.x + obj.PostWidth.Value) posts.add(post) gate.Placement.Base += FreeCAD.Vector(-(obj.GateWidth.Value + gap * 0.5), obj.PostWidth.Value / 2 - tubewidth, 0) gates.add(gate) gate = gate.copy() gate.Placement.Base.x = gap * 0.5 gates.add(gate) obj.Shape = Part.makeCompound([gates, posts]) obj.Placement = pl class ViewProviderGate: def __init__(self, vobj): ''' Set view properties. ''' vobj.Proxy = self def attach(self, vobj): self.Object = vobj.Object def getIcon(self): ''' Return object treeview icon. ''' return str(os.path.join(DirIcons, "gate.svg")) def claimChildren(self): """ Provides object grouping """ children = [] if self.Object.Base: children.append(self.Object.Base) return children class _CommandPVPlantGate: "the PVPlant Fence command definition" def __init__(self): ''' p = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch") self.Thickness = p.GetFloat("WindowThickness", 50) self.Width = p.GetFloat("WindowWidth", 1000) self.Height = p.GetFloat("DoorHeight", 2100)''' self.Width = 0 self.Height = 0 self.Length = 0 self.tracker = None def GetResources(self): return {'Pixmap': str(os.path.join(DirIcons, "gate.svg")), 'Accel': "C, F", 'MenuText': QT_TRANSLATE_NOOP("PVPlantFence", "Gate"), 'ToolTip': QT_TRANSLATE_NOOP("PVPlantFence", "Creates a fence object from a selected section, post and path")} def IsActive(self): return not FreeCAD.ActiveDocument is None def Activated(self): self.tracker = DraftTrackers.boxTracker() self.tracker.length(self.Length) self.tracker.width(self.Width) self.tracker.height(self.Height) self.tracker.on() FreeCAD.Console.PrintMessage("Choose a face on an existing object or select a preset" + "\n") FreeCADGui.Snapper.getPoint(callback=self.getPoint, movecallback=self.update, extradlg=self.taskbox()) def getPoint(self, point=None, obj=None): "this function is called by the snapper when it has a 3D point" if obj.Name[:5] == "Fence": self.tracker.finalize() if point is None: return ver = Part.Vertex(point) dist = ver.distToShape(obj.Base.Shape) point1 = dist[1][0][1] ed = None if dist[2][0][3] == "Edge": ed = obj.Base.Shape.Edges[int(dist[-1][0][4])] vec = ed.Vertexes[1].Point.sub(ed.Vertexes[0].Point) FreeCAD.ActiveDocument.openTransaction("Create Gate") gate = makePVPlantFence() try: import MeshPart as mp point1 = mp.projectPointsOnMesh([point1,], FreeCAD.ActiveDocument.Terrain.Mesh, FreeCAD.Vector(0, 0, 1))[0] except: FreeCAD.Console.PrintError("No se puede encontrar punto 3D.." + "\n") pass gate.Placement.Base = point1 gate.Placement.Rotation = FreeCAD.Rotation(FreeCAD.Vector(-1, 0, 0), vec) tmp = obj.Gate tmp.append(gate) obj.Gate = tmp FreeCAD.ActiveDocument.commitTransaction() FreeCAD.ActiveDocument.recompute() return else: FreeCADGui.Snapper.getPoint(callback=self.getPoint, movecallback=self.update, extradlg=self.taskbox()) def update(self, point, info): ''' this function is called by the Snapper when the mouse is moved ''' delta = FreeCAD.Vector(self.Length / 2, self.Width / 2, self.Height / 2) rot = FreeCAD.Rotation() #self.tracker.setRotation(rot) self.tracker.pos(point) return delta = FreeCAD.Vector(self.Length / 2, self.Width / 2, self.Height / 2) delta = delta.add(FreeCAD.Vector(0, 0, self.Sill)) rot = FreeCAD.Rotation() if info: if "Face" in info['Component']: import WorkingPlane o = FreeCAD.ActiveDocument.getObject(info['Object']) self.baseFace = [o, int(info['Component'][4:]) - 1] # print("switching to ",o.Label," face ",self.baseFace[1]) f = o.Shape.Faces[self.baseFace[1]] p = WorkingPlane.getPlacementFromFace(f, rotated=True) if p: rot = p.Rotation self.tracker.setRotation(rot) r = self.tracker.trans.rotation.getValue().getValue() if r != (0, 0, 0, 1): delta = FreeCAD.Rotation(r[0], r[1], r[2], r[3]).multVec(FreeCAD.Vector(delta.x, -delta.y, -delta.z)) self.tracker.pos(point.add(delta)) def taskbox(self): "sets up a taskbox widget" w = FreeCADGui.PySideUic.loadUi(PVPlantResources.__dir__ + "/PVPlantFenceGate.ui") self.Width = 80 self.Height = 2000 self.Length = 6146 return w w = QtGui.QWidget() ui = FreeCADGui.UiLoader() w.setWindowTitle("Window options") grid = QtGui.QGridLayout(w) # include box include = QtGui.QCheckBox(translate("Arch","Auto include in host object")) include.setChecked(True) grid.addWidget(include,0,0,1,2) QtCore.QObject.connect(include,QtCore.SIGNAL("stateChanged(int)"),self.setInclude) # sill height labels = QtGui.QLabel(translate("Arch","Sill height")) values = ui.createWidget("Gui::InputField") grid.addWidget(labels,1,0,1,1) grid.addWidget(values,1,1,1,1) QtCore.QObject.connect(values,QtCore.SIGNAL("valueChanged(double)"),self.setSill) # check for Parts library and Arch presets # because of the use of FreeCADGui.doCommand() backslashes in the # paths in librarypresets need to be double escaped "\\\\", so let's # use forward slashes instead... self.librarypresets = [] librarypath = FreeCAD.ParamGet("User parameter:Plugins/parts_library").GetString("destination", "") # librarypath should have only forward slashes already, but let's use replace() anyway just to be sure: librarypath = librarypath.replace("\\", "/") + "/Architectural Parts" presetdir = FreeCAD.getUserAppDataDir().replace("\\", "/") + "/Arch" for path in [librarypath, presetdir]: if os.path.isdir(path): for wtype in ["Windows", "Doors"]: wdir = path + "/" + wtype if os.path.isdir(wdir): for subtype in os.listdir(wdir): subdir = wdir + "/" + subtype if os.path.isdir(subdir): for subfile in os.listdir(subdir): if (os.path.isfile(subdir + "/" + subfile) and subfile.lower().endswith(".fcstd")): self.librarypresets.append([wtype + " - " + subtype + " - " + subfile[:-6], subdir + "/" + subfile]) # presets box labelp = QtGui.QLabel(translate("Arch","Preset")) valuep = QtGui.QComboBox() valuep.setMinimumContentsLength(6) valuep.setSizeAdjustPolicy(QtGui.QComboBox.AdjustToContents) valuep.addItems(WindowPresets) valuep.setCurrentIndex(self.Preset) grid.addWidget(labelp,2,0,1,1) grid.addWidget(valuep,2,1,1,1) QtCore.QObject.connect(valuep,QtCore.SIGNAL("currentIndexChanged(int)"),self.setPreset) for it in self.librarypresets: valuep.addItem(it[0]) # image display self.pic = QtGui.QLabel() grid.addWidget(self.pic,3,0,1,2) self.pic.setFixedHeight(128) self.pic.hide() # SVG display self.im = QtSvg.QSvgWidget(":/ui/ParametersWindowFixed.svg") self.im.setMaximumWidth(200) self.im.setMinimumHeight(120) grid.addWidget(self.im,4,0,1,2) #self.im.hide() # parameters i = 5 for param in self.wparams: lab = QtGui.QLabel(translate("Arch",param)) setattr(self,"val"+param,ui.createWidget("Gui::InputField")) wid = getattr(self,"val"+param) if param == "Width": wid.setText(FreeCAD.Units.Quantity(self.Width,FreeCAD.Units.Length).UserString) elif param == "Height": wid.setText(FreeCAD.Units.Quantity(self.Height,FreeCAD.Units.Length).UserString) elif param == "O1": n = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch").GetFloat("WindowO1",0.0) wid.setText(FreeCAD.Units.Quantity(n,FreeCAD.Units.Length).UserString) setattr(self,param,n) elif param == "W1": n = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch").GetFloat("WindowW1",self.Thickness*2) wid.setText(FreeCAD.Units.Quantity(n,FreeCAD.Units.Length).UserString) setattr(self,param,n) else: n = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch").GetFloat("Window"+param,self.Thickness) wid.setText(FreeCAD.Units.Quantity(n,FreeCAD.Units.Length).UserString) setattr(self,param,n) grid.addWidget(lab,i,0,1,1) grid.addWidget(wid,i,1,1,1) i += 1 valueChanged = self.getValueChanged(param) FreeCAD.wid = wid QtCore.QObject.connect(getattr(self,"val"+param),QtCore.SIGNAL("valueChanged(double)"), valueChanged) # restore saved states if self.doormode: i = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch").GetInt("DoorPreset",0) d = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch").GetFloat("DoorSill",0) else: i = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch").GetInt("WindowPreset",0) d = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch").GetFloat("WindowSill",0) if i < valuep.count(): valuep.setCurrentIndex(i) values.setText(FreeCAD.Units.Quantity(d,FreeCAD.Units.Length).UserString) return w