一、前言

上一篇文章寫的廣告輪播控制項,採用的傳統widget堆積設置樣式表做的,這次必須要用到更高級的QPainter來繪製了,這個纔是最高效的辦法,本控制項參考雨田哥的輪播控制項,經過大規模的改造而成,相比於原來的廣告輪播控制項,本控制項可以說完爆他,按在地上使勁摩擦。除了可以設置圖片路徑集合以外,還可以設置對應的提示信息,這個在眾多的web輪播圖片效果中最常見,比如新聞的標題等,可以更直觀的顯示當前圖片,而且單擊圖片還可以支持跳轉,指示器的位置也能設置左邊+中間+右邊,指示器的樣式更加增加到橢圓條狀+圓形+矩形+小圓點+長條狀多種可選擇,可以說涵蓋了各種web輪播圖片的效果,還可以設置滑鼠懸停暫停輪播,以便看清說明後滑鼠移開繼續輪播。指示器的寬高顏色等,都是可以自由設定的,這個對於採用QPainter繪製來說,是最好自定義的,無非就是設置對應的畫筆QPen和對應的畫布QBrush的顏色啦。

二、實現的功能

* 1:可設置圖片路徑集合

* 2:可設置提示信息集合

* 3:可設置指示器最小寬度和最大寬度

* 4:可設置圖片切換間隔

* 5:可設置指示器顏色和提示文字顏色

* 6:可設置指示器高度

* 7:可設置指示器顯示序號

* 8:可設置指示器位置 左邊+中間+右邊

* 9:可設置指示器的樣式 橢圓條狀+圓形+矩形+小圓點+長條狀

* 10:可設置滑鼠懸停停止輪播

三、效果圖

四、頭文件代碼

#ifndef ADSWIDGET2_H
#define ADSWIDGET2_H

/**
* 圖片輪播控制項 作者:feiyangqingyun(QQ:517216493) 2018-9-12
* 參考雨田哥(QQ:3246214072) https://blog.csdn.net/ly305750665/article/details/82496046
* 1:可設置圖片路徑集合
* 2:可設置提示信息集合
* 3:可設置指示器最小寬度和最大寬度
* 4:可設置圖片切換間隔
* 5:可設置指示器顏色和提示文字顏色
* 6:可設置指示器高度
* 7:可設置指示器顯示序號
* 8:可設置指示器位置 左邊+中間+右邊
* 9:可設置指示器的樣式 橢圓條狀+圓形+矩形+小圓點+長條狀
* 10:可設置滑鼠懸停停止輪播
*/

#include <QWidget>

class QLabel;
class QHBoxLayout;
class QSpacerItem;
class QParallelAnimationGroup;
class QPropertyAnimation;

#ifdef quc
#if (QT_VERSION < QT_VERSION_CHECK(5,7,0))
#include <QtDesigner/QDesignerExportWidget>
#else
#include <QtUiPlugin/QDesignerExportWidget>
#endif

class QDESIGNER_WIDGET_EXPORT AdsWidget2 : public QWidget
#else
class AdsWidget2 : public QWidget
#endif

{
Q_OBJECT
Q_ENUMS(NavPosition)
Q_ENUMS(NavStyle)

Q_PROPERTY(bool hoverStop READ getHoverStop WRITE setHoverStop)
Q_PROPERTY(bool showNumber READ getShowNumber WRITE setShowNumber)

Q_PROPERTY(int minHeight READ getMinHeight WRITE setMinHeight)
Q_PROPERTY(int minWidth READ getMinWidth WRITE setMinWidth)
Q_PROPERTY(int maxWidth READ getMaxWidth WRITE setMaxWidth)
Q_PROPERTY(int interval READ getInterval WRITE setInterval)
Q_PROPERTY(int navRadius READ getNavRadius WRITE setNavRadius)

Q_PROPERTY(QColor navColor READ getNavColor WRITE setNavColor)
Q_PROPERTY(QColor textColor READ getTextColor WRITE setTextColor)
Q_PROPERTY(QColor tipColor READ getTipColor WRITE setTipColor)
Q_PROPERTY(QColor darkColor READ getDarkColor WRITE setDarkColor)

Q_PROPERTY(QString imageNames READ getImageNames WRITE setImageNames)
Q_PROPERTY(QString imageTips READ getImageTips WRITE setImageTips)

Q_PROPERTY(NavPosition navPosition READ getNavPosition WRITE setNavPosition)
Q_PROPERTY(NavStyle navStyle READ getNavStyle WRITE setNavStyle)

public:
enum NavPosition {
NavPosition_Left = 0, //左邊位置
NavPosition_Center = 1, //中間位置
NavPosition_Right = 2 //右側位置
};

enum NavStyle {
NavStyle_Ellipse = 0, //橢圓條狀
NavStyle_Circle = 1, //圓形
NavStyle_Rect = 2, //矩形
NavStyle_Dot = 3, //小圓點
NavStyle_LongRect = 4 //長條狀
};

AdsWidget2(QWidget *parent = 0);
~AdsWidget2();

protected:
bool eventFilter(QObject *obj, QEvent *event);
void enterEvent(QEvent *);
void leaveEvent(QEvent *);
void showEvent(QShowEvent *);
void paintEvent(QPaintEvent *);

private:
bool hoverStop; //滑鼠懸停停止輪播
bool showNumber; //是否顯示序號

int minHeight; //指示器最小高度
int minWidth; //指示器最小拉伸寬度
int maxWidth; //指示器最大拉伸寬度
int interval; //圖片切換間隔,單位毫秒
int navRadius; //指示器圓角角度

QColor navColor; //指示器顏色
QColor textColor; //指示器文字顏色
QColor tipColor; //提示文字顏色
QColor darkColor; //當前指示器加深顏色

QString imageNames; //圖片名稱
QString imageTips; //提示信息

NavPosition navPosition;//指示器位置
NavStyle navStyle; //指示器樣式

QList<QString> names; //圖片名稱集合
QList<QString> tips; //提示信息集合
QList<QLabel *> labs; //存儲指示器集合

bool leftToRight; //從左往右切換
int offset; //圖片切換偏移量
int currentIndex; //當前圖片索引
int previousIndex; //上一張圖片索引

QString qssNormal; //正常狀態樣式
QString qssCurrent; //當前狀態樣式

QTimer *timer; //定時器切換
QHBoxLayout *layout; //指示器所在佈局
QSpacerItem *spacerLeft;//左側彈簧
QSpacerItem *spacerRight;//右側彈簧
QWidget *widgetNav; //存放導航指示器的容器

//動畫切換
QParallelAnimationGroup *animationGroup;
QPropertyAnimation *animationImage;
QPropertyAnimation *animationMin;
QPropertyAnimation *animationMax;

private slots:
void initWidget();
void initForm();
void initQss();
void changedAds();
void changedAds(QLabel *lab);
void changedImage(const QVariant &);
void changedMin(const QVariant &);
void changedMax(const QVariant &);

public:
bool getHoverStop() const;
bool getShowNumber() const;

int getMinHeight() const;
int getMinWidth() const;
int getMaxWidth() const;
int getInterval() const;
int getNavRadius() const;

QColor getNavColor() const;
QColor getTextColor() const;
QColor getTipColor() const;
QColor getDarkColor() const;

QString getImageNames() const;
QString getImageTips() const;

NavPosition getNavPosition()const;
NavStyle getNavStyle() const;

QSize sizeHint() const;
QSize minimumSizeHint() const;

public Q_SLOTS:
//設置滑鼠懸停停止輪播
void setHoverStop(bool hoverStop);
//設置是否顯示序號
void setShowNumber(bool showNumber);

//設置指示器高度
void setMinHeight(int minHeight);
//設置最小拉伸寬度
void setMinWidth(int minWidth);
//設置最大拉伸寬度
void setMaxWidth(int maxWidth);
//設置切換間隔
void setInterval(int interval);
//設置指示器圓角角度
void setNavRadius(int navRadius);

//設置指示器顏色
void setNavColor(const QColor &navColor);
//設置指示器文字顏色
void setTextColor(const QColor &textColor);
//設置提示信息顏色
void setTipColor(const QColor &tipColor);
//設置加深顏色
void setDarkColor(const QColor &darkColor);

//設置圖片名稱
void setImageNames(const QString &imageNames);
//設置提示信息
void setImageTips(const QString &imageTips);

//設置指示器位置
void setNavPosition(const NavPosition &navPosition);
//設置指示器樣式
void setNavStyle(const NavStyle &navStyle);
};

#endif // ADSWIDGET2_H

五、核心代碼

void AdsWidget2::paintEvent(QPaintEvent *)
{
if (names.count() == 0) {
return;
}

int width = this->width();
int height = this->height();

QPainter painter(this);
painter.setRenderHints(QPainter::SmoothPixmapTransform | QPainter::TextAntialiasing);
QTextOption option(Qt::AlignLeft | Qt::AlignVCenter);
painter.setPen(tipColor);

//設置字體
QFont font;
font.setPixelSize(15);
painter.setFont(font);

//取出上一張圖片+當前圖片,並平滑縮放
QPixmap previousPix(names.at(previousIndex));
QPixmap currentPix(names.at(currentIndex));
previousPix = previousPix.scaled(width, height, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation);
currentPix = currentPix.scaled(width, height, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation);

int widthOffset = offset + width;
if (leftToRight) {
widthOffset = offset - width;
}

//繪製上一張圖片
painter.drawPixmap(offset, 0, previousPix);
//繪製當前圖片
painter.drawPixmap(widthOffset, 0, currentPix);

//繪製上一張圖片提示信息,有可能上一張圖片提示信息為空
if (previousIndex <= tips.count() - 1) {
painter.drawText(QRect(offset + 10, height - minHeight - 40, width - 20, 30), tips.at(previousIndex), option);
}

//繪製當前圖片提示信息,有可能當前圖片提示信息為空
if (currentIndex <= tips.count() - 1) {
painter.drawText(QRect(widthOffset + 10, height - minHeight - 40, width - 20, 30), tips.at(currentIndex), option);
}
}

void AdsWidget2::initWidget()
{
//放置指示器的窗體載體
widgetNav = new QWidget(this);
widgetNav->setObjectName(QString::fromUtf8("widgetNav"));

//給指示器窗體加上左右佈局
layout = new QHBoxLayout(widgetNav);
layout->setSpacing(3);

//主佈局,上下佈局
QVBoxLayout *verticalLayout = new QVBoxLayout(this);
verticalLayout->setSpacing(0);
verticalLayout->setContentsMargins(0, 0, 0, 0);

//上部彈簧,用於將指示器區域彈到底部
QSpacerItem *verticalSpacer = new QSpacerItem(10, 10, QSizePolicy::Minimum, QSizePolicy::Expanding);
verticalLayout->addItem(verticalSpacer);

//將指示器窗體加入到主佈局中下部
verticalLayout->addWidget(widgetNav);

//實例化左側右側彈簧
spacerLeft = new QSpacerItem(1, 1, QSizePolicy::Expanding, QSizePolicy::Minimum);
spacerRight = new QSpacerItem(1, 1, QSizePolicy::Expanding, QSizePolicy::Minimum);
}

void AdsWidget2::initForm()
{
hoverStop = true;
showNumber = false;

minHeight = 6;
minWidth = 6;
maxWidth = 25;
interval = 3000;
navRadius = 3;

navColor = QColor(220, 220, 220);
textColor = QColor(20, 20, 20);
tipColor = QColor(255, 255, 255);
darkColor = QColor(255, 255, 255);

imageNames.clear();
imageTips.clear();

navPosition = NavPosition_Left;
navStyle = NavStyle_Ellipse;

leftToRight = true;
offset = 0;
currentIndex = 0;
previousIndex = 0;

//定時器切換圖片
timer = new QTimer(this);
timer->setInterval(interval);
connect(timer, SIGNAL(timeout()), this, SLOT(changedAds()));

this->setMouseTracking(true);

//定義動畫組
animationGroup = new QParallelAnimationGroup(this);

//定義動畫切換圖片
animationImage = new QPropertyAnimation(this, "");
connect(animationImage, SIGNAL(valueChanged(const QVariant &)), this, SLOT(changedImage(const QVariant &)));
animationImage->setEasingCurve(QEasingCurve::OutCirc);
animationImage->setDuration(1000);
animationGroup->addAnimation(animationImage);

QSequentialAnimationGroup *sequentialGroup = new QSequentialAnimationGroup(animationGroup);

//用於切換最小拉伸寬度
animationMin = new QPropertyAnimation(sequentialGroup, "");
connect(animationMin, SIGNAL(valueChanged(const QVariant &)), this, SLOT(changedMin(const QVariant &)));
animationMin->setEasingCurve(QEasingCurve::OutCubic);
animationMin->setDuration(500);

//用於切換最大拉伸寬度
animationMax = new QPropertyAnimation(sequentialGroup, "");
connect(animationMax, SIGNAL(valueChanged(const QVariant &)), this, SLOT(changedMax(const QVariant &)));
animationMax->setEasingCurve(QEasingCurve::OutCubic);
animationMax->setDuration(500);

//按鈕切換串列運行
sequentialGroup->addAnimation(animationMin);
sequentialGroup->addAnimation(animationMax);
animationGroup->addAnimation(sequentialGroup);
}

void AdsWidget2::initQss()
{
//可自行拓展自定義樣式
if (navStyle == NavStyle_Dot) {
qssNormal = QString("border:none;background:qradialgradient(spread:pad,cx:0.5,cy:0.5,radius:0.5,fx:0.5,fy:0.5,"
"stop:0 #00FFFFFF,stop:0.4 #00FFFFFF,"
"stop:0.5 #00FFFFFF,stop:0.6 #00FFFFFF,stop:0.7 rgba(%1,%2,%3,%4));"
"color:rgba(%5,%6,%7,%8);border-radius:%9px;")
.arg(navColor.red()).arg(navColor.green()).arg(navColor.blue()).arg(navColor.alpha())
.arg(textColor.red()).arg(textColor.green()).arg(textColor.blue()).arg(navColor.alpha()).arg(navRadius);
qssCurrent = QString("border:none;background:qradialgradient(spread:pad,cx:0.5,cy:0.5,radius:0.5,fx:0.5,fy:0.5,"
"stop:0 rgba(%1,%2,%3,%4),stop:0.4 rgba(%1,%2,%3,%4),"
"stop:0.5 #00FFFFFF,stop:0.6 #00FFFFFF,stop:0.7 rgba(%1,%2,%3,%4));"
"color:rgba(%5,%6,%7,%8);border-radius:%9px;")
.arg(darkColor.red()).arg(darkColor.green()).arg(darkColor.blue()).arg(darkColor.alpha())
.arg(textColor.red()).arg(textColor.green()).arg(textColor.blue()).arg(navColor.alpha()).arg(navRadius);

} else {
qssNormal = QString("border:none;background:rgba(%1,%2,%3,%4);color:rgba(%5,%6,%7,%8);border-radius:%9px;")
.arg(navColor.red()).arg(navColor.green()).arg(navColor.blue()).arg(navColor.alpha())
.arg(textColor.red()).arg(textColor.green()).arg(textColor.blue()).arg(navColor.alpha()).arg(navRadius);
qssCurrent = QString("border:none;background:rgba(%1,%2,%3,%4);color:rgba(%5,%6,%7,%8);border-radius:%9px;")
.arg(darkColor.red()).arg(darkColor.green()).arg(darkColor.blue()).arg(darkColor.alpha())
.arg(textColor.red()).arg(textColor.green()).arg(textColor.blue()).arg(textColor.alpha()).arg(navRadius);
}
}

void AdsWidget2::changedAds()
{
if (names.count() == 0) {
return;
}

previousIndex = currentIndex;
if (currentIndex < names.count() - 1) {
currentIndex++;
} else {
currentIndex = 0;
}

changedAds(labs.at(currentIndex));
}

六、控制項介紹

1. 超過146個精美控制項,涵蓋了各種儀錶盤、進度條、進度球、指南針、曲線圖、標尺、溫度計、導航條、導航欄,flatui、高亮按鈕、滑動選擇器、農曆等。遠超qwt集成的控制項數量。

2. 每個類都可以獨立成一個單獨的控制項,零耦合,每個控制項一個頭文件和一個實現文件,不依賴其他文件,方便單個控制項以源碼形式集成到項目中,較少代碼量。qwt的控制項類環環相扣,高度耦合,想要使用其中一個控制項,必須包含所有的代碼。

3. 全部純Qt編寫,QWidget+QPainter繪製,支持Qt4.6到Qt5.12的任何Qt版本,支持mingw、msvc、gcc等編譯器,支持任意操作系統比如windows+linux+mac+嵌入式linux等,不亂碼,可直接集成到Qt Creator中,和自帶的控制項一樣使用,大部分效果只要設置幾個屬性即可,極為方便。

4. 每個控制項都有一個對應的單獨的包含該控制項源碼的DEMO,方便參考使用。同時還提供一個所有控制項使用的集成的DEMO。

5. 每個控制項的源代碼都有詳細中文注釋,都按照統一設計規範編寫,方便學習自定義控制項的編寫。

6. 每個控制項默認配色和demo對應的配色都非常精美。

7. 超過130個可見控制項,6個不可見控制項。

8. 部分控制項提供多種樣式風格選擇,多種指示器樣式選擇。

9. 所有控制項自適應窗體拉伸變化。

10. 集成自定義控制項屬性設計器,支持拖曳設計,所見即所得,支持導入導出xml格式。

11. 自帶activex控制項demo,所有控制項可以直接運行在ie瀏覽器中。

12. 集成fontawesome圖形字體+阿里巴巴iconfont收藏的幾百個圖形字體,享受圖形字體帶來的樂趣。

13. 所有控制項最後生成一個dll動態庫文件,可以直接集成到qtcreator中拖曳設計使用。

七、SDK下載

- SDK下載鏈接:pan.baidu.com/s/1tD9v1Y 提取碼:lyhk

- 自定義控制項+屬性設計器欣賞:pan.baidu.com/s/1l6L3rK 提取碼:tmvl

- 下載鏈接中包含了各個版本的動態庫文件,所有控制項的頭文件,使用demo。

- 自定義控制項插件開放動態庫dll使用(永久免費),無任何後門和限制,請放心使用。

- 目前已提供26個版本的dll,其中包括了qt5.12.3 msvc2017 32+64 mingw 32+64 的。

- 不定期增加控制項和完善控制項,不定期更新SDK,歡迎各位提出建議,謝謝!

- widget版本(QQ:517216493)qml版本(QQ:373955953)三峯駝(QQ:278969898)。

- 濤哥的知乎專欄 Qt進階之路 Qt進階之路

- 歡迎關注微信公眾號【高效程序員】,C++/Python、學習方法、寫作技巧、熱門技術、職場發展等內容,乾貨多多,福利多多!


推薦閱讀:
相關文章