Sudden app termination when using `sourceModel()` for `fileInfo()`

61 views Asked by At

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)

enter image description here

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.

1

There are 1 answers

0
Eliazar On BEST ANSWER

I totally forgot to use mapToSource, but thankfully @musicamante guided me through it. My question is now solved, and the final implementation of the deleteFile method is given below.

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)