Qt實現桌面右下角彈窗

實現效果

這次製作的桌面右下角彈窗,主要功能有透明度和位置動畫、定時關閉。

實現思路

首先,我們需要獲取桌面大小,然後 move 到右下角去,這裏藉助的 QScreen:

QScreen * screen = QGuiApplication::primaryScreen();
const QRect desk_rect = screen->availableGeometry();

對於動畫,我用的  QPropertyAnimation 屬性動畫配合動畫組。然後根據設置來決定啓用哪些動畫、是否定時關閉等。

還有要注意的是模態框的問題,如果有一個 ApplicationModal 的窗口顯示了,就沒法點擊其他窗口。而且關閉模態窗口時,可能會導致主窗口跑到其他窗口後面去了。這裏我時先設置爲 WindowModal ,關閉時再設置程 NoModal 非模態。

目前還沒有做鼠標放在上面不自動關閉的功能;目前的實現只有單個彈窗實例,可自行修改爲允許多個彈框同時存在。

實現代碼

完整代碼鏈接(RDesktopTip類):https://github.com/gongjianbo/RectangleComponent

首先在ui中拖一個樣式:

主要實現代碼:

#ifndef RDESKTOPTIP_H
#define RDESKTOPTIP_H

#include <QWidget>
#include <QPropertyAnimation>
#include <QParallelAnimationGroup>
#include <QTimer>

namespace Ui {
class RDesktopTip;
}

/**
 * @brief 桌面右下角彈框
 * @author 龔建波
 * @date 2020-6-21
 * @note 可以增加show的parent參數,使模態有父子關係
 * @details 只有單個實例對象顯示彈框,
 * 後面設置的信息會覆蓋之前的。
 * 也可以修改爲每次show單獨創建一個實例對象
 */
class RDesktopTip : public QWidget
{
    Q_OBJECT
    explicit RDesktopTip();
public:
    //動畫模式枚舉
    enum AnimationMode
    {
        //無動畫
        NoAnimation = 0x00,
        //僅透明度動畫
        OpacityAnimation = 0x01,
        //僅位置動畫
        PosAnimation = 0x02,
        //全部動畫
        //OpacityAnimation|PosAnimation
        AllAnimation = 0xFF
    };
public:
    ~RDesktopTip();

    //顯示彈框-已顯示動畫重新開始,timeout<=0不會定時消失
    static void showTip(const QStringList &texts,int timeout=0);
    //顯示彈框-已顯示不重複動畫
    static void keepTip(const QStringList &texts);
    //隱藏彈框
    static void hideTip();
    //設置動畫模式
    static RDesktopTip::AnimationMode getMode();
    static void setMode(RDesktopTip::AnimationMode newMode);

private:
    //初始化動畫設置
    void initAnimation();
    //初始化定時器設置
    void initTimer();
    //準備定時器
    void readyTimer(int timeout);
    //啓動顯示動畫-已顯示動畫重新開始
    void showAnimation();
    //啓動顯示動畫-已顯示不重複動畫
    void keepAnimation();
    //啓動隱藏動畫
    void hideAnimation();
    //顯示的文本
    void setTextList(const QStringList &texts);

private:
    Ui::RDesktopTip *ui;
    //唯一實例
    static RDesktopTip *instance;

    //動畫設置
    static AnimationMode mode;
    //動畫組
    QParallelAnimationGroup *showGroup;
    //保存動畫結束狀態
    bool showAnimEnd=false;
    //透明度屬性動畫
    QPropertyAnimation *showOpacity=nullptr;
    //位置屬性動畫
    QPropertyAnimation *showPos=nullptr;

    //定時關閉
    QTimer *hideTimer=nullptr;
    //定時計數
    int hideCount=0;
};

#endif // RDESKTOPTIP_H
#include "RDesktopTip.h"
#include "ui_RDesktopTip.h"

#include <QApplication>
#include <QScreen>
#include <QDebug>

RDesktopTip* RDesktopTip::instance=nullptr;
RDesktopTip::AnimationMode RDesktopTip::mode=RDesktopTip::AllAnimation;

RDesktopTip::RDesktopTip()
    : QWidget(nullptr),
      ui(new Ui::RDesktopTip),
      showGroup(new QParallelAnimationGroup(this))
{
    ui->setupUi(this);

    setWindowFlags(Qt::FramelessWindowHint | Qt::ToolTip);
    setAttribute(Qt::WA_TranslucentBackground);
    setAttribute(Qt::WA_DeleteOnClose);
    //setWindowModality(Qt::WindowModal);

    //resize會被label撐開
    setFixedSize(310,210);

    //關閉
    connect(ui->btnClose,&QPushButton::clicked,this,&RDesktopTip::hideTip);
    //程序退出時釋放
    connect(qApp,&QApplication::aboutToQuit,this,&RDesktopTip::close);
    //動畫設置
    initAnimation();
    //定時器設置
    initTimer();
}

RDesktopTip::~RDesktopTip()
{
    qDebug()<<"delete DesktopTip";
    delete ui;
}

void RDesktopTip::showTip(const QStringList &texts, int timeout)
{
    if(!instance){
        //僅在ui線程
        instance=new RDesktopTip;
    }
    instance->readyTimer(timeout);
    //模態框
    instance->setWindowModality(Qt::WindowModal);
    instance->setTextList(texts);
    instance->showAnimation();
}

void RDesktopTip::keepTip(const QStringList &texts)
{
    if(!instance){
        //僅在ui線程
        instance=new RDesktopTip;
    }
    instance->readyTimer(0);
    //模態框
    instance->setWindowModality(Qt::WindowModal);
    instance->setTextList(texts);
    instance->keepAnimation();
}

void RDesktopTip::hideTip()
{
    if(!instance){
        return;
    }
    instance->hideAnimation();
}

RDesktopTip::AnimationMode RDesktopTip::getMode()
{
    return mode;
}

void RDesktopTip::setMode(RDesktopTip::AnimationMode newMode)
{
    if(mode!=newMode){
        mode=newMode;
    }
}

void RDesktopTip::initAnimation()
{
    //透明度動畫
    showOpacity=new QPropertyAnimation(this,"windowOpacity");
    //判斷是否設置了此模式的動畫
    if(mode&AnimationMode::OpacityAnimation){
        showOpacity->setDuration(1500);
        showOpacity->setStartValue(0);
    }else{
        showOpacity->setDuration(0);
        showOpacity->setStartValue(1);
    }
    showOpacity->setEndValue(1);
    showGroup->addAnimation(showOpacity);

    //位置動畫
    showPos=new QPropertyAnimation(this,"pos");
    QScreen * screen = QGuiApplication::primaryScreen();
    if (screen) {
        const QRect desk_rect = screen->availableGeometry();
        const QPoint hide_pos{desk_rect.width()-this->width(),
                    desk_rect.height()};
        const QPoint show_pos{desk_rect.width()-this->width(),
                    desk_rect.height()-this->height()};
        //判斷是否設置了此模式的動畫
        if(mode&AnimationMode::PosAnimation){
            showPos->setDuration(1500);
            showPos->setStartValue(hide_pos);
        }else{
            showPos->setDuration(0);
            showPos->setStartValue(show_pos);
        }
        showPos->setEndValue(show_pos);
    }
    showGroup->addAnimation(showPos);
    //
    connect(showGroup,&QParallelAnimationGroup::finished,[this]{
        //back消失動畫結束關閉窗口
        if(showGroup->direction()==QAbstractAnimation::Backward){
            //Qt::WA_DeleteOnClose後手動設置爲null
            instance=nullptr;
            qApp->disconnect(this);
            //關閉時設置爲非模態,方式主窗口被遮擋,待測試
            this->setWindowModality(Qt::NonModal);
            this->close();
        }else{
            //配合keepAnimation
            showAnimEnd=true;
            //配合定時關閉
            if(hideCount>0)
                hideTimer->start();
        }
    });
}

void RDesktopTip::initTimer()
{
    hideTimer=new QTimer(this);
    hideTimer->setInterval(1000); //1s間隔
    connect(hideTimer,&QTimer::timeout,[this]{
        if(hideCount>1){
            hideCount--;
            ui->btnClose->setText(QString("%1 S").arg(hideCount));
        }else{
            ui->btnClose->setText(QString("Close"));
            hideTimer->stop();
            hideTip();
        }
    });
}

void RDesktopTip::readyTimer(int timeout)
{
    //先設置,在顯示動畫結束再start開始計時器
    hideCount=timeout;
    hideTimer->stop();

    if(hideCount>0){
        ui->btnClose->setText(QString("%1 S").arg(hideCount));
    }else{
        ui->btnClose->setText(QString("Close"));
    }
}

void RDesktopTip::showAnimation()
{
    showGroup->setDirection(QAbstractAnimation::Forward);
    //停止正在進行的動畫重新
    if(showGroup->state()==QAbstractAnimation::Running){
        showGroup->stop();
    }
    showGroup->start();
    show();
}

void RDesktopTip::keepAnimation()
{
    //show沒有完成,或者正在動畫中才進入
    if(!showAnimEnd||showGroup->state()!=QAbstractAnimation::Stopped){
        showGroup->setDirection(QAbstractAnimation::Forward);
        showGroup->start();
        show();
    }
}

void RDesktopTip::hideAnimation()
{
    //Backward反向執行動畫
    showGroup->setDirection(QAbstractAnimation::Backward);
    showGroup->start();
}

void RDesktopTip::setTextList(const QStringList &texts)
{
    QString tip_text("<p style='line-height:120%'>");
    for (const QString &text : texts)
    {
        if (text.isEmpty())
            continue;
        tip_text += text + "<br>";
    }
    tip_text += "</p>";
    ui->contentLabel->setText(tip_text);
}

使用方式:

void ToolTipDemo::initDesktopTip()
{
    //待修改樣式
    ui->desktopTipBox->setView(new QListView(this));
    //選擇啓用的動畫
    ui->desktopTipBox->addItems({"All","No","Opacity","Pos"});
    connect(ui->desktopTipBox,QOverload<int>::of(&QComboBox::currentIndexChanged),
            [](int index){
        switch (index) {
        case 0: RDesktopTip::setMode(RDesktopTip::AllAnimation); break;
        case 1: RDesktopTip::setMode(RDesktopTip::NoAnimation); break;
        case 2: RDesktopTip::setMode(RDesktopTip::OpacityAnimation); break;
        case 3: RDesktopTip::setMode(RDesktopTip::PosAnimation); break;
        default: break;
        }
    });
    connect(ui->btnDesktopTipShow,&QPushButton::clicked,[=]{
        RDesktopTip::showTip({
                                 "這是 1 條信息",
                                 "這是 1 條信息",
                                 "這是 1 條信息",
                                 "這是 1 條信息"
                             },5); //只顯示5s就消失
    });
    connect(ui->btnDesktopTipKeep,&QPushButton::clicked,[=]{
        RDesktopTip::keepTip({
                                 "這是 2 條信息",
                                 "這是 2 條信息",
                                 "這是 2 條信息",
                                 "這是 2 條信息"
                             }); //一直顯示,直到點關閉
    });
    connect(ui->btnDesktopTipHide,&QPushButton::clicked,[=]{
        RDesktopTip::hideTip();
    });
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章