1

I'm trying to make a folder selection dialog which looks like this:

enter image description here

The images come from this thread: can the Open File dialog be used to select a Folder?

I tried both QFileDialog::getExistingDirectory() and making an instance of QFileDialog and setting the properties. It just shows the Open File dialog with hidden files.

Andrej Repiský
  • 477
  • 1
  • 6
  • 19

3 Answers3

1

Try this one: QFileDialog::getExistingDirectory()

Both static methods open File selector in directory selection mode. If you don't like the look-n-feel of the opened dialog, you'll need to implement your own. By default Qt tries to open native one if possible, which should work in 99.9% of cases.

ezaquarii
  • 1,914
  • 13
  • 15
  • You didn't read my post, right? I wrote that I tried this one, but it's not opening the right dialog. – Andrej Repiský Jan 22 '15 at 14:25
  • You're probably right. Too many hours with code did harm to my reading abilities. :) – ezaquarii Jan 22 '15 at 15:29
  • Can you paste exact code snippet and give us screenshot of the apparently wrong dialog? – ezaquarii Jan 22 '15 at 15:32
  • Here you are: `QString dirStr = QFileDialog::getExistingDirectory(this, titlePlain(), pathFromSettings().absolutePath());` I added the image to the original post. The problem of that dialog is that it isn't showing files, although it's supposed to: http://stackoverflow.com/questions/19108339/qfiledialog-how-to-set-option-to-show-content-of-folder-in-getexistingdirector – Andrej Repiský Jan 22 '15 at 15:54
  • 1
    I believe that this is how it works - Qt calls native file selection dialog with normal files filtered-out. – ezaquarii Jan 23 '15 at 08:50
  • What's the point in commenting that you believe what I wrote? – Andrej Repiský Nov 22 '17 at 10:30
1

It seems that I have to answer myself. I did it purely in Qt in the end, by making the entire dialog:

Header file:

#pragma once

#include <QtWidgets/QDialog>

class QTreeView;
class QFileSystemModel;
class QLineEdit;
class QPushButton;

class CDirSelectionDlg : public QDialog {
    Q_OBJECT

public:
    CDirSelectionDlg(const QString initialPath, QWidget *parent = nullptr);
    QDir directory() const;

private:
    void onCurrentChanged();

    QTreeView *m_treeView;
    QFileSystemModel *m_model;
    QLineEdit *m_folderName;
    QPushButton *m_OKbutton;
    QString m_initialPath;
};

Source file:

#include "DirSelectionDlg.h"

#include <QLabel>
#include <QBoxLayout>
#include <QDialogButtonBox>
#include <QTreeView>
#include <QFileSystemModel>
#include <QPushButton>
#include <QLineEdit>

CDirSelectionDlg::CDirSelectionDlg(const QString initialPath, QWidget *parent) : QDialog(parent), m_initialPath(initialPath)
{
    setMinimumSize(200, 300);
    resize(400, 430);
    m_model = new QFileSystemModel(this);
    auto rootIdx = m_model->setRootPath(m_initialPath);
    m_treeView = new QTreeView(this);
    m_treeView->setModel(m_model);
    m_treeView->setSelectionMode(QAbstractItemView::SingleSelection);
    m_treeView->setHeaderHidden(true);
    m_treeView->setSortingEnabled(true);
    m_treeView->sortByColumn(0, Qt::AscendingOrder);
    for(int i = 1; i < m_model->columnCount(); i ++)    // don't show Size, Type, etc.
        m_treeView->setColumnHidden(i, true);
    m_treeView->scrollTo(rootIdx);
    m_treeView->selectionModel()->setCurrentIndex(rootIdx, QItemSelectionModel::Current | QItemSelectionModel::Select);
    connect(m_treeView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &CDirSelectionDlg::onCurrentChanged);

    auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
    connect(buttonBox, &QDialogButtonBox::accepted, this, &CDirSelectionDlg::accept);
    connect(buttonBox, &QDialogButtonBox::rejected, this, &CDirSelectionDlg::reject);
    m_OKbutton = buttonBox->button(QDialogButtonBox::Ok);

    auto label = new QLabel(tr("Folder:"));
    m_folderName = new QLineEdit(this);
    m_folderName->setReadOnly(true);
    m_folderName->setText(QFileInfo(m_initialPath).fileName());
    auto pathLayout = new QHBoxLayout();
    pathLayout->addWidget(label);
    pathLayout->addSpacing(10);
    pathLayout->addWidget(m_folderName);

    auto mainLayout = new QVBoxLayout();
    mainLayout->addWidget(m_treeView);
    mainLayout->addSpacing(10);
    mainLayout->addLayout(pathLayout);
    mainLayout->addSpacing(10);
    mainLayout->addWidget(buttonBox);
    setLayout(mainLayout);
}

void CDirSelectionDlg::onCurrentChanged()
{
    auto fileInfo = m_model->fileInfo(m_treeView->selectionModel()->currentIndex());
    m_folderName->setText(fileInfo.fileName());
    m_OKbutton->setEnabled(fileInfo.isDir());
    m_OKbutton->setDefault(fileInfo.isDir());
}

QDir CDirSelectionDlg::directory() const
{
    return QDir(m_model->fileInfo(m_treeView->selectionModel()->currentIndex()).absoluteFilePath());
}

I'm using Qt 5.3.1.

Andrej Repiský
  • 477
  • 1
  • 6
  • 19
1

It's 7 years later, but I had the same need, in this case using Python 3.8 and Qt 5.13.2, so I translated Andrej's code for PySide2. I figured this might be useful for someone else having this problem.

Even though it is not a native Windows widget, I think it looks decent on both Windows and Linux. I decided to change the type of the return value of directory() and the initial_path parameter to pathlib.Path for my application.

class DirectorySelectionDialog(QDialog):
    def __init__(self, initial_path: Path, parent: QWidget) -> None:
        super().__init__(parent=parent)

        self.initial_path = initial_path
        self.setMinimumSize(200, 300)
        self.resize(400, 430)
        
        self.model = QFileSystemModel(parent=self)
        self.model.setFilter(QDir.AllDirs | QDir.NoDotAndDotDot)
        root_index = self.model.setRootPath(str(self.initial_path))
        
        self.tree_view = QTreeView(parent=self)
        self.tree_view.setModel(self.model)
        self.tree_view.setSelectionMode(QAbstractItemView.SingleSelection)
        self.tree_view.setHeaderHidden(True)
        self.tree_view.setSortingEnabled(True)
        self.tree_view.sortByColumn(0, Qt.AscendingOrder)
        for i in range(1, self.model.columnCount()):
            self.tree_view.setColumnHidden(i, True)
        self.tree_view.scrollTo(root_index)
        self.tree_view.selectionModel().setCurrentIndex(root_index, QItemSelectionModel.Current | QItemSelectionModel.Select)
        self.tree_view.selectionModel().selectionChanged.connect(self.onCurrentChanged)

        button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
        button_box.accepted.connect(self.accept)
        button_box.rejected.connect(self.reject)
        self.ok_button = button_box.button(QDialogButtonBox.Ok)

        label = QLabel("Folder:")
        self.folder_name = QLineEdit(parent=self)
        self.folder_name.setReadOnly(True)
        self.folder_name.setText(self.initial_path.name)
        path_layout = QHBoxLayout()
        path_layout.addWidget(label)
        path_layout.addSpacing(10)
        path_layout.addWidget(self.folder_name)

        main_layout = QVBoxLayout()
        main_layout.addWidget(self.tree_view)
        main_layout.addSpacing(10)
        main_layout.addLayout(path_layout)
        main_layout.addSpacing(10)
        main_layout.addWidget(button_box)
        self.setLayout(main_layout)

    def onCurrentChanged(self):
        file_info = self.model.fileInfo(self.tree_view.selectionModel().currentIndex())
        self.folder_name.setText(file_info.fileName())
        self.ok_button.setEnabled(file_info.isDir())
        self.ok_button.setDefault(file_info.isDir())

    def directory(self) -> Path:
        file_info = self.model.fileInfo(self.tree_view.selectionModel().currentIndex())
        return Path(file_info.absoluteFilePath())
javierbg
  • 185
  • 2
  • 12
  • From your answer it seems that this thread was useful for you. Amazingly, both my question and answer have 0 upvotes. Is it possible that you used my code but forgot to upvote it? ;-) – Andrej Repiský Sep 05 '22 at 13:26
  • I'm sorry Andrej, I don't remember if I just forgot or I tried but SO didn't let me (something to do with accounts with low usage or old posts? It has happened to me before). In any case, you are right, I just upvoted both. Sorry! – javierbg Sep 09 '22 at 11:00