src/eric7/UI/BrowserModel.py

branch
server
changeset 10680
306373ccf8fd
parent 10651
280a53840aa3
parent 10679
4d3e0ce54322
child 10704
27d21e5163b8
equal deleted inserted replaced
10651:280a53840aa3 10680:306373ccf8fd
15 15
16 from PyQt6.QtCore import ( 16 from PyQt6.QtCore import (
17 QAbstractItemModel, 17 QAbstractItemModel,
18 QCoreApplication, 18 QCoreApplication,
19 QDir, 19 QDir,
20 QFileSystemWatcher,
21 QModelIndex, 20 QModelIndex,
22 QProcess, 21 QProcess,
23 Qt, 22 Qt,
24 ) 23 )
25 from PyQt6.QtGui import QFont, QImageReader 24 from PyQt6.QtGui import QFont, QImageReader
26 from PyQt6.QtWidgets import QApplication 25 from PyQt6.QtWidgets import QApplication
27 26
28 from eric7 import Preferences 27 from eric7 import Preferences
28 from eric7.EricCore import EricFileSystemWatcher
29 from eric7.EricGui import EricPixmapCache 29 from eric7.EricGui import EricPixmapCache
30 from eric7.SystemUtilities import FileSystemUtilities 30 from eric7.SystemUtilities import FileSystemUtilities
31 from eric7.Utilities import ClassBrowsers 31 from eric7.Utilities import ClassBrowsers
32 from eric7.Utilities.ClassBrowsers import ClbrBaseClasses 32 from eric7.Utilities.ClassBrowsers import ClbrBaseClasses
33 33
82 self.__sysPathItem = None 82 self.__sysPathItem = None
83 83
84 self.__remotefsInterface = fsInterface 84 self.__remotefsInterface = fsInterface
85 85
86 if not nopopulate: 86 if not nopopulate:
87 self.watchedItems = {} 87 self.watchedDirItems = {}
88 self.watchedFileItems = {} 88 self.watchedFileItems = {}
89 self.watcher = QFileSystemWatcher(self) 89 watcher = EricFileSystemWatcher.instance()
90 self.watcher.directoryChanged.connect(self.directoryChanged) 90 watcher.directoryCreated.connect(lambda x: self.entryCreated(x, isDir=True))
91 self.watcher.fileChanged.connect(self.fileChanged) 91 watcher.directoryDeleted.connect(lambda x: self.entryDeleted(x, isDir=True))
92 watcher.fileCreated.connect(lambda x: self.entryCreated(x, isDir=False))
93 watcher.fileDeleted.connect(lambda x: self.entryDeleted(x, isDir=False))
94 watcher.fileModified.connect(self.fileChanged)
92 95
93 rootData = QCoreApplication.translate("BrowserModel", "Name") 96 rootData = QCoreApplication.translate("BrowserModel", "Name")
94 self.rootItem = BrowserItem(None, rootData) 97 self.rootItem = BrowserItem(None, rootData)
95 98
96 self.__populateModel() 99 self.__populateModel()
332 if ( 335 if (
333 dirName != "" 336 dirName != ""
334 and not FileSystemUtilities.isRemoteFileName(dirName) 337 and not FileSystemUtilities.isRemoteFileName(dirName)
335 and not dirName.startswith(("//", "\\\\")) 338 and not dirName.startswith(("//", "\\\\"))
336 ): 339 ):
337 if dirName not in self.watcher.directories(): 340 EricFileSystemWatcher.instance().addPath(dirName)
338 self.watcher.addPath(dirName) 341 if dirName in self.watchedDirItems:
339 if dirName in self.watchedItems: 342 if itm not in self.watchedDirItems[dirName]:
340 if itm not in self.watchedItems[dirName]: 343 self.watchedDirItems[dirName].append(itm)
341 self.watchedItems[dirName].append(itm)
342 else: 344 else:
343 self.watchedItems[dirName] = [itm] 345 self.watchedDirItems[dirName] = [itm]
344 346
345 def _removeWatchedItem(self, itm): 347 def _removeWatchedItem(self, itm):
346 """ 348 """
347 Protected method to remove a watched item. 349 Protected method to remove a watched item.
348 350
351 """ 353 """
352 if isinstance(itm, BrowserDirectoryItem): 354 if isinstance(itm, BrowserDirectoryItem):
353 dirName = itm.dirName() 355 dirName = itm.dirName()
354 with contextlib.suppress(KeyError): 356 with contextlib.suppress(KeyError):
355 with contextlib.suppress(ValueError): 357 with contextlib.suppress(ValueError):
356 self.watchedItems[dirName].remove(itm) 358 self.watchedDirItems[dirName].remove(itm)
357 if len(self.watchedItems[dirName]) == 0: 359 if len(self.watchedDirItems[dirName]) == 0:
358 del self.watchedItems[dirName] 360 del self.watchedDirItems[dirName]
359 self.watcher.removePath(dirName) 361 EricFileSystemWatcher.instance().removePath(dirName)
360 362
361 def directoryChanged(self, path): 363 def entryCreated(self, path, isDir=False):
362 """ 364 """
363 Public slot to handle the directoryChanged signal of the watcher. 365 Public method to handle the creation of a file or directory.
364 366
365 @param path path of the directory 367 @param path path of the created file or directory
366 @type str 368 @type str
367 """ 369 @param isDir flag indicating a created directory (defaults to False)
368 if path not in self.watchedItems: 370 @type bool (optional)
371 """
372 parentPath = os.path.dirname(path)
373 if parentPath not in self.watchedDirItems:
369 # just ignore the situation we don't have a reference to the item 374 # just ignore the situation we don't have a reference to the item
370 return 375 return
371 376
372 dirFilter = ( 377 for itm in self.watchedDirItems[parentPath]:
373 QDir.Filter.AllEntries | QDir.Filter.NoDotAndDotDot | QDir.Filter.Hidden 378 cnt = itm.childCount()
374 ) 379 self.beginInsertRows(self.createIndex(itm.row(), 0, itm), cnt, cnt)
375 380 node = (
376 for itm in self.watchedItems[path]: 381 BrowserDirectoryItem(
377 oldCnt = itm.childCount() 382 itm,
378 383 FileSystemUtilities.toNativeSeparators(path),
379 qdir = QDir(itm.dirName()) 384 False,
380
381 entryInfoList = qdir.entryInfoList(dirFilter)
382
383 # step 1: check for new entries
384 children = itm.children()
385 for f in entryInfoList:
386 fpath = FileSystemUtilities.toNativeSeparators(f.absoluteFilePath())
387 childFound = False
388 for child in children[:]:
389 if child.name() == fpath:
390 childFound = True
391 children.remove(child)
392 break
393 if childFound:
394 continue
395
396 cnt = itm.childCount()
397 self.beginInsertRows(self.createIndex(itm.row(), 0, itm), cnt, cnt)
398 node = (
399 BrowserDirectoryItem(
400 itm,
401 FileSystemUtilities.toNativeSeparators(f.absoluteFilePath()),
402 False,
403 )
404 if f.isDir()
405 else BrowserFileItem(
406 itm,
407 FileSystemUtilities.toNativeSeparators(f.absoluteFilePath()),
408 )
409 ) 385 )
410 self._addItem(node, itm) 386 if isDir
411 self.endInsertRows() 387 else BrowserFileItem(
412 388 itm,
413 # step 2: check for removed entries 389 FileSystemUtilities.toNativeSeparators(path),
414 if len(entryInfoList) != itm.childCount(): 390 )
415 for row in range(oldCnt - 1, -1, -1): 391 )
416 child = itm.child(row) 392 self._addItem(node, itm)
417 childname = FileSystemUtilities.fromNativeSeparators(child.name()) 393 self.endInsertRows()
418 entryFound = False 394
419 for f in entryInfoList[:]: 395 def entryDeleted(self, path, isDir=False): # noqa: U100
420 if f.absoluteFilePath() == childname: 396 """
421 entryFound = True 397 Public method to handle the deletion of a file or directory.
422 entryInfoList.remove(f) 398
423 break 399 @param path path of the deleted file or directory
424 if entryFound: 400 @type str
425 continue 401 @param isDir flag indicating a deleted directory (defaults to False)
426 402 @type bool (optional)
403 """
404 parentPath = os.path.dirname(path)
405 if parentPath not in self.watchedDirItems:
406 # just ignore the situation we don't have a reference to the item
407 return
408
409 for itm in self.watchedDirItems[parentPath]:
410 for row in range(itm.childCount() - 1, -1, -1):
411 child = itm.child(row)
412 if child.name() == path:
427 self._removeWatchedItem(child) 413 self._removeWatchedItem(child)
428 self.beginRemoveRows(self.createIndex(itm.row(), 0, itm), row, row) 414 self.beginRemoveRows(self.createIndex(itm.row(), 0, itm), row, row)
429 itm.removeChild(child) 415 itm.removeChild(child)
430 self.endRemoveRows() 416 self.endRemoveRows()
417 break
431 418
432 def __populateModel(self): 419 def __populateModel(self):
433 """ 420 """
434 Private method to populate the browser model. 421 Private method to populate the browser model.
435 """ 422 """
841 if ( 828 if (
842 parentItem.type_ == BrowserItemType.File 829 parentItem.type_ == BrowserItemType.File
843 and fileName not in self.watchedFileItems 830 and fileName not in self.watchedFileItems
844 ): 831 ):
845 # watch the file only in the file browser not the project viewer 832 # watch the file only in the file browser not the project viewer
846 self.watcher.addPath(fileName) 833 watcher = EricFileSystemWatcher.instance()
834 watcher.addPath(fileName)
847 self.watchedFileItems[fileName] = parentItem 835 self.watchedFileItems[fileName] = parentItem
848 836
849 def repopulateFileItem(self, itm): 837 def repopulateFileItem(self, itm):
850 """ 838 """
851 Public method to repopulate a file item. 839 Public method to repopulate a file item.
877 if os.path.exists(fileName): 865 if os.path.exists(fileName):
878 # the file was changed 866 # the file was changed
879 self.repopulateFileItem(self.watchedFileItems[fileName]) 867 self.repopulateFileItem(self.watchedFileItems[fileName])
880 else: 868 else:
881 # the file does not exist anymore 869 # the file does not exist anymore
870 watcher = EricFileSystemWatcher.instance()
871 watcher.removePath(fileName)
882 del self.watchedFileItems[fileName] 872 del self.watchedFileItems[fileName]
883 873
884 def populateClassItem(self, parentItem, repopulate=False): 874 def populateClassItem(self, parentItem, repopulate=False):
885 """ 875 """
886 Public method to populate a class item's subtree. 876 Public method to populate a class item's subtree.
1201 1191
1202 @return flag indicating a symbolic link 1192 @return flag indicating a symbolic link
1203 @rtype bool 1193 @rtype bool
1204 """ 1194 """
1205 return self.symlink 1195 return self.symlink
1196
1197 def lineno(self):
1198 """
1199 Public method to return the line number of the item.
1200
1201 @return line number defining the object
1202 @rtype int
1203 """
1204 return 0 # just a placeholder implementation
1205
1206 def colOffset(self):
1207 """
1208 Public method to return the column offset of the item definition.
1209
1210 @return column offset defining the object
1211 @rtype int
1212 """
1213 return 0 # default value
1206 1214
1207 1215
1208 class BrowserSimpleDirectoryItem(BrowserItem): 1216 class BrowserSimpleDirectoryItem(BrowserItem):
1209 """ 1217 """
1210 Class implementing the data structure for browser simple directory items. 1218 Class implementing the data structure for browser simple directory items.
1914 @return tuple with start end end line number 1922 @return tuple with start end end line number
1915 @rtype tuple of (int, int) 1923 @rtype tuple of (int, int)
1916 """ 1924 """
1917 return (self._classObject.lineno, self._classObject.endlineno) 1925 return (self._classObject.lineno, self._classObject.endlineno)
1918 1926
1927 def colOffset(self):
1928 """
1929 Public method to return the column offset of the item definition.
1930
1931 @return column offset defining the object
1932 @rtype int
1933 """
1934 return self._classObject.coloffset
1935
1919 def lessThan(self, other, column, order): 1936 def lessThan(self, other, column, order):
1920 """ 1937 """
1921 Public method to check, if the item is less than the other one. 1938 Public method to check, if the item is less than the other one.
1922 1939
1923 @param other reference to item to compare against 1940 @param other reference to item to compare against
2045 @return tuple with start end end line number 2062 @return tuple with start end end line number
2046 @rtype tuple of (int, int) 2063 @rtype tuple of (int, int)
2047 """ 2064 """
2048 return (self._functionObject.lineno, self._functionObject.endlineno) 2065 return (self._functionObject.lineno, self._functionObject.endlineno)
2049 2066
2067 def colOffset(self):
2068 """
2069 Public method to return the column offset of the item definition.
2070
2071 @return column offset defining the object
2072 @rtype int
2073 """
2074 return self._functionObject.coloffset
2075
2050 def lessThan(self, other, column, order): 2076 def lessThan(self, other, column, order):
2051 """ 2077 """
2052 Public method to check, if the item is less than the other one. 2078 Public method to check, if the item is less than the other one.
2053 2079
2054 @param other reference to item to compare against 2080 @param other reference to item to compare against
2237 @return line number the object is assigned to 2263 @return line number the object is assigned to
2238 @rtype list of int 2264 @rtype list of int
2239 """ 2265 """
2240 return self._attributeObject.linenos[:] 2266 return self._attributeObject.linenos[:]
2241 2267
2268 def colOffset(self):
2269 """
2270 Public method to return the column offset of the item definition.
2271
2272 @return column offset defining the object
2273 @rtype int
2274 """
2275 return self._attributeObject.coloffset
2276
2242 def lessThan(self, other, column, order): 2277 def lessThan(self, other, column, order):
2243 """ 2278 """
2244 Public method to check, if the item is less than the other one. 2279 Public method to check, if the item is less than the other one.
2245 2280
2246 @param other reference to item to compare against 2281 @param other reference to item to compare against

eric ide

mercurial