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

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

  1  # -*- coding: utf-8 -*- 
  2  __docformat__ = "restructuredtext" 
  3  import mrv.maya.ui as ui 
  4  from mrv.maya.util import (logException,) 
  5   
  6  from control import FinderElement 
  7   
  8  from util import concat_url 
  9   
 10  __all__ = ('Finder', ) 
11 12 -class Finder(ui.EventSenderUI):
13 """The Finder control implements a finder-like browser, which displays URLs. 14 URLs consist of items separated by the "/" character. Whenever a item is selected, 15 an iProvider compatible instance will be asked for the subitems of the corresponding URL. 16 Using these, a new field will be set up for presentation. 17 A filter can be installed to prevent items from being shown. 18 19 An added benefit is the ability to automatically match previously selected path 20 items on a certain level of the URL with the available ones, allowing to quickly 21 parse through URLs with a similar structure. 22 23 A limitation of the current implementation is, that you can only keep one 24 item selected at once in each url item area.""" 25 26 #{ Configuration 27 t_element = FinderElement 28 #} END configuration 29 30 #{ Signals 31 32 # s() 33 selection_changed = ui.Signal() 34 35 # s(url) 36 url_changed = ui.Signal() 37 38 #} END signals 39
40 - def __init__(self, provider=None, filter=None):
41 self._provider = None 42 self._filter = None 43 44 # initialize layouts 45 self._form = ui.FormLayout() 46 self._form.setParentActive() 47 48 self.setProvider(provider) 49 self.setFilter(filter)
50 51 # { Query 52
53 - def layout(self):
54 """:return: the finder's main layout which contains all controls""" 55 return self._form
56
57 - def provider(self):
58 """:return: current url provider""" 59 return self._provider
60
61 - def selectedUrl(self, absolute=False):
62 """:return: string representing the currently selected, / separated URL, or 63 None if there is no url selected 64 :param absolute: if True, an absolute URL will be provided using the provider's 65 root""" 66 items = list() 67 for elm in self._form.listChildren(): 68 if not elm.p_manage: 69 break 70 sel_item = elm.selectedUnformattedItem() 71 if sel_item is not None: 72 items.append(sel_item) 73 else: 74 break 75 # END for each element 76 77 url = "/".join(items) or None 78 if absolute and url is not None: 79 url = concat_url(self.provider().root(), url) 80 # END handle absolute urls 81 return url
82
83 - def numUrlElements(self):
84 """:return: number of url elements that are currently shown. A url of 1/2 would 85 have two url elements""" 86 return len(tuple(c for c in self._form.listChildren() if c.p_manage))
87
88 - def selectedUrlItemByIndex(self, index):
89 """:return: The selected url item at the given element index or None if nothing 90 is selected 91 :param index: 0 to numUrlElements()-1 92 :raies IndexError:""" 93 return self._form.listChildren()[index].selectedUnformattedItem()
94
95 - def urlItemsByIndex(self, index):
96 """:return: list of item ids which are currently being shown 97 :param index: 0 based element index to numUrlElements()-1 98 :raise IndexError:""" 99 return list(self._form.listChildren()[index].base_items)
100 101 102 #} END Query 103 104 #{ Edit 105
106 - def setFilter(self, filter=None):
107 """Set or unset a filter. All items will be sent through the filter, and will 108 be shown only if they pass. 109 :param filter: Functor called f(url,t) and returns True for each item which may 110 be shown in the Finder. The url is the full relative url leading to, but 111 excluding the item t, whose visibility is being decided upon""" 112 self._filter = filter
113
114 - def setProvider(self, provider=None):
115 """Set the provider to use 116 :param provider: ``iFinderProvider`` compatible instance, or None 117 If no provider is set, the instance will be blank""" 118 if self._provider is provider: 119 return 120 # END early bailout 121 self._provider = provider 122 123 if provider is not None: 124 self._set_element_visible(0) 125 # END handle initial setup 126 127 self.selection_changed.send() 128 self.url_changed.send(self.selectedUrl())
129
130 - def _set_item_by_index(self, elm, index, item):
135
136 - def setItemByIndex(self, item, index):
137 """Set the given string item, which sits at the given index of a url 138 :raise ValueError: if item does not exist at given index 139 :raise IndexError: if index is not currently shown""" 140 assert self.provider() is not None, "Provider is not set" 141 elm = self._form.listChildren()[index] 142 if elm.selectedUnformattedItem() == item: 143 return 144 # END early abort if nothing changes 145 self._set_item_by_index(elm, index, item) 146 147 self.selection_changed.send() 148 self.url_changed.send(self.selectedUrl())
149
150 - def setUrl(self, url, require_all_items=True, allow_memory=False):
151 """Set the given url to be selected 152 :param url: / separated relative url. The individual items must be available 153 in the provider. 154 :parm require_all_items: if False, the control will display as many items as possible. 155 Otherwise it must display all given items, or raise ValueError 156 :param allow_memory: if true, provider memory may be used to show the longest chosen url, 157 being possibly more than you specify. Currently not implemented""" 158 assert self.provider() is not None, "Provider is not set" 159 cur_url = self.selectedUrl() 160 if cur_url == url: 161 return 162 # END ignore similar urls 163 164 for eid, item in enumerate(url.split("/")): 165 elm = self._form.listChildren()[eid] 166 if elm.selectedUnformattedItem() == item: 167 continue 168 # END skip items which already match 169 try: 170 self._set_item_by_index(elm, eid, item) 171 except ValueError: 172 if not require_all_items: 173 break 174 # restore previous url 175 self.setUrl(cur_url) 176 raise 177 # END handle exceptions 178 # END for each item to set 179 180 self.selection_changed.send() 181 self.url_changed.send(self.selectedUrl())
182 183 184 185 #} END edit 186 187 #{ Callbacks 188 189 @logException
190 - def _element_selection_changed(self, element, *args):
191 """Called whenever any element changes its value, which forces the following 192 elements to refresh""" 193 index = self._index_by_item_element(element) 194 # store the currently selected item 195 self.provider().storeUrlItem(index, element.selectedUnformattedItem()) 196 self._set_element_visible(index+1) 197 198 self.selection_changed.send() 199 self.url_changed.send(self.selectedUrl())
200 201 #} END callbacks 202 203 #{ Utilities 204
205 - def _index_by_item_element(self, element):
206 """:return: index matching the given item element, which must be one of our children""" 207 assert '|' in element 208 for cid, c in enumerate(self._form.listChildren()): 209 if c == element: 210 return cid 211 # END for each child to enumerate 212 raise ValueError("Didn't find element: %s" % element)
213
214 - def _set_element_items(self, start_elm_id, elements ):
215 """Fill the items from the start_elm_id throughout to all elements, until 216 one url does not yield any items, or the item cannot be selected 217 :param elements: a full list of all available child elements.""" 218 219 # obtain the root url 220 root_url = "/".join(c.selectedUnformattedItem() for c in elements[:start_elm_id]) 221 222 manage = True 223 for elm_id in range(start_elm_id, len(elements)): 224 225 # refill the items according to our provider 226 elm = elements[elm_id] 227 elm.p_manage=manage 228 229 if not manage: 230 continue 231 # END abort if we just disable all others 232 233 items = self.provider().urlItems(root_url) 234 elm.base_items = items 235 if not items: 236 # keep one item visible, even though empty, if its the only one 237 if len(elements) > 1: 238 elm.p_manage=False 239 manage=False 240 continue 241 # END skip on first empty url 242 243 if elm.p_numberOfItems: 244 elm.p_removeAll = True 245 # END remove prior to re-append 246 247 for item in items: 248 elm.p_append = self.provider().formatItem(root_url, elm_id, item) 249 # END for each item to append 250 251 # try to reselect the previously selected item 252 sel_item = self.provider().storedUrlItemByIndex(elm_id) 253 if sel_item is None: 254 # make sure next item is not being shown 255 manage=False 256 continue 257 # END handle item memorization 258 259 try: 260 elm.selectUnformattedItem(sel_item) 261 except (RuntimeError, ValueError): 262 manage=False 263 continue 264 # END handle exception 265 266 # update the root 267 if root_url: 268 root_url += "/" 269 # END assure / is not the first character 270 root_url += sel_item
271 # END for each url to handle 272 273
274 - def _set_element_visible(self, index):
275 """Possibly create and fill the given element index, all following elements 276 are set invivisble""" 277 children = self._form.listChildren() 278 279 # create as many new scrollLists as required, 280 elms_to_create = max(0, (index+1) - len(children)) 281 if elms_to_create: 282 self._form.setActive() 283 for i in range(elms_to_create): 284 # make sure we keep our array uptodate 285 child = self._form.add(self.t_element(allowMultiSelection=False, font="smallFixedWidthFont")) 286 children.append(child) 287 288 child.e_selectCommand = self._element_selection_changed 289 290 t, b, l, r = self._form.kSides 291 m = 2 292 293 # they are always attached top+bottom 294 self._form.setup( attachForm=((child, t, m), (child, b, m)), 295 attachNone=(child, r) ) 296 297 # we generally keep the right side un-attached 298 if len(children) == 1: 299 # first element goes left 300 self._form.setup(attachForm=(child, l, m)) 301 else: 302 # all other elements attach to the right side 303 self._form.setup(attachControl=(child, l, m, children[-2])) 304 # END handle amount of children 305 # children.append(child) 306 # END for each element to add 307 # END if elms to create 308 309 self._set_element_items(index, children)
310 311 #} END utilities 312