Sean Ang
Sean Ang

Reputation: 47

Choose Directory to create directory in PyQt5

I am creating a Save As... function on PyQt5.

The function is supposed to open a file dialog, and let the user specify the directory they want.

Take for example certain applications like Steam. When you save steam, they let you choose a directory to save it in.

User input: D:// But then they will create D://Steam/ for the user. This D://Steam/ folder's name is default and can be changed to anything the user wants For eg: D://asdfgh/ and all content will be downloaded there.

You could think of the function the same as Save As... function in Microsoft Word, but instead of a word document, it is a directory.

This is my current code

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

I am unable to save the files into a named directory.

Upvotes: 0

Views: 1954

Answers (1)

musicamante
musicamante

Reputation: 48489

In order to get a possibly not yet existing path, using the static methods of QFileDialog is not a viable solution. Also, we cannot use the native OS file dialogs, as, like the static methods, they don't give enough control.

We need to do some little "hacking", considering the following aspects:

  • if the dialog is set in Directory fileMode, writing a non existing path disables the Open button;
  • even if the button is disabled, when pressing Return in the line edit with a non existing path, the dialog will still complain about the non existing path;

So, these precautions must be taken:

  • use the non native file dialog, so that we can have access to the child widgets;
  • get the "Open" button so that we can manually enable it when required;
  • get the line edit of the dialog and manually enable the button whenever the path text is changed and we detect a valid path;
  • overwrite the accept() method of the dialog in order to ignore the warning, and use the base QDialog.accept() method;
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_())

Upvotes: 2

Related Questions