找回密码
 立即注册

QQ登录

只需一步,快速开始

工控课堂 首页 工控文库 上位机编程 查看内容

Qt QPropertyAnimation+QTimer实现自制悬浮窗

2022-4-6 21:33| 发布者: gkket| 查看: 1785| 评论: 0

摘要: 目录Qt下的悬浮窗QPropertyAnimationQTimer事件过滤图标变换自适应窗口大小使用方法Qt下的悬浮窗最近项目需要一个类似于360悬浮球类似的悬浮窗,当鼠标放入停留一段时间,就会展开悬浮窗,移出区域就会自动收起。随 ...

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

另外一个核心的是定时器,简易的逻辑是:

  1. 当鼠标移入标题栏时,会开启弹出定时器,到时间就会执行弹出函数,当没有到时间,但是移除了标题时,关闭弹出计时器;

  2. 当鼠标移入菜单栏时,会关闭收起定时器,当移出菜单栏时,会开启收起定时器,到时间就会执行收起函数,当没有到时间就移入的时,关闭收起定时器;

定时器的初始化代码如下:

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);
}

使用方法

  1. 把文件复制到项目文件夹下

  2. 在pro文件里引入pri文件

    include(floating/floating.pri)

  3. 加入头文件

    include "floating/floating"

  4. 获取单例对象:

// .h 
Floating* m_floating;

// 且需要在使用的类的析构函数里,加上这一句
// 不然就会出现多次释放的问题,因为这个m_floating释放在析构函数之前
// 但是之前创建的子窗口的关系树还在,在主窗口析构的时候还会去析构子对象
<
关注公众号,加入500人微信群,下载100G免费资料!

最新评论

热门文章
关闭

站长推荐上一条 /1 下一条

QQ|手机版|免责声明|本站介绍|工控课堂 ( 沪ICP备20008691号-1 )

GMT+8, 2025-12-22 17:12 , Processed in 0.367857 second(s), 22 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

返回顶部