qt 创建第二个ui_Qt自定义提示信息弹窗
偶然看到 web
端某个应用,右下角的错误信息弹窗配合上动画看起来很不错,于是想通过 Qt
自己实现一个类似的功能
初定需求
- 弹窗信息从右下角屏幕外,移动到右下角,停留 3s 左右后,逐渐消失
- 鼠标悬浮在弹窗上时,即使弹窗已经停留了 3s,依旧不能消失, 等鼠标移出弹窗后,逐渐消失
- 弹窗上添加超链接,用户点击的时候,可以有对应操作
- 旧的弹窗信息没有消失时候,出现新的弹窗的话,新弹窗动画方式不变,旧的弹窗向上移动一个弹窗的单位
先定这几个,以后想到可以在额外实现
功能的实现
先放一个最后实现的 gif,之后在逐步解释实现的过程
接下来的部分主要是代码逻辑的分析,文章最后有工程在 github
的地址
右下角动画弹出
这个功能大概的逻辑
- 初始化一个弹窗,移动到右下角的屏幕外
- 一个
m_showTimer
定时器,每隔 1 ms, 弹窗向上移动 1 个单位,直到移动到指定位置 - 移动到指定位置后,
m_stayTimer
定时器,每隔 1000 ms 触发一次,3次之后,启动关闭的m_closeTimer
的定时器 m_closeTimer
定时器, 每隔 100 ms, 窗口的透明度 (完全不透明为1) 减少 0.2,直到透明度为 0 时,关闭窗口
messageshow.h
// 窗口最后稳定不动时的坐标QPoint end_showPoint;// 因为是从右下角滑出, x 方向是固定的,所以需要记录一下当前窗口的实时 y 坐标int m_currentHeight;
// 3 个定时器QTimer* m_showTimer;QTimer* m_stayTimer;QTimer* m_closeTimer;
// 记录 m_stayTimer 执行的次数int m_stayExeTime;// 记录当前窗口的透明度double m_transparent;
messageshow.cpp
// 所有定时器对应执行的信号槽connect(m_showTimer, SIGNAL(timeout()), this, SLOT(slotMsgMove()));connect(m_stayTimer, SIGNAL(timeout()), this, SLOT(slotMsgStay()));connect(m_closeTimer, SIGNAL(timeout()), this, SLOT(slotMsgClose()));
// 显示弹窗的初始化函数void MessageShow::showMessage(){ m_showTimer->stop(); m_stayTimer->stop(); m_closeTimer->stop(); setWindowOpacity(1);
// 获取当前桌面的位置 QRect desk_rect = QApplication::desktop()->availableGeometry();
// 计算最后呈现的位置 end_showPoint.setX(desk_rect.width() - rect().width()); end_showPoint.setY(desk_rect.height() - rect().height());
// 先将弹窗移动到桌面外 m_currentHeight = desk_rect.height(); move(desk_rect.width(), desk_rect.height());
this->open(); m_showTimer->start(1);}
// m_showTimer 每次 timeout 后,需要执行的函数void MessageShow::slotMsgMove(){ // Qt 默认的坐标系原点是左上角,所以弹窗需要从右下角向上弹出,x 坐标一直不变, y 坐标一直减 m_currentHeight--; move(end_showPoint.x(), m_currentHeight); // 如果移动到制定位置后,m_showTimer 定时器停止,开启 m_stayTimer 定时器 if(m_currentHeight <= end_showPoint.y() ) { m_showTimer->stop(); // m_enterEvent 是为了标识之后的另一个功能,鼠标是否在弹窗内 if (!m_enterEvent) m_stayTimer->start(1000); }}
// m_stayTimer 每次 timeout 后, 需要执行的函数void MessageShow::slotMsgStay(){ // 记录 m_stayTimer 运行次数 m_stayExeTime++; // 当执行了3次,每次1000ms, 也就是停留 3s 之后, 启动 m_closeTimer 定时器 if(m_stayExeTime >= 3) { m_stayTimer->stop(); m_closeTimer->start(100); }}
// m_closeTimer 每次 timeout 后, 需要执行的函数void MessageShow::slotMsgClose(){ // 每次执行 1次后, 透明度减少 0.2, 直到透明度为0后,停止 m_closeTimer, 并关闭窗口 m_transparent -= 0.2; if(m_transparent <= 0.0) { m_closeTimer->stop(); emit sigClose(this); } else { setWindowOpacity(m_transparent); }}
这样,一个完整的从显示,到停留,到最后的消失,代码逻辑就完成
鼠标进入和离开的逻辑
鼠标进入后,弹窗一直停留, 鼠标离开弹窗,停留 1s 后,逐渐淡化消失
void MessageShow::enterEvent(QEvent *event){ Q_UNUSED(event);
m_enterEvent = true; // 弹窗一直停留,停止 m_stayTimer 定时器即可 m_stayTimer->stop(); m_transparent = 1.0; setWindowOpacity(1.0); m_closeTimer->stop();}
void MessageShow::leaveEvent(QEvent *event){ Q_UNUSED(event); m_enterEvent = false;
// 鼠标离开时后, 重新开启 m_stayTimer 定时器 // 因为要停留 1s, 将 m_stayExeTime 设置成已经执行2次, 再运行1次,也就是 1000ms 后,就会开启 m_closeTimer 定时器 m_stayExeTime = 2; m_stayTimer->start(1000);}
弹窗上添加超链接
上面介绍了完整的弹窗显示动画逻辑,现在只剩下设置弹窗中的内容设置
messageshow.h
void setInfomation(QString titleInfo, QString msg);void setInfomation(QString titleInfo, QString msg, QString extraInfo);
messageshow.cpp
// ui 界面里简单用 label_titleInfo 显示 title 内容, label_msg 里显示具体的信息// 此接口主要显示简单的弹窗内容void MessageShow::setInfomation(QString titleInfo, QString msg){ ui->label_titleInfo->setText(titleInfo); ui->label_msg->setText(msg); ui->label_msg->setToolTip(msg);}
// 弹窗上携带超链接的接口void MessageShow::setInfomation(QString titleInfo, QString msg, QString extraInfo){ ui->label_titleInfo->setText(titleInfo); ui->label_msg->setText(QString(") + extraInfo + QString("\">")+msg); ui->label_msg->setToolTip(msg);// void sigClickUrl(QString), 使用弹窗的对象捕获 sigClickUrl 信号,处理用户自定义的超链接 connect(ui->label_msg,SIGNAL(linkActivated(QString)),this,SIGNAL(sigClickUrl(QString)));}
多个弹窗同时出现的处理
涉及到对多个弹窗同时管理,这里使用 单例 的形式实现一个管理弹窗的管理类 PopupManage
,PopupManage
负责 MessageShow
对象的创建和销毁,现在的主要问题是,如何计算每个弹窗在稳定的时候位置
先简单理一下逻辑
- 第一个弹窗出现时,从右下角向上滑出,滑出到指定位置停留若干秒
- 此时弹出第二个弹窗,第二个弹窗依旧从右下角向上滑出,但是此时第一个弹窗应该继续向上移动一个弹窗的身位
所以,整体的代码逻辑可以这样,使用 PopupManage
创建一个新的 MessageShow
的弹窗, 将 MessageShow
对象存入 QList m_popupList
中, 当再次创建新的弹窗时,通知 m_popupList
中所有的 MessageShow
对象, 让它们向上移动一个弹窗的身位
现在看一下简单的代码逻辑
popupmanage.h
class PopupManage : public QObject{ Q_OBJECT
public: // 单例实现 static PopupManage * getInstance();
// 创建 MessageShow 对象的接口 void setInfomation(QString titleInfo, QString msg); void setInfomation(QString titleInfo, QString msg, QString extraInfo);
private slots: // 创建 MessageShow 对象之后,进行一些额外的操作 void addMessageShow(MessageShow *popup); // MessageShow 关闭的时候,PopupManage 需要执行的操作 void deleteMessageShow(MessageShow *popup); // 通知 m_popupList 所有的 MessageShow 的对象 void notifyMessageShow();
signals: void sigClickUrl(QString);
private: PopupManage(); ~PopupManage();
static PopupManage * m_popupManager; QList m_popupList;}popupmanage.cpp```C++// 创建 MessageShow 对象后,需要执行的一些操作void PopupManage::addMessageShow(MessageShow *popup){ connect(popup, &MessageShow::sigClose, this, &PopupManage::deleteMessageShow); connect(popup, &MessageShow::sigClickUrl, this, &PopupManage::sigClickUrl); m_popupList.append(popup);this->notifyMessageShow();}// MessageShow 关闭的时候,PopupManage 需要执行的操作void PopupManage::deleteMessageShow(MessageShow *popup){ disconnect(popup, &MessageShow::sigClose, this, &PopupManage::deleteMessageShow); disconnect(popup, &MessageShow::sigClickUrl, this, &PopupManage::sigClickUrl); m_popupList.removeOne(popup);delete popup; popup = nullptr;}// 通知所有的 MessageShow 改变位置void PopupManage::notifyMessageShow(){for(int i = 0; i m_popupList.at(i)->updatePosition(); }}PopupManage *PopupManage::getInstance(){return m_popupManager;}void PopupManage::setInfomation(QString titleInfo, QString msg){ MessageShow * popup = new MessageShow(); popup->setInfomation(titleInfo, msg); addMessageShow(popup);}void PopupManage::setInfomation(QString titleInfo, QString msg, QString extraInfo){ MessageShow * popup = new MessageShow(); popup->setInfomation(titleInfo, msg, extraInfo); addMessageShow(popup);}
这里 MessageShow
有个函数 updatePosition()
, 在创建 MessageShow
对象之后,直接调用了 updatePosition()
, 所有这里的 updatePosition()
一方面的作用是在弹窗第一次显示的 showMessage
功能,另一方面是创建新的弹窗之后的改变位置功能
void MessageShow::updatePosition(){ // 如果是第一次显示,调用 showMessage 函数, 否则是改变位置 if(m_firstShow){ this->showMessage(); m_firstShow = false; }else{ end_showPoint.setY(end_showPoint.y()- this->height()-2); m_currentHeight = m_currentHeight - this->height()-2; move(end_showPoint.x(), m_currentHeight); }}
这里 PopupManage
也可以这样实现
void PopupManage::setInfomation(QString titleInfo, QString msg){ MessageShow * popup = new MessageShow(); popup->setInfomation(titleInfo, msg); connect(popup, &MessageShow::sigClose, this, &PopupManage::deleteMessageShow); connect(popup, &MessageShow::sigClickUrl, this, &PopupManage::sigClickUrl);
for(int i = 0; i m_popupList.at(i)->updatePosition(); }
popup->showMessage(); m_popupList.append(popup);}
这里 updatePosition()
就可以只是改变位置,因为在通知所有 MessageShow
对象改变位置的时候,新创建的对象还没有存储到 m_popupList
中
还可以优化的地方
- 这里没有对弹窗的数量进行控制,可能会出现,弹窗超出界面的现象
- 目前弹窗的样式和布局设置的最简单的方式,也可以进一步改进
一些疑问
不知道有没有注意到这里的弹窗用的显示方法是 this->open()
, 而不是 show()
或者 exec()
exec()
是模态的弹窗,这个肯定直接被 pass 了,不可能让一个弹窗阻塞了主窗口- 当当前焦点的窗口是
QWidget
等非模态窗口时,show()
和open()
这两种显示的方法都可以,整体的弹窗功能都是完整 - 但是当当前焦点窗口是模态窗口时,使用
show()
时,MessageShow
的对象是无法获取鼠标事件事件的,这个我们是懂得,模态窗口实际上是新创建了一个事件循环,MessageShow
的对象的鼠标事件肯定不能获取,但是open
却可以,难道是新的事件循环里将MessageShow
也添加里进来了? 这里自己不是很懂,先给自己挖个新一篇博客的坑,有时间好好研究一些Qt
的事件循环机制
代码地址
github 地址 :https://github.com/catcheroftime/PopMessage
qt 创建第二个ui_Qt自定义提示信息弹窗相关推荐
- 【QT】自定义日历弹窗
一.构想 自定义日历弹窗的制作主要是分为两部分,1.自定义日历,2.点击LineEdit时,将日历窗口弹出来.首先针对如何自定义日历制定思路,通过上网查询 QT自带了一个日历类QCalendarWid ...
- Qt 之 自定义提示信息框—迅雷风格
一.简述 最近一直在研究迅雷9的界面,花了点时间做了几个通用的提示信息框,整体风格与迅雷9界面相同.支持模态和非模态两种模式窗口.提示框效果见下图. 我们可以根据设置不同的参数来设置提示框标题.显示内 ...
- android自定义xml弹窗,Android自定义弹窗提醒控件使用详解
Android中原生的Dialog弹窗提醒控件样式单一,有时候并不能满足我们的项目需求,而且一个工程里面有时候会在多处都用到弹窗提醒的功能,代码会出现大量的冗余,工作之余,就自己实现了这么一个弹窗提醒 ...
- 微信小程序之自定义模态弹窗(带动画)实例
代码地址如下: http://www.demodashi.com/demo/13991.html 一.前期准备工作 软件环境:微信开发者工具 官方下载地址:https://mp.weixin.qq.c ...
- android 自定义权限弹窗_Android-开源通用弹窗的封装CommonPopupWindow(总得向别人学点什么)...
终于也是自己封装一些东西了,不管烂不烂,还是自我鼓励了一把!(之前学maven发布也是这个目的) 开源地址: FanChael/CommonPopupWindow 弹窗效果如下: @tip后面会逐渐完 ...
- vue 定义全局弹框_VUE路由拦截:Vue自定义全局弹窗组件
前言 在任何一个平台中,如果需要增加用户黏度,除了用户需要的基本内容外,用户登录注册提交信息也是非常重要的一环,可以了解用户基本信息,用户喜欢等. 抛出前后端混合开发外,vue可以轻松的实现路由拦截. ...
- 这可能是第二好的自定义 View 教程之属性动画
上期文章镇楼: 这可能是第二好的自定义 View 教程之绘制 凯哥的文章确实写的细而好呀,这不,活生生把 面试系列 先放一放,继续讲解我们的动画. 为啥是第二好? 一看就是没看 前面的文章 的.这里就 ...
- 使用Qt创建XML文档及XML文档的增删改
目录名字 使用Qt创建XML文档及XML文档的增删改 XML文档的操作 :QXml + QFile+QTextStream 创建XML的一般步骤: XML 文档的操作(添加.查找.更新.删除) 添加的 ...
- Qt Creator添加新的自定义向导
Qt Creator添加新的自定义向导 添加新的自定义向导 向导类型 定位向导 向导开发提示 将动作映射到键盘快捷键 详细输出 将向导集成到内部版本 在向导中使用变量 本地化向导 创建向导 向导可用的 ...
- QT Core | 信号槽03 - 自定义信号与槽
文章目录 一.前言 二.新建一个QT控制台项目 2.1.New File or Project 2.2.Project Location 2.3.Define Build System 2.4.Kit ...
最新文章
- 力扣(LeetCode)刷题,简单+中等题(第28期)
- cs架构嵌入bs_CS与BS架构区别、比较、及现状与趋势分析
- 手机文件share.php,华为P10手机Huawei Share怎么用?一秒共享手机文件
- 白话Elasticsearch73_ES生产集群中的索引管理01
- 联想EDU同传系统 版本7.5 7.6在机房中出现的一些故障和解决办法
- openwrt dhcp 无法获取ip_电脑的 ip 是怎么来的呢?我又没有配置过
- Java 远程通讯技术及原理分析
- 关于laravel数据库问题
- 常用html元素的取值和赋值方法总结
- 烧钱两年,做事对得起工资,也要对得起公司这份决心
- Unity 安装失败原因
- Ubuntu-区域截图
- foxmail超大附件服务器文件怎么删,foxmail邮件太大怎么发?foxmail发送超大附件的方法...
- AIOT:基于智能家居谈AIOT
- 使用Keras画神经网络准确性图
- docker学习笔记(二)docker常用命令
- 【全栈软件测试】软件测试学习路线介绍
- 云计算基础概念——Iaas、PaaS、SaaS
- Flagger on ASM——基于Mixerless Telemetry实现渐进式灰度发布系列 3 渐进式灰度发布
- 禁止windows更新唤醒计算机,windows10睡眠被自动更新唤醒的解决方法
热门文章
- java 注册表 下载_Java修改windows注册表(完全修改)
- python 从入门到实践_Python编程从入门到实践日记Day36
- centos6 和 centos7 防火墙基本操作
- maven实现RBAC权限管理,实现不同角色下对应不同菜单
- fortran语言和python_fortran是什么语言吗?
- 打开gedit_使用 gedit 文本编辑器优化你的 GNOME 体验 | Linux 中国
- VideoView播放视频会引起其它音乐播放器暂停问题解决
- 【转】C#字符串转换为日期
- 数据库IO性能知识整理
- linux命令与示例 week 3