Restrictions on Using Special Python Types as Return Values from Custom Commands

 
 
 

When setting up your return values from a custom command authored in Python, you need to be aware that you cannot pass native Python dictionaries and Python custom classes as-is:

Using Python Dictionaries as Return Values

The Python native dictionary type does not conform to any of the possible data types that Softimage allows for return values from custom commands. As a workaround, you can use an ActiveX scripting dictionary instead, as demonstrated in this snippet from a self-installing custom command:

#---------
def testdictionary_Execute(  ):
	import win32com.client
	oDict = win32com.client.Dispatch( "Scripting.Dictionary" )

	oDict[ 'key1' ] = 123
	oDict[ 'key2' ] = 45
	oDict[ 'key3' ] = 6789
	
	return oDict
#---------

Using Custom Python Classes as Return Values

Python allows you to return a Python class as an ActiveX object, but extra work is needed:

Python Example: Self-Installing Command Returning a Python Class

The following self-installable plug-in returns a Python object which can be used from either JScript or VBScript:

# This class is going to be exported to VB and jscript
class TestPython:
	# Declare list of exported functions:
	_public_methods_ = ['GetAnswer']
	# Declare list of exported attributes
	_public_attrs_ = ['exclamation', 'answer']
	# Declare list of exported read-only attributes:
	_readonly_attrs_ = ['answer']
	# Class init:
	def __init__(self):

		# Initialize exported attributes:
		self.exclamation = 1
		self.answer = 42
		# Perfectly legal to have other non exported attributes

	# Exported function
	def GetAnswer(self, question):
		return "The answer to " + str(question) + " is " + str(self.answer) 
			+ "!"*self.exclamation
		
	# Perfectly legal to have other non exported functions.
		
# Traditional plug-in installation:
true = 1
def XSILoadPlugin( in_reg ):
	in_reg.Author = "Command Wizard User"
	in_reg.Name = "TestPython Plug-in"
	in_reg.Major = 1
	in_reg.Minor = 0
	in_reg.RegisterCommand( "TestPython","TestPython" )
	return true

def TestPython_Init( io_Context ):
	oCmd = io_Context.Source
	Application.LogMessage( "TestPython_Init called" )
	oCmd.Description = ""
	oCmd.ToolTip = ""
	oCmd.ReturnValue = true
	return true

def TestPython_Execute(  ):
	Application.LogMessage( "TestPython_Execute called" )
	oClass = TestPython()
	import win32com.server
	# Class MUST be wrapped before being returned:
	return win32com.server.util.wrap(oClass)

Then you can run this test VBScript snippet in the Script Editor, which uses the Python object successfully:

set a = TestPython()
'INFO : TestPython_Execute called
LogMessage a.GetAnswer("life, the universe, everything")
'INFO : The answer to life, the universe, everything is 42!
a.exclamation = 10
LogMessage a.GetAnswer("life, the universe, everything")
'INFO : The answer to life, the universe, everything is 42!!!!!!!!!!

For more information, see these Python sources:

  • aspn.activestate.com/ASPN/docs/ActivePython/2.3/pywin32/html/com/win32com/HTML/QuickStartServerCom.html—contains information about COM-wrapping custom Python classes and how to expose its methods and properties to COM using the policy attributes

  • www.oreilly.com/catalog/pythonwin32/chapter/ch12.html—this is a sample chapter ("Implementing COM Objects in Python") from the the definitive guide Python Programming on Win32 which also explains working with COM; however, you can skip the CLSID/ProgID information because custom commands already return a fully created object.