zoukankan      html  css  js  c++  java
  • 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>
    

      

      

  • 相关阅读:
    git push 失败出现error: src refspec master does not match any.解决方案
    git:not a git repository (or any of the parent directories)
    初次安装git配置用户名和邮箱
    MYSQL的C API之mysql_query
    PyCharm + PyQt4 环境搭建
    pycharm下打开、执行并调试scrapy爬虫程序
    jQuery火箭图标返回顶部代码
    jQuery火箭图标返回顶部代码
    jQuery火箭图标返回顶部代码
    jQuery火箭图标返回顶部代码
  • 原文地址:https://www.cnblogs.com/suxia/p/13968338.html
Copyright © 2011-2022 走看看