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:
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.
20
__docformat__ = "restructuredtext"
22
import mrv.maya as mrvmaya
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
37
# May not use all as it will receive all submodules
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
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"""
64
parentname = parentClsName
65
if not isinstance( newcls, basestring ):
66
newclsname = newcls.__name__
69
parentname = newcls.__bases__[0].__name__
71
# add to hierarchy tree
72
typ._addCustomType( globals(), parentname, newclsname, **kwargs )
74
# add the class to our module if required
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
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
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 )
117
# END for each class to add
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"""
125
standincls = mrvmayautil.StandinClass
126
for typename in typeNameList:
127
typeCls = getattr( _thismodule, typename )
128
if isinstance( typeCls, standincls ):
129
outclslist.append( typeCls.createCls() )
130
# END for each typename
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"
143
persistence.__initialize( _thismodule )
145
#} END common utilities
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())
158
# code generator needs an initialized nodes dict to work
159
typ.codegen = mdb.PythonMFnCodeGenerator(typ._nodesdict)
161
# initialize base module with our global namespace dict
163
base._nodesdict = globals()
165
# must come last as typ needs full initialization first
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
176
standincls = mrvmayautil.StandinClass
177
for cls in _thismodule.__dict__.itervalues():
178
if isinstance( cls, standincls ):
181
# END for each stored type
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"""
188
pluginDB = PluginDB()
195
"""Simple container keeping information about the loaded plugins, namely the node
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
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__)
211
# yes, we need a string here, yes, its mel
212
# UPDATE: In maya 2011, a method is alright !
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)
217
# Okay, if we do this, maya crashes during shutdown, which is why we
218
# use mel then ... nice work, Autodesk ;)
219
# cmds.pluginInfo(changedCommand=self.plugin_registry_changed)
220
mrvmaya.Mel.eval('pluginInfo -changedCommand "%s"' % melstr.replace('"', '\\"'))
221
# END install callback
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
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
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
258
# register types in the system if possible
259
dgmod = api.MDGModifier()
260
dagmod = api.MDagModifier()
264
for tn in type_names:
267
self.log.debug("Skipped type %s as it did already exist in module" % tnc)
269
# END skip existing node types ( probably user created )
271
# get the type id- first try depend node, then dag node. Never actually
272
# create the nodes in the scene, created MObjects will be discarded
273
# once the modifiers go out of scope
276
apitype = dgmod.createNode(tn).apiType()
279
# most plugin dag nodes require a transform to be created
280
# We create a dummy for the dagmod, otherwise it would create
281
# it for us and return the parent transform instead, which
282
# has no child officially yet as its not part of the dag
283
# ( so we cannot query the child from there ).
285
transobj = dagmod.createNode("transform")
286
# END assure we have parent
288
apitype = dagmod.createNode(tn, transobj).apiType()
290
self.log.error("Failed to retrieve apitype of node type %s - skipped" % tnc)
292
# END dag exception handling
293
# END dg exception handling
295
parentclsname = base._plugin_type_to_node_type_name.get(apitype, 'Unknown')
296
typ._addCustomType( nt, parentclsname, tnc, force_creation=True )
297
# END for each type to handle
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])
311
# deregister types if possible
313
for tn in installed_type_names:
319
# wasnt registered anymore ?
320
self.log.warn("Type %s of unloaded plugin %s was already de-registered in mrv type system - skipped" % (tnc, pluginName))
322
# END handle exception
324
# remove the type only if it was one of our unknown default types
325
parentclsname = node_type.__base__.__name__
326
if not parentclsname.startswith('Unknown'):
328
# END skip custom nodes
330
typ._removeCustomType(nt, tnc)
331
# END for each typename
339
if 'init_done' not in locals():
347
# overwrite dummy node bases with hand-implemented ones
349
from geometry import *
353
from storage import *
357
set = __builtin__.set
359
# import additional classes required in this module
360
from mrv.maya.ns import Namespace
362
# Setup all actual types - this makes the use much easier
363
_force_type_creation()