Search code examples
pythonpyqt5qfiledialogmultipleselection

Pre-select multiple files in a QFileDialog


When a "choose files" dialog is displayed I want to pre-select files in a project which are already configured as being "part of" that project, so the user can select new files OR unselect existing (i.e. previously chosen) files.

This answer suggests multiple selection should be possible.

For this MRE, please make 3 files and put them in a suitable ref_dir:

from PyQt5 import QtWidgets
import sys

class Window(QtWidgets.QWidget):
    def __init__(self):
        super(Window, self).__init__()
        self.button = QtWidgets.QPushButton('Test', self)
        self.button.clicked.connect(self.handle_button)
        layout = QtWidgets.QVBoxLayout(self)
        layout.addWidget(self.button)

    def handle_button(self):
        options = QtWidgets.QFileDialog.Options()
        options |= QtWidgets.QFileDialog.DontUseNativeDialog
        ref_dir = 'D:\\temp'
        files_list = ['file1.txt', 'file2.txt', 'file3.txt']
        fd = QtWidgets.QFileDialog(None, 'Choose project files', ref_dir, '(*.txt)')
        fd.setFileMode(QtWidgets.QFileDialog.ExistingFiles)
        fd.setOptions(options)
        # fd.setVisible(True)
        for file in files_list:
            print(f'selecting file |{file}|')
            fd.selectFile(file)
        string_list = fd.exec()
        print(f'string list {string_list}')

if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    window = Window()
    window.show()
    sys.exit(app.exec_())

Unfortunately, despite ExistingFiles having been chosen as the file mode, I find that it is only the last file selected which has the selection... but I want all three to be selected when the dialog is displayed.

I tried experimenting with setVisible to see whether the multiple selection could be achieved somehow after the dialog is displayed, but this didn't work.


Solution

  • Since a non-native file dialog is being used, we can access its child widgets to control its behavior.

    At first I thought about using the selection model of the item views, but this won't update the line edit, which is responsible of checking if the files exist and enabling the Ok button in that case; considering this, the obvious solution is to directly update the line edit instead:

        def handle_button(self):
            # ...
            existing = []
            for file in files_list:
                if fd.directory().exists(file):
                    existing.append('"{}"'.format(file))
            lineEdit = fd.findChild(QtWidgets.QLineEdit, 'fileNameEdit')
            lineEdit.setText(' '.join(existing))
            if fd.exec():
                print('string list {}'.format(fd.selectedFiles()))
    

    The only drawback of this approach is that the fileSelected and filesSelected signals are not sent.