1
2 """All kinds of utility methods and classes that are used in more than one modules """
3 __docformat__ = "restructuredtext"
4
5 import maya.mel as mm
6 import maya.cmds as cmds
7 import maya.OpenMaya as api
8 import mrv.util as util
9 from mrv.util import capitalize,uncapitalize
10 import networkx.exception as networkxexc
11
12 import traceback
13
14 import weakref
15
16 __all__ = ("noneToList", "isIterable", "pythonToMel", "makeEditOrQueryMethod",
17 "queryMethod", "editMethod", "propertyQE", "Mel", "OptionVarDict",
18 "optionvars", "StandinClass", "MetaClassCreator", "CallbackEventBase",
19 "MEnumeration", "notifyException")
23 """:return: list instead of None"""
24 if res is None:
25 return list()
26 return res
27
30
37
43 def wrapper(*args, **kwargs):
44 try:
45 return func(*args, **kwargs)
46 except Exception, e:
47
48 if reraise:
49 traceback.print_exc()
50
51 if api.MGlobal.mayaState() == api.MGlobal.kInteractive:
52 import mrv.maya.ui as ui
53 msg = str(e)
54 if reraise:
55 msg += "\n\nSee the script editor for details"
56 ui.ChoiceDialog( t=str(type(e)),
57 m=msg,
58 c=['Confirm'] ).choice()
59
60 if reraise:
61 raise
62
63 wrapper.__name__ = func.__name__
64 return wrapper
65
67 """Decorator which shows short exception information in a popup and a full
68 stack trace to stdout. Finally the exception will be reraised"""
69 return _logException(func, reraise=True)
70
72 """As logException, but does not reraise on error. This is useful if you just
73 want to see a popup, but not cause maya to remove the possibly problematic UI
74 callback. Additionally, no stacktrace will be shown"""
75 return _logException(func, reraise=False)
76
82 """Create a function calling inFunc with an edit or query flag set.
83
84 :note: THIS CODE HAS BEEN DUPLICATED TO `mrv.maya.ui.util` !
85 :param inCmd: maya command to call
86 :param flag: name of the query or edit flag
87 :param isEdit: If not False, the method returned will be an edit function
88 :param methodName: the name of the method returned, defaults to inCmd name """
89
90 func = None
91 if isEdit:
92 def editFunc(self, val, **kwargs):
93 kwargs[ 'edit' ] = True
94 kwargs[ flag ] = val
95 return inCmd( self, **kwargs )
96
97 func = editFunc
98
99 else:
100 def queryFunc(self, **kwargs):
101 kwargs[ 'query' ] = True
102 kwargs[ flag ] = True
103 return inCmd( self, **kwargs )
104
105 func = queryFunc
106
107
108 if not methodName:
109 methodName = flag
110 func.__name__ = methodName
111
112 return func
113
115 """ Shorthand query version of makeEditOrQueryMethod """
116 return makeEditOrQueryMethod( inCmd, flag, isEdit=False, methodName=methodName )
117
119 """ Shorthand edit version of makeEditOrQueryMethod """
120 return makeEditOrQueryMethod( inCmd, flag, isEdit=True, methodName=methodName )
121
123 """ Shorthand for simple query and edit properties """
124 editFunc = editMethod( inCmd, flag, methodName = methodName )
125 queryFunc = queryMethod( inCmd, flag, methodName = methodName )
126 return property( queryFunc, editFunc )
127
128
129
130
131
132
133 -class Mel(util.Singleton):
134 """This class is a necessity for calling mel scripts from python. It allows scripts to be called
135 in a cleaner fashion, by automatically formatting python arguments into a string
136 which is executed via maya.mel.eval().
137
138 :note: originated from pymel, added customizations """
139
141 """Only for instances of this class - call methods directly as if they where
142 attributes """
143 if command.startswith('__') and command.endswith('__'):
144 return self.__dict__[command]
145 def _call(*args):
146
147 strArgs = map( pythonToMel, args)
148
149 cmd = '%s(%s)' % ( command, ','.join( strArgs ) )
150 try:
151 return mm.eval(cmd)
152 except RuntimeError, msg:
153 info = self.whatIs( command )
154 if info.startswith( 'Presumed Mel procedure'):
155 raise NameError, 'Unknown Mel procedure'
156 raise RuntimeError, msg
157
158 return _call
159
160 @staticmethod
161 - def call( command, *args ):
162 """ Call a mel script , very simpilar to Mel.myscript( args )
163
164 :todo: more docs """
165 strArgs = map( pythonToMel, args)
166
167 cmd = '%s(%s)' % ( command, ','.join( strArgs ) )
168
169 try:
170 return mm.eval(cmd)
171 except RuntimeError, msg:
172 info = Mel.call( "whatIs", command )
173 if info.startswith( 'Presumed Mel procedure'):
174 raise NameError, ( 'Unknown Mel procedure: ' + cmd )
175 raise RuntimeError, msg
176
177 @staticmethod
179 """mel print command in case the python print command doesn't cut it. i have noticed that python print does not appear
180 in certain output, such as the rush render-queue manager."""
181 mm.eval( r"""print (%s);""" % pythonToMel( ' '.join( map( str, args))) + '\n' )
182
183 @staticmethod
184 - def eval( command ):
185 """ same as maya.mel eval """
186 return mm.eval( command )
187
188 @staticmethod
191
192 error = staticmethod( lambda *args: Mel._melprint( "error", *args ) )
193 trace = staticmethod( lambda *args: Mel._melprint( "trace", *args ) )
194 info = staticmethod( lambda *args: Mel._melprint( "print", *args ) )
195
198 """ A singleton dictionary-like class for accessing and modifying optionVars.
199
200 :note: Idea and base Implementation from PyMel, modified to adapt to mrv """
203 """modify constructor to work with tuple"""
204 newinstpreinit = tuple.__new__( cls, val )
205 newinstpreinit.key = key
206 return newinstpreinit
207
209 """ values appended to the OptionVarList with this method will be
210 added to the Maya optionVar at the key denoted by self.key. """
211 if isinstance( val, basestring):
212 return cmds.optionVar( stringValueAppend=[self.key,unicode(val)] )
213 if isinstance( val, (bool,int) ):
214 return cmds.optionVar( intValueAppend=[self.key,int(val)] )
215 if isinstance( val, float):
216 return cmds.optionVar( floatValueAppend=[self.key,val] )
217
218
221
223 if key not in self:
224 raise KeyError("OptionVar named %s did not exist" % key)
225
226
227 val = cmds.optionVar( q=key )
228 if isinstance(val, list):
229 val = self.OptionVarList( key, val )
230 return val
231
233 if isinstance( val, basestring):
234 return cmds.optionVar( stringValue=[key,unicode(val)] )
235 if isinstance( val, (int,bool)):
236 return cmds.optionVar( intValue=[key,int(val)] )
237 if isinstance( val, float):
238 return cmds.optionVar( floatValue=[key,val] )
239
240 if isinstance( val, (list,tuple) ):
241 if len(val) == 0:
242 return cmds.optionVar( clearArray=key )
243
244 if isinstance( val[0], basestring):
245 cmds.optionVar( stringValue=[key,unicode(val[0])] )
246 for elem in val[1:]:
247 cmds.optionVar( stringValueAppend=[key,unicode(elem)] )
248 return
249
250 if isinstance( val[0], (int,bool)):
251 cmds.optionVar( intValue=[key,int(val[0])] )
252 for elem in val[1:]:
253 cmds.optionVar( intValueAppend=[key,int(elem)] )
254 return
255
256 if isinstance( val[0], float):
257 cmds.optionVar( floatValue=[key,val[0]] )
258 for elem in val[1:]:
259 cmds.optionVar( floatValueAppend=[key,float(elem)] )
260 return
261
262 raise TypeError, 'unsupported datatype: strings, ints, float, lists, and their subclasses are supported'
263
265 """Delete the optionvar identified by key"""
266 cmds.optionVar( rm = str( key ) )
267
269 return cmds.optionVar( list=True )
270
272 """:return: iterator to option var names"""
273 return iter( self.keys() )
274
276 """:return: iterator to optionvar values"""
277 for key in self.iterkeys():
278 yield self[ key ]
279
281 """:return: iterators to tuple of key,value pairs"""
282 for key in self.iterkeys():
283 yield ( key, self[ key ] )
284
285 - def get(self, key, default=None):
290
293
294 - def pop(self, key):
295 val = self[ key ]
296 del( self[ key ] )
297 return val
298
299
300
301 optionvars = OptionVarDict()
309 """ Simple Function Object allowing to embed the name of the type as well as
310 the metaclass object supposed to create the actual class. It mus be able to completely
311 create the given class.
312
313 :note: Use it at placeholder for classes that are to be created on first call, without
314 vasting large amounts of memory if one wants to precreate them."""
315 __slots__ = ( "clsname", "classcreator", "_createdClass" )
316
318 self.clsname = classname
319 self.classcreator = classcreator
320 self._createdClass = None
321
323 """ Create the class of type self.clsname using our classcreator - can only be called once !
324
325 :return : the newly created class"""
326 if self._createdClass is None:
327 self._createdClass = self.classcreator( self.clsname, tuple(), {} )
328
329 return self._createdClass
330
334
390
392 """Allows the mapping of MMessage callbacks to mrv's event sender system.
393 This event will register a new message once the first event receiver registers
394 itself. Once the last event receiver deregisters, the message will be deregistered in
395 maya as well.
396
397 Derived types have to implement the `_getRegisterFunction`
398
399 :note: Its important that you care about deregistering your event to make sure the maya event can
400 be deregistered. Its worth knowing that the eventSender in question is strongly
401 bound to his callback event, so it cannot be deleted while the event is active."""
402
403
405 __slots__ = '_callbackID'
408
411
413 return self._callbackID
414
416 if self._callbackID:
417 api.MMessage.removeCallback(self._callbackID)
418
419 if hasattr(self._callbackID, 'disown'):
420 self._callbackID.disown()
421 self._callbackID = None
422
423
425 return self._callbackID
426
427
428 - def __init__( self, eventId, **kwargs ):
429 """Initialize our instance with the callbackID we are to represent."""
430 super( CallbackEventBase, self ).__init__( **kwargs )
431 self._eventID = eventId
432
433
435 """
436 :return: MMessage::register* compatible callback function which can be
437 used to register the given eventID"""
438 raise NotImplementedError("To be implemented in subclass")
439
440
441
442
450
452 """
453 :return: Callback storage function if it exists or None
454 :param create: if True, the storage will be created if needed, hence
455 you will always receive a valid storage"""
456 functions = self._getFunctionSet(inst)
457 storage_functions = [ cb for cb in functions if isinstance(cb, self.CBStorageFunction) ]
458 if not storage_functions:
459 if not create:
460 return None
461
462 sf = self.CBStorageFunction()
463 functions.add(sf)
464 return sf
465
466
467 assert len(storage_functions) == 1, "Expecting only one storage function, found %i" % len(storage_functions)
468 return storage_functions[0]
469
476
477
478
479
480 - def send( self, inst, *args, **kwargs ):
481 """Sets our instance prior to calling the super class
482
483 :note: must not be called manually"""
484
485 self._last_inst_ref = weakref.ref(inst)
486 super(CallbackEventBase, self).send(*args, **kwargs)
487
488 - def __set__( self, inst, eventfunc ):
501
515
519 """Simple enumeration class which allows access to its enumeration using
520 getattr access.
521 As it is a tuple as well, one can access the enumeration values in the right sequencial
522 order"""
527
530
532 return "MEnumeration(%s)" % self.name
533
534
535
537 """:return: name string with the given integer value
538 :param value: integer value of this enumeration
539 :raise ValueError: if value is not in the enumeration"""
540 for n,v in self.__dict__.items():
541 if not n.startswith('k') or not isinstance(v, int):
542 continue
543
544 if v == value:
545 return n
546
547
548 raise ValueError("Value %i not in enumeration" % value)
549
550
551
552 @classmethod
554 """
555 :return: new instance of this type as initialized from the EnumDescriptor ed and
556 the mfncls
557 """
558 emembers = list()
559
560
561 try:
562 for em in ed:
563 ev = getattr(mfncls, em)
564 emembers.append(ev)
565
566 except AttributeError:
567
568
569 pass
570
571
572 enum = cls(emembers, name=ed.name)
573
574
575 try:
576 for em in ed:
577 ev = getattr(mfncls, em)
578 setattr(enum, em, ev)
579
580 except AttributeError:
581 pass
582
583
584 return enum
585
586
587