【问题标题】:File dialog always returns alpha order文件对话框总是返回 alpha 顺序
【发布时间】:2021-09-22 00:00:03
【问题描述】:

我正在创建一个在文件中绘制数据的应用程序。绘制的第一个文件定义了所有其他数据相对于转换的原点。 AFAICT,QFileDialog 总是按字母顺序返回文件,无论选择顺序如何。

有没有办法返回按选择排序的数据?

为了说明,创建一个文件夹,其中包含名为 A、B、C 或 1、2、3 的文件。无论它们被选择或出现在文件名行编辑中的方式如何,返回的路径列表均按字母顺序排列.

import os
import sys
from PyQt5 import QtCore, QtWidgets

MYDIR = (os.environ['USERPROFILE'] + '/Desktop/numbered').replace("\\", "/")

def on_button_pressed():
    paths, _ = QtWidgets.QFileDialog.getOpenFileNames(
        directory = MYDIR,
        caption='Open',
        filter=(
            'All (*.*)'
        ))

    for i, path in enumerate(paths):
        print(i, path, flush=True)

if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    button = QtWidgets.QPushButton("Open")
    button.pressed.connect(on_button_pressed)

    button.show()
    sys.exit(app.exec_())

编辑可能会挂起的@musicamate 响应的实现:

import os
import sys
from PyQt5 import QtCore, QtWidgets


MYDIR = (os.environ['USERPROFILE'] + '/Desktop/numbered').replace("\\", "/")


class SelectionOrderFileDialog(QtWidgets.QFileDialog):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.setOption(QtWidgets.QFileDialog.DontUseNativeDialog)
        self.setFileMode(QtWidgets.QFileDialog.ExistingFiles)
        self.setWindowFlags(self.windowFlags() & ~QtCore.Qt.WindowContextHelpButtonHint)

        list_view = self.findChild(QtWidgets.QListView, 'listView')
        self.selection_model = list_view.selectionModel()
        self.selection_model.selectionChanged.connect(self.check_selection)

        self.current_selection = []

    def check_selection(self):
        active_selection = []

        for index in self.selection_model.selectedRows():
            path = index.data(QtWidgets.QFileSystemModel.FilePathRole)
            active_selection.append(path)

        updated_current_selection = []

        for path in self.current_selection:
            if path in active_selection:
                updated_current_selection.append(path)
                active_selection.remove(path)

        updated_current_selection.extend(active_selection)
        self.current_selection[:] = updated_current_selection

        print(self.current_selection, flush=True)


def on_button_pressed():

    # Works fine when called as...
    # dialog = SelectionOrderFileDialog()

    # Causes hangs on Open
    dialog = SelectionOrderFileDialog(
        directory = MYDIR,
        caption='Open',
        filter=(
            'text (*.txt)'
            ';;python (*.py)'
            ';;All (*.*)'
        ))

    dialog.exec_()


if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    button = QtWidgets.QPushButton("Open")
    button.resize(300, 25)
    button.pressed.connect(on_button_pressed)

    button.show()
    sys.exit(app.exec_())

【问题讨论】:

  • 可能不会。不要让他们一次选择多个文件,而是要求他们一次选择一个文件。然后,您可以在每次选择文件时将它们添加到全局列表中,并按照它们选择的顺序保留它们。
  • 这是个好建议。
  • 可以做到,但不能使用静态方法:您需要创建一个 QFileDialog 实例,根据您的需要对其进行配置并与filesSelected 信号连接并实现一个“保持”的方法选择更改时的排序。
  • @musicamante 即使是原生对话?
  • @LoremIpsum 你是什么意思?

标签: python pyqt pyside


【解决方案1】:

这种级别的控制无法通过静态方法实现,但可以通过使用 QFileDialog 的实例来实现。

唯一的要求是避免原生对话框,这样我们就可以访问其中一个文件视图(一个非原生的QFileDialog有一个列表视图和一个树视图),然后连接到它的selectionChanged信号选择模型。
然后,每次发出信号时,我们都会检查当前的 ordered 选择,删除已经存在的路径并通过扩展新元素来构建一个新列表。

    def showDialog(self):
        def checkSelection():
            selection = []
            for index in selectionModel.selectedRows():
                path = index.data(QtWidgets.QFileSystemModel.FilePathRole)
                selection.append(path)
            newOrderedPaths = []
            for path in orderedPaths:
                if path in selection:
                    newOrderedPaths.append(path)
                    selection.remove(path)
            newOrderedPaths.extend(selection)
            orderedPaths[:] = newOrderedPaths

        dialog = QtWidgets.QFileDialog(self)
        dialog.setOption(dialog.DontUseNativeDialog)
        dialog.setFileMode(dialog.ExistingFiles)
        listView = dialog.findChild(QtWidgets.QListView, 'listView')
        selectionModel = listView.selectionModel()
        selectionModel.selectionChanged.connect(checkSelection)

        orderedPaths = []

        dialog.exec_()

        print(orderedPaths)

显然,使用子类也可以做到这一点。

【讨论】:

  • 谢谢!我注意到应用程序有时会在 dialog.exec_() 上挂起。它的速度大约是六分之一,有时在第二次打开时。建议是由于事件处理。在 dialog.exec_() 或 closeEvent() 之前放置一个 processEvents() 并不能解决问题。将小部件移动到 MainWindow 以实现对象持久性会产生相同的问题。我想我可以在一个单独的线程中运行对话框,以便它有自己的事件循环,但要谈论矫枉过正。有什么建议吗?打开一个单独的 SO 问题?相关:stackoverflow.com/a/24892872
  • 尝试在单独的线程中运行对话框是行不通的,因为所有 UI 对象必须驻留在主线程上。此外,QDialog exec 确实 创建了自己的事件循环。使用您给定的代码,您所描述的事情不应该发生,您不是在尝试在另一个线程中执行 QApplication,是吗?
  • 我基本上按照所写的方式运行它。在on_button_pressed 中,我创建了一个对话框对象,然后运行 ​​dialog.exec_()。问题的根源似乎与我尝试设置的过滤有关。我会发布一个编辑...
  • @LoremIpsum 我无法重现您的问题。您是否使用了一些可能暂时禁用且无法访问的网络资源?
  • 据我所知。我的系统上有网络目录,但对话框中没有主动查看这些目录。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-02-12
  • 2016-06-07
  • 1970-01-01
  • 2018-03-22
相关资源
最近更新 更多