在 Qt 之 自定义提示信息框—迅雷风格 这篇文章中讲述了如何实现迅雷风格的自定义提示框。那么在这一篇中我们就讲述一下如何实现QQ风格的提示框。
整体代码与Qt 之 自定义提示信息框—迅雷风格 中的代码类似,主要是界面样式上的不同,下面先看一下效果图。
看代码之前需要看一下 Qt 之 自定义窗口标题栏 这一篇文章,因为这里用到了这篇文章中写到的自定义标题栏。
这里新建任务窗口类MyMessageBox 继承了BaseWindow类,所以省去了一些代码实现(主要包括顶部标题栏、鼠标按住标题栏进行拖动,窗口背景色等 ),有需要的小伙伴可以去看一下这一篇文章。
如果需要了解如何实现窗口的模态与非模态对话框可以看一下Qt 之 模态与非模态窗口的介绍及 实现QDialog的exec()方法 这一篇文章。
#include <QWidget>#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_OBJECTpublic: 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;};
#include "mymessagebox.h"#include <QPainter>#include <QCloseEvent>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("非模态窗口")); }}
我们看到设置为模态窗口,弹出提示框后,父窗口被阻塞,无法响应我们的操作。并且在我们点击确定/取消 按钮后,调用showMyMessageBox()方法之后的代码得以执行。
测试代码中根据showMyMessageBox()的返回值来设置父窗口界面上显示用户操作结果(点击确定还是取消),而这些操作都是在提示框关闭之后执行的。以此说明,父窗口的操作都会被阻塞直到关闭提示框。
我们将showMyMessageBox()方法中的父指针修改为NULL。来看看效果
int result = MyMessageBox::showMyMessageBox(NULL, QStringLiteral("删除好友"), QStringLiteral("您确定要删除XXX好友吗?"), MESSAGE_WARNNING, BUTTON_OK_AND_CANCEL, true);
我们看到打开提示框后,父窗口仍可接受用户响应(可以移动,并且点击打开窗口按钮,可以再次弹出一个提示框)。但是父窗口中显示的结果(确定/取消)还是在提示框关闭之后进行更新,说明设置为模态窗口后,尽管不设置父窗口指针,showMyMessageBox()方法之后的代码要等待窗口关闭之后才能执行。
我们将showMyMessageBox()方法最后一个参数修改为false,设置为非模态窗口。
int result = MyMessageBox::showMyMessageBox(this, QStringLiteral("删除好友"), QStringLiteral("您确定要删除XXX好友吗?"), MESSAGE_WARNNING, BUTTON_OK_AND_CANCEL, false);
我们看到设置为非模态窗口,打开提示框,并不会阻塞父窗口的响应。同时,我们将父窗口指针传入showMyMessageBox()方法中,我们看到提示框会一直显示在父窗口之上。在Qt中子窗口会一直显示在父窗口之上。
同时,设置为非模态,showMyMessageBox()方法会立即返回,不会阻塞后面代码的执行。我们也看到在打开提示框时,父窗口结果立即更新为非模态窗口,而不是在提示框关闭之后。
int result = MyMessageBox::showMyMessageBox(NULL, QStringLiteral("删除好友"), QStringLiteral("您确定要删除XXX好友吗?"), MESSAGE_WARNNING, BUTTON_OK_AND_CANCEL, false);
将父窗口指针修改为NULL后,我们发现提示框不会一直显示在父窗口之上了。
这里我们在 Qt 之 自定义提示信息框—迅雷风格 代码的基础上修改了样式,得到了QQ风格的自定义提示框效果。代码很简单,关键在于尝试。
有兴趣的小伙伴可以自行修改样式,来实现另一番效果。欢迎一起交流哈 O(∩_∩)O!
联系客服