QT之“蛛網圖”自繪控件

   在很多軟件中我們會看到用雷達圖的“蛛網圖”來表示各類屬性評分值,雖然我可以通過調用ECharts來實現這種效果,但是沒有發現用QT來實現的控件,所以自己在閒下來的時候寫了一個簡單的圖表,實現效果圖如下:

核心代碼如下:

1.繪製正多邊形

void RadarChart::drawPolygon(QPainter *painter)
{
    painter->save();
    QPen pen;
    pen.setWidthF(lineWidth);
    pen.setColor(polygonLineColor); //添加線的顏色
    painter->setPen(pen);

    double startRad = (360 - startAngle) * (3.14 / 180);
    double deltaRad = 360 * (3.14 / 180) / sides;
    double radiusPer = 80.0 / level ;

    double sina, cosa;
    int x, y;
    for(int j = 0 ; j < level ; j++ )
    {
        float newR = radiusPer * (j + 1);
        QPolygonF polygon;
        for (int i = 0; i < sides; i++)
        {
            sina = sin(startRad - i * deltaRad);
            cosa = cos(startRad - i * deltaRad);

            x = newR * cosa;
            y = -newR * sina;
            QPointF point(x, y);
            polygon.append(point);
        }
        painter->drawPolygon(polygon);
    }
    painter->restore();
}

2.繪製刻度線

void RadarChart::drawLine(QPainter *painter)
{
    painter->save();
    QPen pen;
    pen.setWidthF(lineWidth);
    pen.setColor(lineColor); //添加線的顏色
    painter->setPen(pen);

    double radius = 80.0 ;

    double startRad = (360 - startAngle) * (3.14 / 180);
    double deltaRad = 360 * (3.14 / 180) / sides;

    double sina, cosa;
    int x, y;

    for (int i = 0; i < sides; i++)
    {
        sina = sin(startRad - i * deltaRad);
        cosa = cos(startRad - i * deltaRad);

        x = radius * cosa;
        y = -radius * sina;
        QPointF point(x, y);
        painter->drawLine(QPointF(0, 0), point);
    }

    painter->restore();
}

3.繪製屬性類型

void RadarChart::drawCategories(QPainter *painter)
{
    painter->save();
    QPen pen;
    pen.setColor(categoriesColor); //添加線的顏色
    painter->setPen(pen);

    QFont font("Arial", 5, QFont::Bold, false);
    //設置字體的類型,大小,加粗,斜體
    painter->setFont(font);
    QFontMetrics fm = painter->fontMetrics();

    double radius = 80.0 ;

    double startRad = (360 - startAngle) * (3.14 / 180);
    double deltaRad = 360 * (3.14 / 180) / sides;

    double sina, cosa;
    int x, y;

    for (int i = 0; i < sides; i++)
    {
        sina = sin(startRad - i * deltaRad);
        cosa = cos(startRad - i * deltaRad);

        x = radius * cosa;
        y = -radius * sina;
        QPointF point(x, y);

        QRectF titleRect;
        //分8個方向
        if(x == 0 && y > 0)
            //正下
            titleRect = QRectF(point.x() - fm.width(categories.at(i)) / 2, point.y(), fm.width(categories.at(i)), fm.height());
        else if(x == 0 && y < 0)
            //正上
            titleRect = QRectF(point.x() - fm.width(categories.at(i)) / 2, point.y() - fm.height(), fm.width(categories.at(i)), fm.height());
        else if(x > 0 && y == 0)
            //正右
            titleRect = QRectF(point.x(), point.y() - fm.height() / 2, fm.width(categories.at(i)), fm.height());
        else if(x < 0 && y == 0)
            //正左
            titleRect = QRectF(point.x() - fm.width(categories.at(i)), point.y() - fm.height() / 2, fm.width(categories.at(i)), fm.height());
        else if(x > 0 && y > 0)
            //右下
            titleRect = QRectF(point.x(), point.y(), fm.width(categories.at(i)), fm.height());
        else if(x > 0 && y < 0)
            //右上
            titleRect = QRectF(point.x(), point.y() - fm.height(), fm.width(categories.at(i)), fm.height());
        else if(x < 0 && y < 0)
            //左上
            titleRect = QRectF(point.x() - fm.width(categories.at(i)), point.y() - fm.height(), fm.width(categories.at(i)), fm.height());
        else if(x < 0 && y > 0)
            //左下
            titleRect = QRectF(point.x() - fm.width(categories.at(i)), point.y(), fm.width(categories.at(i)), fm.height());
        else
            titleRect = QRectF(point.x(), point.y(), fm.width(categories.at(i)), fm.height());

        painter->drawText(titleRect, Qt::AlignCenter | Qt::AlignTop, categories.at(i));
    }

    painter->restore();
}

4.繪製標尺值

void RadarChart::drawScaleNum(QPainter *painter)
{
    painter->save();
    QPen pen;
    pen.setColor(categoriesColor); //添加線的顏色
    painter->setPen(pen);

    QFont font("Arial", 3, QFont::Bold, false);
    //設置字體的類型,大小,加粗,斜體
    painter->setFont(font);
    QFontMetrics fm = painter->fontMetrics();

    double startRad = (360 - startAngle) * (3.14 / 180);
    double deltaRad = 360 * (3.14 / 180) / sides;
    double radiusPer = 80.0 / level ;

    double sina, cosa;
    int x, y;
    float scaleStep = (maxValue - minValue) / level * 1.0;

    for(int j = 0 ; j < level + 1 ; j++ )
    {
        QPointF point;
        if(j <= 0)
        {
            point = QPointF(0.0, 0.0);
        }
        else
        {
            float newR = radiusPer * j;
            sina = sin(startRad - scalePos * deltaRad);
            cosa = cos(startRad - scalePos * deltaRad);

            x = newR * cosa;
            y = -newR * sina;
            point = QPointF(x, y);
        }

        QString scaleNum = QString::number(scaleStep * j + minValue, 'f', 0);

        QRectF titleRect = QRectF(point.x() - fm.width(scaleNum) - 2, point.y(), fm.width(scaleNum), fm.height());

        painter->drawText(titleRect, Qt::AlignCenter | Qt::AlignTop, scaleNum);
    }

    painter->restore();
}

5.繪製各曲線圖

void RadarChart::drawValues(QPainter *painter)
{
    painter->save();

    double startRad = (360 - startAngle) * (3.14 / 180);
    double deltaRad = 360 * (3.14 / 180) / sides;
    double radius = 80.0;

    double sina, cosa;
    int x, y;

    QMap<QString, QVector<float>>::const_iterator it = dataMap.constBegin();
    while (it != dataMap.constEnd())
    {

        QVector<float> data = it.value();
        QPolygonF polygon;
        QColor dataColor = dataColorMap[it.key()];
        QPen dataPen;

        for(int j = 0 ; j < sides; j++)
        {
            sina = sin(startRad - j * deltaRad);
            cosa = cos(startRad - j * deltaRad);
            double r;
            //QPen pointPen;

            float value;
            if(j >= data.size())
            {
                value = minValue;
                r = qAbs( minValue / (maxValue - minValue)) * radius;
            }
            else
            {
                value = data.at(j) ;
                if(value < minValue)
                    value = minValue;
                else if(value > maxValue)
                    value = maxValue;
            }
            r = qAbs((value - minValue) / (maxValue - minValue))  * radius;

            x = r * cosa;
            y = -r * sina;
            QPointF point(x, y);
            polygon.append(point);

            dataPen.setWidthF(1.5);
            dataColor.setAlpha(150);
            dataPen.setColor(dataColor); //添加線的顏色

            painter->setPen(dataPen);
            painter->drawPoint(point);
        }

        QPainterPath painterPath;
        painterPath.addPolygon(polygon);
        painterPath.closeSubpath();

        dataPen.setWidthF(lineWidth);
        //線條色
        dataColor.setAlpha(255);
        dataPen.setColor(dataColor);
        painter->setPen(dataPen);

        painter->drawPath(painterPath);

        //填充色
        dataColor.setAlpha(100);
        painter->fillPath( painterPath, dataColor);

        ++it;
    }

    painter->restore();
}

6.繪製圖例

void RadarChart::drawLegends(QPainter *painter)
{
    painter->save();

    QPen pen;
    pen.setColor(categoriesColor); //添加線的顏色
    painter->setPen(pen);

    QFont font("Arial", 3, QFont::Bold, false);
    //設置字體的類型,大小,加粗,斜體
    painter->setFont(font);
    QFontMetrics fm = painter->fontMetrics();

    float sx = 100 - 1;
    float sy = -100 + 1;

    int i = 0;

    int maxW = 0;
    QMap<QString, QVector<float>>::const_iterator it = dataMap.constBegin();
    while (it != dataMap.constEnd())
    {
        int w = fm.width(it.key());
        if(w > maxW)
            maxW = w;
        ++it;
    }


    QMap<QString, QVector<float>>::const_iterator it2 = dataMap.constBegin();
    while (it2 != dataMap.constEnd())
    {

        QRectF legendNameRect = QRectF(sx - maxW, sy + (fm.height() + 1) * i, fm.width(it2.key()), fm.height());
        QRectF legendColorRect = QRectF(sx - maxW - 1.5 - fm.height(), sy + (fm.height() + 1) * i, fm.height(), fm.height());

        painter->drawText(legendNameRect, Qt::AlignCenter | Qt::AlignTop, it2.key());
        //painter->drawRect(legendColorRect);
        painter->fillRect(legendColorRect, dataColorMap[it2.key()]);
        i++;

        ++it2;
    }


    painter->restore();
}

7,調用方法

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    QVector<QString> testData0;
    testData0 << "0" << "1" << "2" << "3" << "4" << "5" << "6";
    ui->widget->setCategories(testData0);

    QVector<float> testData;
    testData << 10 << 20 << 50 << 20 << 60 << 10 << 55;
    ui->widget->addData("test11111", testData);
    ui->widget->setDataColor("test11111", Qt::green);

    QVector<float> testData2;
    testData2 << 30 << 70 << 20 << 10 << 50 << 20 << 65;
    ui->widget->addData("test2", testData2);
    ui->widget->setDataColor("test2", Qt::red);
}

寫得比較粗糙,沒有實現鼠標懸浮等動作,有興趣的可以自己擴展一下。代碼以經上傳,請多指教。。。

https://download.csdn.net/download/octdream/11835243

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章