Thu, Nov 7, 2:49 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: 2024 Sep 18 2:50 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: Scripted Shader Node Animation


Iuvenis_Scriptor ( ) posted Sun, 08 May 2022 at 11:41 PM · edited Thu, 07 November 2024 at 2:37 AM

I've been trying to write a fairly simple script that goes through various materials, identifies certain nodes based on a signature in their external names, animate certain inputs in that node based on index numbers provided by tags in their external names, and renames the resulting parameters.  I've rearranged things to avoid animating any inputs to custom-built compound nodes as at least a temporary solution to this apparent problem, but I'm still getting a NoneType error when the script tries to rename the new dial.  The first time I tested it, it actually seemed to work!  But upon further testing, it seems to work only sporadically.  I haven't yet been able to replicate the complete success of that first test, but most times, no dials seem to be created, and sometimes a few of the dials but definitely not all seem to be created before it gets stuck, apparently failing to find the very parameter it should've just created.  According to the print-outs, it's clearly finding the right nodes and inputs with no problem, but it can't seem to make up it's mind over whether it can animate them or not.  At least once, I've noticed a white key symbol in front of a targeted input but no corresponding dial in the parameters palette.

I use a node tagging system where the external name of a node I want to animate is tagged with signals to the script.  For example, for a node named "Testing || 1_2," the " || " says "Animate me!" and the numbers following it say, "Specifically inputs 1 and 2."  I have tweaked the script since that first test, but never in a way that should affect the core loop.  I've only tried to restrict the materials it applies to and/or load a PZ2 after everything else is done.  I'm working in Poser 12.0.757.  I've tried splitting the loop in two, just animating everything all at once in one loop and renaming the dials in a completely separate loop afterwards.  That did not help.  My code looks like this.


import poser
scene = poser.Scene()
character = scene.CurrentFigure()
actor = character.Actor("Body")
materials = actor.Materials()
main = ["SkinHead","SkinLimbs","SkinTorso","Iris","Fingernails","Teeth","Gums","Tongue"]
for material in materials:
    zone = material.Name()
    if zone in main:
        tree = material.ShaderTree()
        nodes = tree.Nodes()
        print(zone)
        for node in nodes:
            name = node.InternalName()
            label = node.Name()
            print("\t"+label)
            if " || " in label:
                numbers = label[label.find(" || ")+4:len(label)+1].split("_")
                for number in numbers:
                    index = int(number)
                    input = node.Input(index)
                    print("\t\t"+input.InternalName())
                    input.SetAnimated(1)
                    parameter = input.Parameters()[0]
                    identifier = zone+"_"+label[0:label.find(" || ")]+"_"+input.Name()
                    parameter.SetInternalName(identifier)
                    parameter.SetName(identifier)
                    print("\t\t"+identifier)


Thanks in advance for any tips on what I'm doing wrong!  I'll keep tinkering and researching.


structure ( ) posted Sun, 08 May 2022 at 11:55 PM · edited Sun, 08 May 2022 at 11:57 PM
Forum Coordinator

When dealing with compound nodes, you need to address the compounddata ....

 import poser
scene = poser.Scene()
actor = scene.CurrentActor()
materials = actor.Materials()
for material in materials:
    tree = material.ShaderTree()
    nodes = tree.Nodes()
    for node in nodes:
        try:
            data = node.CompoundData()
            containedNodes = data.ContainedNodes()
            for node in containedNodes:
                print( "\t"+u"├  "+f"{node.Type()} {node.Name()}" )    
        except:
            pass

Locked Out


Iuvenis_Scriptor ( ) posted Mon, 09 May 2022 at 1:10 AM · edited Mon, 09 May 2022 at 8:13 AM

Thanks, both for the code and for the links.  The Poser Python Methods Manual was exactly what I was looking for!

For the moment, though, I'm not manipulating any compound nodes, yet the error persists.  If I can't get it to work with normal nodes, I doubt it'll work any better with compound ones.


adp001 ( ) posted Mon, 09 May 2022 at 7:46 AM · edited Mon, 09 May 2022 at 7:46 AM

This works for me (P11):


import poser

scene = poser.Scene()
character = scene.CurrentFigure()
actor = character.Actor("Body")
materials = actor.Materials()

main = ["SkinHead", "SkinLimbs", "SkinTorso", "Iris", "Fingernails", "Teeth", "Gums", "Tongue"]
for (material, zone) in [(mat, mat.Name()) for mat in actor.Materials() if mat.Name() in main]:
    nodes = material.ShaderTree().Nodes()

    for node in [n for n in nodes if "||" in n.Name()]:
        label, p, numberstring = node.Name().partition("||")
        if p and numberstring:
            numberlist = [int(n) for n in numberstring.strip().split("_")]
            for number in numberlist:
                node.Input(number).SetAnimated(1)
            scene.DrawAll()

            for number in numberlist:
                parameter = node.Input(number).Parameters()[0]
                identifier = zone + "_" + label + "_" + input.Name()
                parameter.SetInternalName(identifier)
                parameter.SetName(identifier)
                print(identifier)




structure ( ) posted Mon, 09 May 2022 at 7:55 AM · edited Mon, 09 May 2022 at 8:20 AM
Forum Coordinator

try 

import re
main = ("skinhead", "skinlimbs", "skintorso",
        "iris", "fingernails", "teeth", "gums", "tongue")

for material in materials:
    zone = material.Name()
    if any(m in zone.lower() for m in main):
        tree = material.ShaderTree()
        nodes = tree.Nodes()
        print("%s in main" % zone)
        for node in nodes:
            label = node.Name()
            print("\t"+label)
            result = re.search('[||0-90-9]', label)
            if result:
                print(result)

output:
4_Gums in main PoserSurface Color_Texture Anisotropic 4_Gums 4_Teeth in main PoserSurface Color_Texture Anisotropic 4_Teeth 4_Tongue in main PoserSurface Color_Texture Anisotropic 4_Tongue 2_SkinTorso in main PoserSurface Color_Texture ||12 <_sre.SRE_Match object at 0x000000000E3B9CC8> ColorRamp Diffuse Color_Math Math_Functions 2_SkinTorso 5_Iris in main PoserSurface Color_Texture 5_Iris

Also finds :

Color_Texture ||1_2
<_sre.SRE_Match object at 0x000000000EA233D8> : 2_SkinTorso : Color_Texture ||1_2
Color_Texture |1_2
<_sre.SRE_Match object at 0x000000000EA233D8> : 2_SkinTorso : Color_Texture |1_2
Color_Texture |1_2|
<_sre.SRE_Match object at 0x000000000EA233D8> : 2_SkinTorso : Color_Texture |1_2|

you can then  run the rest of your script on the matched node.

Locked Out


adp001 ( ) posted Mon, 09 May 2022 at 8:29 AM

@structure: And to what extent does this help solve the problem?

List comprehension is much more effective than your approach with "any". Regular expressions are also not very helpful at this point, because it is about extracting a list consisting of integer numbers. Not single characters.




adp001 ( ) posted Mon, 09 May 2022 at 8:34 AM · edited Mon, 09 May 2022 at 8:36 AM

The reason why my version works is

scene.DrawAll()

The Poser UI is wxPython. A running Python script will interrupt Poser completely until the next request to do a refresh. This can be a refresh sent directly to the affected window, or Poser's global refresh command "scene.DrawAll()".


@ Iuvenis_Scriptor: Insert the resfresh into your own code and it will work as expected.




structure ( ) posted Mon, 09 May 2022 at 9:22 AM · edited Mon, 09 May 2022 at 9:27 AM
Forum Coordinator

@adp when looking for solutions, I find that a number of options  offered is always better than one solution, while you think your solution is best (and maybe it is)  It may not fit a coder's particular style, the user can look and try various versions to see which is best for him/her, without being criticized for how they prefer to code. 

The code I offered works to find the node. And while it may not be the most effective solution - it is a solution. 

also my code takes into account the case of names, so "SkinHead" will be found as will "skinhead""Skinhead", and "skinHead" etc.


Locked Out


adp001 ( ) posted Mon, 09 May 2022 at 9:53 AM

Sorry structure, you are generally right. But the problem to solve was not the programming style or how to split words and numbers,but to make that node property editable in the list of actor properties.




adp001 ( ) posted Mon, 09 May 2022 at 10:29 AM

The task was to find a "label" and a list of index numbers. Label and list separated by "||", the numbers separated by "_".

Your regular expression is not really helpful. As far as I know, the extraction can't be done in one go with regular expressions.

Separating label and number string is easy. You can do that with Python's standard "split". Or even with a regular expression (which split does anyway). I like to do this with "partition". This often saves a few tests and conversions.

label, p, numberstring = node.Name().partition("||")
(if p is now not equal to "||" label and numberstring exist but are None).

or:


if "||" in node.Name():
    label, numberstring = node.Name().split("||", 1)

The numberlist can be created very elegant and very "pythonic" with a list:
    numbers = [int(n) for n in numberstring.split("_")]

or with a regular expression:
    r = [int(n) for n in re.split(r"_", numberstring)]

Your regex:
    result = re.search('[||0-90-9]', label)

doesn't find anything particularly helpful (apart from the fact that "|" in a regex means "or", so it should be "\|\|"; twice "0-9" doesn't make much sense either - why not "\d"?). The first matching character from the string specified in square brackets is found (this should actually always be the first "|"). To get the desired result (a label and a list of arbitrarily large integers) you now have to do some work (examine the result and extract what is of interest). I find this quite cumbersome compared to a simple "split" and a generated list.





adp001 ( ) posted Mon, 09 May 2022 at 10:47 AM

structure posted at 9:22 AM Mon, 9 May 2022 - #4438338

also my code takes into account the case of names, so "SkinHead" will be found as will "skinhead""Skinhead", and "skinHead" etc.

what about this:


main = (n.lower() for n in ("SkinHead", "SkinLimbs", "SkinTorso", "Iris", "Fingernails", "Teeth", "Gums", "Tongue"))
for (material, zone) in [(mat, mat.Name()) for mat in actor.Materials() if mat.Name().lower() in main]:

...

Preserves the original Nodename.




structure ( ) posted Mon, 09 May 2022 at 11:39 AM
Forum Coordinator

that works too

Locked Out


Iuvenis_Scriptor ( ) posted Mon, 09 May 2022 at 12:45 PM

Thanks for all the suggestions, guys!  Alas, it has so far been to no avail.  With a temporary focus on just animating the right node inputs and getting the corresponding dials to appear (i.e. no renaming yet), my code now looks like this:


import poser
scene = poser.Scene()
character = scene.CurrentFigure()
actor = character.Actor("Body")
materials = actor.Materials()
main = ["SkinHead","SkinLimbs","SkinTorso","Iris","Fingernails","Teeth","Gums","Tongue"]
for (material,zone) in [(mat,mat.Name()) for mat in materials if mat.Name() in main]:
    nodes = (material.ShaderTree()).Nodes()
    for node in [element for element in nodes if " || " in element.Name()]:
        label,numbers = (node.Name()).split(" || ",1)
        indices = [int(number) for number in numbers.split("_")]
        name = node.InternalName()
        print("\t"+label)
        for index in indices:
            input = node.Input(index)
            print("\t\t"+input.Name())
            input.SetAnimated(1)
scene.DrawAll()


The same problem persists.  The node inputs display as animated (with the white key symbol) in the Material Room, but the Parameters palette in the Pose Room remains barren, which means the dials are most likely not being created, and any attempt to rename them is doomed to fail.


adp001 ( ) posted Mon, 09 May 2022 at 2:44 PM

I have tested the following version extensively with Poser 11 and it works fine. If it doesn't work in P12, then P12 seems to be broken :)


try:
    import poser
except ImportError:
    import POSER_FAKE as poser

scene = poser.Scene()
character = scene.CurrentFigure()
actor = character.Actor("Body")
materials = actor.Materials()

main = ["SkinHead", "SkinLimbs", "SkinTorso", "Iris", "Fingernails", "Teeth", "Gums", "Tongue"]
for (material, zone) in [(mat, mat.Name()) for mat in actor.Materials() if mat.Name() in main]:
    nodes = material.ShaderTree().Nodes()

    for node in [n for n in nodes if "||" in n.Name()]:
        label, p, numberstring = node.Name().partition("||")
        numberlist = [int(n) for n in numberstring.strip().split("_")]
        for number in numberlist:
            if hasattr(node.Input(number), "SetAnimated"):
                node.Input(number).SetAnimated(1)
            else:
                raise AttributeError("Number does not exist")
        scene.DrawAll()

        for number in numberlist:
            inp = node.Input(number)  # type: poser.ShaderNodeInputType
            parameter = inp.Parameters()
            for parameter in [p for p in inp.Parameters() if p]:
                identifier = zone + "_" + label + "_" + inp.Name()
                parameter.SetInternalName(identifier)
                parameter.SetName(identifier)
                print(identifier)






adp001 ( ) posted Mon, 09 May 2022 at 3:53 PM

This version can handle RGB inputs:


from __future__ import print_function
# should work with P11 and P12

try:
    import poser
except ImportError:
    import POSER_FAKE as poser

scene = poser.Scene()
character = scene.CurrentFigure()
actor = character.Actor("Body")
materials = actor.Materials()

# I prefer to leave the names as defined in the material (upper-/lowercase). But
# I'm using a set to prevent duplicates.
main = {"SkinLimbs", "SkinTorso", "Iris", "Fingernails", "Teeth", "Gums", "Tongue"}

for (material, zone) in [(mat, mat.Name()) for mat in actor.Materials() if mat.Name() in main]:
    print(material.Name())
    nodes = material.ShaderTree().Nodes()
    for node in [n for n in nodes if "||" in n.Name()]:  # type: poser.ShaderNodeType
        # Split the string at position "||". Spaces are simply ignored ("label || 1_2_3"
        # is just as valid as "label||1_2_3").
        label, p, numberstring = node.Name().partition("||")
        numberlist = [int(n) for n in (numberstring.strip().split("_") if numberstring else[])]
        for number in numberlist:
            # Indexnumber ok?
            if number < node.NumInputs():
                # animatable?
                if hasattr(node.Input(number), "SetAnimated"):
                    node.Input(number).SetAnimated(1)
                else:
                    raise AttributeError("Input is not animatable")
            else:
                raise IndexError("Input number does not exist")
        scene.DrawAll()

        for number in numberlist:
            inp = node.Input(number)  # type: poser.ShaderNodeInputType
            r_parm, g_parm, b_parm = inp.Parameters()
            if all(inp.Parameters()):
                for pre, parameter in zip("RGB", inp.Parameters()):
                    identifier = "%s_%s_%s_%s" % (zone, label.strip(), inp.Name(), pre)
                    parameter.SetInternalName(identifier)
                    parameter.SetName(identifier)
                    print("...", identifier)
            else:
                identifier = "%s_%s_%s" % (zone, label.strip(), inp.Name())
                r_parm.SetInternalName(identifier)
                r_parm.SetName(identifier)
                print("...", identifier)





Iuvenis_Scriptor ( ) posted Mon, 09 May 2022 at 4:37 PM
adp001 posted at 2:44 PM Mon, 9 May 2022 - #4438364

I have tested the following version extensively with Poser 11 and it works fine. If it doesn't work in P12, then P12 seems to be broken :)


try:
    import poser
except ImportError:
    import POSER_FAKE as poser

scene = poser.Scene()
character = scene.CurrentFigure()
actor = character.Actor("Body")
materials = actor.Materials()

main = ["SkinHead", "SkinLimbs", "SkinTorso", "Iris", "Fingernails", "Teeth", "Gums", "Tongue"]
for (material, zone) in [(mat, mat.Name()) for mat in actor.Materials() if mat.Name() in main]:
    nodes = material.ShaderTree().Nodes()

    for node in [n for n in nodes if "||" in n.Name()]:
        label, p, numberstring = node.Name().partition("||")
        numberlist = [int(n) for n in numberstring.strip().split("_")]
        for number in numberlist:
            if hasattr(node.Input(number), "SetAnimated"):
                node.Input(number).SetAnimated(1)
            else:
                raise AttributeError("Number does not exist")
        scene.DrawAll()

        for number in numberlist:
            inp = node.Input(number)  # type: poser.ShaderNodeInputType
            parameter = inp.Parameters()
            for parameter in [p for p in inp.Parameters() if p]:
                identifier = zone + "_" + label + "_" + inp.Name()
                parameter.SetInternalName(identifier)
                parameter.SetName(identifier)
                print(identifier)



Nope, no luck on my end.  I even tried it on my tablet, where the Poser 12 installation is much fresher (though it was version 12.0.5), and I didn't get an error, but I didn't get any visible dials either.  I'm beginning to suspect it may indeed be a problem with Poser 12 itself.


structure ( ) posted Mon, 09 May 2022 at 4:46 PM
Forum Coordinator

it does appear that P12 is broken, I have tested ADP's script in p12 and nothing happens. 

Locked Out


adp001 ( ) posted Tue, 10 May 2022 at 5:38 AM

@structure: Thanks for verifying.




structure ( ) posted Thu, 02 June 2022 at 12:06 PM
Forum Coordinator

One of the Devs provided the following script 

import poser
scene = poser.Scene()
f = scene.Figures()[0]
m = f.Materials()[0]
st = m.ShaderTree()
n = st.Nodes()[0]
for i in n.Inputs():
    if i.CanBeAnimated():
        i.SetAnimated(1)


which does work



Locked Out


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.