将 sourceModel() 用于 fileInfo() 时应用程序突然终止
Sudden app termination when using `sourceModel()` for `fileInfo()`
一些上下文:
我制作了一个QTreeView
并设置了QFileSystemModel
作为模型。有 2 个 TreeView,右边一个对应目录下的项目文件列表,左边一个用于查看该项目文件的连接文件。在帮助下,我实现了这种行为,当我单击右侧的项目文件时,其连接的文件将出现在左侧。 (我删除了存储和连接文件的机制,因为它会添加大量代码)
问题:
在添加了上面的2个TreeView之后,我又添加了一个context menu,只有一个选项,就是删除选中的文件。我认为我在左侧的第一次尝试是成功的,因为我可以看到打印在控制台中的路径。现在问题出在我试图在右侧进行时。知道这边的模型是 QSortFilterProxyModel
的子类,然后我添加了 sourceModel()
,这样它将 return QFileSystemModel
。但是当我尝试 运行 文件时,它只是给了我一个错误。你可以在下面看到我的实现。
def deleteFile(self, event):
index_list = self.tree_sender.selectedIndexes()
for index in index_list:
if index.column() == 0:
if isinstance(self.tree_sender.model(), ModifiedQSortFilterProxyModel)
fileIn = self.tree_sender.model().sourceModel().fileInfo(index)
else:
fileIn = self.tree_sender.model().fileInfo(index)
# method above is also not working even I add sourceModel()
# It just ends the app imediately
path_to_delete = fileIn.absoluteFilePath()
print(path_to_delete)
我的测试代码:
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QTreeView, QFileSystemModel, QHBoxLayout, \
QVBoxLayout, QPushButton, QListWidget, QListWidgetItem, QMenu, QAction, QAbstractItemView
from PyQt5.QtCore import QSortFilterProxyModel, Qt
from PyQt5.QtGui import QCursor
class ModifiedQSortFilterProxyModel(QSortFilterProxyModel):
fileInfo = None
con_files = None
def filterAcceptsRow(self, source_row, source_parent):
if not self.fileInfo:
return True
source_index = self.sourceModel().index(source_row, 0, source_parent)
info = self.sourceModel().fileInfo(source_index)
if self.fileInfo.absolutePath() != info.absolutePath():
return True
return info.fileName() in self.con_files
def setFilter(self, info, connected_files):
self.fileInfo = info
self.con_files = connected_files
self.invalidateFilter()
class FileSystemView(QWidget):
def __init__(self):
super().__init__()
appWidth = 800
appHeight = 300
self.setWindowTitle('File System Viewer')
self.setGeometry(300, 300, appWidth, appHeight)
dir_path = r'<your directory>'
# -- left -- #
self.model = QFileSystemModel()
self.model.setRootPath(dir_path)
self.tree = QTreeView()
self.tree.setModel(self.model)
self.tree.setRootIndex(self.model.index(dir_path))
self.tree.setColumnWidth(0, 250)
self.tree.setAlternatingRowColors(True)
self.tree.setSelectionMode(QAbstractItemView.ExtendedSelection)
self.tree.clicked.connect(self.onClicked)
self.tree.setContextMenuPolicy(Qt.CustomContextMenu)
self.tree.customContextMenuRequested.connect(self.context_menu)
# -- right -- #
self.model2 = QFileSystemModel()
self.model2.setRootPath(dir_path)
self.model2.setReadOnly(False)
self.filter_proxy_model = ModifiedQSortFilterProxyModel()
self.filter_proxy_model.setSourceModel(self.model2)
self.filter_proxy_model.setFilterCaseSensitivity(Qt.CaseInsensitive)
self.filter_proxy_model.setDynamicSortFilter(True)
self.filter_proxy_model.setFilterKeyColumn(0)
root_index = self.model2.index(dir_path)
proxy_index = self.filter_proxy_model.mapFromSource(root_index)
self.tree2 = QTreeView()
self.tree2.setModel(self.filter_proxy_model)
self.tree2.setRootIndex(proxy_index)
self.tree2.setColumnWidth(0, 250)
self.tree2.setAlternatingRowColors(True)
self.tree2.setSelectionMode(QAbstractItemView.ExtendedSelection)
self.tree2.setContextMenuPolicy(Qt.CustomContextMenu)
self.tree2.customContextMenuRequested.connect(self.context_menu)
# -- layout -- #
self.tree_layout = QHBoxLayout()
self.tree_layout.addWidget(self.tree)
self.tree_layout.addWidget(self.tree2)
self.setLayout(self.tree_layout)
def context_menu(self, event):
self.tree_sender = self.sender()
# ^^^ I dont know how to access the treeview where context menu
# ^^^ is triggered so I just tried to pass it as a variable
self.menu = QMenu(self)
deleteAction = QAction('Delete', self)
deleteAction.triggered.connect(lambda event: self.deleteFile(event))
self.menu.addAction(deleteAction)
self.menu.popup(QCursor.pos())
def deleteFile(self, event):
index_list = self.tree_sender.selectedIndexes()
for index in index_list:
if index.column() == 0:
if isinstance(self.tree_sender.model(), ModifiedQSortFilterProxyModel):
fileIn = self.tree_sender.model().sourceModel().fileInfo(index)
else:
fileIn = self.tree_sender.model().fileInfo(index)
# method above is also not working even I add sourceModel()
# It just ends the app imediately
path_to_delete = fileIn.absoluteFilePath()
print(path_to_delete)
def onClicked(self, index):
print("onclick")
connected_files = [] # list of connected files
self.filter_proxy_model.setFilter(self.model.fileInfo(index), connected_files)
if __name__ == '__main__':
app = QApplication(sys.argv)
demo = FileSystemView()
demo.show()
sys.exit(app.exec_())
我的问题:是什么原因导致的错误?是 sourceModel()
实施吗?还是我做错了什么?
希望有人能指出正确的方向。
我完全忘了使用 mapToSource
,但幸运的是@musicamante 指导我完成了它。我的问题现在解决了,下面给出deleteFile
方法的最终实现。
def deleteFile(self, event):
index_list = self.tree_sender.selectedIndexes()
for index in index_list:
if index.column() == 0:
model = self.tree_sender.model()
if isinstance(model, ModifiedQSortFilterProxyModel):
proxy_index = model.mapToSource(index)
fileIn = proxy_index.model().fileInfo(proxy_index)
else:
fileIn = model.fileInfo(index)
path_to_delete = fileIn.absoluteFilePath()
print(path_to_delete)
一些上下文:
我制作了一个QTreeView
并设置了QFileSystemModel
作为模型。有 2 个 TreeView,右边一个对应目录下的项目文件列表,左边一个用于查看该项目文件的连接文件。在帮助下,我实现了这种行为,当我单击右侧的项目文件时,其连接的文件将出现在左侧。 (我删除了存储和连接文件的机制,因为它会添加大量代码)
问题:
在添加了上面的2个TreeView之后,我又添加了一个context menu,只有一个选项,就是删除选中的文件。我认为我在左侧的第一次尝试是成功的,因为我可以看到打印在控制台中的路径。现在问题出在我试图在右侧进行时。知道这边的模型是 QSortFilterProxyModel
的子类,然后我添加了 sourceModel()
,这样它将 return QFileSystemModel
。但是当我尝试 运行 文件时,它只是给了我一个错误。你可以在下面看到我的实现。
def deleteFile(self, event):
index_list = self.tree_sender.selectedIndexes()
for index in index_list:
if index.column() == 0:
if isinstance(self.tree_sender.model(), ModifiedQSortFilterProxyModel)
fileIn = self.tree_sender.model().sourceModel().fileInfo(index)
else:
fileIn = self.tree_sender.model().fileInfo(index)
# method above is also not working even I add sourceModel()
# It just ends the app imediately
path_to_delete = fileIn.absoluteFilePath()
print(path_to_delete)
我的测试代码:
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QTreeView, QFileSystemModel, QHBoxLayout, \
QVBoxLayout, QPushButton, QListWidget, QListWidgetItem, QMenu, QAction, QAbstractItemView
from PyQt5.QtCore import QSortFilterProxyModel, Qt
from PyQt5.QtGui import QCursor
class ModifiedQSortFilterProxyModel(QSortFilterProxyModel):
fileInfo = None
con_files = None
def filterAcceptsRow(self, source_row, source_parent):
if not self.fileInfo:
return True
source_index = self.sourceModel().index(source_row, 0, source_parent)
info = self.sourceModel().fileInfo(source_index)
if self.fileInfo.absolutePath() != info.absolutePath():
return True
return info.fileName() in self.con_files
def setFilter(self, info, connected_files):
self.fileInfo = info
self.con_files = connected_files
self.invalidateFilter()
class FileSystemView(QWidget):
def __init__(self):
super().__init__()
appWidth = 800
appHeight = 300
self.setWindowTitle('File System Viewer')
self.setGeometry(300, 300, appWidth, appHeight)
dir_path = r'<your directory>'
# -- left -- #
self.model = QFileSystemModel()
self.model.setRootPath(dir_path)
self.tree = QTreeView()
self.tree.setModel(self.model)
self.tree.setRootIndex(self.model.index(dir_path))
self.tree.setColumnWidth(0, 250)
self.tree.setAlternatingRowColors(True)
self.tree.setSelectionMode(QAbstractItemView.ExtendedSelection)
self.tree.clicked.connect(self.onClicked)
self.tree.setContextMenuPolicy(Qt.CustomContextMenu)
self.tree.customContextMenuRequested.connect(self.context_menu)
# -- right -- #
self.model2 = QFileSystemModel()
self.model2.setRootPath(dir_path)
self.model2.setReadOnly(False)
self.filter_proxy_model = ModifiedQSortFilterProxyModel()
self.filter_proxy_model.setSourceModel(self.model2)
self.filter_proxy_model.setFilterCaseSensitivity(Qt.CaseInsensitive)
self.filter_proxy_model.setDynamicSortFilter(True)
self.filter_proxy_model.setFilterKeyColumn(0)
root_index = self.model2.index(dir_path)
proxy_index = self.filter_proxy_model.mapFromSource(root_index)
self.tree2 = QTreeView()
self.tree2.setModel(self.filter_proxy_model)
self.tree2.setRootIndex(proxy_index)
self.tree2.setColumnWidth(0, 250)
self.tree2.setAlternatingRowColors(True)
self.tree2.setSelectionMode(QAbstractItemView.ExtendedSelection)
self.tree2.setContextMenuPolicy(Qt.CustomContextMenu)
self.tree2.customContextMenuRequested.connect(self.context_menu)
# -- layout -- #
self.tree_layout = QHBoxLayout()
self.tree_layout.addWidget(self.tree)
self.tree_layout.addWidget(self.tree2)
self.setLayout(self.tree_layout)
def context_menu(self, event):
self.tree_sender = self.sender()
# ^^^ I dont know how to access the treeview where context menu
# ^^^ is triggered so I just tried to pass it as a variable
self.menu = QMenu(self)
deleteAction = QAction('Delete', self)
deleteAction.triggered.connect(lambda event: self.deleteFile(event))
self.menu.addAction(deleteAction)
self.menu.popup(QCursor.pos())
def deleteFile(self, event):
index_list = self.tree_sender.selectedIndexes()
for index in index_list:
if index.column() == 0:
if isinstance(self.tree_sender.model(), ModifiedQSortFilterProxyModel):
fileIn = self.tree_sender.model().sourceModel().fileInfo(index)
else:
fileIn = self.tree_sender.model().fileInfo(index)
# method above is also not working even I add sourceModel()
# It just ends the app imediately
path_to_delete = fileIn.absoluteFilePath()
print(path_to_delete)
def onClicked(self, index):
print("onclick")
connected_files = [] # list of connected files
self.filter_proxy_model.setFilter(self.model.fileInfo(index), connected_files)
if __name__ == '__main__':
app = QApplication(sys.argv)
demo = FileSystemView()
demo.show()
sys.exit(app.exec_())
我的问题:是什么原因导致的错误?是 sourceModel()
实施吗?还是我做错了什么?
希望有人能指出正确的方向。
我完全忘了使用 mapToSource
,但幸运的是@musicamante 指导我完成了它。我的问题现在解决了,下面给出deleteFile
方法的最终实现。
def deleteFile(self, event):
index_list = self.tree_sender.selectedIndexes()
for index in index_list:
if index.column() == 0:
model = self.tree_sender.model()
if isinstance(model, ModifiedQSortFilterProxyModel):
proxy_index = model.mapToSource(index)
fileIn = proxy_index.model().fileInfo(proxy_index)
else:
fileIn = model.fileInfo(index)
path_to_delete = fileIn.absoluteFilePath()
print(path_to_delete)