Forum: Poser Python Scripting


Subject: Python For Poser by PhilC

OberonRex opened this issue on Sep 14, 2023 ยท 20 posts


OberonRex posted Thu, 14 September 2023 at 3:08 PM Online Now!

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 Online Now!

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 Online Now!

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 Online Now!

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 Online Now!

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 Online Now!

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 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 Online Now!

#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 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

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




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 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.