Пользовательский прокси-фильтр PyQt QFileDialog не работает

Этот рабочий код вызывает QFileDialog, предлагающий пользователю выбрать файл.csv:

def load(self,fileName=None):
        if not fileName:
            fileName=fileDialog.getOpenFileName(caption="Load Existing Radio Log",filter="csv (*.csv)")[0]
  ...
  ...

Теперь я хотел бы изменить этот фильтр, чтобы быть более избирательным. Программа сохраняет каждый проект в виде набора из трех файлов.csv (project.csv, project_fleetsync.csv, project_clueLog.csv), но я хочу, чтобы диалоговое окно файлов отображало только первый (project.csv), чтобы избежать представления пользователя слишком много вариантов, когда только треть из них может быть обработана остальной функцией load().

Согласно этому посту, похоже, что решение заключается в использовании прокси-модели. Итак, я изменил код следующим образом (все закомментированные строки в load() - это вещи, которые я пробовал в различных комбинациях):

def load(self,fileName=None):
    if not fileName:
        fileDialog=QFileDialog()
        fileDialog.setProxyModel(CSVFileSortFilterProxyModel(self))
#           fileDialog.setNameFilter("CSV (*.csv)")
#           fileDialog.setOption(QFileDialog.DontUseNativeDialog)
#           fileName=fileDialog.getOpenFileName(caption="Load Existing Radio Log",filter="csv (*.csv)")[0]
#           fileName=fileDialog.getOpenFileName(caption="Load Existing Radio Log")[0]
#           fileDialog.exec_()

...
...

# code for CSVFileSortFilterProxyModel partially taken from
#  https://github.com/ZhuangLab/storm-control/blob/master/steve/qtRegexFileDialog.py
class CSVFileSortFilterProxyModel(QSortFilterProxyModel):
    def __init__(self,parent=None):
        print("initializing CSVFileSortFilterProxyModel")
        super(CSVFileSortFilterProxyModel,self).__init__(parent)

    # filterAcceptsRow - return True if row should be included in the model, False otherwise
    #
    # do not list files named *_fleetsync.csv or *_clueLog.csv
    #  do a case-insensitive comparison just in case
    def filterAcceptsRow(self,source_row,source_parent):
        print("CSV filterAcceptsRow called")
        source_model=self.sourceModel()
        index0=source_model.index(source_row,0,source_parent)
        # Always show directories
        if source_model.isDir(index0):
            return True
        # filter files
        filename=source_model.fileName(index0)
#       filename=self.sourceModel().index(row,0,parent).data().lower()
        print("testing lowercased filename:"+filename)
        if filename.count("_fleetsync.csv")+filename.count("_clueLog.csv")==0:
            return True
        else:
            return False

Когда я вызываю функцию load(), я получаю вывод "initializing CSVFileSortFilterProxyModel", но очевидно, что filterAcceptsRow не вызывается: выход "CSV filterAcceptsRow named" отсутствует, а файлы _fleetsync.csv и _clueLog.csv по-прежнему перечислены в диалоговом окне. Очевидно, что я делаю что-то не так...?

1 ответ

Нашел решение в другом вопросе stackru здесь.

Из этого решения:

Главное, на что следует обратить внимание, это вызвать dialog.setOption(QFileDialog::DontUseNativeDialog) перед dialog.setProxyModel.

Кроме того, похоже, что вам нужно использовать fileDialog.exec_(), а не fileDialog.getOpenFileName. Значение, которое вы установили для setNameFilter, действительно отображается в циклическом поле фильтра не встроенного диалогового окна, но эффективно только для украшения, так как фильтр проксимодели переопределяет его. На мой взгляд, это хорошая вещь, поскольку вы можете поместить в фильтр циклическую формулировку, которая будет указывать на что-то полезное для пользователя относительно того, какой тип фильтрации происходит.

Спасибо пользователям Frank и ariwez.

ОБНОВЛЕНИЕ: чтобы уточнить, вот полный окончательный код, который я использую:

def load(self,fileName=None):
    if not fileName:
        fileDialog=QFileDialog()
        fileDialog.setOption(QFileDialog.DontUseNativeDialog)
        fileDialog.setProxyModel(CSVFileSortFilterProxyModel(self))
        fileDialog.setNameFilter("CSV Radio Log Data Files (*.csv)")
        fileDialog.setDirectory(self.firstWorkingDir)
        if fileDialog.exec_():
            fileName=fileDialog.selectedFiles()[0]
        else: # user pressed cancel on the file browser dialog
            return
... (the rest of the load function processes the selected file)
...


# code for CSVFileSortFilterProxyModel partially taken from
#  https://github.com/ZhuangLab/storm-control/blob/master/steve/qtRegexFileDialog.py
class CSVFileSortFilterProxyModel(QSortFilterProxyModel):
    def __init__(self,parent=None):
#       print("initializing CSVFileSortFilterProxyModel")
        super(CSVFileSortFilterProxyModel,self).__init__(parent)

    # filterAcceptsRow - return True if row should be included in the model, False otherwise
    #
    # do not list files named *_fleetsync.csv or *_clueLog.csv
    #  do a case-insensitive comparison just in case
    def filterAcceptsRow(self,source_row,source_parent):
#       print("CSV filterAcceptsRow called")
        source_model=self.sourceModel()
        index0=source_model.index(source_row,0,source_parent)
        # Always show directories
        if source_model.isDir(index0):
            return True
        # filter files
        filename=source_model.fileName(index0).lower()
#       print("testing lowercased filename:"+filename)
        # never show non- .csv files
        if filename.count(".csv")<1:
            return False
        if filename.count("_fleetsync.csv")+filename.count("_cluelog.csv")==0:
            return True
        else:
            return False
Другие вопросы по тегам