mrv.maya.nt
Covered: 212 lines
Missed: 19 lines
Skipped 136 lines
Percent: 91 %
  2
"""
  3
All classes required to wrap maya nodes in an object oriented manner into python objects
  4
and allow easy handling of them.
  6
These python classes wrap the API representations of their respective nodes - most general
  7
commands will be natively working on them.
  9
These classes follow the node hierarchy as supplied by the maya api.
 11
Optionally: Attribute access is as easy as using properties like:
 13
	>>> node.translateX
 15
:note: it is important not to cache these as the underlying obejcts my change over time.
 16
	For long-term storage, use handles instead.
 18
Default maya commands will require them to be used as strings instead.
 19
"""
 20
__docformat__ = "restructuredtext"
 22
import mrv.maya as mrvmaya
 23
import typ
 24
_thismodule = __import__( "mrv.maya.nt", globals(), locals(), ['nt'] )
 25
from mrv.util import capitalize
 26
import mrv.maya.env as env
 27
import mrv.maya.util as mrvmayautil
 28
from mrv import init_modules
 30
import maya.cmds as cmds
 31
import maya.OpenMaya as api
 33
import sys
 34
import os
 35
import logging
 42
pluginDB = None
 48
def addCustomType( newcls, parentClsName=None, **kwargs ):
 49
	""" Add a custom class to this module - it will be handled like a native type
 51
	:param newcls: new class object if metaclass is None, otherwise string name of the
 52
		type name to be created by your metaclass
 53
	:param parentClsName: if metaclass is set, the parentclass name ( of a class existing
 54
		in the nodeTypeTree ( see /maya/cache/nodeHierarchy.html )
 55
		Otherwise, if unset, the parentclassname will be extracted from the newcls object
 56
	:param kwargs:
 57
		 * force_creation: 
 58
				if True, default False, the class type will be created immediately. This
 59
				can be useful if you wish to use the type for comparison, possibly before it is first being
 60
				queried by the system. The latter case would bind the StandinClass instead of the actual type.
 61
	:raise KeyError: if the parentClsName does not exist"""
 62
	newclsname = newcls
 63
	newclsobj = None
 64
	parentname = parentClsName
 65
	if not isinstance( newcls, basestring ):
 66
		newclsname = newcls.__name__
 67
		newclsobj = newcls
 68
		if not parentClsName:
 69
			parentname = newcls.__bases__[0].__name__
 72
	typ._addCustomType( globals(), parentname, newclsname, **kwargs )
 75
	if newclsobj:
 76
		setattr( _thismodule, newclsname, newclsobj )
 78
def removeCustomType( customType ):
 79
	"""Removes the given type from this module as well as from the type hierarchy.
 80
	This makes it unavailble to MRV
 82
	:param customType: either string identifying the type's name or the type itself
 83
	:note: does nothing if the type does not exist"""
 84
	if not isinstance(customType, basestring):
 85
		customType = customType.__name__
 86
	typ._removeCustomType(globals(), customType)
 88
def addCustomTypeFromFile( hierarchyfile, **kwargs ):
 89
	"""Add a custom classes as defined by the given tab separated file.
 90
	Call addCustomClasses afterwards to register your own base classes to the system
 91
	This will be required to assure your own base classes will be used instead of auto-generated
 92
	stand-in classes
 94
	:param hierarchyfile: Filepath to file modeling the class hierarchy using tab-indentation.
 95
		The root node has no indentation, whereas each child node adds one indentation level using 
 96
		tabs.
 97
	:param kwargs:
 98
		 * force_creation: see `addCustomType`
 99
	:note: all attributes of `addCustomType` are supported
100
	:note: there must be exactly one root type
101
	:return: iterator providing all class names that have been added"""
102
	dagtree = mrvmaya._dagTreeFromTupleList( mrvmaya._tupleListFromFile( hierarchyfile ) )
103
	typ._addCustomTypeFromDagtree( globals(), dagtree, **kwargs )
104
	return ( capitalize( nodetype ) for nodetype in dagtree.nodes_iter() )
106
def addCustomClasses( clsobjlist ):
107
	"""Add the given classes to the nodes module, making them available to the sytem
109
	:note: first the class hierarchy need to be updated using addCustomTypeFromFile.
110
		This must appen before your additional classes are parsed to assure our metaclass creator will not
111
		be called before it knows the class hierarchy ( and where to actually put your type ).
113
	:param clsobjlist: list of class objects whose names are mentioned in the dagtree"""
115
	for cls in clsobjlist:
116
		setattr( _thismodule, cls.__name__, cls )
119
def forceClassCreation( typeNameList ):
120
	"""Create the types from standin classes from the given typeName iterable.
121
	The typenames must be upper case
123
	:return: List of type instances ( the classes ) that have been created"""
124
	outclslist = list()
125
	standincls = mrvmayautil.StandinClass
126
	for typename in typeNameList:
127
		typeCls = getattr( _thismodule, typename )
128
		if isinstance( typeCls, standincls ):
129
			outclslist.append( typeCls.createCls() )
131
	return outclslist
133
def enforcePersistence( ):
134
	"""Call this method to ensure that the persistance plugin is loaded and available.
135
	This should by used by plugins which require persitence features but want to 
136
	be sure it is not disabled on the target system"""
137
	import mrv.maya.nt.storage as storage
138
	import mrv.maya.nt.persistence as persistence
140
	os.environ[persistence.persistence_enabled_envvar] = "1"
141
	reload(persistence)
142
	reload(storage)
143
	persistence.__initialize( _thismodule )
149
def _init_package( ):
150
	"""Do the main initialization of this package"""
151
	import mrv.maya.mdb as mdb
152
	typ.targetModule = _thismodule			# init metaclass with our module
153
	typ._nodesdict = globals()
154
	typ.initNodeHierarchy()
155
	typ.initTypeNameToMfnClsMap()
156
	typ.initWrappers(globals())
159
	typ.codegen = mdb.PythonMFnCodeGenerator(typ._nodesdict)
162
	import base
163
	base._nodesdict = globals()
166
	import apipatch
167
	apipatch.init_applyPatches()
170
	init_modules( __file__, "mrv.maya.nt", self_module = _thismodule )
173
def _force_type_creation():
174
	"""Enforce the creation of all types - must be called once all custom types 
175
	were imported"""
176
	standincls = mrvmayautil.StandinClass
177
	for cls in _thismodule.__dict__.itervalues():
178
		if isinstance( cls, standincls ):
179
			cls.createCls()
184
def _init_plugin_db():
185
	"""Find loaded plugins and provide dummies for their types - this assures iteration 
186
	will not stop on these types for instance"""
187
	global pluginDB
188
	pluginDB = PluginDB()
194
class PluginDB(dict):
195
	"""Simple container keeping information about the loaded plugins, namely the node
196
	types they register.
198
	As PyMel code has shown, we cannot rely on pluginLoaded and unloaded callbacks, which 
199
	is why we just listen to plugin changed events, and figure out the differences ourselves.
201
	Currently we are only interested in the registered node types, which is why we 
202
	are on ``mrv.maya.nt`` level, not on ``mrv.maya`` level
203
	"""
204
	__slots__ = 'log'
206
	def __init__(self):
207
		"""Upon initialization, we will parse the currently loaded plugins and 
208
		register them. Additionally we register our event to stay in the loop 
209
		if anything changes."""
210
		self.log = logging.getLogger('mrv.maya.nt.%s' % type(self).__name__)
213
		melstr = 'python("import mrv.maya.nt; mrv.maya.nt.pluginDB.plugin_registry_changed()")'
214
		if env.appVersion()[0] < 2011.0:
215
			cmds.pluginInfo(changedCommand=melstr)
216
		else:
220
			mrvmaya.Mel.eval('pluginInfo -changedCommand "%s"' % melstr.replace('"', '\\"'))
222
		self.plugin_registry_changed()
224
	def plugin_registry_changed(self, *args):
225
		"""Called by maya to indicate something has changed. 
226
		We will diff the returned plugin information with our own database 
227
		to determine which plugin was added or removed, to make the appropriate 
228
		calls"""
229
		self.log.debug("registry changed")
231
		loaded_plugins = set(cmds.pluginInfo(q=1, listPlugins=1) or list())
232
		our_plugins = set(self.keys())
235
		for pn in loaded_plugins - our_plugins:
236
			self.plugin_loaded(pn)
239
		for pn in our_plugins - loaded_plugins:
240
			self.plugin_unloaded(pn)
242
	def plugin_loaded(self, pluginName):
243
		"""Retrieve plugin information from a plugin named ``pluginName``, which is 
244
		assumed to be loaded.
245
		Currently the nodetypes found are added to the node-type tree to make them available.
246
		The plugin author is free to add specialized types to the tree afterwards, overwriting 
247
		the default ones.
249
		We loosely determine the inheritance by differentiating them into types suggested
250
		by MFn::kPlugin<Name>Node"""
251
		import base		# needs late import, TODO: reorganize modules
253
		self.log.debug("plugin '%s' loaded" % pluginName)
255
		type_names = cmds.pluginInfo(pluginName, q=1, dependNode=1) or list()
256
		self[pluginName] = type_names
259
		dgmod = api.MDGModifier()
260
		dagmod = api.MDagModifier()
261
		transobj = None
263
		nt = globals()
264
		for tn in type_names:
265
			tnc = capitalize(tn)
266
			if tnc in nt:
267
				self.log.debug("Skipped type %s as it did already exist in module" % tnc)
268
				continue
274
			apitype = None
275
			try:
276
				apitype = dgmod.createNode(tn).apiType()
277
			except RuntimeError:
278
				try:
284
					if transobj is None:
285
						transobj = dagmod.createNode("transform")
288
					apitype = dagmod.createNode(tn, transobj).apiType()
289
				except RuntimeError:
290
					self.log.error("Failed to retrieve apitype of node type %s - skipped" % tnc)
291
					continue
295
			parentclsname = base._plugin_type_to_node_type_name.get(apitype, 'Unknown')
296
			typ._addCustomType( nt, parentclsname, tnc, force_creation=True )
299
	def plugin_unloaded(self, pluginName):
300
		"""Remove all node types registered by pluginName unless they have been 
301
		registered by a third party. We cannot assume that they listen to these events, 
302
		hence we just keep the record as it will not harm.
304
		In any way, we will remove any record of the plugin from our db"""
305
		self.log.debug("plugin '%s' unloaded" % pluginName)
308
		installed_type_names = self[pluginName]
309
		del(self[pluginName])
312
		nt = globals()
313
		for tn in installed_type_names:
314
			tnc = capitalize(tn)
316
			try:
317
				node_type = nt[tnc]
318
			except KeyError:
320
				self.log.warn("Type %s of unloaded plugin %s was already de-registered in mrv type system - skipped" % (tnc, pluginName)) 
321
				continue
325
			parentclsname = node_type.__base__.__name__
326
			if not parentclsname.startswith('Unknown'):
327
				continue
330
			typ._removeCustomType(nt, tnc)
339
if 'init_done' not in locals():
340
	init_done = False
342
if not init_done:
344
	_init_package( )
348
	from base import *
349
	from geometry import *
350
	from set import *
351
	from anim import *
352
	from it import *
353
	from storage import *
356
	import __builtin__
357
	set = __builtin__.set
360
	from mrv.maya.ns import Namespace
363
	_force_type_creation()
364
	_init_plugin_db()
366
init_done = True