2
"""module containing layouts which combine finder ui modules"""
3
__docformat__ = "restructuredtext"
7
from mrv.path import Path
13
from util import ( FrameDecorator, handleUnsavedModifications )
15
import mrv.maya.ui as ui
17
from mrv.maya.util import (logException, notifyException)
19
from mrv.maya.ref import FileReference
20
import maya.utils as mutil
21
import maya.cmds as cmds
23
__all__ = ('FinderLayout', 'FileOpenFinder', 'FileReferenceFinder', 'FileSaveFinder')
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
30
**Instance Variables**
35
# used as names for buttons
37
k_cancel_name = "Cancel"
38
k_stack_item_remove_name = "Remove Item"
41
t_finder_provider = FileProvider
42
t_filepath = FilePathControl
44
t_bookmarks=BookmarkControl
45
t_root_selector=FileRootSelectorControl
47
t_filter=FileFilterControl
50
def __new__(cls, *args, **kwargs):
51
return super(FinderLayout, cls).__new__(cls)
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
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)
64
t, b, l, r = self.kSides
68
pane.p_staticWidthPane=1
69
except (RuntimeError, TypeError):
72
# END exception handling
76
if self.t_stack is not None:
77
finder_form = ui.FormLayout()
80
sdc = FrameDecorator("Stack", self.t_stack)
81
self.stack = sdc.layout
82
popup = ui.PopupMenu(markingMenu=True)
83
self._create_stack_menu(popup)
86
fdc = FrameDecorator("Finder", self.t_finder)
87
self.finder = fdc.layout
109
self.finder = self.t_finder()
113
finder_popup = ui.PopupMenu(markingMenu=True)
114
self._create_finder_menu(finder_popup)
117
if self.t_options is not None:
118
self.options = self.t_options()
122
# if we have a filter, set it to the finder
126
fp = self.t_filepath()
127
fp.setEditable(False)
129
self.finder.url_changed = fp.setPath
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"
137
config = "horizontal%i" % num_panes
138
lpane = ui.PaneLayout(configuration=config)
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
147
if kwargs.get('defaultRoots', False):
149
if not sys.platform.startswith('win'):
150
roots.append(Path("/"))
152
# just try all accessible drives
153
for letter in string.ascii_uppercase:
155
p = Path(letter + ":\\")
160
# END ignore exceptions
161
# END for each letter
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
169
self.rootselector.setSelectedItem(roots[0])
170
# END if we have a provider type
171
# END handle default roots
172
# END root selector setup
175
bdc = FrameDecorator("Bookmarks", self.t_bookmarks)
176
self.bookmarks = bdc.layout
177
self.bookmarks.bookmark_changed = self._on_bookmark_change
180
pmenu = ui.PopupMenu(markingMenu=True)
181
pmenu.e_postMenuCommand = self._build_bookmark_popup
182
# END bookmarks setup
183
# END left pane layout
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()
196
bl = self._create_button_layout()
233
#{ Subclass Interface
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
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
243
mi = ui.MenuItem(rp="SE", label=self.k_confirm_name)
244
mi.e_command = self._confirm_button_pressed
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
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()
258
okb = ui.Button(label=self.k_confirm_name)
259
okb.e_released = self._confirm_button_pressed
261
cnclb = ui.Button(label=self.k_cancel_name)
262
cnclb.e_released = self._cancel_button_pressed
266
t, b, l, r = self.kSides
288
#}END subclass interface
292
def _on_stack_remove_item(self, *args):
293
self.stack.removeItem(self.stack.selectedItem())
295
def _close_parent_window(self):
296
"""helper routine closing the parent window if there is one"""
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')
306
def _confirm_button_pressed(self, *args):
307
"""Called when the ok button was pressed to finalize the operation"""
308
self._close_parent_window()
310
def _cancel_button_pressed(self, *args):
311
"""Called when the cancel button was pressed, terminating the operation without
313
:note: if our parent is a window, we will close it through deletion"""
314
self._close_parent_window()
316
def _build_bookmark_popup(self, popup, *args):
317
popup.p_deleteAllItems = True
320
mi = ui.MenuItem(label="Add Bookmark")
321
mi.p_enable = self.finder.selectedUrl() is not None
323
mi.e_command = self._on_add_bookmark
326
mi = ui.MenuItem(label="Remove Bookmark")
327
mi.p_enable = len(self.bookmarks.selectedItems()) == 1
329
mi.e_command = self._on_remove_bookmark
333
def _on_add_bookmark(self, item, *args):
334
url = self.finder.selectedUrl()
335
provider = self.finder.provider()
337
if not hasattr(provider, 'root'):
338
raise TypeError("Provider doesn't support the 'root' method")
339
# END verify interface
341
self.bookmarks.addItem((provider.root(), url))
344
def _on_remove_bookmark(self, item, *args):
345
self.bookmarks.removeItem(self.bookmarks.selectedItems()[0])
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:
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))
361
actual_provider = None
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
368
# END handle provider match
369
# END for each provider
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
376
self.rootselector.setSelectedItem(root_item)
377
# END handle existance of rootselector
378
self.finder.setUrl(url, allow_memory=False)
383
class FileSaveFinder(FinderLayout):
384
"""Finder optimized to choose a location to save a file"""
386
k_confirm_name = "Save File"
389
t_filepath = FilePathControlEditable
393
def __init__(self, *args, **kwargs):
394
super(FileSaveFinder, self).__init__(*args, **kwargs)
395
self.bookmarks.k_bookmark_store = "MRV_SAVE_Bookmarks"
397
# filepath field is editable in this case
398
self.fpctrl.setEditable(True)
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)
405
super(FileSaveFinder, self)._confirm_button_pressed(*args, **kwargs)
408
class FileOpenFinder(FinderLayout):
409
"""Finder customized for opening files"""
412
k_confirm_name = "Open File"
415
t_options=FileOpenOptions
418
def __init__(self, *args, **kwargs):
419
super(FileOpenFinder, self).__init__(*args, **kwargs)
420
self.bookmarks.k_bookmark_store = "MRV_OPEN_Bookmarks"
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()
428
if not handleUnsavedModifications():
429
print "Cancelled by User"
431
# END handle modification
432
cmds.file(fileToOpen, **opts)
433
super(FileOpenFinder, self)._confirm_button_pressed()
436
class FileReferenceFinder(FinderLayout):
437
"""Finder Layout for creating references"""
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"
450
def __init__(self, *args, **kwargs):
451
super(FileReferenceFinder, self).__init__(*args, **kwargs)
452
self.bookmarks.k_bookmark_store = "MRV_REF_Bookmarks"
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
459
mi = ui.MenuItem(label=self.k_add_and_confirm, rp="NE")
460
mi.e_command = self._on_add_to_stack
463
#{ Subclass Interface
465
def _create_references(self, refpaths):
466
"""Create reference to the given reference paths, being strings to the file
469
FileReference.create(ref)
470
# END for each ref to create
472
#} END subclass interface
477
def _on_add_to_stack(self, menu_item, *args):
478
prov = self.finder.provider()
480
raise AssertionError("Finder is not setup")
482
# default is always add
483
url = self.finder.selectedUrl(absolute=True)
485
raise ValueError("Please select a path and retry")
486
# END handle nothing selected
488
self.stack.addItem(url)
490
if menu_item.p_label == self.k_add_and_confirm:
491
self._confirm_button_pressed()
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
501
self._create_references(self.stack.base_items)
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)