實現效果
這次製作的桌面右下角彈窗,主要功能有透明度和位置動畫、定時關閉。
實現思路
首先,我們需要獲取桌面大小,然後 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();
});
}