参考
参考
mychart.h
#ifndef MYCHART_H
#define MYCHART_H#include <QWidget>
#include <QPainter>
#include <QString>struct DataNode
{int value;QString key;
};typedef struct {QString label;double width;QColor color;
} CvsInfo;// MyChart继承自QWidget类,是一个窗口小部件。
class MyChart : public QWidget
{Q_OBJECT
public:// `explicit` 是 C++ 中的一个关键字,用于修饰类的构造函数,表示该构造函数只能用于显式地创建对象,不能被隐式地调用。// 只能通过MyChart painter = MyChart(parent)的方式显式地创建一个 `MyChart` 对象:// `parent` 参数的默认值为 `nullptr`,这表示如果没有提供父部件的指针,那么 `MyChart` 就没有父部件,即它是一个独立的窗口部件。explicit MyChart(QWidget *parent = nullptr);~MyChart();void updateValue(const DataNode &node);void AxisCurvesUpdate(const QList<QPoint> & pointList);// 坐标轴参数初始化设置typedef struct {QString titleX;quint32 durationX;QString titleYl;quint32 durationYl;QString titleYr;quint32 dudrationYr;} AxisInfo;// 坐标参数初始化设置typedef struct {QPointF spMin;QPointF spMax;QVector<QPointF> sp;QPointF epMin;QPointF epMax;QVector<QPointF> ep;} MyGtChartAxisYScalesInfo;// 坐标轴初始化设置void AxisInit(const AxisInfo & info);// 曲线参数初始化设置void AxisCurvesAppend(const QString & label, double width, QColor color);// 曲线标题显示与设置void AxisCurvesTitleSet();// 坐标轴刻度线设置void AxisScalesLineSet();// 坐标轴刻度值刷新void AxisScalesValueSet();// 坐标轴标题设置void MyChartTitileSet(QPainter & painter, const QPoint &point, const QString &title, const QColor & color, const qreal & fontSize);// 坐标轴标题设置void AxisTitleSet();// 坐标轴初始化设置void AxisInitSet();// 创建坐标轴void AxisCreateTest();// 曲线坐标点刷新void AxisPointsUpdate(QVector<QList<QPoint>> &pointList);// 平滑曲线绘制void SmoothCurvesDrawTest(const QColor &color, qreal &width, const QList<QPoint> &pointList);// 左侧y轴弧线绘制,刻度点记录void AxisYlArcDrawWidthScales(QPainter &painter, int offsetX, int diameters, int tickCount, int margin);// 右侧y轴弧线绘制,刻度点记录void AxisYrArcDrawWidthScales(QPainter &painter, int offsetX, int diameters, int tickCount, int margin);// 曲线图刷新void ChartViewUpdate();void drawArcAtWidget(QPainter * painter, const QPoint& center, int radius);// 右侧码表 //绘制实时时速数字void DrawDbNum(QPainter& painter, QPoint & point);// 绘制一条中间略粗两边略尖的渐变发光线条void DrawDbLightLine(QPainter& painter, QPoint & point);// 中间小圆void DrawCircle_bom_small(QPainter& painter, QPoint & center, int radius);//渐变发光内圈void DrawCircle_bom_shine(QPainter &painter, QPoint ¢er, int radius);// 最外细圆线void DrawCircle_line(QPainter& painter, QPoint ¢er, int radius, int width);// 绘制指针void DrawPointer(QPainter& painter, QPoint ¢er, int radius, int degRotate);// 动态扇形环void DrawCircle_arc(QPainter& painter, QPoint ¢er, int radius, int degRotate);// 渐变发光外扇形void DrawCircle(QPainter& painter, QPoint ¢er, int radius);// 刻度数字void DrawDigital(QPainter& painter, QPoint ¢er, int radius);// 绘制刻度void DrawSmallScale(QPainter& painter, QPoint ¢er, int radius);//绘制码表盘void DrawDb(QPainter& painter, QPoint & center);void DashBoardUpdate(const QPoint & point);protected:void paintEvent(QPaintEvent *event);private:QPainter *m_painter;//坐标轴其实结束点QPoint m_pointZero;QPoint m_pointEndX;QPoint m_pointEndY;QPoint m_pointEndA;// 坐标轴起始结束位置int m_axisStartX;int m_axisStartY;int m_axisEndX;int m_axisEndY;int m_lenY; // 高度int m_lenX; // 长度double m_periodX; // x轴每个刻度之间间隔的长度double m_periodY; // y轴每个刻度之间间隔的长度quint32 m_marginX = 300; // y轴距离左右边界宽度quint32 m_marginY = 25; // x轴距离上下边界宽度int m_durationX = 100; // x刻度间隔数int m_durationY = 10; // y刻度间隔数QVector<double> m_valYMax;int m_nodeNumMax;QList<CvsInfo> m_cvsInfo;AxisInfo m_axisInfo;bool m_isAxisReady = false;QList<DataNode> m_listDataNode;MyGtChartAxisYScalesInfo m_scalesY;quint32 m_marginY2Dash = 0; // 左右y轴到马表盘的间隔// 马表盘int m_maxValueDashBoard = 50;int m_minValueDashBoard = 0;int m_curValueDashBoard = 20;// painter移动位置QPoint m_pleftG;QPoint m_prightT;QPoint m_pAxis;
};#endif // MYCHART_H
mychart.cpp
#include "mychart.h"
#include <QDebug>
#include <QtMath>MyChart::MyChart(QWidget *parent) : QWidget(parent)
{m_painter = new QPainter(this);
}MyChart::~MyChart()
{delete m_painter;
}// 数据刷新
void MyChart::updateValue(const DataNode &node)
{if(m_listDataNode.size() >= m_nodeNumMax) {m_listDataNode.removeFirst();}m_listDataNode.append(node);update();
}void MyChart::AxisCurvesUpdate(const QList<QPoint> & pointList)
{QPen pen;// 折线线条宽度pen.setWidth(3);// 折现线条颜色pen.setColor(Qt::green);m_painter->setPen(pen);// 遍历并连接个数据节点,绘制折线for(int i = 0; i < pointList.size(); i++) {if((i+1) < pointList.size()) {// 连接个数据点,绘制折线m_painter->drawLine(pointList.at(i), pointList.at(i+1));}}
}// 坐标轴初始化设置
void MyChart::AxisInit(const MyChart::AxisInfo &info)
{m_axisInfo = info;m_durationX = info.durationX;m_nodeNumMax = m_durationX + 1;m_durationY = info.durationYl;// 初始化设置m_valYMax.append(10);m_valYMax.append(10);m_isAxisReady = true;
}// 曲线参数初始化设置
void MyChart::AxisCurvesAppend(const QString &label, double width, QColor color)
{CvsInfo info;info.label = label;info.width = width;info.color = color;m_cvsInfo.append(info);
}// 曲线标题显示与设置
void MyChart::AxisCurvesTitleSet()
{QVector<double> strlen; // 每个标题长度double lenSum = 0; // 所有标题总长度double linelen = 40; // 标题前面线条长度double spacelen = 30.0; // 标题之间间隔长度double fontSize = 10.0; // 字体大小// 计算标题占用的总长度for (int i = 0; i < m_cvsInfo.size(); ++i) {m_painter->setFont(font());QFontMetrics fm(m_painter->font());double len = (double)fm.width(m_cvsInfo[i].label + spacelen + linelen);strlen.append(len);lenSum += len;}// 起始偏移长度double startLen = (m_lenX - lenSum) / 2;for (int i = 0; i < m_cvsInfo.size(); ++i) {QPen pen(m_cvsInfo[i].color);pen.setWidthF(m_cvsInfo[i].width);m_painter->setPen(pen);m_painter->drawLine(QPoint(m_scalesY.sp.last().x() + startLen, m_scalesY.sp.last().y() / 2),QPoint(m_scalesY.sp.last().x() + startLen + linelen, m_scalesY.sp.last().y() / 2));m_painter->setPen(Qt::NoPen);startLen += linelen; // 偏移已经占用的长度QFont font("Century", fontSize, QFont::Bold);font.setLetterSpacing(QFont::AbsoluteSpacing, 0.1);m_painter->setFont(font);pen.setColor(Qt::black);m_painter->setPen(pen);m_painter->drawText(QPoint(m_scalesY.sp.last().x() + startLen, m_scalesY.sp.last().y() - fontSize / 2), m_cvsInfo[i].label);startLen += strlen[i]; // 偏移已经占用的长度}
}// 坐标轴刻度线设置
void MyChart::AxisScalesLineSet()
{QPen pen;// 轴线刻度绘制m_periodX = (double) (m_lenX * 1.0) / (m_durationX); // x刻度间隔长度m_periodY = m_lenY / (m_durationY - 1); // y刻度间隔长度// x刻度线绘制pen.setWidthF(0.5);pen.setColor(Qt::blue);m_painter->setPen(pen);for (int i = 0; i < m_durationX; ++i) {m_painter->drawLine(QPointF((double)(m_scalesY.spMax.x() + i * m_periodX) + 80, m_scalesY.sp.first().y() + 5),QPointF((double)(m_scalesY.spMax.x() + i * m_periodX) + 80, m_scalesY.sp.first().y() - 5));for (int j = 0; j < m_scalesY.sp.size(); ++j) {}}
}// 坐标轴刻度值刷新
void MyChart::AxisScalesValueSet()
{QPen pen;pen.setWidthF(0.2);pen.setColor(Qt::black);m_painter->setPen(pen);QFont font;font.setPointSizeF(10);m_painter->setFont(font);// 刷新左侧y轴刻度值for (int i = 0; i < m_durationY; ++i) {double lenY = m_valYMax[0];QString value = QString::number(i * (lenY / m_durationY), 'f', 2);QFontMetrics f(m_painter->font());int w = f.width(value);m_painter->setPen(QPen(Qt::red, 1.1));m_painter->drawLine(m_scalesY.sp[i], QPoint(m_scalesY.sp[i].x() + w + 5, m_scalesY.sp[i].y()));m_painter->setPen(Qt::NoPen);m_painter->setPen(pen);if (m_scalesY.sp[i].y() > m_scalesY.spMax.y()) {if (i == 0) {m_painter->drawText(QPoint(m_scalesY.sp[i].x() + 6, m_scalesY.sp[i].y()), value);} else if (i == 1) {m_painter->drawText(QPoint(m_scalesY.sp[i].x() + 4, m_scalesY.sp[i].y()), value);} else if (i == 2) {m_painter->drawText(QPoint(m_scalesY.sp[i].x() + 3, m_scalesY.sp[i].y()), value);} else {m_painter->drawText(QPoint(m_scalesY.sp[i].x() + m_scalesY.sp.size() - 2 * i, m_scalesY.sp[i].y()), value);}} else {m_painter->drawText(QPoint(m_scalesY.sp[i].x() + 1, m_scalesY.sp[i].y()), value);}}// 刷新右侧y轴刻度值for (int i = 0; i < m_durationY; ++i) {double lenY = m_valYMax[1];QString value = QString::number(i * (lenY / m_durationY), 'f', 2);QFontMetrics f(m_painter->font());int w = f.width(value);m_painter->setPen(QPen(Qt::red, 1.1));m_painter->drawLine(m_scalesY.ep[i], QPoint(m_scalesY.ep[i].x() - w - 5, m_scalesY.ep[i].y()));m_painter->setPen(Qt::NoPen);m_painter->setPen(pen);if (m_scalesY.ep[i].y() > m_scalesY.epMax.y()) {if (i == 0) {m_painter->drawText(QPoint(m_scalesY.ep[i].x() - w - 6, m_scalesY.ep[i].y()), value);} else if (i == 1) {m_painter->drawText(QPoint(m_scalesY.ep[i].x() - w - 4, m_scalesY.ep[i].y()), value);} else if (i == 2) {m_painter->drawText(QPoint(m_scalesY.ep[i].x() - w - 3, m_scalesY.ep[i].y()), value);} else {m_painter->drawText(QPoint(m_scalesY.ep[i].x() - w - 2, m_scalesY.ep[i].y()), value);}} else {m_painter->drawText(QPoint(m_scalesY.ep[i].x() - w - 1, m_scalesY.ep[i].y()), value);}}// 刷新x轴刻度值for (int i = 0; i < m_durationX; ++i) {if (i >= m_listDataNode.size()) {break;}QTransform transform;transform.translate(m_pointZero.x() + i * m_periodX + 2, m_pointZero.y() - 5);transform.rotate(-45);m_painter->setTransform(transform);m_painter->drawText(0, 3, m_listDataNode[i].key);m_painter->resetTransform();}
}// 坐标轴标题设置
void MyChart::MyChartTitileSet(QPainter & painter, const QPoint &point, const QString &title, const QColor & color, const qreal & fontSize)
{painter.save();painter.setPen(QPen(color, fontSize));QFont font("Century", fontSize, QFont::Bold);font.setPointSizeF(fontSize);painter.setFont(font);painter.drawText(point, title);painter.restore();
}// 坐标轴标题设置
void MyChart::AxisTitleSet()
{// x轴标题设置MyChartTitileSet(*m_painter,QPoint((m_scalesY.ep.first().x() - m_scalesY.sp.first().x()) / 2 + m_scalesY.sp.first().x(), m_scalesY.sp.first().y() + 15),m_axisInfo.titleX,Qt::red,12.0);// 左侧y轴MyChartTitileSet(*m_painter,QPoint(m_scalesY.sp.last().x() + 50, m_scalesY.sp.last().y()),m_axisInfo.titleYl,Qt::black,10.0);// 右侧y轴MyChartTitileSet(*m_painter,QPoint(m_scalesY.ep.last().x() - 180, m_scalesY.ep.last().y()),m_axisInfo.titleYr,Qt::black,10.0);
}// 坐标轴初始化设置
void MyChart::AxisInitSet()
{QPen pen;pen.setWidthF(0.5);pen.setColor(Qt::gray);m_painter->setPen(pen);double maxValue = m_valYMax[0];QString strMax = QString::number(m_durationY * (maxValue / m_durationY), 'f', 2);QFontMetrics f(m_painter->font());int xMargin = f.width(strMax);m_axisStartX = xMargin - 3 + qMin(width(), height());maxValue = m_valYMax[1];strMax = QString::number(m_durationY * (maxValue / m_durationY), 'f', 2);xMargin = f.width(strMax);m_axisEndX = this->width() - xMargin - qMin(width(), height());m_axisStartY = this->height() - m_marginY;m_axisEndY = m_marginY;m_lenX = m_axisEndX - m_axisStartX;m_lenY = m_axisStartY - m_axisEndY;// 坐标点设置m_pointZero = QPoint(m_axisStartX, m_axisStartY);m_pointEndX = QPoint(m_axisEndX, m_axisStartY);m_pointEndY = QPoint(m_axisStartX, m_axisEndY);m_pointEndA = QPoint(m_axisEndX, m_axisEndY);// 左侧y轴弧线绘制int offsetXl = qMin(width(), height()) + m_marginY2Dash;int diameters = qMin(width(), height()) + qMin(width(), height()) / 2;AxisYlArcDrawWidthScales(*m_painter, offsetXl, diameters, 10, m_marginY);// 右侧y轴弧线绘制,刻度点记录int diametersR = qMin(width(), height()) + qMin(width(), height()) / 2;int offsetXr = width() - qMin(width(), height()) - m_marginY2Dash;AxisYrArcDrawWidthScales(*m_painter, offsetXr, diametersR, 10, m_marginY);// 底侧x轴线与y轴刻度等分线绘制QPen level; // 等分线for (int i = 0; i < m_scalesY.sp.size(); ++i) {m_painter->setPen(Qt::NoPen);if (i == 0) {level.setColor(Qt::cyan);level.setWidthF(0.5);} else if (i % 2) {level.setColor(Qt::lightGray);level.setWidthF(0.3);} else {level.setColor(Qt::gray);level.setWidthF(0.3);}m_painter->setPen(level);m_painter->drawLine(m_scalesY.sp[i], m_scalesY.ep[i]);}// 坐标轴刻度线设置AxisScalesLineSet();
}// 创建坐标轴
void MyChart::AxisCreateTest()
{m_painter->setRenderHint(QPainter::Antialiasing);// 坐标轴初始化设置AxisInitSet();// 坐标轴刻度值刷新AxisScalesValueSet();// 坐标轴标题设置AxisTitleSet();// 曲线标题显示与设置AxisCurvesTitleSet();
}// 曲线坐标点刷新
void MyChart::AxisPointsUpdate(QVector<QList<QPoint>> &pointList)
{quint16 level = 15;for (int i = 0; i < m_listDataNode.size(); ++i) {DataNode node = m_listDataNode.at(i);for (int j = 0; j < 1; ++j) {if (j >=m_valYMax.size()) {m_valYMax.resize(j + 1);}if (m_valYMax[j] < node.value + node.value / 4) {m_valYMax[j] = node.value + node.value / 4 + j * level;}}}// 刷新曲线坐标点for (int i = 0; i < m_listDataNode.size(); ++i) {DataNode node = m_listDataNode.at(i);quint8 num = 1;if (pointList.size() < num) {pointList.resize(num);}int offsetX = m_pointZero.x() + i * m_periodX;for (int j = 0; j < num; ++j) {if (j >= m_valYMax.size()) {return;}double offsetY = (double)((node.value / m_valYMax[j]) * m_lenY);pointList[j] << QPoint(offsetX, m_pointZero.y() - offsetY);}}
}// 平滑曲线绘制
void MyChart::SmoothCurvesDrawTest(const QColor &color, qreal &width, const QList<QPoint> &pointList)
{QPainterPath path(pointList[0]);for (int i = 0; i < pointList.size() - 1; ++i) {QPointF sp = pointList[i];QPointF ep = pointList[i + 1];QPointF c1 = QPointF((sp.x() + ep.x()) / 2, sp.y());QPointF c2 = QPointF((sp.x() + ep.x()) / 2, ep.y());path.cubicTo(c1, c2, ep);}m_painter->setRenderHints(QPainter::Antialiasing, true);m_painter->setPen(QPen(color, width));m_painter->drawPath(path);
}// 左侧y轴弧线绘制,刻度点记录
// offsetX: 弧线最右侧举例widget最左侧的长度
// diameters: 圆弧直径,即外切举行的边长
// tickCount : 刻度个数
// margin : y轴最上边刻度与最下边刻度距离widget上下边界的距离
void MyChart::AxisYlArcDrawWidthScales(QPainter &painter, int offsetX, int diameters, int tickCount, int margin)
{painter.save();painter.setRenderHint(QPainter::Antialiasing);painter.resetTransform();painter.setPen(QPen(QColor(0, 85, 127), 3.0));// 当前widget内包含最大正方形的边长int rectLen = qMin(width(), height());// 外切矩形的起始位置int rectStartX = offsetX - diameters;// 弧线实际外接矩形的y方向起始位置int rectStartY = (rectLen - diameters) / 2;QPoint rectStartPoint = QPoint(rectStartX, rectStartY);// 弧线实际外切矩形边长设置QSize size = QSize(diameters, diameters);// 弧线实际外切矩形QRect rect(rectStartPoint, size);// 半径长度,对应三角函数中斜边长度int radius = diameters / 2;qreal opSideLen = ((qreal)((rectLen > diameters) ? diameters : rectLen)) / 2;// 起始角度qreal startAngle = -qRadiansToDegrees(qAsin(opSideLen / radius));// 旋转角度qreal spanAngle = 2 * startAngle;// 绘制弧线painter.drawArc(rect, (int)(startAngle * 16), -(int)(spanAngle * 16));// 刻度点记录opSideLen -= margin;startAngle = -qRadiansToDegrees(qAsin(opSideLen / radius));spanAngle = 2 * startAngle;// 刻度点之间的角度间隔qreal intervalAngle = spanAngle / (tickCount - 1);m_scalesY.sp.clear();int i = 0;// 当前刻度对应的角度数qreal angle = startAngle - i * intervalAngle;// 刻度圆点的x坐标qreal x = rectStartX + radius + radius * qCos(qDegreesToRadians(angle));// 刻度点的y轴坐标qreal y = rectStartY + radius - radius * qSin(qDegreesToRadians(angle));m_scalesY.spMin = QPoint(x, y);m_scalesY.sp.append(QPoint(x, y));painter.setPen(QPen(Qt::red, 5));painter.drawPoint(x, y);for (i = 1; i < tickCount; ++i) {angle = startAngle - i * intervalAngle;x = rectStartX + radius + radius * qCos(qDegreesToRadians(angle));y = rectStartY + radius - radius * qSin(qDegreesToRadians(angle));m_scalesY.sp.append(QPoint(x, y));painter.drawPoint(x, y);if (m_scalesY.spMin.x() > x) {m_scalesY.spMin = QPoint(x, y);}if (m_scalesY.spMax.x() < x) {m_scalesY.spMax = QPoint(x, y);}}painter.restore();
}// 右侧y轴弧线绘制,刻度点记录
// offsetX: 弧线最右侧举例widget最左侧的长度
// diameters: 圆弧直径,即外切举行的边长
// tickCount : 刻度个数
// margin : y轴最上边刻度与最下边刻度距离widget上下边界的距离
void MyChart::AxisYrArcDrawWidthScales(QPainter &painter, int offsetX, int diameters, int tickCount, int margin)
{painter.save();painter.setRenderHint(QPainter::Antialiasing);painter.resetTransform();painter.setPen(QPen(QColor(0, 85, 127), 3.0));// 当前widget内包含最大正方形的边长int rectLen = qMin(width(), height());// 外切矩形的起始位置int rectStartX = offsetX;// 弧线实际外接矩形的y方向起始位置int rectStartY = (rectLen - diameters) / 2;QPoint rectStartPoint = QPoint(rectStartX, rectStartY);// 弧线实际外切矩形边长设置QSize size = QSize(diameters, diameters);// 弧线实际外切矩形QRect rect(rectStartPoint, size);// 半径长度,对应三角函数中斜边长度int radius = diameters / 2;qreal opSideLen = ((qreal)((rectLen > diameters) ? diameters : rectLen)) / 2;// 起始角度qreal startAngle = qRadiansToDegrees(qAsin(opSideLen / radius));// 旋转角度qreal spanAngle = 2 * startAngle;// 起始角度从widget下边界开始startAngle = 180 + startAngle;// 绘制弧线painter.drawArc(rect, (int)(startAngle * 16), -(int)(spanAngle * 16));// 刻度点记录opSideLen -= margin;startAngle = qRadiansToDegrees(qAsin(opSideLen / radius));spanAngle = 2 * startAngle;startAngle += 180;// 刻度点之间的角度间隔qreal intervalAngle = spanAngle / (tickCount - 1);m_scalesY.ep.clear();int i = 0;// 当前刻度对应的角度数qreal angle = startAngle + i * intervalAngle;// 刻度圆点的x坐标qreal x = rectStartX + radius + radius * qCos(qDegreesToRadians(angle));// 刻度点的y轴坐标qreal y = rectStartY + radius - radius * qSin(qDegreesToRadians(angle));m_scalesY.epMin = QPoint(x, y);m_scalesY.ep.append(QPoint(x, y));painter.setPen(QPen(Qt::red, 5));painter.drawPoint(x, y);for (i = 1; i < tickCount; ++i) {angle = startAngle - i * intervalAngle;x = rectStartX + radius + radius * qCos(qDegreesToRadians(angle));y = rectStartY + radius - radius * qSin(qDegreesToRadians(angle));m_scalesY.ep.append(QPoint(x, y));painter.drawPoint(x, y);if (m_scalesY.epMin.x() > x) {m_scalesY.epMin = QPoint(x, y);}if (m_scalesY.epMax.x() < x) {m_scalesY.epMax = QPoint(x, y);}}painter.restore();
}// 曲线图刷新
void MyChart::ChartViewUpdate()
{static int speed = 0;m_curValueDashBoard = speed++;QVector<QList<QPoint>> pointList;// 曲线坐标点刷新AxisPointsUpdate(pointList);if (m_valYMax.size() >= 2) {// 创建坐标轴AxisCreateTest();}// 坐标轴标题设置AxisTitleSet();for (int i = 0; i < pointList.size(); ++i) {if (i >= m_cvsInfo.size()) {break;}SmoothCurvesDrawTest(m_cvsInfo[i].color, m_cvsInfo[i].width, pointList[i]);}speed %= 239;
}//绘制实时数字
void MyChart::DrawDbNum(QPainter& painter, QPoint & point)
{painter.save();painter.setPen(QColor(0,255,255));QFont font;font.setFamily("Arial");font.setPointSize(8);painter.setFont(font);// 根据 QFont 对象创建 QFontMetrics 实例QFontMetrics font_metrics(painter.font());QString str = QString::number(m_curValueDashBoard) + QString("kb/s");// 获取文本的长度int text_width = font_metrics.width(str);// 获取文本的高度int text_height = font_metrics.height();QPoint curPoint = QPoint(point.x() - text_width / 2, point.y() - text_height);painter.drawText(curPoint, str);str = "数据传输速率";text_width = font_metrics.width(str);QPoint curPoint2 = QPoint(point.x() - text_width / 2, point.y() + text_height);painter.drawText(curPoint2, str);painter.restore();
}// 绘制一条中间略粗两边略尖的渐变发光线条
void MyChart::DrawDbLightLine(QPainter& painter, QPoint & point)
{painter.save();// 创建白色的画笔,设置较粗的线条和略圆形的线帽QPen pen(QColor(255, 255, 255));pen.setWidth(6);pen.setCapStyle(Qt::RoundCap);// 创建一个渐变,将颜色从白色渐变到透明色QLinearGradient gradient(0, point.y() - 100, 0, point.y() + 100);gradient.setColorAt(0, QColor(255, 255, 255));gradient.setColorAt(1, QColor(255, 255, 255, 0));// 设置画笔的颜色为渐变pen.setBrush(gradient);// 将这个画笔设置给 QPainterpainter.setPen(pen);// 绘制一条直线painter.drawLine(QPoint(point.x() - 100, point.y()), QPoint(point.x() + 100, point.y()));painter.restore();
}// 中间小圆
void MyChart::DrawCircle_bom_small(QPainter& painter, QPoint & center, int radius)
{//保存绘图对象painter.save();//计算圆的位置和大小int x = center.x() - radius;int y = center.y() - radius;int width = 2 * radius;int height = 2 * radius;//创建圆形路径QPainterPath inCircle;inCircle.moveTo(center);inCircle.addEllipse(x, y, width, height);//设置画刷和画笔painter.setBrush(QColor(0, 73, 107));painter.setPen(QColor(255, 255, 255));//绘制圆形painter.drawPath(inCircle);//恢复绘图对象painter.restore();
}// 渐变发光内圈
void MyChart::DrawCircle_bom_shine(QPainter &painter, QPoint ¢er, int radius)
{painter.save();//计算圆的位置和大小int x = center.x() - radius;int y = center.y() - radius;int width = 2 * radius;int height = 2 * radius;//创建渐变对象QRadialGradient radialGradient(center, radius, center);radialGradient.setColorAt(0.5, QColor(10, 68, 185, 150));radialGradient.setColorAt(1.0, Qt::transparent);//设置画刷painter.setBrush(QBrush(radialGradient));painter.setPen(Qt::transparent);//计算圆形路径QPainterPath path;path.addEllipse(x, y, width, height);//绘制圆形渐变painter.setClipPath(path);painter.drawEllipse(x, y, width, height);//恢复绘图对象painter.restore();
}// 最外细圆线
void MyChart::DrawCircle_line(QPainter& painter, QPoint ¢er, int radius, int width)
{painter.save();QPainterPath outRing;QPainterPath inRing;outRing.moveTo(center);inRing.moveTo(center);// 计算弧线的起点和终点位置QPoint startPoint(center.x() + radius * cos(60 * M_PI / 180), center.y() - radius * sin(60 * M_PI / 180));QPoint endPoint(center.x() + radius * cos(300 * M_PI / 180), center.y() - radius * sin(300 * M_PI / 180));outRing.arcTo(center.x() - radius, center.y() - radius, 2 * radius, 2 * radius, -30, 240);inRing.addEllipse(center.x() - radius + width, center.y() - radius + width, 2 * (radius - width), 2 * (radius - width));outRing.closeSubpath();painter.setBrush(QColor(5,228,255));painter.drawPath(outRing.subtracted(inRing));painter.restore();
}// 绘制指针
void MyChart::DrawPointer(QPainter& painter, QPoint ¢er, int radius, int degRotate)
{//组装点的路径图QPainterPath pointPath;pointPath.moveTo(0, 0);pointPath.moveTo(10,0);pointPath.lineTo(1,-radius);pointPath.lineTo(-1,-radius);pointPath.lineTo(-10,0);pointPath.arcTo(-10,0,20,20,180,180);QPainterPath inRing;inRing.addEllipse(-5,-5,10,10);painter.save();// 计算绘制相对原点的坐标系painter.save();painter.translate(center);painter.setPen(Qt::transparent);//计算并选择绘图对象坐标painter.rotate(degRotate - 120);painter.setBrush(QColor(0,255,255));painter.drawPath(pointPath.subtracted(inRing));painter.restore();
}// 动态扇形环
void MyChart::DrawCircle_arc(QPainter& painter, QPoint ¢er, int radius, int degRotate)
{// 计算绘制相对原点的坐标系painter.save();// painter移动至centerpainter.translate(center);// 设置扇形渐变色QRect rect(-radius, -radius, 2 * radius, 2 * radius);QConicalGradient gradient(0,0,-70);gradient.setColorAt(0.1, QColor(255, 88, 127, 200)); // 红色gradient.setColorAt(0.5, QColor(53, 179, 251, 150)); // 蓝色painter.setBrush(gradient);// 绘制扇形painter.drawPie(rect,210*16,-(degRotate)*16);painter.restore();
}// 渐变发光外扇形
void MyChart::DrawCircle(QPainter& painter, QPoint ¢er, int radius)
{//保存绘图对象painter.save();// painter移动至centerpainter.translate(center);//计算大小圆路径QPainterPath outRing;QPainterPath inRing;outRing.moveTo(0,0);inRing.moveTo(0,0);outRing.arcTo(-radius,-radius, 2*radius,2*radius,-30,240);inRing.addEllipse(-radius+50,-radius + 50,2*(radius-50),2*(radius-50));outRing.closeSubpath();//设置渐变色kQRadialGradient radialGradient(0,0,radius,0,0);radialGradient.setColorAt(1,QColor(0,82,199));radialGradient.setColorAt(0.92,Qt::transparent);//设置画刷painter.setBrush(radialGradient);painter.setPen(Qt::transparent);//大圆减小圆painter.drawPath(outRing.subtracted(inRing));painter.restore();
}// 刻度数字
void MyChart::DrawDigital(QPainter& painter, QPoint ¢er, int radius)
{//保存绘图对象painter.save();// painter移动至centerpainter.translate(center);//设置画笔,画笔默认NOPENpainter.setPen(QColor(250,0,150));QFont font;font.setFamily("Arial");font.setPointSize(8);font.setBold(true);painter.setFont(font);for(int i=0;i<13;++i){QPointF point(0,0);painter.save();point.setX(radius*qCos(((210-i*20)*M_PI)/180));point.setY(radius*qSin(((210-i*20)*M_PI)/180));painter.translate(point.x(),-point.y());painter.rotate(-120+i*20);painter.drawText(-25, 0, 50, 20,Qt::AlignCenter,QString::number(i*20));painter.restore();}//还原画笔painter.setPen(Qt::NoPen);painter.restore();
}// 绘制刻度
void MyChart::DrawSmallScale(QPainter& painter, QPoint ¢er, int radius)
{//保存绘图对象painter.save();// painter移动至centerpainter.translate(center);//组装点的路径图QPainterPath pointPath_small;pointPath_small.moveTo(-2,-2);pointPath_small.lineTo(2,-2);pointPath_small.lineTo(2,8);pointPath_small.lineTo(-2,8);QPainterPath pointPath_big;pointPath_big.moveTo(-2,-2);pointPath_big.lineTo(2,-2);pointPath_big.lineTo(2,20);pointPath_big.lineTo(-2,20);painter.setPen(Qt::transparent);//绘制121个小点for(int i=0;i<121;i+=2){QPointF point(0,0);painter.save();point.setX(radius*qCos(((210-i*2)*M_PI)/180));point.setY(radius*qSin(((210-i*2)*M_PI)/180));painter.translate(point.x(),-point.y());painter.rotate(-120+i*2);if(i<80){painter.setBrush(QColor(255,255,255));}if(i>=80){painter.setBrush(QColor(235,70,70));}if(i%5 == 0){painter.drawPath(pointPath_big);//绘画大刻度}else{painter.drawPath(pointPath_small);//绘画小刻度}painter.restore();}painter.restore();
}//绘制码表盘
void MyChart::DrawDb(QPainter& painter, QPoint & center)
{int radius = qMin(width() / 2, height() / 2);// 绘制刻度DrawSmallScale(painter, center, radius - 40);// 刻度数字DrawDigital(painter, center, radius - 50);// 渐变发光外扇形DrawCircle(painter, center, radius - 20);// 动态扇形环DrawCircle_arc(painter, center, radius - 20, m_curValueDashBoard);// 绘制指针DrawPointer(painter, center, radius - 30, m_curValueDashBoard);// 最外细圆线DrawCircle_line(painter, center, radius - 20, 4);// 中间大圈DrawCircle_bom_small(painter, center, radius - 80);// 渐变发光内圈,-80用于调节渐变圆圈的宽度DrawCircle_bom_shine(painter, center, radius - 100);//中间小圆DrawCircle_bom_small(painter, center, radius - 120);//绘制实时时速数字DrawDbNum(painter, center);// 绘制一条中间略粗两边略尖的渐变发光线条DrawDbLightLine(painter, center);
}// 马表盘
void MyChart::DashBoardUpdate(const QPoint & point)
{m_painter->translate(point);// 画一个带有渐变色的圆圈// 镜像渐变QRadialGradient ra(0, 0, qMin(width(), height() / 2));// 设置起点颜色ra.setColorAt(0, Qt::green);// 终点颜色ra.setColorAt(1, Qt::cyan);m_painter->setBrush(ra);m_painter->setPen(Qt::NoPen);m_painter->drawEllipse(QPoint(0, 0), qMin(width(), height()) / 2, qMin(width(), height()) / 2);qreal angle = 270 * 1.0 / (m_maxValueDashBoard - m_minValueDashBoard);// 135°为刻度的起始点对应的角度m_painter->rotate(135);// 绘制刻度线m_painter->setPen(QPen(Qt::white, 4));m_painter->setFont(QFont("微软雅黑", 18));for (int i = m_minValueDashBoard; i <= m_maxValueDashBoard; ++i) {if (i % 10 == 0) {if (135 + angle * i < 270) {m_painter->rotate(180);m_painter->drawText(-qMin(width(), height()) / 2 + 30, 10, QString::number(i));m_painter->rotate(-180);} else {m_painter->drawText(qMin(width(), height()) / 2 - 20 - 50, 10, QString::number(i));}// 第一个参数是起始点的 x 坐标,第二个参数是起始点的 y 坐标,第三个参数是终止点的 x 坐标,第四个参数是终止点的 y 坐标。m_painter->drawLine(qMin(width(), height()) / 2 - 20, 0, qMin(width(), height()) / 2, 0);} else {m_painter->drawLine(qMin(width(), height()) / 2 - 10, 0, qMin(width(), height()) / 2, 0);}m_painter->rotate(angle);}// 刻度画完后,将painter旋转至水平向下的角度,以便在中心位置绘制文字// 之前时顺时针旋转的,现在逆时针旋转45°就可以了m_painter->rotate( - angle - 45);// 绘制一个圆圈// `painter.drawEllipse()` 函数的第二个和第三个参数分别表示椭圆的半长轴长度和半短轴长度。m_painter->drawEllipse(QPoint(0, 0), 100, 100);// 绘制文字m_painter->drawText(QRect(-100, -100, 200, 200), Qt::AlignCenter, QString::number(m_curValueDashBoard));// painter的x轴回到刻度起始位置m_painter->rotate(135 + m_curValueDashBoard * angle);// `width()` 和 `height()` 函数返回的分别是 `QWidget` 控件的宽度和高度。m_painter->drawLine(100, 0, qMin(width(), height()) / 2 - 20 - 50, 0);// 绘制扇形渐变色m_painter->rotate(-135 - m_curValueDashBoard * angle);m_painter->setBrush(QColor(255, 0, 0, 100));m_painter->setPen(Qt::NoPen);m_painter->drawPie(QRect(-qMin(width(), height()) / 2 + 20 + 50,-qMin(width(), height()) / 2 + 20 + 50,qMin(width(), height()) - 40 - 100,qMin(width(), height()) - 40 - 100),(360 - 135) * 16,-m_curValueDashBoard * angle * 16);// 释放画刷m_painter->setBrush(Qt::NoBrush);
}void MyChart::drawArcAtWidget(QPainter * painter, const QPoint& center, int radius)
{int startAngle = -30 * 16; // 弧线的起始角度,从 x 轴负方向开始int endAngle = -90 * 16 + 180 * 16; // 弧线的终止角度,绘制从正上方到正下方的弧线int startX = center.x();int startY = painter->device()->height() * 0.1;int arcHeight = painter->device()->height() * 0.8;// 调用 drawArc() 函数绘制弧线painter->drawArc(startX - radius, startY, radius * 2, arcHeight, startAngle, endAngle);
}void MyChart::paintEvent(QPaintEvent *event)
{(void)event;m_painter->begin(this);// 左侧码盘刷新DashBoardUpdate(QPoint(qMin(width(), height() / 2), qMin(width(), height() / 2)));
#if 1// 将坐标系回到原点m_painter->resetTransform();// 曲线图刷新ChartViewUpdate();// 右侧码盘刷新
#endif#if 1// 右侧码盘刷新// DashBoardUpdate(QPoint(m_axisEndX + qMin(width(), height()) / 2 + 100, qMin(width(), height()) / 2));QPoint dbrPoint = QPoint(width() - qMin(width(), height()) / 2, qMin(width(), height()) / 2);DrawDb(*m_painter, dbrPoint);#endifm_painter->end();
}
charttest.h
#ifndef CHARTTEST_H
#define CHARTTEST_H#include <QWidget>
#include <QTimer>
#include <QTime>
#include "mychart.h"
#include <QGroupBox>QT_BEGIN_NAMESPACE
namespace Ui { class ChartTest; }
QT_END_NAMESPACEtypedef enum {TRANS_GL_PHY_SEND = 0,TRANS_TL_PHY_SEND,TRANS_GL_PHY_RECV,TRANS_TL_PHY_RECV,TRANS_GL_MAC_SEND,TRANS_TL_MAC_SEND,TRANS_GL_MAC_RECV,TRANS_TL_MAC_RECV,TRANS_GL_LC_SEND,TRANS_TL_LC_SEND,TRANS_GL_LC_RECV,TRANS_TL_LC_RECV,TRANS_MAX,
} GtDataTransType;class ChartTest : public QWidget
{Q_OBJECTpublic:ChartTest(QWidget *parent = nullptr);~ChartTest();void initState();typedef struct {quint32 userId;quint32 sfr;quint32 vol;} RcvVolInfo;typedef struct {QVector<quint32> userOn;QVector<RcvVolInfo> infoG; // 获取g的吞吐量数据QVector<QVector<RcvVolInfo>> infoT; // 获取多个t的吞吐量数据} UserInfo;typedef struct {quint32 vol;quint32 sfr;} GtTransInfoGnode;typedef struct {quint32 userId;double rcvRat;} UserRcvRateInfo;typedef struct {QVector<UserRcvRateInfo> ratlc;QVector<UserRcvRateInfo> ratmac;QVector<UserRcvRateInfo> ratphy;} RcvRateInfo;typedef struct {quint32 phyId;quint32 sfrLc;quint32 volLc;quint32 sfrMac;quint32 volMac;quint32 sfrPhy;quint32 volPhy;} GtTransInfoUser;typedef struct {GtTransInfoGnode infoG[TRANS_MAX];quint32 userNum;QVector<GtTransInfoUser> infoT;} GtDataTransAllInfo;typedef enum {RCV_TYPE_LC = 0,RCV_TYPE_MAC,RCV_TYPE_PHY,RCV_TYPE_MAX,} TransRcvType;// 原始数据刷新void VolDataUpdate();// 码速率计算结果数据刷新void TransRetUpdate();// 码速率计算结果txt文本显示void TextViewRefreshTransRate(RcvRateInfo & m_rcvRat);protected:void resizeEvent(QResizeEvent *event);private slots:void on_splitter_splitterMoved(int pos, int index);private:Ui::ChartTest *ui;int index = 0;QTimer timer; //定时器QVector<MyChart *> m_chart;QVector<QGroupBox *> m_chartView;quint32 m_userNum = 5;RcvRateInfo m_rcvRat;};
#endif // CHARTTEST_H
charttest.cpp
#include "charttest.h"
#include "ui_charttest.h"
#include <QDebug>ChartTest::ChartTest(QWidget *parent): QWidget(parent), ui(new Ui::ChartTest)
{ui->setupUi(this);initState();
}ChartTest::~ChartTest()
{timer.stop();delete ui;
}// 页面大小变化触发
void ChartTest::resizeEvent(QResizeEvent *event)
{QWidget::resizeEvent(event);
}void ChartTest::initState()
{m_chart.append(ui->chartViewD0);m_chart.append(ui->chartViewD1);m_chart.append(ui->chartViewD2);m_chartView.append(ui->groupBoxD0);m_chartView.append(ui->groupBoxD1);m_chartView.append(ui->groupBoxD2);ui->groupBox_showSet->setMaximumHeight(50);ui->groupBox_showSet->setMinimumHeight(50);MyChart::AxisInfo axisInfo;// 坐标轴参数初始化设置axisInfo.titleX = "time";axisInfo.durationX = 100;axisInfo.titleYl = "码速率(kbs/s)";axisInfo.durationYl = 10;axisInfo.titleYr = "码速率(kbs/s)";axisInfo.dudrationYr = 10;for (int i = 0; i < m_chart.size(); ++i) {m_chart[i]->AxisInit(axisInfo);// 曲线参数初始化设置m_chart[i]->AxisCurvesAppend("user100", 3, Qt::cyan);m_chart[i]->AxisCurvesAppend("user101", 3, Qt::green);m_chart[i]->AxisCurvesAppend("user102", 3, Qt::blue);m_chart[i]->AxisCurvesAppend("user103", 3, Qt::yellow);m_chart[i]->AxisCurvesAppend("user104", 3, Qt::magenta);}connect(&timer, &QTimer::timeout, [=](){// 码速率计算结果数据刷新TransRetUpdate();TextViewRefreshTransRate(m_rcvRat);// 模拟数据int y = rand() % 4 + 3;static int value = 0;DataNode node = {y, "ABC" + QString::number(value++)};// 刷新数据for (int i = 0; i < m_chart.size(); ++i) {m_chart[i]->updateValue(node);}});timer.start(100);
}// 码速率计算结果数据刷新
void ChartTest::TransRetUpdate()
{// lcm_rcvRat.ratlc.clear();static double lcRatG = 120.0;UserRcvRateInfo lcInfoG = {0, lcRatG};m_rcvRat.ratlc.append(lcInfoG);for (quint32 i = 0; i < m_userNum; ++i) {UserRcvRateInfo inf = {100 + i, double(20 + i * 10)};m_rcvRat.ratlc.append(inf);}// macm_rcvRat.ratmac.clear();static double macRatG = 120.0;UserRcvRateInfo macInfoG = {0, macRatG};m_rcvRat.ratmac.append(macInfoG);for (quint32 i = 0; i < m_userNum; ++i) {UserRcvRateInfo inf = {100 + i, double(20 + i * 10)};m_rcvRat.ratmac.append(inf);}// phym_rcvRat.ratphy.clear();static double phyRatG = 120.0;UserRcvRateInfo phyInfoG = {0, phyRatG};m_rcvRat.ratphy.append(phyInfoG);for (quint32 i = 0; i < m_userNum; ++i) {UserRcvRateInfo inf = {100 + i, double(20 + i * 10)};m_rcvRat.ratphy.append(inf);}if (lcRatG++ >= 130) {lcRatG = 120;}if (macRatG++ >= 130) {macRatG = 120;}if (phyRatG++ >= 130) {phyRatG = 120;}
}void ChartTest::TextViewRefreshTransRate(RcvRateInfo & info)
{QDateTime current_time = QDateTime::currentDateTime();QString time = current_time.toString("yyyy-MM-dd hh:mm:ss");// QString str = "[" + time + "]\r\n";QString str = "";// lcstr += "[" + time + "] " + "lc recv rate : ";for (int i = 0; i < info.ratlc.size(); ++i) {str += "user[" + QString::number(info.ratlc[i].userId) + "] : " + QString::number(info.ratlc[i].rcvRat) + ", ";}str += "\n";// macstr += "[" + time + "] " + "mac recv rate : ";for (int i = 0; i < info.ratmac.size(); ++i) {str += "user[" + QString::number(info.ratmac[i].userId) + "] : " + QString::number(info.ratmac[i].rcvRat) + ", ";}str += "\n";// phystr += "[" + time + "] " + "phy recv rate : ";for (int i = 0; i < info.ratphy.size(); ++i) {str += "user[" + QString::number(info.ratphy[i].userId) + "] : " + QString::number(info.ratphy[i].rcvRat) + ", ";}str += "\n";ui->textEdit_transRate->append(str);ui->textEdit_originData->append(str);
}void ChartTest::on_splitter_splitterMoved(int pos, int index)
{int hi = pos;if (index == 1) {hi += ui->groupBox_showSet->height() * 1.0 / 2;} else {hi -= ui->groupBox_showSet->height() * 1.0 / 2;}double hRate1 = (double)(hi * 1.0 / height());qDebug() << "index " << index << "rate = " << hRate1 << endl;if (hRate1 < 0.10) {ui->groupBox_showSet->setVisible(false);} else if (hRate1 < 0.20) {ui->groupBox_showSet->setVisible(true);m_chartView[0]->setVisible(false);m_chartView[1]->setVisible(false);m_chartView[2]->setVisible(false);} else if (hRate1 < 0.3) {m_chartView[0]->setVisible(false);m_chartView[1]->setVisible(false);m_chartView[2]->setVisible(true);} else if (hRate1 < 0.4) {m_chartView[0]->setVisible(false);m_chartView[1]->setVisible(true);m_chartView[2]->setVisible(true);} else {m_chartView[0]->setVisible(true);m_chartView[1]->setVisible(true);m_chartView[2]->setVisible(true);ui->groupBox_textInfo->setVisible(true);}
}