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

相关推荐
g***B73818 分钟前
Kotlin协程在Android中的使用
android·开发语言·kotlin
火白学安全22 分钟前
《Python红队攻防零基础脚本编写:进阶篇(一)》
开发语言·python·安全·web安全·网络安全·系统安全
6***x54524 分钟前
C++在计算机视觉中的图像处理
c++·图像处理·计算机视觉·游戏引擎·logback·milvus
爱码小白24 分钟前
PyQt5 QTimer总结
开发语言·qt
fpcc27 分钟前
跟我学C++中级篇——内存访问违例
c++
A***279530 分钟前
Kotlin反射机制
android·开发语言·kotlin
E***q53935 分钟前
C++内存对齐优化
开发语言·c++
D_evil__36 分钟前
[C++高频精进] 文件IO:文件操作
c++
q***d1731 小时前
Kotlin在后台服务中的框架
android·开发语言·kotlin
周杰伦fans1 小时前
C# 中的 `Hashtable`
开发语言·c#