[tutorial] make python PartDesign feature

Need help, or want to share a macro? Post here!
Forum rules
Be nice to others! Respect the FreeCAD code of conduct!
Post Reply
User avatar
DeepSOIC
Veteran
Posts: 7896
Joined: Fri Aug 29, 2014 12:45 am
Location: used to be Saint-Petersburg, Russia

[tutorial] make python PartDesign feature

Post by DeepSOIC »

Hi!
Since PartDesign is open for invasion, I think it's worth publishing a little tutorial on what is needed to integrate into PartDesign workflow.

So far I figured out this:

1. build your python feature on PartDesign::FeaturePython

Code: Select all

new_feature = App.ActiveDocument.addObject('PartDesign::FeaturePython', 'NameOfFeature')
proxy = MyCoolFeature(new_feature)
vp_proxy = ViewProviderMyCoolFeature(new_feature.ViewObject)
PartDesign::FeaturePython offers:
* BaseFeature property, which points to the previous object in partdesign sequence. It can be None, if the thing is the first feature in the sequence.
* ViewObject has a method "makeTemporaryVisible", which can be handy in task dialog implementation.

As of now (0.17.10422), it offers no extra overrides compared to Part::FeaturePython


2. Creation and deletion
When creating a new object, you need to add it to active Body. To get active body, use this:

Code: Select all

body = Gui.ActiveDocument.ActiveView.getActiveObject('pdbody')
And to add the new object to body:

Code: Select all

body.addObject(new_feature)
As soon as you do that, Body takes care of integrating the new feature into the chain. It sets BaseFeature property, and updates its own Tip.

Likewise, when your feature is deleted, you should remove the feature from the body, so that it closes the gap in sequence.

Code: Select all

class ViewProviderMyCoolFeature:
    ...
    def onDelete(self, viewprovider, subname):
        body = ...
        body.removeObject(viewprovider.Object)
        return True

As you may have noticed, you need a way to find the body that contains the feature. ActiveBody won't do. In future, there will be a direct way of obtaining that from FreeCAD, but as of now, you have to roll your own.

Code: Select all

def findBodyOf(feature):
    bodies = feature.Document.findObjects("PartDesign::Body")
    result = [body for body in bodies if feature in body.Group]
    assert(len(result)<=1)
    return None if len(result)==0 else result[0]

3. executing
Your execute() should write the shape that includes all the previous features and your feature, to its Shape property. It must write out a single solid. Please note that solid.fuse(othersolid) returns a compound, not solid.

Special care should be taken around Placements. Getting Placement to move only the feature but not the rest of the object requires somewhat tricky manipulations with Placement and shape transform, so the easiest way around is to simply block out user from editing Placement, and update it to match the placement of the computed shape, like that:

Code: Select all

def execute(self, selfobj):
    ....
    selfobj.Placement = result_shape.Placement
    selfobj.Shape = result_shape
If you want to expose Placement for user editing (or for taking advantage of attachment), you should remember that as soon as you write your shape to selfobj.Shape, it will update resultshape.Placement to equal selfobj.Placement, completely ignoring the original resultshape.Placement. So you need to use resultshape.transformShape(...) to account for that.
User avatar
DeepSOIC
Veteran
Posts: 7896
Joined: Fri Aug 29, 2014 12:45 am
Location: used to be Saint-Petersburg, Russia

Re: How To make python PartDesign feature?

Post by DeepSOIC »

DeepSOIC wrote:* What is the easiest way to handle onDelete? Finding the next feature in sequence is a bit involved, so it would be nice to have an API for that...
Figured out the removal. Removing it from body is enough, it was my code being faulty that made me think it's not working.
User avatar
NormandC
Veteran
Posts: 18589
Joined: Sat Feb 06, 2010 9:52 pm
Location: Québec, Canada

Re: How To make python PartDesign feature

Post by NormandC »

Good topic idea. Suggestion: change subject to [How To] Make python PartDesign feature so it's clear it's a how to, not a question. ;)
User avatar
Joel_graff
Veteran
Posts: 1949
Joined: Fri Apr 28, 2017 4:23 pm
Contact:

Re: [tutorial] make python PartDesign feature

Post by Joel_graff »

So I've found this to be a very useful example of how to implement an FPO for a more specific purpose. The scripted objects page in the Wiki is invaluable, but lacking in terms of comprehensiveness.

I don't suppose @DeepSOIC can provide a better or more details on thid PartDesign implementation for an FPO? Maybe add it to the Wiki as an example for the Scripted Objects page?
FreeCAD Trails workbench for transportation engineering: https://www.github.com/joelgraff/freecad.trails

pivy_trackers 2D coin3D library: https://www.github.com/joelgraff/pivy_trackers
User avatar
easyw-fc
Veteran
Posts: 3633
Joined: Thu Jul 09, 2015 9:34 am

Re: [tutorial] make python PartDesign feature

Post by easyw-fc »

DeepSOIC wrote: Sun Mar 05, 2017 1:20 pm Hi!
thanks a lot! nice tutorial :D
I bookmarked it.
User avatar
jonasb
Posts: 162
Joined: Tue Dec 22, 2020 7:57 pm

Re: [tutorial] make python PartDesign feature

Post by jonasb »

Sorry for the corps revival, but this post is still the only resource I found on how to implement a PartDesign::FeaturePython... Thank you for that!
DeepSOIC wrote: Sun Mar 05, 2017 1:20 pm Likewise, when your feature is deleted, you should remove the feature from the body, so that it closes the gap in sequence.

Code: Select all

class ViewProviderMyCoolFeature:
    ...
    def onDelete(self, viewprovider, subname):
        body = ...
        body.removeObject(viewprovider.Object)
        return True
I found this not to be necessary with a v0.20 pre-release (0.20.25025), as Std_Delete seems to do exactly that (App.getDocument('foo').getObject('Body').removeObject(App.getDocument('foo').getObject('myfeature'))).
Do I miss something? At a first glance everything works as it should...
Or was this changed at some point in time (the original post predates the v0.17 release)? If so, do you know up to which FreeCAD versions this is still needed?
User avatar
jonasb
Posts: 162
Joined: Tue Dec 22, 2020 7:57 pm

Re: [tutorial] make python PartDesign feature

Post by jonasb »

I did a little empirical software archeology on that matter and here are my findings:

0.19 (r24267) behaves exactly as my early 0.20 prerelease, see above.

0.18.4 (r16146) does access the individual objects directly via properties instead of getObject('some_name'), but it does the Body.removeObject, too. (App.activeDocument().Body.removeObject(App.activeDocument().myfeature))

An so does 0.17 (r13541).

Therefore I assume the explicit Body.removeObject in the onDelete of the ViewProvider is not needed. The original tutorial predates the 0.17 release, and it seems things have changed in between.
Post Reply