在 QFileDialog 中覆盖 filterAcceptsRow 时结果不一致

Inconsistent results when overriding filterAcceptsRow in QFileDialog

我正在尝试创建一个 QFileDialog 过滤文件扩展名和文件名。可以通过 setNameFilter() 过滤文件扩展名,但名称过滤(据我所知)只能通过创建自定义对话框和代理模型然后覆盖 filterAcceptsRow() 函数来过滤。

当我希望文件显示在对话框中时,我应该 returning True,而当我不希望文件出现在对话框中时,我应该 False。问题是,当我 return 基于自定义检查时,我没有得到任何结果。但是当我总是 return True 时(即使我执行检查并根据它打印出来),我会看到所有预期的文件。如果不是打印输出,我会认为我创建的模式不正确,但事实并非如此。打开此对话框的每个按钮都会为 file_part 发送不同的值,因此我无法对模式进行硬编码。

我已经用一个玩具示例替换了我对示例代码的第一次尝试,希望能展示问题。在我的测试环境中,C:\temp\ 包含以下文件:

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.txt0jjj_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