Package node_manager_fkie :: Module launch_list_model
[frames] | no frames]

Source Code for Module node_manager_fkie.launch_list_model

  1  # Software License Agreement (BSD License) 
  2  # 
  3  # Copyright (c) 2012, Fraunhofer FKIE/US, Alexander Tiderko 
  4  # All rights reserved. 
  5  # 
  6  # Redistribution and use in source and binary forms, with or without 
  7  # modification, are permitted provided that the following conditions 
  8  # are met: 
  9  # 
 10  #  * Redistributions of source code must retain the above copyright 
 11  #    notice, this list of conditions and the following disclaimer. 
 12  #  * Redistributions in binary form must reproduce the above 
 13  #    copyright notice, this list of conditions and the following 
 14  #    disclaimer in the documentation and/or other materials provided 
 15  #    with the distribution. 
 16  #  * Neither the name of Fraunhofer nor the names of its 
 17  #    contributors may be used to endorse or promote products derived 
 18  #    from this software without specific prior written permission. 
 19  # 
 20  # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
 21  # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
 22  # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 
 23  # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 
 24  # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 
 25  # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
 26  # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
 27  # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
 28  # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
 29  # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 
 30  # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
 31  # POSSIBILITY OF SUCH DAMAGE. 
 32   
 33  from python_qt_binding.QtCore import QMimeData, Qt 
 34  try: 
 35      from python_qt_binding.QtGui import QApplication, QInputDialog, QLineEdit 
 36  except: 
 37      from python_qt_binding.QtWidgets import QApplication, QInputDialog, QLineEdit 
 38  from python_qt_binding.QtGui import QIcon, QStandardItem, QStandardItemModel 
 39  import os 
 40  import shutil 
 41   
 42  import node_manager_fkie as nm 
 43   
 44  from .common import is_package, package_name, utf8 
 45  from .detailed_msg_box import MessageBox 
 46  from .packages_thread import PackagesThread 
47 48 49 -class LaunchItem(QStandardItem):
50 ''' 51 The launch item stored in the launch model. 52 ''' 53 54 ITEM_TYPE = QStandardItem.UserType + 40 55 56 NOT_FOUND = -1 57 NOTHING = 0 58 PROFILE = 5 59 RECENT_PROFILE = 6 60 RECENT_FILE = 10 61 LAUNCH_FILE = 11 62 CFG_FILE = 12 63 FOLDER = 20 64 PACKAGE = 21 65 STACK = 22 66
67 - def __init__(self, name, path, lid, parent=None):
68 ''' 69 Initialize the topic item. 70 @param name: the topic name 71 @type name: C{str} 72 ''' 73 QStandardItem.__init__(self, name) 74 self.parent_item = parent 75 self.name = name 76 self.path = path 77 self.package_name = package_name(os.path.dirname(self.path))[0] 78 self.id = lid 79 if self.id == LaunchItem.FOLDER: 80 self.setIcon(QIcon(":/icons/crystal_clear_folder.png")) 81 elif self.id == LaunchItem.PACKAGE: 82 self.setIcon(QIcon(":/icons/crystal_clear_package.png")) 83 elif self.id == LaunchItem.LAUNCH_FILE: 84 self.setIcon(QIcon(":/icons/crystal_clear_launch_file.png")) 85 elif self.id == LaunchItem.RECENT_FILE: 86 self.setIcon(QIcon(":/icons/crystal_clear_launch_file_recent.png")) 87 elif self.id == LaunchItem.STACK: 88 self.setIcon(QIcon(":/icons/crystal_clear_stack.png")) 89 elif self.id == LaunchItem.PROFILE: 90 self.setIcon(QIcon(":/icons/crystal_clear_profile.png")) 91 elif self.id == LaunchItem.RECENT_PROFILE: 92 self.setIcon(QIcon(":/icons/crystal_clear_profile_recent.png"))
93 94 # def __del__(self): 95 # print "delete LAUNCH", self.name 96
97 - def type(self):
98 return LaunchItem.ITEM_TYPE
99
100 - def data(self, role):
101 ''' 102 The view asks us for all sorts of information about our data... 103 @param role: the art of the data 104 @type role: U{QtCore.Qt.DisplayRole<https://srinikom.github.io/pyside-docs/PySide/QtCore/Qt.html>} 105 @see: U{http://www.pyside.org/docs/pyside-1.0.1/PySide/QtCore/Qt.html} 106 ''' 107 if role == Qt.DisplayRole: 108 # return the displayed item name 109 if self.id == LaunchItem.RECENT_FILE or self.id == LaunchItem.RECENT_PROFILE: 110 return "%s [%s]" % (self.name, self.package_name) # .decode(sys.getfilesystemencoding()) 111 else: 112 return "%s" % self.name 113 elif role == Qt.ToolTipRole: 114 # return the tooltip of the item 115 result = "%s" % self.path 116 if self.id == LaunchItem.RECENT_FILE or self.id == LaunchItem.RECENT_PROFILE: 117 result = "%s\nPress 'Delete' to remove the entry from the history list" % self.path 118 return result 119 # elif role == Qt.DecorationRole: 120 # # return the showed icon 121 # pathItem, path, pathId = self.items[index.row()] 122 # if self.id > LaunchListModel.NOTHING and self.model().icons.has_key(self.id): 123 # return self.model().icons[self.id] 124 # return None 125 elif role == Qt.EditRole: 126 return "%s" % self.name 127 else: 128 # We don't care about anything else, so return default value 129 return QStandardItem.data(self, role)
130
131 - def setData(self, value, role=Qt.EditRole):
132 if role == Qt.EditRole: 133 # rename the file or folder 134 if self.name != value and self.id in [self.RECENT_FILE, self.LAUNCH_FILE, self.RECENT_PROFILE, self.PROFILE, self.CFG_FILE, self.FOLDER]: 135 new_path = os.path.join(os.path.dirname(self.path), value) 136 if not os.path.exists(new_path): 137 os.rename(self.path, new_path) 138 if self.name != value and self.id in [self.RECENT_FILE, self.RECENT_PROFILE]: 139 # update in history 140 nm.settings().launch_history_add(new_path, replace=self.path) 141 self.name = value 142 self.path = new_path 143 else: 144 MessageBox.warning(self, "Path already exists", 145 "`%s` already exists!" % value, "Complete path: %s" % new_path) 146 return QStandardItem.setData(self, value, role)
147 148 @classmethod
149 - def getItemList(self, name, path, item_id, root):
150 ''' 151 Creates the list of the items . This list is used for the 152 visualization of topic data as a table row. 153 @param name: the topic name 154 @type name: C{str} 155 @param root: The parent QStandardItem 156 @type root: U{QtGui.QStandardItem<https://srinikom.github.io/pyside-docs/PySide/QtGui/QStandardItem.html>} 157 @return: the list for the representation as a row 158 @rtype: C{[L{LaunchItem} or U{QtGui.QStandardItem<https://srinikom.github.io/pyside-docs/PySide/QtGui/QStandardItem.html>}, ...]} 159 ''' 160 items = [] 161 item = LaunchItem(name, path, item_id, parent=root) 162 items.append(item) 163 return items
164
165 - def isLaunchFile(self):
166 ''' 167 @return: C{True} if it is a launch file 168 @rtype: C{boolean} 169 ''' 170 return self.path is not None and os.path.isfile(self.path) and self.path.endswith('.launch')
171
172 - def isConfigFile(self):
173 ''' 174 @return: C{True} if it is a config file 175 @rtype: C{boolean} 176 ''' 177 return self.id == self.CFG_FILE
178
179 - def isProfileFile(self):
180 ''' 181 @return: C{True} if it is a node_manager profile file 182 @rtype: C{boolean} 183 ''' 184 return self.path is not None and os.path.isfile(self.path) and self.path.endswith('.nmprofile')
185
186 187 -class LaunchListModel(QStandardItemModel):
188 ''' 189 The model to manage the list with launch files. 190 ''' 191 header = [('Name', -1)] 192 '''@ivar: the list with columns C{[(name, width), ...]}''' 193
194 - def __init__(self):
195 ''' 196 Creates a new list model. 197 ''' 198 QStandardItemModel.__init__(self) 199 self.setColumnCount(len(LaunchListModel.header)) 200 self.setHorizontalHeaderLabels([label for label, _width in LaunchListModel.header]) 201 self.pyqt_workaround = dict() # workaround for using with PyQt: store the python object to keep the defined attributes in the TopicItem subclass 202 self.items = [] 203 self.DIR_CACHE = {} 204 self.currentPath = None 205 self.root_paths = [os.path.normpath(p) for p in os.getenv("ROS_PACKAGE_PATH").split(':')] 206 self._setNewList(self._moveUp(None)) 207 self.__packages = {} 208 self._fill_packages_thread = PackagesThread()
209
210 - def _getRootItems(self):
211 result = list(nm.settings().launch_history) 212 result.extend(self.root_paths) 213 return result
214
215 - def _fill_packages(self, packages):
216 self.__packages = packages
217 218 # %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 219 # %%%%%%%%%%%%% Overloaded methods %%%%%%%% 220 # %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 221
222 - def flags(self, index):
223 ''' 224 @param index: parent of the list 225 @type index: U{QtCore.QModelIndex<https://srinikom.github.io/pyside-docs/PySide/QtCore/QModelIndex.html>} 226 @return: Flag or the requested item 227 @rtype: U{QtCore.Qt.ItemFlag<https://srinikom.github.io/pyside-docs/PySide/QtCore/Qt.html>} 228 @see: U{http://www.pyside.org/docs/pyside-1.0.1/PySide/QtCore/Qt.html} 229 ''' 230 if not index.isValid(): 231 return Qt.NoItemFlags 232 try: 233 item = self.itemFromIndex(index) 234 result = Qt.ItemIsSelectable | Qt.ItemIsEnabled | Qt.ItemIsDragEnabled 235 if item.id in [LaunchItem.RECENT_FILE, LaunchItem.RECENT_PROFILE, LaunchItem.LAUNCH_FILE, LaunchItem.CFG_FILE, LaunchItem.FOLDER, LaunchItem.PROFILE]: 236 result = result | Qt.ItemIsEditable | Qt.ItemIsDragEnabled 237 return result 238 except: 239 return Qt.ItemIsEnabled | Qt.ItemIsSelectable
240 241 # %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 242 # %%%%%%%%%%%%% Drag operation %%%%%%%% 243 # %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 244
245 - def mimeTypes(self):
246 return ['text/plain']
247
248 - def mimeData(self, indexes):
249 mimeData = QMimeData() 250 text = '' 251 for index in indexes: 252 if index.isValid(): 253 item = self.itemFromIndex(index) 254 prev = '%s\n' % text if text else '' 255 text = '%sfile://%s' % (prev, item.path) 256 mimeData.setData('text/plain', text.encode('utf-8')) 257 return mimeData
258 259 # %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 260 # %%%%%%%%%%%%% External usage %%%%%%%% 261 # %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 262
263 - def reloadPackages(self):
264 ''' 265 Reloads the cached packag list. 266 ''' 267 if not self._fill_packages_thread.isAlive(): 268 self._fill_packages_thread = PackagesThread() 269 self._fill_packages_thread.packages.connect(self._fill_packages) 270 self._fill_packages_thread.start()
271
272 - def reloadCurrentPath(self):
273 ''' 274 Reloads the current path. 275 ''' 276 # clear the cache for package names 277 try: 278 from .common import PACKAGE_CACHE 279 PACKAGE_CACHE.clear() 280 self.DIR_CACHE = {} 281 except: 282 import traceback 283 print traceback.format_exc(2) 284 try: 285 if self.currentPath is None: 286 self._setNewList(self._moveUp(self.currentPath)) 287 else: 288 self._setNewList(self._moveDown(self.currentPath)) 289 except: 290 self._setNewList(self._moveUp(None))
291
292 - def expandItem(self, path_item, path, item_id):
293 ''' 294 Returns for the given item and path the file path if this is a file. Otherwise the 295 folder will be expanded and None will be returned. 296 @param path_item: the list item 297 @type path_item: C{str} 298 @param path: the real path of the item 299 @type path: C{str} 300 @return: path of the launch file or None 301 @rtype: C{str} 302 @raise Exception if no path to given item was found 303 ''' 304 if path_item == '..': 305 goto_path = os.path.dirname(path) 306 key_mod = QApplication.keyboardModifiers() 307 if key_mod & Qt.ControlModifier: 308 goto_path = None 309 root_path, items = self._moveUp(goto_path) 310 elif os.path.isfile(path): 311 return path 312 elif item_id == LaunchItem.RECENT_FILE or item_id == LaunchItem.LAUNCH_FILE: 313 raise Exception("Invalid file path: %s", path) 314 else: 315 key_mod = QApplication.keyboardModifiers() 316 onestep = False 317 if key_mod & Qt.ControlModifier: 318 onestep = True 319 root_path, items = self._moveDown(path, onestep) 320 self._setNewList((root_path, items)) 321 return None
322
323 - def setPath(self, path):
324 ''' 325 Shows the new path in the launch configuration view. Only if the new path 326 is in ros package paths 327 @param path: new path 328 @type path: C{str} 329 ''' 330 # if self._is_in_ros_packages(path): 331 self._setNewList(self._moveDown(path))
332
333 - def show_packages(self, show):
334 if show: 335 # clear the cache for package names 336 try: 337 items = [] 338 for package, path in self.__packages.items(): 339 items.append((package, path, LaunchItem.PACKAGE)) 340 self._setNewList((self.currentPath if self.currentPath else '', items)) 341 except: 342 import traceback 343 print traceback.format_exc(2) 344 else: 345 self._setNewList(self._moveUp(self.currentPath))
346
347 - def paste_from_clipboard(self):
348 ''' 349 Copy the file or folder to new position... 350 ''' 351 if QApplication.clipboard().mimeData().hasText() and self.currentPath: 352 text = QApplication.clipboard().mimeData().text() 353 if text.startswith('file://'): 354 path = text.replace('file://', '') 355 basename = os.path.basename(text) 356 ok = True 357 if os.path.exists(os.path.join(self.currentPath, basename)): 358 basename, ok = QInputDialog.getText(None, 'File exists', 'New name (or override):', QLineEdit.Normal, basename) 359 if ok and basename: 360 if os.path.isdir(path): 361 shutil.copytree(path, os.path.join(self.currentPath, basename)) 362 elif os.path.isfile(path): 363 shutil.copy2(path, os.path.join(self.currentPath, basename)) 364 self.reloadCurrentPath()
365
366 - def copy_to_clipboard(self, indexes):
367 ''' 368 Copy the selected path to the clipboard 369 ''' 370 mimeData = QMimeData() 371 text = '' 372 for index in indexes: 373 if index.isValid(): 374 item = self.itemFromIndex(index) 375 prev = '%s\n' % text if text else '' 376 text = '%sfile://%s' % (prev, item.path) 377 mimeData.setData('text/plain', text.encode('utf-8')) 378 QApplication.clipboard().setMimeData(mimeData)
379 380 # %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 381 # %%%%%%%%%%%%% Functionality %%%%%%%% 382 # %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 383
384 - def _setNewList(self, (root_path, items)):
385 ''' 386 Sets the list to the given path and insert the items. If the root path is not 387 None the additional item '..' to go up will be inserted. The items parameter 388 is a tupel with three values (the displayed name, the path of the item, the id 389 of the item). 390 @see: L{LaunchListModel._addPathToList()} 391 @param root_path: the root directory 392 @type root_path: C{str} 393 @param items: the list with characterized items 394 @type items: C{[(item, path, id)]} 395 ''' 396 root = self.invisibleRootItem() 397 while root.rowCount(): 398 root.removeRow(0) 399 self.pyqt_workaround.clear() 400 # add new items 401 if root_path is not None: 402 self._addPathToList('..', root_path, LaunchItem.NOTHING) 403 for item_name, item_path, item_id in items: 404 self._addPathToList(item_name, item_path, item_id) 405 self.currentPath = root_path
406
407 - def _is_in_ros_packages(self, path):
408 ''' 409 Test whether the given path is in ROS_PACKAGE_PATH. 410 @return: C{True}, if the path is in the ROS_PACKAGE_PATH 411 @rtype: C{boolean} 412 ''' 413 # TODO fix for paths with symbolic links 414 for p in self.root_paths: 415 if path.startswith(p): 416 return True 417 return False
418
419 - def _addPathToList(self, item, path, path_id):
420 ''' 421 Inserts the given item in the list model. 422 @param item: the displayed name 423 @type item: C{str} 424 @param path: the path of the item 425 @type path: C{str} 426 @param path_id: the id of the item, which represents whether it is a file, package or stack. 427 @type path_id: C{constants of LaunchListModel} 428 ''' 429 root = self.invisibleRootItem() 430 if item is None or path is None or path_id == LaunchItem.NOT_FOUND: 431 return False 432 if (path_id != LaunchItem.NOT_FOUND): 433 # add sorted a new entry 434 try: 435 for i in range(root.rowCount()): 436 launchItem = root.child(i) 437 launch_file_cmp = (path_id in [LaunchItem.RECENT_FILE, LaunchItem.LAUNCH_FILE, LaunchItem.RECENT_PROFILE, LaunchItem.PROFILE] and item < launchItem.name) 438 launch_id_cmp = (launchItem.id > path_id and launchItem.id > LaunchItem.LAUNCH_FILE) 439 launch_name_cmp = (launchItem.id == path_id and item < launchItem.name) 440 if launch_file_cmp or launch_id_cmp or launch_name_cmp: 441 new_item_row = LaunchItem.getItemList(item, path, path_id, root) 442 root.insertRow(i, new_item_row) 443 self.pyqt_workaround[item] = new_item_row[0] # workaround for using with PyQt: store the python object to keep the defined attributes in the TopicItem subclass 444 return True 445 new_item_row = LaunchItem.getItemList(item, path, path_id, root) 446 root.appendRow(new_item_row) 447 self.pyqt_workaround[item] = new_item_row[0] 448 return True 449 except: 450 import traceback 451 print traceback.format_exc(2) 452 return False
453
454 - def _identifyPath(self, path):
455 ''' 456 Determines the id of the given path 457 @return: the id represents whether it is a file, package or stack 458 @rtype: C{constants of LaunchItem} 459 ''' 460 if path in self.DIR_CACHE: 461 if path in nm.settings().launch_history: 462 if path.endswith('.nmprofile'): 463 return LaunchItem.RECENT_PROFILE 464 return LaunchItem.RECENT_FILE 465 return self.DIR_CACHE[path] 466 if os.path.basename(path)[0] != '.': 467 if path in nm.settings().launch_history: 468 if path.endswith('.nmprofile'): 469 self.DIR_CACHE[path] = LaunchItem.RECENT_PROFILE 470 return LaunchItem.RECENT_PROFILE 471 else: 472 self.DIR_CACHE[path] = LaunchItem.RECENT_FILE 473 return LaunchItem.RECENT_FILE 474 elif os.path.isfile(path): 475 if (path.endswith('.launch')): 476 self.DIR_CACHE[path] = LaunchItem.LAUNCH_FILE 477 return LaunchItem.LAUNCH_FILE 478 elif (path.endswith('.nmprofile')): 479 self.DIR_CACHE[path] = LaunchItem.PROFILE 480 return LaunchItem.PROFILE 481 else: 482 for e in nm.settings().launch_view_file_ext: 483 if path.endswith(e): 484 self.DIR_CACHE[path] = LaunchItem.CFG_FILE 485 return LaunchItem.CFG_FILE 486 elif os.path.isdir(path): 487 fileList = os.listdir(path) 488 if self._containsLaunches(path): 489 if 'stack.xml' in fileList: 490 self.DIR_CACHE[path] = LaunchItem.STACK 491 return LaunchItem.STACK 492 elif is_package(fileList): 493 self.DIR_CACHE[path] = LaunchItem.PACKAGE 494 return LaunchItem.PACKAGE 495 else: 496 self.DIR_CACHE[path] = LaunchItem.FOLDER 497 return LaunchItem.FOLDER 498 self.DIR_CACHE[path] = LaunchItem.NOT_FOUND 499 return LaunchItem.NOT_FOUND
500
501 - def _containsLaunches(self, path):
502 ''' 503 Moves recursively down in the path tree and searches for a launch file. If 504 one is found True will be returned. 505 @return: C{True} if the path contains a launch file. 506 @rtype: C{boolean} 507 ''' 508 fileList = os.listdir(path) 509 for cfile in fileList: 510 _, file_extension = os.path.splitext(cfile) 511 if os.path.isfile(os.path.join(path, cfile)) and (cfile.endswith('.launch')) or (file_extension in nm.settings().launch_view_file_ext): 512 return True 513 for cfile in fileList: 514 if os.path.isdir(os.path.join(path, cfile)): 515 if self._containsLaunches(os.path.join(path, cfile)): 516 return True 517 return False
518
519 - def _moveDown(self, path, onestep=False):
520 ''' 521 Moves recursively down in the path tree until the current path contains a 522 launch file. 523 @return: tupel of (root_path, items) 524 @rtype: C{tupel of (root_path, items)} 525 @see: L{LaunchListModel._setNewList()} 526 ''' 527 result_list = [] 528 dirlist = os.listdir(path) 529 for cfile in dirlist: 530 item = os.path.normpath(''.join([path, '/', cfile])) 531 pathItem = os.path.basename(item) 532 if pathItem == 'src': 533 pathItem = '%s (src)' % os.path.basename(os.path.dirname(item)) 534 pathId = self._identifyPath(item) 535 if (pathId != LaunchItem.NOT_FOUND): 536 result_list.append((pathItem, item, pathId)) 537 if len(result_list) == 1 and not os.path.isfile(result_list[0][1]): 538 if not onestep: 539 return self._moveDown(result_list[0][1]) 540 return path, result_list
541
542 - def _moveUp(self, path):
543 ''' 544 Moves recursively up in the path tree until the current path contains a 545 launch file or the root path defined by ROS_PACKAGES_PATH is reached. 546 @return: tupel of (root_path, items) 547 @rtype: C{tupel of (root_path, items)} 548 @see: L{LaunchListModel._setNewList()} 549 ''' 550 result_list = [] 551 if path is None or not self._is_in_ros_packages(path): 552 dirlist = self._getRootItems() 553 path = None 554 else: 555 dirlist = os.listdir(path) 556 for dfile in dirlist: 557 item = os.path.normpath(os.path.join(path, dfile)) if path is not None else dfile 558 pathItem = os.path.basename(item) 559 if pathItem == 'src': 560 pathItem = '%s (src)' % os.path.basename(os.path.dirname(item)) 561 pathId = self._identifyPath(item) 562 if (pathId != LaunchItem.NOT_FOUND): 563 result_list.append((pathItem, item, pathId)) 564 if path is not None and len(result_list) == 1 and not os.path.isfile(result_list[0][1]): 565 return self._moveUp(os.path.dirname(path)) 566 else: 567 self.currentPath = None 568 return path, result_list
569