def _load(filePath): # type: (pathlib.Path) -> NoReturn with itemsLock: icon = self.__iconsCache.get(filePath) if icon is None: iconProvider = QFileIconProvider() posixPath = filePath.as_posix() file = QFileInfo(posixPath) icon = iconProvider.icon(file) if icon.isNull(): mimeDb = QMimeDatabase() for mime in mimeDb.mimeTypesForFileName(posixPath): icon = QIcon.fromTheme(mime.iconName()) if not icon.isNull(): break result = QFileIconLoader.LoadResult(filePath, icon) with itemsLock: loadedItems[filePath] = result self.__iconsCache.set(filePath, icon) self.loaded.emit(result) if len(loadedItems) == len(targetPaths): self.completed.emit(loadedItems)
def setupModelData(self, parent): parents = [parent] numAssets = rt.AssetManager.getNumAssets() # For each asset (row) for i in range(1, numAssets + 1): # Get the asset object asset = rt.AssetManager.getAssetByIndex(i) # Get the asset properties assetFilename = asset.GetFilename() assetBasename = os.path.basename(assetFilename) assetName = os.path.splitext(assetBasename)[0] assetPath = os.path.dirname(assetFilename) assetExt = os.path.splitext(assetBasename)[1] if assetExt == "": continue assetType = str(asset.GetType()) assetStatus = os.path.exists(assetFilename) if (assetStatus): assetSize = os.path.getsize(assetFilename) else: assetSize = 0 # We'll use QFileIconProvider to grab the icon that the OS # is currently using to grab the icon to display in the view fileInfo = QtCore.QFileInfo(assetFilename) iconProvider = QFileIconProvider() assetIcon = iconProvider.icon(fileInfo) # Read the column data from the rest of the line. columnData = [ assetName, assetExt, assetPath, assetType, os.path.exists(assetFilename), assetSize ] # Append a new node to the root parent = parents[-1] parent.insertChildren(parent.childCount(), 1, self._rootItem.columnCount()) node = parent.child(parent.childCount() - 1) # Fill each column in the current row for column in range(len(columnData)): node.setData(column, columnData[column]) node.setIcon(assetIcon)
def __init__(self, parent): super().__init__(parent) iconProvider = QFileIconProvider() self.__defaultIcon = iconProvider.icon(QFileIconProvider.Folder) self.__driveIcon = iconProvider.icon(QFileIconProvider.Drive)
class FolderListModel(QAbstractTableModel): """ This class provides a model for browsing a folder. """ folderChanged = Signal(str) # emitted when the folder changes def __init__(self, parent): super().__init__(parent=parent) self._folder = None self._filter = "*" self._children = [] self._iconProvider = QFileIconProvider() self._reset(self._folder, self._filter) def setFolder(self, folder): """ set the folder of this browser :param folder: a Path or string instance :return: """ if Path(folder) != self._folder: self._reset(folder, self._filter) def setFilter(self, flt): """ Set the filter of this browser :param flt: string or a list of strings containing glob-style patterns :return: """ if isinstance(flt, str): flt = [flt] self._reset(self._folder, flt) def fileToIndex(self, filename): """ return the given file name to a model index. :param filename: a string or Path instance :return: a QModelIndex instance """ filename = Path(filename) try: idx = self._children.index(filename) return self.createIndex(idx, 0) except ValueError: return QModelIndex() def _match(self, path): if path.is_dir(): return True res = QDir.match(self._filter, path.name) return res def _reset(self, folder, flt): self.beginRemoveRows(QModelIndex(), 0, self.rowCount() - 1) self._folder = None self.endRemoveRows() if folder is not None: listDrives = False f = Path(folder).resolve() if platform.system() == "Windows": folder = Path(folder) if folder.name == ".." and folder.parent == Path(folder.drive + "/"): listDrives = True f = Path("<Drives>") self._folder = f self._filter = flt if platform.system() == "Windows": if listDrives: self._children = [ Path("%s:/" % dl) for dl in string.ascii_uppercase if Path("%s:/" % dl).exists() ] else: self._children = [f / ".."] else: self._children = ([] if f.root == f else [f / ".."]) if not listDrives: self._children += [x for x in f.glob("*") if self._match(x)] self._children.sort(key=lambda c: (c.is_file( ), c.drive, int(c.name != ".."), c.name)) self.beginInsertRows(QModelIndex(), 0, len(self._children) - 1) self.endInsertRows() if listDrives: self.folderChanged.emit("<Drives>") else: self.folderChanged.emit( str(self._folder) + (os.path.sep if self._folder.is_dir() else "")) def folder(self): """ Return the current folder. :return: a Path instance """ return self._folder def filter(self): """ Return the current filter :return: a list of strings """ return self._filter def columnCount(self, index=QModelIndex()): # pylint: disable=unused-argument """ overwritten from base class :param index: :return: """ return 4 def rowCount(self, index=QModelIndex()): # pylint: disable=unused-argument """ overwritten from base class :param index: :return: """ return len(self._children) def data(self, index, role): """ overwritten from base class :param index: :param role: :return: """ c = self._children[index.row()] if role == Qt.DisplayRole: if index.column() == 0: return c.name if c.name != "" else str(c) if index.column() == 1: if c.is_dir(): return "" try: s = c.stat().st_size except Exception: # pylint: disable=broad-except return "" if s >= 1024 * 1024 * 1024: return "%.0f GB" % (s / (1024 * 1024 * 1024)) if s >= 1024 * 1024: return "%.0f MB" % (s / (1024 * 1024)) if s >= 1024: return "%.0f kB" % (s / 1024) return s if index.column() == 2: try: return QDateTime.fromMSecsSinceEpoch(c.stat().st_mtime * 1000) except Exception: # pylint: disable=broad-except return "" if role == Qt.DecorationRole: if index.column() == 0: if c.is_dir(): return self._iconProvider.icon(QFileIconProvider.Drive) return self._iconProvider.icon(QFileInfo(str(c.absolute()))) if role == Qt.UserRole: if index.column() == 0: return c if role in [Qt.DisplayRole, Qt.EditRole]: if index.column() == 3: if index.row() > 0: return str(c) + (os.path.sep if c.is_dir() else "") return str(c.parent) + os.path.sep return None def headerData(self, section, orientation, role): """ overwritten from base class :param section: :param orientation: :param role: :return: """ if orientation == Qt.Horizontal and role == Qt.DisplayRole: return ["Name", "Size", "Time", ""][section] return super().headerData(section, orientation, role)
class SD_FS_Model(QAbstractItemModel): def __init__(self, parent=None, *args): super().__init__(parent, *args) self.root = FS_Item("/") self.header_labels = ['Name', 'Children'] self.icon_provider = QFileIconProvider() # return an index for the given row/column pair and the parent element # this index is then used by views to access data def index(self, row: int, column: int, parent: QModelIndex = None) -> QModelIndex: if not parent or not parent.isValid(): parent_item = self.root else: parent_item = parent.internalPointer() # check if row is valid if row >= parent_item.child_count(): return QModelIndex() child_item = parent_item.child(row) # check if column is valid for child if column >= child_item.column_count(): return QModelIndex() # return the valid index for the childs column return self.createIndex(row, column, child_item) def parent(self, index: QModelIndex = None) -> QModelIndex: if not index.isValid(): return QModelIndex() child_item = index.internalPointer() parent_item = child_item.parent() # return invalid index if we hit root if parent_item == self.root: return QModelIndex() return self.createIndex(parent_item.row(), 0, parent_item) # returns the amount of nested rows aka children for the parent item the index identifies def rowCount(self, parent: QModelIndex = None) -> int: # if the column count is nonzero we return # indices that identify a node/row and not a value/field have column=0 by convention if parent is not None and parent.column() > 0: return 0 if not parent or not parent.isValid(): parent_item = self.root else: parent_item = parent.internalPointer() return parent_item.child_count() # returns the number of columns available for an index def columnCount(self, index: QModelIndex = None) -> int: # for now, we accept indices with column>0, as there is no drawback if index is not None and index.isValid(): return index.internalPointer().column_count() return self.root.column_count() # returns the value of the data attribute mapped to the column # column mapping is done in the respective class the CanItem holds # via the column_representation classmethod def data(self, index: QModelIndex, role: int = None): # we return None for root data or roles we dont serve if not index.isValid(): return None if role == Qt.DecorationRole and index.column() == 0: if index.internalPointer().dir: return self.icon_provider.icon(QFileIconProvider.Folder) else: return self.icon_provider.icon(QFileIconProvider.File) elif role == Qt.DisplayRole: # we get the desired data by calling the CanItems data() function # that then calls the held objects get_data function return index.internalPointer().data(index.column()) return None def flags(self, index: QModelIndex) -> Qt.ItemFlags: if not index.isValid(): return Qt.NoItemFlags return Qt.ItemIsEnabled | Qt.ItemIsSelectable # returns header labeling, atm just copied from the table model def headerData(self, section, orientation, role: Qt.DisplayRole = None): if role == Qt.DisplayRole and orientation == Qt.Orientation.Horizontal: return self.header_labels[section] return QAbstractTableModel.headerData(self, section, orientation, role) def add_item(self, index: QModelIndex, name: str, is_dir: bool): # find the parent if index is None or not index.isValid(): parent_item = self.root else: parent_item = index.internalPointer() item = FS_Item(name, is_dir=is_dir, parent=parent_item) # send insert signals and append child if index is not None: self.beginInsertRows(index, parent_item.child_count(), parent_item.child_count()) parent_item.append_child(item) self.endInsertRows() else: print(f"heres the bug: \n {item.name} \n {item.get_filepath()}") def remove_item(self, index: QModelIndex): # cant remove root if index is None or not index.isValid(): return # retrieve parameters fo signal call parent_item = index.internalPointer().parent() parent_index = self.parent(index) row = index.row() # remove child from parent self.beginRemoveRows(parent_index, row, row) parent_item.remove_child(row) self.endRemoveRows() def child_index_by_name(self, parent_index: QModelIndex, name: str) -> QModelIndex: if not parent_index.isValid(): parent = self.root else: parent = parent_index.internalPointer() for child in parent.children: if child.name == name: return self.createIndex(child.row(), 0, child) # no child has the name -> return invalid Index return QModelIndex() # search through the items according to the given directory path and return index to end of path def index_for_path(self, path: str) -> QModelIndex: # return an invalid index for the root if path == "/": return QModelIndex() slash1 = 0 slash2 = path.find("/", slash1 + 1) if slash2 == -1: directory = path[slash1 + 1:] else: directory = path[slash1 + 1:slash2] path_end_index = self.child_index_by_name(QModelIndex(), directory) while True: if not path_end_index.isValid(): # subdirectory not found, return None return None # go deeper if we have a longer path if slash2 != -1: slash1 = slash2 slash2 = path.find("/", slash1 + 1) else: return path_end_index if slash2 == -1: directory = path[slash1 + 1:] else: directory = path[slash1 + 1:slash2] path_end_index = self.child_index_by_name(path_end_index, directory) def is_filled(self) -> bool: return self.root.child_count() > 0