在 QFileDialog 中覆盖 filterAcceptsRow 时结果不一致
Inconsistent results when overriding filterAcceptsRow in QFileDialog
我正在尝试创建一个 QFileDialog
过滤文件扩展名和文件名。可以通过 setNameFilter()
过滤文件扩展名,但名称过滤(据我所知)只能通过创建自定义对话框和代理模型然后覆盖 filterAcceptsRow()
函数来过滤。
当我希望文件显示在对话框中时,我应该 returning True
,而当我不希望文件出现在对话框中时,我应该 False
。问题是,当我 return 基于自定义检查时,我没有得到任何结果。但是当我总是 return True
时(即使我执行检查并根据它打印出来),我会看到所有预期的文件。如果不是打印输出,我会认为我创建的模式不正确,但事实并非如此。打开此对话框的每个按钮都会为 file_part
发送不同的值,因此我无法对模式进行硬编码。
我已经用一个玩具示例替换了我对示例代码的第一次尝试,希望能展示问题。在我的测试环境中,C:\temp\
包含以下文件:
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_()
目前所有文件都通过过滤器,而我希望只显示 0jjj_record2_part1.txt
和 0jjj_record2_part2.txt
。
问题是您过滤了所有内容,包括将要显示的目录。如果当前选择的路径与正则表达式不匹配,则不会接受该路径的索引,结果将是一个空文件对话框(因为该路径“不存在”)。
一个可能的解决方案是检查索引是否是一个目录:
def filterAcceptsRow(self, source_row, srcidx):
model = self.sourceModel()
index0 = model.index(source_row, 0, srcidx)
if model.isDir(index0):
return True
# ...
这也将解决当前目录中的文件夹不可见,从而阻止用户浏览的问题。
如果出于某种原因您不想这样做,您可以检查对话框的当前目录是否与索引的父目录相匹配:
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
但考虑到这会导致一些问题:如果用户尝试访问父目录,则从那里看不到子目录。
问题是,如果您过滤文件夹中的第 i 个文件,则 srcidx 将指向该目录,因此 isDir()
在过滤文件时将始终 return 为真。解决办法是改成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
我正在尝试创建一个 QFileDialog
过滤文件扩展名和文件名。可以通过 setNameFilter()
过滤文件扩展名,但名称过滤(据我所知)只能通过创建自定义对话框和代理模型然后覆盖 filterAcceptsRow()
函数来过滤。
当我希望文件显示在对话框中时,我应该 returning True
,而当我不希望文件出现在对话框中时,我应该 False
。问题是,当我 return 基于自定义检查时,我没有得到任何结果。但是当我总是 return True
时(即使我执行检查并根据它打印出来),我会看到所有预期的文件。如果不是打印输出,我会认为我创建的模式不正确,但事实并非如此。打开此对话框的每个按钮都会为 file_part
发送不同的值,因此我无法对模式进行硬编码。
我已经用一个玩具示例替换了我对示例代码的第一次尝试,希望能展示问题。在我的测试环境中,C:\temp\
包含以下文件:
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_()
目前所有文件都通过过滤器,而我希望只显示 0jjj_record2_part1.txt
和 0jjj_record2_part2.txt
。
问题是您过滤了所有内容,包括将要显示的目录。如果当前选择的路径与正则表达式不匹配,则不会接受该路径的索引,结果将是一个空文件对话框(因为该路径“不存在”)。
一个可能的解决方案是检查索引是否是一个目录:
def filterAcceptsRow(self, source_row, srcidx):
model = self.sourceModel()
index0 = model.index(source_row, 0, srcidx)
if model.isDir(index0):
return True
# ...
这也将解决当前目录中的文件夹不可见,从而阻止用户浏览的问题。
如果出于某种原因您不想这样做,您可以检查对话框的当前目录是否与索引的父目录相匹配:
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
但考虑到这会导致一些问题:如果用户尝试访问父目录,则从那里看不到子目录。
问题是,如果您过滤文件夹中的第 i 个文件,则 srcidx 将指向该目录,因此 isDir()
在过滤文件时将始终 return 为真。解决办法是改成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