首页 > 解决方案 > 选择目录在 PyQt5 中创建目录

问题描述

我正在 PyQt5 上创建一个 Save As... 函数。

该函数应该打开一个文件对话框,并让用户指定他们想要的目录。

以 Steam 等某些应用程序为例。当您保存 Steam 时,他们让您选择一个目录来保存它。

用户输入:D:// 但随后他们将为用户创建 D://Steam/。这个 D://Steam/ 文件夹的名称是默认名称,可以更改为用户想要的任何名称。例如:D://asdfgh/,所有内容都将下载到那里。

您可以认为该功能与 Microsoft Word 中的 Save As... 功能相同,但它不是 Word 文档,而是一个目录。

这是我当前的代码

saveLocation = QFileDialog.getExistingDirectory(None, "Save As...", os.getenv('HOME'))
    if saveLocation:
        currentSaveLocation = saveLocation
        fromDirectory = Main.tempPath
        toDirectory = saveLocation
        copy_tree(fromDirectory, toDirectory)

我无法将文件保存到命名目录中。

标签: python-3.xpyqtdialogpyqt5

解决方案


为了获得一个可能还不存在的路径,使用 QFileDialog 的静态方法不是一个可行的解决方案。此外,我们不能使用本机操作系统文件对话框,因为与静态方法一样,它们没有提供足够的控制。

我们需要做一些小“黑客”,考虑以下几个方面:

  • 如果对话框设置为DirectoryfileMode,则写入不存在的路径会禁用“打开”按钮;
  • 即使按钮被禁用,当按下Return不存在路径的行编辑时,对话框仍然会抱怨不存在路径;

因此,必须采取这些预防措施:

  • 使用非本机文件对话框,以便我们可以访问子小部件;
  • 获取“打开”按钮,以便我们在需要时手动启用它;
  • 每当路径文本发生更改并且我们检测到有效路径时,获取对话框的行编辑并手动启用按钮;
  • 覆盖对话框的accept()方法以忽略警告,并使用基本QDialog.accept()方法;
class Test(QtWidgets.QWidget):
    def __init__(self):
        super().__init__()
        layout = QtWidgets.QHBoxLayout(self)
        self.pathEdit = QtWidgets.QLineEdit(placeholderText='Select path...')
        self.button = QtWidgets.QToolButton(text='...')
        layout.addWidget(self.pathEdit)
        layout.addWidget(self.button)
        self.button.clicked.connect(self.selectTarget)

    def selectTarget(self):
        dialog = QtWidgets.QFileDialog(self)

        if self.pathEdit.text():
            dialog.setDirectory(self.pathEdit.text())

        dialog.setFileMode(dialog.Directory)

        # we cannot use the native dialog, because we need control over the UI
        options = dialog.Options(dialog.DontUseNativeDialog | dialog.ShowDirsOnly)
        dialog.setOptions(options)

        def checkLineEdit(path):
            if not path:
                return
            if path.endswith(QtCore.QDir.separator()):
                return checkLineEdit(path.rstrip(QtCore.QDir.separator()))
            path = QtCore.QFileInfo(path)
            if path.exists() or QtCore.QFileInfo(path.absolutePath()).exists():
                button.setEnabled(True)
                return True

        # get the "Open" button in the dialog
        button = dialog.findChild(QtWidgets.QDialogButtonBox).button(
            QtWidgets.QDialogButtonBox.Open)

        # get the line edit used for the path
        lineEdit = dialog.findChild(QtWidgets.QLineEdit)
        lineEdit.textChanged.connect(checkLineEdit)

        # override the existing accept() method, otherwise selectedFiles() will 
        # complain about selecting a non existing path
        def accept():
            if checkLineEdit(lineEdit.text()):
                # if the path is acceptable, call the base accept() implementation
                QtWidgets.QDialog.accept(dialog)
        dialog.accept = accept

        if dialog.exec_() and dialog.selectedFiles():
            path = QtCore.QFileInfo(dialog.selectedFiles()[0]).absoluteFilePath()
            self.pathEdit.setText(path)

if __name__ == '__main__':
    import sys
    app = QtWidgets.QApplication(sys.argv)
    w = Test()
    w.show()
    sys.exit(app.exec_())

推荐阅读