mrv.maya.scene
Covered: 155 lines
Missed: 8 lines
Skipped 89 lines
Percent: 95 %
  2
"""
  3
Provides methodes to query and alter the currently loaded scene. It covers
  4
most of the functionality of the 'file' command, but has been renamed to scene
  5
as disambiguation to a filesystem file.
  6
"""
  7
__docformat__ = "restructuredtext"
  9
import util as mutil
 10
import mrv.util as util
 11
import maya.OpenMaya as api
 12
import maya.cmds as cmds
 13
from mrv.path import make_path
 15
import inspect
 17
__all__ = [ 'Scene' ]
 20
class _SceneEvent( mutil.CallbackEventBase ):
 21
	""" Implements Scene Callbacks"""
 23
	_checkCBSet = set( ( 	api.MSceneMessage.kBeforeNewCheck,
 24
							api.MSceneMessage.kBeforeSaveCheck ) )
 26
	_checkFileCBSet = set( ( 	api.MSceneMessage.kBeforeImportCheck,
 27
							  	api.MSceneMessage.kBeforeOpenCheck,
 28
								api.MSceneMessage.kBeforeExportCheck,
 29
								api.MSceneMessage.kBeforeReferenceCheck,
 30
								api.MSceneMessage.kBeforeLoadReferenceCheck  ) )
 33
	use_weakref = False
 34
	remove_on_error = True
 36
	weakref_sender = True
 40
	def _getRegisterFunction(self, eventID):
 41
		reg_method = api.MSceneMessage.addCallback
 42
		if eventID in self._checkCBSet:
 43
			reg_method = api.MSceneMessage.addCheckCallback
 44
		elif eventID in self._checkFileCBSet:
 45
			reg_method = api.MSceneMessage.addCheckFileCallback
 47
		return reg_method
 53
class Scene( util.Singleton, util.EventSender ):
 54
	"""Singleton Class allowing access to the maya scene
 56
	You can register all events available in MSceneMessage easily usnig the following 
 57
	syntax:
 59
		>>> scene.beforeSoftwareRender = myFunctionObject
 61
	"""
 64
	kFileTypeMap = { 	""	  : "mayaAscii",		# treat untitled scenes as ma
 65
						".ma" : "mayaAscii",
 66
						".mb" : "mayaBinary" }
 69
	sender_as_argument = False
 73
	for eidName, eid in ((n,v) for n,v in inspect.getmembers(api.MSceneMessage) if n.startswith('k')):
 74
		locals()[util.uncapitalize(eidName[1:])] = _SceneEvent(eid)
 82
	@classmethod
 83
	def open( cls, scenepath=None, force=False, **kwargs ):
 84
		""" Open the scene at the given scenepath
 86
		:param scenepath: The path to the file to be opened
 87
			If None, the currently loaded file will reopened
 88
		:param force: if True, the new scene will be loaded although currently
 89
			loaded contains unsaved changes
 90
		:param kwargs: passed to *cmds.file*
 91
		:return: a Path to the loaded scene"""
 92
		if not scenepath:
 93
			scenepath = cls.name()
 96
		sourcePath = make_path( scenepath )
 97
		kwargs.pop('open', kwargs.pop('o', None))
 98
		kwargs.pop('force', kwargs.pop('f', None))
 99
		lastReference = cmds.file( sourcePath.abspath(), open=1, force=force, **kwargs )
100
		return make_path( sourcePath )
102
	@classmethod
103
	def new( cls, force = False, **kwargs ):
104
		""" Create a new scene
106
		:param force: if True, the new scene will be created even though there
107
			are unsaved modifications
108
		:param kwargs: passed to *cmds.file*
109
		:return: Path with name of the new file"""
110
		kwargs.pop('new', kwargs.pop('n', None))
111
		kwargs.pop('force', kwargs.pop('f', None))
112
		return make_path( cmds.file( new = True, force = force, **kwargs ) )
114
	@classmethod
115
	def rename( cls, scenepath ):
116
		"""Rename the currently loaded file to be the file at scenepath
118
		:param scenepath: string or Path pointing describing the new location of the scene.
119
		:return: Path to scenepath
120
		:note: as opposed to the normal file -rename it will also adjust the extension
121
		:raise RuntimeError: if the scene's extension is not supported."""
122
		scenepath = make_path(scenepath)
123
		try:
124
			cmds.file( rename = scenepath.expandvars() )
125
			cmds.file( type = cls.kFileTypeMap[ scenepath.ext() ] )
126
		except KeyError:
127
			raise RuntimeError( "Unsupported filetype of: " + scenepath  )
130
		return scenepath
132
	@classmethod
133
	def save( cls, scenepath=None, autodeleteUnknown = False, **kwargs ):
134
		"""Save the currently opened scene under scenepath in the respective format
136
		:param scenepath: if None, the currently opened scene will be saved, otherwise 
137
			the name will be changed. Paths leading to the file will automatically be created.
138
		:param autodeleteUnknown: if true, unknown nodes will automatically be deleted
139
			before an attempt is made to change the maya file's type
140
		:param kwargs: passed to cmds.file
141
		:return: Path at which the scene has been saved."""
142
		if scenepath is None or scenepath == "":
143
			scenepath = cls.name( )
145
		scenepath = make_path( scenepath )
146
		curscene = cls.name()
147
		try :
148
			filetype = cls.kFileTypeMap[ scenepath.ext() ]
149
			curscenetype = cls.kFileTypeMap[ curscene.ext() ]
150
		except KeyError:
151
			raise RuntimeError( "Unsupported filetype of: " + scenepath  )
154
		if curscene != scenepath:
155
			cls.rename(scenepath)
158
		parentdir = scenepath.dirname( )
159
		if not parentdir.exists( ):
160
			parentdir.makedirs( )
164
		if autodeleteUnknown and curscenetype != filetype:
165
			cls.deleteUnknownNodes()
169
		kwargs.pop('save', kwargs.pop('s', None))
170
		kwargs.pop('type', kwargs.pop('typ', None))
171
		try:
172
			return make_path( cmds.file( save=True, type=filetype, **kwargs ) )
173
		except RuntimeError:
174
			if curscene != cls.name():
175
				cls.rename(curscene)
177
			raise
180
	@classmethod
181
	def export(cls, outputFile, nodeListOrIterable=None, **kwargs):
182
		"""Export the given nodes or everything into the file at path
184
		:param outputFile: Path object or path string to which the data should 
185
			be written to. Parent directories will be created as needed
186
		:param nodeListOrIterable: if None, everything will be exported. 
187
			Otherwise it may be an MSelectionList ( recommended ), or a list of
188
			Nodes, MObjects or MDagPaths
189
		:param kwargs: passed to cmds.file, see the mel docs for modifying flags
190
		:return: Path to which the data was exported"""
191
		outputFile = make_path(outputFile) 
192
		if not outputFile.dirname().isdir():
193
			outputFile.dirname().makedirs()
196
		prev_selection = None
197
		if nodeListOrIterable is None:
198
			kwargs['exportAll'] = True
199
		else:
201
			kwargs['exportSelected'] = True
202
			prev_selection = api.MSelectionList()
203
			api.MGlobal.getActiveSelectionList(prev_selection)
205
			import nt
206
			nt.select(nt.toSelectionList(nodeListOrIterable))
209
		typ = kwargs.pop('type', kwargs.pop('typ', cls.kFileTypeMap.get(outputFile.ext(), None)))
210
		if typ is None:
211
			raise RuntimeError("Invalid type in %s" % outputFile)
214
		try:
215
			cmds.file(outputFile, type=typ, **kwargs)
216
			return outputFile
217
		finally:
218
			if prev_selection is not None:
219
				api.MGlobal.setActiveSelectionList(prev_selection)
226
	@classmethod
227
	def deleteUnknownNodes( cls ):
228
		"""Deletes all unknown nodes in the scene
230
		:note: only do this if you are about to change the type of the scene during
231
			save or export - otherwise the operation would fail if there are still unknown nodes
232
			in the scene"""
233
		unknownNodes = cmds.ls( type="unknown" )		# using mel is the faatest here
234
		if unknownNodes:
235
			cmds.delete( unknownNodes )
240
	@classmethod
241
	def name( cls ):
242
		return make_path( cmds.file( q=1, exn=1 ) )
244
	@classmethod
245
	def isModified( cls ):
246
		return cmds.file( q=1, amf=True )