限制QFileDialog中的目录遍历

时间:2012-08-29 01:50:15

标签: qt

我正在使用QFileDialog来选择目录。我遇到了一个我无法解决的问题。我花了很多时间在谷歌上搜索这个,但是想出了zilch。

我指定起始目录(例如/ home / dhoti / downloads),我想禁用此目录上方的导航。例如,不应该允许用户转到/ home / dhoti或/ tmp等。我该如何实现?

这是我的代码:

QFileDialog dlg(this, "Select Firmware Version");
dlg.setDirectory("/home/dhoti/downloads");
dlg.setFileMode(QFileDialog::DirectoryOnly);
dlg.setOption(QFileDialog::ReadOnly, true);
dlg.setOption(QFileDialog::HideNameFilterDetails, true);
dlg.setViewMode(QFileDialog::List);
dlg.setAcceptMode(QFileDialog::AcceptOpen);
dlg.exec();
qDebug() << "selected files: " << dlg.selectedFiles();

感谢您的帮助 兜提

4 个答案:

答案 0 :(得分:2)

您可以检测当前目录何时更改以及是否超出限制,将目录设置回限制目录。

您可以通过执行非阻塞对话框并将QFileDialog::directoryEntered(const QString& directory)信号连接到您自己可以执行检查的插槽来执行此操作。如果检查失败,请通过QFileDialog::setDirectory(const QString& directory)将当前目录设置为限制目录。

免责声明我没有尝试过,但如果它不起作用我会感到惊讶。

答案 1 :(得分:1)

尝试以下方法:

  

filedialog.h

#ifndef FILEDIALOG_H
#define FILEDIALOG_H

class QEvent;

#include <QFileDialog>
#include <QString>

class FileDialog : public QFileDialog
{
    Q_OBJECT
public:
    explicit FileDialog(QWidget *parent = 0);
public:
    bool eventFilter(QObject *o, QEvent *e);
    void setTopDir(const QString &path);
    QString topDir() const;
private:
    bool pathFits(const QString &path) const;
private slots:
    void checkHistory();
    void checkGoToParent();
    void checkLineEdit(const QString &text);
private:
    QString mtopDir;
};

#endif // FILEDIALOG_H
  

filedialog.cpp

#include "filedialog.h"

#include <QString>
#include <QStringList>
#include <QFileDialog>
#include <QList>
#include <QToolButton>
#include <QDir>
#include <QLineEdit>
#include <QDialogButtonBox>
#include <QEvent>
#include <QKeyEvent>
#include <QAbstractButton>
#include <QCompleter>
#include <QAbstractItemView>
#include <QFileInfo>

FileDialog::FileDialog(QWidget *parent) :
    QFileDialog(parent)
{
    connect(this, SIGNAL(directoryEntered(QString)), this, SLOT(checkHistory()));
    connect(this, SIGNAL(directoryEntered(QString)), this, SLOT(checkGoToParent()));
    connect(findChild<QToolButton *>("backButton"), SIGNAL(clicked()), this, SLOT(checkGoToParent()));
    connect(findChild<QToolButton *>("forwardButton"), SIGNAL(clicked()), this, SLOT(checkGoToParent()));
    connect(findChild<QLineEdit *>("fileNameEdit"), SIGNAL(textChanged(QString)), this, SLOT(checkLineEdit(QString)));
    findChild<QLineEdit *>("fileNameEdit")->installEventFilter(this);
    findChild<QWidget *>("listView")->installEventFilter(this);
    findChild<QWidget *>("treeView")->installEventFilter(this);
    findChild<QLineEdit *>("fileNameEdit")->completer()->popup()->installEventFilter(this);
    setOption(DontUseNativeDialog, true);
}

bool FileDialog::eventFilter(QObject *o, QEvent *e)
{
    if (e->type() != QEvent::KeyPress)
        return false;
    int key = static_cast<QKeyEvent *>(e)->key();
    if (o->objectName() == "listView" || o->objectName() == "treeView")
    {
        return (Qt::Key_Backspace == key && !pathFits(directory().absolutePath()));
    }
    else
    {
        if (Qt::Key_Return != key && Qt::Key_Enter != key)
            return false;
        QString text = findChild<QLineEdit *>("fileNameEdit")->text();
        QString path = QDir::cleanPath(directory().absolutePath() + (text.startsWith("/") ? "" : "/") + text);
        bool a = QDir(text).isAbsolute();
        return !((!a && pathFits(path)) || (a && pathFits(text)));
    }
}

void FileDialog::setTopDir(const QString &path)
{
    if (path == mtopDir)
        return;
    mtopDir = (!path.isEmpty() && QFileInfo(path).isDir()) ? path : QString();
    if (!pathFits(path))
    {
        setDirectory(mtopDir);
        checkHistory();
        checkLineEdit(findChild<QLineEdit *>("fileNameEdit")->text());
    }
    else
    {
        QLineEdit *ledt = findChild<QLineEdit *>("fileNameEdit");
        ledt->setText(ledt->text());
    }
    findChild<QWidget *>("lookInCombo")->setEnabled(mtopDir.isEmpty());
    findChild<QWidget *>("sidebar")->setEnabled(mtopDir.isEmpty());
    checkGoToParent();
}

QString FileDialog::topDir() const
{
    return mtopDir;
}

bool FileDialog::pathFits(const QString &path) const
{
    return mtopDir.isEmpty() || (path.startsWith(mtopDir) && path.length() > mtopDir.length());
}

void FileDialog::checkHistory()
{
    QStringList list = history();
    for (int i = list.size() - 1; i >= 0; --i)
        if (!pathFits(list.at(i)))
            list.removeAt(i);
    setHistory(list);
}

void FileDialog::checkGoToParent()
{
    findChild<QToolButton *>("toParentButton")->setEnabled(pathFits(directory().absolutePath()));
}

void FileDialog::checkLineEdit(const QString &text)
{
    QAbstractButton *btn = findChild<QDialogButtonBox *>("buttonBox")->buttons().first();
    QString path = QDir::cleanPath(directory().absolutePath() + (text.startsWith("/") ? "" : "/") + text);
    bool a = QDir(text).isAbsolute();
    btn->setEnabled(btn->isEnabled() && ((!a && pathFits(path)) || (a && pathFits(text))));
}

这段代码可能看起来像一些魔法,它并不完美,但它有效。我在Qt源中搜索了QFileDialog子对象名称并使用了

  

findChild()

访问它们的方法。您只需要使用

即可
  

setTopDir()

指定不允许用户访问的目录的方法。

以下是使用此类的示例项目:https://docs.google.com/file/d/0B3P3dwuDIZ1-Q19FbkFMY2puUE0/edit?usp=sharing

答案 2 :(得分:1)

您可以使用@ololoepepe的解决方案。并使用以下内容清除顶部comboBox中不需要的条目:

connect(findChild<QComboBox *>("lookInCombo"), static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &FileDialog::checkComboBox);

void FileDialog::checkComboBox(int index) {
    int i;
    QComboBox *cb = findChild<QComboBox *>("lookInCombo");

    if (index == 0 && cb->model()->rowCount() > 1) {
        for (i = 0; i < cb->model()->rowCount(); ++i) {
            if (!pathFits(cb->model()->index(i, 0).data().toString() + "/")) {
                cb->model()->removeRow(i);
                --i;
            }
        }
    }
}

答案 3 :(得分:1)

这是最简单的解决方案,只需要最少的步骤来限制目录遍历。

想法:使用directoryEntered(const QString &)的公共信号QFileDialog来获取有关目录可能更改的通知,在其中一个类中为其实现插槽并为其设置逻辑确保该目录是您需要的目录。

QFileDialog dialog(this);
    connect(&dialog, SIGNAL(directoryEntered(const QString &)), this, SLOT(onFileDialogDirectoryChanged(const QString &)));