1. 程式人生 > >Qt編寫自定義控制元件7-自定義可拖動多邊形

Qt編寫自定義控制元件7-自定義可拖動多邊形

前言

自定義可拖動多邊形控制元件,原創作者是趙彥博(QQ:408815041 [email protected]),創作之初主要是為了能夠在視訊區域內使用者自定義可拖動的多個區域,即可用來作為警戒區域,也可用來其他的處理,拿到對應的多邊形座標集合,本控制元件的主要難點是如何計算一個點在一個多邊形區域內,何時完成一個多邊形區域,支援多個多邊形。

實現的功能

  • 1:自定義隨意繪製多邊形
  • 2:產生閉合形狀後可單擊選中移動整個多邊形
  • 3:可拉動某個點
  • 4:支援多個多邊形
  • 5:滑鼠右鍵退出繪製
  • 6:可設定各種顏色

效果圖

標頭檔案程式碼

#ifndef CUSTOMGRAPHICS_H
#define CUSTOMGRAPHICS_H

/**
 * 自定義多邊形控制元件 作者:趙彥博(QQ:408815041 [email protected]) 2019-3-28
 * 1:自定義隨意繪製多邊形
 * 2:產生閉合形狀後可單擊選中移動整個多邊形
 * 3:可拉動某個點
 * 4:支援多個多邊形
 * 5:滑鼠右鍵退出繪製
 * 6:可設定各種顏色
 */

#include <QWidget>

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

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

{
    Q_OBJECT
    Q_PROPERTY(bool selectDotVisible READ getSelectDotVisible WRITE setSelectDotVisible)
    Q_PROPERTY(int dotRadius READ getDotRadius WRITE setDotRadius)
    Q_PROPERTY(int lineWidth READ getLineWidth WRITE setLineWidth)

    Q_PROPERTY(QColor dotColor READ getDotColor WRITE setDotColor)
    Q_PROPERTY(QColor lineColor READ getLineColor WRITE setLineColor)
    Q_PROPERTY(QColor polygonColor READ getPolygonColor WRITE setPolygonColor)
    Q_PROPERTY(QColor selectColor READ getSelectColor WRITE setSelectColor)

public:
    typedef struct {
        QVector<QPoint> pos;
        bool selected;
    } Polygon;

    explicit CustomGraphics(QWidget *parent = 0);

protected:
    void mousePressEvent(QMouseEvent *e);
    void mouseMoveEvent(QMouseEvent *e);
    void mouseReleaseEvent(QMouseEvent *e);
    void paintEvent(QPaintEvent *);
    void drawPolygon(QPainter *p, const Polygon &v);
    void drawLines(QPainter *p, const QList<QPoint> &list, bool isFirst = true);

private:
    bool selectDotVisible;      //選中點可見
    int dotRadius;              //點的半徑
    int lineWidth;              //線條寬度

    QColor dotColor;            //點的顏色
    QColor lineColor;           //線條顏色
    QColor polygonColor;        //多邊形顏色
    QColor selectColor;         //選中顏色

    QPoint tempPoint;           //臨時點
    QList<QPoint> tempPoints;   //點集合
    QList<Polygon> tempPolygons;//多邊形集合

    bool pressed;               //滑鼠是否按下
    QPoint lastPoint;           //滑鼠按下處的座標
    QPoint ellipsePos;          //儲存按下點的座標
    int selectedEllipseIndex;   //選中點的index
    Polygon pressedPolygon;     //儲存按下時多邊形的原始座標
    int selectedIndex;          //選中多邊形的index

private:
    //計算兩點間的距離
    double length(const QPoint &p1, const QPoint &p2);
    //檢測是否選中多邊形
    bool checkPoint(const QVector<QPoint> &points, int x, int y);

public:
    bool getSelectDotVisible()  const;
    int getDotRadius()          const;
    int getLineWidth()          const;

    QColor getDotColor()        const;
    QColor getLineColor()       const;
    QColor getPolygonColor()    const;
    QColor getSelectColor()     const;

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

public Q_SLOTS:
    void setSelectDotVisible(bool selectDotVisible);
    void setDotRadius(int dotRadius);
    void setLineWidth(int lineWidth);

    void setDotColor(const QColor &dotColor);
    void setLineColor(const QColor &lineColor);
    void setPolygonColor(const QColor &polygonColor);
    void setSelectColor(const QColor &selectColor);

    //清除臨時繪製的
    void clearTemp();
    //清除所有
    void clearAll();
};

#endif // CUSTOMGRAPHICS_H


核心程式碼

void CustomGraphics::mousePressEvent(QMouseEvent *e)
{
    QPoint p = e->pos();
    pressed = true;
    lastPoint = this->mapToGlobal(p);

    //連線模式下不選中
    if(tempPoints.isEmpty()) {
        //如果選中了,檢測是否點到點上
        bool selectedPot = false;
        selectedEllipseIndex = -1;
        if (selectedIndex != -1) {
            for(int i = tempPolygons.at(selectedIndex).pos.size() - 1; i >= 0; --i) {
                if(length(p, tempPolygons.at(selectedIndex).pos[i]) <= 36) {
                    selectedPot = true;
                    selectedEllipseIndex = i;
                    ellipsePos = tempPolygons.at(selectedIndex).pos[i];
                    break;
                }
            }
        }

        //當前選中了點則不用重繪
        if(selectedPot) {
            return;
        }

        //判斷是否選中一個
        selectedIndex = -1;
        for(int i = tempPolygons.size() - 1; i >= 0; --i) {
            tempPolygons[i].selected = checkPoint(tempPolygons.at(i).pos, p.x(), p.y());
            if(tempPolygons.at(i).selected) {
                //防止重疊部分
                if(selectedIndex == -1) {
                    selectedIndex = i;
                    pressedPolygon = tempPolygons.at(i);
                } else {
                    tempPolygons[i].selected = false;
                }
            }
        }

        this->update();
    }
}

void CustomGraphics::mouseMoveEvent(QMouseEvent *e)
{
    tempPoint = e->pos();
    if(pressed && selectedIndex != -1) {
        //整體偏移座標
        QPoint delta = this->mapToGlobal(tempPoint) - lastPoint;
        int len = tempPolygons.at(selectedIndex).pos.size();

        if(selectedEllipseIndex != -1) { //移動點
            tempPolygons[selectedIndex].pos[selectedEllipseIndex] = ellipsePos + delta;
        } else if(selectedIndex != -1) { //移動面
            for(int i = 0; i < len; ++i) {
                tempPolygons[selectedIndex].pos[i] = pressedPolygon.pos.at(i) + delta;
            }
        }
    }

    this->update();
}

void CustomGraphics::mouseReleaseEvent(QMouseEvent *e)
{
    //滑鼠右鍵清空臨時的
    if (e->button() == Qt::RightButton) {
        clearTemp();
        return;
    }

    //檢測再次點選與最後個點 - 還沒寫
    pressed = false;
    if(selectedIndex != -1) {
        return;
    }

    QPoint point = e->pos();
    if(tempPoints.count() > 0) {
        qreal len = (qPow(tempPoints.first().x() - point.x() , 2.0) + qPow(tempPoints.first().y() - point.y() , 2.0) );
        if(len < 100) {
            //完成一個多邊形
            if(tempPoints.size() >= 3) {
                Polygon pol;
                pol.pos = tempPoints.toVector();
                pol.selected = false;
                tempPolygons.append(pol);
            }

            tempPoints.clear();
            this->update();
            return;
        }
    }

    tempPoints.append(point);
    this->update();
}

void CustomGraphics::paintEvent(QPaintEvent *)
{
    QPainter painter(this);
    painter.setRenderHints(QPainter::Antialiasing, true);

    //繪製多邊形
    foreach(const Polygon &p, tempPolygons) {
        drawPolygon(&painter, p);
    }

    //繪製點集合
    drawLines(&painter, tempPoints, false);
}

void CustomGraphics::drawPolygon(QPainter *p, const Polygon &v)
{
    p->save();

    //繪製多邊形
    p->setPen(QPen(lineColor, lineWidth));
    v.selected ? p->setBrush(selectColor) : p->setBrush(polygonColor);
    p->drawPolygon(v.pos.data(), v.pos.size());

    //繪製圓點
    if(selectDotVisible && v.selected) {
        p->setPen(Qt::NoPen);
        p->setBrush(dotColor);
        foreach(const QPoint &point, v.pos) {
            p->drawEllipse(point, dotRadius, dotRadius);
        }
    }

    p->restore();
}

void CustomGraphics::drawLines(QPainter *p, const QList<QPoint> &list, bool isFirst)
{
    p->save();

    int count = list.count();
    if (count > 0) {
        //繪製點集合
        p->setPen(Qt::NoPen);
        p->setBrush(dotColor);
        for(int i = 0; i < count; ++i) {
            p->drawEllipse(list.at(i), dotRadius, dotRadius);
        }

        //繪製線條集合
        p->setPen(QPen(lineColor, lineWidth));
        p->setBrush(Qt::NoBrush);
        for(int i = 0; i < count - 1; ++i) {
            p->drawLine(list.at(i), list.at(i + 1));
        }

        //繪製最後一條線條
        p->drawLine(list.last(), isFirst ? list.first() : tempPoint);
    }

    p->restore();
}

double CustomGraphics::length(const QPoint &p1, const QPoint &p2)
{
    //平方和
    return qPow(p1.x() - p2.x(), 2.0) + qPow(p1.y() - p2.y(), 2.0);
}

bool CustomGraphics::checkPoint(const QVector<QPoint> &points, int testx, int testy)
{
    //最少保證3個點
    const int count = points.size();
    if(count < 3) {
        return false;
    }

    QList<int> vertx, verty;
    for(int i = 0; i < count; ++i) {
        vertx << points.at(i).x();
        verty << points.at(i).y();
    }

    //核心演算法,計算座標是否在多邊形內部
    int i = 0, j, c = 0;
    for (i = 0, j = count - 1; i < count; j = i++) {
        bool b1 = (verty.at(i) > testy) != (verty.at(j) > testy);
        bool b2 = (testx < (vertx.at(j) - vertx.at(i)) * (testy - verty.at(i)) / (verty.at(j) - verty.at(i)) + vertx.at(i));
        if (b1 && b2) {
            c = !c;
        }
    }

    return c;
}

控制元件介紹

  1. 超過140個精美控制元件,涵蓋了各種儀表盤、進度條、進度球、指南針、曲線圖、標尺、溫度計、導航條、導航欄,flatui、高亮按鈕、滑動選擇器、農曆等。遠超qwt整合的控制元件數量。
  2. 每個類都可以獨立成一個單獨的控制元件,零耦合,每個控制元件一個頭檔案和一個實現檔案,不依賴其他檔案,方便單個控制元件以原始碼形式整合到專案中,較少程式碼量。qwt的控制元件類環環相扣,高度耦合,想要使用其中一個控制元件,必須包含所有的程式碼。
  3. 全部純Qt編寫,QWidget+QPainter繪製,支援Qt4.6到Qt5.12的任何Qt版本,支援mingw、msvc、gcc等編譯器,不亂碼,可直接整合到Qt Creator中,和自帶的控制元件一樣使用,大部分效果只要設定幾個屬性即可,極為方便。
  4. 每個控制元件都有一個對應的單獨的包含該控制元件原始碼的DEMO,方便參考使用。同時還提供一個所有控制元件使用的整合的DEMO。
  5. 每個控制元件的原始碼都有詳細中文註釋,都按照統一設計規範編寫,方便學習自定義控制元件的編寫。
  6. 每個控制元件預設配色和demo對應的配色都非常精美。
  7. 超過120個可見控制元件,6個不可見控制元件。
  8. 部分控制元件提供多種樣式風格選擇,多種指示器樣式選擇。
  9. 所有控制元件自適應窗體拉伸變化。
  10. 整合自定義控制元件屬性設計器,支援拖曳設計,所見即所得,支援匯入匯出xml格式。
  11. 自帶activex控制元件demo,所有控制元件可以直接執行在ie瀏覽器中。
  12. 整合fontawesome圖形字型+阿里巴巴iconfont收藏的幾百個圖形字型,享受圖形字型帶來的樂趣。
  13. 所有控制元件最後生成一個dll動態庫檔案,可以直接整合到qtcreator中拖曳設計使用。

SDK下載

  • SDK下載連結:https://pan.baidu.com/s/1tD9v1YPfE2fgYoK6lqUr1Q 提取碼:lyhk
  • 自定義控制元件+屬性設計器欣賞:https://pan.baidu.com/s/1l6L3rKSiLu_uYi7lnL3ibQ 提取碼:tmvl
  • 下載連結中包含了各個版本的動態庫檔案,所有控制元件的標頭檔案,使用demo。
  • 自定義控制元件外掛開放動態庫dll使用(永久免費),無任何後門和限制,請放心使用。
  • 不定期增加控制元件和完善控制元件,不定期更新SDK,歡迎各位提出建議,謝謝!
  • widget版本(QQ:517216493)qml版本(QQ:373955953)三峰駝(QQ:278969898)。

相關推薦

Qt編寫定義控制元件7-定義多邊形

前言 自定義可拖動多邊形控制元件,原創作者是趙彥博(QQ:408815041 [email protected]),創作之初主要

Qt編寫定義控制元件20-定義餅圖

一、前言 上次在寫視覺化資料大屏電子看板專案的時候,為了逐步移除對QChart的依賴(主要是因為QChart真的太垃圾了,是所有Q

[Android定義控制元件] Android定義控制元件

轉載自: http://blog.163.com/[email protected]/blog/static/103242241201382210910473/ 開發自定義控制元件的步驟: 1、瞭解View的工作原理  2、

【安卓定義控制元件定義ViewGroup實現透明背景的ViewPager效果

HelloWorld! 作為一名屌絲程式設計師,在部落格園寫第一篇技術部落格內心是無比激動滴,其實作為一名忙成狗的Android開發人員,一直覺得自己永遠都不會有時間去寫部落格, 因為我TM連找女朋友的時間都沒用== 言歸正傳,今天自定義控制元件系列要實現的效果是自定義Vi

C# 定義控制元件定義屬性,定義事件

using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Data; using System.Li

C#定義控制元件之-定義MessageBox

每次談到系統自帶的控制元件,博主就是各種不滿意(應該不止我一個吧),這次也不例外,今天的主角是messagebox控制元件。任何開發者都無法直視當自己做的美觀的軟體彈出提示時居然是奇醜無比的系統自帶提

Android定義控制元件定義TextView,實現drawableLeft可以和文字一起居中

LZ-Says:給大家推薦一個網站,有興趣可以查閱,想為大家貢獻一點自己的力量也可以投稿,老大稽核通過會發表,更好的幫助有需要的人~歡迎大家踴躍投稿~地址如下: http://ww

android定義控制元件(五) 定義組合控制元件

< ?xml version="1.0" encoding="utf-8"?>    < LinearLayout xmlns:Android="http://schemas.android.com/apk/res/android"    android:layout_widt

android定義控制元件_完全定義控制元件定義開關)

前面總結到自定義控制元件分為 組合控制元件 繼承已有控制元件 比如自定義SmartImageView繼承ImageView 完全自定義控制元件 上一篇寫了自定義控制元件的自定義屬性深入理解點選連結檢視,是自定控制元件比較難以理解的地方,但是是很重

[WPF定義控制元件庫] 定義控制元件的程式碼如何與ControlTemplate互動

1. 前言 WPF有一個靈活的UI框架,使用者可以輕鬆地使用程式碼控制控制元件的外觀。例設我需要一個控制元件在滑鼠進入的時候背景變成藍色,我可以用下面這段程式碼實現: protected override void OnMouseEnter(MouseEventArgs e) { base.OnMou

工作記錄--WPF定義控制元件,實現一個設定編輯模式的TextBox

1. 背景   因為最近在使用wpf開發桌面端應用,在檢視頁面需要把TextBox和Combox等控制元件設定為只讀的。原本是個很簡單的事,設定屬性IsReadOnly="True"或IsEnabled="False"就可以解決了,可是產品覺得樣式不是他想要的(背景是灰色的),想要實現的效果是和編輯時的樣式一

[WPF定義控制元件庫]定義Expander

1. 前言 上一篇文章介紹了使用Resizer實現Expander簡單的動畫效果,執行效果也還好,不過只有展開/摺疊而缺少了淡入/淡出的動畫(畢竟Resizer模仿Expander只是附帶的功能)。這篇繼續Measure的話題,自定義了一個帶有動畫的ExtendedExpander。 2. ExtendedE

QT中給各控制元件增加背景圖片(縮放旋轉)的幾種方法

1. 給QPushButton 增加背景圖片:背景圖片可根據Button大小自由縮放。 void setButtonBackImage(QPushButton *button,QString image,int sizeW, int sizeH) { //163

Qt編寫定義控制元件屬性設計器

以前做.NET開發中,.NET直接就集成了屬性設計器,VS不愧是宇宙第一IDE,你能夠想到的都給你封裝好了,用起來不要太爽!因為專案需要自從全面轉Qt開發已經6年有餘,在工業控制領域,有一些應用場景需要自定義繪製一些控制元件滿足特定的需求,比如儀器儀表、組態等,而且需要直接使用者通過屬性設計的形式生成匯出控制

Qt編寫定義控制元件一開關按鈕

從2010年進入網際網路+智慧手機時代以來,各種各樣的APP大行其道,手機上面的APP有很多流行的元素,開關按鈕個人非常喜歡,手機QQ、360衛士、金山毒霸等,都有很多開關控制一些操作,在Qt widgets應用專案上,在專案中應用些類似的開關按鈕,估計也會為專案增添不少新鮮

編寫Qt Designer定義控制元件(二)——編寫定義控制元件介面

        既然是控制元件,就應該有介面,預設生成的控制元件類只是一個繼承了QWidget的類,如下: #ifndef LOGLATEDIT_H #define LOGLATEDIT_H #include <QWidget> class LogLat

編寫Qt Designer定義控制元件(一)——如何建立並使用Qt定義控制元件

    http://blog.csdn.net/giselite/article/details/12622429   在使用Qt Designer設計窗體介面時,我們可以使用Widget Box裡的窗體控制元件非常方便的繪製介面,比如拖進去一個按鈕,一個文字編輯器等。雖然Qt Designer裡的控制元

Qt編寫定義控制元件外掛開放動態庫dll使用(永久免費)

這套控制元件陸陸續續完善了四年多,目前共133個控制元件,除了十幾個控制元件參考網友開源的程式碼寫的,其餘全部原創,在釋出之初就有

Qt編寫定義控制元件1-汽車儀表盤

前言 汽車儀表盤幾乎是qt寫儀表盤控制元件中最常見的,一般來說先要求美工做好設計圖,然後設計效果圖給到程式設計師,由程式設計師根據

Qt編寫定義控制元件2-進度條標尺

前言 進度條標尺控制元件的應用場景一般是需要手動拉動進度,上面有標尺可以看到當前進度,類似於qslider控制元件,其實就是qsl