天天看點

Qt編寫自定義控件62-探探雷達

一、前言

随着移動網際網路的盛行,現在手機APP大行其道,每個人的手機沒有十幾個APP都不好意思說自己是現代人,各種聊天、購物、直播、小視訊等APP,有個陌生人社交的APP叫探探,本人用過幾次,當然不是去為了找對象,而是純粹為了好玩研究下他的U設計和軟體邏輯流程等,其中有個雷達控件,單擊以後可以搜尋附近的異性進行配對,這個雷達控件的效果蠻好的,于是手癢琢磨着用Qt來實作一個,畢竟自己寫了150多個控件了,已經上瘾了,對各種效果都如魚得水,看到各種效果都不自然的想到編碼思路等。

這個控件的核心其實就是外圍的那個掃描圈和發散的掃描線,中間變大變小恢複正常的圓形頭像,外圍的掃描圈采用錐形漸變顔色,通過透明度控制形成掃描效果,核心方法就是drawPie,至于擴散圈,需要識别到單擊以後将擴散圈存入隊列,因為可能會單擊多次,産生多個擴散圈,至于中間頭像的動态效果,采用三個QPropertyAnimation來實作,一個負責變大,一個負責變小,一個負責恢複正常,然後三個動畫加入到QSequentialAnimationGroup動畫序列中,按照順序執行。

二、實作的功能

  • 1:可設定中間圖像
  • 2:可設定圖像的邊框寬度+邊框顔色,産生圓形圖像效果
  • 3:可設定掃描線的最大半徑
  • 4:可設定掃描線的邊框寬度
  • 5:可設定擴散圈的線條寬度
  • 6:可設定掃描線的每次移動的步長
  • 7:可設定擴散圈的每次移動的步長
  • 8:可設定掃描線的顔色
  • 9:可設定擴散圈的顔色

三、效果圖

Qt編寫自定義控件62-探探雷達

四、頭檔案代碼

#ifndef SCANTANTAN_H
#define SCANTANTAN_H

/**
 * 探探雷達控件 作者:東門吹雪(QQ:709102202) 整理:feiyangqingyun(QQ:517216493) 2019-10-01
 * 1:可設定中間圖像
 * 2:可設定圖像的邊框寬度+邊框顔色,産生圓形圖像效果
 * 3:可設定掃描線的最大半徑
 * 4:可設定掃描線的邊框寬度
 * 5:可設定擴散圈的線條寬度
 * 6:可設定掃描線的每次移動的步長
 * 7:可設定擴散圈的每次移動的步長
 * 8:可設定掃描線的顔色
 * 9:可設定擴散圈的顔色
 */

#include <QWidget>

class QSequentialAnimationGroup;

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

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

{
    Q_OBJECT
    Q_PROPERTY(QPixmap image READ getImage WRITE setImage)
    Q_PROPERTY(int imageBorderWidth READ getImageBorderWidth WRITE setImageBorderWidth)
    Q_PROPERTY(QColor imageBorderColor READ getImageBorderColor WRITE setImageBorderColor)

    Q_PROPERTY(int scanRadius READ getScanRadius WRITE setScanRadius)
    Q_PROPERTY(int scanWidth READ getScanWidth WRITE setScanWidth)
    Q_PROPERTY(int ringWidth READ getRingWidth WRITE setRingWidth)

    Q_PROPERTY(int scanStep READ getScanStep WRITE setScanStep)
    Q_PROPERTY(int ringStep READ getRingStep WRITE setRingStep)

    Q_PROPERTY(QColor scanColor READ getScanColor WRITE setScanColor)
    Q_PROPERTY(QColor ringColor READ getRingColor WRITE setRingColor)

public:
    struct RingData {
        int radius;     //半徑
        float width;    //畫筆粗細
        int alpha;      //透明度
    };

    explicit ScanTanTan(QWidget *parent = 0);

protected:
    void mousePressEvent(QMouseEvent *);
    void mouseReleaseEvent(QMouseEvent *);
    void paintEvent(QPaintEvent *);
    void drawScan(QPainter *painter);
    void drawRing(QPainter *painter);
    void drawImage(QPainter *painter);

private slots:
    void changeScan();
    void changeRing();
    void updateImage(const QVariant &value);
    double twoPtDistance(const QPointF &pt1, const QPointF &pt2);

private:
    QPixmap image;          //中間圖檔
    int imageBorderWidth;   //圖檔邊框寬度
    QColor imageBorderColor;//圖檔邊框顔色

    int scanRadius;         //掃描線最大半徑
    int scanWidth;          //掃描線邊框寬度
    int ringWidth;          //擴散圈線條寬度

    int scanStep;           //掃描線每次移動的步長
    int ringStep;           //擴散圈每次移動的步長

    QColor scanColor;       //掃描線顔色
    QColor ringColor;       //擴散圈顔色

    bool isPressed;         //滑鼠是否按下
    int ringRadius;         //擴散圈半徑
    int imageRadius;        //圖檔半徑
    int scanDeg;            //目前掃描線角度

    //擴散圈集合,滑鼠可能按下多次則産生多個擴散圈,用隊列存起來
    QList<RingData> rings;
    //動畫組合,用于中間圖檔的變大放小
    QSequentialAnimationGroup *animationGroup;

public:
    QPixmap getImage()          const;
    int getImageBorderWidth()   const;
    QColor getImageBorderColor()const;

    int getScanRadius()         const;
    int getScanWidth()          const;
    int getRingWidth()          const;

    int getScanStep()           const;
    int getRingStep()           const;

    QColor getScanColor()       const;
    QColor getRingColor()       const;

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

public Q_SLOTS:
    //設定圖檔+圖檔邊框寬度+圖檔邊框顔色
    void setImage(const QPixmap &image);
    void setImageBorderWidth(int imageBorderWidth);
    void setImageBorderColor(const QColor &imageBorderColor);

    //設定掃描線最大半徑+掃描線邊框寬度+擴散圈線條寬度
    void setScanRadius(int scanRadius);
    void setScanWidth(int scanWidth);
    void setRingWidth(int ringWidth);

    //設定掃描線步長+擴散圈步長
    void setScanStep(int scanStep);
    void setRingStep(int ringStep);

    //設定掃描線顔色+擴散圈顔色
    void setScanColor(const QColor &scanColor);
    void setRingColor(const QColor &ringColor);
};

#endif // SCANTANTAN_H

           

五、核心代碼

void ScanTanTan::paintEvent(QPaintEvent *)
{
    int width = this->width();
    int height = this->height();
    int side = qMin(width, height);

    //繪制準備工作,啟用反鋸齒,平移坐标軸中心,等比例縮放
    QPainter painter(this);
    painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform);
    painter.translate(width / 2, height / 2);
    painter.scale(side / 200.0, side / 200.0);

    //繪制掃描線
    drawScan(&painter);
    //繪制擴散圈
    drawRing(&painter);
    //繪制中間圖檔
    drawImage(&painter);
}

void ScanTanTan::drawScan(QPainter *painter)
{
    painter->save();

    //錐形漸變顔色,通過透明度控制形成掃描效果
    QConicalGradient conicalGradient(0, 0, scanDeg);
    QColor color = scanColor;
    color.setAlpha(50);
    conicalGradient.setColorAt(0, color);
    color.setAlpha(0);
    conicalGradient.setColorAt(1, color);

    //設定畫筆畫刷
    QPen pen;
    pen.setWidth(scanWidth);
    pen.setBrush(conicalGradient);
    painter->setPen(pen);
    painter->setBrush(conicalGradient);

    //繪制餅圓
    QRect rect(-scanRadius, -scanRadius, scanRadius * 2, scanRadius * 2);
    painter->drawPie(rect, scanDeg * 16, 360 * 16);

    painter->restore();
}

void ScanTanTan::drawRing(QPainter *painter)
{
    painter->save();
    painter->setBrush(Qt::NoBrush);

    //繪制所有擴散圈,擴散圈其實就是個沒有背景顔色的圓形
    for (int i = 0; i < rings.count(); i++) {
        RingData ring = rings.at(i);
        int radius = ring.radius;
        float width = ring.width;
        int alpha = 255 - ring.alpha;
        QColor color = ringColor;
        color.setAlpha(alpha);

        QPen pen;
        pen.setWidthF(width);
        pen.setColor(color);
        painter->setPen(pen);
        painter->drawEllipse(-radius, -radius, radius * 2, radius * 2);
    }

    painter->restore();
}

void ScanTanTan::drawImage(QPainter *painter)
{
    painter->save();

    //設定圓形遮罩路徑,産生圓形頭像效果
    QPainterPath path;
    path.addEllipse(QPoint(0, 0), imageRadius, imageRadius);
    painter->setClipPath(path);

    //繪制圖檔
    QRect rect(-imageRadius, -imageRadius, imageRadius * 2, imageRadius * 2);
    painter->drawPixmap(rect, image);

    //繪制圖檔邊緣圓形
    QPen pen;
    pen.setWidth(imageBorderWidth);
    pen.setColor(imageBorderColor);
    painter->setPen(pen);
    painter->setBrush(Qt::NoBrush);

    //以下兩種方法二選一,其實繪制360度的圓弧=繪制無背景的圓形
    //painter->drawArc(rect, 0, 360 * 16);
    painter->drawEllipse(rect);

    painter->restore();
}
           

六、控件介紹

  1. 超過160個精美控件,涵蓋了各種儀表盤、進度條、進度球、指南針、曲線圖、标尺、溫度計、導覽列、導航欄,flatui、高亮按鈕、滑動選擇器、農曆等。遠超qwt內建的控件數量。
  2. 每個類都可以獨立成一個單獨的控件,零耦合,每個控件一個頭檔案和一個實作檔案,不依賴其他檔案,友善單個控件以源碼形式內建到項目中,較少代碼量。qwt的控件類環環相扣,高度耦合,想要使用其中一個控件,必須包含所有的代碼。
  3. 全部純Qt編寫,QWidget+QPainter繪制,支援Qt4.6到Qt5.13的任何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或者so等),可以直接內建到qtcreator中拖曳設計使用。
  14. 目前已經有qml版本,後期會考慮出pyqt版本,如果使用者需求量很大的話。
  15. 自定義控件插件開放動态庫使用(永久免費),無任何後門和限制,請放心使用。
  16. 目前已提供26個版本的dll,其中包括了qt5.12.3 msvc2017 32+64 mingw 32+64 的。
  17. 不定期增加控件和完善控件,不定期更新SDK,歡迎各位提出建議,謝謝!
  18. Qt入門書籍推薦霍亞飛的《Qt Creator快速入門》《Qt5程式設計入門》,Qt進階書籍推薦官方的《C++ GUI Qt4程式設計》。
  19. 強烈推薦程式員自我修養和規劃系列書《大話程式員》《程式員的成長課》《解憂程式員》,受益匪淺,受益終生!
  20. SDK位址: https://gitee.com/feiyangqingyun/QUCSDK

繼續閱讀