如何使用Qt创建一个浮在MainWindow上的滑动小Panel

如何使用Qt创建一个浮在MainWindow上的滑动小Panel

前言

​ 之前笔者去其他城市做了点事情,现在回到家中可以好好的扩展一下自己的Qt的功能库。笔者最近正在给我的小日历写一个超级简单的浮动的小窗口,源码中就是一个简单的小PanelWidget,比起来更加像是一种静态的可继承的扩展库(Floatable Panel Property),注意到的朋友可以自行继承PanelWidget,无论是带有Ui文件者还是其他均可复用。

分析

​ PanelWidget作为一个更加像是模板的Widget,实际上中心思路非常简单:那就是将PanelWidget自身设置成为SubWindow,这个SubWindow总是对齐于ParentWindow(主窗口)进行坐标系的参考(不然的话他就会总是出现在Screen坐标的最左边或者云云)

setWindowFlags实际上设置的是QtWidgets的一些特性,当然,我看到最经常大伙用的就是经典的FrameLessWindow的flags,这里我们使用SubWindow来重新调整我们的Qt的计算的坐标系。

​ 我们的期望是这样的------我们的ParentWindow动的时候,我们的PanelWidget显然也要跟随移动,但是因为他不在Layout中,我们就要自己监听我们的ParentWindow来做出对应的举措,为此,installEventFilter是我们需要做的(嗯,监听我们的夫窗口)

cpp 复制代码
bool PanelWidget::eventFilter(QObject* watched, QEvent* event) {
    if (watched == parentWidget() && (event->type() == QEvent::Move || event->type() == QEvent::Resize)) {
        updatePosition();	///< 在这里,我们重新计算我们的位置
    }
    return QWidget::eventFilter(watched, event);
}

​ 重新计算是非常简单的,我们的Widget在静态的时候无非就是两个

cpp 复制代码
void PanelWidget::updatePosition() {
    if (!parentWidget())
        return;

    QRect parentRect = parentWidget()->geometry();
    QRect targetRect(0, 0, width(), parentRect.height());

    if (isVisible() && !isSlidingIn) {
        setGeometry(targetRect);	///< 看得见的时候,放置到可以看到的左侧栏上
    } else {
        setGeometry(targetRect.translated(-width(), 0)); ///< 反之,放置到右边刚好跟主窗口对齐的位置上
    }
}

​ 我们还需要滑动的特效,看过我博客的兄弟们都知道:QPropertyAnimation总是一个非常好的选择,这里我们就是对Geometry做这个东西玩,当然,这里就直接抽象两个重要的接口函数

cpp 复制代码
void PanelWidget::slideIn() {
    isSlidingIn = true;

    updatePosition();
    show();
    raise();

    animation->stop();
    animation->setStartValue(geometry());
    animation->setEndValue(QRect(0, 0, width(), height()));

    disconnect(animation, &QPropertyAnimation::finished, this, &PanelWidget::closePanel);
    animation->start();

    connect(animation, &QPropertyAnimation::finished, this, [this]() {
        isSlidingIn = false;
    });
}

void PanelWidget::slideOut() {
    animation->stop();
    animation->setStartValue(geometry());
    animation->setEndValue(QRect(-width(), 0, width(), height()));
    connect(animation, &QPropertyAnimation::finished, this, &PanelWidget::closePanel);
    animation->start();
}

void PanelWidget::closePanel() {
    disconnect(animation, &QPropertyAnimation::finished, this, &PanelWidget::closePanel);
    hide();
}

源代码

cpp 复制代码
#include "PanelWidget.h"
#include <QEvent>
#include <QPropertyAnimation>
PanelWidget::PanelWidget(QWidget* parent)
    : QWidget { parent } {
    animation = new QPropertyAnimation(this, "geometry");
    animation->setDuration(300);

    setWindowFlags(Qt::SubWindow | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint);
    setAttribute(Qt::WA_DeleteOnClose, false);
    parent->installEventFilter(this);
}

void PanelWidget::updatePosition() {
    if (!parentWidget())
        return;

    QRect parentRect = parentWidget()->geometry();
    QRect targetRect(0, 0, width(), parentRect.height());

    if (isVisible() && !isSlidingIn) {
        setGeometry(targetRect);
    } else {
        setGeometry(targetRect.translated(-width(), 0));
    }
}

bool PanelWidget::eventFilter(QObject* watched, QEvent* event) {
    if (watched == parentWidget() && (event->type() == QEvent::Move || event->type() == QEvent::Resize)) {
        updatePosition();
    }
    return QWidget::eventFilter(watched, event);
}

void PanelWidget::slideIn() {
    isSlidingIn = true;

    updatePosition();
    show();
    raise();

    animation->stop();
    animation->setStartValue(geometry());
    animation->setEndValue(QRect(0, 0, width(), height()));

    disconnect(animation, &QPropertyAnimation::finished, this, &PanelWidget::closePanel);
    animation->start();

    connect(animation, &QPropertyAnimation::finished, this, [this]() {
        isSlidingIn = false;
    });
}

void PanelWidget::slideOut() {
    animation->stop();
    animation->setStartValue(geometry());
    animation->setEndValue(QRect(-width(), 0, width(), height()));
    connect(animation, &QPropertyAnimation::finished, this, &PanelWidget::closePanel);
    animation->start();
}

void PanelWidget::closePanel() {
    disconnect(animation, &QPropertyAnimation::finished, this, &PanelWidget::closePanel);
    hide();
}
cpp 复制代码
#ifndef PANELWIDGET_H
#define PANELWIDGET_H

#include <QWidget>
class QPropertyAnimation;
class PanelWidget : public QWidget {
	Q_OBJECT
public:
    /**
     * @brief PanelWidget
     * @param parent
     */
    explicit PanelWidget(QWidget* parent = nullptr);

    void slideIn(); ///< slide the widget in
    void slideOut(); ///< slide the widget out

private:
    QPropertyAnimation* animation; ///< Animation Holder
    bool isSlidingIn { false }; ///< marking for the sliding
    void closePanel(); ///< panel close IMPL
    void updatePosition(); ///< position calculation
    /**
     * @brief eventFilter
     * @param watched
     * @param event
     * @return
     */
    bool eventFilter(QObject* watched, QEvent* event) override;
};

#endif // PANELWIDGET_H

CCQtArchives/portable_modules/floaty_panel_widget at main · Charliechen114514/CCQtArchives

笔者的一个Example:

cpp 复制代码
class DateNoter : public PanelWidget {
	Q_OBJECT

public:
    explicit DateNoter(QWidget* parent = nullptr);
	~DateNoter();

private:
    Ui::DateNoter* ui;
};

​ 现在你就可以自由的在这个牌生类中开发业务,自动具备SlideIn, SlideOut了

相关推荐
小镇敲码人3 分钟前
【深入浅出】之Qt 事件系统实战:鼠标、键盘、滚轮、窗口与定时器事件
数据库·qt·网络协议·mysql·http
布莱克60524 分钟前
队列详解:定义、分类、应用场景及与栈的区别(C/C++ 代码讲解)
c语言·开发语言·c++·队列
CPETW24 分钟前
USB TO I2C_(Excel)_Scan ---- 1000KHz总线速率测试_A
c语言·开发语言·网络·科技·单片机
2601_9622035129 分钟前
【SpringBoot3】面向切面 AspectJ AOP 使用详解
开发语言·前端·python
程序员大雄学编程31 分钟前
微积分43. 无穷积分入门:从概念到Python实战可视化
开发语言·python·学习·微积分
何以解忧,唯有..32 分钟前
LangChain 中 Pydantic 格式输出:结构化输出的完整指南
java·开发语言·langchain
郝学胜-神的一滴40 分钟前
C++11 工程级应用 06:自己造一个类似Python的Range迭代器
开发语言·c++·windows·python·程序人生·microsoft
zh_xuan1 小时前
c++ jthread
开发语言·c++·jthread
旧梦95271 小时前
Java 捕获多个异常:从基础语法到最佳实践
java·开发语言
海兰1 小时前
【 Python 量化交易】开篇
开发语言·python·信息可视化·量化交易