Below, code of a simplified object to better understand what happens when creating a feature python object.
I have several question when executing this program.
Code: Select all
import FreeCAD as App
import FreeCADGui as Gui
import Part
def create(obj_name="TubeCompound"):
obj = App.ActiveDocument.addObject("Part::FeaturePython", "tube_1")
Tubetest(obj, 1000, 150)
obj.ViewObject.Proxy = 0
print("\n-> call to App.activeDocument().recompute()")
App.activeDocument().recompute()
class Tubetest:
def __init__(self, obj, length=1000, dia=150):
print("\n-> in init tubetest")
self.Type = "Tube"
obj.Proxy = self
self.Name = obj.Name
obj.addProperty('App::PropertyLength', 'length', 'Afmetingen', 'buis lengte').length = length
obj.addProperty('App::PropertyLength', 'dia', 'Afmetingen', 'buis buitendiameter').dia = dia
# obj.recompute()
def execute(self, obj):
print("\n-> in execute tubetest")
obj.Shape = Part.makeCylinder(obj.dia/2, obj.length)
def onChanged(self, obj, prop):
#https://forum.freecad.org/viewtopic.php?style=4&t=48182
print("\n-> Hello, I am a Tubetest object : {}".format(self))
print("My onChanged method is currently executed")
print("because the property {}".format(prop))
print("of the FeaturePython Object {}".format(obj.Label))
print("has been changed to {}".format(obj.getPropertyByName(prop)))
print("and this FeaturePython Object Proxy points to me : {}".format(obj.getPropertyByName("Proxy")))
Code: Select all
>>> test2.create()
-> in init tubetest
-> Hello, I am a Tubetest object : <fpo.tube.test2.Tubetest object at 0x0000025F0AEF05E0>
My onChanged method is currently executed
because the property Proxy
of the FeaturePython Object tube_1
has been changed to <fpo.tube.test2.Tubetest object at 0x0000025F0AEF05E0>
and this FeaturePython Object Proxy points to me : <fpo.tube.test2.Tubetest object at 0x0000025F0AEF05E0>
-> Hello, I am a Tubetest object : <fpo.tube.test2.Tubetest object at 0x0000025F0AEF05E0>
My onChanged method is currently executed
because the property length
of the FeaturePython Object tube_1
has been changed to 1000.0 mm
and this FeaturePython Object Proxy points to me : <fpo.tube.test2.Tubetest object at 0x0000025F0AEF05E0>
-> Hello, I am a Tubetest object : <fpo.tube.test2.Tubetest object at 0x0000025F0AEF05E0>
My onChanged method is currently executed
because the property dia
of the FeaturePython Object tube_1
has been changed to 150.0 mm
and this FeaturePython Object Proxy points to me : <fpo.tube.test2.Tubetest object at 0x0000025F0AEF05E0>
-> call to App.activeDocument().recompute()
-> in execute tubetest
-> Hello, I am a Tubetest object : <fpo.tube.test2.Tubetest object at 0x0000025F0AEF05E0>
My onChanged method is currently executed
because the property Shape
of the FeaturePython Object tube_1
has been changed to <Solid object at 0000025F13B002E0>
and this FeaturePython Object Proxy points to me : <fpo.tube.test2.Tubetest object at 0x0000025F0AEF05E0>
Text in Italic I left for reference because this is not the correct way of updating the object but what I do all the time. This should be handled by the document, therefore I commented out "obj.recompute()" in the __init__ function and added "App.activeDocument().recompute()" in the create() methode.
***
In the __init__() method I recompute the object (obj.recompute()) so the execute() function is called and the Shape is created. Because this Shape is assigned to the Shape property the onChanged() function is called and therefore the object is still touched - I think, is this correct?
To resolve this you have to recompute the object and then the object is ['Up-to-date']. This looks strange because a new Shape solid is created which calls again the onChanged() function.
Is this normal behavior? Can this be avoided that the execute() functions gets called two times?
***
2.Do all the properties have to be initialized in the __init__() function or may they also be defined and assigned in the execute() method? I don't think they all should be assigned in the __init__() function, because the Shape property is assigned in the execute() method and all my other code which assigns values to properties works fine. Is this correct and what is the best practice?
3. What to do in execute() and what in the onChanged() function?
- execute(): only the creation of the Shape and/or everything else also (see also point 2) (eg. calculate properties, create and assign other sub shapes which then can be used by other objects ...)
- onChanged(): handling when properties change in the GUI? I see that the execute() method is also called when changing a property in the GUI (as read elsewhere this should and is handled by the document it self).
Below is the output when changing property length to 1mm.
Code: Select all
-> Hello, I am a Tubetest object : <fpo.tube.test2.Tubetest object at 0x0000025F0AEF05E0>
13:48:25 My onChanged method is currently executed
13:48:25 because the property length
13:48:25 of the FeaturePython Object tube_1
13:48:25 has been changed to 1.0 mm
13:48:25 and this FeaturePython Object Proxy points to me : <fpo.tube.test2.Tubetest object at 0x0000025F0AEF05E0>
13:48:30
-> in execute tubetest
13:48:30
-> Hello, I am a Tubetest object : <fpo.tube.test2.Tubetest object at 0x0000025F0AEF05E0>
13:48:30 My onChanged method is currently executed
13:48:30 because the property Shape
13:48:30 of the FeaturePython Object tube_1
13:48:30 has been changed to <Solid object at 0000025F09EEBB80>
13:48:30 and this FeaturePython Object Proxy points to me : <fpo.tube.test2.Tubetest object at 0x0000025F0AEF05E0>
Do you have to use the onChanged() method for the object it self If it hasn't any child objects you wish to update, like code below? When to use it?
Code: Select all
def onChanged(self, obj, prop):
App.Console.PrintMessage("Change property: " + str(obj.Name) + ": " + str(prop) + "\n")
if prop == "Shape" and obj.Links[2].Placement.Base != App.Vector(0, 0, obj.Links[1].Hoogtebuis):
obj.Links[2].Placement.Base = App.Vector(0, 0, obj.Links[1].Hoogtebuis)
thanks in advance for the elaborated feedback and new insides !!