FreeCad数据扩展

除了标注对象类型(如注释,网格和零件对象)以外,FreeCAD还提供了构建100%python脚本对象(称为Python功能)的可能性。这些对象的行为与任何其余FreeCAD对象彻底相同,并在文件保存/加载时自动保存和恢复。php

这些对象使用python的json模块保存在FreeCAD FcStd文件中。该模块将python对象转换为字符串,容许将其添加到保存的文件中。在加载时,json模块使用该字符串从新建立原始对象,前提是它能够访问建立该对象的源代码。这意味着若是保存这样的自定义对象并在不存在生成该对象的python代码的机器上打开它,则不会从新建立该对象。若是将这些对象分发给其余人,则须要分发建立它的python脚本。python

Python功能遵循与全部FreeCAD功能相同的规则:它们分为App和GUI部分。应用程序部分Document对象定义了对象的几何形状,而GUI部分View Provider Object定义了如何在屏幕上绘制对象。与任何其余FreeCAD功能同样,View Provider Object仅在您本身的GUI中运行FreeCAD时可用。有几个属性和方法可用于构建对象。属性必须是FreeCAD提供的任何预约义属性类型,而且将显示在属性视图窗口中,以便用户能够编辑它们。这样,FeaturePython对象就是真正彻底参数化的。您能够单独定义Object及其ViewObject的属性。web

提示:在之前的版本中,咱们使用了Python的cPickle模块。可是,该模块执行任意代码,从而致使安全问题。所以,咱们转向Python的json模块。shell

基本的例子

能够在src / Mod / TemplatePyMod / FeaturePython.py文件中找到如下示例,以及其余几个示例:json

'''Examples for a feature class and its view provider.'''

import FreeCAD, FreeCADGui
from pivy import coin

class Box:
    def __init__(self, obj):
        '''添加Box自定义属性特征'''
        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
        obj.Proxy = self
   
    def onChanged(self, fp, prop):
        '''定义属性改变时的操做'''
        FreeCAD.Console.PrintMessage("Change property: " + str(prop) + "\n")
 
    def execute(self, fp):
        '''在进行从新计算时执行某些操做,此方法是必需的'''
        FreeCAD.Console.PrintMessage("Recompute Python Box feature\n")

class ViewProviderBox:
    def __init__(self, obj):
        '''将此对象设置为实际视图提供者的代理对象Set this object to the proxy object of the actual view provider'''
        obj.addProperty("App::PropertyColor","Color","Box","Color of the box").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()
       
        data=coin.SoCube()
        self.shaded.addChild(self.scale)
        self.shaded.addChild(self.color)
        self.shaded.addChild(data)
        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(data)
        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
        l = fp.getPropertyByName("Length")
        w = fp.getPropertyByName("Width")
        h = fp.getPropertyByName("Height")
        self.scale.scaleFactor.setValue(float(l),float(w),float(h))
        pass
 
    def getDisplayModes(self,obj):
        '''返回显示模式列表Return a list of display modes.'''
        modes=[]
        modes.append("Shaded")
        modes.append("Wireframe")
        return modes
 
    def getDefaultDisplayMode(self):
        '''返回默认显示模式的名称。它必须在getDisplayModes中定义。Return the name of the default display mode. It must be defined in getDisplayModes.'''
        return "Shaded"
 
    def setDisplayMode(self,mode):
        '''将attach中定义的显示模式映射到getDisplayModes中定义的那些。\ 
                由于它们具备相同的名称,因此不须要作任何事情。这个方法是可选的
                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")
        if prop == "Color":
            c = vp.getPropertyByName("Color")
            self.color.rgb.setValue(c[0],c[1],c[2])
 
    def getIcon(self):
        '''返回XPM格式的图标,该图标将显示在树形视图中。此方法是\ 
                optional,若是未定义,则显示默认图标。
                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):
        '''保存文档时,使用Python的json模块存储此对象。
                返回全部可序列化对象的元组或无。
                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


def makeBox():
    FreeCAD.newDocument()
    a=FreeCAD.ActiveDocument.addObject("App::FeaturePython","Box")
    Box(a)
    ViewProviderBox(a.ViewObject)

makeBox()

可用的属性

属性是FeaturePython对象的真正构建元素。经过它们,用户将可以交互和修改您的对象。在文档中建立新的FeaturePython对象(obj = FreeCAD.ActiveDocument.addObject(“App :: FeaturePython”,“Box”))后,您能够经过发出如下命令获取可用属性的列表:安全

obj.supportedProperties()

您将得到可用属性的列表:app

App :: PropertyBool 
App :: PropertyBoolList 
App :: PropertyFloat 
App :: PropertyFloatList 
App :: PropertyFloatConstraint 
App :: PropertyQuantity 
App :: PropertyQuantityConstraint 
App :: PropertyAngle 
App :: PropertyDistance 
App :: PropertyLength 
App :: PropertySpeed 
App :: PropertyAcceleration 
App: :PropertyForce 
App :: PropertyPressure 
App :: PropertyInteger 
App :: PropertyIntegerConstraint 
App :: PropertyPercent 
App :: PropertyEnumeration 
App :: PropertyIntegerList 
App :: PropertyIntegerSet 
App :: PropertyMap 
App :: PropertyString 
App :: PropertyUUID 
App :: PropertyFont
App :: PropertyStringList 
App :: PropertyLink 
App :: PropertyLinkSub 
App :: PropertyLinkList 
App :: PropertyLinkSubList 
App :: PropertyMatrix 
App :: PropertyVector 
App :: PropertyVectorList 
App :: PropertyPlacement 
App :: PropertyPlacementLink 
App :: PropertyColor 
App :: PropertyColorList 
App: :PropertyMaterial 
App :: PropertyPath 
App :: PropertyFile 
App :: PropertyFileIncluded 
App :: PropertyPythonObject 
Part :: PropertyPartShape 
Part :: PropertyGeometryList 
Part :: PropertyShapeHistory 
Part :: PropertyFilletEdges 
Sketcher :: PropertyConstraintList

向自定义对象添加属性时,请注意如下事项:ide

  • 不要在属性描述中使用字符“<”或“>”(这会破坏.fcstd文件中的xml片断)
  • 属性按字母顺序存储在.fcstd文件中。若是属性中有形状,则按字母顺序在“Shape”以后的任何属性将在形状以后加载,这可能会致使奇怪的行为。

财产类型

默认状况下,能够更新属性。可使属性为只读,例如在想要显示方法结果的状况下。也能够隐藏酒店。可使用设置属性类型函数

obj.setEditorMode(“MyPropertyName”,mode)

其中mode是一个short int,能够设置为:ui

0  - 默认模式,读写
 1  - 只读
 2  - 隐藏

在FreeCAD文件从新加载时未设置EditorModes。这能够经过__setstate__函数完成。请参阅http://forum.freecadweb.org/v...。经过使用setEditorMode,属性仅在PropertyEditor中读取。它们仍然能够从python中更改。要真正使它们只读,必须直接在addProperty函数内传递设置。有关示例,请参见http://forum.freecadweb.org/v...


其余更复杂的例子

此示例使用“ 零件模块”建立八面体,而后使用“关键”建立其硬币表示。

首先是Document对象自己:

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
Then, we have the view provider object, responsible for showing the object in the 3D scene:

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
相关文章
相关标签/搜索