QT实现轮播显示图片(支持两种图片切换模式,间隔时间设置,按钮尺寸设置,缩放比自适应)

废话不多说,直接上代码和效果图

两种切换效果(滑动切换,淡入淡出切换)

#include <QWidget>
#include <QButtonGroup>
#include <vector>
#include <QPropertyAnimation>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QLabel>
#include <QPushButton>
#include <windows.h>
#include <QParallelAnimationGroup>
#include <QTimer>
#include <QGraphicsOpacityEffect>
#include <QMouseEvent>
#include <QDesktopServices>

//图片代表的ID,用完一次自加1
static int g_imageid = 0;

enum SCROLL_MODE
{
    FADE,//消失,渐变特效
    SLIDE//从右向左滑动
};

struct ScrollImage
{
    int iId; //ID代表着图片的标志,从窗口开始到结束始终不变
    QString qsImagePath;//图片对应的地址,一般为qrc资源文件的url
    QString qsUrl;//点击图片时的跳转链接,可以为NULL;
};

class ScrollImageWidget : public QWidget
{
    Q_OBJECT

public:
    ScrollImageWidget(QWidget *parent=NULL);
    ~ScrollImageWidget();

public:
    //添加图片,图片可以指定点击时跳转的链接。函数返回图片加入后其对应的ID,此ID从当前窗口开始到结束始终不变
    int addImage(QString imagePath, QString url = NULL);
    //开始滚动,可指定滚动效果为消失还是滑动,从第一张图片开始滚动
    void startScroll(int scrollmode=SCROLL_MODE::FADE);
    //切换图片到指定索引位置
    void switchToImg(int index);
    //设置按钮尺寸
    void setButtonSize(int diameter);
    //设置图片切换的动画时间
    void setSwitchTime(int switchtime);
    //设置每张图片等待的时间
    void setWaitingTime(int waitingtime);

private:
    //初始化界面布局
    void initUi();
    //启动一系列定时器
    void startTimer();
    //获取系统缩放比
    double getDPI();
protected:
    //鼠标按压响应
    virtual void mousePressEvent(QMouseEvent *event);
    //鼠标移动响应
    virtual void mouseMoveEvent(QMouseEvent * event);
    //鼠标释放响应
    virtual void mouseReleaseEvent(QMouseEvent *event);
    //界面尺寸变化响应
    void resizeEvent(QResizeEvent *event);
public Q_SLOTS:
    void OnTimerSwitch();

private:
    //顶层图片所在的QLabel
    QLabel* m_topLabel;
    //底层层图片所在的QLabel
    QLabel* m_bottomLabel;
    //页面主布局
    QVBoxLayout* m_vMainLayout;
    //按钮对应的布局
    QHBoxLayout* m_hButtonLayout;
    //系统缩放比
    double m_dpi;
    //当前滚动动画类型
    int m_currentMode;
    //当前显示的图片的索引
    int m_currentindex;
    //按钮对应的集合
    QButtonGroup* m_pButtonGroup;
    //图片资源对应的数组,后续可扩展方法众多
    std::vector<ScrollImage *> m_imageArray;
    //按钮对应的数组
    std::vector<QPushButton *> m_buttonArray;
    //切换图片
    QPixmap m_currentPixmap;
    QPixmap m_nextPixmap;
    //每张图片等待的时间对应的定时器
    QTimer* m_timerwait;
    //图片切换的时候对应的定时器
    QTimer* m_timerswitch;
    //淡入淡出特效切换图片所用的特效
    QGraphicsOpacityEffect* m_opacityeffect;
    //鼠标按压标记位
    bool m_bMousePress;
    //图片与图片之间间隔时间,不包括图片切换时间
    int m_waitingtime;
    //图片之间切换的动画时间
    int m_switchtime;
    //按钮的尺寸
    int m_buttonsize;
    //顶层图片当前的透明度,淡入淡出特效所用
    float m_imageopacity = 1;
    //顶层图片平移的距离,滑动特效所用
    int m_imagepos = 0;    
};
#include "ScrollImageWidget.h"

#define BUTTON_SPACING 10

ScrollImageWidget::ScrollImageWidget(QWidget *parent)
    : QWidget(parent)
{
    //初始化内部元素
    m_bMousePress = false;
    m_dpi = getDPI();
    m_waitingtime = 5000;
    m_switchtime = 1000;
    m_buttonsize = 30;
    //初始化页面布局
    initUi();
    //初始化透明度特效
    m_opacityeffect = new QGraphicsOpacityEffect(this);
    m_opacityeffect->setOpacity(m_imageopacity);
    //初始化定时器,并绑定对应槽函数
    m_timerwait = new QTimer(this);
    m_timerswitch = new QTimer(this);
    m_timerwait->setSingleShot(true);
    m_timerswitch->setSingleShot(false);
    connect(m_timerswitch, SIGNAL(timeout()), this, SLOT(OnTimerSwitch()));
    connect(m_timerwait, &QTimer::timeout, this, [=](){m_timerswitch->start(100); });
}

ScrollImageWidget::~ScrollImageWidget()
{
    //QObject及其派生类的对象,其parent析构时会析构该对象,所以不做处理
    //析构存储图片信息的容器里元素
    for (std::vector<ScrollImage*>::iterator s = m_imageArray.begin();
        s != m_imageArray.end(); 
        ++s){
        delete *s;
    }
}

int ScrollImageWidget::addImage(QString imagePath, QString url /*= NULL*/)
{
    //将图片元素添加到图片数组中
    ScrollImage* img = new ScrollImage;
    img->iId = g_imageid;
    g_imageid++;
    img->qsImagePath = imagePath;
    img->qsUrl = url;
    m_imageArray.push_back(img);
    //初始化按钮并将其添加到按钮组和布局中
    QPushButton* btn = new QPushButton(this);
    btn->setFixedSize(QSize(m_buttonsize, m_buttonsize));
    btn->setCheckable(true);
    btn->setStyleSheet("QPushButton{border-image:url(:/ScrollImage/noselect.png);}
                        QPushButton:checked{border-image:url(:/ScrollImage/selected.png);}");
    m_buttonArray.push_back(btn);
    m_pButtonGroup->addButton(btn, m_buttonArray.size() - 1);
    m_hButtonLayout->addWidget(btn);
    //设置主布局,每添加一个按钮,键盘布局的大小需要扩大
    m_vMainLayout->setContentsMargins((this->width() - m_imageArray.size()*(20 + m_buttonsize)*m_dpi) / 2,
        this->height() - (BUTTON_SPACING * 2 + m_buttonsize)*m_dpi,
        (this->width() - m_imageArray.size()*(BUTTON_SPACING * 2 + m_buttonsize)*m_dpi) / 2,
        BUTTON_SPACING*m_dpi);

    return img->iId;
}

void ScrollImageWidget::startScroll(int scrollmode/*=SCROLL_MODE::FADE*/)
{
    m_currentMode = scrollmode;
    //滚动之前判定目前需要展示的图片数目,0张和1张都不需要启动定时器
    if (m_imageArray.size() == 0)
    {
        QPixmap img(QString(":/ScrollImage/default.png"));
        m_topLabel->setPixmap(img);
    }
    else if (m_imageArray.size() == 1)
    {
        QPixmap img(m_imageArray[0]->qsImagePath);
        m_topLabel->setPixmap(img);
        m_pButtonGroup->button(0)->setChecked(true);
    }
    else
    {
        QPixmap img(m_imageArray[0]->qsImagePath);
        QPixmap nextimg(m_imageArray[1]->qsImagePath);
        m_currentindex = 0;
        m_pButtonGroup->button(m_currentindex)->setChecked(true);
        m_topLabel->setPixmap(img);    
        m_bottomLabel->setPixmap(nextimg);
        this->startTimer();
    }    
}

void ScrollImageWidget::switchToImg(int index)
{
    //改变当前图片索引
    m_currentindex=index;
    //加载顶层Label和底层Label所需要显示的图片
    QPixmap currentimg(m_imageArray[m_currentindex]->qsImagePath);
    QPixmap nextimg;
    if (m_currentindex + 1 >= m_imageArray.size())
    {
        nextimg.load(m_imageArray[0]->qsImagePath);
    }
    else
    {
        nextimg.load(m_imageArray[m_currentindex + 1]->qsImagePath);
    }
    m_topLabel->setPixmap(currentimg);
    m_bottomLabel->setPixmap(nextimg);
    m_pButtonGroup->button(m_currentindex)->setChecked(true);
    //启动一系列定时器
    this->startTimer();
}


void ScrollImageWidget::setButtonSize(int diameter)
{
    m_buttonsize = diameter;
}

void ScrollImageWidget::setSwitchTime(int switchtime)
{
    m_switchtime = switchtime;
}

void ScrollImageWidget::setWaitingTime(int waitingtime)
{
    m_waitingtime = waitingtime;
}

void ScrollImageWidget::initUi()
{
    //初始化图片
    m_bottomLabel = new QLabel(this);
    m_bottomLabel->setScaledContents(true);
    m_topLabel = new QLabel(m_bottomLabel);
    m_topLabel->setScaledContents(true);
    //初始化布局
    m_vMainLayout = new QVBoxLayout(this);
    this->setLayout(m_vMainLayout);
    m_hButtonLayout = new QHBoxLayout(this);
    m_hButtonLayout->setContentsMargins(BUTTON_SPACING*m_dpi, 0, BUTTON_SPACING*m_dpi, 0);
    m_vMainLayout->addLayout(m_hButtonLayout);
    //初始化按钮集合
    m_pButtonGroup = new QButtonGroup(this);
    connect(m_pButtonGroup, static_cast<void (QButtonGroup::*)(int)>(&QButtonGroup::buttonClicked), this, &ScrollImageWidget::switchToImg);
}


void ScrollImageWidget::startTimer()
{
    //开始定时器时伴随着停止定时器
    m_timerswitch->stop();
    if (m_currentMode == SCROLL_MODE::FADE)
    {
        //图片透明度重置为100%
        m_imageopacity = 1;
        m_opacityeffect->setOpacity(m_imageopacity);
        m_topLabel->setGraphicsEffect(m_opacityeffect);
    }
    else if (m_currentMode == SCROLL_MODE::SLIDE)
    {
        //标记置0
        m_imagepos = 0;
        //图片移位到初始化位置
        m_topLabel->move(0, 0);
    }
    //按照用户指定等待时间启动定时器
    m_timerwait->start(m_waitingtime);
}

double ScrollImageWidget::getDPI()
{
    double dDpi = 1;
    //提取桌面的DC句柄
    HDC desktopDc = GetDC(NULL);
    //获取本地分辨率
    float horizontalDPI = GetDeviceCaps(desktopDc, LOGPIXELSX);
    float verticalDPI = GetDeviceCaps(desktopDc, LOGPIXELSY);
    //释放句柄
    ReleaseDC(NULL, desktopDc);
    //计算缩放比
    int dpi = (horizontalDPI + verticalDPI) / 2;
    dDpi = 1 + ((dpi - 96) / 24)*0.25;
    if (dDpi < 1)
    {
        dDpi = 1;
    }
    return dDpi;
}

void ScrollImageWidget::OnTimerSwitch()
{
    if (m_currentMode == SCROLL_MODE::FADE)
    {
        //计算下一个定时器周期内图片透明度
        m_imageopacity = m_imageopacity - 100.0 / m_switchtime;
        //透明度小于0,意味着图片需要切换了
        if (m_imageopacity <= 0)
        {
            m_currentindex++;
            //加载切换后顶层Label和底层Label对应的图片
            if (m_currentindex >= m_imageArray.size())
            {
                m_currentindex = 0;
            }
            QPixmap img(m_imageArray[m_currentindex]->qsImagePath);
            QPixmap nextimg;
            if (m_currentindex + 1 >= m_imageArray.size())
            {
                nextimg.load(m_imageArray[0]->qsImagePath);
            }
            else
            {
                nextimg.load(m_imageArray[m_currentindex + 1]->qsImagePath);
            }
            m_topLabel->setPixmap(img);
            m_bottomLabel->setPixmap(nextimg);
            m_pButtonGroup->button(m_currentindex)->setChecked(true);
            //启动一系列定时器
            this->startTimer();
        }
        else
        {
            m_opacityeffect->setOpacity(m_imageopacity);
            m_topLabel->setGraphicsEffect(m_opacityeffect);
        }
    }
    else if (m_currentMode == SCROLL_MODE::SLIDE)
    {
        m_imagepos += (m_topLabel->width())/(m_switchtime / 100);
        if (m_imagepos >= m_topLabel->width())
        {
            m_imagepos = 0;
            m_currentindex++;
            //加载切换后顶层Label和底层Label对应的图片
            if (m_currentindex >= m_imageArray.size())
            {
                m_currentindex = 0;
            }
            QPixmap img(m_imageArray[m_currentindex]->qsImagePath);
            QPixmap nextimg;
            if (m_currentindex + 1 >= m_imageArray.size())
            {
                nextimg.load(m_imageArray[0]->qsImagePath);
            }
            else
            {
                nextimg.load(m_imageArray[m_currentindex + 1]->qsImagePath);
            }
            m_topLabel->setPixmap(img);
            m_bottomLabel->setPixmap(nextimg);
            m_pButtonGroup->button(m_currentindex)->setChecked(true);
            //启动一系列定时器
            this->startTimer();
        }
        else
        {
            m_topLabel->move(-m_imagepos, 0);
        }

    }
}
    


void ScrollImageWidget::mousePressEvent(QMouseEvent *event)
{
    if (event->button() == Qt::LeftButton)
    {
        //鼠标左键按压,并且鼠标位置不在button区域,激活鼠标按压标记
        if (!this->m_hButtonLayout->geometry().contains(this->mapFromGlobal(QCursor::pos())))
        {
            m_bMousePress = true;
        }
    }
    return QWidget::mousePressEvent(event);
}

void ScrollImageWidget::mouseMoveEvent(QMouseEvent * event)
{
    return QWidget::mouseMoveEvent(event);
}

void ScrollImageWidget::mouseReleaseEvent(QMouseEvent *event)
{
    if (m_bMousePress)
    {
        //鼠标释放时,当前图片的连接不为空,则打开对应链接
        if (m_imageArray[m_currentindex]->qsUrl != NULL)
        {
            QDesktopServices::openUrl(m_imageArray[m_currentindex]->qsUrl);
        }
    }
    m_bMousePress = false;
    return QWidget::mouseReleaseEvent(event);
}

void ScrollImageWidget::resizeEvent(QResizeEvent *event)
{
    //底层图片自适应窗口
    m_bottomLabel->setGeometry(0, 0, this->width(), this->height());
    //顶层图片自适应底层图片
    m_topLabel->setGeometry(0, 0, this->width(), this->height());
    //主布局适应窗口
    m_vMainLayout->setGeometry(QRect(0, 0, this->width(), this->height()));
    //设置主布局
    m_vMainLayout->setContentsMargins((this->width() - m_imageArray.size()*(20 + m_buttonsize)*m_dpi) / 2,
        this->height() - (BUTTON_SPACING * 2 + m_buttonsize)*m_dpi,
        (this->width() - m_imageArray.size()*(BUTTON_SPACING * 2 + m_buttonsize)*m_dpi) / 2,
        BUTTON_SPACING*m_dpi);
    return QWidget::resizeEvent(event);
}
原文地址:https://www.cnblogs.com/suxia/p/14261677.html