1
2 """
3 Contains some basic classes that are required to run the UI system
4 """
5 __docformat__ = "restructuredtext"
6 import maya.cmds as cmds
7 from mrv.util import capitalize
8 from mrv.maya.util import noneToList
9 from mrv.interface import iDagItem
10 from util import EventSenderUI
11 import util as uiutil
12 from mrv.exc import MRVError
13 import typ
14 _uidict = None
21 """
22 :return: uitype string having a corresponding mel command - some types returned do not correspond
23 to the actual name of the command used to manipulate the type """
24 uitype = cmds.objectTypeUI( uiname )
25 return typ._typemap.get( uitype, uitype )
26
27
28 -def wrapUI( uinameOrList, ignore_errors = False ):
29 """
30 :return: a new instance ( or list of instances ) of a suitable python UI wrapper class for the
31 UI with the given uiname(s)
32 :param uinameOrList: if single name, a single instance will be returned, if a list of names is given,
33 a list of respective instances. None will be interpreted as empty list
34 :param ignore_errors: ignore ui items that cannot be wrapped as the type is unknown.
35 :raise RuntimeError: if uiname does not exist or is not wrapped in python """
36 uinames = uinameOrList
37 islisttype = isinstance( uinameOrList, ( tuple, list, set ) )
38 if not islisttype:
39 if uinameOrList is None:
40 islisttype = True
41 uinames = list()
42 else:
43 uinames = [ uinameOrList ]
44
45 out = list()
46 for uiname in uinames:
47 uitype = getUIType( uiname )
48 clsname = capitalize( uitype )
49
50 try:
51 out.append( _uidict[clsname]( name=uiname, wrap_only = 1 ) )
52 except KeyError:
53 if not ignore_errors:
54 raise RuntimeError( "ui module has no class named %s, failed to wrap %s" % ( clsname, uiname ) )
55
56
57 if islisttype:
58 return out
59
60 return out[0]
61
62
63 UI = wrapUI
66 """:return: True if the user interface element with the given name exists"""
67 try:
68 wrapUI(uiname)
69 return True
70 except RuntimeError:
71 return False
72
73
74 -def lsUI( **kwargs ):
75 """ List UI elements as python wrapped types
76
77 :param kwargs: flags from the respective maya command are valid
78 If no special type keyword is specified, all item types will be returned
79 :return: list of NamedUI instances of respective UI elements """
80 long = kwargs.pop( 'long', kwargs.pop( 'l', True ) )
81 head = kwargs.pop( 'head', kwargs.pop( 'hd', None ) )
82 tail = kwargs.pop( 'tail', kwargs.pop( 'tl', None) )
83
84 if not kwargs:
85 kwargs = {
86 'windows': 1, 'panels' : 1, 'editors' : 1, 'controls' : 1,
87 'controlLayouts' : 1,'collection' : 1, 'radioMenuItemCollections' : 1,
88 'menus' : 1, 'menuItems' : 1, 'contexts' : 1, 'cmdTemplates' : 1 }
89
90
91 kwargs['long'] = long
92 if head is not None: kwargs['head'] = head
93 if tail is not None: kwargs['tail'] = tail
94
95
96
97
98
99 return wrapUI( set( cmds.lsUI( **kwargs ) ), ignore_errors = True )
100
101
102
103
104
105
106 -class BaseUI( object ):
115
116
117 -class NamedUI( unicode, BaseUI , iDagItem, EventSenderUI ):
118 """Implements a simple UI element having a name and most common methods one
119 can apply to it. Derived classes should override these if they can deliver a
120 faster implementation.
121 If the 'name' keyword is supplied, an existing UI element will be wrapped
122
123 **Events**
124
125 As subclass of EventSenderUI, it can provide events that are automatically
126 added by the metaclass as described by the _events_ attribute list.
127 This allows any number of clients to register for one maya event. Derived classes
128 may also use their own events which is useful if you create components
129
130 Register for an event like:
131
132 >>> uiinstance.e_eventlongname = yourFunction( sender, *args, **kwargs )
133 >>> *args and **kwargs are determined by maya
134
135 :note: although many access methods look quite 'repeated' as they are quite
136 similar except for a changing flag, they are hand-written to provide proper docs for them"""
137 __metaclass__ = typ.MetaClassCreatorUI
138
139
140 _sep = "|"
141 _is_menu = False
142
143
144
145 @classmethod
147 """
148 :return: 1 if the given UI element exists, 0 if it does not exist
149 and 2 it exists but the passed in name does not guarantee there are not more
150 objects with the same name"""
151 try:
152 uitype = cmds.objectTypeUI( uiname )
153 except RuntimeError:
154 return 0
155 else:
156
157
158
159
160 if "Window" not in uitype and cls._sep not in uiname:
161 return 2
162 return 1
163
164 - def __new__( cls, *args, **kwargs ):
165 """If name is given, the newly created UI will wrap the UI with the given name.
166 Otherwise the UIelement will be created
167
168 :param kwargs:
169
170 * name:
171 name of the user interface to wrap or the target name of a new elf element.
172 Valid names for creation are short names ( without a | in it's path ), valid names
173 for wrapping are short and preferably long names.
174
175 * wrap_only:
176 if True, default False, a wrap will be done even if the passed
177 in name uses the short form ( for non-window elements ). If it exists, one cannot be sure
178 whether more elements with the given name exist. If False, the system will create a new
179 element of our type.
180
181 * force_creation:
182 if True, default False, a new item will be created
183 even if an item with the given name uniquely exists. This might be necessary that
184 you wish to create the given named item under the current parent, although an item
185 with that name might already exist below another parent. This is required if
186 you have a short name only
187
188 :note: you can use args safely for your own purposes
189 :note: if name is set but does not name a valid user interface, a new one
190 will be created, and passed to the constructor"""
191 name = kwargs.pop( "name", None )
192 exists = (( name is not None) and NamedUI._exists(str(name))) or False
193 force_creation = kwargs.pop( "force_creation", False )
194
195
196
197 has_valid_name = True
198 if name and ('|' in name):
199 has_valid_name = False
200
201
202
203 if not kwargs.pop( "wrap_only", False ) and exists == 2:
204 exists = 0
205
206
207 created = False
208 if has_valid_name and (name is None or not exists or force_creation):
209 try:
210 if name:
211 name = cls.__melcmd__( name, **kwargs )
212
213
214
215 if cls._sep not in name and Window not in cls.mro():
216 raise AssertionError( "%s instance named '%s' does not have a long name after creation" % ( cls, name ) )
217 else:
218 name = cls.__melcmd__( **kwargs )
219
220 created = True
221 except (RuntimeError,TypeError), e:
222 raise RuntimeError( "Creation of %s using melcmd %s failed: %s" % ( cls, cls.__melcmd__, str( e ) ) )
223
224
225
226 inst = unicode.__new__( cls, name )
227
228
229
230
231
232 if created and cls.uiDeleted != NamedUI.uiDeleted:
233 cmds.scriptJob(uiDeleted=(name, inst.uiDeleted), runOnce=1)
234
235
236 return inst
237
239 return u"%s('%s')" % ( self.__class__.__name__, self )
240
242 """Prevent properties or events that do not exist to be used by anyone,
243 everything else is allowed though"""
244 if ( attr.startswith( "p_" ) or attr.startswith( "e_" ) ):
245 try:
246 getattr( self, attr )
247 except AttributeError:
248 raise AttributeError( "Cannot create per-instance properties or events: %s.%s ( did you misspell an existing one ? )" % ( self, attr ) )
249 except Exception:
250
251
252 pass
253
254
255 return super( NamedUI, self ).__setattr__( attr, value )
256
257 - def __init__( self , *args, **kwargs ):
258 """ Initialize instance and check arguments """
259
260 forbiddenKeys = [ 'edit', 'e' , 'query', 'q' ]
261 for fkey in forbiddenKeys:
262 if fkey in kwargs:
263 raise ValueError( "Edit or query flags are not permitted during initialization as interfaces must be created onclass instantiation" )
264
265
266
267 super( NamedUI, self ).__init__( *args, **kwargs )
268
269
271 """:return: all intermediate child instances
272 :note: the order of children is lexically ordered at this time
273 :note: this implementation is slow and should be overridden by more specialized subclasses"""
274 return filter( lambda x: len( x.replace( self , '' ).split('|') ) - 1 ==len( self.split( '|' ) ), self.childrenDeep() )
275
277 """:return: all child instances recursively
278 :note: the order of children is lexically ordered at this time
279 :note: this implementation is slow and should be overridden by more specialized subclasses"""
280 kwargs['long'] = True
281 return filter( lambda x: x.startswith(self) and not x == self, lsUI(**kwargs))
282
284 """:return: string of the parent, without a wrap
285 :note: this helps mainly as a workaround for a maya 2011 issues, causing
286 objectTypeUI not to work on many items"""
287 return '|'.join(self.split('|')[:-1])
288
290 """:return: parent instance of this ui element"""
291 return wrapUI(self._parentString())
292
293
294
295 @classmethod
297 """:return: NameUI of the currently set parent
298 :raise RuntimeError: if no active parent was set"""
299
300 wrapuiname = None
301 if cls._is_menu:
302 wrapuiname = cmds.setParent( q=1, m=1 )
303 else:
304
305 wrapuiname = cmds.setParent( q=1 )
306
307 if not wrapuiname or wrapuiname == "NONE":
308 raise RuntimeError( "No current parent set" )
309
310 return wrapUI( wrapuiname )
311
312
313
315 """If overridden in subclass, it will be called once the UI gets deleted
316 within maya ( i.e. the user closed the window )eee
317 The base implementation assures that all event-receivers that are bound to
318 your events will be freed, allowing them to possibly be destroyed as well.
319
320 Use this callback to register yourself from all your event senders, then call
321 the base class method.
322
323 :note: This is not related to the __del__ method of your object. Its worth
324 noting that your instance will be strongly bound to a maya event, hence
325 your instance will exist as long as your user interface element exists
326 within maya."""
327 self.clearAllEvents()
328
330 """:return: the python class able to create this class
331 :note: The return value is NOT the type string, but a class """
332 uitype = getUIType( self )
333 return getattr( ui, capitalize( uitype ) )
334
336 """:return: shortname of the ui ( name without pipes )"""
337 return self.split('|')[-1]
338
340 """Delete this UI - the wrapper instance must not be used after this call"""
341 cmds.deleteUI( self )
342
344 """:return: True if this instance still exists in maya"""
345 try:
346 return self.__melcmd__( self, ex=1 )
347 except RuntimeError:
348
349 return False
350
351
352 p_exists = property(exists)
353 p_ex = p_exists
354
358 """Base Class for all controls having a dimension"""
359 __metaclass__ = typ.MetaClassCreatorUI
360 _properties_ = ( "dt", "defineTemplate",
361 "ut", "useTemplate",
362 "w","width",
363 "h", "height",
364 "vis", "visible",
365 "m", "manage",
366 "en", "enable",
367 "io", "isObscured",
368 "npm", "numberOfPopupMenus",
369 "po", "preventOverride",
370 "bgc", "backgroundColor",
371 "dtg", "doctTag",
372
373 "fpn", "fullPathName",
374 "ebg", "enableBackground"
375 )
376
377 _events_ = ( "dgc", "dragCallback" ,
378 "dpc", "dropCallback" ,
379
380 "vcc", "visibleChangeCommand")
381
382
383
385 """:return : the annotation string """
386 try:
387 return self.__melcmd__( self, q=1, ann=1 )
388 except TypeError:
389 return ""
390
392 """:return: (x,y) tuple of x and y dimensions of the UI element"""
393 return ( self.__melcmd__( self, q=1, w=1 ), self.__melcmd__( self, q=1, h=1 ) )
394
396 """:return: popup menus attached to this control"""
397 return wrapUI( self.__melcmd__( self, q=1, pma=1 ) )
398
399
400
401
402
404 """Set the UI element's annotation
405 :note: not all named UI elements can have their annotation set"""
406 self.__melcmd__( self, e=1, ann=ann )
407
409 """Set the UI elements dimension
410 :param dimension: (x,y) : tuple holding desired x and y dimension"""
411 self.__melcmd__( self, e=1, w=dimension[0] )
412 self.__melcmd__( self, e=1, h=dimension[1] )
413
415 """Set the global keyboard focus to this control"""
416 cmds.setFocus(self)
417
418
419
420 p_annotation = property( annotation, setAnnotation )
421 p_ann = p_annotation
422 p_dimension = property( dimension, setDimension )
423 p_pma = property( popupMenuArray )
424 p_popupMenuArray = property( popupMenuArray )
425
426
427
428 -class Window( SizedControl, uiutil.UIContainerBase ):
429 """Simple Window Wrapper
430
431 :note: Window does not support some of the properties provided by sizedControl"""
432 __metaclass__ = typ.MetaClassCreatorUI
433 _properties_ = ( "t", "title",
434 "i", "iconify",
435 "s", "sizeable",
436 "wh", "widthHeight"
437 "in", "iconName",
438 "tb","titleBar",
439 "mnb", "minimizeButton",
440 "mxb", "maximizeButton",
441 "tlb", "toolbox",
442 "tbm", "titleBarMenu",
443 "mbv", "menuBarVisible",
444 "tlc", "topLeftCorner",
445 "te", "topEdge",
446 "tl", "leftEdge",
447 "mw", "mainWindow",
448 "rtf", "resizeToFitChildren",
449 "dt", "docTag" )
450
451 _events_ = ( "rc", "restoreCommand", "mnc", "minimizeCommand" )
452
453
454
455
457 """ Show Window
458 :return: self"""
459 cmds.showWindow( self )
460 return self
461
463 """:return: number of menus in the menu array"""
464 return int( self.__melcmd__( self, q=1, numberOfMenus=1 ) )
465
467 """:return: Menu instances attached to this window"""
468 return wrapUI( self.__melcmd__( self, q=1, menuArray=1 ) )
469
471 """:return: True if we are the front window """
472 return bool( self.__melcmd__( self, q=1, frontWindow=1 ) )
473
475 """Set the menu index of the specified menu
476
477 :param menu: name of child menu to set
478 :param index: new index at which the menu should appear"""
479 return self.__melcmd__( self, e=1, menuIndex=( menu, index ) )
480
481
482
483 p_numberOfMenus = property( numberOfMenus )
484 p_nm = p_numberOfMenus
485
488 """Common base for all menus"""
489
490
491 _is_menu = True
492
493
494 _properties_ = (
495 "en", "enable",
496 "l", "label",
497 "mn", "mnemonic",
498 "aob", "allowOptionBoxes",
499 "dt", "docTag"
500 )
501
502 _events_ = (
503 "pmc", "postMenuCommand",
504 "pmo", "postMenuCommandOnce"
505 )
506
509 """Implements the container abilities of all menu types"""
510
512 """Make ourselves the active menu
513
514 :return: self"""
515 cmds.setParent( self, m=1 )
516 return self
517
519 """Make our parent the active menu layout
520
521 :return: self
522 :note: only useful self is a submenu"""
523 cmds.setParent( ".." , m=1 )
524 return self
525
528 _properties_ = (
529 "hm", "helpMenu",
530 "ia", "itemArray",
531 "ni", "numberOfItems",
532 "dai", "deleteAllItems",
533 "fi", "familyImage"
534 )
535
537 """:return: An array of our menuItems
538 :note: This method worksaround a maya 2011 problem that makes it impossible
539 to properly wrap menuItems with short names as returned by p_itemArray"""
540 return [MenuItem(name="%s|%s" % (self, i)) for i in noneToList(self.p_itemArray)]
541
544 _properties_ = (
545 "alt", "altModifier",
546 "ctl", "ctrlModifier",
547 "sh", "shiftModifier",
548 "b", "button",
549 "aob", "allowOptionBoxes",
550 "mm", "markingMenu",
551 "ni", "numberOfItems",
552 "ia", "itemArray",
553 "dai", "deleteAllItems"
554 )
555
556 _events_ = (
557 "pmc", "postMenuCommand",
558 "pmo", "postMenuCommandOnce",
559 )
560
563
564 _properties_ = (
565 "d", "divider",
566 "cb", "checkBox",
567 "icb", "isCheckBox",
568 "rb", "radioButton",
569 "irb", "isRadioButton",
570 "iob", "isOptionBox",
571 "cl", "collection",
572 "i", "image",
573 "iol", "imageOverlayLabel",
574 "sm", "subMenu",
575 "ann", "annotation",
576 "da", "data",
577 "rp", "radialPosition",
578 "ke", "keyEquivalent",
579 "opt", "optionModifier",
580 "ctl", "controlModifier",
581 "sh", "shiftModifier",
582 "ecr", "enableCommandRepeat",
583 "ec", "echoCommand",
584 "it", "italicized",
585 "bld", "boldFont"
586 )
587
588 _events_ = (
589 "dmc", "dragMenuCommand",
590 "ddc", "dragDoubleClickCommand",
591 "c", "command"
592 )
593
595 """:return: Menu representing self if it is a submenu
596 :raise TypeError: if self i no submenu"""
597 if not self.p_sm:
598 raise TypeError( "%s is not a submenu and cannot be used as menu" )
599
600 return Menu(name=self)
601
604 """A menu which is always a submenu. This type greatly facilitates subclasses to
605 enforce being a MenuItem which is a submenu as no additional code is required"""
606
607
608
609 tearOff = False
610 allowOptionBoxes = False
611
612
619
620
621
622 CommandMenuItem = MenuItem
623