QT實現右下角彈框提示


程序動畫部分來自龔建波大佬的博客:https://blog.csdn.net/gongjianbo1992/article/details/106885483

首先上實機效果圖,相較於龔建波大佬的代碼,打破了原有的界面布局,新的界面布局用於縮放比適配和陰影效果,相當於做了些界面美化的工作

實現思路:

動畫部分使用QPropertyAnimation 屬性動畫配合動畫組。然后根據設置來決定啟用哪些動畫、是否定時關閉

由於只有一個實例存在,所以可以看到如演示所示,第二次調用彈框顯示的時候,會進行判斷,判斷實例上的動畫組是否完結,如果沒完結的話,會立即完結動畫開始下一組動畫

陰影效果部分使用QGraphicsDropShadowEffect,使用此效果需要界面UI留有顯示陰影的空間

代碼部分:

獲取系統縮放比的代碼

//************************************
// Method:    getDpi
// Description:獲取系統dpi(縮放比例)
// Returns: 縮放比例 
//************************************
double getDpi()
{
	double dDpi = 1;
	// Get desktop dc
	HDC desktopDc = GetDC(NULL);
	// Get native resolution
	float horizontalDPI = GetDeviceCaps(desktopDc, LOGPIXELSX);
	float verticalDPI = GetDeviceCaps(desktopDc, LOGPIXELSY);

	int dpi = (horizontalDPI + verticalDPI) / 2;
	dDpi = 1 + ((dpi - 96) / 24)*0.25;
	//為了保證頁面顯示正常,暫時不支持小於1和大於2的縮放系數
	if (dDpi < 1)
	{
		dDpi = 1;
	}
	return dDpi;
}

  

TrayMessageDlg.h

#pragma once
#include <QWidget>
#include <QPropertyAnimation>
#include <QParallelAnimationGroup>
#include <QTimer>
#include "ui_TrayMessageDlg.h"


class TrayMessageDlg : public QWidget
{
	Q_OBJECT
public:
	//動畫模式枚舉
	enum AnimationMode
	{
		//無動畫
		NoAnimation = 0x00,
		//僅透明度動畫
		OpacityAnimation = 0x01,
		//僅位置動畫
		PosAnimation = 0x02,
		//全部動畫
		//OpacityAnimation|PosAnimation
		AllAnimation = 0xFF
	};
public:
	explicit TrayMessageDlg();
	~TrayMessageDlg();
	//顯示彈框-已顯示動畫重新開始,timeout<=0不會定時消失
	static void showTip(const QString &title, const QString &texts, int timeout);
	//顯示彈框-已顯示不重復動畫
	static void keepTip(const QString &texts);
	//隱藏彈框
	static void hideTip();
	//設置動畫模式
	static TrayMessageDlg::AnimationMode getMode();
	static void setMode(TrayMessageDlg::AnimationMode newMode);

protected:
	void paintEvent(QPaintEvent *event);
private:
	//初始化動畫設置
	void initAnimation();
	//初始化定時器設置
	void initTimer();
	//准備定時器
	void readyTimer(int timeout);
	//啟動顯示動畫-已顯示動畫重新開始
	void showAnimation();
	//啟動顯示動畫-已顯示不重復動畫
	void keepAnimation();
	//啟動隱藏動畫
	void hideAnimation();

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

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

	//定時關閉
	QTimer *hideTimer = nullptr;
	//定時計數
	int hideCount = 0;
	//縮放比例,適配高分屏
	double m_dpi;
	QGraphicsDropShadowEffect *m_pEffect;
};

TrayMessageDlg.cpp

無法編譯的部分為日志輸出

#include <stdafx.h>
#include "TrayMessageDlg.h"
#include "ui_TrayMessageDlg.h"
#include <QApplication>
#include <QScreen>
#include <QDebug>
#include "../Common/Utils.h"
#include "Log.h"

TrayMessageDlg* TrayMessageDlg::instance = nullptr;
TrayMessageDlg::AnimationMode TrayMessageDlg::mode = TrayMessageDlg::AllAnimation;
#define SHADOW_WIDTH 10 //邊框陰影寬度

TrayMessageDlg::TrayMessageDlg()
	: QWidget(nullptr),ui(new Ui::TrayMessageDlg),showGroup(new QParallelAnimationGroup(this))
{
	try
	{
		ui->setupUi(this);
		setWindowFlags(Qt::FramelessWindowHint | Qt::ToolTip);
		setAttribute(Qt::WA_TranslucentBackground,true);//背景透明
		setAttribute(Qt::WA_DeleteOnClose);
		//setWindowModality(Qt::WindowModal);

		ui->btnClose->setIcon(QIcon(":/YozoUCloud/Resources/Main/window_close_n.png"));

		//縮放比適配
		m_dpi = getDpi();
		setFixedSize(300 * m_dpi, 160 * m_dpi);
		QFont font;
		font.setPixelSize(14*m_dpi); //字體基礎是14
		font.setFamily(QString::fromLocal8Bit("微軟雅黑"));
		ui->titleLabel->setFont(font);
		ui->contentLabel->setFont(font);
		ui->countLabel->setFont(font);

		//添加陰影效果
		m_pEffect = new QGraphicsDropShadowEffect(this);//該類提供了圖形元素的陰影效果,用於增加立體感。
		m_pEffect->setOffset(0, 0);//用於設定在哪個方向產生陰影效果,如果dx為負數,則陰影在圖形元素的左邊
		m_pEffect->setColor(Qt::gray);//用於設定陰影的顏色
		m_pEffect->setBlurRadius(20);//用於設定陰影的模糊度
		ui->frame->setGraphicsEffect(m_pEffect);
		ui->verticalLayout->setContentsMargins(10,10,10,0);//設置frame和主窗口的距離,也就是陰影的距離

		//關閉按鈕事件綁定
		connect(ui->btnClose, &QPushButton::clicked, this, &TrayMessageDlg::hideTip);
		//程序退出時釋放
		connect(qApp, &QApplication::aboutToQuit, this, &TrayMessageDlg::close);
		//動畫初始化設置
		initAnimation();
		//定時器設置
		initTimer();
	}
	catch (...)
	{
		Log::WriteOutput(LogType::Error, L"TrayMessageDlg::TrayMessageDlg() Failed!");
	}

}

TrayMessageDlg::~TrayMessageDlg()
{
	try
	{
		if (ui)
		{
			delete ui;
			ui = NULL;
		}
		if (showGroup)
		{
			delete showGroup;
			showGroup = NULL;
		}
		if (hideTimer)
		{
			delete hideTimer;
			hideTimer = NULL;
		}
		if (m_pEffect)
		{
			delete m_pEffect;
			m_pEffect = NULL;
		}
	}
	catch (...)
	{
		Log::WriteOutput(LogType::Error, L"TrayMessageDlg::~TrayMessageDlg() Failed!");
	}
}


void TrayMessageDlg::showTip(const QString &title, const QString &texts, int timeout)
{
	try
	{
		if (!instance){
			//僅在ui線程
			instance = new TrayMessageDlg();
		}
		instance->readyTimer(timeout);
		//模態框
		instance->setWindowModality(Qt::WindowModal);
		instance->ui->contentLabel->setText(texts);
		instance->ui->titleLabel->setText(title);
		instance->showAnimation();
	}
	catch (...)
	{
		Log::WriteOutput(LogType::Error, L"TrayMessageDlg::showTip() Failed!");
		return;
	}
	
}

void TrayMessageDlg::keepTip(const QString &texts)
{
	try
	{
		if (!instance){
			//僅在ui線程
			instance = new TrayMessageDlg;
		}
		instance->readyTimer(0);
		//模態框
		instance->setWindowModality(Qt::WindowModal);
		instance->ui->contentLabel->setText(texts);
		instance->keepAnimation();
	}
	catch (...)
	{
		Log::WriteOutput(LogType::Error, L"TrayMessageDlg::keepTip() Failed!");
		return;
	}
	
}


//關閉按鈕點擊事件
void TrayMessageDlg::hideTip()
{
	try
	{
		if (!instance){
			return;
		}
		instance->ui->countLabel->hide();
		instance->hideAnimation();
	}
	catch (...)
	{
		Log::WriteOutput(LogType::Error, L"TrayMessageDlg::hideTip() Failed!");
		return;
	}
}

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

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

void TrayMessageDlg::initAnimation()
{
	try
	{
		//透明度動畫
		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();
			}
		});
	}
	catch (...)
	{
		Log::WriteOutput(LogType::Error, L"TrayMessageDlg::initAnimation() Failed!");
		return;
	}
	
}

void TrayMessageDlg::initTimer()
{
	try
	{
		hideTimer = new QTimer(this);
		hideTimer->setInterval(1000); //1s間隔
		connect(hideTimer, &QTimer::timeout, [this]{
			if (hideCount>1){
				hideCount--;
				ui->countLabel->setText(QString::fromLocal8Bit("%1s后自動關閉").arg(hideCount));
			}
			else{
				ui->countLabel->hide();
				hideTimer->stop();
				hideTip();
			}
		});
	}
	catch (...)
	{
		Log::WriteOutput(LogType::Error, L"TrayMessageDlg::initTimer() Failed!");
		return;
	}
	
}

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

		if (hideCount>0){
			ui->countLabel->setText(QString::fromLocal8Bit("%1s后自動關閉").arg(hideCount));
		}
		else{
			ui->countLabel->hide();
		}
	}
	catch (...)
	{
		Log::WriteOutput(LogType::Error, L"TrayMessageDlg::readyTimer() Failed!");
		return;
	}
}

void TrayMessageDlg::showAnimation()
{
	try
	{
		showGroup->setDirection(QAbstractAnimation::Forward);
		if (showGroup->state() == QAbstractAnimation::Running)
		{
			showGroup->stop();	//停止正在進行的動畫重新
		}
		showGroup->start();
		show();
	}
	catch (...)
	{
		Log::WriteOutput(LogType::Error, L"TrayMessageDlg::showAnimation() Failed!");
		return;
	}
	
}

void TrayMessageDlg::keepAnimation()
{
	try
	{
		//show沒有完成,或者正在動畫中才進入
		if (!showAnimEnd || showGroup->state() != QAbstractAnimation::Stopped){
			showGroup->setDirection(QAbstractAnimation::Forward);
			showGroup->start();
			show();
		}
	}
	catch (...)
	{
		Log::WriteOutput(LogType::Error, L"TrayMessageDlg::keepAnimation() Failed!");
		return;
	}

}

void TrayMessageDlg::hideAnimation()
{
	try
	{
		//Backward反向執行動畫
		showGroup->setDirection(QAbstractAnimation::Backward);
		showGroup->start();
	}
	catch (...)
	{
		Log::WriteOutput(LogType::Error, L"TrayMessageDlg::hideAnimation() Failed!");
		return;
	}
}



void TrayMessageDlg::paintEvent(QPaintEvent *event)
{
	try
	{
		//目前沒有動作,可以刪除
	}
	catch (...)
	{
		Log::WriteOutput(LogType::Error, L"TrayMessageDlg::paintEvent() Failed!");
		return;
	}
}

TrayMessageDlg.ui

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>TrayMessageDlg</class>
 <widget class="QWidget" name="TrayMessageDlg">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>300</width>
    <height>160</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>Form</string>
  </property>
  <property name="styleSheet">
   <string notr="true">		#frame{
		background-color: white;
		border:none;
		border-radius:6px;
		}
        #titleArea{
        background-color: white;
		border-bottom:1px solid rgb(227, 227, 227);
        }
		#titleLabel{
		background-color:white;
		border:none;
		}
       	#contentLabel{
		background-color:white;
		padding: 6px 20px;
		}
		#countLabel{
		background-color:white;	
		color:rgb(59, 119, 229);
		}
        #btnClose{
        background-color:white;
		border:none;
		border-radius:5px
        }
        #btnClose:hover{
        background-color:rgb(255,64,64);
        color: rgb(0, 85, 127);
        }
      </string>
  </property>
  <layout class="QVBoxLayout" name="verticalLayout">
   <property name="spacing">
    <number>0</number>
   </property>
   <property name="leftMargin">
    <number>0</number>
   </property>
   <property name="topMargin">
    <number>0</number>
   </property>
   <property name="rightMargin">
    <number>0</number>
   </property>
   <property name="bottomMargin">
    <number>0</number>
   </property>
   <item>
    <widget class="QFrame" name="frame">
     <property name="frameShape">
      <enum>QFrame::StyledPanel</enum>
     </property>
     <property name="frameShadow">
      <enum>QFrame::Raised</enum>
     </property>
     <layout class="QVBoxLayout" name="verticalLayout_2" stretch="2,5">
      <property name="spacing">
       <number>0</number>
      </property>
      <property name="leftMargin">
       <number>6</number>
      </property>
      <property name="topMargin">
       <number>6</number>
      </property>
      <property name="rightMargin">
       <number>6</number>
      </property>
      <property name="bottomMargin">
       <number>6</number>
      </property>
      <item>
       <widget class="QWidget" name="titleArea" native="true">
        <property name="minimumSize">
         <size>
          <width>0</width>
          <height>50</height>
         </size>
        </property>
        <layout class="QHBoxLayout" name="horizontalLayout" stretch="2,2,1">
         <property name="spacing">
          <number>0</number>
         </property>
         <property name="leftMargin">
          <number>20</number>
         </property>
         <property name="topMargin">
          <number>0</number>
         </property>
         <property name="rightMargin">
          <number>20</number>
         </property>
         <property name="bottomMargin">
          <number>1</number>
         </property>
         <item>
          <widget class="QLabel" name="titleLabel">
           <property name="text">
            <string>提示</string>
           </property>
          </widget>
         </item>
         <item>
          <widget class="QLabel" name="countLabel">
           <property name="text">
            <string>3s后自動關閉</string>
           </property>
           <property name="alignment">
            <set>Qt::AlignCenter</set>
           </property>
          </widget>
         </item>
         <item>
          <widget class="QPushButton" name="btnClose">
           <property name="sizePolicy">
            <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
             <horstretch>0</horstretch>
             <verstretch>0</verstretch>
            </sizepolicy>
           </property>
           <property name="minimumSize">
            <size>
             <width>16</width>
             <height>16</height>
            </size>
           </property>
           <property name="maximumSize">
            <size>
             <width>25</width>
             <height>25</height>
            </size>
           </property>
           <property name="text">
            <string/>
           </property>
          </widget>
         </item>
        </layout>
       </widget>
      </item>
      <item>
       <widget class="QLabel" name="contentLabel">
        <property name="baseSize">
         <size>
          <width>0</width>
          <height>0</height>
         </size>
        </property>
        <property name="text">
         <string/>
        </property>
        <property name="alignment">
         <set>Qt::AlignCenter</set>
        </property>
        <property name="wordWrap">
         <bool>true</bool>
        </property>
       </widget>
      </item>
     </layout>
    </widget>
   </item>
  </layout>
 </widget>
 <resources/>
 <connections/>
</ui>

  

  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM