QT 写一个属于自己的消息弹窗MessageBox

Posted cpp_learners

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了QT 写一个属于自己的消息弹窗MessageBox相关的知识,希望对你有一定的参考价值。

前言
在接触公司的一个桌面应用项目后,发现里面很多窗体都是自己写的而不是使用QT自带的,例如消息弹窗。今天这篇博客就记录下来如何自己写一个消息弹窗。

内容可能有点多,但都是本人自己一步一步操作后,测试可行后才记录下博客这里来的,希望对看到这篇博客的朋友有所帮助!

由于我是在centos7使用QT编写的代码,操作步骤或者界面什么的可能与Window使用QT会有些不一样,但是都是类似的。

下面是一部分运行截图:



我写的整个项目,将上传 csdn 和 百度网盘 后,放在下面 总结 处,需要的朋友自提!
项目所用到的资源文件也一起上传。


目录


一、创建消息窗体

首先得有一个主窗体,自己先手动创建一个QT项目,创建后如下:

然后根据下图指引,在创建一个子窗体,用做消息弹窗:

  1. 右键选择Add New…

  2. 选择Qt设计师界面类

  3. 选择Widget

  4. 可以适当修改一下名字

  5. 到此,窗体就创建好了

  6. 添加资源文件
    将资源文件夹msgbox放到项目路径下

    1). 右键选择Add New…

    2). 选择Qt Resource File

    3). 可以设当修改名字

    4). 再出现的页面中,下面点击 添加 - 添加前缀

    5). 此时前缀这里会出现一些字符串,删掉剩下一个反斜杠即可

    6). 点击 添加 - 添加文件

    7). 再出现的弹窗中全选文件,点击右上角Open添加进来

    8). 添加进来是这样着的,记得 Ctrl + s 保存一下

到了这样,说明资源文件已经添加进来了!

也可以添加自己喜欢的图片、资源进来设置!


二、设置消息弹窗

  1. 双击MessageBox.ui进入设计师界面,将宽度设置为400,高度设置为150.

  2. 窗体设置样式
    右键对象,选择改变样式表…
    将样式设置为:

    background:rgb(255,255,255);
    border:1px solid rgba(110, 123, 146, 1);
    

  1. 拖动两个Widget
    托两个Widget到窗体上:
    1). 第一个widget设置宽度400,高度30,x为0,y为0;修改对象名为:widget_1
    2). 第二个widget设置宽度400,高度120,x为0,y为30;修改对象名为:widget_2

  2. 给两个Widget添加标签和按钮

    1). widget_1
    设置widget_1样式为:
    background-image: url(:/msgbox/title_bg.png);

    拖一个Label:设置宽度90,高度20,x为10,y为5;修改对象名为:label_title;文本修改为:提示
    设置label_title样式为:
    border:0;color: rgb(255, 255, 255); background:transparent;

    拖一个PushButton:设置宽度18,高度18,x为375,y为6;修改对象名为:btn_close;删掉文本
    设置btn_close样式为:
    border:0px; background:transparent; background-image: url(:/msgbox/close_normal.png);

    2). widget_2
    设置widget_2样式为:
    background:transparent; background-color: rgb(255, 255, 255);

    拖一个Label:设置宽度32,高度32,x为40,y为30;修改对象名为:label_icon;删掉文本
    设置label_icon样式为:
    border:0; background:transparent; background-image: url(:/msgbox/tips_icon.png);

    拖一个Label:设置宽度291,高度41,x为80,y为30;修改对象名为:label_tips;文本可随意设置
    设置label_tips样式为:
    border:0; background:transparent; color: rgb(51, 51, 51);

    拖一个PushButton:设置宽度70,高度30,x为200,y为80;修改对象名为:btn_confirm;文本修改为:是
    设置btn_confirm样式为:
    border:0; background:transparent; background-image: url(:/msgbox/confirm_normal.png);

    拖一个PushButton:设置宽度70,高度30,x为290,y为80;修改对象名为:btn_cancel;文本修改为:否
    设置btn_cancel样式为:
    border:0; background:transparent; background-image: url(:/msgbox/cancel_normal.png);

    根据上面设置完成后,效果如下:

    注意自己添加资源文件的路径,路径不对,是无法设置图片的!

具体坐标,大小,可以自己根据具体情况而设置!


三、编写代码

严格按照步骤完成上面的操作后,下面我们可以开始写代码了。

  1. 首先在MessageBox.h文件中添加两个结构体:

    // 设在按钮个数
    enum MSG_TYPE
    
    	SA_OK           = 0,    // 只有一个“确定”按钮,且两秒钟后自动关闭消息窗口
    	SA_OKCANCEL     = 1,    // 一个“是”按钮,一个“否”按钮
    	SA_OKS          = 2     // 只有一个“确定”按钮,不会自动关闭消息窗口
    ;
    
    // 设在提示的类型
    enum MSG_TIP_TYPE
    
    	SA_SUCCESS      = 0,    // 完成
    	SA_FAILED       = 1,    // 错误
    	SA_WARNING      = 2,    // 警告
    	SA_TIPS         = 3,    // 提示
    	SA_QUESTION     = 4     // 未知
    ;
    
  2. 添加头文件 #include < QDialog >
    并将MessageBox的基础修改为 class MessageBox : public QDialog ;

    在构造函数中添加两个参数:

    MessageBox(QWidget *parent = nullptr, MSG_TYPE nType = SA_OKCANCEL, MSG_TIP_TYPE nTipType = SA_TIPS);
    

    定义两个私有成员变量,用于接收构造函数传过来的两个参数:

    private:
    	int m_nType;
    	int m_nTipType;
    

    实现构造函数:

    MessageBox::MessageBox(QWidget *parent, MSG_TYPE nType, MSG_TIP_TYPE nTipType) :
    	QDialog(parent),
    	ui(new Ui::MessageBox)
    
    	ui->setupUi(this);
    
    	m_nType = nType;
    	m_nTipType = nTipType;
    
    	setWindowFlags(Qt::Tool | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint);
    	// 设置窗体关闭时自动释放内存
    	this->setAttribute(Qt::WA_DeleteOnClose);
    
    
    	// 设在按钮
    	if(MSG_TYPE::SA_OK == m_nType)
    	
        	ui->btn_cancel->setVisible(false);  // 隐藏“否”按钮
        	ui->btn_confirm->setText("确定");
    	
    	else if(MSG_TYPE::SA_OKCANCEL == m_nType)
    	
        	ui->btn_cancel->setVisible(true);   // 显示“否”按钮
        	ui->btn_confirm->setText("是");
    	
    	else if (MSG_TYPE::SA_OKS == m_nType)
    	
        	ui->btn_cancel->setVisible(false);  // 隐藏“否”按钮
        	ui->btn_confirm->setText("确定");
    	
    
    	// 设在两个按钮样式,这样设在效果:当鼠标移动到按钮上方,会切换对应设在的图片,移开后又会设在回来
    	QString strStyle = "QPushButtonborder:0;background:transparent;background-image: url(:/msgbox/cancel_normal.png);"
                       "QPushButton:hoverborder:0;background:transparent;background-image: url(:/msgbox/cancel_hover.png);";
    	ui->btn_cancel->setStyleSheet(strStyle);
    
    	strStyle = "QPushButtonborder:0;background:transparent;background-image: url(:/msgbox/confirm_normal.png);"
               "QPushButton:hoverborder:0;background:transparent;background-image: url(:/msgbox/confirm_hover.png);";
    	ui->btn_confirm->setStyleSheet(strStyle);
    
    
    	// 根据参数,设在当前消息弹窗需要显示的图片(显示类型)
    	if(SA_SUCCESS == m_nTipType)
    	
        	strStyle = "QLabelborder:0;background:transparent;background-image: url(:/msgbox/success_icon.png);";
    	
    	else if (SA_FAILED == m_nTipType)
    	
        	strStyle = "QLabelborder:0;background:transparent;background-image: url(:/msgbox/error_icon.png);";
    	
    	else if (SA_WARNING == m_nTipType)
    	
        	strStyle = "QLabelborder:0;background:transparent;background-image: url(:/msgbox/warning_icon.png);";
    	
    	else if (SA_TIPS == m_nTipType)
    	
        	strStyle = "QLabelborder:0;background:transparent;background-image: url(:/msgbox/tips_icon.png);";
    	
    	else if (SA_QUESTION == m_nTipType)
    	
        	strStyle = "QLabelborder:0;background:transparent;background-image: url(:/msgbox/question_icon.png);";
    	
    	ui->label_icon->setStyleSheet(strStyle);
    
    

    也就是设置按钮个数和设置现实的图片!

  3. 实现按钮的槽函数

    private slots:
    	void on_btn_close_clicked();    // “叉”按钮槽函数
    	void on_btn_confirm_clicked();  // “是”按钮槽函数
    	void on_btn_cancel_clicked();   // “否”按钮槽函数
    
    void MessageBox::on_btn_close_clicked()
    
    	// .exec()结束后返回0
    	done(0);
    	this->close();
    
    
    void MessageBox::on_btn_confirm_clicked()
    
    	// .exec()结束后返回1
    	done(1);
    	this->close();
    
    
    void MessageBox::on_btn_cancel_clicked()
    
    	// .exec()结束后返回0
    	done(0);
    	this->close();
    
    

    当实现到这一步,说明已经自己写的messagebox可以运行了!

  4. 测试MessageBox
    在maindow的构造函数函数中写下代码:

    MessageBox *messagebox = new MessageBox(this);
    messagebox->exec();
    


运行后效果,感觉还可以喔,慢慢的成就感!
但是还不全面,还有很多地方要优化,例如设置成一个按钮时,两秒后自动关闭窗体的功能还没有写;还有这个窗体不能移动;还有提示语还不能设置;下面将开始优化!


四、优化代码

1).窗体移动

在MessageBox.h文件中:

  1. 添加头文件
    添加头文件:#include < QPoint >、#include < QMouseEvent >

  2. 添加私有变量:

    private:
    	QPoint m_pMousePoint;
    	bool m_bMousePressed;
    
  3. 重写鼠标移动事件方法

    protected:
    	void mouseMoveEvent(QMouseEvent *event) override;		// 鼠标移动
    	void mousePressEvent(QMouseEvent *event) override;		// 鼠标按下
    	void mouseReleaseEvent(QMouseEvent *event) override;	// 鼠标释放
    
    
    void MessageBox::mouseMoveEvent(QMouseEvent *event)
    
    	// 如果鼠标是按下的
    	if(m_bMousePressed)
    	
        	// 移动窗体
        	move(event->pos() - m_pMousePoint + pos());
    	
    
    
    void MessageBox::mousePressEvent(QMouseEvent *event)
    
    	// 鼠标左键按下
    	if(event->button() == Qt::LeftButton)
    	
        	// 标志设置true
        	m_bMousePressed = true;
        	// 获取当前窗体位置
        	m_pMousePoint = event->pos();
    	
    
    
    void MessageBox::mouseReleaseEvent(QMouseEvent *event)
    
    	// 鼠标左键释放(松开)
    	if(event->button() == Qt::LeftButton) 
        	// 鼠标松开,标志设在false
        	m_bMousePressed = false;
    	
    
    
  4. 不出意外的话,到这一步,窗体已经可以移动了,赶快去试试吧!

2). 设置消息弹窗提示语

在MessageBox.h文件中:

  1. 添加公共方法:

    public:
    	void SetTips(const std::string& strTips);
    
  2. 实现它设置提示语

    void MessageBox::SetTips(const std::string &strTips)
    
    	ui->label_tips->setText(strTips.c_str());
    
    
  3. 测试
    在maindow的构造函数中添加代码:

    MessageBox *messagebox = new MessageBox(this);
    messagebox->SetTips("你好,测试提示语!");
    messagebox->exec();
    


可以发现,已经可以设置提示语了!

3). 两秒后自动关闭窗体

在MessageBox.h文件中:

  1. 添加头文件
    添加头文件:#include < thread >、#include < unistd.h >

  2. 添加私有变量:

    private:
        std::thread *t1;	// C++线程
    

    并在构造函数的最上方进行初始化:t1 = nullptr;
    一定要在构造函数里的最上方进行初始化,否则接下来的操作会导致软件崩溃!

  3. 添加两个私有成员方法
    一个用来定义线程,一个用来被线程执行

    private:
    	// 设在sec秒后关闭窗体
    	void _startTimerClose(int sec = 2);
    	// 线程执行的函数,用于计时关闭窗体
    	void ticktack(int sec = 2);
    

    实现这两个私有成员方法

    void MessageBox::_startTimerClose(int sec)
    
    	if (t1) 
        	delete t1;
        	t1 = nullptr;
    	
    
    	// 定义线程
    	t1 = new std::thread(&MessageBox::ticktack, this, sec);
    	t1->detach();
    
    
    void MessageBox::ticktack(int sec)
    
    	int secTicktack = 0;
    	while (false == this->isHidden())      // 如果窗体是显示的
    
        	// 判断秒数相等,则关闭窗体
        	if (secTicktack == sec) 
            	done(1);
            	this->close();
            	break;
        	
    
        	sleep(1);   // Sleep(1);
        	secTicktack++;
        	printf("%d   -   ticktack = %d\\n", sec, secTicktack);
    	
    
    
  4. 重写窗体显示事件方法

    protected:
    	// 窗体show前的事件操作
    	void showEvent(QShowEvent *) override;
    
    
    void MessageBox::showEvent(QShowEvent *)
    
    	// 判断窗体类型,启动线程计时关闭窗体
    	if(MSG_TYPE::SA_OK == m_nType) 
        	_startTimerClose(2);
    	
    
    
  5. 析构函数
    在析构函数中释放线程内存

    MessageBox::~MessageBox()
    
    	if (t1) delete t1;
    	delete ui;
    
    

好了,到了这里,代码已经写完了,可以去测试了,不出意外的话,只要设置的是MSG_TYPE::SA_OK类型的提示窗体,那么,两秒后就会自动关闭窗体!
测试代码:

MessageBox *messagebox = new MessageBox(this, MSG_TYPE::SA_OK, MSG_TIP_TYPE::SA_SUCCESS);
messagebox->SetTips("你好,测试提示语!");
messagebox->exec();


五、封装调用

接下来,调用代码封装成一个函数,方便调用!

在mainwindow中添加方法:

public:
	int ShowMsgBox(const std::string strTips, MSG_TYPE nType, const MSG_TIP_TYPE nTipType);	

实现该方法:

int MainWindow::ShowMsgBox(const std::string strTips, MSG_TYPE nType, const MSG_TIP_TYPE nTipType)

    MessageBox* pMsgBox = new MessageBox(nullptr, nType, nTipType); // 定义指针,不需要手动释放内存,因为我们设置了窗体关闭时自动释放内存
    pMsgBox->SetTips(strTips.c_str());      // 设置提示语
    pMsgBox->setModal(true);                // 设置为模态
    int nRes = pMsgBox->exec();             // 运行
    return nRes;                            // 返回值

简单测试:

int res = ShowMsgBox("实现了代码封装,调用起来更加简单了!", MSG_TYPE::SA_OKCANCEL, MSG_TIP_TYPE::SA_FAILED);


已经完美的调用了,看起来还不错喔,哈哈!


六、返回值

这里再说一下返回值。

代码中,我们设置了,只有点击“是”按钮,或者“确定”按钮,或者两秒后自动关闭窗体才会返回1,其他情况都是返回0!

根据这个特性,我们就可以进行具体判断使用了!

例如:

int res = ShowMsgBox("您却要退出程序吗?", MSG_TYPE::SA_OKCANCEL, MSG_TIP_TYPE::SA_QUESTION);
if (res == 1) 
    printf("退出程序!\\n");
 else 
    printf("返回值:%d\\n", res);

当点击"是":

当点击"否":


七、MessageBox代码

  1. MessageBox.h

    #ifndef MESSAGEBOX_H
    #define MESSAGEBOX_H
    
    #include <QDialog>
    #include <QPoint>
    #include <QMouseEvent>
    
    #include <thread>
    #include <unistd.h>
    
    namespace Ui 
    class MessageBox;
    
    
    // 设在按钮个数
    enum MSG_TYPE
    
        SA_OK           = 0,    // 只有一个“确定”按钮,且两秒钟后自动关闭消息窗口
        SA_OKCANCEL     = 1,    // 一个“是”按钮,一个“否”按钮
        SA_OKS          = 2     // 只有一个“确定”按钮,不会自动关闭消息窗口
    ;
    
    // 设在提示的类型
    enum MSG_TIP_TYPE
    
        SA_SUCCESS      = 0,    // 完成
        SA_FAILED       = 1,    // 错误
        SA_WARNING      = 2,    // 警告
        SA_TIPS         = 3,    // 提示
        SA_QUESTION     = 4     // 未知
    ;
    
    
    class MessageBox : public QDialog
    
        Q_OBJECT
    
    public:
        explicit MessageBox(QWidget *parent = nullptr,MSG_TYPE nType = SA_OKCANCEL,MSG_TIP_TYPE nTipType = SA_TIPS);
        ~MessageBox();
    
        // 设置提示语
        void SetTips(const std::string& strTips);
    
    private:
        // 设在sec秒后关闭窗体
        void _startTimerClose(int sec = 2);
        // 线程执行的函数,用于计时关闭窗体
        void ticktack(int sec = 2);
    
    
    private slots:
        void on_btn_close_clicked();    // “叉”按钮槽函数
        void on_btn_confirm_clicked();  // “是”按钮槽函数
        void on_btn_cancel_clicked();   // “否”按钮槽函数
    
    
    protected:
        void mouseMoveEvent(QMouseEvent *event) override;       // 鼠标移动
        void mousePressEvent(QMouseEvent *event) override;      // 鼠标按下
        void mouseReleaseEvent(QMouseEvent *event) override;    // 鼠标释放
    
        // 窗体show前的事件操作
        void showEvent(QShowEvent *) override;
    
    private:
        Ui::MessageBox *ui;
        int m_nType;
        int m_nTipType;
        QPoint m_pMousePoint;
        bool m_bMousePressed;
        std::thread *t1;
    ;
    
    #endif // MESSAGEBOX_H
    
  2. MessageBox.cpp

    #include "MessageBox.h"
    #include "ui_MessageBox.h"
    
    MessageBox::MessageBox(QWidget *parent, MSG_TYPE nType, MSG_TIP_TYPE nTipType) :
        QDialog(parent),
        ui(new Ui::MessageBox)
    
        ui->setupUi(this);
    
        m_nType = nType;
        m_nTipType = nTipType;
        t1 =以上是关于QT 写一个属于自己的消息弹窗MessageBox的主要内容,如果未能解决你的问题,请参考以下文章

    python 弹窗提示和警告框MessageBox部件

    Qt 实现桌面右下角消息弹窗提示

    怎么样在java中弹出一个messagebox

    C#-WinForm-弹窗提示框-如何知道用户点击的是哪个按钮?

    python之tkinter库弹窗messagebox

    python之tkinter库弹窗messagebox