Python + MXS


#221

It returns a job handle, very useful for controlling a job or getting further information about that job. Current rendering status of that job, The name of the job and what type of job it is. That’s all BB returns for a joblist and it in plain English in one string and tab delimited for easy use in MXscript.

Sorry it wasn’t useful to you.


#222

As you can see from what I posted it is turnacating the info, so I get the handle, status, and part of the version.

-Eric


#223

Okay, uploaded an update to the updated version, it’s now 4.0… I’m not using 2009, but will be soon… so thanks for the Beta Testing and a bug find in my code…


#224

As I mentioned before, you could always put the DosCommand in a try/catch with HiddenDosCommand as I outlined previously for those using 2008+ or AVG so they don’t get the cmd prompt flash, or just replace the doscommand code with the hiddendoscommand in this copy of the code:

(
struct pythonModualsStruct ( BackBurner )
try (hiddendosCommand ( "\""+( getDir #maxRoot ) +"plugins\\Script Global Functions\\PythonLib_BackBurner.py\"") startpath:"c:/") catch (dosCommand ( "\""+( getDir #maxRoot ) +"plugins\\Scripted Global Functions\\PythonLib_BackBurner.py\""))
global PythonLib = ( pythonModualsStruct BackBurner:(createOLEObject("PythonLib.BackBurner")) )	
)

-Eric


#225

Done. Thanks Eric. Can’t wait to make the move to 2009, so much good stuff there…


#226

As you can see there is a ultra simple function that will just execute whatever is parsed to it. Now for the Python part, this will require the win32 module.

      #Import win32com.
    import win32com.client
    #Create a connection to Max
    conn = win32com.client.Dispatch("MAX.Application.9")
 I am having trouble Dispatch'ing MAX.  I have the trial version of 2009, and I always get an "Invalid class string" error (I tried to change 9 by 11 or 2009, and also without a version number).  I understand this has something to do with Max 2009 not being registered, correct?
 
 I searched a bit the pywin32 mailing list archives, but even by looking at the COM Makepy Utility, I could not find anything that would help.  I also read about creating a Reg entry through pythoncom.CreateGuid(), but that did not help either (I tried to mimic what was there for Photoshop since I can do win32com.client.Dispatch("Photoshop.Application")  without a problem).
 
 Could anyone tell me what I need to look for to get this working?
 
 Thanks. nico.

#227

I think the names you can use are listed in the registry in HKEY_CLASSES_ROOT. For max 8, there is a key called MAX.Application.8, see if there is a similar one for 2009.


#228

See my reply above, dated 10-15-2008, 08:00 PM. Specifically the maxscript file I mentioned. It should help you make Max register itself as a COM server.


#229

Thanks for the script; this is getting me moving forward. Please forgive my ignorance on all this, but after installing the avguard plugin, and running your script, I get an error that reg_key/HKey is ‘undefined’. Might this because of a lack of permissions to access the registry properly? Where does the value of HKey come from when running the script?

Thanks again for your help. Appreciated.
nico


#230

Can you paste in the exact error you’re seeing? And what maxscript line is triggering it?


#231

Note: this was tested on version 8–not 2009, at work (hence my concern about permissions to write to the registry). From running register3dsMaxCom.ms:


 -- Error occured in create_reg_key(); filename C:\[path to file]\register3dsMaxCom.ms;
 
 position: 470
 --  Frame:
 --    HKey: HKEY_CLASSES_ROOT
 --    key_value_name: ""
 --    key_value:  "OLE Automation MAX Application"
 --    reg_key: undefined
 --    key_name:  "Max.Application"
 --    key_value_type:  #REG_SZ
 --    called in anonymous codeblock
 --  Frame:
 -- 	write_sub_key_data: write_sub_key_data()
 -- 	create_reg_key: create_reg_key()
 -- 	max_version: "8"
 -- 	reg_key: undefined
 --  Type error: HKey requires HKey, got: undefined
 OK

The line highlighted in the script is:

registry.setValue reg_key key_value_name key_value_type key_value

I am trying to ask someone with Admin privileges to run the script to see if he gets the same error.

Thank you for your time,
nico


#232

This must be something with Admin privileges: I just tried this at home with 2009, and I managed to run the previous examples fine. I will check with our SysAdmin at work tomorrow to get around this. Man, do I miss working on Linux… :sad:

Thanks everyone for the help, and thanks Adam for your blog as I would have never known how to get started with Python to write my Photoshop tools, and now–let’s hope–to write some procedures for Max.

Cheers. nico


#233

Well, we’re at it again. While I had no problem getting it to work at home, I finally got a SysAdmin at work to run the scripts and we got some errors. He ran the Maxscript fine (to register Max), but when trying to run the Python code, it complained about a class not being registered.

Python code:

#Import win32com.
           import win32com.client
           #Create a connection to Max
           conn = win32com.client.Dispatch("MAX.Application.8")

Error:

Traceback (most recent call last):
    File "path	o\callingmaxsc.py",  line 6, in ?
  	con = win32com.client.Dispatch("MAX.Application.8")
    File "C:\Python24\lib\site-packages\win32com\client\dynamic.py", line 95, in Dispatch
  	dispatch, username = dynamic._GetGoodDispatchAndUserName(dispatch, username, clsctx)
    File "C:\Python24\lib\site-packages\win32com\client\dynamic.py", line 98, in  _GetGoodDispatchAndUserName
  	return (_GetGoodDispatchAndUserName(IDispatch, clsctx), userName)
    File "C:\Python24\lib\site-packages\win32com\client\dynamic.py", line 78, in  _GetGoodDispatch
  	IDispatch = pythoncom.CoCreateInstance(IDispatch, None, clsctx, pythoncom.IID_IDispatch)
  com_error: (-2147221164, 'Class not registered', None, None)

This was run by the Admin. We looked at the registry, and all the keys and values looked good. Setup: Max 8, Python 2.4.3 with pywin32, XP 64 SP2.

I got it to work at home with XP SP3, Python 2.5 and pywin32, Max 2009. I will try, when I get home, to use an older version of Python and Max, to see if I can reproduce the problem. I also tried using comtypes since I had some issues with win32com, but I get the same errors, so it has something to do with the classes in the register not being found…

Anyone who encountered this problem? I will try to post again before the weekend to see if I can reproduce this at home with older versions of Max and Python.

Thanks.
Nico


#234

Update on this: I was maybe expecting too much. When I called win32com.client.Dispatch(“MAX.Application.8”), I was expecting the script to launch Max, the way it would launch Photoshop. After I had Max open, everything worked fine.

I’m not a programmer, but I would love it if someone could explain to me the difference when I do:

win32com.client.Dispatch(“MAX.Application”)

and

win32com.client.Dispatch(“Photoshop.Application”)

Why can the script launch Photoshop but not Max? It is not a problem at all as I will need Max to be running in the first place, but I am curious as to why the two apps behave differently.

Cheers. nico


#235

In my experience they don’t behave differently. Both of those commands open their respective app on my machines. I couldn’t tell you why, perhaps you’re missing some registry key/value that’s normally there?

Also, if there’s an open instance of Max (or Photoshop, for that matter) when you do the dispatch, it will use that one and not open a new instance. Is it possible you had an instance of 3dsmax.exe running in the background? That sometimes happens after it crashes, etc. If so, it would consume any COM connections you make.

Just a thought.


#236

ndu, I was having the same Hkey error here (Both with max2008 32bit, and max2009 32bit, though the script appears to work in the 64bit installs?

Anyway, this is the contents of the regfile if you were to install in 2008, modify the location of LocalServer32 to reflect your max installation though, also adjust the MAX.Application.10’s to read MAX.Application.11’s if you’re doing it for 2009.

this is basically the final output of the above script, with the localserver being added in so that it will launch that app if the required dispatch isn’t already open on the machine


Windows Registry Editor Version 5.00

[HKEY_CLASSES_ROOT\MAX.Application]
@="OLE Automation MAX Application"

[HKEY_CLASSES_ROOT\MAX.Application\Clsid]
@="{7FA22CB1-D26F-11d0-B260-00A0240CEEA3}"

[HKEY_CLASSES_ROOT\MAX.Application\CurVer]
@="MAX.Application.10"

[HKEY_CLASSES_ROOT\MAX.Application.10]
@="OLE Automation MAX 10.0 Application"

[HKEY_CLASSES_ROOT\MAX.Application.10\Clsid]
@="{7FA22CB1-D26F-11d0-B260-00A0240CEEA3}"

[HKEY_CLASSES_ROOT\CLSID\{7FA22CB1-D26F-11d0-B260-00A0240CEEA3}]
@="OLE Automation MAX 10.0 Application"

[HKEY_CLASSES_ROOT\CLSID\{7FA22CB1-D26F-11d0-B260-00A0240CEEA3}\Localserver32]
@="C:\\3dsmax08\\3dsmax.exe"

[HKEY_CLASSES_ROOT\CLSID\{7FA22CB1-D26F-11d0-B260-00A0240CEEA3}\ProgID]
@="MAX.Application.10"

[HKEY_CLASSES_ROOT\CLSID\{7FA22CB1-D26F-11d0-B260-00A0240CEEA3}\VersionIndependentProgID]
@="MAX.Application"

would be nice to figure out why the script is failing though, as doing it manually doesn’t allow for easy access to different versions of the application on the same workstation without running a different .reg file


#237

Has anyone been able to query Max scene elements through python?

Basic things like get selected objects, or get the joints in the scene into a python array?

example:


 myArray = []
 mx.command_from_python('for i in $ do ("python.myArray.append(i)")')
 
 #so I could later do something like
 for obj in myArray:
 	print obj
 

#238

If you’re using COM to connect to Max using Python, you’re going to be limited to the data types that COM supports for return values. Offhand, I believe the supported types coming from Max are string, float, integer, boolean, color and one-dimensional arrays of those same types.

In any case, if you issue a command from Python that would normally return an data type that’s specific to 3ds Max (such as Color, Node, quat, etc), the COM layer will throw an error.

I’ve dealt with this in my Python-Max tools by creating a function that acts like a little layer between Python and MaxScript. If the command executed returns a value that’s not one of the native COM types, it casts the value to a string before sending it back to Python. That way the Python code has the option of doing something useful with it instead of being faced with a useless error. The wrapper function is then what’s exposed over COM, or some other function that uses it.

For instance, a node value in MaxScript would normally look like this:

$Teapot:Teapot01 @ [-36.208855,-22.871155,0.000000]

A Python script using a type-casting wrapper would know that wasn’t a supported COM type, and convert it to a string. The Python script would instead get this back:

“$Teapot:Teapot01 @ [-36.208855,-22.871155,0.000000]”

You can’t interact with that in Python the same way you would in MaxScript, but you do have some info to work with (object’s type, name, position). You just need to parse it from the string.

Attached is a small MaxScript I wrote for my GDC talk last year. There’s one function for converting Max-specific data types to something COM can grok, and another that takes requests from Python and actually executes the commands, calling the convert function as needed. The second function is also registered as a COM object for your Python tools to use.


#239

I really don’t know much about MaxScript, but for my tool, where I need to make a list of cameras to extract values from, I had to create the array inside Max and then call it from Python.

(the MaxDo is just a shorter name from the earlier example using DoSomething)
conn.MaxDo(‘myMaxArray = #()’) # create array in Max
conn.MaxDo(‘myMaxArray[1]’) # access the first element in your Python code


#240

Thanks for the ideas

90% of the time I am dealing with arrays, I need the size and looping abilities. So calling a max array doesn’t seem too valuable since I can’t


 for i in myArray:
 	print i
 

But I do like the idea of getting a string from Max and parsing the crap out of that. Especially since python is pretty good dealing with strings. :slight_smile: