コード例 #1
0
def _addCustomType( targetmoduledict, parentclsname, newclsname,
				   	metaclass=MetaClassCreatorNodes, **kwargs ):
	"""Add a custom type to the system such that a node with the given type will
	automatically be wrapped with the corresponding class name
	
	:param targetmoduledict: the module's dict to which standin classes are supposed to be added
	:param parentclsname: the name of the parent node type - if your new class
		has several parents, you have to add the new types beginning at the first exsiting parent
		as written in the maya/cache/nodeHierarchy.html file
	:param newclsname: the new name of your class - it must exist targetmodule
	:param metaclass: meta class object to be called to modify your type upon creation
		It will not be called if the class already exist in targetModule. Its recommended to derive it
		from the metaclass given as default value.
	:raise KeyError: if the parentclsname does not exist"""
	# add new type into the type hierarchy #
	parentclsname = uncapitalize( parentclsname )
	newclsname = uncapitalize( newclsname )
	nodeTypeTree.add_edge( parentclsname, newclsname )

	# create wrapper ( in case newclsname does not yet exist in target module )
	mrvmaya.initWrappers( targetmoduledict, [ newclsname ], metaclass, **kwargs )
コード例 #2
0
ファイル: typ.py プロジェクト: mrv-developers/mrv
def _addCustomType( targetmoduledict, parentclsname, newclsname,
                    metaclass=MetaClassCreatorNodes, **kwargs ):
    """Add a custom type to the system such that a node with the given type will
    automatically be wrapped with the corresponding class name
    
    :param targetmoduledict: the module's dict to which standin classes are supposed to be added
    :param parentclsname: the name of the parent node type - if your new class
        has several parents, you have to add the new types beginning at the first exsiting parent
        as written in the maya/cache/nodeHierarchy.html file
    :param newclsname: the new name of your class - it must exist targetmodule
    :param metaclass: meta class object to be called to modify your type upon creation
        It will not be called if the class already exist in targetModule. Its recommended to derive it
        from the metaclass given as default value.
    :raise KeyError: if the parentclsname does not exist"""
    # add new type into the type hierarchy #
    parentclsname = uncapitalize( parentclsname )
    newclsname = uncapitalize( newclsname )
    nodeTypeTree.add_edge( parentclsname, newclsname )

    # create wrapper ( in case newclsname does not yet exist in target module )
    mrvmaya.initWrappers( targetmoduledict, [ newclsname ], metaclass, **kwargs )
コード例 #3
0
ファイル: typ.py プロジェクト: mrv-developers/mrv
 def func_nameToTree( name ):
     # first check whether an uncapitalized name is known, if not, check 
     # the capitalized version ( for special cases ), finalyl return
     # the uncapitalized version which is the default
     name = uncapitalize(name)
     if nodeTypeTree.has_node(name):
         return name
         
     capname = capitalize(name)
     if nodeTypeTree.has_node(capname):
         return capname
     
     return name
コード例 #4
0
		def func_nameToTree( name ):
			# first check whether an uncapitalized name is known, if not, check 
			# the capitalized version ( for special cases ), finalyl return
			# the uncapitalized version which is the default
			name = uncapitalize(name)
			if nodeTypeTree.has_node(name):
				return name
				
			capname = capitalize(name)
			if nodeTypeTree.has_node(capname):
				return capname
			
			return name
コード例 #5
0
ファイル: typ.py プロジェクト: mrv-developers/mrv
def _removeCustomType( targetmoduledict, customTypeName ):
    """Remove the given typename from the given target module's dictionary as 
    well as from internal caches
    
    :note: does nothing if the type does not exist
    :param targetmoduledict: dict of your module to remove the type from
    :param customTypeName: name of the type to be removed, its expected
        to be capitalized"""
    try:
        del(targetmoduledict[customTypeName])
    except KeyError:
        pass
    # END remove from dictionary
    
    customTypeName = uncapitalize(customTypeName)
    if nodeTypeTree.has_node(customTypeName):
        nodeTypeTree.remove_node(customTypeName)
コード例 #6
0
def _removeCustomType( targetmoduledict, customTypeName ):
	"""Remove the given typename from the given target module's dictionary as 
	well as from internal caches
	
	:note: does nothing if the type does not exist
	:param targetmoduledict: dict of your module to remove the type from
	:param customTypeName: name of the type to be removed, its expected
		to be capitalized"""
	try:
		del(targetmoduledict[customTypeName])
	except KeyError:
		pass
	# END remove from dictionary
	
	customTypeName = uncapitalize(customTypeName)
	if nodeTypeTree.has_node(customTypeName):
		nodeTypeTree.remove_node(customTypeName)
コード例 #7
0
ファイル: typ.py プロジェクト: kthulhu/mrv
	def __new__( metacls, name, bases, clsdict ):
		""" Called to create the class with name """
		# HANDLE MEL COMMAND
		#######################
		cmdname = uncapitalize( name )
		if hasattr( mcmds, cmdname ):
			melcmd = getattr( mcmds, cmdname )
			clsmelcmd = staticmethod( melcmd )
			clsdict['__melcmd__'] = clsmelcmd
		else:
			pass # don't bother, can be one of our own classes that will
			#raise ValueError( "Did not find command for " + cmdname )

		# HANDLE PROPERTIES
		####################
		# read the properties attribute to find names to automatically create
		# query and edit properties
		propertynames = clsdict.get( "_properties_", list() )
		for pname in propertynames:
			attrname = "p_%s" % pname
			# allow user overrides
			if attrname not in clsdict:
				clsdict[ attrname ] = propertyQE( pname )
		# END for each property

		# HANDLE EVENTS
		##################
		# read the event description and create Event instances that will
		# register themselves on first use, allowing multiple listeners per maya event
		eventnames = clsdict.get( "_events_", list() )
		event_kwargs = dict()
		if clsdict.get( "strong_event_handlers", False ):
			event_kwargs[ "weak" ] = False

		for ename in eventnames:
			attrname = "e_%s" % ename
			# allow user overrides
			if attrname not in clsdict:
				clsdict[ attrname ] = EventSenderUI._UIEvent( ename, **event_kwargs )
		# END for each event name

		newcls = super( MetaClassCreatorUI, metacls ).__new__( _typetree, _uipackage,
																metacls, name, bases, clsdict )

		return newcls
コード例 #8
0
ファイル: mdb.py プロジェクト: adamcobabe/mrv
	def unMFn(name):
		return uncapitalize(name[3:])
コード例 #9
0
ファイル: mdb.py プロジェクト: adamcobabe/mrv
def generateNodeHierarchy( ):
	"""Generate the node-hierarchy for the current version based on all node types 
	which can be created in maya.
	
	:return: tuple(DAGTree, typeToMFnClsNameList)
	
		* DAGTree representing the type hierarchy
		* list represents typeName to MFnClassName associations
		 
	:note: should only be run as part of the upgrade process to prepare MRV for  a
		new maya release. Otherwise the nodetype tree will be read from a cache"""
	from mrv.util import DAGTree
	from mrv.util import uncapitalize, capitalize
	from mrv.maya.util import MEnumeration
	
	# init DagTree
	root = "_root_" 
	depnode = 'dependNode'
	depnode_list = [depnode]
	noderoottype = 'node'
	dagTree = DAGTree()
	dagTree.add_edge(root, noderoottype)
	dagTree.add_edge(noderoottype, depnode)
	
	apiTypeToNodeTypeMap = dict()		# apiTypeStr -> nodeTypeName
	mfnTypes = set()					# apiTypeStr of mfns used by NodeTypes
	sl = list()							# string list
	
	
	mfndep = api.MFnDependencyNode()
	def getInheritanceAndUndo(obj, modifier):
		"""Takes a prepared modifier ( doIt not yet called ) and the previously created object, 
		returning the inheritance of the obj which was retrieved before undoing
		its creation"""
		modifier.doIt()
		mfndep.setObject(obj)
		inheritance = cmds.nodeType(mfndep.name(), i=1)
		modifier.undoIt()
		return inheritance
	# END utility
	
	
	# CREATE ALL NODE TYPES
	#######################
	# query the inheritance afterwards
	for nodetype, obj, mod in _iterAllNodeTypes():
		inheritance = getInheritanceAndUndo(obj, mod)
		
		if not inheritance:
			log.error("Failed on type %s" % nodetype)
			continue
		# END handle unusual case
		
		# filter bases
		for parent, child in zip(depnode_list + inheritance[:-1], inheritance):
			dagTree.add_edge(parent, child)
		# END for each edge to add
		
		# retrieve all compatible MFnTypes - these refer to apiTypes which are 
		# also used by Nodes. Currently we have only the type of the node, fortunately, 
		# it will match very well with the MFnType, by just prepending MFn.
		# As some MFn are in other modules, we will search em all ... later
		apiTypeToNodeTypeMap[obj.apiTypeStr()] = nodetype
		
		api.MGlobal.getFunctionSetList(obj, sl)
		for mfnType in sl:
			mfnTypes.add(mfnType)
	# END for each node type
	
	# INSERT SPECIAL TYPES
	######################
	# used by the type system if it cannot classify a node at all
	dagTree.add_edge(depnode, 'unknown')
	
	# can be iterated using the DagIterator, and it should at least be a dag node, 
	# not unknown. The groundPlane is actually a special object that users shouldn't
	# touch directly
	dagTree.add_edge('transform', 'groundPlane')
	
	# although we don't handle manips directly, we must still support them if it
	# is a plugin manipulator
	dagTree.add_edge('transform', 'manipContainer')
	
	
	
	# INSERT PLUGIN TYPES
	######################
	for edge in (	(depnode, 'DependNode'),
					('shape', 'Shape'), 
					('locator', 'LocatorNode'), 
					('spring', 'SpringNode'), 
					('transform', 'TransformNode'), 
					('manipContainer', 'ManipContainer'), 
					('dynBase', 'EmitterNode'), 
					('field', 'FieldNode'), 
					('objectSet', 'ObjectSet'), 
					('geometryFilter', 'DeformerNode'), 
					(depnode, 'HwShaderNode'), 
					('ikSolver', 'IkSolver'), 
					(depnode, 'ImagePlaneNode'), 
					(depnode, 'ParticleAttributeMapperNode')	):
		dagTree.add_edge(edge[0], 'unknownPlugin'+edge[1])
	# END for each plugin edge to add
	
	
	
	# BULD TYPE-TO-MFN MAP
	######################
	# Prepare data to be put into a type separated file, it associates 
	# a nodeType or nodeApiType with the respecitve MFnClass name
	typeToMFn = set()		# list((typeName, MFnClsName), ...)
	
	# add default associations - some are not picked up due to name mismatches
	typeToMFn.add((noderoottype, 'MFn'))
	typeToMFn.add((depnode, 'MFnDependencyNode'))
	typeToMFn.add(('dagContainer', 'MFnContainerNode'))
	
	abstractMFns = ('MFnBase', )		# MFns which cannot be instantiated ans should be ignored
	failedMFnTypes = list()			# list of types we could not yet associate
	
	modsapi = apiModules()
	for mfnApiType in mfnTypes:
		mfnNodePseudoType = uncapitalize(mfnApiType[1:])	# # kSomething -> something
		nodeType = apiTypeToNodeTypeMap.get(mfnApiType, mfnNodePseudoType)
		
		# MFnSets follow their kMFnType names, but when we try to associate it with 
		# the actual nodeType . Sometimes, they follow the actual nodeType, so we 
		# have to use this one as well
		found = False
		for nt, mfnNameCandidate in ( (mfnNodePseudoType, "MFn%s" % capitalize(mfnApiType[1:])), 
									   (nodeType, "MFn%s" % capitalize(nodeType)) ):
			# ignore abstract ones
			if mfnNameCandidate in abstractMFns:
				continue
			
			for modapi in modsapi:
				if hasattr(modapi, mfnNameCandidate):
					found = True
					
					# prefer a real nodetype if we have one - it will default
					# to the pseudotype
					typeToMFn.add((nodeType, mfnNameCandidate))
					break
				# END module with given name exists
			# END for each api module
			
			if found:
				break
		# END for each nodeType/mfnNamecandidate
		
		# still not found ? Keep it, but only if there is a nodetype 
		# associated with it
		if not found and mfnApiType in apiTypeToNodeTypeMap:
			failedMFnTypes.append(mfnApiType)
		# END keep a record
	# END for each mfnType
	
	
	
	# DATA, COMPONENTS, ATTRIBUTES
	###############################
	# get inheritance of Data, Component and Attribute types
	def unMFn(name):
		return uncapitalize(name[3:])
	# END remove MFn in front of MFnSomething strings
	
	for mfnsuffix in ("data", "component", "attribute"):
		mfnsuffixcap = capitalize(mfnsuffix)
		mfnnames = list()
		for modapi in modsapi:
			mfnnames.extend( n for n in dir(modapi) if n.endswith(mfnsuffixcap) )
		# END for each api module to get information from
		
		dagTree.add_edge(root, mfnsuffix)
		
		mfnsuffix_root = [ mfnsuffix ]
		for mfnname in mfnnames:
			for modapi in modsapi:
				try:
					mfncls = getattr(modapi, mfnname)
				except AttributeError:
					continue
				# END handle multi-modules
				
				# skip classes which are just named like the super type, but 
				# don't actually inherit it
				if "MFn%s" % mfnsuffixcap not in ( p.__name__ for p in mfncls.mro() ):
					continue
				
				# add type->MFn association
				typeToMFn.add((unMFn(mfnname), mfnname))
				
				# cut object and MFnBase
				# from the names, cut the MFn and uncaptialize it: MFnData -> data
				pclsnames = [ unMFn(p.__name__) for p in list(reversed(mfncls.mro()))[2:] ]
				for parent, child in zip(pclsnames[:-1], pclsnames[1:]):
					dagTree.add_edge(parent, child)
				# END for each mfn child to add
				
				break
			# END for each api module
		# END for each name
	# END for each mfnsuffix
	
	
	# HANDLE FAILED MFN-ASSOCITAIONS
	################################
	# lets take some very special care !
	if failedMFnTypes:
		# Here we handle cases which don't follow any naming conventions really
		# Hence we have to instantiate an object of the failed type, then 
		# we instantiate all the remaining functions sets to see which one fits.
		# If the required one has the requested type, we have a match. 
		# If we have no type match, its still a valid MFn - If we haven't seen it 
		# yet, its probably a base MFn whose kType string allows deduction of the 
		# actual abtract node type which we will use instead.
		associatedMFns = ( t[1] for t in typeToMFn )
		allMFnSetNames = list()
		for modapi in modsapi:
			allMFnSetNames.extend( n for n in dir(modapi) if n.startswith('MFn') and 
															not n.endswith('Ptr') and 
															not '_' in n and 		# skip 'special' ones
															not 'Manip' in n )		# skip everything about Manipulators
		# END get all MFn names
		
		# find MFnClasses for each candidate name
		candidateMFnNames = (set(allMFnSetNames) - set(associatedMFns)) - set(abstractMFns)
		candidateMFns = list()
		for cn in list(candidateMFnNames):
			for modapi in modsapi:
				try:
					mfncls = getattr(modapi, cn)
					# ignore them if they don't derive from MFnBase
					if not hasattr(mfncls, "type"):
						log.debug("Skipped MFn %s as it didn't derive from MFnBase" % mfncls)
						candidateMFnNames.discard(cn)
						continue
					# END skip mfn
					candidateMFns.append(mfncls)
					break
				except AttributeError:
					continue
			# END for each api module
		# END for each candidate name
		
		succeededMFnNames = set()
		
		
		# PRUNE REMAINING MFNs
		# prune out all MFnClasses that can be constructed without an actual object
		enumMembers = MEnumDescriptor('Type')
		enumMembers.extend( m for m in dir(api.MFn) if m.startswith('k') )
		mfntypes = MEnumeration.create(enumMembers, api.MFn)
		
		for mfncls in candidateMFns[:]:
			try:
				mfninst = mfncls()
				if mfntypes.nameByValue(mfninst.type()) in failedMFnTypes:
					continue
				# END keep actually missing MFns
				candidateMFns.remove(mfncls)
				candidateMFnNames.remove(mfncls.__name__)
			except RuntimeError:
				continue
		# END for each possible MFn to prune
		
		# at this point, we have about 500 api types with no MFn, but only 
		# about 10 function sets, 
		# Now ... we  brute-force test our way through all of these to find 
		# matching ones ... argh
		derivedMatches = list()		# keeps tuple(kTypeStr, mfnname) of matches of derived types
		perfectMatches = list()		# keeps mfnnames of perfect matches
		for failedApiTypeStr in failedMFnTypes:
			nodeType = apiTypeToNodeTypeMap[failedApiTypeStr]
			obj, mod = _createTmpNode(nodeType)
			
			removeThisMFn = None
			for mfncls in candidateMFns:
				try:
					mfninst = mfncls(obj)
				except RuntimeError:
					continue
				# END handle incompatability
				
				apiTypeStr = mfntypes.nameByValue(mfninst.type())
				
				if apiTypeStr not in failedMFnTypes:
					removeThisMFn = mfncls
					break
				# END remove MFns that no one wants
				
				if apiTypeStr == failedApiTypeStr:
					mfnname = mfncls.__name__
					typeToMFn.add((nodeType, mfnname))
					perfectMatches.append(mfnname)
					removeThisMFn = mfncls
					break
				# END perfect match 
				
				# its matching, but its not perfectly suited for our node type
				# We keep it, and will map it later if we don't find a better match
				derivedMatches.append((apiTypeStr, mfncls.__name__))
			# END for each mfncls
			
			if removeThisMFn is not None:
				succeededMFnNames.add(removeThisMFn.__name__)
				candidateMFns.remove(removeThisMFn)
			# END remove matched MFn
			
			if not candidateMFns:
				break
			# END abort search if there is nothing left 
		# END for each failed type
		
		# HANDLE DERIVED MFns
		# prune out all derived mfs which have found a perfect match in the meanwhile
		# the rest will be added to the list
		for apiTypeStr, mfnname in filter(lambda t: t not in perfectMatches, derivedMatches):
			typeToMFn.add((apiTypeToNodeTypeMap[apiTypeStr], mfnname))
			succeededMFnNames.add(mfnname)
		# END for each apiTypeStr left ot add
		
		
		# LAST MANUAL WORK
		##################
		# SubDees, if created empty, cannot be attached to their function set
		# Hence we don't see the match, but ... we know its there, so we add it
		# ourselves
		for nodeType, mfnname in (('subdiv', 'MFnSubd'), ):
			typeToMFn.add((nodeType, mfnname))
			succeededMFnNames.add(mfnname)
		# END for each manually added type
		
		
		for mfnname in candidateMFnNames - succeededMFnNames:
			log.warn("Could not associate MFn: %s" % mfnname)
		# END provide some info
	# END special treatment
	return (dagTree, sorted(typeToMFn, key=lambda t: t[0]))
コード例 #10
0
ファイル: scene.py プロジェクト: kthulhu/mrv
class Scene(util.Singleton, util.EventSender):
    """Singleton Class allowing access to the maya scene
	
	You can register all events available in MSceneMessage easily usnig the following 
	syntax:
	
		>>> scene.beforeSoftwareRender = myFunctionObject
	
	"""

    kFileTypeMap = {
        "": "mayaAscii",  # treat untitled scenes as ma
        ".ma": "mayaAscii",
        ".mb": "mayaBinary"
    }

    #{ Events
    sender_as_argument = False

    # create events from 'kEventName', creating a corresponding event named
    # 'eventName'
    for eidName, eid in ((n, v)
                         for n, v in inspect.getmembers(api.MSceneMessage)
                         if n.startswith('k')):
        locals()[util.uncapitalize(eidName[1:])] = _SceneEvent(eid)
    # END for each message id to create

    #} END events

    #{ Edit Methods
    @classmethod
    def open(cls, scenepath=None, force=False, **kwargs):
        """ Open the scene at the given scenepath
		
		:param scenepath: The path to the file to be opened
			If None, the currently loaded file will reopened
		:param force: if True, the new scene will be loaded although currently
			loaded contains unsaved changes
		:param kwargs: passed to *cmds.file*
		:return: a Path to the loaded scene"""
        if not scenepath:
            scenepath = cls.name()

        # NOTE: it will return the last loaded reference instead of the loaded file - lets fix this !
        sourcePath = make_path(scenepath)
        kwargs.pop('open', kwargs.pop('o', None))
        kwargs.pop('force', kwargs.pop('f', None))
        lastReference = cmds.file(sourcePath.abspath(),
                                  open=1,
                                  force=force,
                                  **kwargs)
        return make_path(sourcePath)

    @classmethod
    def new(cls, force=False, **kwargs):
        """ Create a new scene
		
		:param force: if True, the new scene will be created even though there
			are unsaved modifications
		:param kwargs: passed to *cmds.file*
		:return: Path with name of the new file"""
        kwargs.pop('new', kwargs.pop('n', None))
        kwargs.pop('force', kwargs.pop('f', None))
        return make_path(cmds.file(new=True, force=force, **kwargs))

    @classmethod
    def rename(cls, scenepath):
        """Rename the currently loaded file to be the file at scenepath
		
		:param scenepath: string or Path pointing describing the new location of the scene.
		:return: Path to scenepath
		:note: as opposed to the normal file -rename it will also adjust the extension
		:raise RuntimeError: if the scene's extension is not supported."""
        scenepath = make_path(scenepath)
        try:
            cmds.file(rename=scenepath.expandvars())
            cmds.file(type=cls.kFileTypeMap[scenepath.ext()])
        except KeyError:
            raise RuntimeError("Unsupported filetype of: " + scenepath)
        # END exception handling

        return scenepath

    @classmethod
    def save(cls, scenepath=None, autodeleteUnknown=False, **kwargs):
        """Save the currently opened scene under scenepath in the respective format
		
		:param scenepath: if None, the currently opened scene will be saved, otherwise 
			the name will be changed. Paths leading to the file will automatically be created.
		:param autodeleteUnknown: if true, unknown nodes will automatically be deleted
			before an attempt is made to change the maya file's type
		:param kwargs: passed to cmds.file
		:return: Path at which the scene has been saved."""
        if scenepath is None or scenepath == "":
            scenepath = cls.name()

        scenepath = make_path(scenepath)
        curscene = cls.name()
        try:
            filetype = cls.kFileTypeMap[scenepath.ext()]
            curscenetype = cls.kFileTypeMap[curscene.ext()]
        except KeyError:
            raise RuntimeError("Unsupported filetype of: " + scenepath)

        # is it a save as ?
        if curscene != scenepath:
            cls.rename(scenepath)

        # assure path exists
        parentdir = scenepath.dirname()
        if not parentdir.exists():
            parentdir.makedirs()
        # END assure parent path exists

        # delete unknown before changing types ( would result in an error otherwise )
        if autodeleteUnknown and curscenetype != filetype:
            cls.deleteUnknownNodes()
        # END handle unkonwn nodes

        # safe the file
        kwargs.pop('save', kwargs.pop('s', None))
        kwargs.pop('type', kwargs.pop('typ', None))
        return make_path(cmds.file(save=True, type=filetype, **kwargs))

    @classmethod
    def export(cls, outputFile, nodeListOrIterable=None, **kwargs):
        """Export the given nodes or everything into the file at path
		
		:param outputFile: Path object or path string to which the data should 
			be written to. Parent directories will be created as needed
		:param nodeListOrIterable: if None, everything will be exported. 
			Otherwise it may be an MSelectionList ( recommended ), or a list of
			Nodes, MObjects or MDagPaths
		:param kwargs: passed to cmds.file, see the mel docs for modifying flags
		:return: Path to which the data was exported"""
        outputFile = make_path(outputFile)
        if not outputFile.dirname().isdir():
            outputFile.dirname().makedirs()
        # END create parent dirs

        prev_selection = None
        if nodeListOrIterable is None:
            kwargs['exportAll'] = True
        else:
            # export selected mode
            kwargs['exportSelected'] = True
            prev_selection = api.MSelectionList()
            api.MGlobal.getActiveSelectionList(prev_selection)

            import nt
            nt.select(nt.toSelectionList(nodeListOrIterable))
        # END handle nodes

        typ = kwargs.pop(
            'type',
            kwargs.pop('typ', cls.kFileTypeMap.get(outputFile.ext(), None)))
        if typ is None:
            raise RuntimeError("Invalid type in %s" % outputFile)
        # END handle type

        try:
            cmds.file(outputFile, type=typ, **kwargs)
            return outputFile
        finally:
            if prev_selection is not None:
                api.MGlobal.setActiveSelectionList(prev_selection)
            # END if we have a selection to restore
        # END handle selection

    #} END edit methods

    #{ Utilities
    @classmethod
    def deleteUnknownNodes(cls):
        """Deletes all unknown nodes in the scene
		
		:note: only do this if you are about to change the type of the scene during
			save or export - otherwise the operation would fail if there are still unknown nodes
			in the scene"""
        unknownNodes = cmds.ls(type="unknown")  # using mel is the faatest here
        if unknownNodes:
            cmds.delete(unknownNodes)

    #} END utilities

    #{ Query Methods
    @classmethod
    def name(cls):
        return make_path(cmds.file(q=1, exn=1))

    @classmethod
    def isModified(cls):
        return cmds.file(q=1, amf=True)
コード例 #11
0
 def unMFn(name):
     return uncapitalize(name[3:])
コード例 #12
0
def generateNodeHierarchy():
    """Generate the node-hierarchy for the current version based on all node types 
	which can be created in maya.
	
	:return: tuple(DAGTree, typeToMFnClsNameList)
	
		* DAGTree representing the type hierarchy
		* list represents typeName to MFnClassName associations
		 
	:note: should only be run as part of the upgrade process to prepare MRV for  a
		new maya release. Otherwise the nodetype tree will be read from a cache"""
    from mrv.util import DAGTree
    from mrv.util import uncapitalize, capitalize
    from mrv.maya.util import MEnumeration

    # init DagTree
    root = "_root_"
    depnode = 'dependNode'
    depnode_list = [depnode]
    noderoottype = 'node'
    dagTree = DAGTree()
    dagTree.add_edge(root, noderoottype)
    dagTree.add_edge(noderoottype, depnode)

    apiTypeToNodeTypeMap = dict()  # apiTypeStr -> nodeTypeName
    mfnTypes = set()  # apiTypeStr of mfns used by NodeTypes
    sl = list()  # string list

    mfndep = api.MFnDependencyNode()

    def getInheritanceAndUndo(obj, modifier):
        """Takes a prepared modifier ( doIt not yet called ) and the previously created object, 
		returning the inheritance of the obj which was retrieved before undoing
		its creation"""
        modifier.doIt()
        mfndep.setObject(obj)
        inheritance = cmds.nodeType(mfndep.name(), i=1)
        modifier.undoIt()
        return inheritance

    # END utility

    # CREATE ALL NODE TYPES
    #######################
    # query the inheritance afterwards
    for nodetype, obj, mod in _iterAllNodeTypes():
        inheritance = getInheritanceAndUndo(obj, mod)

        if not inheritance:
            log.error("Failed on type %s" % nodetype)
            continue
        # END handle unusual case

        # filter bases
        for parent, child in zip(depnode_list + inheritance[:-1], inheritance):
            dagTree.add_edge(parent, child)
        # END for each edge to add

        # retrieve all compatible MFnTypes - these refer to apiTypes which are
        # also used by Nodes. Currently we have only the type of the node, fortunately,
        # it will match very well with the MFnType, by just prepending MFn.
        # As some MFn are in other modules, we will search em all ... later
        apiTypeToNodeTypeMap[obj.apiTypeStr()] = nodetype

        api.MGlobal.getFunctionSetList(obj, sl)
        for mfnType in sl:
            mfnTypes.add(mfnType)
    # END for each node type

    # INSERT SPECIAL TYPES
    ######################
    # used by the type system if it cannot classify a node at all
    dagTree.add_edge(depnode, 'unknown')

    # can be iterated using the DagIterator, and it should at least be a dag node,
    # not unknown. The groundPlane is actually a special object that users shouldn't
    # touch directly
    dagTree.add_edge('transform', 'groundPlane')

    # although we don't handle manips directly, we must still support them if it
    # is a plugin manipulator
    dagTree.add_edge('transform', 'manipContainer')

    # INSERT PLUGIN TYPES
    ######################
    for edge in ((depnode, 'DependNode'), ('shape', 'Shape'), ('locator',
                                                               'LocatorNode'),
                 ('spring', 'SpringNode'), ('transform', 'TransformNode'),
                 ('manipContainer', 'ManipContainer'), ('dynBase',
                                                        'EmitterNode'),
                 ('field', 'FieldNode'), ('objectSet', 'ObjectSet'),
                 ('geometryFilter', 'DeformerNode'), (depnode, 'HwShaderNode'),
                 ('ikSolver', 'IkSolver'), (depnode, 'ImagePlaneNode'),
                 (depnode, 'ParticleAttributeMapperNode')):
        dagTree.add_edge(edge[0], 'unknownPlugin' + edge[1])
    # END for each plugin edge to add

    # BULD TYPE-TO-MFN MAP
    ######################
    # Prepare data to be put into a type separated file, it associates
    # a nodeType or nodeApiType with the respecitve MFnClass name
    typeToMFn = set()  # list((typeName, MFnClsName), ...)

    # add default associations - some are not picked up due to name mismatches
    typeToMFn.add((noderoottype, 'MFn'))
    typeToMFn.add((depnode, 'MFnDependencyNode'))
    typeToMFn.add(('dagContainer', 'MFnContainerNode'))

    abstractMFns = (
        'MFnBase', )  # MFns which cannot be instantiated ans should be ignored
    failedMFnTypes = list()  # list of types we could not yet associate

    modsapi = apiModules()
    for mfnApiType in mfnTypes:
        mfnNodePseudoType = uncapitalize(
            mfnApiType[1:])  # # kSomething -> something
        nodeType = apiTypeToNodeTypeMap.get(mfnApiType, mfnNodePseudoType)

        # MFnSets follow their kMFnType names, but when we try to associate it with
        # the actual nodeType . Sometimes, they follow the actual nodeType, so we
        # have to use this one as well
        found = False
        for nt, mfnNameCandidate in ((mfnNodePseudoType,
                                      "MFn%s" % capitalize(mfnApiType[1:])),
                                     (nodeType,
                                      "MFn%s" % capitalize(nodeType))):
            # ignore abstract ones
            if mfnNameCandidate in abstractMFns:
                continue

            for modapi in modsapi:
                if hasattr(modapi, mfnNameCandidate):
                    found = True

                    # prefer a real nodetype if we have one - it will default
                    # to the pseudotype
                    typeToMFn.add((nodeType, mfnNameCandidate))
                    break
                # END module with given name exists
            # END for each api module

            if found:
                break
        # END for each nodeType/mfnNamecandidate

        # still not found ? Keep it, but only if there is a nodetype
        # associated with it
        if not found and mfnApiType in apiTypeToNodeTypeMap:
            failedMFnTypes.append(mfnApiType)
        # END keep a record
    # END for each mfnType

    # DATA, COMPONENTS, ATTRIBUTES
    ###############################
    # get inheritance of Data, Component and Attribute types
    def unMFn(name):
        return uncapitalize(name[3:])

    # END remove MFn in front of MFnSomething strings

    for mfnsuffix in ("data", "component", "attribute"):
        mfnsuffixcap = capitalize(mfnsuffix)
        mfnnames = list()
        for modapi in modsapi:
            mfnnames.extend(n for n in dir(modapi) if n.endswith(mfnsuffixcap))
        # END for each api module to get information from

        dagTree.add_edge(root, mfnsuffix)

        mfnsuffix_root = [mfnsuffix]
        for mfnname in mfnnames:
            for modapi in modsapi:
                try:
                    mfncls = getattr(modapi, mfnname)
                except AttributeError:
                    continue
                # END handle multi-modules

                # skip classes which are just named like the super type, but
                # don't actually inherit it
                if "MFn%s" % mfnsuffixcap not in (p.__name__
                                                  for p in mfncls.mro()):
                    continue

                # add type->MFn association
                typeToMFn.add((unMFn(mfnname), mfnname))

                # cut object and MFnBase
                # from the names, cut the MFn and uncaptialize it: MFnData -> data
                pclsnames = [
                    unMFn(p.__name__) for p in list(reversed(mfncls.mro()))[2:]
                ]
                for parent, child in zip(pclsnames[:-1], pclsnames[1:]):
                    dagTree.add_edge(parent, child)
                # END for each mfn child to add

                break
            # END for each api module
        # END for each name
    # END for each mfnsuffix

    # HANDLE FAILED MFN-ASSOCITAIONS
    ################################
    # lets take some very special care !
    if failedMFnTypes:
        # Here we handle cases which don't follow any naming conventions really
        # Hence we have to instantiate an object of the failed type, then
        # we instantiate all the remaining functions sets to see which one fits.
        # If the required one has the requested type, we have a match.
        # If we have no type match, its still a valid MFn - If we haven't seen it
        # yet, its probably a base MFn whose kType string allows deduction of the
        # actual abtract node type which we will use instead.
        associatedMFns = (t[1] for t in typeToMFn)
        allMFnSetNames = list()
        for modapi in modsapi:
            allMFnSetNames.extend(
                n for n in dir(modapi)
                if n.startswith('MFn') and not n.endswith('Ptr')
                and not '_' in n and  # skip 'special' ones
                not 'Manip' in n)  # skip everything about Manipulators
        # END get all MFn names

        # find MFnClasses for each candidate name
        candidateMFnNames = (set(allMFnSetNames) -
                             set(associatedMFns)) - set(abstractMFns)
        candidateMFns = list()
        for cn in list(candidateMFnNames):
            for modapi in modsapi:
                try:
                    mfncls = getattr(modapi, cn)
                    # ignore them if they don't derive from MFnBase
                    if not hasattr(mfncls, "type"):
                        log.debug(
                            "Skipped MFn %s as it didn't derive from MFnBase" %
                            mfncls)
                        candidateMFnNames.discard(cn)
                        continue
                    # END skip mfn
                    candidateMFns.append(mfncls)
                    break
                except AttributeError:
                    continue
            # END for each api module
        # END for each candidate name

        succeededMFnNames = set()

        # PRUNE REMAINING MFNs
        # prune out all MFnClasses that can be constructed without an actual object
        enumMembers = MEnumDescriptor('Type')
        enumMembers.extend(m for m in dir(api.MFn) if m.startswith('k'))
        mfntypes = MEnumeration.create(enumMembers, api.MFn)

        for mfncls in candidateMFns[:]:
            try:
                mfninst = mfncls()
                if mfntypes.nameByValue(mfninst.type()) in failedMFnTypes:
                    continue
                # END keep actually missing MFns
                candidateMFns.remove(mfncls)
                candidateMFnNames.remove(mfncls.__name__)
            except RuntimeError:
                continue
        # END for each possible MFn to prune

        # at this point, we have about 500 api types with no MFn, but only
        # about 10 function sets,
        # Now ... we  brute-force test our way through all of these to find
        # matching ones ... argh
        derivedMatches = list(
        )  # keeps tuple(kTypeStr, mfnname) of matches of derived types
        perfectMatches = list()  # keeps mfnnames of perfect matches
        for failedApiTypeStr in failedMFnTypes:
            nodeType = apiTypeToNodeTypeMap[failedApiTypeStr]
            obj, mod = _createTmpNode(nodeType)

            removeThisMFn = None
            for mfncls in candidateMFns:
                try:
                    mfninst = mfncls(obj)
                except RuntimeError:
                    continue
                # END handle incompatability

                apiTypeStr = mfntypes.nameByValue(mfninst.type())

                if apiTypeStr not in failedMFnTypes:
                    removeThisMFn = mfncls
                    break
                # END remove MFns that no one wants

                if apiTypeStr == failedApiTypeStr:
                    mfnname = mfncls.__name__
                    typeToMFn.add((nodeType, mfnname))
                    perfectMatches.append(mfnname)
                    removeThisMFn = mfncls
                    break
                # END perfect match

                # its matching, but its not perfectly suited for our node type
                # We keep it, and will map it later if we don't find a better match
                derivedMatches.append((apiTypeStr, mfncls.__name__))
            # END for each mfncls

            if removeThisMFn is not None:
                succeededMFnNames.add(removeThisMFn.__name__)
                candidateMFns.remove(removeThisMFn)
            # END remove matched MFn

            if not candidateMFns:
                break
            # END abort search if there is nothing left
        # END for each failed type

        # HANDLE DERIVED MFns
        # prune out all derived mfs which have found a perfect match in the meanwhile
        # the rest will be added to the list
        for apiTypeStr, mfnname in filter(lambda t: t not in perfectMatches,
                                          derivedMatches):
            typeToMFn.add((apiTypeToNodeTypeMap[apiTypeStr], mfnname))
            succeededMFnNames.add(mfnname)
        # END for each apiTypeStr left ot add

        # LAST MANUAL WORK
        ##################
        # SubDees, if created empty, cannot be attached to their function set
        # Hence we don't see the match, but ... we know its there, so we add it
        # ourselves
        for nodeType, mfnname in (('subdiv', 'MFnSubd'), ):
            typeToMFn.add((nodeType, mfnname))
            succeededMFnNames.add(mfnname)
        # END for each manually added type

        for mfnname in candidateMFnNames - succeededMFnNames:
            log.warn("Could not associate MFn: %s" % mfnname)
        # END provide some info
    # END special treatment
    return (dagTree, sorted(typeToMFn, key=lambda t: t[0]))