Python to MaxScript to Python

Hey guys…

So after some digging around I succeeded in sending commands from Python to MaxScript but I can’t wrap my head around returning information back to Python. This is the set up right now:

Max (runs on startup):

fn maxCommand input = (
try (
execute input;
return true;
) catch (
return false;
)
)
registerOLEInterface #(maxCommand);

Python:

import win32com.client

def messageMax(message):
maxCOM = win32com.client.Dispatch(“Max.Application.9”)
maxCOM._FlagAsMethod(“maxCommand”)
maxCOM.maxCommand(message)

Now I just need some way to send responses back to Max… You can laugh but please help.

If you’re going to do something as complex as IPC, you may as well learn how to use something like ZeroMQ and do it properly, rather than use the dying and shit technology that is COM.

<3

I used to be a staunch COM user but in recent years I’ve been using other methods. I made a blog post awhile back on how to set up a socket listener/server between MaxScript and Python. If you’re set on using COM to talk to go Python-to-MaxScript, this is going to be your only option. COM both directions will not work, due to how Python works with it.

Here’s the blog post:

Not a COM fan either
I got inspired by Adam’s post and wrote something similar with named pipes. The server is in Python (http://bytes.com/topic/python/answers/43082-example-code-named-pipes-python-2-4-ctypes-windows), the client in C# (http://www.switchonthecode.com/tutorials/dotnet-35-adds-named-pipes-support and http://www.switchonthecode.com/tutorials/interprocess-communication-using-named-pipes-in-csharp). To handle events coming from Python, were using System.Windows.Forms.Timer in 3ds max to poll - 50 to 100ms are okay since you don’t need realtime reaction time, and it doesn’t bog down max. A basic implementation is fairly easy to write and gives you two way communication that allows you to use sort of “events” on both sides, so you can link up a max UI with a e.g. PyQt UI.

If you don’t need support for “events” on the max side it’s easier. By default you have the possibility to send a response to every data received via the named pipe. Then you don’t need a listener in Max at all.

Of course you could then go crazy and automatically launch Python from max, use JSON-RPC to encode the strings you’re sending, and even go as far as calling your C# dll from a C++ wrapper from Photoshop (making some progress on this one right now) or ZBrush and make them all talk to Python too.

So it’s not that trivial, but I wouldn’t say it’s that difficult either to get Python and Max talk to each other in some way.

Ok… You guys are too advanced for me. Maybe what I was after was more simple than I had the wisdom to realize but at the price of shaming myself here’s what went down.
The task:
Python tells max to execute a script and report back some information. (in this case the max script of choice is to return a list of currently selected objects)

--------------------IN MAX----------------------
–this runs first so that python has something to attach to.
–returns a list of selected object names as string.
fn getSelection = (
retList = #();
sel = $ as array;
for each in sel do(
[INDENT]append retList each.name;
)
return retList;[/INDENT]
)

–runs a maxscript command and returns it’s return value (not bothering with converting to a valid c type in this… just string)
fn maxCommand input = (
try (
[INDENT]returnVal = execute input;
return (returnVal as string);
) catch (
return false;
)[/INDENT]
)
registerOLEInterface #(maxCommand);

------------------------IN PYTHON-----------------------------
def messageMax(message):
maxCOM = win32com.client.Dispatch(“Max.Application.9”)
maxCOM._FlagAsMethod(“maxCommand”)
return maxCOM.maxCommand(message)

if name == “main”:
print messageMax(“getSelection()”)

-----------------------RESULT---------------------------
–provided you made a bunch of boxes and had them selected.
“#(“Box02”, “Box03”, “Box04”, “Box01”)”

i’m new at this… so go easy on the “you’re a blight on our community and we have never…ever…met anyone we believe in less… go back to polycount”

don’t worry - we all started small :slight_smile:
The Max Python issue is just one that’s not as trivial as it sounds. We tried COM briefly, but it’s just a one way communication, being able to talk to just 1 3ds Max version (in case you have multiple installs), assuming it’s all correctly registered in the registry. There’s just so many drawbacks. We also investigated blur-dev (http://code.google.com/p/blur-dev/) but it also wasn’t working that well, mostly because the Python version for 3ds Max x64 was 2.4, and nobody wanted to go back there - but maybe this can work for you?
After all the trial and error we went to sockets, and then to named pipes. I would have loved if 0MQ were around when we started doing this. Anyway, first it sounds easy, but the more we played around, the more complex the solutions became, because we wanted a reliable solution that we can distribute across the studio without worrying about different 3ds max versions or broken windows registries.

I’ve also heard about people using hybrid solutions - 1 direction COM, the other direction sockets.

[QUOTE=dekorkh;16373]
i’m new at this… so go easy on the “you’re a blight on our community and we have never…ever…met anyone we believe in less… go back to polycount”[/QUOTE]

You may (or not) be surprised at how little of that there tends to be around here…

Well thanks guys! I’m pretty excited to check out 0mq but probably tease myself by trying out the socket workflow as well just so that I can fully appreciate. This stuff is super fun.

I’ve used Blur python and it works really great in Softimage 2013 (and max 2012) and made some really elaborate pyqt scripts. I want to use them in Max 2013 but unfortunately it only goes up to 2012. Im fine with the COM 1 way behavior.

I tried taking the provided source from the blur code and compiling it against the max 2013 sdk but I got so many errors and even though I worked at it for around 8 hours I didnt make much progress and decided to give up on recompiling it.

Having said that - what can I do to get pyqt into max 13? I tried Adam’s DotNet example but it slowed max down a ton and it wouldnt have the pyqt bindings that the blur code has(?) It seems like the blur method is quite elaborate…perhaps some of the other members here can help me through this with zeromq?

No worries mate. As mentioned, the reason we’re discouraging you from COM is that its deceptively simple. It will certainly be easier to get something working, but you’re in for endless headaches trying to learn a dead technology. Better to attempt to tackle a seemingly much more complex topic (server/client communication), but it will make more sense in the end and teach you absolutely important things that will probably expand what you think is possible with pipelines (at least that’s how I felt when I started writing services).

As for ZMQ, I should write up some proper examples for helping people to get started.

That would be really awesome Rob, as I am also very interested in this topic.

Cheers

As for ZMQ, I should write up some proper examples for helping people to get started.

I second that. I’d be interested as well. I read your “Everything can be a server/client” post. It went over my head, and some toe-dipping examples would be great.

I third the request for some ZMQ content. I’ve been doing a fair amount of python and COM work and I’d be willing to invest some effort into learning better tech.

Just chiming in on the ZMQ content. While we are not doing incredible loads of COM, some of it is pretty crucial and at the core of our workflows. I’d love to get rid of that!

Bump.

Have been working on a solution over here for Max -> Py -> Max based off Adam Pletcher’s blog post on the subject. TL;DR, it mirrors the send code of Maxscript to Python, and the receive code in Python to Maxscript. When Max launches, I start a python “server” in the background that’s always listening to a port. When it receives data, it triggers a command that you’ve pre-programmed to match a received input, and sends a string back to Maxscript, which was placed in listening mode immediately after firing the command to Python. The trick with setting up the listener in Maxscript was that I had to Bind to a System.Net.IPEndPoint object, instead of being able to, as in Python, bind to a string IPAddress/port combo.

Or, if you know C#, you can go as far as we’ve gone with our Python bridge. :slight_smile: Though it’s based on IronPython (not CPython), for what we use it for, it’s more than enough.

http://blog.duber.cz/?s=duberPython

We went ahead here and changed our system to use ZMQ - thanks Rob for the tip! :slight_smile:

We set up a “pair” communication between a C# dll and a CPython script using ZMQ. Plus we added C++ wrappers for the C# dll, where we host a CLR, for Photoshop’s ExtendScript and even for Python itself, so we got a truly flexible communication solution to talk to Python from 4 major packages.

[QUOTE=RobertKist;16939]We went ahead here and changed our system to use ZMQ - thanks Rob for the tip! :slight_smile:

We set up a “pair” communication between a C# dll and a CPython script using ZMQ. Plus we added C++ wrappers for the C# dll, where we host a CLR, for Photoshop’s ExtendScript and even for Python itself, so we got a truly flexible communication solution to talk to Python from 4 major packages.[/QUOTE]

Do you have any information on how to achieve this?
I’ve tried to do it with a python import and a zmq import, but can’t get a connection as max errors out there as “the function does not exist”.
I’m stuck at the moment especially with virtually no dotnet knowledge.

Thanks!