Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

QFileDialog - differences between PyQt4/PyQt5/PySide

I have been working my way through Summerfields book on Rapid GUI programming with Python and QT (PyQt to be more precise), but the book from 2007 uses version 4.x and I am trying to get going with the current version (5.4.2).

There are some changes that I am trying to figure out and would love some assistance on how to find stuff. Here is an example for a file save dialog - from the book:

    fname = QFileDialog.getSaveFileName(self,
            "Image Changer - Save Image", fname,
            "Image files ({})".format(" ".join(formats)))

This does not work, perhaps primarily because in PyQt5 the QFileDialog returns a tuple rather than a string. The only way I can figure this out is just by trial and error. The PyQt5 documentation refers you to QT, which I really do not understand.

I got the following to work:

   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()

Wow, it works! But it is just by slogging through that I get any progress. I tried running python interpreter and typed:

from PyQt5.QtWidgets import  QFileDialog

help(QFileDialog)

This is (sort of) helpful, but the syntax of the help does not make a lot of sense to me, and I do not see what getSaveFileName is supposed to return.

What am I missing?

like image 737
Keith Miller Avatar asked Jun 19 '15 05:06

Keith Miller


People also ask

What is the difference between PyQt4 and PyQt5?

PyQt4 supports a number of different API versions ( QString , QVariant etc.). With the exception of QVariant , PyQt5 only implements v2 of those APIs for all versions of Python. The changed support for QVariant , including the removal of QPyNullVariant , is described in Support for QVariant.

What's the difference between PyQt and PySide?

The key difference in the two versions — in fact the entire reason PySide2 exists — is licensing. PyQt5 is available under a GPL or commercial license, and PySide2 under a LGPL license.

Is PyQt4 compatible with PyQt5?

PyQt version 4 works with both Qt 4 and Qt 5. PyQt version 5 only supports Qt version 5, and drops support for features that are deprecated in Qt 5.

What is the difference between PyQt5 and PyQt6?

Conclusion. As we've discovered, there are no major differences between PyQt5 and PyQt6. The changes that are there can be easily worked around. If you are new to Python GUI programming with Qt you may find it easier to start with PyQt5 still, but for any new project I'd suggest starting with PyQt6.


2 Answers

Some of the static functions of QFileDialog have an odd history in PyQt. If you don't know this history, it's hard to make sense of the differences between the various versions of PyQt.

The underlying issue is quite simple. In Python, if a function needs to return more than one value, the most common solution is to return a tuple. But in C++, this is not really possible, so the usual solution is to instead provide arguments that can be modified.

The C++ signature of QFileDialog.getSaveFileName is this:

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

As you can see, the four QString arguments aren't all the same. The first three are const, and so won't be modifed by the function, but the selectedFilter argument takes a pointer to a QString, which means it can be.

Originally, the main use of PyQt was for C++ proto-typing (rather than developing Python applications), and so its APIs were much more faithful to the Qt APIs. This meant that, up until PyQt-4.6, the only way to get the selected filter from QFileDialog, was to do it the C++ way, like this:

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

And in fact, this still works in current versions of PyQt4 (providing it has QString enabled, of course).

PyQt4 steadily introduced a lot of changes that have gradually made it more and more Python-friendly over the years - but as the above example shows, this was all done without breaking backwards-compatibility. At the time, changing the signature of getSaveFileName to return a tuple would have caused far too much breakage, and so functions like getSaveFileNameAndFilter were added as a temporary compromise instead.

PyQt5 does not have such restrictions (it doesn't even need to provide QString anymore). So it has finally become possible to do the right thing (from a Python point of view) and just return a tuple from getSaveFileName. And this principle now applies in general: if you're using PyQt5, and you see a function in the Qt docs that modifies its arguments, you can always expect a tuple to be returned instead.


(PS: users of PySide - which is much younger than PyQt - have never had to deal with these issues. For them, the static QFileDialog functions have always done the right thing).

like image 174
ekhumoro Avatar answered Oct 09 '22 08:10

ekhumoro


These methods of QFileDialog appear to be a bit special because PyQt have implemented their own methods rather than directly wrapping the Qt methods.

Firstly, the PyQt5 QFileDialog.getSaveFileName() method implements the behaviour of the QFileDialog.getSaveFileNameAndFilter() method from PyQt4 (source). Secondly, the QFileDialog.getSaveFileNameAndFilter() method in PyQt4 returns a tuple of (filename, selectedFilter) (source).

For reference, the calling signature of the PyQt4 QFileDialog.getSaveFileNameAndFilter() method is

getSaveFileNameAndFilter (QWidget parent = None, QString caption = QString(), 
                          QString directory = QString(), QString filter = QString(), 
                          QString initialFilter = QString(), Options options = 0)

Hopefully this helps resolve any confusion. Most PyQt5 classes/methods will not be this confusing to decode!

like image 38
three_pineapples Avatar answered Oct 09 '22 09:10

three_pineapples