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

相关推荐
zwc321043205529 分钟前
Day 5:threshold与inRange阈值分割
c++·图像处理·人工智能·opencv·算法·计算机视觉
OxYGC34 分钟前
[工程] PHP2Shell 漏洞解析:两条 PHP 内核 RCE,为什么应用层的防护全部失效
android·开发语言·php
小徐不徐说38 分钟前
数据库存储基础:理解表结构、主键、事务与 BLOB
开发语言·数据库·c++·qt·程序设计
草莓熊Lotso1 小时前
【C++ 基于微服务的即时通讯系统】网关基建:cpp-httplib 与 WebSocketpp 从入门到实战
linux·开发语言·数据库·c++·redis·微服务·云原生
李纲明1 小时前
WordPress 媒体库上传失败或一直转圈:先核 PHP 上传限制与目录权限,再查安全插件与对象存储
运维·开发语言·安全·php·wordpress·媒体库·devdiy
第九序言T9 小时前
Python 数据处理进阶(十八)批量处理同结构文件:一个文件夹一次跑完
开发语言·python·自动化·pandas·数据处理
Sakura_j_h_10 小时前
EasyEDA PCB 快速设计Skills
c++·python·github
龙亘川11 小时前
构建城市公共安全数字底座:亘川智城智慧安防系统全维度解读
java·大数据·开发语言·人工智能·信息可视化
程序喵大人11 小时前
【C++入门】内存模型与资源管理 - 09 shared_ptr 和 weak_ptr 解决共享,也带来代价
开发语言·c++·资源管理·智能指针·内存模型
Raas10011 小时前
AI网关部署方案?从部署到上线全流程
java·开发语言·人工智能·gateway·ai网关·企业级ai网关·企业级ai产品