欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

QT之“蛛网图”自绘控件

程序员文章站 2022-03-11 10:17:10
...

   在很多软件中我们会看到用雷达图的“蛛网图”来表示各类属性评分值,虽然我可以通过调用ECharts来实现这种效果,但是没有发现用QT来实现的控件,所以自己在闲下来的时候写了一个简单的图表,实现效果图如下:

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);
}

写得比较粗糙,没有实现鼠标悬浮等动作,有兴趣的可以自己扩展一下。代码以经上传,请多指教。。。