Реализация пользовательского файлового браузера на PySide6

Я хотел бы реализовать файловый браузер на PySide6, и мои цели:

  1. Отображать файлы и папки с .. всегда вверху (независимо от сортировки), чтобы пользователь мог дважды щелкнуть по ним и перейти на один уровень вверх.
  2. После .. я хотел бы отображать папки, а затем файлы (как это делает проводник Windows) независимо от сортировки.
  3. Есть альтернативный режим отображения, который показывает определенный набор файлов (они могут быть на разных дисках, в разных папках и т.д.).

В настоящее время я использую следующий код для инициализации модели и представления:

self.model = QFileSystemModel()
self.model.setRootPath(path)
self.model.setFilter(QDir.NoDot | QDir.AllEntries)
self.model.sort(0,Qt.SortOrder.AscendingOrder)
self.ui.treeView.setModel(self.model)
self.ui.treeView.setRootIndex(self.model.index(path))
self.ui.treeView.header().setSortIndicator(0, Qt.AscendingOrder)
self.ui.treeView.setSortingEnabled(True)

Вместо QFileSystemModel() я фактически использую свою настроенную QFileSystemModel с дополнительным столбцом.

Проблемы, с которыми я сталкиваюсь, заключаются в следующем:

  • .. сортируется вместе с другим содержимым и не отображается вверху
  • каталоги не остаются вверху после сортировки

Я не понимаю, как лучше всего решать проблемы, которые я решаю.

Я вижу следующие варианты:

  • используйте QSortFilterProxyModel и каким-то образом заставьте .. всегда быть сверху, независимо от сортировки (не уверен, что это возможно), а также сначала сохраняйте каталоги (есть связанный вопрос), я также потенциально мог бы использовать его для пункта 3 выше, чтобы отображать файлы по определенным критериям.
  • используйте совершенно другой подход, возможно, QFileSystemWatcher или QTreeWidget, которые я заполню вручную (сохранение .. всегда наверху, похоже, в любом случае вызывает проблемы).
  • как-то добавить .. вверху QTreeView после его загрузки или сортировки

Я попытался реализовать QSortFilterProxyModel, но столкнулся с другой проблемой: я не понимаю, как мне изменить вызов treeView.setRootIndex().

Итак, мои конкретные вопросы:

  1. Могу ли я использовать QSortFilterProxyModel для решения всех упомянутых выше проблем? Если да, приведите пример реализации.
  2. Если вы считаете, что есть лучший подход к этой проблеме, пожалуйста, опишите его.

person Code Your Dream    schedule 16.02.2021    source источник


Ответы (2)


    import PySide2
    from PySide2 import QtWidgets
    from PySide2.QtWidgets import QFileSystemModel
    from PySide2.QtWidgets import QMainWindow, QWidget
    from PySide2.QtWidgets import QTreeView
    from PySide2.QtWidgets import QVBoxLayout
    from PySide2.QtCore import QDir
    from PySide2.QtCore import QSortFilterProxyModel
    from PySide2.QtCore import Qt

    class View(QMainWindow):
        def __init__(self):
            super().__init__()

            self._w_main = QWidget()
            self.setCentralWidget(self._w_main)
            self.tree_view = QTreeView(self._w_main)

            self._layout = QVBoxLayout()
            self._layout.addWidget(self.tree_view)
            self._w_main.setLayout(self._layout)

    class SortingModel(QSortFilterProxyModel):
        def lessThan(
                self,
                source_left: PySide2.QtCore.QModelIndex,
                source_right: PySide2.QtCore.QModelIndex
        ):
            file_info1 = self.sourceModel().fileInfo(source_left)
            file_info2 = self.sourceModel().fileInfo(source_right)

            if file_info1.isDir() and file_info2.isDir():
                return super().lessThan(source_left, source_right)
            return file_info1.isDir()

    app = QtWidgets.QApplication([])
    view = View()
    model = QFileSystemModel()
    model.setRootPath('.')
    model.setFilter(QDir.NoDot | QDir.AllEntries)
    model.sort(0, Qt.SortOrder.AscendingOrder)
    sorting_model = SortingModel()
    sorting_model.setSourceModel(model)
    view.tree_view.setModel(sorting_model)
    view.tree_view.setRootIndex(sorting_model.mapFromSource(model.index('.')))
    view.tree_view.header().setSortIndicator(0, Qt.AscendingOrder)
    view.tree_view.setSortingEnabled(True)
    view.showMaximized()
    return sys.exit(app.exec_())

QSortFilterProxyModel помещает .. в начало списка по умолчанию в PySide2 5.14.1 на моей машине.

mapFromSource используется для отображения индекса для setRootIndex

person bartolo-otrit    schedule 16.02.2021
comment
Спасибо, но это не всегда ставит .. на первое место, и это моя главная цель. И, кстати, если есть папка с именем типа !something, она становится выше .. даже после сортировки. Кроме того, если я сортирую по имени, каталоги не отображаются первыми, и это еще одна цель. - person Code Your Dream; 16.02.2021
comment
Добавление этого в код SortingModel помогает вывести .. наверх, но не помогает сохранить его, если сортировка изменилась (что ожидается): if file_info1.fileName() == "..": return True Может быть, мне нужно переопределить функцию sort()? - person Code Your Dream; 16.02.2021
comment
Нашел и выложил рабочее решение, спасибо за помощь. - person Code Your Dream; 16.02.2021
comment
@CodeYourDream Вы можете пометить свой ответ как принятый. - person bartolo-otrit; 17.02.2021
comment
Нет, я не могу... пока. Придется подождать, SO позволяет принимать собственные ответы только через некоторое время. - person Code Your Dream; 17.02.2021

Работает следующее решение:

class SortingModel(QSortFilterProxyModel):
    def lessThan(self, source_left: QModelIndex, source_right: QModelIndex):
        file_info1 = self.sourceModel().fileInfo(source_left)
        file_info2 = self.sourceModel().fileInfo(source_right)       
        
        if file_info1.fileName() == "..":
            return self.sortOrder() == Qt.SortOrder.AscendingOrder

        if file_info2.fileName() == "..":
            return self.sortOrder() == Qt.SortOrder.DescendingOrder
                
        if (file_info1.isDir() and file_info2.isDir()) or (file_info1.isFile() and file_info2.isFile()):
            return super().lessThan(source_left, source_right)

        return file_info1.isDir() and self.sortOrder() == Qt.SortOrder.AscendingOrder

Код для инициализации представления и модели такой же, как в ответе @bartolo-otrit:

    model = QFileSystemModel()
    model.setRootPath('.')
    model.setFilter(QDir.NoDot | QDir.AllEntries)
    model.sort(0, Qt.SortOrder.AscendingOrder)
    sorting_model = SortingModel()
    sorting_model.setSourceModel(model)
    view.tree_view.setModel(sorting_model)
    view.tree_view.setRootIndex(sorting_model.mapFromSource(model.index('.')))
    view.tree_view.header().setSortIndicator(0, Qt.AscendingOrder)
    view.tree_view.setSortingEnabled(True)
person Code Your Dream    schedule 16.02.2021