1
2 """
3 All classes required to wrap maya nodes in an object oriented manner into python objects
4 and allow easy handling of them.
5
6 These python classes wrap the API representations of their respective nodes - most general
7 commands will be natively working on them.
8
9 These classes follow the node hierarchy as supplied by the maya api.
10
11 Optionally: Attribute access is as easy as using properties like:
12
13 >>> node.translateX
14
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.
17
18 Default maya commands will require them to be used as strings instead.
19 """
20 __docformat__ = "restructuredtext"
21
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
29
30 import maya.cmds as cmds
31 import maya.OpenMaya as api
32
33 import sys
34 import os
35 import logging
36
37
38
39
40
41
42 pluginDB = None
43
44
45
46
47
49 """ Add a custom class to this module - it will be handled like a native type
50
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__
70
71
72 typ._addCustomType( globals(), parentname, newclsname, **kwargs )
73
74
75 if newclsobj:
76 setattr( _thismodule, newclsname, newclsobj )
77
79 """Removes the given type from this module as well as from the type hierarchy.
80 This makes it unavailble to MRV
81
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)
87
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
93
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() )
105
107 """Add the given classes to the nodes module, making them available to the sytem
108
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 ).
112
113 :param clsobjlist: list of class objects whose names are mentioned in the dagtree"""
114
115 for cls in clsobjlist:
116 setattr( _thismodule, cls.__name__, cls )
117
118
120 """Create the types from standin classes from the given typeName iterable.
121 The typenames must be upper case
122
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() )
130
131 return outclslist
132
144
145
146
147
148
171
172
180
181
182
183
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()
189
190
191
192
193
195 """Simple container keeping information about the loaded plugins, namely the node
196 types they register.
197
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.
200
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'
205
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
212
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:
217
218
219
220 mrvmaya.Mel.eval('pluginInfo -changedCommand "%s"' % melstr.replace('"', '\\"'))
221
222 self.plugin_registry_changed()
223
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")
230
231 loaded_plugins = set(cmds.pluginInfo(q=1, listPlugins=1) or list())
232 our_plugins = set(self.keys())
233
234
235 for pn in loaded_plugins - our_plugins:
236 self.plugin_loaded(pn)
237
238
239 for pn in our_plugins - loaded_plugins:
240 self.plugin_unloaded(pn)
241
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.
248
249 We loosely determine the inheritance by differentiating them into types suggested
250 by MFn::kPlugin<Name>Node"""
251 import base
252
253 self.log.debug("plugin '%s' loaded" % pluginName)
254
255 type_names = cmds.pluginInfo(pluginName, q=1, dependNode=1) or list()
256 self[pluginName] = type_names
257
258
259 dgmod = api.MDGModifier()
260 dagmod = api.MDagModifier()
261 transobj = None
262
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
269
270
271
272
273
274 apitype = None
275 try:
276 apitype = dgmod.createNode(tn).apiType()
277 except RuntimeError:
278 try:
279
280
281
282
283
284 if transobj is None:
285 transobj = dagmod.createNode("transform")
286
287
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
292
293
294
295 parentclsname = base._plugin_type_to_node_type_name.get(apitype, 'Unknown')
296 typ._addCustomType( nt, parentclsname, tnc, force_creation=True )
297
298
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.
303
304 In any way, we will remove any record of the plugin from our db"""
305 self.log.debug("plugin '%s' unloaded" % pluginName)
306
307
308 installed_type_names = self[pluginName]
309 del(self[pluginName])
310
311
312 nt = globals()
313 for tn in installed_type_names:
314 tnc = capitalize(tn)
315
316 try:
317 node_type = nt[tnc]
318 except KeyError:
319
320 self.log.warn("Type %s of unloaded plugin %s was already de-registered in mrv type system - skipped" % (tnc, pluginName))
321 continue
322
323
324
325 parentclsname = node_type.__base__.__name__
326 if not parentclsname.startswith('Unknown'):
327 continue
328
329
330 typ._removeCustomType(nt, tnc)
331
332
333
334
335
336
337
338
339 if 'init_done' not in locals():
340 init_done = False
341
342 if not init_done:
343
344 _init_package( )
345
346
347
348 from base import *
349 from geometry import *
350 from set import *
351 from anim import *
352 from it import *
353 from storage import *
354
355
356 import __builtin__
357 set = __builtin__.set
358
359
360 from mrv.maya.ns import Namespace
361
362
363 _force_type_creation()
364 _init_plugin_db()
365
366 init_done = True
367