QFileDialog that accepts a single file or a single directory

前端 未结 4 1357
有刺的猬
有刺的猬 2021-02-13 23:46

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

QFileDialog::getOpenFileName() accepts only files, while

相关标签:
4条回答
  • 2021-02-14 00:23

    Quite old question but I think I have a simpler solution than most for the lazy ones. You can connect the signal currentChanged of QFileDialog with a function that dynamically changes the fileMode.

    //header
    class my_main_win:public QMainWindow  
    {
    private:
        QFileDialog file_dialog;    
    }
    
    //constructor 
    
    my_main_win(QWidget * parent):QMainWindow(parent)
    {
        connect(&file_dialog,QFileDialog::currentChanged,this,[&](const QString & str)
            {
            QFileInfo info(str);
            if(info.isFile())
                file_dialog.setFileMode(QFileDialog::ExistingFile);
            else if(info.isDir())
                file_dialog.setFileMode(QFileDialog::Directory);
        });
    }
    

    And then simply call file_dialog as you would.

    if(file_dialog.exec()){
        QStringList dir_names=file_dialog.selectedFiles():
    }
    
    0 讨论(0)
  • 2021-02-14 00:26

    Connect to the currentChanged signal and then set the file mode to the currently selected item (directory or file). This is a Python3 implementation:

    class GroupFileObjectDialog(QFileDialog):
    
        def __init__(self, parent):
            super().__init__(parent)
            self.setOption(QFileDialog.DontUseNativeDialog)
            self.setFileMode(QFileDialog.Directory)
            self.currentChanged.connect(self._selected)
    
        def _selected(self,name):
            if os.path.isdir(name):
                self.setFileMode(QFileDialog.Directory)
            else:
                self.setFileMode(QFileDialog.ExistingFile)
    

    Tested on PyQt 5.14 running on linux / Ubuntu18.04.

    0 讨论(0)
  • 2021-02-14 00:31

    What worked for me was to use:

    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.

    0 讨论(0)
  • 2021-02-14 00:37

    QFileDialog currently does not support this. I think the main problem for you here is that the FileMode is not a Q_FLAGS and the values are not power of 2, either, and so, you cannot write this to solve this issue.

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

    To solve this, you would need quite a bit of fiddling, e.g.:

    • Override the open button click operation.

    • Get the "treeview" indices properly for both files and directories.

    My attempt below demonstrates the former, but I did not really go as far as solving the second because that seems to involve some more fiddling with the selection model.

    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
    

    Build and Run

    qmake && make && ./main
    
    0 讨论(0)
提交回复
热议问题