窗体设计大概是这样
前面我们写到这里,接下来继续
这里的要点,每张图片的位置不同,每张图片的大小不同,没张图片显示的层级不同(就是图片重叠的情况下优先展示的)我们先看这几点,我这里把每张图片的这些特点都写进了list集合,这里详细介绍下
QList<int> m_ZValueList; //每张图片优先级的list,设置那张图片在上那张在下,如同raise()和lower()效果
QList<qreal> m_PixmapScaleList; //每张图片的缩放比例list
QList<QPointF> m_pointList; //每张图片的点
//P1-P10,为10个将图片x轴距离原点的位置
#define P0 (qreal)0.00
#define P1 (qreal)0.15
#define P2 (qreal)0.44
#define P3 (qreal)0.15
#define P4 (qreal)0.15
#define P5 (qreal)0.15
#define P6 P1
#define P7 P1
#define P8 P1
#define P9 P1
QList<qreal> pointList;
pointList<< P0<< P1 << P2<< P3 << P4 << P5 << P6 << P7 << P8 << P9; //P1-P10的list
m_ZValueList << 1 << 2 << 1 << 0 << 0 << 0 << 0 << 0 << 0 << 0;
m_PixmapScaleList << 0.8<< 1 << 0.8 << 0 << 0 << 0 << 0 << 0 << 0<<0;
QLineF MidLine(QPointF(0,0),QPointF(ui->bannerGraphicsView->width(),0));
//GraphicsView的x轴长度,用他和P1-P10来确定每张图片的位置
for(int i = 0;i < 10;i++)
{
QPointF point = MidLine.pointAt(pointList[i]);
if(i != 1)
{
point.setY(ui->bannerGraphicsView->height()/10);
}
m_pointList.append(point);
}
//放入到对应位置
然后位置就定好了,我们初始化图片项,我这里把项也放到一个list集合里,方便以后遍历
{
for (int i = 0; i < 10; i++)
{
QString pixPath = QString(":/images/homeTab/recommend/homeBanner/banner%1.png").arg(i);
QPixmap pix = QPixmap(pixPath);
pix = pix.scaled(ui->bannerGraphicsView->size(),Qt::KeepAspectRatio,Qt::SmoothTransformation);
//这句我也不知道有啥用,我试了去掉也没什么变化,可能图片的大小刚刚好,如果图片大小不合适的话就会出问题,还是加上合适
itemList.append(new BannerItem(pix));
itemList[i]->setScale(m_PixmapScaleList[i]);
itemList[i]->setIndex(i);
itemList[i]->setOffset(QPointF(0,0));
}
for(int i = 0; i<10; i++)
{
m_bannerScene->addItem(itemList[i]);//添加图元
itemList[i]->setTransformationMode(Qt::SmoothTransformation);//设置缩放模式
itemList[i]->setPos(m_pointList[i]);//设置位置
itemList[i]->setZValue(m_ZValueList[i]);//设置显示优先级
}
写到这里的时候,运行起来,每张图片都已经在正确的位置了,只是还不会动,接下来我们让他动起来,这里索引有些多,小伙伴们仔细看,别搞混了
这里定义每个项的动画,因为每个项的点不同,不能使用一个动画,还是使用list集合,然后qtimeline表示动画时间和循环次数,我这里3毫秒,循环1次
给每个动画绑定项
m_itemAnimation = QVector<QGraphicsItemAnimation *>(10);
m_timeline = new QTimeLine(300); //动画在多长时间完成,默认为1000,就是1秒
m_timeline->setLoopCount(1);//设置1次
for (int i = 0; i < 10; i++)
{
m_itemAnimation[i] = new QGraphicsItemAnimation();
m_itemAnimation[i]->setItem(itemList[i]);
m_itemAnimation[i]->setTimeLine(m_timeline);
}
接下来使用QTimer 设置一个间隔setInterval()
因为我们的动画只让他动一次,过多长时间又动,需要这个计时器
计时器我就不写了,调用start函数开始计时,到间隔后发送timeout()信号,用一个槽函数接受这个信号就可以了
我定义了三个成员变量
int m_currentItemIndex; //当前中心项索引
int m_firstItemIndex; //当前首项索引
int m_lastItemIndex; //当前尾项索引
这里时间到了,我们要整体向前推,就用这三个成员变量
`
``cpp
m_lastItemIndex = m_firstItemIndex;
m_firstItemIndex = m_currentItemIndex;
m_currentItemIndex = indexMap(m_currentItemIndex+1);
bannerRun();
这是动画函数,从第一个项向后重新定义大小,使用animation动画的setPosAt函数设置每项最终点,就完成了,这里如果到了0索引,再-1会变成
-1,如果到了9索引,再加会变成10索引,所以定义了一个索引映射函数indexMap(),保证每个索引是正确的
int CircularBannerWidget::indexMap(int index)
{
if(index == 10)
{
index = 0;
}
if(index == -1)
{
index = 9;
}
return index;
}
void CircularBannerWidget::bannerRun()
{
m_timeline->stop();
int index = m_firstItemIndex;
for (int i = 0; i<10; i++,index++)
{
index = indexMap(index);
itemList[index]->setTransformationMode(Qt::SmoothTransformation);//设置缩放模式
itemList[index]->setZValue(m_ZValueList[i]);//设置显示优先级
itemList[index]->setScale(m_PixmapScaleList[i]);
//itemList[index]->setPos(m_pointList[i]);
m_itemAnimation[index]->setPosAt(0.6,m_pointList[i]);
}
m_timeline->start();
m_timer->start(); //重新从0计时
m_buttonGroup->button(m_currentItemIndex)->setChecked(true);
}
这些大体的功能就完成了,接下来就是些小细节,我就不讲了,直接上代码,大家看看就懂了
头文件
#include <QWidget>
#include <QButtonGroup>
#include <QGraphicsScene>
#include <banneritem.h>
#include <QGraphicsItemAnimation>
#include <QVector>
#include <QTimeLine>
//P1-P10,为10个将图片x轴距离原点的位置
#define P0 (qreal)0.00
#define P1 (qreal)0.15
#define P2 (qreal)0.44
#define P3 (qreal)0.15
#define P4 (qreal)0.15
#define P5 (qreal)0.15
#define P6 P1
#define P7 P1
#define P8 P1
#define P9 P1
namespace Ui {
class CircularBannerWidget;
}
class CircularBannerWidget : public QWidget
{
Q_OBJECT
public:
explicit CircularBannerWidget(QWidget *parent = 0);
~CircularBannerWidget();
void initButtonGroup(); //初始化按钮组
void initDisplayPositionList(); //初始化位置,优先级,缩放比例的List
void initBanner(); //初始化轮播图
void initTimer(); //初始化计时器
void initSignalConnect(); //连接信号
void setPixmapList();
void addBannerItem();
void initAnimationAndTimeline();
void bannerRun();
int indexMap(int index); //索引映射,当index到最大或到最小转成正确的
private:
Ui::CircularBannerWidget *ui;
QButtonGroup *m_buttonGroup; //翻页按钮组
QList<int> m_ZValueList; //每张图片优先级的list,设置那张图片在上那张在下,如同raise()和lower()效果
QList<qreal> m_PixmapScaleList; //每张图片的缩放比例list
QGraphicsScene *m_bannerScene; //graphicsScene指针对象
//QList<QPixmap> m_PixmapList;
QList<BannerItem *> itemList; //每个轮播图项list
QTimer *m_timer; //计时器用来设置定时动画
QVector<QGraphicsItemAnimation *> m_itemAnimation; //项动画向量
QTimeLine *m_timeline; //timeline动画计时
int m_currentItemIndex; //当前中心项索引
int m_firstItemIndex; //当前首项索引
int m_lastItemIndex; //当前尾项索引
QList<QPointF> m_pointList; //每张图片的点
signals:
void ItemLink(int index);
private slots:
void m_timer_timeout();
void itemclick(int pageIndex);
void buttonEnterd(int index);
void on_lButton_clicked();
void on_rButton_clicked();
};
源文件
#include "circularbannerwidget.h"
#include "ui_circularbannerwidget.h"
#include <QDebug>
#include <QTimer>
#include <banneritem.h>
CircularBannerWidget::CircularBannerWidget(QWidget *parent) :
QWidget(parent),
ui(new Ui::CircularBannerWidget)
{
ui->setupUi(this);
this->setAttribute(Qt::WA_StyledBackground);
initButtonGroup();
initDisplayPositionList();
setPixmapList();
initBanner();
initTimer();
initAnimationAndTimeline();
initSignalConnect();
m_timer->start();
this->setStyleSheet("background:transparent");
this->setStyleSheet("QPushButton#lButton{image:url(:/images/homeTab/recommend/homeBanner/后退.png);border-radius:10px;background-color:rgb(59,70,68,100);}"
"QPushButton#rButton{image:url(:/images/homeTab/recommend/homeBanner/前进.png);border-radius:10px;background-color:rgb(59,70,68,100);}"
"QPushButton:hover#lButton,:hover#rButton{background-color:rgb(213,43,43);}");
}
CircularBannerWidget::~CircularBannerWidget()
{
delete ui;
}
void CircularBannerWidget::initButtonGroup()
{
m_buttonGroup = new QButtonGroup(this);
m_buttonGroup->addButton(ui->page0Button,0);
m_buttonGroup->addButton(ui->page1Button,1);
m_buttonGroup->addButton(ui->page2Button,2);
m_buttonGroup->addButton(ui->page3Button,3);
m_buttonGroup->addButton(ui->page4Button,4);
m_buttonGroup->addButton(ui->page5Button,5);
m_buttonGroup->addButton(ui->page6Button,6);
m_buttonGroup->addButton(ui->page7Button,7);
m_buttonGroup->addButton(ui->page8Button,8);
m_buttonGroup->addButton(ui->page9Button,9);
m_buttonGroup->setExclusive(true);
m_buttonGroup->button(1)->setChecked(true);
for (int i = 0; i<10; i++) {
qobject_cast<PageButton*>(m_buttonGroup->button(i))->setPage(i);
}
}
void CircularBannerWidget::initDisplayPositionList()
{
QList<qreal> pointList;
pointList<< P0<< P1 << P2<< P3 << P4 << P5 << P6 << P7 << P8 << P9; //P1-P10的list
m_ZValueList << 1 << 2 << 1 << 0 << 0 << 0 << 0 << 0 << 0 << 0;
m_PixmapScaleList << 0.8<< 1 << 0.8 << 0 << 0 << 0 << 0 << 0 << 0<<0;
QLineF MidLine(QPointF(0,0),QPointF(ui->bannerGraphicsView->width(),0));
//GraphicsView的x轴长度,用他和P1-P10来确定每张图片的位置
for(int i = 0;i < 10;i++)
{
QPointF point = MidLine.pointAt(pointList[i]);
if(i != 1)
{
point.setY(ui->bannerGraphicsView->height()/10);
}
m_pointList.append(point);
}
//放入到对应位置
}
void CircularBannerWidget::initBanner()
{
ui->bannerGraphicsView->setStyleSheet("background: transparent; padding: 0px; border: 0px;");
m_bannerScene = new QGraphicsScene(this);
ui->bannerGraphicsView->setScene(m_bannerScene);
ui->bannerGraphicsView->setSceneRect(0,0,ui->bannerGraphicsView->width(),ui->bannerGraphicsView->height());
addBannerItem();
m_currentItemIndex = 1;
m_firstItemIndex = 0;
m_lastItemIndex = 9;
}
void CircularBannerWidget::initTimer()
{
m_timer = new QTimer(this);
m_timer->setInterval(5000);
}
void CircularBannerWidget::initSignalConnect()
{
connect(m_timer,SIGNAL(timeout()),this,SLOT(m_timer_timeout()));
for(int i = 0; i<10; i++)
{
connect(itemList[i],SIGNAL(clicked(int)),this,SLOT(itemclick(int)));
//void (pageButton::*signalPtr)(int) = &pageButton::entered; //信号指针,指向pageButton的enterd信号,我这里就不这样写了
connect(qobject_cast<PageButton *>(m_buttonGroup->button(i)),SIGNAL(clicked(int)),this,SLOT(buttonEnterd(int)));
//使用cast转换成自定义按钮类型
}
}
void CircularBannerWidget::setPixmapList()
{
for (int i = 0; i < 10; i++)
{
QString pixPath = QString(":/images/homeTab/recommend/homeBanner/banner%1.png").arg(i);
QPixmap pix = QPixmap(pixPath);
pix = pix.scaled(ui->bannerGraphicsView->size(),Qt::KeepAspectRatio,Qt::SmoothTransformation);
//这句我也不知道有啥用,我试了去掉也没什么变化,可能图片的大小刚刚好,如果图片大小不合适的话就会出问题,还是加上合适
itemList.append(new BannerItem(pix));
itemList[i]->setScale(m_PixmapScaleList[i]);
itemList[i]->setIndex(i);
itemList[i]->setOffset(QPointF(0,0));
}
}
void CircularBannerWidget::addBannerItem()
{
for(int i = 0; i<10; i++)
{
m_bannerScene->addItem(itemList[i]);//添加图元
itemList[i]->setTransformationMode(Qt::SmoothTransformation);//设置缩放模式
itemList[i]->setPos(m_pointList[i]);//设置位置
itemList[i]->setZValue(m_ZValueList[i]);//设置显示优先级
}
}
void CircularBannerWidget::initAnimationAndTimeline()
{
m_itemAnimation = QVector<QGraphicsItemAnimation *>(10);
m_timeline = new QTimeLine(300); //动画在多长时间完成,默认为1000,就是1秒
m_timeline->setLoopCount(1);//设置1次
for (int i = 0; i < 10; i++)
{
m_itemAnimation[i] = new QGraphicsItemAnimation();
m_itemAnimation[i]->setItem(itemList[i]);
m_itemAnimation[i]->setTimeLine(m_timeline);
}
}
void CircularBannerWidget::itemclick(int itemIndex)
{
emit ItemLink(itemIndex);
//这里由于有item重叠,点中间项的时候会点到两个项,如果写if(itemIndex == m_currentItemIndex)不可行
//我这里也不知道怎么搞,就先这样写,以后有时间再看看
return;
}
void CircularBannerWidget::bannerRun()
{
m_timeline->stop();
int index = m_firstItemIndex;
for (int i = 0; i<10; i++,index++)
{
index = indexMap(index);
itemList[index]->setTransformationMode(Qt::SmoothTransformation);//设置缩放模式
itemList[index]->setZValue(m_ZValueList[i]);//设置显示优先级
itemList[index]->setScale(m_PixmapScaleList[i]);
//itemList[index]->setPos(m_pointList[i]);
m_itemAnimation[index]->setPosAt(0.6,m_pointList[i]);
}
m_timeline->start();
m_timer->start(); //重新从0计时
m_buttonGroup->button(m_currentItemIndex)->setChecked(true);
}
int CircularBannerWidget::indexMap(int index)
{
if(index == 10)
{
index = 0;
}
if(index == -1)
{
index = 9;
}
return index;
}
void CircularBannerWidget::m_timer_timeout()
{
m_lastItemIndex = m_firstItemIndex;
m_firstItemIndex = m_currentItemIndex;
m_currentItemIndex = indexMap(m_currentItemIndex+1);
bannerRun();
}
void CircularBannerWidget::buttonEnterd(int index)
{
m_currentItemIndex = index;
m_firstItemIndex = indexMap(index-1);
m_lastItemIndex = indexMap(m_firstItemIndex-1);
bannerRun();
}
void CircularBannerWidget::on_lButton_clicked()
{
m_currentItemIndex = m_firstItemIndex;
m_firstItemIndex = m_lastItemIndex;
m_lastItemIndex = indexMap(m_lastItemIndex-1);
bannerRun();
}
void CircularBannerWidget::on_rButton_clicked()
{
m_lastItemIndex = m_firstItemIndex;
m_firstItemIndex = m_currentItemIndex;
m_currentItemIndex = indexMap(m_currentItemIndex+1);
bannerRun();
}
然后我这里是参考了博客https://blog.csdn.net/weixin_42126427/article/details/121083770?spm=1001.2014.3001.5502
以及
https://blog.csdn.net/h391998495979/article/details/101868838
的代码,只是我取其精华去其糟粕了,把好多多余的地方都删掉了,不过还是感谢两位,大家也可以参考下
大家喜欢我的博客,可以关注我,感谢大家的支持