QFileDialog that accepts one file or one directory

Is it possible to show QFileDialog where the user can select a file or directory, or one?

QFileDialog::getOpenFileName() only accepts files, and QFileDialog::getExistingDirectory() only QFileDialog::getExistingDirectory() directories, but I need to show a file dialog that can accept both a file and a directory (this makes sense for my program). QFileDialog::​Options there was nothing promising.

+5
source share
2 answers

QFileDialog does not currently support this. I think the main problem for you here is that FileMode is not Q_FLAGS , and the values ​​are not 2, and therefore you cannot write this to solve this problem.

 setFileMode(QFileDialog::Directory|QFileDialog::ExistingFiles) 

To solve this problem, you need to bother a lot, for example:

  • Cancel the operation of pressing the open button.

  • Get tree indexes properly for files and directories.

My attempt below demonstrates the first, but I really have not gone as far as the second decides, because this, apparently, is associated with even more messing with the model of choice.

main.cpp

 #include <QFileDialog> #include <QApplication> #include <QWidget> #include <QTreeWidget> #include <QPushButton> #include <QStringList> #include <QModelIndex> #include <QDir> #include <QDebug> class FileDialog : public QFileDialog { Q_OBJECT public: explicit FileDialog(QWidget *parent = Q_NULLPTR) : QFileDialog(parent) { setOption(QFileDialog::DontUseNativeDialog); setFileMode(QFileDialog::Directory); // setFileMode(QFileDialog::ExistingFiles); for (auto *pushButton : findChildren<QPushButton*>()) { qDebug() << pushButton->text(); if (pushButton->text() == "&Open" || pushButton->text() == "&Choose") { openButton = pushButton; break; } } disconnect(openButton, SIGNAL(clicked(bool))); connect(openButton, &QPushButton::clicked, this, &FileDialog::openClicked); treeView = findChild<QTreeView*>(); } QStringList selected() const { return selectedFilePaths; } public slots: void openClicked() { selectedFilePaths.clear(); qDebug() << treeView->selectionModel()->selection(); for (const auto& modelIndex : treeView->selectionModel()->selectedIndexes()) { qDebug() << modelIndex.column(); if (modelIndex.column() == 0) selectedFilePaths.append(directory().absolutePath() + modelIndex.data().toString()); } emit filesSelected(selectedFilePaths); hide(); qDebug() << selectedFilePaths; } private: QTreeView *treeView; QPushButton *openButton; QStringList selectedFilePaths; }; #include "main.moc" int main(int argc, char **argv) { QApplication application(argc, argv); FileDialog fileDialog; fileDialog.show(); return application.exec(); } 

main.pro

 TEMPLATE = app TARGET = main QT += widgets CONFIG += c++11 SOURCES += main.cpp 

Assembly and launch

 qmake && make && ./main 
+4
source

What worked for me was:

 file_dialog.setProxyModel(nullptr); 

as suggested here , or

 class FileFilterProxyModel : public QSortFilterProxyModel { protected: virtual bool filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const { QFileSystemModel* fileModel = qobject_cast<QFileSystemModel*>(sourceModel()); return (fileModel!=NULL && fileModel->isDir(sourceModel()->index(sourceRow, 0, sourceParent))) || QSortFilterProxyModel::filterAcceptsRow(sourceRow, sourceParent); } }; ... FileFilterProxyModel* proxyModel = new FileFilterProxyModel; file_dialog.setProxyModel(proxyModel); 

as suggested here , or

 class FileDialog : public QFileDialog { Q_OBJECT public: explicit FileDialog(QWidget *parent = Q_NULLPTR) : QFileDialog(parent) { m_btnOpen = NULL; m_listView = NULL; m_treeView = NULL; m_selectedFiles.clear(); this->setOption(QFileDialog::DontUseNativeDialog, true); this->setFileMode(QFileDialog::Directory); QList<QPushButton*> btns = this->findChildren<QPushButton*>(); for (int i = 0; i < btns.size(); ++i) { QString text = btns[i]->text(); if (text.toLower().contains("open") || text.toLower().contains("choose")) { m_btnOpen = btns[i]; break; } } if (!m_btnOpen) return; m_btnOpen->installEventFilter(this); //connect(m_btnOpen, SIGNAL(changed()), this, SLOT(btnChanged())) m_btnOpen->disconnect(SIGNAL(clicked())); connect(m_btnOpen, SIGNAL(clicked()), this, SLOT(chooseClicked())); m_listView = findChild<QListView*>("listView"); if (m_listView) m_listView->setSelectionMode(QAbstractItemView::ExtendedSelection); m_treeView = findChild<QTreeView*>(); if (m_treeView) m_treeView->setSelectionMode(QAbstractItemView::ExtendedSelection); } QStringList selectedFiles() { return m_selectedFiles; } bool eventFilter( QObject* watched, QEvent* event ) { QPushButton *btn = qobject_cast<QPushButton*>(watched); if (btn) { if(event->type()==QEvent::EnabledChange) { if (!btn->isEnabled()) btn->setEnabled(true); } } return QWidget::eventFilter(watched, event); } public slots: void chooseClicked() { QModelIndexList indexList = m_listView->selectionModel()->selectedIndexes(); foreach (QModelIndex index, indexList) { if (index.column()== 0) m_selectedFiles.append(this->directory().absolutePath() + "/" + index.data().toString()); } QDialog::accept(); } private: QListView *m_listView; QTreeView *m_treeView; QPushButton *m_btnOpen; QStringList m_selectedFiles; }; 

as suggested here . Credits for the original authors and me.

+1
source

Source: https://habr.com/ru/post/1209306/


All Articles