FreeCAD - Scripting 06 - Komut Dosyasıyla Yazılmış Nesneler
Cts 26 Şubat 2022Komut Dosyasıyla Yazılmış Nesneler
Takdim
Açıklamalar, ağlar ve parça nesneleri gibi standart nesne türlerinin yanı sıra, FreeCAD ayrıca Python Özellikleri adı verilen %100 python komut dosyasıyla oluşturulmuş parametrik nesneler oluşturmak için harika bir olanak sunar. Bu nesneler, tam olarak diğer herhangi bir FreeCAD nesnesi gibi davranacak ve dosya kaydetme/yükleme sırasında otomatik olarak kaydedilecek ve geri yüklenecektir.
Anlaşılması, bilinmesi gereken bir husus var, o da şudur: FreeCAD dosyaları hiçbir zaman gömülü kod taşımaz, bu güvenlik nedenleriyledir. Parametrik nesneler oluşturmak için yazdığınız Python kodu, hiçbir zaman bir dosyanın içine kaydedilmez. Bu, başka bir makinede (bilgisayarda) böyle bir nesne içeren bir dosyayı açarsanız, bu python kodu o makinede (bilgisayarda) mevcut değilse, nesnenin tamamen yeniden oluşturulmayacağı anlamına gelir. Bu tür nesneleri başkalarına dağıtırsanız (gönderirseniz), Python komut dosyanızı da örneğin bir Makro olarak dağıtmanız, göndermeniz gerekir.
Not: Python kodunu bir App::PropertyPythonObject
ile json
serileştirmeyi kullanarak bir FreeCAD dosyası içinde paketlemek (yerleştirmek) mümkündür, ancak bu kod hiçbir zaman doğrudan çalıştırılamaz ve bu nedenle buradaki amacımız için çok kullanışlı değildir.
Python Özellikleri, tüm FreeCAD özellikleriyle aynı kuralı izler: App
ve GUI
bölümlerine ayrılırlar. App
bölümü, Belge Nesnesi, nesnemizin geometrisini tanımlarken, GUI
bölümü, Görünüm Sağlayıcı Nesnesi, nesnenin ekranda nasıl çizileceğini tanımlar. Görünüm Sağlayıcı Nesnesi, diğer FreeCAD özellikleri gibi, yalnızca FreeCAD'i kendi GUI'sinde çalıştırdığınızda kullanılabilir. Nesnenizi oluşturmak için kullanılabilecek birkaç özellik ve yöntem vardır. Özellikler, FreeCAD'in sunduğu önceden tanımlanmış özellik türlerinden herhangi biri olmalıdır ve kullanıcı tarafından düzenlenebilmeleri için özellik görünümü penceresinde görünecektir. Bu şekilde, FeaturePython
nesneleri gerçekten ve tamamen parametriktir. Object
ve ViewObject
için özellikleri ayrı ayrı tanımlayabilirsiniz.
Basit / Temel Örnek
Aşağıdaki örnek, diğer birkaç örnekle birlikte src/Mod/TemplatePyMod/FeaturePython.py dosyasında bulunabilir:
"""
Examples for a feature class and its view provider.
(c) 2009 Werner Mayer LGPL
"""
__author__ = "Werner Mayer <wmayer@users.sourceforge.net>"
import FreeCAD, Part, math
from FreeCAD import Base
from pivy import coin
class PartFeature:
def __init__(self, obj):
obj.Proxy = self
class Box(PartFeature):
def __init__(self, obj):
PartFeature.__init__(self, obj)
''' Add some custom properties to our box feature '''
obj.addProperty("App::PropertyLength","Length","Box","Length of the box").Length=1.0
obj.addProperty("App::PropertyLength","Width","Box","Width of the box").Width=1.0
obj.addProperty("App::PropertyLength","Height","Box", "Height of the box").Height=1.0
def onChanged(self, fp, prop):
''' Print the name of the property that has changed '''
FreeCAD.Console.PrintMessage("Change property: " + str(prop) + "\n")
def execute(self, fp):
''' Print a short message when doing a recomputation, this method is mandatory '''
FreeCAD.Console.PrintMessage("Recompute Python Box feature\n")
fp.Shape = Part.makeBox(fp.Length,fp.Width,fp.Height)
class ViewProviderBox:
def __init__(self, obj):
''' Set this object to the proxy object of the actual view provider '''
obj.Proxy = self
def attach(self, obj):
''' Setup the scene sub-graph of the view provider, this method is mandatory '''
return
def updateData(self, fp, prop):
''' If a property of the handled feature has changed we have the chance to handle this here '''
return
def getDisplayModes(self,obj):
''' Return a list of display modes. '''
modes=[]
return modes
def getDefaultDisplayMode(self):
''' Return the name of the default display mode. It must be defined in getDisplayModes. '''
return "Shaded"
def setDisplayMode(self,mode):
''' Map the display mode defined in attach with those defined in getDisplayModes.
Since they have the same names nothing needs to be done. This method is optional.
'''
return mode
def onChanged(self, vp, prop):
''' Print the name of the property that has changed '''
FreeCAD.Console.PrintMessage("Change property: " + str(prop) + "\n")
def getIcon(self):
''' Return the icon in XMP format which will appear in the tree view. This method is optional
and if not defined a default icon is shown.
'''
return """
/* XPM */
static const char * ViewProviderBox_xpm[] = {
"16 16 6 1",
" c None",
". c #141010",
"+ c #615BD2",
"@ c #C39D55",
"# c #000000",
"$ c #57C355",
" ........",
" ......++..+..",
" .@@@@.++..++.",
" .@@@@.++..++.",
" .@@ .++++++.",
" ..@@ .++..++.",
"###@@@@ .++..++.",
"##$.@@$#.++++++.",
"#$#$.$$$........",
"#$$####### ",
"#$$#$$$$$# ",
"#$$#$$$$$# ",
"#$$#$$$$$# ",
" #$#$$$$$# ",
" ##$$$$$# ",
" ####### "};
"""
def __getstate__(self):
''' When saving the document this object gets stored using Python's cPickle module.
Since we have some un-pickable here -- the Coin stuff -- we must define this method
to return a tuple of all pickable objects or None.
'''
return None
def __setstate__(self,state):
''' When restoring the pickled object from document we have the chance to set some
internals here. Since no data were pickled nothing needs to be done here.
'''
return None
def makeBox():
doc=FreeCAD.newDocument()
a=FreeCAD.ActiveDocument.addObject("Part::FeaturePython","Box")
Box(a)
ViewProviderBox(a.ViewObject)
doc.recompute()
# -----------------------------------------------------------------------------
class Line:
def __init__(self, obj):
''' Add two point properties '''
obj.addProperty("App::PropertyVector","p1","Line","Start point")
obj.addProperty("App::PropertyVector","p2","Line","End point").p2=FreeCAD.Vector(1,0,0)
obj.Proxy = self
def execute(self, fp):
''' Print a short message when doing a recomputation, this method is mandatory '''
fp.Shape = Part.makeLine(fp.p1,fp.p2)
class ViewProviderLine:
def __init__(self, obj):
''' Set this object to the proxy object of the actual view provider '''
obj.Proxy = self
def getDefaultDisplayMode(self):
''' Return the name of the default display mode. It must be defined in getDisplayModes. '''
return "Flat Lines"
def makeLine():
doc=FreeCAD.newDocument()
a=FreeCAD.ActiveDocument.addObject("Part::FeaturePython","Line")
Line(a)
#ViewProviderLine(a.ViewObject)
a.ViewObject.Proxy=0 # just set it to something different from None
doc.recompute()
# -----------------------------------------------------------------------------
class Octahedron:
def __init__(self, obj):
"Add some custom properties to our box feature"
obj.addProperty("App::PropertyLength","Length","Octahedron","Length of the octahedron").Length=1.0
obj.addProperty("App::PropertyLength","Width","Octahedron","Width of the octahedron").Width=1.0
obj.addProperty("App::PropertyLength","Height","Octahedron", "Height of the octahedron").Height=1.0
obj.addProperty("Part::PropertyPartShape","Shape","Octahedron", "Shape of the octahedron")
obj.Proxy = self
def execute(self, fp):
# Define six vetices for the shape
v1 = FreeCAD.Vector(0,0,0)
v2 = FreeCAD.Vector(fp.Length,0,0)
v3 = FreeCAD.Vector(0,fp.Width,0)
v4 = FreeCAD.Vector(fp.Length,fp.Width,0)
v5 = FreeCAD.Vector(fp.Length/2,fp.Width/2,fp.Height/2)
v6 = FreeCAD.Vector(fp.Length/2,fp.Width/2,-fp.Height/2)
# Make the wires/faces
f1 = self.make_face(v2,v1,v5)
f2 = self.make_face(v4,v2,v5)
f3 = self.make_face(v3,v4,v5)
f4 = self.make_face(v1,v3,v5)
f5 = self.make_face(v1,v2,v6)
f6 = self.make_face(v2,v4,v6)
f7 = self.make_face(v4,v3,v6)
f8 = self.make_face(v3,v1,v6)
shell=Part.makeShell([f1,f2,f3,f4,f5,f6,f7,f8])
solid=Part.makeSolid(shell)
fp.Shape = solid
# helper method to create the faces
def make_face(self,v1,v2,v3):
wire = Part.makePolygon([v1,v2,v3,v1])
face = Part.Face(wire)
return face
class ViewProviderOctahedron:
def __init__(self, obj):
"Set this object to the proxy object of the actual view provider"
obj.addProperty("App::PropertyColor","Color","Octahedron","Color of the octahedron").Color=(1.0,0.0,0.0)
obj.Proxy = self
def attach(self, obj):
"Setup the scene sub-graph of the view provider, this method is mandatory"
self.shaded = coin.SoGroup()
self.wireframe = coin.SoGroup()
self.color = coin.SoBaseColor()
self.data=coin.SoCoordinate3()
self.face=coin.SoIndexedFaceSet()
self.shaded.addChild(self.color)
self.shaded.addChild(self.data)
self.shaded.addChild(self.face)
obj.addDisplayMode(self.shaded,"Shaded");
style=coin.SoDrawStyle()
style.style = coin.SoDrawStyle.LINES
self.wireframe.addChild(style)
self.wireframe.addChild(self.color)
self.wireframe.addChild(self.data)
self.wireframe.addChild(self.face)
obj.addDisplayMode(self.wireframe,"Wireframe");
self.onChanged(obj,"Color")
def updateData(self, fp, prop):
"If a property of the handled feature has changed we have the chance to handle this here"
# fp is the handled feature, prop is the name of the property that has changed
if prop == "Shape":
s = fp.getPropertyByName("Shape")
self.data.point.setNum(6)
cnt=0
for i in s.Vertexes:
self.data.point.set1Value(cnt,i.X,i.Y,i.Z)
cnt=cnt+1
self.face.coordIndex.set1Value(0,0)
self.face.coordIndex.set1Value(1,2)
self.face.coordIndex.set1Value(2,1)
self.face.coordIndex.set1Value(3,-1)
self.face.coordIndex.set1Value(4,3)
self.face.coordIndex.set1Value(5,2)
self.face.coordIndex.set1Value(6,0)
self.face.coordIndex.set1Value(7,-1)
self.face.coordIndex.set1Value(8,4)
self.face.coordIndex.set1Value(9,2)
self.face.coordIndex.set1Value(10,3)
self.face.coordIndex.set1Value(11,-1)
self.face.coordIndex.set1Value(12,1)
self.face.coordIndex.set1Value(13,2)
self.face.coordIndex.set1Value(14,4)
self.face.coordIndex.set1Value(15,-1)
self.face.coordIndex.set1Value(16,1)
self.face.coordIndex.set1Value(17,5)
self.face.coordIndex.set1Value(18,0)
self.face.coordIndex.set1Value(19,-1)
self.face.coordIndex.set1Value(20,0)
self.face.coordIndex.set1Value(21,5)
self.face.coordIndex.set1Value(22,3)
self.face.coordIndex.set1Value(23,-1)
self.face.coordIndex.set1Value(24,3)
self.face.coordIndex.set1Value(25,5)
self.face.coordIndex.set1Value(26,4)
self.face.coordIndex.set1Value(27,-1)
self.face.coordIndex.set1Value(28,4)
self.face.coordIndex.set1Value(29,5)
self.face.coordIndex.set1Value(30,1)
self.face.coordIndex.set1Value(31,-1)
def getDisplayModes(self,obj):
"Return a list of display modes."
modes=[]
modes.append("Shaded")
modes.append("Wireframe")
return modes
def getDefaultDisplayMode(self):
"Return the name of the default display mode. It must be defined in getDisplayModes."
return "Shaded"
def setDisplayMode(self,mode):
return mode
def onChanged(self, vp, prop):
"Here we can do something when a single property got changed"
FreeCAD.Console.PrintMessage("Change property: " + str(prop) + "\n")
if prop == "Color":
c = vp.getPropertyByName("Color")
self.color.rgb.setValue(c[0],c[1],c[2])
def getIcon(self):
return """
/* XPM */
static const char * ViewProviderBox_xpm[] = {
"16 16 6 1",
" c None",
". c #141010",
"+ c #615BD2",
"@ c #C39D55",
"# c #000000",
"$ c #57C355",
" ........",
" ......++..+..",
" .@@@@.++..++.",
" .@@@@.++..++.",
" .@@ .++++++.",
" ..@@ .++..++.",
"###@@@@ .++..++.",
"##$.@@$#.++++++.",
"#$#$.$$$........",
"#$$####### ",
"#$$#$$$$$# ",
"#$$#$$$$$# ",
"#$$#$$$$$# ",
" #$#$$$$$# ",
" ##$$$$$# ",
" ####### "};
"""
def __getstate__(self):
return None
def __setstate__(self,state):
return None
def makeOctahedron():
doc=FreeCAD.newDocument()
a=FreeCAD.ActiveDocument.addObject("App::FeaturePython","Octahedron")
Octahedron(a)
ViewProviderOctahedron(a.ViewObject)
doc.recompute()
# -----------------------------------------------------------------------------
class PointFeature:
def __init__(self, obj):
obj.Proxy = self
def onChanged(self, fp, prop):
''' Print the name of the property that has changed '''
return
def execute(self, fp):
''' Print a short message when doing a recomputation, this method is mandatory '''
return
class ViewProviderPoints:
def __init__(self, obj):
''' Set this object to the proxy object of the actual view provider '''
obj.Proxy = self
def attach(self, obj):
''' Setup the scene sub-graph of the view provider, this method is mandatory '''
return
def updateData(self, fp, prop):
''' If a property of the handled feature has changed we have the chance to handle this here '''
return
def getDisplayModes(self,obj):
''' Return a list of display modes. '''
modes=[]
return modes
def getDefaultDisplayMode(self):
''' Return the name of the default display mode. It must be defined in getDisplayModes. '''
return "Points"
def setDisplayMode(self,mode):
''' Map the display mode defined in attach with those defined in getDisplayModes.
Since they have the same names nothing needs to be done. This method is optional.
'''
return mode
def onChanged(self, vp, prop):
''' Print the name of the property that has changed '''
return
def getIcon(self):
''' Return the icon in XMP format which will appear in the tree view. This method is optional
and if not defined a default icon is shown.
'''
return """
/* XPM */
static const char * ViewProviderBox_xpm[] = {
"16 16 6 1",
" c None",
". c #141010",
"+ c #615BD2",
"@ c #C39D55",
"# c #000000",
"$ c #57C355",
" ........",
" ......++..+..",
" .@@@@.++..++.",
" .@@@@.++..++.",
" .@@ .++++++.",
" ..@@ .++..++.",
"###@@@@ .++..++.",
"##$.@@$#.++++++.",
"#$#$.$$$........",
"#$$####### ",
"#$$#$$$$$# ",
"#$$#$$$$$# ",
"#$$#$$$$$# ",
" #$#$$$$$# ",
" ##$$$$$# ",
" ####### "};
"""
def __getstate__(self):
''' When saving the document this object gets stored using Python's cPickle module.
Since we have some un-pickable here -- the Coin stuff -- we must define this method
to return a tuple of all pickable objects or None.
'''
return None
def __setstate__(self,state):
''' When restoring the pickled object from document we have the chance to set some
internals here. Since no data were pickled nothing needs to be done here.
'''
return None
def makePoints():
doc=FreeCAD.newDocument()
import Mesh
m=Mesh.createSphere(5.0).Points
import Points
p=Points.Points()
l=[]
for s in m:
l.append(s.Vector)
p.addPoints(l)
a=FreeCAD.ActiveDocument.addObject("Points::FeaturePython","Points")
a.Points=p
PointFeature(a)
ViewProviderPoints(a.ViewObject)
doc.recompute()
# -----------------------------------------------------------------------------
class MeshFeature:
def __init__(self, obj):
obj.Proxy = self
def onChanged(self, fp, prop):
''' Print the name of the property that has changed '''
return
def execute(self, fp):
''' Print a short message when doing a recomputation, this method is mandatory '''
return
class ViewProviderMesh:
def __init__(self, obj):
''' Set this object to the proxy object of the actual view provider '''
obj.Proxy = self
def attach(self, obj):
''' Setup the scene sub-graph of the view provider, this method is mandatory '''
return
def getDefaultDisplayMode(self):
''' Return the name of the default display mode. It must be defined in getDisplayModes. '''
return "Shaded"
def getIcon(self):
''' Return the icon in XMP format which will appear in the tree view. This method is optional
and if not defined a default icon is shown.
'''
return """
/* XPM */
static const char * ViewProviderBox_xpm[] = {
"16 16 6 1",
" c None",
". c #141010",
"+ c #615BD2",
"@ c #C39D55",
"# c #000000",
"$ c #57C355",
" ........",
" ......++..+..",
" .@@@@.++..++.",
" .@@@@.++..++.",
" .@@ .++++++.",
" ..@@ .++..++.",
"###@@@@ .++..++.",
"##$.@@$#.++++++.",
"#$#$.$$$........",
"#$$####### ",
"#$$#$$$$$# ",
"#$$#$$$$$# ",
"#$$#$$$$$# ",
" #$#$$$$$# ",
" ##$$$$$# ",
" ####### "};
"""
def __getstate__(self):
''' When saving the document this object gets stored using Python's cPickle module.
Since we have some un-pickable here -- the Coin stuff -- we must define this method
to return a tuple of all pickable objects or None.
'''
return None
def __setstate__(self,state):
''' When restoring the pickled object from document we have the chance to set some
internals here. Since no data were pickled nothing needs to be done here.
'''
return None
def makeMesh():
doc=FreeCAD.newDocument()
import Mesh
a=FreeCAD.ActiveDocument.addObject("Mesh::FeaturePython","Mesh")
a.Mesh=Mesh.createSphere(5.0)
MeshFeature(a)
ViewProviderMesh(a.ViewObject)
doc.recompute()
# -----------------------------------------------------------------------------
class Molecule:
def __init__(self, obj):
''' Add two point properties '''
obj.addProperty("App::PropertyVector","p1","Line","Start point")
obj.addProperty("App::PropertyVector","p2","Line","End point").p2=FreeCAD.Vector(5,0,0)
obj.Proxy = self
def execute(self, fp):
''' Print a short message when doing a recomputation, this method is mandatory '''
fp.Shape = Part.makeLine(fp.p1,fp.p2)
class ViewProviderMolecule:
def __init__(self, obj):
''' Set this object to the proxy object of the actual view provider '''
sep1=coin.SoSeparator()
self.trl1=coin.SoTranslation()
sep1.addChild(self.trl1)
sep1.addChild(coin.SoSphere())
sep2=coin.SoSeparator()
self.trl2=coin.SoTranslation()
sep2.addChild(self.trl2)
sep2.addChild(coin.SoSphere())
obj.RootNode.addChild(sep1)
obj.RootNode.addChild(sep2)
# triggers an updateData call so the assignment at the end
obj.Proxy = self
def updateData(self, fp, prop):
"If a property of the handled feature has changed we have the chance to handle this here"
# fp is the handled feature, prop is the name of the property that has changed
if prop == "p1":
p = fp.getPropertyByName("p1")
self.trl1.translation=(p.x,p.y,p.z)
elif prop == "p2":
p = fp.getPropertyByName("p2")
self.trl2.translation=(p.x,p.y,p.z)
def __getstate__(self):
return None
def __setstate__(self,state):
return None
def makeMolecule():
doc=FreeCAD.newDocument()
a=FreeCAD.ActiveDocument.addObject("Part::FeaturePython","Molecule")
Molecule(a)
ViewProviderMolecule(a.ViewObject)
doc.recompute()
# -----------------------------------------------------------------------------
class CircleSet:
def __init__(self, obj):
obj.addProperty("Part::PropertyPartShape","Shape","Circle","Shape")
obj.Proxy = self
def execute(self, fp):
pass
class ViewProviderCircleSet:
def __init__(self, obj):
''' Set this object to the proxy object of the actual view provider '''
obj.Proxy = self
def attach(self, obj):
self.coords=coin.SoCoordinate3()
self.lines=coin.SoLineSet()
obj.RootNode.addChild(self.coords)
obj.RootNode.addChild(self.lines)
def updateData(self, fp, prop):
if prop == "Shape":
edges = fp.getPropertyByName("Shape").Edges
pts=[]
ver=[]
for i in edges:
length=i.Length
ver.append(10)
for j in range(10):
v=i.valueAt(j/9.0*length)
pts.append((v.x,v.y,v.z))
self.coords.point.setValues(pts)
self.lines.numVertices.setValues(ver)
def __getstate__(self):
return None
def __setstate__(self,state):
return None
def makeCircleSet():
x=0.5
comp=Part.Compound([])
for j in range (630):
y=0.5
for i in range (630):
c = Part.makeCircle(0.1, Base.Vector(x,y,0), Base.Vector(0,0,1))
#Part.show(c)
comp.add(c)
y=y+0.5
x=x+0.5
doc=FreeCAD.newDocument()
a=FreeCAD.ActiveDocument.addObject("App::FeaturePython","Circles")
CircleSet(a)
ViewProviderCircleSet(a.ViewObject)
a.Shape=comp
doc.recompute()
# -----------------------------------------------------------------------------
class EnumTest:
def __init__(self, obj):
''' Add enum properties '''
obj.addProperty("App::PropertyEnumeration","Enum","","Enumeration").Enum=["One","Two","Three"]
obj.addProperty("App::PropertyEnumeration","Enum2","","Enumeration2").Enum2=["One","Two","Three"]
obj.Proxy = self
def execute(self, fp):
return
class ViewProviderEnumTest:
def __init__(self, obj):
''' Set this object to the proxy object of the actual view provider '''
obj.addProperty("App::PropertyEnumeration","Enum3","","Enumeration3").Enum3=["One","Two","Three"]
obj.addProperty("App::PropertyEnumeration","Enum4","","Enumeration4").Enum4=["One","Two","Three"]
obj.Proxy = self
def updateData(self, fp, prop):
print("prop updated:",prop)
def __getstate__(self):
return None
def __setstate__(self,state):
return None
def makeEnumTest():
FreeCAD.newDocument()
a=FreeCAD.ActiveDocument.addObject("Part::FeaturePython","Enum")
EnumTest(a)
ViewProviderEnumTest(a.ViewObject)
# -----------------------------------------------------------------------------
class DistanceBolt:
def __init__(self, obj):
''' Add the properties: Length, Edges, Radius, Height '''
obj.addProperty("App::PropertyInteger","Edges","Bolt","Number of edges of the outline").Edges=6
obj.addProperty("App::PropertyLength","Length","Bolt","Length of the edges of the outline").Length=10.0
obj.addProperty("App::PropertyLength","Radius","Bolt","Radius of the inner circle").Radius=4.0
obj.addProperty("App::PropertyLength","Height","Bolt","Height of the extrusion").Height=20.0
obj.Proxy = self
def onChanged(self, fp, prop):
if prop == "Edges" or prop == "Length" or prop == "Radius" or prop == "Height":
self.execute(fp)
def execute(self, fp):
edges = fp.Edges
if edges < 3:
edges = 3
length = fp.Length
radius = fp.Radius
height = fp.Height
m=Base.Matrix()
m.rotateZ(math.radians(360.0/edges))
# create polygon
polygon = []
v=Base.Vector(length,0,0)
for i in range(edges):
polygon.append(v)
v = m.multiply(v)
polygon.append(v)
wire = Part.makePolygon(polygon)
# create circle
circ=Part.makeCircle(radius)
# Create the face with the polygon as outline and the circle as hole
face=Part.Face([wire,Part.Wire(circ)])
# Extrude in z to create the final solid
extrude=face.extrude(Base.Vector(0,0,height))
fp.Shape = extrude
def makeDistanceBolt():
doc=FreeCAD.newDocument()
bolt=FreeCAD.ActiveDocument.addObject("Part::FeaturePython","Distance_Bolt")
bolt.Label = "Distance bolt"
DistanceBolt(bolt)
bolt.ViewObject.Proxy=0
doc.recompute()
Dikkat Edilmesi Gerekenler
Nesneniz oluşturulur oluşturulmaz yeniden hesaplanması gerekiyorsa, otomatik olarak çağrılmadığı için bunu __init__
işlevinde manuel olarak yapmanız gerekir. Box
sınıfının onChanged
yöntemi, execute
(çalıştır) fonksiyonuyla aynı etkiye sahip olduğu için bu örnek buna gerek duymaz, ancak aşağıdaki örnekler, 3B görünümde herhangi bir şey görüntülenmeden önce yeniden hesaplanma mantığına dayanır. Örneklerde
bu, ActiveDocument.recompute()
ile manuel olarak yapılır, ancak daha karmaşık senaryolarda, tüm belgenin veya FeaturePython
nesnesinin nerede yeniden hesaplanacağına karar vermeniz gerekir.
Bu örnek, rapor görünümü penceresinde bir dizi istisna yığını izi üretir. Bunun nedeni, __init__
içine her özellik eklendiğinde Box
sınıfının onChanged
yönteminin çağrılmasıdır. İlki eklendiğinde, Genişlik ve Yükseklik özellikleri henüz mevcut değildir ve bu nedenle bunlara erişme girişimi başarısız olur.
__getstate__
ve __setstate__
için bir açıklama obj.Proxy.Type is a dict, not a string forum başlığında bulunmaktadır.
Mevcut Yöntemler / Metotlar
PythonÖzelliği Yöntemleri tüm referans kaynak için sayfaya bakın.
Kullanılabilir Özellikler
Özellikler, FeaturePython
nesnelerinin gerçek yapı taşlarıdır. Bunlar aracılığıyla, kullanıcı etkileşimde bulunabilecek ve nesnenizi değiştirebilecektir. Belgenizde yeni bir FeaturePython
nesnesi oluşturduktan sonra;
(obj=FreeCAD.ActiveDocument.addObject("App::FeaturePython","Box")
), aşağıdaki kodu yazarak, kullanılabilir özelliklerin bir listesini elde edebilirsiniz:
obj.supportedProperties()
FeaturePython Özel Özellikler sayfasında daha ayrıntılı olarak açıklanan mevcut özelliklerin bir listesini elde edeceksiniz:
- App::PropertyAcceleration
- App::PropertyAngle
- App::PropertyArea
- App::PropertyBool
- App::PropertyBoolList
- App::PropertyColor
- App::PropertyColorList
- App::PropertyDirection
- App::PropertyDistance
- App::PropertyEnumeration
- App::PropertyExpressionEngine
- App::PropertyFile
- App::PropertyFileIncluded
- App::PropertyFloat
- App::PropertyFloatConstraint
- App::PropertyFloatList
- App::PropertyFont
- App::PropertyForce
- App::PropertyFrequency
- App::PropertyInteger
- App::PropertyIntegerConstraint
- App::PropertyIntegerList
- App::PropertyIntegerSet
- App::PropertyLength
- App::PropertyLink
- App::PropertyLinkChild
- App::PropertyLinkGlobal
- App::PropertyLinkHidden
- App::PropertyLinkList
- App::PropertyLinkListChild
- App::PropertyLinkListGlobal
- App::PropertyLinkListHidden
- App::PropertyLinkSub
- App::PropertyLinkSubChild
- App::PropertyLinkSubGlobal
- App::PropertyLinkSubHidden
- App::PropertyLinkSubList
- App::PropertyLinkSubListChild
- App::PropertyLinkSubListGlobal
- App::PropertyLinkSubListHidden
- App::PropertyMap
- App::PropertyMaterial
- App::PropertyMaterialList
- App::PropertyMatrix
- App::PropertyPath
- App::PropertyPercent
- App::PropertyPersistentObject
- App::PropertyPlacement
- App::PropertyPlacementLink
- App::PropertyPlacementList
- App::PropertyPosition
- App::PropertyPrecision
- App::PropertyPressure
- App::PropertyPythonObject
- App::PropertyQuantity
- App::PropertyQuantityConstraint
- App::PropertySpeed
- App::PropertyString
- App::PropertyStringList
- App::PropertyUUID
- App::PropertyVacuumPermittivity
- App::PropertyVector
- App::PropertyVectorDistance
- App::PropertyVectorList
- App::PropertyVolume
- App::PropertyXLink
- App::PropertyXLinkList
- App::PropertyXLinkSub
- App::PropertyXLinkSubList
- Mesh::PropertyCurvatureList
- Mesh::PropertyMeshKernel
- Mesh::PropertyNormalList
- Part::PropertyFilletEdges
- Part::PropertyGeometryList
- Part::PropertyPartShape
- Part::PropertyShapeHistory
- Path::PropertyPath
- Path::PropertyTool
- Path::PropertyTooltable
- Sketcher::PropertyConstraintList
- Spreadsheet::PropertyColumnWidths
- Spreadsheet::PropertyRowHeights
- Spreadsheet::PropertySheet
- Spreadsheet::PropertySpreadsheetQuantity
- TechDraw::PropertyCenterLineList
- TechDraw::PropertyCosmeticEdgeList
- TechDraw::PropertyCosmeticVertexList
- TechDraw::PropertyGeomFormatList
Özel nesnelerinize özellikler eklerken şunlara dikkat edin:
-
Özellikler açıklamalarında
<
veya>
karakterlerini kullanmayın (bu, .fcstd dosyasındaki xml parçalarını bozar) -
Özellikler bir .fcstd dosyasında alfabetik olarak saklanır. Eğer özelliklerinizde bir şekil varsa, ismi alfabetik olarak "Şekil"den sonra gelen herhangi bir özellik şekilden SONRA yüklenecektir ve bu da garip davranışlara neden olabilir.
Özellik özniteliklerinin tam listesi PropertyStandard C++ başlık dosyasında görülebilir. Örneğin, kullanıcının yalnız sınırlı bir değer aralığı girmesine izin vermek istiyorsanız (örneğin PropertyIntegerConstraint
kullanarak), Python'da yalnız özellik değerini değil, aynı zamanda alt ve üst sınırı ve adım boyutunu da içeren bir tanımlama grubu
atayacaksınız, aşağıda olduğu gibi:
prop = (value, lower, upper, stepsize)
Özellik Türü
Varsayılan olarak özellikler güncellenebilir. Örneğin bir yöntemin, sonucunu göstermek istemesi durumunda, özellikleri salt okunur yapmak mümkündür. Ayrıca özelliği gizlemek de mümkündür. Özellik türü aşağıdakiler kullanılarak ayarlanabilir:
obj.setEditorMode("MyPropertyName", mode)
mod, şu şekilde ayarlanabilen kısa bir tamsayıdır (int):
0 -- varsayılan mod, okuma ve yazma (default mode, read and write)
1 -- Salt okunur (read-only)
2 -- gizli (hidden)
EditörModları, FreeCAD dosyasının yeniden yüklenmesine ayarlanmamıştır. Bu, __setstate__
fonksiyonu tarafından yapılabilir. Eigenmode frequency always 0? - Page 2 - FreeCAD Forum adresine bakın. setEditorMode
kullanılarak, özellikler yalnızca PropertyEditor'de okunur. Hala python'dan değiştirilebilirler. Bunları gerçekten okumalarını sağlamak için, ayarın doğrudan addProperty
fonksiyonunun içine iletilmesi gerekir. Örnek için Eigenmode frequency always 0? - Page 3 - FreeCAD Forum adresine bakın.
AddProperty
fonksiyonundaki doğrudan ayarı kullanarak, daha fazla olanağınız da olur. Özellikle, bir özelliği çıktı özelliği olarak işaretlemek ilginçtir. Bu şekilde FreeCAD, özelliği değiştirirken dokunulduğunda işaretlemeyecektir (böylece yeniden hesaplamaya gerek yoktur).
Çıktı özelliği örneği (ayrıca bkz. [Solved] Skip excution for some properties - FreeCAD Forum):
obj.addProperty("App::PropertyString","MyCustomProperty","","",8)
addProperty
işlevinin son parametresinde ayarlanabilen özellik türleri şunlardır:
0 -- Prop_None, Özel bir özellik türü yok
1 -- Prop_ReadOnly, Özellik düzenleyicide salt okunurdur
2 -- Prop_Transient, Özellik dosyaya kaydedilmeyecek
4 -- Prop_Hidden, Özellik düzenleyicide görünmeyecek
8 -- Prop_Output, Değiştirilmiş özellik, üst kapsayıcısına (konteyner) dokunmuyor
16 -- Prop_NoRecompute, Değiştirilmiş özellik, yeniden hesaplama için kapsayıcısına (konteyner) dokunmuyor
PropertyContainer için C++ kaynak kodu başlığında tanımlanan bu farklı özellik türlerini bulabilirsiniz.
Diğer Daha Karmaşık Örnek
Bu örnek, bir oktahedron (sekizyüzlü / sekiz yüzeyli katı bir şekil) oluşturmak için Part modülünü kullanır, ardından pivy
ile onun coin gösterimini oluşturur.
Kodun birinci bölümü, Document nesnesinin kendisidir:
import FreeCAD, FreeCADGui, Part
import pivy
from pivy import coin
class Octahedron:
def __init__(self, obj):
"Add some custom properties to our box feature"
obj.addProperty("App::PropertyLength","Length","Octahedron","Length of the octahedron").Length=1.0
obj.addProperty("App::PropertyLength","Width","Octahedron","Width of the octahedron").Width=1.0
obj.addProperty("App::PropertyLength","Height","Octahedron", "Height of the octahedron").Height=1.0
obj.addProperty("Part::PropertyPartShape","Shape","Octahedron", "Shape of the octahedron")
obj.Proxy = self
def execute(self, fp):
# Define six vetices for the shape
v1 = FreeCAD.Vector(0,0,0)
v2 = FreeCAD.Vector(fp.Length,0,0)
v3 = FreeCAD.Vector(0,fp.Width,0)
v4 = FreeCAD.Vector(fp.Length,fp.Width,0)
v5 = FreeCAD.Vector(fp.Length/2,fp.Width/2,fp.Height/2)
v6 = FreeCAD.Vector(fp.Length/2,fp.Width/2,-fp.Height/2)
# Make the wires/faces
f1 = self.make_face(v1,v2,v5)
f2 = self.make_face(v2,v4,v5)
f3 = self.make_face(v4,v3,v5)
f4 = self.make_face(v3,v1,v5)
f5 = self.make_face(v2,v1,v6)
f6 = self.make_face(v4,v2,v6)
f7 = self.make_face(v3,v4,v6)
f8 = self.make_face(v1,v3,v6)
shell=Part.makeShell([f1,f2,f3,f4,f5,f6,f7,f8])
solid=Part.makeSolid(shell)
fp.Shape = solid
# helper mehod to create the faces
def make_face(self,v1,v2,v3):
wire = Part.makePolygon([v1,v2,v3,v1])
face = Part.Face(wire)
return face
Ardından, nesneyi 3B sahnesinde göstermekten sorumlu olan görünüm sağlayıcı nesnesine sahip kodları yazıyoruz:
class ViewProviderOctahedron:
def __init__(self, obj):
"Set this object to the proxy object of the actual view provider"
obj.addProperty("App::PropertyColor","Color","Octahedron","Color of the octahedron").Color=(1.0,0.0,0.0)
obj.Proxy = self
def attach(self, obj):
"Setup the scene sub-graph of the view provider, this method is mandatory"
self.shaded = coin.SoGroup()
self.wireframe = coin.SoGroup()
self.scale = coin.SoScale()
self.color = coin.SoBaseColor()
self.data=coin.SoCoordinate3()
self.face=coin.SoIndexedLineSet()
self.shaded.addChild(self.scale)
self.shaded.addChild(self.color)
self.shaded.addChild(self.data)
self.shaded.addChild(self.face)
obj.addDisplayMode(self.shaded,"Shaded");
style=coin.SoDrawStyle()
style.style = coin.SoDrawStyle.LINES
self.wireframe.addChild(style)
self.wireframe.addChild(self.scale)
self.wireframe.addChild(self.color)
self.wireframe.addChild(self.data)
self.wireframe.addChild(self.face)
obj.addDisplayMode(self.wireframe,"Wireframe");
self.onChanged(obj,"Color")
def updateData(self, fp, prop):
"If a property of the handled feature has changed we have the chance to handle this here"
# fp is the handled feature, prop is the name of the property that has changed
if prop == "Shape":
s = fp.getPropertyByName("Shape")
self.data.point.setNum(6)
cnt=0
for i in s.Vertexes:
self.data.point.set1Value(cnt,i.X,i.Y,i.Z)
cnt=cnt+1
self.face.coordIndex.set1Value(0,0)
self.face.coordIndex.set1Value(1,1)
self.face.coordIndex.set1Value(2,2)
self.face.coordIndex.set1Value(3,-1)
self.face.coordIndex.set1Value(4,1)
self.face.coordIndex.set1Value(5,3)
self.face.coordIndex.set1Value(6,2)
self.face.coordIndex.set1Value(7,-1)
self.face.coordIndex.set1Value(8,3)
self.face.coordIndex.set1Value(9,4)
self.face.coordIndex.set1Value(10,2)
self.face.coordIndex.set1Value(11,-1)
self.face.coordIndex.set1Value(12,4)
self.face.coordIndex.set1Value(13,0)
self.face.coordIndex.set1Value(14,2)
self.face.coordIndex.set1Value(15,-1)
self.face.coordIndex.set1Value(16,1)
self.face.coordIndex.set1Value(17,0)
self.face.coordIndex.set1Value(18,5)
self.face.coordIndex.set1Value(19,-1)
self.face.coordIndex.set1Value(20,3)
self.face.coordIndex.set1Value(21,1)
self.face.coordIndex.set1Value(22,5)
self.face.coordIndex.set1Value(23,-1)
self.face.coordIndex.set1Value(24,4)
self.face.coordIndex.set1Value(25,3)
self.face.coordIndex.set1Value(26,5)
self.face.coordIndex.set1Value(27,-1)
self.face.coordIndex.set1Value(28,0)
self.face.coordIndex.set1Value(29,4)
self.face.coordIndex.set1Value(30,5)
self.face.coordIndex.set1Value(31,-1)
def getDisplayModes(self,obj):
"Return a list of display modes."
modes=[]
modes.append("Shaded")
modes.append("Wireframe")
return modes
def getDefaultDisplayMode(self):
"Return the name of the default display mode. It must be defined in getDisplayModes."
return "Shaded"
def setDisplayMode(self,mode):
return mode
def onChanged(self, vp, prop):
"Here we can do something when a single property got changed"
FreeCAD.Console.PrintMessage("Change property: " + str(prop) + "\n")
if prop == "Color":
c = vp.getPropertyByName("Color")
self.color.rgb.setValue(c[0],c[1],c[2])
def getIcon(self):
return """ /* XPM */ static const char * ViewProviderBox_xpm[] = { "16 16 6 1", " c None", ". c #141010", "+ c #615BD2", "@ c #C39D55", "# c #000000", "$ c #57C355", " ........", " ......++..+..", " .@@@@.++..++.", " .@@@@.++..++.", " .@@ .++++++.", " ..@@ .++..++.", "###@@@@ .++..++.", "##$.@@$#.++++++.", "#$#$.$$$........", "#$$####### ", "#$$#$$$$$# ", "#$$#$$$$$# ", "#$$#$$$$$# ", " #$#$$$$$# ", " ##$$$$$# ", " ####### "}; """
def __getstate__(self):
return None
def __setstate__(self,state):
return None
Son olarak, nesnemiz ve onun viewobject
'i tanımlandıktan sonra, onları çağırmamız (komutu çalıştırmamız) yeterlidir (Octahedron sınıfı ve viewprovider sınıf kodu doğrudan FreeCAD python konsolunda kopyalanabilir):
FreeCAD.newDocument()
a=FreeCAD.ActiveDocument.addObject("App::FeaturePython","Octahedron")
Octahedron(a)
ViewProviderOctahedron(a.ViewObject)
Nesneleri Seçilebilir Yapma
Eğer nesnenizi veya en azından bir kısmını görünüm alanında tıklayarak seçilebilir yapmak istiyorsanız, onun coin geometrisini bir SoFCSelection
düğümüne (node) dahil etmeniz gerekir. Nesnenizin widget'lar, açıklamalar vb. ile karmaşık bir temsili varsa, bunun yalnızca bir bölümünü SoFCSelection
'a dahil etmek isteyebilirsiniz. SoFCSelection
olan her şey, seçimi/ön seçimi algılamak için FreeCAD tarafından
sürekli olarak taranır, bu nedenle gereksiz tarama ile aşırı yüklemeden kaçınmak mantıklıdır.
Sahne grafiğinin seçilebilir olan kısımları SoFCSelection
düğümlerinin içinde olduğunda, seçim yolunu işlemek için iki yöntem sağlamanız gerekir. Seçim yolu, yoldaki her bir öğenin veya bir dizi sahne grafiği nesnesinin adlarını veren bir dize (string) biçimini alabilir. Sağladığınız iki yöntem, bir dize yolundan bir dizi sahne
grafiği nesnesine dönüştüren getDetailPath
ve sahne grafiğinde tıklanan bir öğeyi alan ve dize adını döndüren getElementPicked
'dir (dize yolunu değil, not edin).
Molekülün öğelerini seçilebilir hale getirmek için uyarlanmış yukarıdaki molekül örneği:
class Molecule:
def __init__(self, obj):
''' Add two point properties '''
obj.addProperty("App::PropertyVector","p1","Line","Start point")
obj.addProperty("App::PropertyVector","p2","Line","End point").p2=FreeCAD.Vector(5,0,0)
obj.Proxy = self
def onChanged(self, fp, prop):
if prop == "p1" or prop == "p2":
''' Print the name of the property that has changed '''
fp.Shape = Part.makeLine(fp.p1,fp.p2)
def execute(self, fp):
''' Print a short message when doing a recomputation, this method is mandatory '''
fp.Shape = Part.makeLine(fp.p1,fp.p2)
class ViewProviderMolecule:
def __init__(self, obj):
''' Set this object to the proxy object of the actual view provider '''
obj.Proxy = self
self.ViewObject = obj
sep1=coin.SoSeparator()
sel1 = coin.SoType.fromName('SoFCSelection').createInstance()
# sel1.policy.setValue(coin.SoSelection.SHIFT)
sel1.ref()
sep1.addChild(sel1)
self.trl1=coin.SoTranslation()
sel1.addChild(self.trl1)
sel1.addChild(coin.SoSphere())
sep2=coin.SoSeparator()
sel2 = coin.SoType.fromName('SoFCSelection').createInstance()
sel2.ref()
sep2.addChild(sel2)
self.trl2=coin.SoTranslation()
sel2.addChild(self.trl2)
sel2.addChild(coin.SoSphere())
obj.RootNode.addChild(sep1)
obj.RootNode.addChild(sep2)
self.updateData(obj.Object, 'p2')
self.sel1 = sel1
self.sel2 = sel2
def getDetailPath(self, subname, path, append):
vobj = self.ViewObject
if append:
path.append(vobj.RootNode)
path.append(vobj.SwitchNode)
mode = vobj.SwitchNode.whichChild.getValue()
if mode >= 0:
mode = vobj.SwitchNode.getChild(mode)
path.append(mode)
sub = Part.splitSubname(subname)[-1]
if sub == 'Atom1':
path.append(self.sel1)
elif sub == 'Atom2':
path.append(self.sel2)
else:
path.append(mode.getChild(0))
return True
def getElementPicked(self, pp):
path = pp.getPath()
if path.findNode(self.sel1) >= 0:
return 'Atom1'
if path.findNode(self.sel2) >= 0:
return 'Atom2'
raise NotImplementedError
def updateData(self, fp, prop):
"If a property of the handled feature has changed we have the chance to handle this here"
# fp is the handled feature, prop is the name of the property that has changed
if prop == "p1":
p = fp.getPropertyByName("p1")
self.trl1.translation=(p.x,p.y,p.z)
elif prop == "p2":
p = fp.getPropertyByName("p2")
self.trl2.translation=(p.x,p.y,p.z)
def __getstate__(self):
return None
def __setstate__(self,state):
return None
def makeMolecule():
FreeCAD.newDocument()
a=FreeCAD.ActiveDocument.addObject("Part::FeaturePython","Molecule")
Molecule(a)
ViewProviderMolecule(a.ViewObject)
FreeCAD.ActiveDocument.recompute()
Basit Şekillerle Çalışmak
Parametrik nesneniz basitçe bir şekil veriyorsa, bir görünüm sağlayıcı nesnesi kullanmanıza gerek yoktur. Şekil, FreeCAD'in standart şekil gösterimi kullanılarak görüntülenecektir:
import FreeCAD as App
import FreeCADGui
import FreeCAD
import Part
class Line:
def __init__(self, obj):
'''"App two point properties" '''
obj.addProperty("App::PropertyVector","p1","Line","Start point")
obj.addProperty("App::PropertyVector","p2","Line","End point").p2=FreeCAD.Vector(1,0,0)
obj.Proxy = self
def execute(self, fp):
'''"Print a short message when doing a recomputation, this method is mandatory" '''
fp.Shape = Part.makeLine(fp.p1,fp.p2)
a=FreeCAD.ActiveDocument.addObject("Part::FeaturePython","Line")
Line(a)
a.ViewObject.Proxy=0 # just set it to something different from None (this assignment is needed to run an internal notification)
FreeCAD.ActiveDocument.recompute()
ViewProviderLine kullanımı ile aynı kod
import FreeCAD as App
import FreeCADGui
import FreeCAD
import Part
class Line:
def __init__(self, obj):
'''"App two point properties" '''
obj.addProperty("App::PropertyVector","p1","Line","Start point")
obj.addProperty("App::PropertyVector","p2","Line","End point").p2=FreeCAD.Vector(100,0,0)
obj.Proxy = self
def execute(self, fp):
'''"Print a short message when doing a recomputation, this method is mandatory" '''
fp.Shape = Part.makeLine(fp.p1,fp.p2)
class ViewProviderLine:
def __init__(self, obj):
''' Set this object to the proxy object of the actual view provider '''
obj.Proxy = self
def getDefaultDisplayMode(self):
''' Return the name of the default display mode. It must be defined in getDisplayModes. '''
return "Flat Lines"
a=FreeCAD.ActiveDocument.addObject("Part::FeaturePython","Line")
Line(a)
ViewProviderLine(a.ViewObject)
App.ActiveDocument.recompute()
Sahne Yapısı
Yukarıdaki örneklerin sahne grafiklerini biraz farklı şekillerde oluşturduğunu fark etmiş olabilirsiniz. Bazıları obj.addDisplayMode(node, "modename")
kullanırken diğerleri obj.SwitchNode.getChild(x).addChild(y)
kullanır.
Bir FreeCAD belgesindeki her özellik aşağıdaki sahne grafiği yapısını temel alır:
RootNode
\- SwitchNode
\- Shaded
- Wireframe
- etc
SwitchNode
, FreeCAD'de hangi görüntüleme modunun seçildiğine bağlı olarak, alt öğelerinden yalnızca birini görüntüler.
addDisplayMode
kullanan örnekler, sahne grafiklerini yalnızca coin3d sahne grafiği öğelerinden oluşturuyor. Kapakların altında, addDisplayMode
, SwitchNode
'a yeni bir alt öğe ekler; o düğümün adı, geçtiği görüntüleme moduyla eşleşir.
SwitchNode.getChild(x).addChild
kullanan örnekler ayrıca, fp.Shape = Part.makeLine(fp.p1,fp.p2)
gibi Part (Parça) tezgahındaki işlevleri kullanarak geometrilerinin bir bölümünü oluşturur. Bu, SwitchNode
altında farklı görüntüleme modu sahne grafiklerini oluşturur; Daha sonra sahne grafiğine coin3d öğeleri eklemeye geldiğimizde, bunları SwitchNode
'un yeni bir alt öğesini oluşturmak yerine addChild
kullanarak mevcut görüntüleme modu sahne grafiklerine eklememiz gerekir.
Sahne grafiğine geometri eklemek için addDisplayMode()
kullanılırken, her görüntüleme modunun addDisplayMode()
öğesine geçirilen kendi düğümü olmalıdır; bunun için aynı düğümü tekrar kullanmayın. Bunu yapmak, seçim mekanizmasını karıştıracaktır. Her görüntüleme modunun düğümünün altına aynı geometri düğümleri eklenmişse sorun değil, yalnızca her görüntüleme modunun kökünün farklı olması
gerekir.
Part (Parça) tezgahından nesneler kullanmak yerine yalnızca Coin3D sahne grafiği nesneleriyle çizilmek üzere uyarlanmış yukarıdaki molekül örneği:
import Part
from pivy import coin
class Molecule:
def __init__(self, obj):
''' Add two point properties '''
obj.addProperty("App::PropertyVector","p1","Line","Start point")
obj.addProperty("App::PropertyVector","p2","Line","End point").p2=FreeCAD.Vector(5,0,0)
obj.Proxy = self
def onChanged(self, fp, prop):
pass
def execute(self, fp):
''' Print a short message when doing a recomputation, this method is mandatory '''
pass
class ViewProviderMolecule:
def __init__(self, obj):
''' Set this object to the proxy object of the actual view provider '''
self.constructed = False
obj.Proxy = self
self.ViewObject = obj
def attach(self, obj):
material = coin.SoMaterial()
material.diffuseColor = (1.0, 0.0, 0.0)
material.emissiveColor = (1.0, 0.0, 0.0)
drawStyle = coin.SoDrawStyle()
drawStyle.pointSize.setValue(10)
drawStyle.style = coin.SoDrawStyle.LINES
wireframe = coin.SoGroup()
shaded = coin.SoGroup()
self.wireframe = wireframe
self.shaded = shaded
self.coords = coin.SoCoordinate3()
self.coords.point.setValues(0, 2, [FreeCAD.Vector(0, 0, 0), FreeCAD.Vector(1, 0, 0)])
wireframe += self.coords
wireframe += drawStyle
wireframe += material
shaded += self.coords
shaded += drawStyle
shaded += material
g = coin.SoGroup()
sel1 = coin.SoType.fromName('SoFCSelection').createInstance()
sel1.style = 'EMISSIVE_DIFFUSE'
p1 = coin.SoType.fromName('SoIndexedPointSet').createInstance()
p1.coordIndex.set1Value(0, 0)
sel1 += p1
g += sel1
wireframe += g
shaded += g
g = coin.SoGroup()
sel2 = coin.SoType.fromName('SoFCSelection').createInstance()
sel2.style = 'EMISSIVE_DIFFUSE'
p2 = coin.SoType.fromName('SoIndexedPointSet').createInstance()
p2.coordIndex.set1Value(0, 1)
sel2 += p2
g += sel2
wireframe += g
shaded += g
g = coin.SoGroup()
sel3 = coin.SoType.fromName('SoFCSelection').createInstance()
sel3.style = 'EMISSIVE_DIFFUSE'
p3 = coin.SoType.fromName('SoIndexedLineSet').createInstance()
p3.coordIndex.setValues(0, 2, [0, 1])
sel3 += p3
g += sel3
wireframe += g
shaded += g
obj.addDisplayMode(wireframe, 'Wireframe')
obj.addDisplayMode(shaded, 'Shaded')
self.sel1 = sel1
self.sel2 = sel2
self.sel3 = sel3
self.constructed = True
self.updateData(obj.Object, 'p2')
def getDetailPath(self, subname, path, append):
vobj = self.ViewObject
if append:
path.append(vobj.RootNode)
path.append(vobj.SwitchNode)
mode = vobj.SwitchNode.whichChild.getValue()
FreeCAD.Console.PrintWarning("getDetailPath: mode {} is active\n".format(mode))
if mode >= 0:
mode = vobj.SwitchNode.getChild(mode)
path.append(mode)
sub = Part.splitSubname(subname)[-1]
print(sub)
if sub == 'Atom1':
path.append(self.sel1)
elif sub == 'Atom2':
path.append(self.sel2)
elif sub == 'Line':
path.append(self.sel3)
else:
path.append(mode.getChild(0))
return True
def getElementPicked(self, pp):
path = pp.getPath()
if path.findNode(self.sel1) >= 0:
return 'Atom1'
if path.findNode(self.sel2) >= 0:
return 'Atom2'
if path.findNode(self.sel3) >= 0:
return 'Line'
raise NotImplementedError
def updateData(self, fp, prop):
"If a property of the handled feature has changed we have the chance to handle this here"
# fp is the handled feature, prop is the name of the property that has changed
if not self.constructed:
return
if prop == "p1":
p = fp.getPropertyByName("p1")
self.coords.point.set1Value(0, p)
elif prop == "p2":
p = fp.getPropertyByName("p2")
self.coords.point.set1Value(1, p)
def getDisplayModes(self, obj):
return ['Wireframe', 'Shaded']
def getDefaultDisplayMode(self):
return 'Shaded'
def setDisplayMode(self, mode):
return mode
def __getstate__(self):
return None
def __setstate__(self,state):
return None
def makeMolecule():
FreeCAD.newDocument()
a=FreeCAD.ActiveDocument.addObject("App::FeaturePython","Molecule")
Molecule(a)
b=ViewProviderMolecule(a.ViewObject)
a.touch()
FreeCAD.ActiveDocument.recompute()
return a,b
a,b = makeMolecule()
ParçaTasarımı (Part Design) Komut Dosyasıyla Yazılmış Nesneler
Parça Tasarımında kodlanmış nesneler oluştururken, süreç yukarıda tartışılan komut dosyası oluşturulmuş nesnelere benzer, ancak birkaç ek husus vardır. Biri 3B görünümde gördüğümüz şekil için, diğeri ise polar desen özellikleri gibi desen araçları tarafından kullanılan şekil için olmak üzere 2 şekil özelliğini ele almalıyız. Nesne şekillerinin ayrıca, halihazırda Gövdede bulunan herhangi bir mevcut malzemeyle kaynaştırılması (veya Çıkarma özellikleri durumunda ondan kesilmesi) gerekir. Nesnelerimizin yerleştirilmesini ve bağlanmasını biraz farklı hesaba katmalıyız.
Parça Tasarımı komut dosyasıyla yazılmış katı nesne özellikleri, Part::FeaturePython
yerine PartDesign::FeaturePython
, PartDesign::FeatureAdditivePython
veya PartDesign::FeatureSubtractivePython
'a dayanmalıdır. Çoğaltma özelliklerinde yalnız Toplama ve Çıkarma varyantları kullanılabilir ve Part::FeaturePython
'a
dayalıysa, kullanıcı nesneyi bir Parça Tasarım Gövdesine bıraktığında, Gövde tarafından yerel bir Parça Tasarım nesnesi olarak ele alınmak yerine bir BaseFeature
olur. Not: bunların hepsinin katı olması beklenir, bu nedenle katı olmayan bir özellik yapıyorsanız bunun Part::FeaturePython
'a dayanması gerekir, yoksa ağaçtaki bir sonraki özellik katı olarak birleşmeye çalışır ve başarısız olur .
Burada, Parça Çalışma Tezgahındaki (Part Workbench'teki) Boru (Tube) temel şekline (primitifine) benzer bir Boru temel şekil oluşturmanın basit bir örneği verilmiştir, ancak bu bir Parça Tasarımı (Part Design) katı özellik nesnesi olacaktır. Bunun için 2 ayrı dosya oluşturacağız: pdtube.FCMacro
ve pdtube.py
. .FCMacro
dosyası, nesneyi oluşturmak için kullanıcı tarafından çalıştırılacaktır. .py
dosyası, .FCMacro
tarafından içe aktarılan sınıf tanımlarını tutacaktır. Bunu bu şekilde yapmanın nedeni, FreeCAD'i yeniden başlattıktan ve Borularımızdan birini içeren bir belgeyi açtıktan sonra nesnenin parametrik yapısını korumaktır.
İlk olarak, sınıf tanım dosyasını oluşturuyoruz:
# -*- coding: utf-8 -*-
#classes should go in pdtube.py
import FreeCAD, FreeCADGui, Part
class PDTube:
def __init__(self,obj):
obj.addProperty("App::PropertyLength","Radius1","Tube","Radius1").Radius1 = 5
obj.addProperty("App::PropertyLength","Radius2","Tube","Radius2").Radius2 = 10
obj.addProperty("App::PropertyLength","Height","Tube","Height of tube").Height = 10
self.makeAttachable(obj)
obj.Proxy = self
def makeAttachable(self, obj):
if int(FreeCAD.Version()[1]) >= 19:
obj.addExtension('Part::AttachExtensionPython')
else:
obj.addExtension('Part::AttachExtensionPython', obj)
obj.setEditorMode('Placement', 0) #non-readonly non-hidden
def execute(self,fp):
outer_cylinder = Part.makeCylinder(fp.Radius2, fp.Height)
inner_cylinder = Part.makeCylinder(fp.Radius1, fp.Height)
if fp.Radius1 == fp.Radius2: #just make cylinder
tube_shape = outer_cylinder
elif fp.Radius1 < fp.Radius2:
tube_shape = outer_cylinder.cut(inner_cylinder)
else: #invert rather than error out
tube_shape = inner_cylinder.cut(outer_cylinder)
if not hasattr(fp, "positionBySupport"):
self.makeAttachable(fp)
fp.positionBySupport()
tube_shape.Placement = fp.Placement
#BaseFeature (shape property of type Part::PropertyPartShape) is provided for us
#with the PartDesign::FeaturePython and related classes, but it might be empty
#if our object is the first object in the tree. it's a good idea to check
#for its existence in case we want to make type Part::FeaturePython, which won't have it
if hasattr(fp, "BaseFeature") and fp.BaseFeature != None:
if "Subtractive" in fp.TypeId:
full_shape = fp.BaseFeature.Shape.cut(tube_shape)
else:
full_shape = fp.BaseFeature.Shape.fuse(tube_shape)
full_shape.transformShape(fp.Placement.inverse().toMatrix(), True) #borrowed from gears workbench
fp.Shape = full_shape
else:
fp.Shape = tube_shape
if hasattr(fp,"AddSubShape"): #PartDesign::FeatureAdditivePython and
#PartDesign::FeatureSubtractivePython have this
#property but PartDesign::FeaturePython does not
#It is the shape used for copying in pattern features
#for example in making a polar pattern
tube_shape.transformShape(fp.Placement.inverse().toMatrix(), True)
fp.AddSubShape = tube_shape
class PDTubeVP:
def __init__(self, obj):
'''Set this object to the proxy object of the actual view provider'''
obj.Proxy = self
def attach(self,vobj):
self.vobj = vobj
def updateData(self, fp, prop):
'''If a property of the handled feature has changed we have the chance to handle this here'''
pass
def getDisplayModes(self,obj):
'''Return a list of display modes.'''
modes=[]
modes.append("Flat Lines")
modes.append("Shaded")
modes.append("Wireframe")
return modes
def getDefaultDisplayMode(self):
'''Return the name of the default display mode. It must be defined in getDisplayModes.'''
return "Flat Lines"
def setDisplayMode(self,mode):
'''Map the display mode defined in attach with those defined in getDisplayModes.\ Since they have the same names nothing needs to be done. This method is optional'''
return mode
def onChanged(self, vp, prop):
'''Here we can do something when a single property got changed'''
#FreeCAD.Console.PrintMessage("Change property: " + str(prop) + "\n")
pass
def getIcon(self):
'''Return the icon in XPM format which will appear in the tree view. This method is\ optional and if not defined a default icon is shown.'''
return """ /* XPM */ static const char * ViewProviderBox_xpm[] = { "16 16 6 1", " c None", ". c #141010", "+ c #615BD2", "@ c #C39D55", "# c #000000", "$ c #57C355", " ........", " ......++..+..", " .@@@@.++..++.", " .@@@@.++..++.", " .@@ .++++++.", " ..@@ .++..++.", "###@@@@ .++..++.", "##$.@@$#.++++++.", "#$#$.$$$........", "#$$####### ", "#$$#$$$$$# ", "#$$#$$$$$# ", "#$$#$$$$$# ", " #$#$$$$$# ", " ##$$$$$# ", " ####### "}; """
def __getstate__(self):
'''When saving the document this object gets stored using Python's json module.\ Since we have some un-serializable parts here -- the Coin stuff -- we must define this method\ to return a tuple of all serializable objects or None.'''
return None
def __setstate__(self,state):
'''When restoring the serialized object from document we have the chance to set some internals here.\ Since no data were serialized nothing needs to be done here.'''
return None
Ve şimdi nesneyi oluşturmak için makro dosyası içeriğini yazıyoruz:
# -*- coding: utf-8 -*-
#pdtube.FCMacro
import pdtube
#above line needed if the class definitions above are place in another file: PDTube.py
#this is needed if the tube object is to remain parametric after restarting FreeCAD and loading
#a document containing the object
body = FreeCADGui.ActiveDocument.ActiveView.getActiveObject("pdbody")
if not body:
FreeCAD.Console.PrintError("No active body.\n")
else:
from PySide import QtGui
window = FreeCADGui.getMainWindow()
items = ["Additive","Subtractive","Neither additive nor subtractive"]
item,ok =QtGui.QInputDialog.getItem(window,"Select tube type","Select whether you want additive, subtractive, or neither:",items,0,False)
if ok:
if item == items[0]:
className = "PartDesign::FeatureAdditivePython"
elif item == items[1]:
className = "PartDesign::FeatureSubtractivePython"
else:
className = "PartDesign::FeaturePython" #not usable in pattern features, such as polar pattern
tube = FreeCAD.ActiveDocument.addObject(className,"Tube")
pdtube.PDTube(tube)
pdtube.PDTubeVP(tube.ViewObject)
body.addObject(tube) #optionally we can also use body.insertObject() for placing at particular place in tree
Daha Fazla Bilgi için
Ek Sayfalar:
- Öznitelikleri kaydeden komut dosyası nesneleri
- Komut dosyasıyla oluşturulmuş nesneleri taşıma/yükseltme
- Ekli komut dosyası nesneleri
- Görünüm sağlayıcılar
Komut dosyasıyla yazılmış nesneler hakkında ilginç forum konuları:
- Yükleme esnasında kaybolan Python nesnesi özellikleri
- Yeni FeaturePython gridir
__getstate__
ve__setstate__
ile ilgili açıklama, resmi belgeler, official documentation- Eigenmode frekansı her zaman 0?
- python özelliğinin setEdit'e düzgün bir şekilde nasıl uygulanır?
Burada sunulan örneklere ek olarak, daha fazla örnek için FreeCAD kaynak kodu src/Mod/TemplatePyMod/FeaturePython.py'ye bakın.