如何使用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了

相关推荐
2501_930707781 小时前
使用C#代码在 PowerPoint 中组合或取消组合形状
开发语言·c#
晚烛1 小时前
CANN 调试工具与性能剖析:从日志分析到 NPU 行为追踪的完整调试体系
开发语言·windows·python·深度学习·缓存
惊鸿一博2 小时前
图标加载方式_zeroIcon_是否加前缀mdi
开发语言·前端·javascript
森G2 小时前
TypeScript 基础类型
开发语言·typescript
小poop2 小时前
string 类从入门到深入
c++
huipeng9262 小时前
企业级微服务开发实战(一):项目启动与工程化设计
java·开发语言·spring boot·spring cloud·微服务·云原生·架构
爱吃土豆的马铃薯ㅤㅤㅤㅤㅤㅤㅤㅤㅤ3 小时前
java实现excel导入、下载模板方法
java·开发语言·excel
眠りたいです3 小时前
现代C++:C++14中的新语言特性和库特性
c语言·开发语言·c++
叶小鸡4 小时前
Java 篇-项目实战-AI 天机学堂(从 0 到 1)-day1
java·开发语言
浅念-4 小时前
LeetCode 回溯算法题——综合练习
数据结构·c++·算法·leetcode·职场和发展·深度优先·dfs