The PySide6 QDialog.exec()
docs state to avoid using exec()
:
Avoid using this function; instead, use
open()
. Unlike , open() is asynchronous, and does not spin an additional event loop. This prevents a series of dangerous bugs from happening (e.g. deleting the dialog’s parent while the dialog is open via ). When using open() you can connect to the finished() signal of QDialog to be notified when the dialog is closed.
open()
is a virtual function, but I don't believe it is pure virtual since I can call it directly on any subclass to immediately open the dialog.
However, QFileDialog.open(receiver, member)
is a bit of a mystery. It connects either the filesSelected()
or fileSelected()
signal (depending on the fileMode
) to
a slot specified by
receiver
andmember
and
The signal will be disconnected from the slot when the dialog is closed.
Considering the above, is the correct (i.e. recommended) way to use QFileDialog
like so:
from qtpy import QtCore, QtWidgets
class MyWindow(QtWidgets.QMainWindow):
def __init__(self) -> None:
QtWidgets.QMainWindow.__init__(self)
self.dialog = QtWidgets.QFileDialog(self)
self.dialog.setFileMode(QtWidgets.QFileDialog.Directory)
self.dialog.setWindowTitle('Open folder...')
self.dialog.finished.connect(self.on_finished)
@QtCore.Slot(QtWidgets.QDialog.DialogCode)
def on_finished(
self,
result: QtWidgets.QDialog.DialogCode,
) -> None:
if result == QtWidgets.QDialog.Accepted:
print('Accepted')
else: # QtWidgets.QDialog.Rejected
print('Rejected')
if __name__ == '__main__':
app = QtWidgets.QApplication([])
window = MyWindow()
window.show()
window.dialog.open()
app.exec()
or is QFileDialog.open(receiver, member)
supposed to be used? If so, how does one use receiver
and member
?
NOTE: I'm aware the slot decorator isn't strictly necessary in PySide6, but I add it since it allows me to see at a glance which of my methods are slots vs. just methods.
TL;DR: exec()
, open()
, and static functions can all be used. Which one you choose depends on your use case, which primarily has to do with whether the dialog is application or window modal. exec()
is application modal and open()
window modal, the former being subject to bugs if the dialog is able to delete its parent while the dialog is open. To use receiver
and member
requires old-style signal/slot syntax, which is demonstrated below.
QFileDialog
?Per @musicamante's comment, there is no "correct" way and using either exec()
or the static functions are acceptable. For example, the PySide6 QFileDialog docs state
The easiest way to create a
QFileDialog
is to use the static functions.
and then again, the PySide6 docs also state
The most common way to display a modal dialog is to call its
exec()
function.
The docs include examples that use exec()
, and in fact, if you review the QFileDialog C++ source code, you will see that most of these static methods ultimately call exec()
.
Hence, how QFileDialog
is used depends on one's needs:
DontUseNativeDialog
, in which case a QFileDialog
is returned:By default, a platform-native file dialog will be used if the platform has one. In that case, the widgets which would otherwise be used to construct the dialog will not be instantiated, so related accessors such as
layout()
and itemDelegate() will return null. Also, not all platforms show file dialogs with a title bar, so be aware that the caption text might not be visible to the user. You can set theDontUseNativeDialog
option to ensure that the widget-based implementation will be used instead of the native dialog.
parent
widget during execution of the dialog. On Windows, the static functions spin a blocking modal event loop that does not dispatch QTimers. Quite literally, open()
behaves in a window modal fashion, and the docs corroborate this.Thus, I argue the most important functional characteristic to consider when choosing whether to use exec()
or open()
is the modality of the dialog and whether or not widgets can be deleted/closed when it is open.
open()
: How Does One Use receiver
and member
?As mentioned in the question, the wording of the QFileDialog.open
docs is confusing:
The specific signal depends is filesSelected() if fileMode is
ExistingFiles
and fileSelected() if fileMode is anything else.
This is saying receiver
is passed either a list of files or a single file (depending on the dialog file mode), but the receiver
/member
nomenclature may feel odd to those of us who do not recall the old-style signal/slot syntax, which more closely mirrors how signals and slots are connected in C++ (see C++ GUI Programming with Qt4, Ch. 2, section "Signals and Slots in Depth"):
connect(sender, SIGNAL(signal), receiver, SLOT(slot));
Indeed, all QFileDialog.open()
does is
receiver
and member
,QDialog.open()
Hence, using QFileDialog.open()
with receiver
and member
requires the old-style slot/signal syntax with the SLOT
macro and @Slot
decorator. Without the macro, Qt
will issue the warning:
qt.core.qobject.connect: QObject::connect: Use the SLOT or SIGNAL macro to connect MyWindow::on_finished()
Without the decorator, Qt
will complain:
qt.core.qobject.connect: QObject::connect: No such slot MyWindow::on_finished()
Example:
from __future__ import annotations
from qtpy import QtCore, QtWidgets
class MyWindow(QtWidgets.QMainWindow):
def __init__(self) -> None:
QtWidgets.QMainWindow.__init__(self)
self.dialog = QtWidgets.QFileDialog(self)
self.dialog.setFileMode(QtWidgets.QFileDialog.Directory)
self.dialog.setWindowTitle('Open folder...')
@QtCore.Slot()
def on_finished(self) -> None:
for path in self.dialog.selectedFiles():
print(path)
if __name__ == '__main__':
app = QtWidgets.QApplication([])
window = MyWindow()
window.show()
window.dialog.open(window, QtCore.SLOT('on_finished()'))
app.exec()
Since QFileDialog.open(receiver, member)
calls QDialog.open()
under the hood, you can use that instead. The benefit is this does not require the old-style syntax, with the caveat that you are responsible for connecting signals and properly disconnecting them when the dialog closes.