【问题标题】:PyQt QFileDialog custom proxy filter not workingPyQt QFileDialog 自定义代理过滤器不起作用
【发布时间】:2021-02-15 05:41:14
【问题描述】:

这个工作代码会弹出一个 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() 函数的其余部分只能处理其中的三分之一。

根据this 的帖子,看起来解决方案是使用代理模型。因此,我将代码更改为以下内容(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() 函数时,我确实得到了“正在初始化 CSVFileSortFilterProxyModel”输出,但显然 filterAcceptsRow 没有被调用:没有“CSV filterAcceptsRow 调用”输出,以及 _fleetsync.csv 和 _clueLog.csv文件仍然在对话框中列出。显然我做错了什么......?

【问题讨论】:

    标签: python proxy pyqt


    【解决方案1】:

    在另一个stackoverflow问题here找到了解决方案。

    从那个解决方案:

    要注意的主要事情是打电话 dialog.setOption(QFileDialog::DontUseNativeDialog) 之前 dialog.setProxyModel.

    而且看起来您必须使用 fileDialog.exec_() 而不是 fileDialog.getOpenFileName。您设置为 setNameFilter 的值确实显示在非本机对话框的过滤器循环字段中,但实际上只是为了装饰,因为 proxymodel 过滤器会覆盖它。在我看来,这是一件好事,因为您可以在过滤器循环中添加一些措辞,以指示对用户有用的内容,了解正在进行的过滤类型。

    感谢用户 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
    

    【讨论】:

    • 非常感谢那个。您可以在filterAcceptsRow 的开头使用if not filterAcceptsRow(self,source_row,source_parent): return False 来保留仅包含CSV。或者使用fnmatch.fnmatch(filename, "*.csv") 重新实现通配符匹配。另外,你为什么要filename.count(".csv")&lt;1 而不仅仅是".csv" in filename
    • 很高兴它有帮助。已经有一段时间了 - 没有详细查看它,是的,我同意可能有很多方法可以实现相同的目标,而我选择的方法不一定是最短的。
    猜你喜欢
    • 2017-08-16
    • 2012-08-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-29
    • 2017-07-25
    • 2015-03-01
    相关资源
    最近更新 更多