Package mrv :: Package maya :: Package nt
[hide private]
[frames] | no frames]

Source Code for Package mrv.maya.nt

  1  # -*- coding: utf-8 -*- 
  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  # May not use all as it will receive all submodules  
 38  # __all__ 
 39   
 40  #{ Globals 
 41   
 42  pluginDB = None 
 43   
 44  #} END globals 
 45   
 46  #{ Common 
 47   
48 -def addCustomType( newcls, parentClsName=None, **kwargs ):
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 # add to hierarchy tree 72 typ._addCustomType( globals(), parentname, newclsname, **kwargs ) 73 74 # add the class to our module if required 75 if newclsobj: 76 setattr( _thismodule, newclsname, newclsobj )
77
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 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
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 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
106 -def addCustomClasses( clsobjlist ):
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 # add the classes 115 for cls in clsobjlist: 116 setattr( _thismodule, cls.__name__, cls )
117 # END for each class to add 118
119 -def forceClassCreation( typeNameList ):
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 # END for each typename 131 return outclslist
132
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 139 140 os.environ[persistence.persistence_enabled_envvar] = "1" 141 reload(persistence) 142 reload(storage) 143 persistence.__initialize( _thismodule )
144 145 #} END common utilities 146 147 #{ Initialization 148
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()) 157 158 # code generator needs an initialized nodes dict to work 159 typ.codegen = mdb.PythonMFnCodeGenerator(typ._nodesdict) 160 161 # initialize base module with our global namespace dict 162 import base 163 base._nodesdict = globals() 164 165 # must come last as typ needs full initialization first 166 import apipatch 167 apipatch.init_applyPatches() 168 169 # initialize modules 170 init_modules( __file__, "mrv.maya.nt", self_module = _thismodule )
171 172
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()
180 # END create type 181 # END for each stored type 182 183
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()
189 190 #} END initialization 191 192 #{ Utility Classes 193
194 -class PluginDB(dict):
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
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__) 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) 216 else: 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()
223
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") 230 231 loaded_plugins = set(cmds.pluginInfo(q=1, listPlugins=1) or list()) 232 our_plugins = set(self.keys()) 233 234 # plugins loaded 235 for pn in loaded_plugins - our_plugins: 236 self.plugin_loaded(pn) 237 238 # plugins unloded 239 for pn in our_plugins - loaded_plugins: 240 self.plugin_unloaded(pn)
241
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. 248 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 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 # register types in the system if possible 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 # END skip existing node types ( probably user created ) 270 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 274 apitype = None 275 try: 276 apitype = dgmod.createNode(tn).apiType() 277 except RuntimeError: 278 try: 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 ). 284 if transobj is None: 285 transobj = dagmod.createNode("transform") 286 # END assure we have parent 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 # END dag exception handling 293 # END dg exception handling 294 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 298
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. 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 # clear our record 308 installed_type_names = self[pluginName] 309 del(self[pluginName]) 310 311 # deregister types if possible 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 # wasnt registered anymore ? 320 self.log.warn("Type %s of unloaded plugin %s was already de-registered in mrv type system - skipped" % (tnc, pluginName)) 321 continue 322 # END handle exception 323 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'): 327 continue 328 # END skip custom nodes 329 330 typ._removeCustomType(nt, tnc)
331 # END for each typename 332 333 334 335 #} END utilty classes 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 # overwrite dummy node bases with hand-implemented ones 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 # fix set 356 import __builtin__ 357 set = __builtin__.set 358 359 # import additional classes required in this module 360 from mrv.maya.ns import Namespace 361 362 # Setup all actual types - this makes the use much easier 363 _force_type_creation() 364 _init_plugin_db() 365 366 init_done = True 367