40、对话框---------事件系统

对话框

  1. 对话框简介

对话框(Dialog) 是一种弹出式窗口,用于与用户进行交互,传递信息或获取用户输入。在 Qt 中,对话框广泛用于提示信息、获取输入、设置选项等。

●模态对话框(Modal Dialog):在显示期间,禁止用户与其他窗口进行交互,用户必须先关闭对话框才能操作主窗口。

●非模态对话框(Non-Modal Dialog):允许用户在对话框显示的同时,与其他窗口进行交互。

  1. 对话框类型

模态对话框

●QMessageBox:用于显示信息、警告、错误或提问等消息。

●QInputDialog:用于获取用户输入,如文本、数字等。

●QFileDialog: 用于打开文件

●QProgressDialog: 进度对话框,用来显示进度

●QDialog:用于创建自定义的对话框。

非模态对话框

可以使用 show() 方法显示非模态对话框,与主窗口同时存在且可交互。

3. 常用对话框类

1 QMessageBox

QMessageBox 提供了标准的对话框用于显示信息、警告、错误和提问。

常用方法:

  • information(QWidget *parent, const QString &title, const QString &text, ...)
  • warning(QWidget *parent, const QString &title, const QString &text, ...)
  • critical(QWidget *parent, const QString &title, const QString &text, ...)
  • question(QWidget *parent, const QString &title, const QString &text, ...)

案例

创建一个MainWindow,在MainWindow里添加几个按钮,每个按钮点击后显示不同的QMessageBox,比如按下了警告按钮,就弹出警告信息。如下图

开发思路

在MainWindow构造函数里调整大小,并且增加垂直布局,将四个按钮加入布局中,并且链接点击信号和槽函数,在槽函数里弹出不同的消息框

cpp 复制代码
MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    //重置大小
    this->resize(600,400);
    //设置中心部件
    QWidget *centralWidget = new QWidget(this);
    QVBoxLayout *layout = new QVBoxLayout(centralWidget);

    //创建按钮点击后响应基本信息
    QPushButton * infoBtn = new QPushButton("显示信息对话框", this);
    layout->addWidget(infoBtn);

    //创建警告信息
    QPushButton * warnBtn = new QPushButton("显示警告对话框", this);
    layout->addWidget(warnBtn);

    //创建危机信息
    QPushButton * criticalBtn = new QPushButton("显示危机对话框", this);
    layout->addWidget(criticalBtn);

    //创建提问信息
    QPushButton * questionBtn = new QPushButton("显示提问对话框", this);
    layout->addWidget(questionBtn);

    setCentralWidget(centralWidget);
    // 响应信息按钮
    connect(infoBtn, &QPushButton::clicked, this, [=]() {
           //弹出信息对话框
           QMessageBox::information(this, "信息", "这是一个信息对话框");
       });

    //响应警告按钮被点击
    connect(warnBtn, &QPushButton::clicked, this, [=]() {
            //弹出警告对话框
           QMessageBox::warning(this, "警告", "这是一个警告对话框");
       });

    //响应危机按钮被点击
    connect(criticalBtn, &QPushButton::clicked, this, [=]() {
            //弹出警告对话框
           QMessageBox::critical(this, "危机", "这是一个危机对话框");
       });

    //响应提问按钮被点击
    connect(questionBtn, &QPushButton::clicked, this, [=]() {
            //弹出警告对话框
           QMessageBox::question(this, "提问", "元神启动了吗?");
       });
}

运行程序后,发现这些消息框如果不点击确定,会阻塞MainWindow。

2 QInputDialog

QInputDialog 用于获取用户输入,可以是文本、整数或浮点数。

常用方法:

  • getText(QWidget *parent, const QString &title, const QString &label, ...)
  • getInt(QWidget *parent, const QString &title, const QString &label, ...)
  • getDouble(QWidget *parent, const QString &title, const QString &label, ...)

案例练习:

我们需实现如下界面,点击输入姓名按钮后弹出输入对话框

我们在MainWindow里继续添加一个按钮,提示输入姓名

cpp 复制代码
//创建一个输入对话框按钮
QPushButton *inputButton = new QPushButton("输入姓名", this);
layout->addWidget(inputButton);

然后编写槽函数,在槽函数中弹出一个输入对话框

cpp 复制代码
//响应输入框按钮被点击
connect(inputButton, &QPushButton::clicked, this, [=]() {
    bool ok;
    //弹出输入对话框
    QString name = QInputDialog::getText(this,
                                         "输入", "请输入你的姓名:",
                                         QLineEdit::Normal, "", &ok);
    //返回ok为true表示创建成功,name不为空则弹出信息框
    if (ok && !name.isEmpty()) {
        QMessageBox::information(this, "姓名", QString("你好,%1!").arg(name));
    }
});

3 QDialog(自定义对话框)

QDialog 是一个基类,用于创建自定义的对话框,可以通过 Qt Designer 设计界面,并添加自定义功能。

界面如下

点击ok后弹出提示框

创建自定义对话框的步骤:

  1. 设计界面
    • 使用 Qt Designer 创建一个新的对话框(.ui 文件)。
    • 添加需要的控件(如标签、输入框、按钮等)。
  1. 创建对话框类
    • 使用 QDialog 作为基类。
    • 在类中包含生成的 UI 类。
    • 实现对话框的功能,如接受用户输入、验证输入、信号与槽等。

开发思路:

我们创建一个简单的设置对话框,用户可以输入用户名和密码。右键项目选择新建设计师界面类

选择界面模板,创建一个Dialog带按钮组,按钮组在下方的界面

点击下一步,创建的Dialog名字为SetDialog

拖动两个水平布局放入SetDialog中,再将SetDialog设置为垂直布局

右侧布局管理器修改属性名

我们在SetDialog的构造函数中设置密码输入框的模式为不可见

cpp 复制代码
SetDialog::SetDialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::SetDialog)
{
    ui->setupUi(this);
    //设置密码输入框模式
    ui->passwdEdit->setEchoMode(QLineEdit::Password);
}

我们为SetDialog增加两个方法,获取输入框里填写的名字和密码信息

cpp 复制代码
QString SetDialog::getUserName() const
{
    return ui->nameEdit->text();
}

QString SetDialog::getPassword() const
{
    return ui->passwdEdit->text();
}

接下来我们在MainWindow中添加一个按钮,提示用户点击,进而显示这个设置对话框

cpp 复制代码
QPushButton *openDialogButton = new QPushButton("打开设置对话框", this);
layout->addWidget(openDialogButton);

然后链接按钮点击信号,当按钮被点击后,显示对话框,对话框展示可以使用exec()方法,展示后对话框会阻塞用户其他操作,只能点击确定或者取消,

点击确定或者取消后,exec()会返回QDialog::Accept或者QDialog::Rejected.

如果点击的是确定,则弹出用户输入的信息

cpp 复制代码
//链接点击信号
connect(openDialogButton, &QPushButton::clicked, this, [=]() {
        //创建设置对话框
       SetDialog dialog(this);
       //exec表示模态方式启动
       if (dialog.exec() == QDialog::Accepted) {
           QString userName = dialog.getUserName();
           QString password = dialog.getPassword();
           QMessageBox::information(this, "设置",
                                    QString("用户名:%1\n密码:%2").arg(userName, password));
       }
   });

运行程序,点击打开设置对话框按钮,会触发槽函数,显示对话框,只有点击ok或者cancel之后,对话框才会退出,否则一直阻塞。

4. 对话框的实现与使用

1 创建和显示对话框

  • 模态对话框 :使用 exec() 方法显示,调用该方法后,程序会等待对话框关闭后继续执行。
cpp 复制代码
QDialog dialog;
dialog.exec(); // 模态
  • 非模态对话框 :使用 show() 方法显示,对话框显示后,程序继续执行,不会等待对话框关闭。
cpp 复制代码
QDialog *dialog = new QDialog(this);
dialog->show(); // 非模态

2 模态与非模态对话框的区别

  • 模态对话框
    • 阻塞主窗口,用户必须先关闭对话框才能返回主窗口。
    • 常用于需要强制用户响应的情况,如确认操作、输入关键信息等。
  • 非模态对话框
    • 不阻塞主窗口,用户可以在对话框和主窗口之间自由切换。
    • 常用于辅助信息显示、工具窗口等。

扩展

1 对话框事件过滤

在我们使用其他厂商的应用的时候,经常遇到这样一个场景

点击关闭窗口后会弹出提示框,提示是否确认关闭,如果点击取消,则不关闭对话框。

点击确认才关闭对话框。

怎么实现上述功能呢?

开发思路

1 先重写对话框的关闭事件处理函数closeEvent, 这个函数有个参数QCloseEvent,表示关闭事件

cpp 复制代码
void closeEvent(QCloseEvent *) override;

对于关闭事件继承自QEvent,QEvent提供了两个方法

cpp 复制代码
//表示接受
inline void accept() { m_accept = true; }
//表示忽略
inline void ignore() { m_accept = false; }

如果我们把这个事件忽略,他就不会被事件轮询处理。如果我们接受这个事件,那么他就会被事件轮询处理,所以给出关闭处理的完美答案

cpp 复制代码
void closeEvent(QCloseEvent *event)  {
    QMessageBox::StandardButton resBtn = QMessageBox::question(this, "关闭确认",
        tr("你确定要关闭对话框吗?\n"),
        QMessageBox::No | QMessageBox::Yes,
        QMessageBox::Yes);
    if (resBtn != QMessageBox::Yes) {
        event->ignore();
    } else {
        event->accept();
    }
}

2 QFileDialog

QT为了方便我们操作,提供了很多快速创建不同类型对话框的类,以及静态方法,比如文件对话框

常见用途:

  • 打开文件(如文本文件、图片等)
  • 保存文件
  • 选择目录
常用方法

静态函数:

  • static QString getOpenFileName(QWidget *parent = nullptr, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = nullptr, Options options = Options())

打开一个"打开文件"对话框,允许用户选择单个文件。

  • static QStringList getOpenFileNames(QWidget *parent = nullptr, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = nullptr, Options options = Options())

打开一个"打开文件"对话框,允许用户选择多个文件。

  • static QString getSaveFileName(QWidget *parent = nullptr, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = nullptr, Options options = Options())

打开一个"保存文件"对话框,允许用户选择或输入要保存的文件名。

  • static QString getExistingDirectory(QWidget *parent = nullptr, const QString &caption = QString(), const QString &dir = QString(), Options options = Options())

打开一个"选择目录"对话框,允许用户选择一个现有的目录。

示例代码

示例 1:打开单个文件

cpp 复制代码
#include <QApplication>
#include <QPushButton>
#include <QFileDialog>
#include <QMessageBox>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    QPushButton button("打开文件");
    button.resize(200, 50);
    button.show();

    QObject::connect(&button, &QPushButton::clicked, [&]() {
        QString fileName = QFileDialog::getOpenFileName(
            &button,
            "选择一个文件",
            "/home",
            "所有文件 (*.*);;文本文件 (*.txt);;图片文件 (*.png *.jpg *.bmp)");

        if (!fileName.isEmpty()) {
            QMessageBox::information(&button, "文件选择", QString("你选择了:\n%1").arg(fileName));
        }
    });

    return a.exec();
}

示例 2:保存文件

cpp 复制代码
#include <QApplication>
#include <QPushButton>
#include <QFileDialog>
#include <QMessageBox>
#include <QFile>
#include <QTextStream>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    QPushButton button("保存文件");
    button.resize(200, 50);
    button.show();

    QObject::connect(&button, &QPushButton::clicked, [&]() {
        QString fileName = QFileDialog::getSaveFileName(
            &button,
            "保存文件",
            "/home/untitled.txt",
            "文本文件 (*.txt);;所有文件 (*.*)");

        if (!fileName.isEmpty()) {
            QFile file(fileName);
            if (file.open(QIODevice::WriteOnly | QIODevice::Text)) {
                QTextStream out(&file);
                out << "这是测试保存的文件内容。\n";
                file.close();
                QMessageBox::information(&button, "保存文件", "文件已成功保存。");
            } else {
                QMessageBox::warning(&button, "保存文件", "无法打开文件进行写入。");
            }
        }
    });

    return a.exec();
}

示例 3:选择多个文件

cpp 复制代码
#include <QApplication>
#include <QPushButton>
#include <QFileDialog>
#include <QMessageBox>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    QPushButton button("选择多个文件");
    button.resize(200, 50);
    button.show();

    QObject::connect(&button, &QPushButton::clicked, [&]() {
        QStringList fileNames = QFileDialog::getOpenFileNames(
            &button,
            "选择多个文件",
            "/home",
            "所有文件 (*.*);;图片文件 (*.png *.jpg *.bmp);;文本文件 (*.txt)");

        if (!fileNames.isEmpty()) {
            QString files = fileNames.join("\n");
            QMessageBox::information(&button, "文件选择", QString("你选择了:\n%1").arg(files));
        }
    });

    return a.exec();
}

示例 4:选择目录

cpp 复制代码
#include <QApplication>
#include <QPushButton>
#include <QFileDialog>
#include <QMessageBox>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    QPushButton button("选择目录");
    button.resize(200, 50);
    button.show();

    QObject::connect(&button, &QPushButton::clicked, [&]() {
        QString dir = QFileDialog::getExistingDirectory(
            &button,
            "选择一个目录",
            "/home",
            QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);

        if (!dir.isEmpty()) {
            QMessageBox::information(&button, "目录选择", QString("你选择了目录:\n%1").arg(dir));
        }
    });

    return a.exec();
}
实际案例

案例:图像浏览器

创建一个简单的图像浏览器,允许用户选择一个目录,显示该目录中的所有图片文件。

选择文件夹后,会将选择文件夹内的所有图片展开,点击左侧列表内容,会在右侧显示图片

步骤概述:

  1. 创建主窗口,包含:
    • 一个按钮"选择目录"
    • 一个 QListWidget 显示图片列表
    • 一个 QLabel 显示选中的图片
  1. 实现功能
    • 点击按钮时,弹出 QFileDialog 选择目录
    • 扫描目录中的图片文件,并在 QListWidget 中显示
    • 选择列表中的图片时,显示在 QLabel

示例代码:

cpp 复制代码
#include <QPushButton>
#include <QFileDialog>
#include <QListWidget>
#include <QLabel>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QPixmap>
#include <QMessageBox>
#include <QDebug>

class ImageBrowser : public QDialog
{
    Q_OBJECT

public:
    ImageBrowser(QWidget *parent = nullptr) : QDialog(parent)
    {
        QPushButton *button = new QPushButton("选择目录", this);
        listWidget = new QListWidget(this);
        auto *right_wid = new QWidget(this);

        auto *right_layout = new QVBoxLayout(right_wid);

        imageLabel = new QLabel(this);
        imageLabel->setFixedSize(400,400);

        right_layout->addWidget(imageLabel);

        QHBoxLayout *mainLayout = new QHBoxLayout(this);
        QVBoxLayout *leftLayout = new QVBoxLayout();
        leftLayout->addWidget(button);
        leftLayout->addWidget(listWidget);
        mainLayout->addLayout(leftLayout);
        mainLayout->addWidget(right_wid);

        connect(button, &QPushButton::clicked, this, &ImageBrowser::selectDirectory);
        connect(listWidget, &QListWidget::itemClicked, this, &ImageBrowser::displayImage);
    }

    ~ImageBrowser(){
       // QMessageBox::information(nullptr,"提示信息","图片浏览器析构");
    }

private slots:
    void selectDirectory()
    {
        _dir = QFileDialog::getExistingDirectory(
            this,
            "选择图片目录",
            "/home",
            QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);

        if (!_dir.isEmpty()) {
            listWidget->clear();
            QStringList filters;
            filters << "*.png" << "*.jpg" << "*.jpeg" << "*.bmp" << "*.gif";
            QDir directory(_dir);
            QStringList images = directory.entryList(filters, QDir::Files);
            for (const QString &image : images) {
                listWidget->addItem(image);
            }
        }
    }

    void displayImage(QListWidgetItem *item)
    {
        QString imagePath = _dir + QDir::separator() + item->text();
        QPixmap pixmap(imagePath);

        if (!pixmap.isNull()) {
            //保持横纵比,且平滑缩放,返回一个新的缩放后的图片
            pixmap = pixmap.scaled(imageLabel->width(),imageLabel->height(),Qt::KeepAspectRatio,
                          Qt::SmoothTransformation);
            imageLabel->setPixmap(pixmap);
        }
    }

private:
    QListWidget *listWidget;
    QLabel *imageLabel;
    QString _dir;
};

mainwindow中添加按钮,并响应点击信号

cpp 复制代码
    //图像浏览器
    //创建按钮提示用户打开对话框
    QPushButton *openPicBtn = new QPushButton("打开图片浏览器", this);
    layout->addWidget(openPicBtn);

    //链接点击信号
    connect(openPicBtn, &QPushButton::clicked, this, [=]() {
        //创建浏览器
        _browser = new ImageBrowser();
        _browser->setWindowTitle("简单图像浏览器");
        _browser->resize(600, 450);
        _browser->exec();
        _browser->deleteLater();

    });

运行程序,点击按钮后可实现上述效果。

QProgressDialog

QProgressDialog 是 Qt 提供的一个对话框类,用于显示长时间运行任务的进度。它可以显示任务的进度条、标签信息,以及一个"取消"按钮,允许用户中断任务。

常见用途:

  • 下载或上传文件
  • 数据处理或转换
  • 文件复制或移动
常用方法
  • 构造函数:
cpp 复制代码
QProgressDialog(QWidget *parent = nullptr, Qt::WindowFlags flags = Qt::WindowFlags())

或者指定详细信息:

cpp 复制代码
QProgressDialog(const QString &labelText, const QString &cancelButtonText, int minimum, int maximum, QWidget *parent = nullptr, Qt::WindowFlags flags = Qt::WindowFlags())
  • 设置文本:
cpp 复制代码
void setLabelText(const QString &label);
void setCancelButtonText(const QString &text);
  • 范围和值:
cpp 复制代码
void setRange(int minimum, int maximum);
void setValue(int value);

或者使用单一参数设置进度范围:

cpp 复制代码
void setMaximum(int maximum);
void setMinimum(int minimum);
  • 其他设置:
cpp 复制代码
void setWindowModality(Qt::WindowModality modality);
void setAutoClose(bool autoClose);
void setAutoReset(bool autoReset);
  • 信号:
    • void canceled(): 当用户点击"取消"按钮时发出。
示例代码

示例 1:模拟一个耗时任务

实现如下图效果

我们可以在main函数中新增按钮和点击的响应逻辑

cpp 复制代码
//创建一个按钮演示进度框
//创建按钮提示用户打开对话框
QPushButton *progressBtn = new QPushButton("演示进度框", this);
layout->addWidget(progressBtn);

//链接点击信号
connect(progressBtn, &QPushButton::clicked, this, [=]() {
    //演示进度框
    QProgressDialog progress("正在执行任务...", "取消", 0, 100,this);
         progress.setWindowModality(Qt::WindowModal);
         progress.setMinimumDuration(0);
         progress.setValue(0);

         for (int i = 0; i <= 100; ++i) {
             // 模拟耗时任务
              // 暂停1秒
             QEventLoop loop;//定义一个新的事件循环
             QTimer::singleShot(1000, &loop, SLOT(quit()));//创建单次定时器,槽函数为事件循环的退出函数
             loop.exec();//事件循环开始执行,程序会卡在这里,直到定时时间到,本循环被退出


             progress.setValue(i);

             if (progress.wasCanceled()) {
                 QMessageBox::information(this, "取消", "任务已被取消。");
                 break;
             }
         }

         if (progress.value() == progress.maximum()) {
             QMessageBox::information(this, "完成", "任务已完成。");
         }

});
相关推荐
旖-旎2 小时前
分治(计算右侧小于当前元素的个数)(7)
c++·学习·算法·leetcode·排序算法·归并排序
迷海2 小时前
C++内存对齐
开发语言·c++
炘爚2 小时前
C++(流类:istream /ostream/istringstream /ostringstream)
开发语言·c++·算法
!停2 小时前
C++入门—内存管理
java·jvm·c++
A.A呐2 小时前
【C++第二十五章】智能指针
c++
塞北山巅2 小时前
Windows 下基于 MSYS2 搭建 C++ 开发环境:从安装到配置全指南
开发语言·c++·windows
海参崴-2 小时前
C语言与C++语言发展历史详解
java·c语言·c++
佳木逢钺3 小时前
[开源]玄武门之变的多变量数学建模与C++模拟系统——从历史事件到量化分析
c++·opencv·数学建模
Miki Makimura3 小时前
C++ 聊天室项目:Linux 环境搭建与问题总结
linux·开发语言·c++