Tue, Apr 30, 1:01 AM CDT

Welcome to the Poser Python Scripting Forum

Forum Moderators: Staff

Poser Python Scripting F.A.Q (Last Updated: 2024 Apr 26 1:10 pm)

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: Python For Poser by PhilC


OberonRex ( ) posted Thu, 14 September 2023 at 3:08 PM · edited Sun, 28 April 2024 at 10:28 PM

Does anyone know where I can get a copy of Python For Poser by PhilC?

The Renderosity Marketplace lists it for $29.95, but then shows it as "unavailable". As far as I can tell, PhilC is gone. I wouldn't mind paying him the $29.95, but so far can't find a copy anywhere, period.


ThunderStone ( ) posted Thu, 14 September 2023 at 5:38 PM

PhilC is dead. Not sure how paying him would work.


===========================================================

OS: Windows 11 64-bit
Poser: Poser 11.3 ...... Units: inches or meters depends on mood
Bryce: Bryce Pro 7.1.074
Image Editing: Corel Paintshop Pro
Renderer: Superfly, Firefly

9/11/2001: Never forget...

Smiles are contagious... Pass it on!

Today is the tomorrow you worried about yesterday

 


OberonRex ( ) posted Thu, 14 September 2023 at 5:55 PM

Yeah, I kind of figured that. I learned a lot from him, back in the days of poser 4. A real poser guru. So I figure that the book would be exactly what I'm looking for. Maybe someone has a copy I can buy, although I don't even know if it's hardback or a download.


hborre ( ) posted Thu, 14 September 2023 at 8:39 PM

The only problem is that Python for Poser is just about obsolete since it has adopted Python Script 3.  It may help with older versions of Poser and give you a basic knowledge of how to use it in Poser but some aspects of Python have changed.


OberonRex ( ) posted Fri, 15 September 2023 at 4:44 PM

I'm not particulary worried about the version of Python. I grok Python fairly well. What I'm looking for are definitions of the Poser classes,  methods, etc., and that's been a real bitch to find. However, for anyone else looking for similar information, I did find this site useful: https://vdocuments.mx/poser-python-methods-manual.html?page=1. Not perfect, but it is possible to dig out the needed information.


FVerbaas ( ) posted Sat, 16 September 2023 at 11:14 AM
Forum Coordinator

You can use the document system that comes within poserPython. It gives the current state. The PoserPython related info in PhilC's docs will by now be quite obsolete. This not because of Poser going to Python3, that did not change the PoserPython function calls, but because of development Poser had over time, API extensions,  and more. 

The key changes are in Python itself. 

 




OberonRex ( ) posted Sat, 16 September 2023 at 12:01 PM

Thanks for the tip! I'll definitely look for the document system within poserPython. And, yeah, it sounds like Phil's book wouldn't help. And you put your finger exactly and succinctly on what I am looking for too, current documentation about the poser classes, methods, etc., available in Python scripts.

It's curious though that you say that the key changes are in Python itself. I'm comfortable with either version of Python and not finding writing the Python code to be an issue at all, so the changes in Python don't show up as a problem. Actually, I think maybe I'm not exactly understanding that last sentence. Sorry.



FVerbaas ( ) posted Sat, 16 September 2023 at 2:03 PM
Forum Coordinator

in the shell import pydoc and browse it. Problem is it locks Poser until you quit the browser. The HTML page remains available though.

Most common problem with old scripts available on code (.py)  level is that 'print' now requires its arguments between brackets. Most PoserPython scripts available on source code level work if you add the brackets and make a fix here and there. Also I've seen scripts where some 'clever' use of strings is no longer supported.

Compiled scripts suffer from the 'magic number' issue. Sometimes decompiled code just runs, or does so after 'print brackets' was solved.

Last but not least there's often access rights issues with old scripts. Today a PC is no longer a personal computer. 

But if there were a 'Poser 13 Python Methods Manual' it would not be very different from same for Poser 11, except for the additions and corrections.    





OberonRex ( ) posted Sun, 17 September 2023 at 9:25 AM

I'm getting nowhere with pydoc. I import it but then I have enormous problems navigating around.

And I don't see how to browse it. I'm just stuck in the python shell window.

Any tips would be appreciated.

My experience with python, I have to admit, is totally outside this universe. Think Visual Studio and AWS.


FVerbaas ( ) posted Sun, 17 September 2023 at 12:54 PM
Forum Coordinator

import pydoc

pydoc.browse()


Access to Poser module is via link 'poser' in te reightmost column, about mid-height.


OberonRex ( ) posted Sun, 17 September 2023 at 2:02 PM

Wow!!! Absolutely Fantastic!! Thank you, thank you, thank you!!

And, btw, once you opened it in a browser, it seems to work to save a shortcut to your desktop (say) and later use that to relaunch that window, without needing to launch the python shell at all.


HartyBart ( ) posted Mon, 18 September 2023 at 3:20 AM

As you mention Visual Studio Code, ADP's Fake_poser3a.py may be a useful assistant. His site/page appears to have been unavailable for some months, and the Wayback Machine hasn't saved his .ZIP files (though some of the code insets still display). But here is his Fake_poser3a.py (latest version) for use with Visual Studio Code etc. I've added an expanded header.


# Fake_poser3a.py Class and Method headers to support editors while writing Python scripts.
# Works in Visual Studio Code
#
# Updated: 04/17/2020
#
# Updated: 08/25/2022
# More types for P12 version added
#
# Updated: 07/23/2022
# Some bugfixes for P12. Ready to go lib for Poser 12 included as "POSER_FAKE_12.py".

# Source: https://web.archive.org/web/20200805051440/https://adp.spdns.org/
# All possible Poser keywords should be covered.
# Contains a ready to go lib for Poser 11. Insert file "POSER_FAKE.py" into a path
# where your editor can find it. In your script insert the line:
#
#                import POSER_FAKE as poser
#
# Or, as a better solution:
#                try:
#                    import poser
#                except ImportError:
#                    import POSER_FAKE as poser
#
# For use with other Poser versions than 11, run the script once inside your own Poser and copy the resulting file "POSER_FAKE.py" where you need it.

from __future__ import print_function

import inspect
import os
import re
import sys
import traceback
from collections import namedtuple

try:
    import poser
except ImportError:
    poser = None

if sys.version_info.major > 2:
    basestring = str
    xrange = range
else:
    range = xrange

PARMS = namedtuple("PARMS""Name args doc")
classnames = set()

sc = poser.Scene()


DONT_CALL_FILE = None
DONT_CALL = set()

def debug(*args):
    print(*args, file=sys.stderr)


#def dont_call(funcstr):
#    if DONT_CALL_FILE:
#        print(funcstr, file=DONT_CALL_FILE)


def analyze(obj2analyze):
    container = dict(
            classes=list(),
            builtins=list(),
            consts=list()
    )

    members = inspect.getmembers(obj2analyze)
    if members:
        for name, obj in members:
            if name.startswith("_"):
                continue
            doc = obj.__doc__.replace("  ""\n").split("\n"if obj.__doc__ else None
            if name.startswith("k"):
                #if isinstance(obj, basestring):
                #    obj = "'{}'".format(obj)
                container["consts"].append(PARMS(name, obj, ""))
            elif inspect.isclass(obj):
                container["classes"].append(PARMS(name, None, doc))
                classnames.add(name.strip().lower())
            elif inspect.isbuiltin(obj):
                container["builtins"].append(PARMS(name, None, doc))
    else:
        t = getattr(poser, obj2analyze.__class__.__name__ + "Type")
        if t:
            for name in dir(t):
                if name.startswith("_"):
                    continue
                obj = getattr(obj2analyze, name)
                doc = obj.__doc__.replace("  ""\n").split("\n"if obj.__doc__ else None
                container["builtins"].append(PARMS(name, None, doc))

    return container


def print_obj(containertabs=0):
    if not container:
        debug(">>>>>>>>>EMPTY")
        return
    t = "\t" * tabs
    for _entry in sorted(container["consts"]):
        if _entry.doc:
            print(t + "# ".join(_entry.doc))
        frmt = "{}{} = '{}'" if isinstance(_entry.args, basestring) \
            else "{}{} = {}"
        print(frmt.format(t, _entry.Name, _entry.args))
    if tabs == 0:
        print("#""-" * 70)

    for _entry in sorted(container["classes"]):
        print("\n{}class {}:".format(t, _entry.Name))
        if _entry.doc:
            doc = ("\n" + t + "\t").join(_entry.doc)
            print('{}"""\n{}\n{}"""'.format(t, doc, t))
        if _entry.args is not None:
            print("{}\tdef __init__({}):".format(t, _entry.args))
            print("{}\t\tpass".format(t))

        try:
            if _entry.Name == "SceneType":
                print_obj(analyze(poser.Scene()), tabs + 1)
            elif _entry.Name == "CredManagerType":
                print_obj(analyze(CredManager), tabs + 1)
            elif _entry.Name == "ClothSimulatorType" and cloth_simulator:
                print_obj(analyze(cloth_simulator), tabs + 1)
            elif _entry.Name == "ActorType" and Actor:
                print_obj(analyze(Actor), tabs + 1)
            elif _entry.Name == "ParmType" and Parameter:
                print_obj(analyze(Parameter), tabs + 1)
            elif _entry.Name == "GeomType" and Geometry:
                print_obj(analyze(Geometry), tabs + 1)
            elif _entry.Name == "PolygonType" and Geometry:
                print_obj(analyze(Geometry.Polygons()[0]), tabs + 1)
            elif _entry.Name == "TexPolygonType" and Geometry:
                print_obj(analyze(Geometry.TexPolygons()[0]), tabs + 1)
            elif _entry.Name == "VertType" and Geometry:
                print_obj(analyze(Geometry.Vertices()[0]), tabs + 1)
            elif _entry.Name == "TexVertType" and Geometry:
                print_obj(analyze(Geometry.TexVertices()[0]), tabs + 1)
            elif _entry.Name == "FigureType" and Figure:
                print_obj(analyze(Figure), tabs + 1)
            elif _entry.Name == "MaterialType" and Material:
                print_obj(analyze(Material), tabs + 1)
            elif _entry.Name == "ShaderTreeType" and Shadertree:
                print_obj(analyze(Shadertree), tabs + 1)
            elif _entry.Name == "ShaderNodeType" and Node:
                print_obj(analyze(Node), tabs + 1)
            elif _entry.Name == "ShaderNodeInputType" and Node:
                print_obj(analyze(Node.Inputs()[0]), tabs + 1)
            elif _entry.Name == "ImExporterType" and Imexporter:
                print_obj(analyze(Imexporter), tabs + 1)
            elif _entry.Name == "MovieMakerType" and Moviemaker:
                print_obj(analyze(Moviemaker), tabs + 1)
            elif _entry.Name == "MotionNodeType" and Motionrig:
                print_obj(analyze(Motionrig), tabs + 1)
            elif _entry.Name == "SuperFlyOptionsType" and Superfly:
                print_obj(analyze(Superfly), tabs + 1)
            elif _entry.Name == "FireFlyOptionsType" and Firefly:
                print_obj(analyze(Firefly), tabs + 1)
            elif _entry.Name == "Dialog":
                dlg = poser.Dialog()
                print_obj(analyze(dlg), tabs + 1)
                del dlg
            elif _entry.Name == "DialogDirChooser":
                dlg = poser.DialogDirChooser()
                print_obj(analyze(dlg), tabs + 1)
                del dlg
            elif _entry.Name == "DialogFileChooser":
                dlg = poser.DialogFileChooser(2)
                print_obj(analyze(dlg), tabs + 1)
                del dlg
            elif _entry.Name == "DialogTextEntry":
                dlg = poser.DialogTextEntry()
                print_obj(analyze(dlg), tabs + 1)
                del dlg
            else:
                print("{}\tpass".format(t))
        except poser.error as err:
            debug(err)
            print("{}\tpass".format(t))

        print()

    for _entry in sorted(container["builtins"]):
        args = _entry.args or "*args"
        if tabs > 0:
            args = "self, {}".format(_entry.args or "*args")
        print("\n{}def {}({}):".format(t, _entry.Name, args))
        if _entry.doc:
            doc = ("\n" + t + "\t").join(_entry.doc)
            print('{}\t"""\n{}\t{}\n{}\t"""'.format(t, t, doc, t))

            is_list = False
            return_str = None
            last_line = _entry.doc[-1].strip()
            res = re.match(r"[\(\[]\s*[\(\[].*?[\)\]]\s*[\]\)]", last_line)
            if res:
                # return type is tuple or list of tuples
                res_str = res.string[res.regs[0][0+ 1:res.regs[0][1- 1].strip()
                res_str = [s for s in re.split(r"([\(\[][^\)]*[\)\[]),", res_str) if s.strip()]
                for idx, _subentry in enumerate(res_str):
                    for _r in re.findall(r"<((\w+)\s*type)>", _subentry, flags=re.IGNORECASE):
                        if _r[1].lower() in "int float list tuple":
                            _subentry = re.sub("<"+_r[0]+">", _r[1].lower()+"(0)", _subentry, flags=re.IGNORECASE)
                        else:
                            _subentry = re.sub("<"+_r[0]+">", _r[0]+"()", _subentry, flags=re.IGNORECASE)
                    res_str[idx] = _subentry.strip()

                return_str = "list({})" if last_line.startswith("["else "tuple({})"
                return_str = return_str.format(", ".join(res_str))

            if return_str is None:
                res = re.match(r"\(?<([^>]+)>", last_line)
                if res:
                    return_type = res.groups(1)[0].strip()
                    is_list = False
                    if return_type.endswith("List"or return_type.endswith("list"):
                        return_type = return_type.split()[0]
                        is_list = True

                    if return_type.lower() in classnames:
                        return_str = return_type + "()"
                    elif return_str == "NoneType":
                        return_str = ""
                    elif return_type == "IntType":
                        return_str = "int(1)"
                    elif return_type == "FloatType":
                        return_str = "float(1.0)"
                    elif return_type == "StringType":
                        return_str = "str('{}()')".format(_entry.Name)
                    elif return_type == "DictType":
                        return_str = 'dict()'
                    elif return_type == "HairType":
                        return_str = "HairType()"

                    if not return_str.strip():
                        DONT_CALL.add(_entry.Name)

            if return_str is None:
                if "FloatType 4x4 Tuple" in last_line:
                    return_str = "(float(0), float(0), float(0), float(0))"

                if _entry.Name == "WxAuiManager":
                    return_str = "wx.AuiManager()"

                elif _entry.Name == "WxApp":
                    return_str = "poser.WxApp()"

            if is_list:
                return_str = "list({})".format(return_str)

            if return_str is not None:
                print("{}\treturn {}".format(t, return_str))
            else:
                print("{}\treturn".format(t))
        else:
            print("{}\tpass".format(t))
            DONT_CALL.add(_entry.Name)

# temp_dir = poser.TempLocation()
temp_dir = os.path.dirname(sys.argv[0])
fname = os.path.join(temp_dir, "POSER_FAKE.py")

_stdout = sys.stdout
errmsg = None

Actor = poser.Scene().CurrentActor()
Figure = poser.Scene().CurrentFigure()
if Figure and Actor:
    Geometry = Actor.Geometry()
    Parameter = Actor.Parameters()[0]
    Material = Actor.Materials()[0]
    if Material:
        Shadertree = Material.ShaderTree()
        if Shadertree:
            Node = Shadertree.Nodes()[0]

    if sc.NumClothSimulators() == 0:
        cloth_simulator = sc.CreateClothSimulator()
    else:
        cloth_simulator = sc.ClothSimulator(0)

    Imexporter = sc.ImExporter()
    Moviemaker = sc.MovieMaker()
    CredManager = poser.CredManager()
    Motionrig = poser.NewMotionRig()
    Superfly = sc.CurrentSuperFlyOptions()
    Firefly = sc.CurrentFireFlyOptions()

    try:
        Hair = Actor.HairGroup(0)
    except:
        Hair = None

    try:
        with open(fname, "w"as fh:
            sys.stdout = fh
            print_obj(analyze(poser))
            if Hair:
                print("\nclass HairType:")
                print_obj(analyze(Hair), 1)

    except Exception:
        exc_type, exc_value, exc_traceback = sys.exc_info()
        print(traceback.format_exception(exc_type, exc_value, exc_traceback), file=_stdout)
    finally:
        sys.stdout = _stdout

        with open(os.path.join(temp_dir, "DONT_CALL.txt"), "w"as fh:
            for entry in DONT_CALL:
                print(entry, file=fh)

    print(errmsg or "Written to: {}".format(fname))

else:
    print("Can not work with an empty scene.")



Learn the Secrets of Poser 11 and Line-art Filters.


Bastep ( ) posted Wed, 27 September 2023 at 4:32 AM
FVerbaas posted at 12:54 PM Sun, 17 September 2023 - #4474731

import pydoc

pydoc.browse()


Access to Poser module is via link 'poser' in te reightmost column, about mid-height.

Hello!
I am not so familiar with the pydoc.modul. Is there a way to save the browser page somewhere, so that you do not always have to load the pydoc module and start the server? I find this very annoying when working. The browser pages are empty when you save them. I also tried it with a PDF print. It just does not work.
Physical printing is not an option as I have been working without a printer for years.

With kind regards



FVerbaas ( ) posted Wed, 27 September 2023 at 5:06 AM
Forum Coordinator

I agree it is a bit annoying, but at least the info is up-to-date and complete. 

I have not yet heard of a way to make a persistent document via the browser.   

Not sure what a website ripper will do.


FVerbaas ( ) posted Thu, 28 September 2023 at 5:34 AM · edited Thu, 28 September 2023 at 5:44 AM
Forum Coordinator

I looked a little deeper and as far as I can see there's two options:

1 - you can start the pydocs as described, leave the browser window open and quit the pydoc.browse. Your Poser and your Python is no longer blocked. All hyperlinks are internal so your browser will work from the cache as long as you keep the browser window open.

2 - To produce a persistent copy, say as a word document:

- open Microsoft Word (That's what I used. Not sure about other similar apps.)

- In Poser start the pydocs browser as described above. Page shows in your browser. 

- From the address field of that browser copy the address.

- Tell MS-Word to open that page.

- on that page 'copy all'

- open a new blank page and paste all.

You can then save the info as an independent word document. Hyperlinks refer to the original on 'localhost' sadly. 

When you are clever with code hacking you may try to save the .docx as a .rtf and search/replace the link address. 






OberonRex ( ) posted Fri, 29 September 2023 at 2:20 PM

#1 sounds like a really great tip. Thank you very much for that. #2 sounds a bit iffy, what with the problem with hyperlinks. I think I'll leave that one alone.


FVerbaas ( ) posted Sat, 30 September 2023 at 4:26 PM · edited Sat, 30 September 2023 at 4:32 PM
Forum Coordinator

The pydoc.browse() method starts a servrer and tell your webbrowser to load a page that contains all the info, with hyperlinks to locations in that page. The addresses in these hyperlinks however are absolute and lead to the server that pydoc.browse() started. If you save the document and reload it later, that server usually no longer exists.

You would need to change the links into jump links.



Bastep ( ) posted Mon, 02 October 2023 at 2:43 AM · edited Mon, 02 October 2023 at 2:44 AM

FVerbaas posted at 5:34 AM Thu, 28 September 2023 - #4475445

I looked a little deeper and as far as I can see there's two options:

1 - you can start the pydocs as described, leave the browser window open and quit the pydoc.browse. Your Poser and your Python is no longer blocked. All hyperlinks are internal so your browser will work from the cache as long as you keep the browser window open.

2 - To produce a persistent copy, say as a word document:

- open Microsoft Word (That's what I used. Not sure about other similar apps.)

- In Poser start the pydocs browser as described above. Page shows in your browser. 

- From the address field of that browser copy the address.

- Tell MS-Word to open that page.

- on that page 'copy all'

- open a new blank page and paste all.

You can then save the info as an independent word document. Hyperlinks refer to the original on 'localhost' sadly. 

When you are clever with code hacking you may try to save the .docx as a .rtf and search/replace the link address. 


Thank you for the information. Went a similar route, just used the UltraEdit text editor instead of Word. The whole thing slightly revised and saved as a py file. The syntax support then shows me at least a class list. There I can hop then fine back and forth.
Have a great day

uWTjXqQxLTNlDFwen1tgsiiqt2vkuL3QT4QzUCMW.png



ShaneNewville ( ) posted Tue, 10 October 2023 at 1:34 AM

johnsta2oo posted at 4:44 PM Fri, 15 September 2023 - #4474634

I'm not particulary worried about the version of Python. I grok Python fairly well. What I'm looking for are definitions of the Poser classes,  methods, etc., and that's been a real bitch to find. However, for anyone else looking for similar information, I did find this site useful: https://vdocuments.mx/poser-python-methods-manual.html?page=1. Not perfect, but it is possible to dig out the needed information.

I'm trying to find into too.  That link make all the virus alerts go nuts on my compy. 
How exactly does one access the pydoc server thing?  I'm not knowledgeable in this area.

_____________________________
My most recent Poser animation:

Previs Dummies 2


FVerbaas ( ) posted Sat, 14 October 2023 at 4:56 AM · edited Sat, 14 October 2023 at 4:58 AM
Forum Coordinator
FVerbaas posted at 5:34 AM Thu, 28 September 2023 - #4475445

I looked a little deeper and as far as I can see there's two options:

1 - you can start the pydocs as described, leave the browser window open and quit the pydoc.browse. Your Poser and your Python is no longer blocked. All hyperlinks are internal so your browser will work from the cache as long as you keep the browser window open.

2 - To produce a persistent copy, say as a word document:

- open Microsoft Word (That's what I used. Not sure about other similar apps.)

- In Poser start the pydocs browser as described above. Page shows in your browser. 

- From the address field of that browser copy the address.

- Tell MS-Word to open that page.

- on that page 'copy all'

- open a new blank page and paste all.

You can then save the info as an independent word document. Hyperlinks refer to the original on 'localhost' sadly. 

When you are clever with code hacking you may try to save the .docx as a .rtf and search/replace the link address. 

Addition: no need to refuge to .rtf. You can change all links in the word document itself as follows:

- press ALT+f9 keys. The linked addresses now become visible on the screen

- use find&replace to remove the 'localhost' strings.

- press ALT+f9 again to restore normal view.

- you can now export to .pdf or whatever format.

The above is for MSWord. Other editors will use other keystrokes.


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.