Scripted objects/de: Difference between revisions

From FreeCAD Documentation
mNo edit summary
mNo edit summary
Line 374: Line 374:
== Objekte wählbar machen ==
== Objekte wählbar machen ==


Wollen Sie Ihr Objekt wählbar machen, oder zumindest ein Teil davon, indem Sie im Editor darauf klicken, müssen Sie seine coin-Geometrie in einen SoFCSelection-Knoten enibinden. Verfügt Ihr Objekt über komplexe Darstellung, mit Widgets, Anmerkungen, etc., möchten Sie vielleicht nur einen Teil davon in einem SoFCSelection einschliessen. Alles, was ein SoFCSelection ist, wird ständig durch FreeCAD gescannt, um eine Auswahl/Vorwahl zu entdecken, der Sinn dabei ist, zu versuchen, es nicht mit unnötigen Abtastungen zu überlasten. Folgendes würden Sie tun, um einen self.face vom Beispiel oben einzuschließen:


---------------
'''Fortsetzung folgt -- to be continued ''


If you want to make your object selectable, or at least part of it, by clicking on it in the viewport, you must include its coin geometry inside a SoFCSelection node. If your object has complex representation, with widgets, annotations, etc, you might want to include only a part of it in a SoFCSelection. Everything that is a SoFCSelection is constantly scanned by FreeCAD to detect selection/preselection, so it makes sense try not to overload it with unneeded scanning. This is what you would do to include a self.face from the example above:


selectionNode = coin.SoType.fromName("SoFCSelection").createInstance()
selectionNode = coin.SoType.fromName("SoFCSelection").createInstance()
Line 391: Line 385:
self.wireframe.addChild(selectionNode)
self.wireframe.addChild(selectionNode)


== Arbeiten mit einfachen Formen ==
Simply, you create a SoFCSelection node, then you add your geometry nodes to it, then you add it to your main node, instead of adding your geometry nodes directly.

== Working with simple shapes ==


Erstellen Sie einfach einen SoFCSelection Knoten, dann fügen Sie Ihre Geometrie-Knoten dazu hinzu, dann fügen Sie alles zu Ihrem Hauptknoten hinzu, anstatt Ihre Geometrie-Knoten direkt einzufügen. Die Form wird mittels der FreeCAD Standard-Form-Darstellung angezeigt:
If your parametric object simply outputs a shape, you don't need to use a view provider object. The shape will be displayed using FreeCAD's standard shape representation:


class Line:
class Line:
Line 410: Line 402:
a=FreeCAD.ActiveDocument.addObject("Part::FeaturePython","Line")
a=FreeCAD.ActiveDocument.addObject("Part::FeaturePython","Line")
Line(a)
Line(a)
a.ViewObject.Proxy=0 # just set it to something different from None (this assignment is needed to run an internal notification)
a.ViewObject.Proxy=0 # einfach etwas anderes als, nichts angeben(Diese Zuordnung ist notwendig für interne Vorgänge)
FreeCAD.ActiveDocument.recompute()
FreeCAD.ActiveDocument.recompute()


{{docnav/de|PyQt|Embedding FreeCAD}}
{{docnav/de|PyQt/de|Embedding FreeCAD/de}}


{{languages | {{en|Scripted objects}} {{es|Scripted objects/es}} {{ru|Scripted objects/ru}} {{se|Scripted objects/se}} }}
{{languages | {{en|Scripted objects}} {{es|Scripted objects/es}} {{ru|Scripted objects/ru}} {{se|Scripted objects/se}} }}

Revision as of 14:07, 6 April 2012

Außer den Standardobjektarten wie Anmerkungen, Mesh-und Teile-Objekten, bietet FreeCAD auch die erstaunliche Möglichkeit an, 100%-ige python-Skript-Objekte zu erstellen, genannt Python Features. Diese Objekte verhalten sich genau so wie jedes andere Objekt in FreeCAD und werden automatisch in Dateien gespeichert und wiederhergestellt, wenn laden / speichern ausgeführt wird.

Eine Besonderheit muss verstanden werden, jene Gegenstände werden in FreeCAD FcStd Dateien gespeichert mit python's cPickle-modul. Das Modul bildet aus einem Python-Objekt einen String, der dann einer gespeicherten Datei hinzugefügt werden kann. Beim Dateiöffnen, verwendet das cPickle Modul diese Zeichenfolge , um das ursprüngliche Objekt neu zu erstellen, vorausgesetzt, es hat den Zugriff auf den Quellcode, welcher das Objekt erstellt hat. Dies bedeutet, dass wenn Sie ein solches benutzerdefiniertes Objekt speichern und öffnen es auf einer Maschine, wo der Python-Code, der das Objekt erzeugt hat, ist nicht vorhanden, wird das Objekt nicht neu erstellt werden.

Python-Features folgen der gleichen Regeln wie alle FreeCAD Features: sie werden in App und GUI Teile getrennt. Der App Teil, das Document Object, definiert die Geometrie unseres Objektes, während der GUI-Teil, das View-Objekt, definiert, wie das Objekt auf dem Bildschirm gezeichnet wird. Das View-Objekt, ist wie jedes andere FreeCAD Feature nur verfügbar, wenn Sie FreeCAD in seiner eigenen GUI ausführen. Es gibt mehrere Eigenschaften und Methoden, die verfügbar sind, um Ihr Objekt zu erstellen. Eigenschaften muss von einem der in FreeCAD vordefinierten Typen sein, um im Eigenschafts-View-Fenster zu erscheinen, wo sie vom Benutzer editiert werden können. Auf diese Weise sind FeaturePython-Objekte wirklich und völlig parametrisch. Sie können die Eigenschaften für den Gegenstand und sein ViewObjekt getrennt definieren.


Grundlegendes Beispiel

Das folgende Beispiel kann in der src/Mod/TemplatePyMod/FeaturePython.py-Datei gefunden werden, zusammen mit einigen anderen Beispielen:

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

import FreeCAD, FreeCADGui
from pivy import coin

class Box:
	def __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
		obj.Proxy = self
	
	def onChanged(self, fp, prop):
		"Do something when a property has changed"
		FreeCAD.Console.PrintMessage("Change property: " + str(prop) + "\n")
 
	def execute(self, fp):
		"Do something when doing a recomputation, this method is mandatory"
		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(l,w,h)
		pass
 
	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):
		"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):
		"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():
	FreeCAD.newDocument()
	a=FreeCAD.ActiveDocument.addObject("App::FeaturePython","Box")
	Box(a)
 	ViewProviderBox(a.ViewObject)

Verfügbare Eigenschaften

Eigenschaften sind die wahren Bausteine von FeaturePython-Gegenständen. Durch ist der Benutzer im Stande, mit einem Objekt zu interagieren und es zu ändern. Nach dem Erstellen eines neuen FeaturePython-Objekts in Ihrem Dokument( a=FreeCAD.ActiveDocument.addObject("App::FeaturePython","Box") ), können eine Liste der verfügbaren Eigenschaften bekommen, indem Sie folgendes eingeben:

a.supportedProperties()

Sie werden eine Liste von verfügbaren Eigenschaften bekommen mit:

App::PropertyBool
App::PropertyFloat
App::PropertyFloatList
App::PropertyFloatConstraint
App::PropertyAngle
App::PropertyDistance
App::PropertyInteger
App::PropertyIntegerConstraint
App::PropertyPercent
App::PropertyEnumeration
App::PropertyIntegerList
App::PropertyString
App::PropertyStringList
App::PropertyLink
App::PropertyLinkList
App::PropertyMatrix
App::PropertyVector
App::PropertyVectorList
App::PropertyPlacement
App::PropertyPlacementLink
App::PropertyColor
App::PropertyColorList
App::PropertyMaterial
App::PropertyPath
App::PropertyFile
App::PropertyFileIncluded
Part::PropertyPartShape
Part::PropertyFilletContour
Part::PropertyCircle

Beim Hinzufügen von Eigenschaften zu benutzerdefinierten Objekte, achten Sie bitte auf folgendes:

  • Verwenden Sie keine Zeichen "<" oder ">" in den Eigenschaftes-Beschreibungen (das würde den XML-Teil in der .Fcstd-Datei zerbrechen)
  • Eigenschaften werden alphabetisch in einer .fcstd Datei gespeichert. Wenn Sie eine Form("Shape") in Ihren Eigenschaften haben, wird jede Eigenschaft, deren Name in alphabetischen Reihenfolge nach "Shape" kommt, auch nach der Form geladen, was zu seltsamen Verhaltensweisen führen kann.

Andere komplexere Beispiele

Dieses Beispiel macht von Part Module Gebrauch, um ein Oktaeder zu schaffen, erstellt dann seine coin-Darstellung mit pivy. Zuerst erstellen wir das Document-Objekt selbst:

import FreeCAD, FreeCADGui, Part

   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

Dann haben wir das Darstellungs-Objekt, verantwortlich für die Ansicht des Objekts in der 3D-Szene:

   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

Schließlich, sobald unser Objekt und sein Darstellungs-Objekt definiert sind, müssen wir sie nur noch aufrufen:

      FreeCAD.newDocument()
      a=FreeCAD.ActiveDocument.addObject("App::FeaturePython","Octahedron")
      Octahedron(a)
      ViewProviderOctahedron(a.ViewObject)

Objekte wählbar machen

Wollen Sie Ihr Objekt wählbar machen, oder zumindest ein Teil davon, indem Sie im Editor darauf klicken, müssen Sie seine coin-Geometrie in einen SoFCSelection-Knoten enibinden. Verfügt Ihr Objekt über komplexe Darstellung, mit Widgets, Anmerkungen, etc., möchten Sie vielleicht nur einen Teil davon in einem SoFCSelection einschliessen. Alles, was ein SoFCSelection ist, wird ständig durch FreeCAD gescannt, um eine Auswahl/Vorwahl zu entdecken, der Sinn dabei ist, zu versuchen, es nicht mit unnötigen Abtastungen zu überlasten. Folgendes würden Sie tun, um einen self.face vom Beispiel oben einzuschließen:

selectionNode = coin.SoType.fromName("SoFCSelection").createInstance()
selectionNode.documentName.setValue(FreeCAD.ActiveDocument.Name)
selectionNode.objectName.setValue(obj.Object.Name) # here obj is the ViewObject, we need its associated App Object
selectionNode.subElementName.setValue("Face")
selectNode.addChild(self.face)
...
self.shaded.addChild(selectionNode)
self.wireframe.addChild(selectionNode)

Arbeiten mit einfachen Formen

Erstellen Sie einfach einen SoFCSelection Knoten, dann fügen Sie Ihre Geometrie-Knoten dazu hinzu, dann fügen Sie alles zu Ihrem Hauptknoten hinzu, anstatt Ihre Geometrie-Knoten direkt einzufügen. Die Form wird mittels der FreeCAD Standard-Form-Darstellung angezeigt:

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 # einfach etwas anderes als, nichts angeben(Diese Zuordnung ist notwendig für interne Vorgänge)
FreeCAD.ActiveDocument.recompute()
PyQt/de
Embedding FreeCAD/de
Available translations of this page: Template:Se