PySide6 上的自定义文件浏览器实现
Custom file browser implementation on PySide6
我想在PySide6上实现文件浏览器,我的目标是:
- 显示文件和文件夹
..
始终在顶部(无论排序如何),以便用户可以双击它并上一级。
- 在
..
之后,我想显示文件夹,然后显示文件(就像 Windows 资源管理器所做的那样),而不考虑排序。
- 有一个显示特定文件集的替代显示模式(它们可以在不同的驱动器、不同的文件夹等)。
我目前正在使用以下代码来初始化模型和视图:
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 并以某种方式强制
..
始终位于最前面,无论排序如何(不确定是否可能)并且还保留目录在前(有一个 related question),我也可能使用上面第 3 点按特定标准显示文件
- 使用完全不同的方法,也许是我手动填写的 QFileSystemWatcher 或 QTreeWidget(无论如何保持
..
总是在最前面似乎会造成麻烦)。
- 加载或排序后,以某种方式在 QTreeView 的顶部添加
..
我尝试实现 QSortFilterProxyModel,但 运行 陷入另一个问题:我不明白我应该如何修改 treeView.setRootIndex()
调用。
所以我的具体问题是:
- 我可以使用QSortFilterProxyModel来解决上面提到的所有问题吗?如果是,请提供示例实现。
- 如果您认为有解决此问题的更好方法,请描述。
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
的索引映射
以下解决方案有效:
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)
我想在PySide6上实现文件浏览器,我的目标是:
- 显示文件和文件夹
..
始终在顶部(无论排序如何),以便用户可以双击它并上一级。 - 在
..
之后,我想显示文件夹,然后显示文件(就像 Windows 资源管理器所做的那样),而不考虑排序。 - 有一个显示特定文件集的替代显示模式(它们可以在不同的驱动器、不同的文件夹等)。
我目前正在使用以下代码来初始化模型和视图:
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 并以某种方式强制
..
始终位于最前面,无论排序如何(不确定是否可能)并且还保留目录在前(有一个 related question),我也可能使用上面第 3 点按特定标准显示文件 - 使用完全不同的方法,也许是我手动填写的 QFileSystemWatcher 或 QTreeWidget(无论如何保持
..
总是在最前面似乎会造成麻烦)。 - 加载或排序后,以某种方式在 QTreeView 的顶部添加
..
我尝试实现 QSortFilterProxyModel,但 运行 陷入另一个问题:我不明白我应该如何修改 treeView.setRootIndex()
调用。
所以我的具体问题是:
- 我可以使用QSortFilterProxyModel来解决上面提到的所有问题吗?如果是,请提供示例实现。
- 如果您认为有解决此问题的更好方法,请描述。
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
以下解决方案有效:
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)