Python + MXS


#81

Conn.DoSomething(-- aren’t we maxscripting here and not pythonning ?)
and we’re not bothered how python handles conversion to strings ??

That’s exactly right and that’s exactly your problem.

Basically what you were saying to maxscript was “i*10” which means nothing to maxscript, “i” is undefined as far as maxscript is concerened (or worse, equal to something you don’t know)!!

You need to evaluate the string so maxscript reads it as;

…assumining i = 10

“Box position:[’ + i10 +’,’ + i10 + ‘,’ + i10 + ‘] length:10 Width:10 height:10"
="Box position:[’ + 10
10 +’,’ + 1010 + ‘,’ + 1010 + '] length:10 Width:10 height:10”
=“Box position:[100,100,100] length:10 Width:10 height:10”

And pass THIS value to maxscript, where it can then evaluate the value.

Previous, as I said eairler, maxscript had no concept of what “i” is or it’s value, it is out side of it’s context, “i” is only valid within python, or if you like, with in the local context of the loop…it is not translated over to max…(this is not totally accurate, but the concept is the same)

Again, I’m not a python developer and I’ve not tried any of this, I just like the idea!

Try this, in maxscript:


 execute "Box  position:[i*10,i*10,i*10] length:10 Width:10 height:10"
 

and see what happens, because that is, in effect, what you are saying.

I hope this makes some kind of scense, not having python in front of me, nor never having done this kinda work, I can only “assume” that this is correct, but as far as I can tell, “i” is not been evaluated as a variable, but a litrial.

Shane


#82

well , eh this is more confusing !

cause … in the first post in this thread , we didn’t define anything but an empty function

and in python we excuted this function and provided the function definition !

i do see that maxscript can’t understand what is (i) , but as i said … this is more confusing

python is passing what to maxscript ? only strings ?
all the benefit of communication is passing the green words as mxs commands ?
cause clearly this is what you tell when you say


Try this, in maxscript:
Code:

 execute "Box  position:[i*10,i*10,i*10] length:10 Width:10 height:10"
 

and see what happens, because that is, in effect, what you are saying.

 

#83

Hay cyfer!

Hay, I could be completely wrong here, as I said, I have no experience in this particular area, but from you original post, that's what jumped out at me

    conn.DoSomething("Box  position:[10,10,10] length:10 Width:10 height:10")
    conn.DoSomething("Box  position:[i*10,i*10,i*10] length:10 Width:10 height:10")
    
The second statment says to me, pass the string value "Box  position:[i*10,i*10,i*10] length:10 Width:10 height:10" to max and max is going "I don't know what [i][b]i [/b][/i]is and [i][b]undefined * 10[/b][/i] is not a valid calculation!?"

I just had a look at this line 

    "Box  position:[' + i*10 +',' + i*10 + ',' + i*10 + '] length:10 Width:10 height:10"
    

and I’d be interested to know, is the " i * 10 " been evaluated to a final value (ie i = 10; i * 10 = 100) or been passed through as a string literal “i*10”

Basically, what you want to do is evaludate the result of " i*10 " before it gets to max and pass that [b]result[/b] through, not the calculation...

Have you tried assigning the final result to a string value first and passing that in???

    result = i * 10
    evaluateThis = "Box  position:[" + result + "," + result + "," + result"] length:10 Width:10 height:10"
    conn.DoSomething(evaluateThis)
    

And yes, I want to concat the “evaluteThis” string

So that max will recieve the string:

    Box  position:[100,100,100] length:10 Width:10 height:10
    
Assuming that i is 10

#84

"python is passing what to maxscript ? only strings ? "

This is essentially the case.


#85

A nice and simple response…I like it


#86

joel & rustyknight

thanks guys , now there is no confusion … but frustration !


#87

Try compiling a C++ solution then talk to me about frustration!! :wink: Glad we could be of help!!


#88

failed to learn it 8 years ago ! lol


#89
 Hi Guys,
 
 First off Martin, Shane your more than welcome, I'm just glad that I can give something back to the community. One of these day we will have to get together and paint the town red... 
 
 As Shane and Joel correctly pointed out, in this case just strings are being parsed, that is not to say that other data type cannot be parsed across. When it come to formating strings for things like this I would typically format it this way:

     for i in range(1,11):
    	conn.DoSomething("Box  position:[%s*10,%s*10,%s*10] length:10 Width:10 height:10" % (i,i,i))
    
 In this case the "%" character which marks the start of the 'specifier'. "s" will convert any python objects into a string, the same as going str(i).  At the end of the string we give it a tuple of out variables, in this case we want i there each time so its  just (i,i,i) these variable could had have been any python obj!

Cheers
Dave


#90

thanks dave for the help
i’ll reRead this thread from the beginning …


#91

So using Com, would there be a way to pass a bitmap object to max somehow? or would the best method be to open a PIL image, convert it to an array, pass that to max & use set pixels or something to recreate an bitmap? I’m guessing that would be fairly slow…


#92

Hi Chris,

That would work just fine but as you rightly pointed out it would be pretty slow, other alternatives would be to just parse the path to the image to max and open it that way. I guess it depends on what you doing, you could also use PIL to save a temp image and then parse the temp file path to max…

Cheers
Dave


#93

Hi Guys,

This is a little script I quickly through together for a mate and thought some of you might like it. You can parse commands from max to python and have the tracbacks and stdout returned to you.

Here is a simple example:
http://forums.cgsociety.org/attachment.php?attachmentid=118728&stc=1

Here is an example of a traceback:
http://forums.cgsociety.org/attachment.php?attachmentid=118729&stc=1

Anyways time for some code:
here is the python code:


  
  
  """
  	Simple Com Class for parsing commands to the python.
  	Will return sys.stout or in the event of a exception, it will catch the error
  	and return the traceback.
  
  	Crafted By
  	David Mackenzie
  
  	Notes:
  		- If you get a error in max that is something like "Cannot create instance of (com class)
  		try registering the Com Class from a admin account.
  		- This is just a hack, so if for some reason your computer self destructs its not my fault.
  
  """
  
  
  
  import sys
  import os
  import win32com.server.register
  
  Writing = True
  
  def TBWrite():
  	"""Returns the traceback as a string, VERY handy dandy biatch"""
  	import traceback
  	return traceback.format_exc()
  
  #Catch sys.stdout, this is what would normaly be returned to the screen by python
  class CatchOut:
  	def __init__(self, obj):
  		"""obj requires the method COut"""
  		self.Obj = obj
  	def write(self, data):
  		"""This method catches sys.stdout, stdout expects a write method"""
  		self.Obj.COut(data)
  		#global Writing
  		#Writing = False
  
  class MXSPrompt:
  	_public_methods_ = [ 'RunCom' ]
  	_reg_progid_ = "DaveTools.MXSPrompt"
  	_reg_clsid_ = '{CF24CDE3-680B-4A38-82E0-958C57921AF7}'
  
  	def __init__(self):
  		self.Buffer = ""
  		self.StdOut = CatchOut(self)
  		#sys.stderr = self.StdOut
  		sys.stdout = self.StdOut
  		
  	def RunCom(self, commstr):
  		"""Method that is visible to COM"""
  		try:
  			global Writing
  			self.Buffer = ""
  			exec commstr
  			"""
  			while(Writing):
  				pass
  			Writing = True
  			"""
  			return self.Buffer
  		except:
  			return TBWrite()
  		
  	def COut(self, dstr):
  		"""Just Updates the Buffer"""
  		self.Buffer = self.Buffer + dstr
  
  
  if __name__ == "__main__":
  	win32com.server.register.UseCommandLine(MXSPrompt)
  

And now for the max code:

/*
  	Just a quick hack to demonstraight some python in max. This is a very script on both ends.
  	If there is any interest I will develop it further.
  		crafted by, David Mackenize
  	
  	Notes:
  		-Any strange errors, python or max please send to me.
  		-I would recomend, where ever you would like to place a tab insert 4 'spaces'
  		-You might find that if you have 2 or more Dialogs open (of this script) the previous ones
  		will drop/loose there connection with COM.
  	
  	Cheers
  	Dave
  	
  */
  
  
  
  fn GetComObj = (
  	try(
  		o = createOLEObject "DaveTools.MXSPrompt"
  		return o
  	)catch(
  		print "error create ole object"
  		return False
  	)
  )
  
  rollout pyprompt "Python Prompter" (
  	
  	group "Py Prompt" (
  		edittext py_out "" height:125
  		edittext py_in "" height:150
  	)
  	button btn_submit "Submit" align:#right
  	
  	local obj = GetComObj()
  	
  	on btn_submit pressed do(
  		t = obj.RunCom py_in.text
  		py_out.text = t
  	)
  	
  	
  )
  
  createDialog pyprompt 450 350

Cheers
Dave


#94

Hi Guys,

A couple people following the thread have asked about some database code, I did have some stuff I wrote ages ago, which for whatever reason has seemingly disappeared from my hard drive! None the less I had been promising to post some code for a long time, I finally got off my ass and wrote some new stuff…

So here a really simple class to demo straight saving maxfiles to (or any data for that matter) to a database (I was using MySQL i have tested the code with anything else).

I will post a com version later tonight… anyways enough jibba jabba here is the code, if you get any errors just post them or pm me and yes I was lazy and did not comment my code I will fix this later though…

Cheers
Dave

"""
 	Demo Class showing interaction with MySQLdb.
 
 	The Class assumes there is db called 'mymaxfiles' and that it has a table called 'maxfiles'. Its just a basic example of how one
 	my want to store some files etc...
 
 	All the methods below assume the database mymaxfile exists as well.
 
 	Crafted By,
 		David Mackenzie
 
 	First off I don't pretend to be some SQL hacker! I just know what I need to....
 
 	This is a simple class for interacting with simple db and table. It could easily be modified to do allot more.
 	You will also require MySQLdb for this to work you can download here "http://sourceforge.net/projects/mysql-python".
 
 	I should also point out that I have not debugged this a great deal, I have however had no errors thrown in all my testing,
 	if you do get an error just shoot it acrros to me...
 
 	Cheers
 	Dave
 """
 
 import MySQLdb as mys
 import os
 import os.path as pt
 
 def InsertMaxFile(thefile):
 	db = mys.connect(host="localhost", user="root", passwd="daveRoot", db="mymaxfiles")
 	mycurs = db.cursor()
 	mycurs.execute("INSERT INTO maxfiles (files) values('%s')" % (thefile))
 	db.commit()
 	return True
 
 def CreateTable(self):
 	try:
 		db = mys.connect(host="localhost", user="root", passwd="daveRoot", db="mymaxfiles")
 		mycurs = db.cursor()
 		db.commit("CREATE TABLE maxfiles (id INT NOT NULL AUTO_INCREMENT, files varchar(256) NOT NULL, primary key(id));")
 		return True
 	except:
 		return [False, TBWrite()]
 
 	
 def TBWrite():
 	import traceback
 	return traceback.format_exc()
 
 class MaxFileSaver:
 	def __init__(self, server, username, password, database):
 		
 		try:
 			self.db = mys.connect(host=server, user=username, passwd=password, db=database)
 			self.Cursor = self.db.cursor()
 			self.Connected = True
 			self.Commited = True
 		except:
 			self.Connected = False
 			self.db = None
 			self.Cursor = None
 			self.Commited = True
 			print TBWrite()
 			
 	def InsertFile(self, thefile):
 		if self.Connected == True:
 			try:
 				self.Cursor.execute("INSERT INTO maxfiles (files) values('%s')" % (thefile))
 				self.Commited = False
 				return True
 			except:
 				return [False, TBWrite()]
 		else:
 			return [False, "Something went wrong"]
 	def CommitChanges(self, yayorney):
 		if self.Connected:
 			if yayorney == True:
 				try:
 					self.db.commit()
 					self.Commited = True
 					return True
 				except:
 					self.Commited = False
 					return [False, TBWrite()]
 		else:
 			return [False, "Not Connected"]
 	def GetFiles(self):
 		if self.Connected:
 			try:
 				self.Cursor.execute("SELECT * FROM maxfiles")
 				return (self.Cursor.fetchall())
 			except:
 				return [False, TBWrite()]
 	def SetFile(self, theid, thevalue):
 		if self.Connected:
 			try:
 				print self.Cursor.execute("UPDATE maxfiles
SET files='%s' WHERE id='%s'" % (thevalue, theid))
 				return True
 			except:
 				return [False, TBWrite()]
 		else:
 			return [False, "Not Connected"]
 	def DeleteFile(self, theid):
 		if self.Connected:
 			try:
 				self.Cursor.execute("DELETE FROM maxfiles WHERE id=%s" % (theid))
 				return True
 			except:
 				return [False, TBWrite()]
 		else:
 			return [False, "Not Connected"]
 	def RollBackChanges(self, yayorney):
 		if self.Commited == True:
 			return [False,"Data already commited, better luck next time"]
 		if self.Connected:
 			if yayorney:
 				try:
 					self.db.rollback()
 					return True
 				except:
 					return [False, TBWrite()]
 		else:
 			return [False, "Not Connected"]
 	def ReConnect(self, server, username, password, database):
 		self.Connected = False
 		self.db = None
 		self.Cursor = None
 		self.Commited = True
 		try:
 			self.db = mys.connect(host=server, user=username, passwd=password, db=database)
 			self.Cursor = self.db.cursor()
 			self.Connected = True
 		except:
 			return [False, TBWrite()]
 	
 
 if __name__ == "__main__":
 
 	con = MaxFileSaver("localhost", "root", "daveRoot", "mymaxfiles")
 	print con.InsertFile("C:\\\\With Any luck this will work.max")
 	print con.CommitChanges(True)
 	for i in con.GetFiles():
 		print i
 	print con.DeleteFile("14")
 	print con.SetFile("23", "C:\\\\SomeNewFile")
 	print con.CommitChanges(True)
 	
 	
 
 	
 

#95

Hi guys,

Just wrote a new com class for MySQL connections. It works well and is simple to implement.

Here is the code:


  """
  	MySql com class, provides basic connection to MySQL across com.
  	
  	Crafted By, 
  		David Mackenzie
  	
  	Notes:
  		-All errors are handled, if an exception is caught, a list is returned as [False, the traceback]
  		-Requires MySQLdb, http://sourceforge.net/projects/mysql-python
  		-If you get any tracebacks please post them or send them to me!
  		-I pulled this code from a larger implementation im working on so there may be some bugs, although there should not.
  	
  
  	Max Usage:
  		>> a = createOLEObject "DavesTools.ComSql"
  		<OLEObject:DavesTools.ComSql>
  		>> a.Connect "localhost" "root" "daveRoot" "mymaxfiles"
  		true
  		>> a.Execute("SELECT files FROM maxfiles WHERE id=3")
  		true
  		>> a.Fetch()
  		SafeArrayWrapper #("Somemaxfile.max") #(0)
  	
  """
  
  
  import MySQLdb as mys
  import traceback
  
  class ComSql:
  	"""Com interface for parsing SQL commands."""
  	
  	_public_methods_ = ['GetCommited',
  						'GetConnected',
  						'Connect',
  						'Execute',
  						'Fetch',
  						'RollBack',
  						'Commit']
  	_reg_progid_ = 'DavesTools.ComSql'
  	_reg_clsid_ = '{3B165F3B-6E90-4E1B-AA34-D6B3E51C9115}'
  	
  	def __init__(self):
  		self.Connected = False
  		self.Commited = True
  		self.Cursor = None
  		self.db = None
  	def GetCommited(self):
  		return self.Commited
  	def GetConnected(self):
  		return self.Connected
  	def Connect(self, server, username, password, database):
  		"""Connect to the database"""
  		try:
  			self.db = mys.connect(host=server, user=username, passwd=password, db=database)
  			self.Cursor = self.db.cursor()
  			self.Connected = True
  			self.Commited = True
  			return True
  		except:
  			self.Connected = False
  			self.Commited = True
  			self.Cursor = None
  			self.db = None
  			return [False, self.__sqlerror()]
  	def Execute(self, command):
  		"""Execute the parsed command."""
  		if self.Connected:
  			try:
  				self.Cursor.execute(command)
  				self.Commited = False
  				return True
  			except:
  				return [False, self.__sqlerror()]
  		else:
  			return [False, "Not Connected"]
  	def Fetch(self):
  		"""Return Data from the db, the appropiate command should had have been parsed to execute before hand"""
  		if self.Connected:
  			try:
  				return self.Cursor.fetchall()
  			except:
  				return [False, self.__sqlerror()]
  		else:
  			return [False, "Not Connected"]
  	def RollBack(self):
  		"""Roll back any un-commited changes"""
  		if self.Commited:
  			return [False, "Data already commited"]
  		else:
  			if self.Connected:
  				try:
  					self.db.rollback()
  					return True
  				except:
  					return [False, self.__sqlerror()]
  			else:
  				return [False, self.__sqlerror()]
  	def Commit(self):
  		"""Commit changes to database"""
  		if self.Connected:
  			try:
  				if self.Commited == False:
  					self.db.commit()
  					self.Commited = True
  					return True
  			except:
  				return [False, self.__sqlerror()]
  		else:
  			return [False, self.__sqlerror()]
  	def __sqlerror(self):
  		return traceback.format_exc()
  	
  if __name__ == "__main__":
  	TESTING = False
  	if TESTING:
  		print "Testing
"  
  		con = ComSql()
  		print "Connect:
	", con.Connect("localhost", "root", "daveRoot", "mymaxfiles")
  		print "Execute:
	", con.Execute("INSERT INTO maxfiles (files) values('C:\\\\SomeNewFile.max')")
  		print "Commint:
	", con.Commit()
  		print "Execute:
	", con.Execute("SELECT * FROM maxfiles")  
  		print "Fetching:
	", con.Fetch()
  		print "
Done"
  	else:
  		print "Registering COM server..."
  		import win32com.server.register
  		win32com.server.register.UseCommandLine(ComSql)
  

Cheers
Dave


#96

I’m getting some wierd garbage collection errors when pairing python and max. So my script basically sends the “loadmaxfile” command with a file, max opens it, run a maxscript on it, and return a list of strings. then moves on to another file. simple enough right?

Well after about 3-4 files, max throws this wierd error:

“Unknown error occured during garbage collection. If you see this message frequently, restart max.” or something to that extent. I put the gc() in my maxscript hoping that would fix it, but no luck.

Am i not doing something here? My python stuff is pretty simple:


 pyMax = win32com.client.Dispatch("MAX.Application.8")
 pyMax._FlagAsMethod("loadMaxFile")
 pyMax._FlagAsMethod("RenderItem")
 ...
 for z in names:
 	print "Building object..."
 	newItem = item(z)
 	print "Object built, Loading into max..."
 	try:
 		pyMax.loadMaxFile("Z:\XXX" + z.replace("/", "\\"))
 	except pywintypes.com_error, e:
 		print e[1]
 	print "Rendering Item..."
 	renderedItems = []
 	try:
 		renderedItems = pyMax.RenderItem()
 		newItem.dbInsert(renderedItems)
 	except pywintypes.com_error, e:
 		print e[1]					
 	print "Success, moving on..."
 

“item()” is a python object which does some DB stuff.
“RenderItem()” is the maxscript being run.

Thanks


#97

I had a simular issue recently (not with the com bridge, but with max). I may be totally off the mark, but…it may have something to do with the inital heap settings for maxscript.

Go to Customize -> Preferences -> Maxscript (tab) and increase the maxscript memory to a larger amount…about 60-70meg should do the trick. Then try again.

At least you will know if it is a max issue or a com issue pretty quickly.

Also, play around the amount of memory as well till you find a setting you are comfortable with.

Hope it helps
Shane


#98

Hi Brett,

I have a similar setup for keeping asset library’s in order, which essentially recursively goes through a library and updates the database where it needs to. I did notice that the machine I run it on has the max script heap size set to 128mb, so maybe Shane is on to something, I might try lowering the heap size and see what happens.

I’m interested to here how you go…

Cheers
Dave


#99

ok, i set it to 128mb and it seemed to work pretty good. Due to some bugs with my script it will sometimes blow up max. But upping the heap size let me get through 12+ without issue which is way more than before. I will keep tweaking it and let you know what i find.

Thanks again


#100

OK, so i removed the gc() call in my script and upped my heap to 256MB and its working much better now. Thanks for the help guys.