mrv.maya.ui.browse.layout
Covered: 103 lines
Missed: 231 lines
Skipped 175 lines
Percent: 30 %
  2
"""module containing layouts which combine finder ui modules"""
  3
__docformat__ = "restructuredtext"
  4
import sys
  5
import string
  7
from mrv.path import Path
  9
from control import *
 10
from finder import *
 11
from option import *
 13
from util import ( FrameDecorator, handleUnsavedModifications )
 15
import mrv.maya.ui as ui
 16
import mrv.maya
 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
 28
	through derivation
 30
	**Instance Variables**
 31
	* finder 
 32
	* options"""
 36
	k_confirm_name = "OK"
 37
	k_cancel_name = "Cancel"
 38
	k_stack_item_remove_name = "Remove Item"
 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
 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
 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)
 64
		t, b, l, r = self.kSides
 65
		m = 2
 67
		try:
 68
			pane.p_staticWidthPane=1
 69
		except (RuntimeError, TypeError):
 71
			pass
 75
		if pane:
 76
			if self.t_stack is not None:
 77
				finder_form = ui.FormLayout()
 78
				if finder_form:
 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
 88
					fi, st = fdc, sdc
 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
										)
107
				self.setActive()
108
			else:
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()
120
		self.setActive()
126
		fp = self.t_filepath()
127
		fp.setEditable(False)
128
		self.fpctrl = fp
129
		self.finder.url_changed = fp.setPath
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)
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
147
				if kwargs.get('defaultRoots', False):
148
					roots = list()
149
					if not sys.platform.startswith('win'):
150
						roots.append(Path("/"))
151
					else:
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
164
					if self.t_finder_provider is not None:
165
						for rootpath in roots:
166
							self.rootselector.addItem(self.t_finder_provider(rootpath))
168
						if roots:
169
							self.rootselector.setSelectedItem(roots[0])
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
180
				pmenu = ui.PopupMenu(markingMenu=True)
181
				pmenu.e_postMenuCommand = self._build_bookmark_popup
184
		self.setActive()
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()
196
		bl = self._create_button_layout()
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
								),
214
					attachNone=(
215
								(fp, t),
216
								(lpane, r),
217
								(fil, b),
218
								(bl, l),
219
								(bl, t),
220
								),
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
					)
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
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()
257
		if bform:
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
264
		self.setActive()
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
					)
286
		return bform
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"""
297
		p = self.parent()
298
		if isinstance(p, ui.Window):
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
312
		any changes.
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
318
		popup.setActive()
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
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
332
	@logException
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")
341
		self.bookmarks.addItem((provider.root(), url))
343
	@logException
344
	def _on_remove_bookmark(self, item, *args):
345
		self.bookmarks.removeItem(self.bookmarks.selectedItems()[0])
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
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
364
			for provider in self.rootselector.providers():
365
				if provider.root() == root:
366
					actual_provider = provider
367
					break
371
			if actual_provider is None:
372
				actual_provider = self.t_finder_provider(root)
373
				self.rootselector.addItem(actual_provider)
376
			self.rootselector.setSelectedItem(root_item)
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"
387
	t_stack = None
389
	t_filepath = FilePathControlEditable
390
	t_options=None
393
	def __init__(self, *args, **kwargs):
394
		super(FileSaveFinder, self).__init__(*args, **kwargs)
395
		self.bookmarks.k_bookmark_store = "MRV_SAVE_Bookmarks"
398
		self.fpctrl.setEditable(True)
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)
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"
413
	t_stack = None
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"
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
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"
446
	t_stack=FileStack
447
	t_options=None
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
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)
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")
483
		url = self.finder.selectedUrl(absolute=True)
484
		if url is None:
485
			raise ValueError("Please select a path and retry")
488
		self.stack.addItem(url)
490
		if menu_item.p_label == self.k_add_and_confirm:
491
			self._confirm_button_pressed()
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")
501
		self._create_references(self.stack.base_items)
505
		mutil.executeDeferred(super(FileReferenceFinder, self)._confirm_button_pressed)