QFileDialog - PyQt4/PyQt5/PySide之间的差异

5
我一直在阅读Summerfields关于Python和QT(更确切地说是PyQt)快速GUI编程的书籍,但这本2007年的书使用的是4.x版本,而我正在尝试使用当前版本(5.4.2)。
有一些变化我正在努力弄清楚,希望能得到一些帮助来查找相关信息。以下是一个文件保存对话框的示例 - 来自该书:
    fname = QFileDialog.getSaveFileName(self,
            "Image Changer - Save Image", fname,
            "Image files ({})".format(" ".join(formats)))

这段代码无法正常工作,可能主要是因为在PyQt5中QFileDialog返回的是一个元组而不是一个字符串。我只能通过尝试错误来弄清楚这一点。PyQt5文档引用了QT,但我实在不理解。

我找到了以下可行的方法:

   fname = QFileDialog.getSaveFileName(self, 'some text',
            "whatever.png", '*.png')
   if "." not in fname[0]:
       fname[0] += ".png"
       self.addRecentFile(fname[0])
       self.filename = fname[0]
       return self.fileSave()

哇,它奏效了!但是只有通过不断努力才能取得进步。我尝试运行Python解释器并输入:

from PyQt5.QtWidgets import  QFileDialog

help(QFileDialog)

这有点有用,但是帮助的语法对我来说不太合理,我也看不出getSaveFileName应该返回什么。我错过了什么?

这可能会对你有所帮助:阅读Qt文档。但对于信号和槽(函数connect),它已经不是最新的了。 - Mel
@tmoreau。这似乎来自一本2001年编写的书籍 - 适用于PyQt3。 - ekhumoro
3个回答

10

QFileDialog 的一些静态函数在 PyQt 中有着奇怪的历史。如果您不了解这个历史,就很难理解各个版本 PyQt 之间的差异。

根本问题非常简单。在 Python 中,如果一个函数需要返回多个值,最常见的解决方案是返回元组。但在 C++ 中,这实际上并不可行,因此通常的解决方案是提供可以修改的参数。

QFileDialog.getSaveFileName 的 C++ 签名如下:

getSaveFileName(
    QWidget * parent = 0, const QString & caption = String(),
    const QString & dir = QString(), const QString & filter = QString(),
    QString * selectedFilter = 0, Options options = 0)

正如您所看到的,这四个 QString 参数并不相同。前三个是 const 的,因此函数不会对其进行修改,但是 selectedFilter 参数接受指向 QString 的指针,这意味着它可以被修改。

最初,PyQt 的主要用途是 C++ 原型设计(而不是开发 Python 应用程序),因此其 API 更忠实于 Qt API。这意味着,在 PyQt-4.6 之前,从 QFileDialog 获取选定的过滤器的唯一方法是按照 C++ 的方式执行,像这样:

>>> s = QString() # string to be modified
>>> f = QFileDialog.getSaveFileName(None, 'Save', '', 'Img(*.png *.jpg)', s)
>>> print s
Img(*.png *.jpg)

实际上,在目前版本的PyQt4中,这仍然适用(当然需要启用QString)。

随着时间的推移,PyQt4不断引入了许多改变,逐渐使其更加友好于Python语言。但正如上面的例子所示,所有这些改变都是在不破坏向后兼容性的情况下完成的。当时,将getSaveFileName的签名更改为返回元组将导致远远超过可接受的错误,因此像getSaveFileNameAndFilter这样的函数被添加为临时妥协。

PyQt5没有这样的限制(它甚至不再需要提供QString)。因此,现在终于可以从Python的角度做正确的事情,并从getSaveFileName中返回一个元组。现在,这个原则也适用于一般情况:如果你正在使用PyQt5,并在Qt文档中看到修改其参数的函数,你总是可以期望返回一个元组。


PS:使用比PyQt年轻得多的PySide的用户从来没有遇到过这些问题。对于他们来说,静态的QFileDialog函数总是做正确的事情。


我简直不敢相信这个帖子从未得到任何点赞 - 它完美地解释了正在发生的事情,并为我提供了如何解释文档的指导。谢谢!如果你没有历史记录并且去官方PyQt5文档参考,所有在QFileDialog中的内容都只是一个指向C++文档的指针(当然只返回一个QString),这真是令人恼火。注意可能修改的参数将是关键! - Ajean

1
这些QFileDialog方法似乎有点特殊,因为PyQt实现了自己的方法而不是直接包装Qt方法。
首先,PyQt5的QFileDialog.getSaveFileName()方法实现了PyQt4的QFileDialog.getSaveFileNameAndFilter()方法的行为(source)。其次,在PyQt4中,QFileDialog.getSaveFileNameAndFilter()方法返回一个元组(filename, selectedFilter)source)。
供参考,PyQt4 QFileDialog.getSaveFileNameAndFilter()方法的调用签名为:
getSaveFileNameAndFilter (QWidget parent = None, QString caption = QString(), 
                          QString directory = QString(), QString filter = QString(), 
                          QString initialFilter = QString(), Options options = 0)

希望这能解决任何困惑。大多数PyQt5类/方法不会像这样令人困惑!


0
使用此语法(添加逗号和下划线),可以绕过使用PyQt5的getSaveFileName方法的“元组问题”:
fname, _ = QFileDialog.getSaveFileName(self,
        "Image Changer - Save Image", fname,
        "Image files ({})".format(" ".join(formats)))

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接