Tue, Nov 19, 9:57 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: Poser Python UI options


bagginsbill ( ) posted Wed, 12 March 2008 at 5:41 PM · edited Sun, 27 October 2024 at 1:01 PM

Hello Poser Pythonistas!

I think many of us are simultaneously working on the same thing - a better way to do GUI stuff for Poser Python plugins.

I long ago rejected the Tk stuff - I'm just not interested in that ugliness.

I want to have a rich, interactive, easy-to-develop GUI experience to be included with my scripts, such as Matmatic 2.0, Parmatic, my as-yet-unfinished Versatile Shader System, my "Carpenter" wood scripts, my new "Geomatic" script (what Matmatic did for procedural materials, Geomatic does for procedurally generated geometry) and a few other ideas I've got. Some requirements:

  1. Can easily do complicated interactive data editing (sliders, color pickers)
    2) Extremely concise programming - must be able to do a lot in one line of script
  2. Lots of smart automation for common repetitive tasks
  3.  Does not require a complicated install on the part of the user
  4. Must work perfectly with no conditional logic on Mac and PC

In the past, I was pretty happy with WxPython, which could satisfy most of those, but my understanding is that it isn't straightforward for a user to install onto Poser Python. Thus it breaks rule 4.

I've done a lot of experimenting and have come up with something I quite like at the moment. I made a Poser Python script that acts like an application server, something similar to a Java-based server. But instead of Java, you script it with Python. I've produced a pretty nice library of components to go with it. This system, I call Paser (Poser Application Server). I intend to make Paser open source - meaning you all get the source code and can contribute extensions and improvements to the framework.

To add a new application to Paser, you write a Python script and put it in the Paser/apps folder. Paser has a built-in system for detecting installed scripts, and incorporating them into its "menu" database.  

For any development environment, one must present the "Hello World" application. Here is the Paser version:

def main(): print "Hello World"

Done! If you put that in a test.py file in the Poser apps folder (or some subfolder) it will instantly be available. You then click a link on the Paser Main Menu page to run it.

Basically, when you hit the Paser server with something like /test.py, it finds the file, compiles it if it hasn't yet been compiled, sets up some initial state, and calls your "main" method. Anything you print will be sent in the response, which by default is assumed to be an HTML document. You can, however, instruct Paser that you're doing some other kind of document and then write the data any way you like.

To get it to call other methods, you say /test.py?op=foo, where foo is the name of the method you want to call.

So I can write:

def main(): print 'Click me to call Foo'
def foo(): print '

Foo!

You called Foo successfully."

Here, main made a link which upon clicking, calls foo.

Now it can get pretty annoying to type and print all that HTML markup, so I made an HTML "emitter" class library that takes care of all that crapola for you.

Using the library, you write:

from html import *
def main(): return Link("Click me to call Foo").clicks('foo')
def foo(): return Emitter(H1("Foo!"), "You called Foo successfully.")

There's a lot more to it, but that's the basics. Additional parameters can be passed in the URL and they are easily pulled out by the python.

Then I started to play with AJAX. Using an AJAX library, I can make complicated widgets inside the page, and have those widgets send me events in real time, back to my python code. I can also make the python code generate new contents that get sent back to the page, which can replace all or part of what is on the screen.

I have a system of "delegate" objects that take care of all the plumbing for this sort of thing. So for example, if I want to iterate over all poser lights and make a slider that controls the intensity for each, in real time, I can do something like this (not exact yet - still working out details):

def main():
  p = Panel()
  for light in poser.Scene().Lights():
    p << [ QHTML(light.Name()), Slider().invoke(setLightIntensity, light, Callback.value), BR]
  return p

def setLightIntensity(light, value):
    light.Parameter("Intensity").SetValue(value)

The QHTML function just quotes any characters from the light name that might cause problems in HTML.

The Slider is a class that knows about the Javascript library I've got on the client side in the browser. It makes a true slider.

The "invoke" method specifies that when the slider is updated by the user, I want it to call the setLightIntensity method, passing the specified light as an argument.

The object "Callback" is special. Asking for .value on it returns a callback object that later will be substituted with the URL 'value=' parameter. So my method will also be passed the value of the slider.

This is pretty cool stuff, and very easy to code.

I've been exploring the various free Ajax libraries: prototype, jquery, dojo, and mootools.

Of the four, I think I like mootools best. So my first question is - any opinions?

Now on the flip side, lately I've been doing a lot of .NET/XAML/C#/IronPython stuff, and I gotta tell you it is flipping awesome! So I'm looking into Silverlight - which supposedly would let me use all the full-blown WPF user interface stuff I now love, but it will work IN ALMOST ANY BROWSER, and it will work ON A MAC!!!!!!

So - second question. Any opinions on Silverlight? Any MAC users - have you run it - does it really f'ing work? I mean, if I make a rich GUI with listboxes, tree views, images, animations, blah blah, you see it all correctly on your MAC?

Because I gotta tell you, the Javascript/AJAX stuff is cool and all, but its hard to work with. Debugging is difficult, the language ain't that great, and the widgets are not truly object oriented in the full sense of the word. If I can code up the UI behavior in XAML and IronPython and it works everywhere, and just handle the callbacks inside Poser Python as RPC calls, well I think I'd be pretty happy with that.


Renderosity forum reply notifications are wonky. If I read a follow-up in a thread, but I don't myself reply, then notifications no longer happen AT ALL on that thread. So if I seem to be ignoring a question, that's why. (Updated September 23, 2019)


dburdick ( ) posted Thu, 13 March 2008 at 2:53 AM

Sounds like a heck of a lot of work to build and maintain when there are other options out there.  Also, if I understand you correctly, in order for the UI calls to work,  the user would have to be connected to the web, yes?

Why not just use wxPython?  I use wxPython for my Vue SkinVue product and it works just fine on all platforms - PC and Mac.  Plus, unlike tkinter, it retains the look and feel of the platform.  Also, there's a 64-bit version coming out soon.  I haven't tried it in Poser but it's simple for any user to install (it's only a couple of megs for the runtime) or to install as part of a product installation.  Maybe we could form a wxPython for Poser interest group and build off of that.


bagginsbill ( ) posted Thu, 13 March 2008 at 7:10 AM

db:

I was persuaded avoid wxP because of the conversation in this thread:
wxPython in Poser

According to info in there, the user will have to replace the version of Python that comes with Poser, and will have to make registry changes. That sounds a bit too complicated - I think I'd be asking for a support nightmare with that choice.

Answering your other question, no they would not need an internet connection. The browser can talk to an application server on the same machine at all times, regardless of whether or not you're connected to the internet. You only need the internet connection to talk to servers that are hosted on other computers. Any server hosted on your own computer can be reached with what is called the "internal loopback" connection.

This is a common technique I use in other apps. Any "background" utility I make, I always put a tiny web server in it, so that I can query it to find out what state it is in or to configure it.

My intention for building a web server into Poser was NOT so that it could be accessed from another computer. Rather, I was simply taking advantage of the fact that everybody has a browser of some sort today. The browser is a pretty feature rich user interface framework that I can count on to alread be installed on every user's computer.

I'd rather not use the client-server approach. It would be so much nicer to just write a Python script and have it open its own windows. WxPython would be perfect, because it is very easy for a script to create whatever UI it needs and have it executed directly in Poser.

There is also the option to use client-server, but develop my client as a standalone Python program, so that it does not use Poser's Python. Then I could use wxPython. But the user would have to install the real Python engine first, then install the real wxPython library into that Python engine. Then they'd install my plugin.

The way I'm currently implementing it, they have to install Paser first, then install one of my plugins. Paser is tiny and can be included with any plugin. My plan was to have all my plugins come with a copy of Paser, which would install itself first if they don't already have Paser.


Renderosity forum reply notifications are wonky. If I read a follow-up in a thread, but I don't myself reply, then notifications no longer happen AT ALL on that thread. So if I seem to be ignoring a question, that's why. (Updated September 23, 2019)


dburdick ( ) posted Thu, 13 March 2008 at 2:03 PM

Quote - db:

I was persuaded avoid wxP because of the conversation in this thread:
wxPython in Poser

According to info in there, the user will have to replace the version of Python that comes with Poser, and will have to make registry changes. That sounds a bit too complicated - I think I'd be asking for a support nightmare with that choice.

Hmm... that does sound like a showstopper unless the EF (SmithMicro) people could be persuaded to use a threaded version of Python.  It's odd that they don't.


nruddock ( ) posted Thu, 13 March 2008 at 4:43 PM

Quote - ... unless the EF (SmithMicro) people could be persuaded to use a threaded version of Python.  It's odd that they don't.

The threading routines are in the Python DLL but they don't work properly (running IDLE from within Poser used to be possible).

The Python library contains classes for a simple web server (see SimpleHTTPServer.py) and also an XMLRPC server (see SimpleXMLRPCServer.py) which only need a Poser friendly main loop to get working and offers a bigger choice of potential clients than a custom protocol as used by PRPC.

The RESTful approach BB is using is definitely a good one.


bagginsbill ( ) posted Thu, 13 March 2008 at 5:46 PM · edited Thu, 13 March 2008 at 5:48 PM

I am using the SimpleHTTPServer. I made a derivative that knows how to parse the URL parameters into a dictionary, and use them to dispatch callbacks to my Python methods. If instead of a GET, you do a POST, it parses the form submission parameters in the body of the POST. Either way, the Python just sees a dictionary "args" with all the parameters decoded already. Anything that looks like a number, is decoded as a number, so you don't have to assume they're just strings and do the conversion yourself. It's very convenient. 

It also checks to see if the python file being asked for is different than last time. If its the same (based on time stamp) then I just use the module already compiled and loaded. If it's not the same, I reload the module first. This makes it easy to edit the python script and hit a link again to see the new behavior, while avoiding a recompile on every request. 

I also had to override the built-in handler for just serving a plain file. The problem is that the version that came with Poser is old, and did not include checks to see of the browser included a date stamp. The browser doesn't need to keep reloading files that haven't changed. It indicates this by supplying a date and I use that in the response. I got the correct code from a newer version of the Python library source, and just pasted it in there. This is very important, because I deliver images and Javascript files through Paser as well, and we don't want to keep transmitting those over and over.

By the way, I was being silly and had overlooked something. In that example I showed earlier about making sliders for a light control panel, I overcomplicated it. There is no reason to have to implement my own callback handler. Because of the neato way that Python handles bound member functions it is possible to get Paser to directly call the Poser Python interface API, without any intermediate code written by me.

It is done like this:

def main():
  p = Panel()
  for light in poser.Scene().Lights():
    p << [ QHTML(light.Name()), Slider().invoke(light.Parameters("Intensity").SetValue, Callback.value), BR]
  return p

Also, using Python's powerful list comprehension, the whole thing becomes a one-liner:

def main():
  return Panel([(QHTML(light.Name()), Slider().invoke(light.Parameters("Intensity").SetValue, Callback.value), BR) for light in poser.Scene().Lights()])

Let's see you say that in one line of code with wxPython - or anything else for that matter. :biggrin:


Renderosity forum reply notifications are wonky. If I read a follow-up in a thread, but I don't myself reply, then notifications no longer happen AT ALL on that thread. So if I seem to be ignoring a question, that's why. (Updated September 23, 2019)


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.