Custom QFileDialog QInputDialog

1, Transformation reasons

When using qinputdialog and QFileDialog, the most commonly used are their own static functions. The advantage is that the parameters are clear and easy to call, but at the same time, the disadvantage is lack of flexibility.

Scenario 1

The current page has controls that regularly update the status, such as waveform, OpenGL model, etc;

When the static methods such as QFileDialog::getOpenFileName are executed, the controls that update the status regularly are blocked in the pages blocked by the pop-up dialog box, and will not be updated during the pop-up dialog box.

The reason for blocking is that the dialog box is executed by exec method, blocking other interfaces and waiting for user input.

Scenario 2

To customize the qinputdialog and QFileDialog interfaces, for example, pop up the built-in soft keyboard in the input box of the pop-up dialog box.

In general, to pop up the built-in soft keyboard, you need to register the control event filter. The dialog boxes pop up by directly executing the static method are all new, and then run exec directly. There is no chance to register its input box control at all.

Scenario 3

Or interface customization. QFileDialog has a parameter, dontuseinatedialog, which is false by default. This parameter controls whether the QFileDialog interface adopts the system file dialog box or the dialog box customized by Qt control.

When using the system dialog box, the file resource manager of the operating system pops up. We can't customize the qss style of the interface. So we need to manually set it to true. setOption(DontUseNativeDialog, true);

2, Transformation method

To sum up, we mainly have two requirements that cannot be met in calling the convenient static function.

  1. The properties of the dialog box itself need to be customized. If the operating system interface is not used, set its controls after the interface is loaded.
  2. The operation mode of the dialog box needs to be changed, and other interface updates cannot be blocked.

From the perspective of demand, the solution is:

Define a member variable in advance and register it when it is constructed. Use this member variable to operate when you want to pop up a dialog box later. However, this method is very inconvenient to call. Calling the static method directly is only one statement.

Generally, there are many interactive interfaces such as loading files or obtaining input. If each interface is handled in this way, the structure and amount of code do not meet our expectations.

3, Transformation code

Therefore, the new problem is code encapsulation and optimization. We are very familiar with this problem. We directly inherit the control and override the static method. Since it is not a virtual function and cannot be rewritten directly, I changed the name of the new method function. The code is as follows:

1. QLsFileDialog

qls_file_dialog.h

#ifndef QLSFILEDIALOG_H
#define QLSFILEDIALOG_H

#include <functional>

#include <QFileDialog>

class QLsFileDialog : public QFileDialog
{
    Q_OBJECT
public:
    QLsFileDialog(QWidget *parent = nullptr, const QString &caption = QString(),
                  const QString &dir = QString(),
                  const QString &filter = QString());

    // O: Means fdlg - > open(); Formal asynchronous execution
    static QString getOpenFileNameO(std::function<void()> run,
                                    QWidget *parent = nullptr,
                                    const QString &caption = QString(),
                                    const QString &dir = QString(),
                                    const QString &filter = QString(),
                                    QString *selectedFilter = nullptr,
                                    Options options = Options());
    static QUrl getOpenFileUrlO(
        std::function<void()> run, QWidget *parent = nullptr,
        const QString &caption = QString(), const QUrl &dir = QUrl(),
        const QString &filter = QString(), QString *selectedFilter = nullptr,
        Options options = Options(),
        const QStringList &supportedSchemes = QStringList());

    static QString getSaveFileNameO(std::function<void()> run,
                                    QWidget *parent = nullptr,
                                    const QString &caption = QString(),
                                    const QString &dir = QString(),
                                    const QString &filter = QString(),
                                    QString *selectedFilter = nullptr,
                                    Options options = Options());
    static QUrl getSaveFileUrlO(
        std::function<void()> run, QWidget *parent = nullptr,
        const QString &caption = QString(), const QUrl &dir = QUrl(),
        const QString &filter = QString(), QString *selectedFilter = nullptr,
        Options options = Options(),
        const QStringList &supportedSchemes = QStringList());
};

#endif // QLSFILEDIALOG_H

qls_file_dialog.cpp

#include "ui_wrapper/qls_file_dialog.h"

#include <QApplication>
#include <QLineEdit>

QLsFileDialog::QLsFileDialog(QWidget *parent, const QString &caption,
                             const QString &dir, const QString &filter)
    : QFileDialog(parent, caption, dir, filter)
{
}

QString QLsFileDialog::getOpenFileNameO(std::function<void()> run,
                                        QWidget *parent, const QString &caption,
                                        const QString &dir,
                                        const QString &filter,
                                        QString *selectedFilter,
                                        Options options)
{
    const QStringList schemes = QStringList(QStringLiteral("file"));
    const QUrl selectedUrl =
        getOpenFileUrlO(run, parent, caption, QUrl::fromLocalFile(dir), filter,
                        selectedFilter, options, schemes);
    return selectedUrl.toLocalFile();
}

QUrl QLsFileDialog::getOpenFileUrlO(std::function<void()> run, QWidget *parent,
                                    const QString &caption, const QUrl &dir,
                                    const QString &filter,
                                    QString *selectedFilter, Options options,
                                    const QStringList &supportedSchemes)
{
    QUrl selectedUrl;
    QEventLoop loop;
    QSharedPointer<QLsFileDialog> fdlg(
        new QLsFileDialog(parent, caption, dir.toLocalFile(), filter));
    fdlg->setFileMode(ExistingFile);
    fdlg->setOptions(options);
    fdlg->setSupportedSchemes(supportedSchemes);
    fdlg->setOption(DontUseNativeDialog, true); // Do not use native dialog boxes
    fdlg->findChild<QLineEdit *>()->setReadOnly(true); // The input box is read-only
    if (selectedFilter && !selectedFilter->isEmpty()) {
        fdlg->selectNameFilter(*selectedFilter);
    }

    fdlg->connect(fdlg.get(), &QFileDialog::accepted, [&] {
        if (selectedFilter) {
            *selectedFilter = fdlg->selectedNameFilter();
        }

        selectedUrl = fdlg->selectedUrls().value(0);
    });

    fdlg->connect(fdlg.get(), &QFileDialog::finished,
                  [&](int) { loop.exit(); });

    // open
    fdlg->open();

    // Asynchronous operation
    if (run) {
        run();
    }

    // Non blocking wait
    loop.exec(QEventLoop::DialogExec);

    return selectedUrl;
}

QString QLsFileDialog::getSaveFileNameO(std::function<void()> run,
                                        QWidget *parent, const QString &caption,
                                        const QString &dir,
                                        const QString &filter,
                                        QString *selectedFilter,
                                        Options options)
{
    const QStringList schemes = QStringList(QStringLiteral("file"));
    const QUrl selectedUrl =
        getSaveFileUrlO(run, parent, caption, QUrl::fromLocalFile(dir), filter,
                        selectedFilter, options, schemes);
    return selectedUrl.toLocalFile();
}

QUrl QLsFileDialog::getSaveFileUrlO(std::function<void()> run, QWidget *parent,
                                    const QString &caption, const QUrl &dir,
                                    const QString &filter,
                                    QString *selectedFilter, Options options,
                                    const QStringList &supportedSchemes)
{
    QUrl selectedUrl;
    QEventLoop loop;
    QSharedPointer<QLsFileDialog> fdlg(
        new QLsFileDialog(parent, caption, dir.toLocalFile(), filter));
    fdlg->setFileMode(AnyFile);
    fdlg->setOptions(options);
    fdlg->setSupportedSchemes(supportedSchemes);
    fdlg->setAcceptMode(AcceptSave);            // Save
    fdlg->setOption(DontUseNativeDialog, true); // Do not use native dialog boxes
    if (selectedFilter && !selectedFilter->isEmpty()) {
        fdlg->selectNameFilter(*selectedFilter);
    }

    fdlg->connect(fdlg.get(), &QFileDialog::accepted, [&] {
        if (selectedFilter) {
            *selectedFilter = fdlg->selectedNameFilter();
        }

        selectedUrl = fdlg->selectedUrls().value(0);
    });

    fdlg->connect(fdlg.get(), &QFileDialog::finished,
                  [&](int) { loop.exit(); });

    // open
    fdlg->open();

    // Asynchronous operation
    if (run) {
        run();
    }

    // Non blocking wait
    loop.exec(QEventLoop::DialogExec);

    return selectedUrl;
}

Call example:

...
QString file_name = QLsFileDialog::getOpenFileNameO(
    [=] {}, this, tr("choose script(*.lua): "), ".",
    tr("script(*.lua);; all(*)"));
...
...
QString file_name = QLsFileDialog::getSaveFileNameO(
    [=] {
        // Register keyboard
        RegisterInputWidget(
                findChild<QLsFileDialog *>()->findChild<QLineEdit *>(),
                KeyboardType::StandardType);
    },
    this, tr("choose script(*.lua): "), ".",
    tr("script(*.lua);; all(*.*)"));
...

2. QLsInputDialog

qls_input_dialog.h

#ifndef QLSINPUTDIALOG_H
#define QLSINPUTDIALOG_H

#include <QInputDialog>

class QLsInputDialog : public QInputDialog
{
    Q_OBJECT
public:
    QLsInputDialog(QWidget *parent = nullptr,
                   Qt::WindowFlags flags = Qt::WindowFlags());

    // O: Means idlg - > open(); Formal asynchronous execution
    static QString getTextO(
        std::function<void()> run, QWidget *parent, const QString &title,
        const QString &label, QLineEdit::EchoMode echo = QLineEdit::Normal,
        const QString &text = QString(), bool *ok = nullptr,
        Qt::WindowFlags flags = Qt::WindowFlags(),
        Qt::InputMethodHints inputMethodHints = Qt::ImhNone);
};

#endif // QLSINPUTDIALOG_H

qls_input_dialog.cpp

#include "ui_wrapper/qls_input_dialog.h"

#include <QApplication>

QLsInputDialog::QLsInputDialog(QWidget *parent, Qt::WindowFlags flags)
    : QInputDialog(parent, flags)
{
}

QString QLsInputDialog::getTextO(std::function<void()> run, QWidget *parent,
                                 const QString &title, const QString &label,
                                 QLineEdit::EchoMode echo, const QString &text,
                                 bool *ok, Qt::WindowFlags flags,
                                 Qt::InputMethodHints inputMethodHints)
{
    QString textValue;
    QEventLoop loop;
    QSharedPointer<QLsInputDialog> idlg(new QLsInputDialog(parent, flags));
    idlg->setWindowTitle(title);
    idlg->setLabelText(label);
    idlg->setTextEchoMode(echo);
    idlg->setTextValue(text);
    idlg->setInputMethodHints(inputMethodHints);

    idlg->connect(idlg.get(), &QInputDialog::accepted,
                  [&] { textValue = idlg->textValue(); });

    idlg->connect(idlg.get(), &QInputDialog::finished, [&](int result) {
        if (ok) {
            *ok = (result == QDialog::Accepted);
        }

        loop.exit();
    });

    // open
    idlg->open();

    // Asynchronous operation
    if (run) {
        run();
    }

    // Non blocking wait
    loop.exec(QEventLoop::DialogExec);

    return textValue;
}

Call example:

...
QString str_op_mode_pwd = QLsInputDialog::getTextO(
    [=] {
        RegisterInputWidget(
            findChild<QInputDialog *>()->findChild<QLineEdit *>(),
            KeyboardType::StandardType);
    },
    this, tr("Operational Mode Password"), tr("Enter Password"),
    QLineEdit::Password);
...

Keywords: Qt UI

Added by baennaeck on Fri, 21 Jan 2022 10:55:00 +0200