废话不多说,直接上代码和效果图
两种切换效果(滑动切换,淡入淡出切换)
#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); }