| Trees | Indices | Help |
|
|---|
|
|
1 # -*- coding: utf-8 -*-
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")
27
30
32 if isinstance(arg,basestring):
33 return u'"%s"' % cmds.encodeString(arg)
34 elif isIterable(arg):
35 return u'{%s}' % ','.join( map( pythonToMel, arg) )
36 return unicode(arg)
37 #} END utility functions
43 def wrapper(*args, **kwargs):
44 try:
45 return func(*args, **kwargs)
46 except Exception, e:
47 # print a full stack trace - for now not using the log
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 # END show popup
60 if reraise:
61 raise
62 # END wrapper
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
77 #} END decorator
78
79 #{ MEL Function Wrappers
80
81 -def makeEditOrQueryMethod( inCmd, flag, isEdit=False, methodName=None ):
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 # END if edit
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 # END if query
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
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
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
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 # END raise if k not in d
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])] ) # force to this datatype
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])] ) # force to this datatype
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]] ) # force to this datatype
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
267
270
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
290
293
298
299
300 # use it as singleton
301 optionvars = OptionVarDict()
302
303 #} END utility classes
304
305
306 #{ API Utilities Classes
307
308 -class StandinClass( object ):
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
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
337 """ Builds the base hierarchy for the given classname based on our
338 typetree """
339
340 - def __new__( dagtree, module, metacls, name, bases, clsdict,
341 nameToTreeFunc=uncapitalize, treeToNameFunc=capitalize ):
342 """Create a new class from hierarchy information found in dagtree and
343 put it into the module if it not yet exists
344
345 :param dagtree: `mrv.util.DAGTree` instance with hierarchy information
346 :param module: the module instance to which to add the new classes to
347 :param nameToTreeFunc: convert the class name to a name suitable for dagTree look-up
348 :param treeToNameFunc: convert a value from the dag tree into a valid class name ( used for parent lookup )"""
349 # recreate the hierarchy of classes leading to the current type
350 nameForTree = nameToTreeFunc( name )
351 parentname = None
352 try:
353 parentname = dagtree.parent( nameForTree )
354 except networkxexc.NetworkXError:
355 # we can handle and thus ignore key errors, mostly created by subclass
356 # of our wrapped classes
357 pass
358 # END parent name handling
359
360 parentcls = None
361
362 # ASSURE PARENTS
363 #####################
364 # Parent classes must be available in advance
365 if parentname is not None:
366 parentclsname = treeToNameFunc( parentname )
367 parentcls = module.__dict__[ parentclsname ]
368 if isinstance( parentcls, StandinClass ):
369 parentcls = parentcls.createCls( )
370 # END if parent cls name defined
371
372 # could be a user-defined class coming with some parents already - thus assure
373 # that the auto-parent is not already in there
374 # NOTE: bases is sometimes filled with types, sometimes with classes
375 if parentcls and not ( parentcls in bases or isinstance( parentcls, bases ) ):
376 bases += ( parentcls, )
377
378 # create the class
379 newcls = super( MetaClassCreator, metacls ).__new__( metacls, str( name ), bases, clsdict )
380
381 # change the module - otherwise it will get our module
382 newcls.__module__ = module.__name__
383
384 # replace the dummy class in the module
385 module.__dict__[ name ] = newcls
386
387
388
389 return newcls
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 #{ Utility Classes
405 __slots__ = '_callbackID'
408
410 self._callbackID = callbackID
411
414
416 if self._callbackID:
417 api.MMessage.removeCallback(self._callbackID)
418 # prevent memory leak message if possible
419 if hasattr(self._callbackID, 'disown'):
420 self._callbackID.disown()
421 self._callbackID = None
422 # END
423
426 #} END utility classes
427
429 """Initialize our instance with the callbackID we are to represent."""
430 super( CallbackEventBase, self ).__init__( **kwargs )
431 self._eventID = eventId
432
433 #{ Subclass Implementation Needed
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 #} END subclass implementation needed
441
442 #{ CallbackID handling
444 """Store the given callbackID in the event sender instance.
445 We do that by registering it as function for the given instance which
446
447 :return: the callback ID on call"""
448 storage = self._getCallbackIDStorage(inst, create=True)
449 storage.setCallbackID(callbackID)
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 # END handle storage does not exists
466
467 assert len(storage_functions) == 1, "Expecting only one storage function, found %i" % len(storage_functions)
468 return storage_functions[0]
469
471 """:return: stored callback ID or None"""
472 storage = self._getCallbackIDStorage(inst)
473 if storage is None:
474 return None
475 return storage.callbackID()
476
477 #} END handle callback ID
478
479
481 """Sets our instance prior to calling the super class
482
483 :note: must not be called manually"""
484 # fake the instance
485 self._last_inst_ref = weakref.ref(inst)
486 super(CallbackEventBase, self).send(*args, **kwargs)
487
489 eventset = self._getFunctionSet( inst )
490
491 # REGISTER MCALLBACK IF THIS IS THE FIRST EVENTRECEIVER
492 if not eventset:
493 reg_method = self._getRegisterFunction(self._eventID)
494
495 dyncall = lambda *args, **kwargs: self.send(inst, *args, **kwargs)
496 callbackID = reg_method(self._eventID, dyncall)
497 self._storeCallbackID(inst, callbackID)
498 # END create event
499
500 super( CallbackEventBase, self ).__set__( inst, eventfunc )
501
503 """Also removes our callback if the last receiver is gone"""
504 super(CallbackEventBase, self).remove(eventfunc)
505 inst = self._get_last_instance()
506 functions = self._getFunctionSet( inst )
507
508 # if there is only one item left, this should be our storage function
509 if len(functions) == 1:
510 cbstorage = iter(functions).next()
511 assert isinstance(cbstorage, self.CBStorageFunction)
512
513 cbstorage.removeCallback()
514 functions.remove(cbstorage)
515 # END dergister event
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
529 return self.name
530
532 return "MEnumeration(%s)" % self.name
533
534 #{ Interface
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 # END if value matches
547 # END for each item in our dict
548 raise ValueError("Value %i not in enumeration" % value)
549
550 #} END interface
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() # temporary
559
560 # get the values in the right sequence
561 try:
562 for em in ed:
563 ev = getattr(mfncls, em)
564 emembers.append(ev)
565 # END for each enumeration member
566 except AttributeError:
567 # happens in 2008+ as they have ifdeffed items that we pick up,
568 # but which are somewhat inoffical
569 pass
570 # END exception handling
571
572 enum = cls(emembers, name=ed.name)
573
574 # assign each member by name
575 try:
576 for em in ed:
577 ev = getattr(mfncls, em)
578 setattr(enum, em, ev)
579 # END for each enumeration member
580 except AttributeError:
581 pass
582 # END exception handlign
583
584 return enum
585
586 #} END api utility classes
587
| Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1 on Tue Apr 19 18:00:19 2011 | http://epydoc.sourceforge.net |