Some context:
I made a QTreeView
and set QFileSystemModel
as its model. There are 2 TreeViews, the one on the right corresponds in the list of the project files in a directory, and the left one serves for the view of the connected files for that project file. With help, I've implemented this behavior where when I click on a project file on the right side, its connected files will appear on the left side. (I removed the mechanism of storing and the connected files as it will add a ton of code)
The problem:
After adding the 2 TreeViews above, I then added a context menu with only one option, which is to delete the selected files. I think my first attempt which is on the left side is successful because I can see the path printed in the console. Now the problem lies when I tried to do it on the right side. Knowing that the model in this side is subclassed from the QSortFilterProxyModel
, I then added the sourceModel()
so that it will return the QFileSystemModel
. But when I tried to run the file, It just gives me an error. You can see my implementation below.
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)
My Testing Code:
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_())
My Question: What causes the error? is it sourceModel()
implementation? or I did something very wrong?
I hope someone can point me out to the right direction.
I totally forgot to use
mapToSource
, but thankfully @musicamante guided me through it. My question is now solved, and the final implementation of thedeleteFile
method is given below.