Sat, Feb 8, 8:17 AM CST

Renderosity Forums / Poser Python Scripting



Welcome to the Poser Python Scripting Forum

Forum Moderators: Staff

Poser Python Scripting F.A.Q (Last Updated: 2025 Feb 05 6:41 am)

We now have a ProPack Section in the Poser FreeStuff.
Check out the new Poser Python Wish List thread. If you have an idea for a script, jot it down and maybe someone can write it. If you're looking to write a script, check out this thread for useful suggestions.

Also, check out the official Python site for interpreters, sample code, applications, cool links and debuggers. This is THE central site for Python.

You can now attach text files to your posts to pass around scripts. Just attach the script as a txt file like you would a jpg or gif. Since the forum will use a random name for the file in the link, you should give instructions on what the file name should be and where to install it. Its a good idea to usually put that info right in the script file as well.

Checkout the Renderosity MarketPlace - Your source for digital art content!



Subject: Example program: Figure hierarchy and recursion for beginners


Bastep ( ) posted Wed, 22 November 2023 at 3:58 PM · edited Sat, 08 February 2025 at 8:08 AM

Example program: Figure hierarchy and recursion for beginners

I have been playing with the actor hierarchy of a figure for the last few days.

This hierarchy is a classic data tree. It allows you to work very effectively with recursive methods. A recursive method is a method that calls itself again and again. It is important that this method has a termination condition that can be fulfilled. Otherwise our method disappears into nowhere.

The structure of the hierarchy of a figure is quite simple. Each figure has a root actor method. This returns the first actor. This is always the body actor.
Each actor has a list of actor children. This list can be queried with actor.Children().

This list also provides us with the perfect termination condition. If the list is empty, our recursive method jumps back to the previous recursive method.

Each actor of a figure has the type ID_TYPE_BODY_ACTOR = 7. ID_TYPE_BODY_ACTOR is a separate definition that I use for the actor.GetIdType() method. There are no definitions of the values returned by GetIdType() in the Poser API. I use GetIdType() to filter the objects I want to see and not see.

The example program can be downloaded here: Example program: Figure hierarchy and recursion
The installation is done as usual via

Here is the source code:

#----------------------------------------------------------------------------------
# RecursiveFigureCrawler.py
# Definition of the class for browsing the figure Hierachy
# Date:             03.11.2023 23:41:36
# Last Change:      08.11.2023 22:03:28
# Version:          1.0
# Author:           Bastep
# Contact:          via Renderocity
#
# This is free to use.
#----------------------------------------------------------------------------------

import poser

# ID Types for actor.GetIdType().
# Missing in the Poser API
ID_TYPE_BODY_ACTOR              =    7
ID_TYPE_CAMERA                  =    8
ID_TYPE_LIGHT                   =    9
ID_TYPE_PROPS                   =   12
ID_TYPE_CURVE                   =   15 # from  an0malaus https://www.renderosity.com/forums/comments/4477830/permalink
ID_TYPE_MAGNET_BASE             =   16 # Magnet
ID_TYPE_MAGNET_DEFORMER         =   18 # Magnet
ID_TYPE_MAGNET_ZONE             =   21 # Magnet
ID_TYPE_HAIRPROP                =   23 # Only applies to hair done in the hair room.
ID_TYPE_UNIFORM_FORCE_FIELDPROP =   24 # from  an0malaus https://www.renderosity.com/forums/comments/4477830/permalink
ID_TYPE_CONE_FORCE_FIELD_PROP   =   25 # from  an0malaus https://www.renderosity.com/forums/comments/4477830/permalink
ID_TYPE_CONTROL_HANDLE_PROP     =   28
ID_TYPE_GROUPING                =   29
ID_TYPE_MEASUREMENT_LINE        =   34
ID_TYPE_MEASUREMENT_ANGLE       =   35 # from  an0malaus https://www.renderosity.com/forums/comments/4477830/permalink
ID_TYPE_MEASUREMENT_CIRCLE      =   36 # from  an0malaus https://www.renderosity.com/forums/comments/4477830/permalink
ID_TYPE_MEASUREMENT_POLYLINE    =   37 # from  an0malaus https://www.renderosity.com/forums/comments/4477830/permalink

# The following ID covers various scene objects:
# CenterOfMass
# GoalCenterOfMass
# BackgroundMaterialActor
# AtmosphereMaterialActor
# FocusDistanceControl
ID_TYPE_CONTROL_PROP        =   26

ID_WITH_PARENTED_FIGURE = True
ID_UNUSED_PARENTED_FIGURE = False

#----------------------------------------------------------------------------------
# Class for representing the object hierarchy of a figure.
#----------------------------------------------------------------------------------
class RecursiveFigureCrawlerClass:
    def __init__(self):
        # Counter to correctly indent an object/actor when displaying the hierarchy.
        self.__HierachyCounter  = 0

        self.__Figure   = poser.Scene().CurrentFigure()
        self.__Filter   = [ID_TYPE_BODY_ACTOR]

        # Figures that are connected to the target figure as non-conforming
        # child figures (parented) can be hidden(value = false).
        self.__UseParentedFigures = False # True = show the parented Figures

        # The hierarchy of figure actors begins with the root actor.
        # This does not necessarily have to be the body actor.
        # You can use the chest actor, for example.
        # You will then see everything that is attached to the chest actor.
        if self.__Figure:
            self.__RootActor = self.__Figure.RootActor()
        else:
            self.__RootActor = None

        #Clean layout of the print edition
        self.__fs_actor = '{0:02}{1:s}{2:s} - {3}'
        self.__fs_figure = '{0:02}{1:s}{2:s} - {3:s} - {4}'

    #----------------------------------------------------------------------------------
    # The recursive method
    #----------------------------------------------------------------------------------
    def Crawl(self, actor):
        if actor:
            # The cancel query. If there is no further actor in the child list,
            # we jump to the last line (100) and set the hierarchy counter back by 1 step.
            # Then we go back to the call of the crawl method. And so on ...
            for child in actor.Children():
                self.__HierachyCounter += 1
                if self.CrawlFilter(child): #The filter method. What we want to see ...
                    # Calculating the indent for displaying the hierarchy levels
                    hierachy = ' ' * (self.__HierachyCounter * 3)
                    # If the object is a parented figure, the name of the figure
                    # is placed in front of the bodyactor name.
                    if child.Name() == 'Body':
                        figure = child.ItsFigure()
                        print(self.__fs_figure.format(self.__HierachyCounter, hierachy, figure.Name(), child.Name(), child.GetIdType()))
                    else:
                        print(self.__fs_actor.format(self.__HierachyCounter, hierachy, child.Name(), child.GetIdType()))
                # Here the recursive method calls itself.
                # In Python, the maximum recursion depth is 1000.
                # The value can be changed with the function sys.setrecursionlimit(n).
                # This value can be queried with sys.getrecursionlimit().
                # But I don't think you will exceed this limit in Poser.
                self.Crawl(child)
            self.__HierachyCounter -= 1

    #----------------------------------------------------------------------------------
    # The filter method.
    #----------------------------------------------------------------------------------
    def CrawlFilter(self, actor):
        if not self.__UseParentedFigures:
            # The 'actor.ItsFigure() == self.__Figure' test prevents the insertion
            # of parented figures into the list
            if actor.ItsFigure() != self.__Figure:
                return False
        # Filter out the props you do not want
        if actor.GetIdType() not in self.__Filter:
            return False
        return True

    #----------------------------------------------------------------------------------
    # Initiates the recusion process
    #----------------------------------------------------------------------------------
    def ShowFigureActorTree(self):
        if self.__RootActor:
            print(self.__Figure.Name(), ' - ', self.__RootActor.Name())
            self.__HierachyCounter = 0
            # This is where the recursion process begins.
            self.Crawl(self.__RootActor)
        else:
            print('No figure is selected.')

    #----------------------------------------------------------------------------------
    # Set the class variables
    #----------------------------------------------------------------------------------
    def SetFigure(self, figure):
        self.__Figure = figure

    # The parameter withparentedfigures can either have the value
    # ID_WITH_PARENTED_FIGURE(True) or ID_UNUSED_PARENTED_FIGURE(False).
    # The list of idtypes is assigned to the idtypes parameter.
    # These can be specified in any number. They are each separated by a comma.
    def SetFilter(self, withparentedfigures, *idtypes):
        self.__Filter = []
        self.__UseParentedFigures = withparentedfigures
        for idtype in idtypes:
            self.__Filter.append(idtype)

    def SetRootActor(self, rootactor):
        self.__RootActor = rootactor

    def SetWithParentedFigures(self, withparentedfigures=True):
        self.__UseParentedFigures = withparentedfigures


#----------------------------------------------------------------------------------
# FigureCrawler.py
#
# Example program for using the FigureCrawlerClass
#
# Date:             08.11.2023 21:01:16
# Letzte Aenderung: 08.11.2023 21:01:19
# Version:          1.0
# Author:           Bastep
# Contact:          via Renderocity
#
# This is free to use.
#----------------------------------------------------------------------------------

import poser
import RecursiveFigureCrawler as myCrawler
import importlib
importlib.reload(myCrawler)

#----------------------------------------------------------------------------------
# Main
#----------------------------------------------------------------------------------
def main():
    example = 0 # 0 = Uses the values that are defined when the class is initialized
    fc = myCrawler.RecursiveFigureCrawlerClass()
    
    # Example using the body actor. Props such as earrings,
    # necklaces etc. are displayed. Parented figures are also displayed.
    if example == 1:
        fc.SetFilter(myCrawler.ID_WITH_PARENTED_FIGURE, myCrawler.ID_TYPE_BODY_ACTOR, myCrawler.ID_TYPE_PROPS)

    # Example using the neck actor. Only the body actors belonging
    # to the figure are displayed.
    elif example == 2:
        
        figure = poser.Scene().CurrentFigure()
        actor = figure.ActorByInternalName('neck')
        fc.SetFigure(figure)
        fc.SetRootActor(actor)
        fc.SetFilter(myCrawler.ID_UNUSED_PARENTED_FIGURE, myCrawler.ID_TYPE_BODY_ACTOR)

    # shows the tree
    fc.ShowFigureActorTree()

if __name__ == "__main__":
    main()



Bastep ( ) posted Wed, 22 November 2023 at 4:14 PM

Addendum:
Installation in Poser via <File><Install from Zip Archive>.
You will then find the program under <Scripts><Poser 13 Content><Bastep><Examples>.
If you have installed the program in another runtime, use the name of this runtime. <Scripts><Any Runtime><Bastep><Examples>



an0malaus ( ) posted Sat, 25 November 2023 at 8:11 PM

Nice work @Bastep.

One observation is that given how long Poser's Python API has been developing, certain methods tended to vary in what returns they would give if they normally returned a list, but the list had no content. Actor.Children() is well behaved, in that it actually returns an empty list, making it safe to iterate over in a for loop.

Other methods tended to return None, which unhelpfully breaks for loops as it cannot be iterated. A quick hack to overcome this is:

for iter in ReturnsNoneIfListEmptyMethod() or []:
    ...

That way there's always something which can be iterated over or skipped if the list is empty and the expression/method returns None.



My ShareCG Stuff

Verbosity: Profusely promulgating Graham's number epics of complete and utter verbiage by the metric monkey barrel.


Bastep ( ) posted Sun, 26 November 2023 at 2:48 AM

an0malaus posted at 8:11 PM Sat, 25 November 2023 - #4478167

Nice work @Bastep.

One observation is that given how long Poser's Python API has been developing, certain methods tended to vary in what returns they would give if they normally returned a list, but the list had no content. Actor.Children() is well behaved, in that it actually returns an empty list, making it safe to iterate over in a for loop.

Other methods tended to return None, which unhelpfully breaks for loops as it cannot be iterated. A quick hack to overcome this is:

for iter in ReturnsNoneIfListEmptyMethod() or []:
    ...

That way there's always something which can be iterated over or skipped if the list is empty and the expression/method returns None.


Good morning! @an0malaus, thanks for the tip. Yes, the Poser API has its pitfalls. That's why I work extensively with the try/except statements.This was not necessary in the example program. Because, as you said, the Actor.Children() method does exactly what it should.
Have a nice Sunday ...



Privacy Notice

This site uses cookies to deliver the best experience. Our own cookies make user accounts and other features possible. Third-party cookies are used to display relevant ads and to analyze how Renderosity is used. By using our site, you acknowledge that you have read and understood our Terms of Service, including our Cookie Policy and our Privacy Policy.