Reputation: 23
I'm trying to create a QFileDialog
with filtering on the file extension and file name. The file extension can be filtered via setNameFilter()
, but name filtering (as far as I know) can only be filtered by creating a custom dialog and proxy model and then overriding the filterAcceptsRow()
function.
I should be returning True
when I want a file to show up in the dialog and False
when I don't. The trouble is that when I return based on the custom checking I get no results. But when I return True
always (even when I execute the check and print out based on it), I see all of the expected files. If it weren't for the printout I'd think I'm creating the pattern improperly, but that's not the case. And each button that opens this dialog sends a different value for file_part
so I can't hard-code the pattern.
I have replaced my first attempt at example code with a toy example that hopefully showcases the problem. In my test environment, C:\temp\
contains the following files:
0bbb_record2_part1.txt
0bbb_record2_part2.txt
0jjj_record1_part1.txt
0jjj_record1_part2.txt
0jjj_record2_part1.txt
0jjj_record2_part2.txt
0jjj_record3_part1.txt
0jjj_record2_part2.txt
import sys
import re
from PyQt5 import Qt, QtCore, QtGui, QtWidgets
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
class FileFilterProxyModel(QtCore.QSortFilterProxyModel):
def __init__(self, file_part, parent = None):
super(FileFilterProxyModel, self).__init__(parent)
self.__pattern = re.compile(f'0{file_part}_record2')
def filterAcceptsRow(self, source_row, srcidx):
model = self.sourceModel()
index0 = model.index(source_row, 0, srcidx)
file_name = model.fileName(index0)
if model.isDir(srcidx):
return True
return self.__pattern.search(file_name.lower()) != None
class MyFilePicker(QFileDialog):
def __init__(self, file_part):
super().__init__()
self.setWindowTitle('Filtered File Picker')
self.setOption(QFileDialog.DontUseNativeDialog)
self.setNameFilter('Record 2 Files (*.txt)')
self.setDirectory('C:\\temp\\')
self.setFileMode(QFileDialog.ExistingFile)
self.setAcceptMode(QFileDialog.AcceptOpen)
self.setProxyModel(FileFilterProxyModel(file_part, self))
if __name__ == '__main__':
app = QApplication(sys.argv)
fp = MyFilePicker('jjj')
fp.show()
fp.exec_()
Currently all files are passing the filter while I would expect only 0jjj_record2_part1.txt
and 0jjj_record2_part2.txt
to be displayed.
Upvotes: 0
Views: 228
Reputation: 48270
The problem is that you're filtering everything, including the directory that is going to be shown. If the current selected path doesn't match the regex, then the index for that path won't be accepted, and the result would be an empty file dialog (since that path "doesn't exist").
A possible solution is to check that if the index is a directory:
def filterAcceptsRow(self, source_row, srcidx):
model = self.sourceModel()
index0 = model.index(source_row, 0, srcidx)
if model.isDir(index0):
return True
# ...
This will also solve the issue of folders inside the current directory, which wouldn't be visible, thus preventing the user to browse them.
If, for some reason, you don't want that, you can check whether the current directory of the dialog matches the parent of the index:
class FileFilterProxyModel(QtCore.QSortFilterProxyModel):
def __init__(self, file_part, dialog):
super(FileFilterProxyModel, self).__init__(dialog)
self.__pattern = re.compile(f'^0{file_part}_')
self.dialog = dialog
def filterAcceptsRow(self, source_row, srcidx):
model = self.sourceModel()
if self.dialog.directory().absolutePath() != model.filePath(srcidx):
return True
index0 = model.index(source_row, 0, srcidx)
file_name = model.fileName(index0)
return self.__pattern.search(file_name.lower()) != None
But consider that this would cause some issues: if the user tries to access the parent directory, the child directory won't be visible from there.
Upvotes: 0
Reputation: 243983
The problem is that if you are filtering the i-th file in the folder then srcidx will point to the directory so isDir()
will always return True when filtering files. The solution is to change with index0:
def filterAcceptsRow(self, source_row, srcidx):
model = self.sourceModel()
index0 = model.index(source_row, 0, srcidx)
file_name = model.fileName(index0)
if model.isDir(index0):
return True
return self.__pattern.search(file_name.lower()) is not None
Upvotes: 2