Reputation: 1113
I am trying to create a file picker dialog that uses checkboxes to allow multiple file to be selected. Additionally, I would like to dialog to start with some items already selected. I have found some examples online that use QTreeView
and a subclass of QFileSystemModel
to do this. How can I connect my list of file paths to be initially checked with the QModelIndex
items in the model describing those paths once the dialog is fully rendered?
The examples I looked at keep a list of which QModelIndex
items should be checked and override some methods of the QFileSystemModel
subclass like the data()
method to update this list when items are checked or unchecked. To support having some items checked when the dialog is first created, I tried to use a second list of file paths that should be pre-checked. In the data()
method of my QFileSystemModel
subclass, I check the file path of the QModelIndex
input and, if it is in my the pre-checked list, I remove the file path from the pre-checked list and put that QModelIndex
into the other list of checked items. The problem I am having is that the model seems to regenerate itself several times during creation. The first time data()
is called for an index with a certain path that path is removed from the pre-checked list and the QModelIndex
added to the other list. However, once the dialog is fully displayed, the QModelIndex
for that file path is a different instance from the one that was put into the pre-checked list and so my code doesn't know that it should be checked.
Here is an example to illustrate what I mean:
import os
import sys
from PyQt5 import QtWidgets
all_entries = list()
class MyFileSystemModel(QtWidgets.QFileSystemModel):
def data(self, index, role):
if index not in all_entries:
all_entries.append(index)
return super().data(index, role)
class Ui_Dialog(QtWidgets.QDialog):
def __init__(self, parent=None):
QtWidgets.QDialog.__init__(self, parent)
self.model = MyFileSystemModel()
self.model.setRootPath(os.path.abspath('.'))
self.tree = QtWidgets.QTreeView()
self.tree.setModel(self.model)
self.tree.setRootIndex(self.model.index(os.path.abspath('.')))
self.llayout = QtWidgets.QVBoxLayout(parent)
self.but = QtWidgets.QPushButton("OK")
self.llayout.addWidget(self.tree)
self.llayout.addWidget(self.but)
self.setLayout(self.llayout)
self.but.clicked.connect(self.print_entries)
def print_entries(self):
print('*'*80)
for index in all_entries:
print(index, os.path.relpath(self.model.filePath(index),
os.path.abspath('.')))
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
ui = Ui_Dialog()
ui.show()
sys.exit(app.exec_())
Running this script in a directory with a single file named test
and then clicking the OK
button, I see
********************************************************************************
<PyQt5.QtCore.QModelIndex object at 0x7ff46b991f28> test
<PyQt5.QtCore.QModelIndex object at 0x7ff46b991f98> test
<PyQt5.QtCore.QModelIndex object at 0x7ff46b910048> test
<PyQt5.QtCore.QModelIndex object at 0x7ff46b9100b8> test
So all_entries
ends up with four different QModelIndex
items for the test
file path. In my more involved checkable dialog, I would end up with the QModelIndex
instance at 0x7ff46b991f28
in list of checked items but it seems like once the dialog is displayed I need to be using the instance at 0x7ff46b9100b8
. I could handle everything in my own list of paths and not use QModelIndex
objects, but the advantage of using them is that QFileSystemModel
uses a file system watcher to keep track of file system changes and I would like to take advantage of that to keep track of properties of the selected files without needing to query the file sytem to tell if files have changed.
Upvotes: 0
Views: 800
Reputation: 243993
The problem has the following causes:
Note: Model indexes should be used immediately and then discarded. You should not rely on indexes to remain valid after calling model functions that change the structure of the model or delete items. If you need to keep a model index over time use a QPersistentModelIndex.
Considering both things the code should be the following:
all_entries = list()
class MyFileSystemModel(QtWidgets.QFileSystemModel):
def data(self, index, role):
if index.column() == 0:
if QPersistentModelIndex(index) not in all_entries:
all_entries.append(QPersistentModelIndex(index))
return super().data(index, role)
[...]
def print_entries(self):
print('*'*80)
for index in all_entries:
ix = QModelIndex(index) # get QModelIndex from QPersistentModelIndex
print(index, os.path.relpath(self.model.filePath(ix),os.path.abspath('.')))
Upvotes: 2