Qt 之 自定义提示信息框—QQ风格

一、简述

在 Qt 之 自定义提示信息框—迅雷风格 这篇文章中讲述了如何实现迅雷风格的自定义提示框。那么在这一篇中我们就讲述一下如何实现QQ风格的提示框

整体代码与Qt 之 自定义提示信息框—迅雷风格 中的代码类似,主要是界面样式上的不同,下面先看一下效果图。

Qt 之 自定义提示信息框—QQ风格_第1张图片

二、代码之路

看代码之前需要看一下 Qt 之 自定义窗口标题栏 这一篇文章,因为这里用到了这篇文章中写到的自定义标题栏。

这里新建任务窗口类MyMessageBox 继承了BaseWindow类,所以省去了一些代码实现(主要包括顶部标题栏、鼠标按住标题栏进行拖动,窗口背景色等 ),有需要的小伙伴可以去看一下这一篇文章。

如果需要了解如何实现窗口的模态与非模态对话框可以看一下Qt 之 模态与非模态窗口的介绍及 实现QDialog的exec()方法 这一篇文章。

mymessagebox.h

#include 
#include "ui_mymessagebox.h"
#include "basewindow.h"

enum ChosseResult
{
    ID_OK = 0,                      // 确定;
    ID_CANCEL                       // 取消;
};

enum MessageType
{
    MESSAGE_INFORMATION = 0,        // 提示信息;
    MESSAGE_WARNNING,               // 提示警告;
    MESSAGE_QUESTION,               // 提示询问;
    MESSAGE_INPUT                   // 提示输入框;
};

enum MessageButtonType
{
    BUTTON_OK = 0,                  // 只有确定按钮;
    BUTTON_OK_AND_CANCEL,           // 确定、取消按钮;
    BUTTON_CLOSE                    // 关闭按钮;
};

class MyMessageBox : public BaseWindow
{
    Q_OBJECT

public:
    MyMessageBox(QWidget *parent = 0);
    ~MyMessageBox();

    void setWindowTitle(QString title, int titleFontSize = 10);
    void setContentText(QString contentText);
    void setMessageType(MessageType messageType);
    void setButtonType(MessageButtonType buttonType);
    void setMessageContent(QString messageContent);

public:
    int static showMyMessageBox(QWidget* parent, const QString &title,const QString &contentText , MessageType messageType, MessageButtonType messageButtonType , bool isModelWindow = false);


private:
    void initWindow();
    void initTitleBar();
    int exec();

    void paintEvent(QPaintEvent *event);
    void closeEvent(QCloseEvent *event);

private slots:
    void onOkClicked();
    void onCancelClicked();

private:
    Ui::MyMessageBox ui;
    QEventLoop* m_eventLoop;
    ChosseResult m_chooseResult;
};

mymessagebox.cpp

#include "mymessagebox.h"
#include 
#include 

MyMessageBox::MyMessageBox(QWidget *parent)
    : BaseWindow(parent)
    , m_eventLoop(NULL)
    , m_chooseResult(ID_CANCEL)
{
    ui.setupUi(this);
    initWindow();
}

MyMessageBox::~MyMessageBox()
{

}

void MyMessageBox::initWindow()
{
    initTitleBar();
    loadStyleSheet("MyMessageBox/MyMessageBox");
    Qt::WindowFlags flags = this->windowFlags();
    this->setWindowFlags(flags | Qt::Window);

    ui.inputContent->setVisible(false);

    connect(ui.pButtonOk, SIGNAL(clicked()), this, SLOT(onOkClicked()));
    connect(ui.pButtonCancel, SIGNAL(clicked()), this, SLOT(onCancelClicked()));
}

// 初始化标题栏;
void MyMessageBox::initTitleBar()
{
    m_titleBar->move(0, 0);
    m_titleBar->setWindowBorderWidth(0);
    // 设置标题栏颜色,在迅雷风格中标题栏默认为白色;
    m_titleBar->setBackgroundColor(185, 92, 125);
    m_titleBar->setButtonType(ONLY_CLOSE_BUTTON);
    m_titleBar->setTitleWidth(this->width());
    // 设置标题栏图标,在迅雷风格中没有设置图标;
    m_titleBar->setTitleIcon(":/Resources/MyMessageBox/WindowIcon.png", QSize(16, 16));
}

void MyMessageBox::paintEvent(QPaintEvent *event)
{
    QPainter painter(this);

    // 绘制窗口白色背景色;
    QPainterPath pathBack;
    pathBack.setFillRule(Qt::WindingFill);
    pathBack.addRoundedRect(QRect(0, 0, this->width(), this->height()) , 3 , 3);
    painter.setRenderHint(QPainter::SmoothPixmapTransform, true);
    painter.fillPath(pathBack, QBrush(QColor(255, 255, 255)));

    // 绘制按钮部分灰色背景;
    QPainterPath pathButtonBack;
    pathButtonBack.setFillRule(Qt::WindingFill);
    pathButtonBack.addRoundedRect(QRect(0, 116, this->width(), 48) , 3 , 3);
    painter.setRenderHint(QPainter::SmoothPixmapTransform, true);
    painter.fillPath(pathButtonBack, QBrush(QColor(245, 230, 233)));

    return QWidget::paintEvent(event);
}

void MyMessageBox::setWindowTitle(QString title , int titleFontSize)
{
    m_titleBar->setTitleContent(title, titleFontSize);
}

void MyMessageBox::setContentText(QString contentText)
{
    ui.MessageContent->setText(contentText);
}

void MyMessageBox::setMessageType(MessageType messageType)
{
    switch (messageType)
    {
    case MESSAGE_INFORMATION:
        ui.MessageIcon->setPixmap(QPixmap(":/Resources/MyMessageBox/information.png"));
        break;
    case MESSAGE_WARNNING:
        ui.MessageIcon->setPixmap(QPixmap(":/Resources/MyMessageBox/warnning.png"));
        break;
    case MESSAGE_QUESTION:
        ui.MessageIcon->setPixmap(QPixmap(":/Resources/MyMessageBox/question.png"));
        break;
    case MESSAGE_INPUT:
        ui.MessageIcon->setVisible(false);
        ui.inputContent->setVisible(true);
    default:
        break;
    }
}

void MyMessageBox::setButtonType(MessageButtonType buttonType)
{
    switch (buttonType)
    {
    case BUTTON_OK:
    {
        ui.pButtonOk->setText(QStringLiteral("确定"));
        ui.pButtonCancel->setVisible(false);
    }
        break;
    case BUTTON_OK_AND_CANCEL:
    {
        ui.pButtonOk->setText(QStringLiteral("确定"));
        ui.pButtonCancel->setText(QStringLiteral("取消"));
    }
        break;
    default:
        break;
    }
}

void MyMessageBox::setMessageContent(QString messageContent)
{
    ui.MessageContent->setText(messageContent);
}

int MyMessageBox::showMyMessageBox(QWidget* parent, const QString &title, const QString &contentText, MessageType messageType, MessageButtonType messageButtonType, bool isModelWindow)
{
    MyMessageBox * myMessageBox = new MyMessageBox(parent);
    myMessageBox->setWindowTitle(title);
    myMessageBox->setContentText(contentText);
    myMessageBox->setMessageType(messageType);
    myMessageBox->setButtonType(messageButtonType);
    if (isModelWindow)
    {
        // 设置为模态窗口时,参数parent必须设置父窗口指针,否则模态设置无效;
        // 因为 Qt::WindowModal 参数只对父窗口有效,如果想要模态对全局窗口都有效可以设置 Qt::ApplicationModal
        return myMessageBox->exec();
    }
    else
    {
        myMessageBox->show();
    }

    return -1;
}

int MyMessageBox::exec()
{
    // 因为QWidget没有exec()方法,所以需要自己定义来完成exec()方法;
    // 而exec()方法就是直接设置窗口显示为模态,并且窗口关闭结束后返回用户选择结果(按了确定还是取消按钮);
    // 而show()方法只是显示窗口,并不会设置窗口的模态或者非模态,需要自己调用setWindowModality()方法进行设置;
    // 而且show()方法并不会返回用户选择结果;
    // 这里也可以继承QDialog类,QDialog有自己的exec()方法,根据返回 Accepted, Rejected来决定是否按了确定按钮;

    // 设置为窗口级模态,也可设置为应用程序及模态 Qt::ApplicationModal;
    this->setWindowModality(Qt::WindowModal); 
    show();
    // 使用事件循环QEventLoop ,不让exec()方法结束,在用户选择确定或者取消后,关闭窗口结束事件循环,并返回最后用户选择的结果;
    // 根据返回结果得到用户按下了确定还是取消,采取相应的操作。从而模拟出QDialog类的exec()方法;
    m_eventLoop = new QEventLoop(this);
    m_eventLoop->exec();

    return m_chooseResult;
}

void MyMessageBox::onOkClicked()
{
    m_chooseResult = ID_OK;
    close();
}

void MyMessageBox::onCancelClicked()
{
    m_chooseResult = ID_CANCEL;
    close();
}

void MyMessageBox::closeEvent(QCloseEvent *event)
{
    // 关闭窗口时结束事件循环,在exec()方法中返回选择结果;
    if (m_eventLoop != NULL)
    {
        m_eventLoop->exit();
    }
    event->accept();
}

样式

*{font-family:Microsoft YaHei;}

QLabel#MessageContent
{
    font-size:14px;
}

QPushButton#pButtonOk
{
    border-radius:3px;
    background-color:rgb(244 , 244 , 244);
    border: 1px solid rgb(160 , 150 , 152);
}

QPushButton#pButtonOk:hover
{
    background-color:rgb(190 , 231 , 253);
    border: 1px solid rgb(79 , 173 , 216);
}

QPushButton#pButtonOk:pressed
{
    background-color:rgb(235 , 236 , 237);
    border: 1px solid rgb(79 , 173 , 216);
    padding-left:2px;
    padding-top:2px;
}

QPushButton#pButtonCancel
{
    border-radius:3px;
    background-color:rgb(244 , 244 , 244);
    border: 1px solid rgb(160 , 150 , 152);
}

QPushButton#pButtonCancel:hover
{
    background-color:rgb(190 , 231 , 253);
    border: 1px solid rgb(79 , 173 , 216);
}

QPushButton#pButtonCancel:pressed
{
    background-color:rgb(235 , 236 , 237);
    border: 1px solid rgb(79 , 173 , 216);
    padding-left:2px;
    padding-top:2px;
}

QLineEdit#inputContent
{
    border: 1px solid rgb(195 , 195 , 195);
}

QLineEdit#inputContent:hover
{
    border: 1px solid rgb(1 , 186 , 255 );
}

测试代码

设置为模态窗口

设置父窗口指针

void onButtonClicked()
{
    // showMyMessageBox()方法的最后一个参数为是否设置提示框为模态窗口;
    // 设置提示框为模态窗口,那么必须将父窗口指针传进showMyMessageBox()方法中,否则模态无效;
    // 因为MyMessageBox模态时设置的为Qt::WindowModal,只会影响父窗口,如需要阻塞整个应用程序,可设置为Qt::ApplicationModal;
    int result = MyMessageBox::showMyMessageBox(this, QStringLiteral("删除好友"), QStringLiteral("您确定要删除XXX好友吗?"), MESSAGE_WARNNING, BUTTON_OK_AND_CANCEL, true);
    if (result == ID_OK)
    {
        ui.label->setText(QStringLiteral("结果:确定"));
    }
    else if (result == ID_CANCEL)
    {
        ui.label->setText(QStringLiteral("结果:取消"));
    }
    else
    {
        ui.label->setText(QStringLiteral("非模态窗口"));
    }
}

效果图一

Qt 之 自定义提示信息框—QQ风格_第2张图片

我们看到设置为模态窗口,弹出提示框后,父窗口被阻塞,无法响应我们的操作。并且在我们点击确定/取消 按钮后,调用showMyMessageBox()方法之后的代码得以执行。
测试代码中根据showMyMessageBox()的返回值来设置父窗口界面上显示用户操作结果(点击确定还是取消),而这些操作都是在提示框关闭之后执行的。以此说明,父窗口的操作都会被阻塞直到关闭提示框。


不设置父窗口指针

我们将showMyMessageBox()方法中的父指针修改为NULL。来看看效果

int result = MyMessageBox::showMyMessageBox(NULL, QStringLiteral("删除好友"), QStringLiteral("您确定要删除XXX好友吗?"), MESSAGE_WARNNING, BUTTON_OK_AND_CANCEL, true);

效果图二

Qt 之 自定义提示信息框—QQ风格_第3张图片

我们看到打开提示框后,父窗口仍可接受用户响应(可以移动,并且点击打开窗口按钮,可以再次弹出一个提示框)。但是父窗口中显示的结果(确定/取消)还是在提示框关闭之后进行更新,说明设置为模态窗口后,尽管不设置父窗口指针,showMyMessageBox()方法之后的代码要等待窗口关闭之后才能执行。


设置为非模态窗口

设置父窗口指针

我们将showMyMessageBox()方法最后一个参数修改为false,设置为非模态窗口。

int result = MyMessageBox::showMyMessageBox(this, QStringLiteral("删除好友"), QStringLiteral("您确定要删除XXX好友吗?"), MESSAGE_WARNNING, BUTTON_OK_AND_CANCEL, false);

效果图三

Qt 之 自定义提示信息框—QQ风格_第4张图片

我们看到设置为非模态窗口,打开提示框,并不会阻塞父窗口的响应。同时,我们将父窗口指针传入showMyMessageBox()方法中,我们看到提示框会一直显示在父窗口之上。在Qt中子窗口会一直显示在父窗口之上

同时,设置为非模态,showMyMessageBox()方法会立即返回,不会阻塞后面代码的执行。我们也看到在打开提示框时,父窗口结果立即更新为非模态窗口,而不是在提示框关闭之后。


不设置父窗口指针

int result = MyMessageBox::showMyMessageBox(NULL, QStringLiteral("删除好友"), QStringLiteral("您确定要删除XXX好友吗?"), MESSAGE_WARNNING, BUTTON_OK_AND_CANCEL, false);

效果图四

Qt 之 自定义提示信息框—QQ风格_第5张图片

将父窗口指针修改为NULL后,我们发现提示框不会一直显示在父窗口之上了。


这里我们在 Qt 之 自定义提示信息框—迅雷风格 代码的基础上修改了样式,得到了QQ风格的自定义提示框效果。代码很简单,关键在于尝试。

有兴趣的小伙伴可以自行修改样式,来实现另一番效果。欢迎一起交流哈 O(∩_∩)O!


更多文章

Qt 之 自定义提示信息框—迅雷风格

Qt 之 模态与非模态窗口的介绍及 实现QDialog的exec()方法

Qt 之 自定义窗口标题栏

代码下载

Qt 之 自定义提示信息框—QQ、迅雷风格 (包含了QQ和迅雷两种风格的提示框)

你可能感兴趣的:(Qt,Qt,之,实现QQ窗口界面)