I have a simple function that opens a file dialog using pyqt6 to select a file. The function works, but the dialog hangs after being called and I'm not sure why it's not getting automatically closed when the file is selected.
def ui_find_file(title=None, initialdir=None, file_type=None):
import os
from PyQt6.QtWidgets import QApplication, QFileDialog
app = QApplication([])
file_dialog = QFileDialog()
file_dialog.setFileMode(QFileDialog.FileMode.ExistingFile)
if file_type:
filter = f"{file_type} files (*.{file_type})"
file_dialog.setNameFilter(filter)
else:
file_dialog.setNameFilter("All files (*);;")
if initialdir is None:
initialdir = os.getcwd()
elif initialdir == '~':
initialdir = os.path.expanduser('~')
file_dialog.setDirectory(initialdir)
if title:
file_dialog.setWindowTitle(title)
if file_dialog.exec():
file_path = file_dialog.selectedFiles()[0]
print(f"Selected {file_path}")
file_dialog.deleteLater() # Clean up the file dialog window
app.quit() # Exit the event loop
return file_path
else:
print("No file selected")
file_dialog.deleteLater() # Clean up the file dialog window
app.quit() # Exit the event loop
return None
When I try the first lines in the command line
from PyQt6.QtWidgets import QApplication, QFileDialog
app = QApplication([])
file_dialog = QFileDialog()
file_dialog.setFileMode(QFileDialog.FileMode.ExistingFile)
and I use either file_dialog.open()
or file_dialog.exec()
, I can select a file and the dialog closes without freezing the python instance. I don't even have to call file_dialog.deleteLater()
for it to close itself.
When I try to use this from another script, for example doing:
from utils import ui_find_file
ui_find_file()
It produces the freezing behavior.
I am running this in python 3.10, Ubuntu 20.04, from a terminal.
destroy()
instead of destroyLater()
.app.processEvents()
below destroy()
with del(app)
The reason I am creating app = QApplication([])
was because when I didn't, I got QWidget: Must construct a QApplication before a QWidget
(which I can reproduce by commenting out the line)
Python 3.10.6 (main, Mar 10 2023, 10:55:28) [GCC 11.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from utils import ui_find_file
>>> ui_find_file()
QWidget: Must construct a QApplication before a QWidget
Aborted (core dumped)
I modified the code as follows to quit when being called from another script
def ui_find_file(title=None, initialdir=None, file_type=None):
"""
Find a file using a GUI.
:param title: Title of the dialog.
:param initialdir: Initial directory.
:param file_type: File type.
:return: File path.
"""
import os
from PyQt6.QtWidgets import QApplication, QFileDialog
app = QApplication.instance()
if app is None:
app = QApplication([])
is_new_instance = True
else:
is_new_instance = False
if title is None:
title = 'Find a file'
if initialdir is None:
initialdir = os.getcwd()
if file_type is None:
file_filter = 'All files (*.*)'
else:
file_filter = f"{file_type} files (*.{file_type})"
file_path, _ = QFileDialog.getOpenFileName(None, title, initialdir, file_filter)
if file_path:
# no multiple files for now...file_path will be a string
#file_path = file_path[0]
print(f"Selected {file_path}")
result = file_path
else:
print("No file selected")
result = None
if is_new_instance:
app.quit()
return result
In this modification, we added a boolean variable is_new_instance to determine whether the QApplication
instance was newly created in this function. If it was, we quit the QApplication
instance by calling app.quit()
after the file dialog is closed. This ensures that the event loop is properly managed and should prevent the script from hanging after the file selection.