Qt QPropertyAnimation+QTimer实现自制悬浮窗
Qt下的悬浮窗
最近项目需要一个类似于360悬浮球类似的悬浮窗,当鼠标放入停留一段时间,就会展开悬浮窗,移出区域就会自动收起。随便在网上找了一下,没找到,想着熟悉Qt提升自己编程技术的出发点,我就自己造了个轮子,如果有问题,希望大家指正。
QPropertyAnimation
我用的是Qt自带的动画类,官方文档的解释是:
上面画红框的意思是,你可以指定属性的开始和结束值。
使用方法如下:
// 设置property为geometry,代表位置大小
m_Animation = new QPropertyAnimation(this, "geometry");
// 设置动画持续时间(单位ms)
m_Animation->setDuration(600);
// 设置动画的终止值
m_Animation->setEndValue(QRect(m_posX,m_posY,
this->width(), this->height()));
// 设置动画的开始
m_Animation->setStartValue(QRect(m_posX - ui->m_menu->width(), m_posY,
this->width(), this->height()));
// 设置动画的运动轨迹
m_Animation->setEasingCurve(QEasingCurve::InQuad);
m_Animation->start();
关于setEasingCurve()
这个函数,这个是设置动画的行动轨迹,参看Qt官方文档:
这里介绍了很多的动画曲线。
QTimer
另外一个核心的是定时器,简易的逻辑是:
当鼠标移入标题栏时,会开启弹出定时器,到时间就会执行弹出函数,当没有到时间,但是移除了标题时,关闭弹出计时器;
当鼠标移入菜单栏时,会关闭收起定时器,当移出菜单栏时,会开启收起定时器,到时间就会执行收起函数,当没有到时间就移入的时,关闭收起定时器;
定时器的初始化代码如下:
m_expandTimer = new QTimer();
m_flodTimer = new QTimer();
// 设置定时器时间(单位ms)
m_expandTimer->setInterval(700);
m_flodTimer->setInterval(700);
// 连接信号和槽
connect(m_expandTimer, &QTimer::timeout, this, &Floating::expandMenu);
connect(m_flodTimer, &QTimer::timeout, this, &Floating::flodMenu);
定时器时间到处理函数如下:
void Floating::expandMenu()
{
if (m_Animation->state() == QPropertyAnimation::Running) {
return;
}
m_isExpand = true;
setTitleIcon();
m_Animation->setStartValue(QRect(m_posX, m_posY,
this->width(), this->height()));
m_Animation->setEndValue(QRect(m_posX - ui->m_menu->width(), m_posY,
this->width(), this->height()));
m_Animation->start();
m_expandTimer->stop();
}
void Floating::flodMenu()
{
if (m_Animation->state() == QPropertyAnimation::Running) {
return;
}
m_isExpand = false;
setTitleIcon();
m_Animation->setEndValue(QRect(m_posX,m_posY,
this->width(), this->height()));
m_Animation->setStartValue(QRect(m_posX - ui->m_menu->width(), m_posY,
this->width(), this->height()));
m_Animation->start();
m_flodTimer->stop();
}
事件过滤
需要将悬浮窗的子控件的鼠标移入移出事件进行一个设计,代码如下:
bool Floating::eventFilter(QObject *target, QEvent *event)
{
//拖动
// TODO
if (target == ui->m_title) {
if (event->type() == QEvent::Enter) {
if (!m_bDragFlag && !m_isExpand) {
m_expandTimer->start();
return QWidget::eventFilter(target, event);
}
}
if (event->type() == QEvent::Leave) {
m_expandTimer->stop();
}
}
if (target == ui->m_menu || target == this) {
if (event->type() == QEvent::Enter) {
m_flodTimer->stop();
return QWidget::eventFilter(target, event);
}
if (event->type() == QEvent::Leave) {
if (!m_bDragFlag && m_isExpand) {
m_flodTimer->start();
}
}
}
return QWidget::eventFilter(target, event);
}
图标变换
void Floating::setTitleIcon()
{
m_isExpand ? ui->m_title->setProperty("status", "show")
: ui->m_title->setProperty("status", "hide");
// 设置完之后,一定要polish,不然样式可能不会显示
ui->m_title->style()->polish(ui->m_title);
}
这里是根据设置的动态属性来设置对应的样式,详情请参见这篇博客
自适应窗口大小
由于有时候会出现窗口大小调整后,悬浮窗的位置可能会重新变化,或者分辨率改变之后。所以需要重载需要放置的窗口的resizeEvent
来动态的设置悬浮窗的大小。
代码如下:
// mainwindow.h
class MainWindow
{
// ...
protected:
virtual void resizeEvent(QResizeEvent* event) override;
}
// mainwindow.cpp
void resizeEvent(QResizeEvent* event)
{
m_floating->adjustParent(this.width());
}
// floating.cpp
void Floating::adjustParent(int parentWidth)
{
// 这里我的posY是设置的固定的50,
// m_posX = 主窗口的宽度 - (标题栏的宽度 +布局的间隙)
int horSpacing = static_cast<QGridLayout*>(this->layout())->horizontalSpacing();
m_posX = parentWidth - ui->m_title->width() - horSpacing;
m_posY = 50;
// 移动窗口
this->move(m_posX, m_posY);
}
使用方法
把文件复制到项目文件夹下
在pro文件里引入pri文件
include(floating/floating.pri)
加入头文件
include "floating/floating"
获取单例对象:
// .h
Floating* m_floating;
// 且需要在使用的类的析构函数里,加上这一句
// 不然就会出现多次释放的问题,因为这个m_floating释放在析构函数之前
// 但是之前创建的子窗口的关系树还在,在主窗口析构的时候还会去析构子对象
// 就会出错。所以需要在对象树中删除这段关系。
m_floating->setParent(nullptr);
// .cpp
m_floating = &Floating::getInstance(this);
- 设置样式表
/* 设置收起的样式 */
Floating QWidget#m_title[status=hide]
{
/* border-image:url(:/img/images/ZT.png);*/
background-color: rgba(12,55,214,1);
}
/* 设置展开的样式 */
Floating QWidget#m_title[status=show]
{
/* border-image:url(:/img/images/YT.png);*/
background-color: rgba(12,55,214,1);
}
/* 设置菜单栏的样式 */
Floating QWidget#m_menu
{
border: 2px solid #969696;
background-color: rgba(0, 25, 67, 0.9);;
border-top-left-radius: 10px; /* 角弧度:左下角*/
border-bottom-left-radius: 10px; /* 角弧度:右下角*/
}
- 如果你需要让主窗体变化之后,悬浮窗的位置也能跟着变化,请重载主窗口的resize事件,以手动更改悬浮窗的大小
// mainwindow.h
class MainWindow
{
// ...
protected:
virtual void resizeEvent(QResizeEvent* event) override;
}
// mainwindow.cpp
void resizeEvent(QResizeEvent* event)
{
m_floating->adjustParent(this.width());
}
果然只有通过写出来或者教别人的方式,才能够真正检验自己的知识学的到不到位,在写博客的同事,我又对这些代码和QPropertyAnimation这个类了解更深了。
代码资源请去此处下载
Qt QPropertyAnimation+QTimer实现自制悬浮窗的更多相关文章
- Qt实现悬浮窗效果
当鼠标移动到头像控件时,显示悬浮窗,当鼠标离开时,悬浮窗隐藏. 1.控件选择 悬浮窗可以从QDialog派生,并将窗口的属性设置为无边框 this->setWindowFlags(this- ...
- WindowManager 实现悬浮窗 详解
WindowManager 实现悬浮窗 详解 一:对于想直接看效果的,可以看看我的demo app. 链接:http://sj.qq.com/myapp/detail.htm?apkName=com. ...
- Android悬浮窗实现 使用WindowManager
Android悬浮窗实现 使用WindowManager WindowManager介绍 通过Context.getSystemService(Context.WINDOW_SERVICE)可以获得 ...
- Android仿360手机卫士悬浮窗效果
请看下图: 首先是一个小的悬浮窗显示的是当前使用了百分之多少的内存,点击一下小悬浮窗,就会弹出一个大的悬浮窗,可以一键加速.好,我们现在就来模拟实现一下 ...
- 简易的可拖动的桌面悬浮窗效果Demo
首先,我们需要知道,悬浮窗分为两种:Activity级别的悬浮窗,系统级别的悬浮窗 Activity级别的悬浮窗跟随所属Activity的生命周期而变化,而系统级别的悬浮窗则可以脱离Activity而 ...
- Android悬浮窗注意事项
一 动画无法运行 有时候,我们对添加的悬浮窗口,做动画的时候,始终无法运行. 那么,这个时候,我们可以对要做动画的View,再添加一个parent,即容器.将要做动画的View放入容器中. 二 悬浮窗 ...
- Android WindowManager悬浮窗:不需要申请权限实现悬浮
Android WindowManager悬浮窗:不需要申请权限实现悬浮 附录文章1介绍了Android平台上的悬浮窗WindowManager,WindowManager悬浮窗可以悬浮在And ...
- Android 高仿UC浏览器监控剪切板弹出悬浮窗功能
UC浏览器应该是android手机里 最流行的浏览器之一了,他们有一个功能 相信大家都体验过,就是如果你复制了什么文字,(在其他app中 复制也有这个效果!,所以能猜到肯定是监控了剪切板),就会弹出一 ...
- 关于Android悬浮窗要获取按键响应的问题
要在Android中实现顶层的窗口弹出,一般都会用WindowsManager来实现,但是几乎所有的网站资源都是说弹出的悬浮窗不用接受任何按键响应. 而问题就是,我们有时候需要他响应按键,比如电视上的 ...
随机推荐
- ASP.NET Core 6框架揭秘实例演示[22]:如何承载你的后台服务[补充]
借助 .NET提供的服务承载(Hosting)系统,我们可以将一个或者多个长时间运行的后台服务寄宿或者承载我们创建的应用中.任何需要在后台长时间运行的操作都可以定义成标准化的服务并利用该系统来承载,A ...
- CentOS 8 网卡命令使用
之前一直用CENTOS7中service来重启网卡,但是换成CENTOS8后centos8的网卡服务与centos7有所不同,无法通过systemctl或者service命令重启网卡.centos8网 ...
- 『现学现忘』Docker基础 — 23、使用Docker安装Tomcat
目录 步骤1:搜索镜像 步骤2:下载Tomcat镜像 步骤3:运行Tomcat镜像 步骤4:本机和外网测试 步骤5:解决问题 补充:--rm选项 步骤1:搜索镜像 使用docker search命令进 ...
- 编译OneAPI(支持Nvidia显卡)
开始使用DPC++ 官方安装教程 预备条件 请确保当前开发环境满足如下条件: git cmake版本需要满足3.14及以上. python版本3.6以上的python. nijia版本1.8及以上(使 ...
- MacOS新功能“通用控制”,多台设备操作互联太方便了!
昨天看到macOS推送了12.3的更新,记得之前预告过一个"通用控制"的功能,所以赶紧升级一波体验一下,效果惊艳到我了,赶紧安利一波! 先交代一下现在隔离在家的办公情况,我主要是用 ...
- springboot 踩坑之路之 Configuration Annotation Proessor not found in classpath
1.出现spring boot Configuration Annotation Proessor not found in classpath的提示是在用了@ConfigurationPropert ...
- TransactionScope是什么
TransactionScope使用说明 TransactionScope是.Net Framework 2.0滞后,新增了一个名称空间.它的用途是为数据库访问提供了一个"轻量级" ...
- [八省联考2018]制胡窜 (SAM+大讨论)
正着做着实不太好做,正难则反,考虑反着做. 把i,j看成在切割字符串,我们统计有多少对(i,j)会切割所有与\(s_{l,r}\)相同的串.对于在后缀自动机上表示\(s_{l,r}\)的节点x,x的p ...
- Java的重载以及与重写的区别
一.什么是方法重载 方法的重载就是在同一个类中,有着若干个名字相同的方法.在具体调用这些方法的时候,通过传递参数的不同来调用这些重载方法. 二.为什么需要方法重载 方法名的定义需要做到见名知意,功能类 ...
- 【freertos】004-任务创建与删除及其实现细节
前言 后面都是已动态内存任务为例来分析. 注意: 由于当前学习是在linux上跑的freertos,对于freertos底层相关接口,从demo工程来看,都是posix标准相关. 鉴于freertos ...