在 QFileDialog 中预选多个文件

问题描述

当显示“选择文件”对话框时,我想预先选择项目中已配置为该项目“一部分”的文件,以便用户可以选择新文件或 取消选择 现有(即之前选择的)文件。

This answer 建议可以进行多项选择。

对于这个 MRE,请制作 3 个文件并将它们放在合适的 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_())

不幸的是,尽管已选择 ExistingFiles 作为文件模式,但我发现只有最后一个选择的文件具有选择权...但我希望在显示对话框时选择所有三个文件。

我尝试使用 setVisible 进行试验,以查看在显示对话框后是否可以以某种方式实现多选,但这不起作用。

解决方法

由于使用了非本地文件对话框,我们可以访问其子小部件来控制其行为。

一开始我想过使用item视图的选择模型,但这不会更新line edit,它负责检查文件是否存在并在这种情况下启用Ok按钮;考虑到这一点,显而易见的解决方案是直接更新行编辑:

.next.config.js

这种方法的唯一缺点是不发送 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())) fileSelected 信号。

,

Musicamante 的回答非常非常有帮助,特别是表明选择实际上是通过用路径字符串填充 QLE 来触发的。

但实际上有一个致命的缺陷,当目的是我所说的:不幸的是,如果你试图取消选择目录中最终选定的文件,实际上这个名字是not然后从QLE。事实上,如果 QLE 设置为空白,这将禁用“选择”按钮。所有这一切都是设计使然:QFileDialog 的功能是要么“打开”或“保存”,而不是“修改”。

但我确实找到了一个解决方案,它涉及找到列出目录中文件的 QListView,然后在其选择模型上使用信号。

这需要处理的另一件事是当您更改目录时会发生什么:显然,您随后希望根据在该目录中找到(或未找到)的项目文件来更新选择。事实上,我已经更改了“选择”按钮的文字,以显示“修改”是游戏的名称。

fd = QtWidgets.QFileDialog(app.get_main_window(),'Modify project files',start_directory,'(*.docx)')
fd.setFileMode(QtWidgets.QFileDialog.ExistingFiles)
fd.setViewMode(QtWidgets.QFileDialog.List)

fd.setLabelText(QtWidgets.QFileDialog.Reject,'&Cancel')
fd.setLabelText(QtWidgets.QFileDialog.Accept,'&Modify')
fd.setOptions(options)

file_name_line_edit = fd.findChild(QtWidgets.QLineEdit,'fileNameEdit')
list_view = fd.findChild(QtWidgets.QListView,'listView')

# utility to cope with all permutations of backslashes and forward slashes in path strings:
def split_file_path_str(path_str):
    dir_path_str,filename = ntpath.split(path_str)
    return dir_path_str,(filename or ntpath.basename(dir_path_str))

fd.displayed_dir = None
sel_model = list_view.selectionModel()
def sel_changed():
    if not fd.displayed_dir:
        return

    selected_file_paths_in_shown_dir = []
    sel_col_0s = sel_model.selectedRows()
    for sel_col_0 in sel_col_0s:
        file_path_str = os.path.join(fd.displayed_dir,sel_col_0.data())
        selected_file_paths_in_shown_dir.append(file_path_str)
        already_included = file_path_str in self.files_list
        if not already_included:
            fd.project_files_in_shown_dir.append(file_path_str)
            
    # now find if there are any project files which are now NOT selected
    for project_file_path_str in fd.project_files_in_shown_dir:
        if project_file_path_str not in selected_file_paths_in_shown_dir:
            fd.project_files_in_shown_dir.remove(project_file_path_str)
            
sel_model.selectionChanged.connect(sel_changed)

def file_dlg_dir_entered(displayed_dir):
    displayed_dir = os.path.normpath(displayed_dir)
    
    # this is set to None to prevent unwanted selection processing triggered by setText(...) below 
    fd.displayed_dir = None
    
    fd.project_files_in_shown_dir = []
    existing = []
    for file_path_str in self.files_list:
        dir_path_str,filename = split_file_path_str(file_path_str)
        if dir_path_str == displayed_dir:     
            existing.append('"{}"'.format(file_path_str))
            fd.project_files_in_shown_dir.append(file_path_str)
    file_name_line_edit.setText(' '.join(existing))            
    fd.displayed_dir = displayed_dir
    
fd.directoryEntered.connect(file_dlg_dir_entered)

# set the initially displayed directory...
file_dlg_dir_entered(start_directory)

if fd.exec():
    # for each file,if not present in self.files_list,add to files list and make self dirty
    for project_file_in_shown_dir in fd.project_files_in_shown_dir:
        if project_file_in_shown_dir not in self.files_list:
            self.files_list.append(project_file_in_shown_dir)
            # also add to list widget...
            app.get_main_window().ui.files_list.addItem(project_file_in_shown_dir)
            if not self.is_dirty():
                self.toggle_dirty()
    
    # but we also have to make sure that a file has not been UNselected...
    docx_files_in_start_dir = [f for f in os.listdir(fd.displayed_dir) if os.path.isfile(os.path.join(fd.displayed_dir,f)) and os.path.splitext(f)[1] == '.docx' ]
    for docx_file_in_start_dir in docx_files_in_start_dir:
        docx_file_path_str = os.path.join(fd.displayed_dir,docx_file_in_start_dir)
        if docx_file_path_str in self.files_list and docx_file_path_str not in fd.project_files_in_shown_dir:
            self.files_list.remove(docx_file_path_str)
            list_widget = app.get_main_window().ui.files_list
            item_for_removal = list_widget.findItems(docx_file_path_str,QtCore.Qt.MatchExactly)[0]
            list_widget.takeItem(list_widget.row(item_for_removal))
            if not self.is_dirty():
                self.toggle_dirty()

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...