Reputation: 487
I'm trying to make a folder selection dialog which looks like this:
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.
Upvotes: 2
Views: 5316
Reputation: 195
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())
Upvotes: 1
Reputation: 487
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.
Upvotes: 1
Reputation: 1916
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.
Upvotes: 1