Scripted objects

From FreeCAD Documentation
Revision as of 22:43, 6 December 2013 by Renatorivo (talk | contribs) (page)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

Förutom standard objekttyper som annoteringar, nät och delobjekt, så erbjuder FreeCAD också den fantastiska möjligheten att bygga 100% python-skriptade objekt, som kallas för Python Funktioner. dessa objekt kommer att bete sig exakt som alla andra FreeCAD objekt, de kan sparas i ett dokument och öppnas av en annan FreeCAD installation, eftersom den python kod som definierar objektet även sparas i dokumentet.


Python Funktioner följer samma regler som alla FreeCAD funktioner: de är separerade i App och GUI delar. App delen, Dokumentobjektet, definierar vårt objekts geometri , medan dess gränssnittsdel, Visaobjektet, definierar hur objektet kommer att ritas på skärmen. VisaObjektet, är som alla andra FreeCAD funktioner, endast tillgängligt när du kör FreeCAD i dess eget gränssnitt. Det finns flera egenskaper och metoder tillgängliga för att bygga ditt objekt. Egenskaperna måste vara en av de fördefinierade egenskapstyperna som FreeCAD erbjuder, och kommer att visas i egenskapsfönstret, så de kan redigeras av användaren. på detta sätt så är Python Funktionsobjekt totalt parametrisk. Du kan definiera egenskaper för objektet och dess Visaobjekt separat.


Enkelt exempel

Följande exempel kan hittas i src/Mod/TemplatePyMod/FeaturePython.py filen, tillsammans med flera andra exempel:

"Exempel på en funktionsklass och dess visare."

import FreeCAD, FreeCADGui
from pivy import coin

class Box:
	def __init__(self, obj):
		"Lägg till några anpassade egenskaper till vår lådfunktion"
		obj.addProperty("App::PropertyLength","Length","Box","Lådans längd").Length=1.0
		obj.addProperty("App::PropertyLength","Width","Box","Lådans bredd").Width=1.0
		obj.addProperty("App::PropertyLength","Height","Box", "Lådans höjd").Height=1.0
		obj.Proxy = self
	
	def onChanged(self, fp, prop):
		"Gör något när en egenskap har förändrats"
		FreeCAD.Console.PrintMessage("Change property: " + str(prop) + "\n")
 
	def execute(self, fp):
		"Gör något när en omberäkning görs, denna metod är obligatorisk"
		FreeCAD.Console.PrintMessage("Beräkna om Python lådfunktion\n")
 
class ViewProviderBox:
	def __init__(self, obj):
		"Sätt detta objekt till den aktuella visarens proxy objekt"
		obj.addProperty("App::PropertyColor","Color","Box","Color of the box").Color=(1.0,0.0,0.0)
		obj.Proxy = self
 
	def attach(self, obj):
		"Ställ in visarens scen subgraf, denna metod är obligatorisk"
		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):
		"Om en egenskap på den hanterade funktionen har förändrats, så har vi chansen att hantera det här"
		# fp är den hanterade funktionen, prop är namnet på den egenskap som har förändrats
		l = fp.getPropertyByName("Length")
		w = fp.getPropertyByName("Width")
		h = fp.getPropertyByName("Height")
		self.scale.scaleFactor.setValue(l,w,h)
		pass
 
	def getDisplayModes(self,obj):
		"Returnera en lista på visningslägen."
		modes=[]
		modes.append("Shaded")
		modes.append("Wireframe")
		return modes
 
	def getDefaultDisplayMode(self):
		"Returnera namnet på standardvisningsläget. Det måste definieras i getDisplayModes."
		return "Shaded"
 
	def setDisplayMode(self,mode):
		"Kartlägg det definierade visningsläget i anknytning med de som definierats i getDisplayModes.
                Eftersom de har samma namn så behöver inget göras. Denna metod är valfri"
		return mode
 
	def onChanged(self, vp, prop):
		"Här kan vi göra något när en enstaka egenskap har ändrats"
		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):
		"Returnera ikonen i XPM vilken kommer att synas i trädvyn. Denna metod är
                valfri och om den inte definieras så visas en standardikon."
		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):
		"När dokumentet sparas så kommer detta objekt att sparas genom användandet av Python's cPickle modul.
                Eftersom vi har lite o-plockbart här -- Coin sakerna -- så måste vi definiera denna metod
                för att returnera en tupel på alla plockbara objekt eller inget."
		return None
 
	def __setstate__(self,state):
		"När det plockade objektet återkallas från dokumentet så har vi chansen ställa in några interna saker här.
                Eftersom ingen data plockades, så behöver inget göras här."
		return None
 
 
def makeBox():
	FreeCAD.newDocument()
	a=FreeCAD.ActiveDocument.addObject("App::FeaturePython","Box")
	Box(a)
 	ViewProviderBox(a.ViewObject)


Tillgängliga egenskaper

Egenskaper är PythonFunktion objektens sanna byggstenar. Genom dem, så kan användaren interagera och ändra objektet. Efter att ett nytt PythonFunktion objekt har skapats i ditt dokument ( a=FreeCAD.ActiveDocument.addObject("App::FeaturePython","Box") ), så kan du få en lista på de tillgängliga egenskaperna genom att skriva:

a.supportedProperties()


Du kommer att få en lista på tillgängliga egenskaper:

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

När du adderar egenskaper till dina anpassade objekt, var uppmärksam på detta:

  • Använd inte tecknen "<" eller ">" i egenskapsbeskrivningen (det förstör xml delarna i .fcstd filen)
  • Egenskaper lagras i alfabetisk ordning i en .fcstd fil. Om du har en form i dina egenskaper, så kommer alla egenskaper vars namn kommer efter "Form" i alfabetisk ordning att laddas EFTER formen, vilket kan orsaka ett märkligt beteende.


Andra mer komplexa exempel

Detta exempel använder sig av Del Modulen för att skapa en oktahedron, sedan skapas dess coin representation med pivy.

Det första är själva Dokument objektet:

import FreeCAD, FreeCADGui, Part

   class Octahedron:
      def __init__(self, obj):
         "Lägg till lite anpassade egenskaper till vår låda"
         obj.addProperty("App::PropertyLength","Length","Octahedron","Oktahedronens längd").Length=1.0
         obj.addProperty("App::PropertyLength","Width","Octahedron","Oktahedronens bredd").Width=1.0
         obj.addProperty("App::PropertyLength","Height","Octahedron", "Oktahedronens höjd").Height=1.0
         obj.addProperty("Part::PropertyPartShape","Shape","Octahedron", "Oktahedronens form")
         obj.Proxy = self

      def execute(self, fp):
         # Definiera sex hörn för formen
         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)
         
         # skapa trådarna/ytorna
         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

      # hjälpmetod för att skapa ytorna
      def make_face(self,v1,v2,v3):
         wire = Part.makePolygon([v1,v2,v3,v1])
         face = Part.Face(wire)
         return face


Sedan så har vi visarobjektet, ansvarigt för att visa objektet i 3D scenen:

   class ViewProviderOctahedron:
      def __init__(self, obj):
         "Sätt detta objekt till de aktuella visarens proxy objekt "
         obj.addProperty("App::PropertyColor","Color","Octahedron","Oktahedronens färg").Color=(1.0,0.0,0.0)
         obj.Proxy = self

      def attach(self, obj):
         "Ställ in visarens scen subgraf, denna metod är obligatorisk"
         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):
		"Om en egenskap på den hanterade funktionen har förändrats, så har vi chansen att hantera det här"
		# fp är den hanterade funktionen, prop är namnet på den egenskap som har förändrats
         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):
         "Returnera en lista på visningslägen."
         modes=[]
         modes.append("Shaded")
         modes.append("Wireframe")
         return modes

      def getDefaultDisplayMode(self):
         "Returnera namnet på standardvisningsläget. Det måste definieras i getDisplayModes."
         return "Shaded"

      def setDisplayMode(self,mode):
         return mode

      def onChanged(self, vp, prop):
         "Här kan vi göra något när en enstaka egenskap har ändrats"
         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


Slutligen, när vårt objekt och dess visningsobjekt är definierade, så behöver vi bara anropa dem:

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


Göra objekt valbara

Om du vill göra ditt objekt valbart, eller åtminstone en del av den, genom att klicka på den i vyn, så måste du inkludera dess geometri inuti en SoFCSelection nod. Om ditt objekt har en komplex representation, med widgetar, annoteringar, etc, så kanske du bara vill inkludera en del av den i en SoFCSelection. Allt som är en SoFCSelection skannas konstant av FreeCAD för att detektera val/förval, så det är vettigt att inte försöka överbelasta den med onödig skanning. Detta är vad du skulle göra för att inkludera en self.face från det ovan visade exemplet:

selectionNode = coin.SoType.fromName("SoFCSelection").createInstance()
selectionNode.documentName.setValue(FreeCAD.ActiveDocument.Name)
selectionNode.objectName.setValue(obj.Object.Name) # här är obj ViewObject, vi behöver dess associerade App Object
selectionNode.subElementName.setValue("Face")
selectNode.addChild(self.face)
...
self.shaded.addChild(selectionNode)
self.wireframe.addChild(selectionNode)


Du skapar en SoFCSelection nod, sedan lägger du till dina geometrinoder till den, sedan lägger du till den till din huvudnod, istället för att lägga till dina geometrinoder direkt.


Arbeta med enkla former

Om ditt parametriska objektbara resulterar i en form, så behöver du inte använda ett visarobjekt. Formen kommer att visas med hjälp av FreeCAD's standard form representation:

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):
      "Skriv ut ett kort meddelande när en omberäkning görs, denna metod är obligatorisk" 
     fp.Shape = Part.makeLine(fp.p1,fp.p2)

a=FreeCAD.ActiveDocument.addObject("Part::FeaturePython","Line")
Line(a)
a.ViewObject.Proxy=0 # Ställ bara in den till något annan än None (denna assignering behövs för att köra en intern notifiering)


PyQt
Embedding FreeCAD
Available translations of this page: