Qt 标准控件改造计划 文件/文本输入框 QLsFileDialog&QLsInputDialog

一、改造原因

使用 QInputDialog, QFileDialog 时,最常用的就是它们自带的几个 static 函数。优点是参数明确,调用方便,但与此同时带来的缺点就是灵活性不足。

在这里插入图片描述

情景1

当前页面有定时更新状态的控件,如波形图、OpenGL 模型等;

执行 QFileDialog::getOpenFileNamestatic 方法时,被弹出的对话框遮挡的页面中,定时更新状态的控件都被阻塞,对话框弹出期间不再更新。

被阻塞的原因是对话框采用 exec 方法执行,阻塞其他界面等待用户输入完成。

在这里插入图片描述

情景2

想要对 QInputDialog, QFileDialog 界面进行定制,如在弹出对话框输入框中弹出内置软键盘。

一般弹出内置软键盘需要注册控件事件过滤器,而直接执行 static 方法弹出的对话框,都是新 new 出来的,然后就直接 exec 运行了,根本没有机会去注册其输入框控件。

情景3

还是界面定制问题,QFileDialog 有个参数 DontUseNativeDialog 默认为 false,该参数控制 QFileDialog 界面采用的是系统文件对话框还是 Qt 控件自定义的对话框。

在这里插入图片描述

在采用系统对话框时,弹出的是操作系统的文件资源管理器,我们无法定制界面的 qss 样式。所以我们要手动将其设为 truesetOption(DontUseNativeDialog, true);

在这里插入图片描述

二、改造方法

综上所诉,我们主要有两个需求在调用方便的 static 函数中无法被满足。

  1. 对话框本身属性需要定制,如不使用操作系统界面,界面加载后对其控件进行设置。
  2. 对话框运行方式需要更改,不能阻塞其他界面更新。

从需求角度出发,解决方法就是:

提前定义一个成员变量,在构造的时候就进行注册,后续要弹出对话框时使用该成员变量进行操作。但这个方法调用起来很不方便,直接调用 static 方法才一条语句。

一般需要加载文件或者获取输入等交互界面很多,如果每一个界面都这么处理,结构和代码量上也不符合我们预期。

三、改造代码

于是,新的问题就是代码上的封装与优化。对于这种问题我们就很熟悉了,直接继承控件重写 static 方法。由于不是虚函数,无法直接重写,个人将新方法函数名做了更改,代码如下:

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: 指 fdlg->open(); 形式异步执行
    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); // 不使用本机对话框
    fdlg->findChild<QLineEdit *>()->setReadOnly(true); // 输入框只读
    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(); });

    // 打开
    fdlg->open();

    // 异步操作
    if (run) {
        run();
    }

    // 非阻塞等待
    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); // 不使用本机对话框
    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(); });

    // 打开
    fdlg->open();

    // 异步操作
    if (run) {
        run();
    }

    // 非阻塞等待
    loop.exec(QEventLoop::DialogExec);

    return selectedUrl;
}

调用举例:

...
QString file_name = QLsFileDialog::getOpenFileNameO(
    [=] {}, this, tr("choose script(*.lua):"), ".",
    tr("script(*.lua);; all(*)"));
...
...
QString file_name = QLsFileDialog::getSaveFileNameO(
    [=] {
        // 注册键盘
        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: 指 idlg->open(); 形式异步执行
    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();
    });

    // 打开
    idlg->open();

    // 异步操作
    if (run) {
        run();
    }

    // 非阻塞等待
    loop.exec(QEventLoop::DialogExec);

    return textValue;
}

调用举例:

...
QString str_op_mode_pwd = QLsInputDialog::getTextO(
    [=] {
        RegisterInputWidget(
            findChild<QInputDialog *>()->findChild<QLineEdit *>(),
            KeyboardType::StandardType);
    },
    this, tr("Operational Mode Password"), tr("Enter Password"),
    QLineEdit::Password);
...
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值