Scripted objects: Difference between revisions

From FreeCAD Documentation
(New page: Besides the standard object types such as annotations, meshes and parts objects, FreeCAD also offers the amazing possibility to build 100% python-scripted objects, called Python Features. ...)
 
Line 14: Line 14:
class Box:
class Box:
def __init__(self, obj):
def __init__(self, obj):
"Add some custom properties to our box feature"
"'''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","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","Width","Box","Width of the box").Width=1.0
Line 21: Line 21:
def onChanged(self, fp, prop):
def onChanged(self, fp, prop):
"Do something when a property has changed"
"'''Do something when a property has changed'''"
FreeCAD.Console.PrintMessage("Change property: " + str(prop) + "\n")
FreeCAD.Console.PrintMessage("Change property: " + str(prop) + "\n")
def execute(self, fp):
def execute(self, fp):
"Do something when doing a recomputation, this method is mandatory"
"'''Do something when doing a recomputation, this method is mandatory'''"
FreeCAD.Console.PrintMessage("Recompute Python Box feature\n")
FreeCAD.Console.PrintMessage("Recompute Python Box feature\n")
class ViewProviderBox:
class ViewProviderBox:
def __init__(self, obj):
def __init__(self, obj):
"Set this object to the proxy object of the actual view provider"
"'''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.addProperty("App::PropertyColor","Color","Box","Color of the box").Color=(1.0,0.0,0.0)
obj.Proxy = self
obj.Proxy = self
def attach(self, obj):
def attach(self, obj):
"Setup the scene sub-graph of the view provider, this method is mandatory"
"'''Setup the scene sub-graph of the view provider, this method is mandatory'''"
self.shaded = coin.SoGroup()
self.shaded = coin.SoGroup()
self.wireframe = coin.SoGroup()
self.wireframe = coin.SoGroup()
Line 56: Line 56:
def updateData(self, fp, prop):
def updateData(self, fp, prop):
"If a property of the handled feature has changed we have the chance to handle this here"
"'''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
# fp is the handled feature, prop is the name of the property that has changed
l = fp.getPropertyByName("Length")
l = fp.getPropertyByName("Length")
Line 65: Line 65:
def getDisplayModes(self,obj):
def getDisplayModes(self,obj):
"Return a list of display modes."
"'''Return a list of display modes.'''"
modes=[]
modes=[]
modes.append("Shaded")
modes.append("Shaded")
Line 72: Line 72:
def getDefaultDisplayMode(self):
def getDefaultDisplayMode(self):
"Return the name of the default display mode. It must be defined in getDisplayModes."
"'''Return the name of the default display mode. It must be defined in getDisplayModes.'''"
return "Shaded"
return "Shaded"
def setDisplayMode(self,mode):
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"
"'''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
return mode
def onChanged(self, vp, prop):
def onChanged(self, vp, prop):
"Here we can do something when a single property got changed"
"'''Here we can do something when a single property got changed'''"
FreeCAD.Console.PrintMessage("Change property: " + str(prop) + "\n")
FreeCAD.Console.PrintMessage("Change property: " + str(prop) + "\n")
if prop == "Color":
if prop == "Color":
Line 87: Line 88:
def getIcon(self):
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 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 """
return """
/* XPM */
/* XPM */
Line 117: Line 119:
def __getstate__(self):
def __getstate__(self):
''' When saving the document this object gets stored using Python's cPickle module.
"'''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
'''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.
'''to return a tuple of all pickable objects or None.'''"
'''
return None
return None
def __setstate__(self,state):
def __setstate__(self,state):
''' When restoring the pickled object from document we have the chance to set some
"'''When restoring the pickled object from document we have the chance to set some internals here.'''
internals here. Since no data were pickled nothing needs to be done here.
'''Since no data were pickled nothing needs to be done here.'''"
'''
return None
return None

Revision as of 13:17, 17 August 2009

Besides the standard object types such as annotations, meshes and parts objects, FreeCAD also offers the amazing possibility to build 100% python-scripted objects, called Python Features. Those objects will behave exactly as any other FreeCAD object, can be saved in a document and opened on any other installation of FreeCAD, since the python code that defines the object is also saved in the document.

Python Features follow the same rule as all FreeCAD features: they are separated into App and GUI parts. The app part, the Document Object, defines the geometry of our object, while its GUI part, the View Provider Object, defines how the object will be drawn on screen. The View Provider Object, as any other FreeCAD feature, is only available when you run FreeCAD in its own GUI. There are several properties and methods available to build your object. Properties must be of any of the predefined properties types that FreeCAD offers, and will appear in the property view window, so they can be edited by the user. This way, FeaturePython objects are truly and totally parametric. you can define properties for the Object and its ViewObject separately.

Example

The following sample can be found in the src/Mod/TemplatePyMod/FeaturePython.py file:

"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)

Properties

Properties are the true building stones of FeaturePython objects. Through them, the user will be able to interact and modify your object. After creating a new FeaturePython object in your document ( a=FreeCAD.ActiveDocument.addObject("App::FeaturePython","Box") ), you can get a list of the available properties by issuing:

a.supportedProperties()

You will get a list of available properties:

App::Property
App::PropertyLists
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::PropertyComplexGeoData
App::PropertyColor
App::PropertyColorList
App::PropertyMaterial
App::PropertyPath
App::PropertyFile
App::PropertyFileIncluded
Part::PropertyPartShape
Part::PropertyFilletContour
Part::PropertyCircle