Package mrv :: Package maya :: Package ui :: Package browse :: Module layout
[hide private]
[frames] | no frames]

Source Code for Module mrv.maya.ui.browse.layout

  1  # -*- coding: utf-8 -*- 
  2  """module containing layouts which combine finder ui modules""" 
  3  __docformat__ = "restructuredtext" 
  4  import sys 
  5  import string 
  6   
  7  from mrv.path import Path 
  8   
  9  from control import * 
 10  from finder import * 
 11  from option import * 
 12   
 13  from util import ( FrameDecorator, handleUnsavedModifications ) 
 14   
 15  import mrv.maya.ui as ui 
 16  import mrv.maya 
 17  from mrv.maya.util import (logException, notifyException) 
 18   
 19  from mrv.maya.ref import FileReference 
 20  import maya.utils as mutil 
 21  import maya.cmds as cmds 
 22   
 23  __all__ = ('FinderLayout', 'FileOpenFinder', 'FileReferenceFinder', 'FileSaveFinder') 
24 25 -class FinderLayout(ui.FormLayout):
26 """Implements a layout with a finder as well a surrounding elements. It can 27 be configured using class configuration variables, and allows easy modification 28 through derivation 29 30 **Instance Variables** 31 * finder 32 * options""" 33 34 #{ Configuration 35 # used as names for buttons 36 k_confirm_name = "OK" 37 k_cancel_name = "Cancel" 38 k_stack_item_remove_name = "Remove Item" 39 40 t_finder=Finder 41 t_finder_provider = FileProvider 42 t_filepath = FilePathControl 43 t_options=None 44 t_bookmarks=BookmarkControl 45 t_root_selector=FileRootSelectorControl 46 t_stack=None 47 t_filter=FileFilterControl 48 #} END configuration 49
50 - def __new__(cls, *args, **kwargs):
51 return super(FinderLayout, cls).__new__(cls)
52
53 - def __init__(self, *args, **kwargs):
54 """Initialize all ui elements 55 :param kwargs: The following keywords are defined 56 * **defaultRoots**: default False, if True, show all roots available 57 on the system.""" 58 num_splits = 1 + (self.t_options is not None) 59 config = (num_splits == 1 and "single") or "vertical%i" % num_splits 60 pane = ui.PaneLayout(configuration=config) 61 pane.p_paneSize=(1, 75, 100) 62 63 # attach the elements 64 t, b, l, r = self.kSides 65 m = 2 66 67 try: 68 pane.p_staticWidthPane=1 69 except (RuntimeError, TypeError): 70 # maya >= 2011 71 pass 72 # END exception handling 73 74 # populate main pane 75 if pane: 76 if self.t_stack is not None: 77 finder_form = ui.FormLayout() 78 if finder_form: 79 80 sdc = FrameDecorator("Stack", self.t_stack) 81 self.stack = sdc.layout 82 popup = ui.PopupMenu(markingMenu=True) 83 self._create_stack_menu(popup) 84 85 86 fdc = FrameDecorator("Finder", self.t_finder) 87 self.finder = fdc.layout 88 fi, st = fdc, sdc 89 90 finder_form.setup( 91 attachForm=( 92 (fi, t, m), 93 (fi, b, m), 94 (fi, l, m), 95 (st, t, m), 96 (st, b, m), 97 (st, r, m), 98 ), 99 attachNone=( 100 (st, l) 101 ), 102 attachControl=( 103 (fi, r, m, st) 104 ) 105 ) 106 # END finder form 107 self.setActive() 108 else: 109 self.finder = self.t_finder() 110 # END handle stack 111 112 # setup RMB menu 113 finder_popup = ui.PopupMenu(markingMenu=True) 114 self._create_finder_menu(finder_popup) 115 # END popupMenu 116 117 if self.t_options is not None: 118 self.options = self.t_options() 119 # END pane layout 120 self.setActive() 121 122 # if we have a filter, set it to the finder 123 124 # FILEPATH 125 ########## 126 fp = self.t_filepath() 127 fp.setEditable(False) 128 self.fpctrl = fp 129 self.finder.url_changed = fp.setPath 130 131 132 # BOOKMARKS AND SELECTOR 133 ######################## 134 num_panes = (self.t_bookmarks is not None) + (self.t_root_selector is not None) 135 assert num_panes, "Require at least one bookmark type or a selector type" 136 137 config = "horizontal%i" % num_panes 138 lpane = ui.PaneLayout(configuration=config) 139 140 if lpane: 141 self.rootselector, self.bookmarks = None, None 142 if self.t_root_selector: 143 rdc = FrameDecorator("Roots", self.t_root_selector) 144 self.rootselector = rdc.layout 145 self.rootselector.root_changed = self.finder.setProvider 146 147 if kwargs.get('defaultRoots', False): 148 roots = list() 149 if not sys.platform.startswith('win'): 150 roots.append(Path("/")) 151 else: 152 # just try all accessible drives 153 for letter in string.ascii_uppercase: 154 try: 155 p = Path(letter + ":\\") 156 p.stat() 157 roots.append(p) 158 except OSError: 159 continue 160 # END ignore exceptions 161 # END for each letter 162 # END handle roots 163 164 if self.t_finder_provider is not None: 165 for rootpath in roots: 166 self.rootselector.addItem(self.t_finder_provider(rootpath)) 167 # END for each rootpath 168 if roots: 169 self.rootselector.setSelectedItem(roots[0]) 170 # END if we have a provider type 171 # END handle default roots 172 # END root selector setup 173 lpane.setActive() 174 if self.t_bookmarks: 175 bdc = FrameDecorator("Bookmarks", self.t_bookmarks) 176 self.bookmarks = bdc.layout 177 self.bookmarks.bookmark_changed = self._on_bookmark_change 178 179 # BOOKMARK POPUP 180 pmenu = ui.PopupMenu(markingMenu=True) 181 pmenu.e_postMenuCommand = self._build_bookmark_popup 182 # END bookmarks setup 183 # END left pane layout 184 self.setActive() 185 186 # FILTER ELEMENT 187 ################ 188 assert self.t_filter is not None, "Require filter element, replace it by a dummy filter if it is not required" 189 self.filter = self.t_filter() 190 fil = self.filter 191 self.setActive() 192 193 194 # BUTTONS 195 ######### 196 bl = self._create_button_layout() 197 198 199 # CONTROL ASSEMBLY 200 ################## 201 self.setup( 202 attachForm=( 203 (fil, t, m), 204 (fil, l, m), 205 (fil, r, m), 206 (lpane, l, m), 207 (lpane, b, m), 208 (pane, r, m), 209 (fp, b, m), 210 (bl, r, m), 211 (bl, b, m), 212 ), 213 214 attachNone=( 215 (fp, t), 216 (lpane, r), 217 (fil, b), 218 (bl, l), 219 (bl, t), 220 ), 221 222 attachControl=( 223 (lpane, t, m, fil), 224 (pane, t, m, fil), 225 (pane, b, m, bl), 226 (pane, l, m, lpane), 227 (fp, l, m, lpane), 228 (fp, r, m, bl), 229 ), 230 )
231 # END setup 232 233 #{ Subclass Interface 234
235 - def _create_finder_menu(self, menu):
236 """Create a static menu for the finder. The active parent is a popupMenu 237 at the time of this call. The default buttons allow to quickly confirm 238 and cancel. 239 If a stack is present, items can be added to it""" 240 mi = ui.MenuItem(rp="SW", label=self.k_cancel_name) 241 mi.e_command = self._cancel_button_pressed 242 243 mi = ui.MenuItem(rp="SE", label=self.k_confirm_name) 244 mi.e_command = self._confirm_button_pressed
245 246
247 - def _create_stack_menu(self, menu):
248 mi = ui.MenuItem(label=self.k_stack_item_remove_name, rp="W") 249 mi.e_command = self._on_stack_remove_item
250
251 - def _create_button_layout(self):
252 """Create a layout with two main buttons, one to confirm, the other 253 to cancel the operation 254 :return: parent layout containing the buttons""" 255 bform = ui.FormLayout() 256 257 if bform: 258 okb = ui.Button(label=self.k_confirm_name) 259 okb.e_released = self._confirm_button_pressed 260 261 cnclb = ui.Button(label=self.k_cancel_name) 262 cnclb.e_released = self._cancel_button_pressed 263 # END create buttons 264 self.setActive() 265 266 t, b, l, r = self.kSides 267 m = 2 268 bform.setup( 269 attachForm=( 270 (okb, t, m), 271 (okb, b, m), 272 (okb, l, m), 273 (cnclb, t, m), 274 (cnclb, b, m), 275 (cnclb, r, m), 276 ), 277 attachNone=( 278 (okb, r), 279 ), 280 attachControl=( 281 (cnclb, l, m, okb), 282 ) 283 ) 284 # END setup 285 286 return bform
287 288 #}END subclass interface 289 290 #{ Callbacks 291
292 - def _on_stack_remove_item(self, *args):
293 self.stack.removeItem(self.stack.selectedItem())
294
295 - def _close_parent_window(self):
296 """helper routine closing the parent window if there is one""" 297 p = self.parent() 298 if isinstance(p, ui.Window): 299 # If its not deferred, it will crash maya for some reason, maybe 300 # something related to garbage collection. 301 mutil.executeDeferred(self.parent().delete) 302 elif isinstance(p, ui.FormLayout) and p.startswith('layoutDialog'): 303 cmds.layoutDialog(dismiss='close')
304 # END close window 305
306 - def _confirm_button_pressed(self, *args):
307 """Called when the ok button was pressed to finalize the operation""" 308 self._close_parent_window()
309
310 - def _cancel_button_pressed(self, *args):
311 """Called when the cancel button was pressed, terminating the operation without 312 any changes. 313 :note: if our parent is a window, we will close it through deletion""" 314 self._close_parent_window()
315
316 - def _build_bookmark_popup(self, popup, *args):
317 popup.p_deleteAllItems = True 318 popup.setActive() 319 320 mi = ui.MenuItem(label="Add Bookmark") 321 mi.p_enable = self.finder.selectedUrl() is not None 322 if mi.p_enable: 323 mi.e_command = self._on_add_bookmark 324 # END setup command 325 326 mi = ui.MenuItem(label="Remove Bookmark") 327 mi.p_enable = len(self.bookmarks.selectedItems()) == 1 328 if mi.p_enable: 329 mi.e_command = self._on_remove_bookmark
330 # END setup command 331 332 @logException
333 - def _on_add_bookmark(self, item, *args):
334 url = self.finder.selectedUrl() 335 provider = self.finder.provider() 336 337 if not hasattr(provider, 'root'): 338 raise TypeError("Provider doesn't support the 'root' method") 339 # END verify interface 340 341 self.bookmarks.addItem((provider.root(), url))
342 343 @logException
344 - def _on_remove_bookmark(self, item, *args):
345 self.bookmarks.removeItem(self.bookmarks.selectedItems()[0])
346 347 @logException
348 - def _on_bookmark_change(self, root, url):
349 """Propagate changed bookmarks to changed roots. If necessary, add a new 350 root to the root selector. Otherwise just set the root and url of the finder""" 351 cur_provider = self.finder.provider() 352 if cur_provider and root == cur_provider.root() and self.finder.selectedUrl() == url: 353 return 354 # END early bailout 355 356 if self.rootselector is None: 357 ptype = type(cur_provider) 358 assert ptype is not type(None), "Finder needs provider to be set beforehand" 359 self.finder.setProvider(ptype(root)) 360 else: 361 actual_provider = None 362 root_item = root 363 # find a provider matching the root - if not, add it 364 for provider in self.rootselector.providers(): 365 if provider.root() == root: 366 actual_provider = provider 367 break 368 # END handle provider match 369 # END for each provider 370 371 if actual_provider is None: 372 actual_provider = self.t_finder_provider(root) 373 self.rootselector.addItem(actual_provider) 374 # END add a new provider to root selector 375 376 self.rootselector.setSelectedItem(root_item) 377 # END handle existance of rootselector 378 self.finder.setUrl(url, allow_memory=False)
379
380 #} END callbacks 381 382 383 -class FileSaveFinder(FinderLayout):
384 """Finder optimized to choose a location to save a file""" 385 #{ Configuration 386 k_confirm_name = "Save File" 387 t_stack = None 388 389 t_filepath = FilePathControlEditable 390 t_options=None 391 #} END configuration 392
393 - def __init__(self, *args, **kwargs):
394 super(FileSaveFinder, self).__init__(*args, **kwargs) 395 self.bookmarks.k_bookmark_store = "MRV_SAVE_Bookmarks" 396 397 # filepath field is editable in this case 398 self.fpctrl.setEditable(True)
399 400 @notifyException
401 - def _confirm_button_pressed(self, *args, **kwargs):
402 save_path = self.finder.provider().root() / self.fpctrl.path() 403 mrv.maya.Scene.save(save_path) 404 405 super(FileSaveFinder, self)._confirm_button_pressed(*args, **kwargs)
406
407 408 -class FileOpenFinder(FinderLayout):
409 """Finder customized for opening files""" 410 411 #{ Configuration 412 k_confirm_name = "Open File" 413 t_stack = None 414 415 t_options=FileOpenOptions 416 #} END configuration 417
418 - def __init__(self, *args, **kwargs):
419 super(FileOpenFinder, self).__init__(*args, **kwargs) 420 self.bookmarks.k_bookmark_store = "MRV_OPEN_Bookmarks"
421 422 @notifyException
423 - def _confirm_button_pressed(self, *args):
424 """Called when the ok button was pressed to finalize the operation""" 425 fileToOpen = self.finder.selectedUrl(absolute=True) 426 opts = self.options.fileOptions() 427 opts['open'] = True 428 if not handleUnsavedModifications(): 429 print "Cancelled by User" 430 return 431 # END handle modification 432 cmds.file(fileToOpen, **opts) 433 super(FileOpenFinder, self)._confirm_button_pressed()
434
435 436 -class FileReferenceFinder(FinderLayout):
437 """Finder Layout for creating references""" 438 439 #{ Configuration 440 441 k_add_single = "Add to Stack" 442 k_add_and_confirm = "Add to Stack and Confirm" 443 k_confirm_name = "Create Reference(s)" 444 k_stack_item_remove_name = "Remove File from Stack" 445 446 t_stack=FileStack 447 t_options=None 448 #} END configuration 449
450 - def __init__(self, *args, **kwargs):
451 super(FileReferenceFinder, self).__init__(*args, **kwargs) 452 self.bookmarks.k_bookmark_store = "MRV_REF_Bookmarks"
453
454 - def _create_finder_menu(self, menu):
455 super(FileReferenceFinder, self)._create_finder_menu(menu) 456 mi = ui.MenuItem(label=self.k_add_single, rp="E") 457 mi.e_command = self._on_add_to_stack 458 459 mi = ui.MenuItem(label=self.k_add_and_confirm, rp="NE") 460 mi.e_command = self._on_add_to_stack
461 462 463 #{ Subclass Interface 464
465 - def _create_references(self, refpaths):
466 """Create reference to the given reference paths, being strings to the file 467 in question""" 468 for ref in refpaths: 469 FileReference.create(ref)
470 # END for each ref to create 471 472 #} END subclass interface 473 474 #{ Callbacks 475 476 @notifyException
477 - def _on_add_to_stack(self, menu_item, *args):
478 prov = self.finder.provider() 479 if prov is None: 480 raise AssertionError("Finder is not setup") 481 482 # default is always add 483 url = self.finder.selectedUrl(absolute=True) 484 if url is None: 485 raise ValueError("Please select a path and retry") 486 # END handle nothing selected 487 488 self.stack.addItem(url) 489 490 if menu_item.p_label == self.k_add_and_confirm: 491 self._confirm_button_pressed()
492 # END handle confirm 493 494 @notifyException
495 - def _confirm_button_pressed(self, *args):
496 if not self.stack.base_items: 497 raise ValueError("Please add at least one item to the stack and retry") 498 # END handle empty refs 499 500 # create refs 501 self._create_references(self.stack.base_items) 502 503 # finally let base take care of the rest 504 # NOTE: Needs to be deferred, crashes otherwis 505 mutil.executeDeferred(super(FileReferenceFinder, self)._confirm_button_pressed)
506 507 508 #} END callbacks 509