Qt-QtChart自定义显示样式

 
 
csdn同步更新,可下载源代码包:
https://download.csdn.net/download/zhujianqiangqq/90404707

main.cpp
复制代码
 1 #include "mainwindow.h"
 2 
 3 #include <QApplication>
 4 
 5 int main(int argc, char *argv[])
 6 {
 7     QApplication a(argc, argv);
 8     MainWindow w;
 9     w.show();
10     return a.exec();
11 }
View Code
复制代码

mainwindow.h

复制代码
 1 #ifndef MAINWINDOW_H
 2 #define MAINWINDOW_H
 3 
 4 #include <QMainWindow>
 5 #include <QHBoxLayout>
 6 #include <QVBoxLayout>
 7 
 8 #include "ChartShowWidget.h"
 9 
10 QT_BEGIN_NAMESPACE
11 namespace Ui { class MainWindow; }
12 QT_END_NAMESPACE
13 
14 class MainWindow : public QMainWindow
15 {
16     Q_OBJECT
17 
18 public:
19     MainWindow(QWidget *parent = nullptr);
20     ~MainWindow();
21 
22 private:
23     Ui::MainWindow *ui;
24 };
25 #endif // MAINWINDOW_H
View Code
复制代码

mainwindow.cpp

复制代码
 1 #include "mainwindow.h"
 2 #include "ui_mainwindow.h"
 3 
 4 MainWindow::MainWindow(QWidget *parent)
 5     : QMainWindow(parent)
 6     , ui(new Ui::MainWindow)
 7 {
 8     ui->setupUi(this);
 9     this->setWindowTitle(QStringLiteral("QtChart自定义显示样式"));
10 
11     QVBoxLayout *oVBox = new QVBoxLayout();
12     TChartShowWidget *o = new TChartShowWidget(this);
13 
14     oVBox->addWidget(o);
15     o->setVisible(true);
16 
17     this->centralWidget()->setLayout(oVBox);
18     this->setMinimumSize(2000, 1000);
19 }
20 
21 MainWindow::~MainWindow()
22 {
23     delete ui;
24 }
View Code
复制代码

minawindow.ui

复制代码
 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <ui version="4.0">
 3  <class>MainWindow</class>
 4  <widget class="QMainWindow" name="MainWindow">
 5   <property name="geometry">
 6    <rect>
 7     <x>0</x>
 8     <y>0</y>
 9     <width>800</width>
10     <height>600</height>
11    </rect>
12   </property>
13   <property name="windowTitle">
14    <string>MainWindow</string>
15   </property>
16   <property name="styleSheet">
17    <string notr="true">background-color: rgb(170, 170, 127);</string>
18   </property>
19   <widget class="QWidget" name="centralwidget"/>
20   <widget class="QMenuBar" name="menubar">
21    <property name="geometry">
22     <rect>
23      <x>0</x>
24      <y>0</y>
25      <width>800</width>
26      <height>21</height>
27     </rect>
28    </property>
29   </widget>
30   <widget class="QStatusBar" name="statusbar"/>
31  </widget>
32  <resources/>
33  <connections/>
34 </ui>
View Code
复制代码

ChartShowWidget.h

复制代码
 1 #ifndef CHARTSHOWWIDGET_H
 2 #define CHARTSHOWWIDGET_H
 3 
 4 #include <QObject>
 5 #include <QDebug>
 6 #include <QPushButton>
 7 #include <QCheckBox>
 8 
 9 #include "LineChart.h"
10 #include "BarChart.h"
11 #include "ScatterChart.h"
12 #include "PieChart.h"
13 #include "RadarChart.h"
14 
15 #include "C_V_AreaChart.h"
16 #include "SplineChart.h"
17 #include "ScoreLineChart.h"
18 #include "StructureChart.h"
19 
20 class TChartShowWidget : public QWidget
21 {
22     Q_OBJECT
23 public:
24     TChartShowWidget(QWidget* parent, Qt::WindowFlags f = Qt::WindowFlags());
25     ~TChartShowWidget();
26 
27 protected:
28     virtual void executeCommand();
29 public slots:
30 private:
31     void initBeamNumberChart();
32     void initBombSpeedChart();
33     void initDamnifyChart();
34     void initBeamNumberRadarChart();
35     void initBeamNumberRadarChart2();
36     void initBeamNumberRadarChart3();
37 
38     void initLineChart();
39     void initC_V_AreaChart();
40     void initSplineChart();
41     void initScoreLineChart();
42     void initStructureChart();
43     //
44     int m_nInterval = 0;
45 
46     LineChart* m_chart = nullptr;
47     BarChart* m_BeamNumberChart = nullptr;
48     ScatterChart* m_BombSpeedChart = nullptr;
49     PieChart* m_DamnifyChart;
50     RadarChart* m_BeamNumberRadar;
51     RadarChart* m_BeamNumberRadar2;
52     RadarChart* m_BeamNumberRadar3;
53 
54 
55     C_V_AreaChart* m_C_V_AreaChart;
56     SplineChart* m_SplineChart;
57     ScoreLineChart* m_ScoreLineChart;
58     StructureChart *m_pStructureChart;
59 };
60 
61 #endif // CHARTSHOWWIDGET_H
View Code
复制代码

ChartShowWidget.cpp

复制代码
  1 #include "ChartShowWidget.h"
  2 
  3 #include <Windows.h>
  4 #include <QApplication>
  5 #include <iostream>
  6 #include <assert.h>
  7 #include <time.h>
  8 #include "mainwindow.h"
  9 
 10 TChartShowWidget::TChartShowWidget(QWidget *parent, Qt::WindowFlags f)
 11     : QWidget(parent, f)
 12 {
 13     initLineChart();
 14     initBeamNumberChart();
 15     initBombSpeedChart();
 16     initDamnifyChart();
 17     initBeamNumberRadarChart();
 18     initBeamNumberRadarChart2();
 19     initBeamNumberRadarChart3();
 20 
 21     initC_V_AreaChart();
 22     initSplineChart();
 23     initScoreLineChart();
 24     initStructureChart();
 25 }
 26 
 27 TChartShowWidget::~TChartShowWidget()
 28 { 
 29 
 30 }
 31 
 32 void TChartShowWidget::executeCommand()
 33 {
 34 
 35 }
 36 
 37 void TChartShowWidget::initBeamNumberChart()
 38 {
 39     m_BeamNumberChart = new BarChart(BarChart::vertical, this);
 40     m_BeamNumberChart->setGeometry(m_chart->x() + m_chart->width(),m_chart->y(),500,300);
 41     m_BeamNumberChart->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
 42     m_BeamNumberChart->setLabel(QStringLiteral("BeamNumberChart"));
 43     m_BeamNumberChart->toStyle1();
 44     int maxValue = 12;
 45     QStringList categorys;
 46     QList<qreal> launchValues;
 47     QList<qreal> DamageValues;
 48     QList<qreal> MachineValues;
 49     categorys << QStringLiteral("launch") << QStringLiteral("Damage") << QStringLiteral("Machine");
 50     launchValues << 6 << 5 << 7;
 51     DamageValues << 5 << 3 << 4;
 52     MachineValues << 6 << 7 << 4;
 53     m_BeamNumberChart->clearData();
 54     m_BeamNumberChart->setYAxisMaxValue(maxValue);
 55     m_BeamNumberChart->setCategoryAxisLabel(categorys);
 56     m_BeamNumberChart->appendData(QStringLiteral("launch"), launchValues, QColor(84, 125, 255, 0.85 * 255));
 57     m_BeamNumberChart->appendData(QStringLiteral("Damage"), DamageValues, QColor(232, 92, 74, 0.85 * 255));
 58     m_BeamNumberChart->appendData(QStringLiteral("Machine"), MachineValues, QColor(100, 92, 74, 0.85 * 255));
 59 }
 60 
 61 void TChartShowWidget::initBombSpeedChart()
 62 {
 63     m_BombSpeedChart = new ScatterChart(this);
 64     m_BombSpeedChart->setGeometry(m_BeamNumberChart->x() + m_BeamNumberChart->width(),m_BeamNumberChart->y(),500,300);
 65     m_BombSpeedChart->setLabel(QStringLiteral("BombSpeedChart"));
 66     QVector<float> values1;
 67     QVector<float> values2;
 68     QVector<float> values3;
 69 
 70     QStringList categorys;
 71 
 72     int maxValue = 10;
 73     for(int indxe = 0; indxe < maxValue; ++indxe)
 74     {
 75         categorys << QString::number(indxe) + "m";
 76     }
 77     values1 << 2 << 0 << 4 << 0 << 2 << 4 << 3 << 3 << 2 << 0;
 78     values2 << 0 << 1 << 0 << 2 << 1 << 0 << 0 << 2 << 0 << 1;
 79     values3 << 0 << 2 << 1 << 0 << 3 << 0 << 1 << 0 << 0 << 2;
 80     m_BombSpeedChart->clearData();
 81     m_BombSpeedChart->setCategoryLabels(categorys);
 82     m_BombSpeedChart->appendData(QStringLiteral("A"), values1, QColor(84, 125, 255, 0.85 * 255));
 83     m_BombSpeedChart->appendData(QStringLiteral("B"), values2, QColor(232, 92, 74, 0.85 * 255));
 84     m_BombSpeedChart->appendData(QStringLiteral("C"), values3, QColor(100, 92, 74, 0.85 * 255));
 85 }
 86 
 87 void TChartShowWidget::initDamnifyChart()
 88 {
 89     m_DamnifyChart = new PieChart(this);
 90     m_DamnifyChart->setGeometry(m_BombSpeedChart->x() + m_BombSpeedChart->width(), m_BombSpeedChart->y(),500,300);
 91     m_DamnifyChart->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
 92     m_DamnifyChart->setLabel(QStringLiteral("DamnifyChart"));
 93 
 94     int redDamageCount = 10;
 95     int blueDamageCount = 5;
 96     float damageCount = redDamageCount + blueDamageCount;
 97 
 98     m_DamnifyChart->clearData();
 99     m_DamnifyChart->appendData(QStringLiteral("A"), QString::number(redDamageCount / damageCount * 100, 'f', 2).toDouble(),  QColor(232, 92, 74, 0.85 * 255));
100     m_DamnifyChart->appendData(QStringLiteral("B"), QString::number(blueDamageCount / damageCount * 100, 'f', 2).toDouble(), QColor(84, 125, 255, 0.85 * 255));
101 }
102 
103 void TChartShowWidget::initBeamNumberRadarChart()
104 {
105     m_BeamNumberRadar = new RadarChart(this);
106     m_BeamNumberRadar->setGeometry(m_BeamNumberChart->x(), m_BeamNumberChart->y() + m_BeamNumberChart->height(),500,300);
107     m_BeamNumberRadar->setVisible(true);
108     m_BeamNumberRadar->setLegendVisible(false);
109     m_BeamNumberRadar->setAxisRange(0, 20);
110     m_BeamNumberRadar->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
111 
112     QStringList radarLabels;
113     QVector<float> values;
114     radarLabels << QStringLiteral("A") << QStringLiteral("B") << QStringLiteral("C");
115     values << 6 << 5 << 7;
116 
117     RadarChart::RadarChartItem data;
118     data.color = QColor(84, 125, 255, 0.85 * 255);
119     data.legendName = "ability";
120     data.values = values;
121 
122     m_BeamNumberRadar->setPeripheryLabel(radarLabels);
123     m_BeamNumberRadar->setData(data);
124 }
125 
126 void TChartShowWidget::initBeamNumberRadarChart2()
127 {
128     m_BeamNumberRadar2 = new RadarChart(this);
129     m_BeamNumberRadar2->setGeometry(m_BeamNumberRadar->x() + m_BeamNumberRadar->width(), m_BeamNumberRadar->y(),500,300);
130     m_BeamNumberRadar2->setVisible(true);
131     m_BeamNumberRadar2->setLegendVisible(false);
132     m_BeamNumberRadar2->setAxisRange(0, 20);
133     m_BeamNumberRadar2->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
134 
135     QStringList radarLabels;
136     QVector<float> values;
137     radarLabels << QStringLiteral("A") << QStringLiteral("B") << QStringLiteral("C");
138     values << 5 << 3 << 4;
139 
140     RadarChart::RadarChartItem data;
141     data.color = QColor(232, 92, 74, 0.85 * 255);
142     data.legendName = "ability";
143     data.values = values;
144 
145     m_BeamNumberRadar2->setPeripheryLabel(radarLabels);
146     m_BeamNumberRadar2->setData(data);
147 }
148 
149 void TChartShowWidget::initBeamNumberRadarChart3()
150 {
151     m_BeamNumberRadar3 = new RadarChart(this);
152     m_BeamNumberRadar3->setGeometry(m_BeamNumberRadar2->x() + m_BeamNumberRadar2->width(), m_BeamNumberRadar2->y(),500,300);
153     m_BeamNumberRadar3->setVisible(true);
154     m_BeamNumberRadar3->setLegendVisible(false);
155     m_BeamNumberRadar3->setAxisRange(0, 20);
156     m_BeamNumberRadar3->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
157 
158     QStringList radarLabels;
159     QVector<float> values;
160     radarLabels << QStringLiteral("A") << QStringLiteral("B") << QStringLiteral("C");
161     values << 6 << 7 << 4;
162 
163     RadarChart::RadarChartItem data;
164     data.color = QColor(100, 92, 74, 0.85 * 255);
165     data.legendName = "ability";
166     data.values = values;
167 
168     m_BeamNumberRadar3->setPeripheryLabel(radarLabels);
169     m_BeamNumberRadar3->setData(data);
170 }
171 
172 void TChartShowWidget::initLineChart()
173 {
174     m_chart = new LineChart(this);
175     m_chart->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
176     m_chart->setGeometry(0,0,500,300);
177     m_chart->setVisible(true);
178     m_chart->setLabel(QStringLiteral("LineChart"));
179     //
180     QStringList categorys;
181     QVector<float> values;
182     for(int indxe = 0; indxe < 10; ++indxe)
183     {
184         categorys << "indxe"+ QString::number(indxe/10.0);
185         values << indxe/10.0;
186     }
187     m_chart->clearData();
188     m_chart->setCategoryLabels(categorys);
189     m_chart->appendData(QStringLiteral("A"), values, QColor(0, 0, 0, 255));
190 }
191 
192 void TChartShowWidget::initC_V_AreaChart()
193 {
194     m_C_V_AreaChart = new C_V_AreaChart(this);
195     m_C_V_AreaChart->setLabel(QStringLiteral("C_V_AreaChart"));
196     m_C_V_AreaChart->setGeometry(m_chart->x(), m_chart->y() + m_chart->height(),500,300);
197     m_C_V_AreaChart->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
198 
199     QStringList categorys;
200     QVector<float> totalValue;
201     QVector<float> trainValues;
202     QString trainkey;
203 
204     int ntype = 1;
205      switch(ntype)
206      {
207          case 1:
208              trainkey = QStringLiteral("A");
209              break;
210          case 2:
211              trainkey = QStringLiteral("B");
212              break;
213      }
214 
215      int maxValue = 10;
216      for(int indxe = 0; indxe < maxValue; ++indxe)
217      {
218          categorys << QString::number(indxe);
219          totalValue << indxe;
220          trainValues << indxe;
221      }
222      m_C_V_AreaChart->clearData();
223      m_C_V_AreaChart->setCategoryLabels(categorys);
224      m_C_V_AreaChart->setMaxValue(maxValue);
225      m_C_V_AreaChart->appendData(QStringLiteral("C"), totalValue, QColor(232, 92, 74, 0.85 * 255));
226 
227      switch(ntype)
228      {
229          case 1:
230              m_chart->appendData("D", trainValues, QColor(84, 125, 255, 0.85 * 255));
231              break;
232          case 2:
233              m_chart->appendData("E", trainValues, QColor(84, 125, 255, 0.85 * 255));
234              break;
235      }
236 }
237 
238 void TChartShowWidget::initSplineChart()
239 {
240     m_SplineChart = new SplineChart(this);
241     m_SplineChart->setLabel(QStringLiteral("SplineChart"));
242     m_SplineChart->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
243     m_SplineChart->setGeometry(m_C_V_AreaChart->x(), m_C_V_AreaChart->y() + m_C_V_AreaChart->height(),500,300);
244 
245     QList<QPointF> redValues;
246     QList<QPointF> blueValues;
247 
248     redValues << QPointF(10, 10) << QPointF(20, 20) << QPointF(30, 30) << QPointF(80, 80) << QPointF(50, 60);
249     blueValues << QPointF(90, 10) << QPointF(50, 80) << QPointF(40, 70) << QPointF(25, 30) << QPointF(60, 70);
250 
251     m_SplineChart->appendData(QStringLiteral("A"), redValues,  QColor(232, 92, 74, 0.85 * 255));
252     m_SplineChart->appendData(QStringLiteral("B"), blueValues, QColor(84, 125, 255, 0.85 * 255));
253 
254 }
255 
256 void TChartShowWidget::initScoreLineChart()
257 {
258     m_ScoreLineChart = new ScoreLineChart(this);
259     m_ScoreLineChart->setTitle(QStringLiteral("ScoreLineChart"));
260     m_ScoreLineChart->setGeometry(m_SplineChart->x() + m_SplineChart->width(), m_SplineChart->y(),500,300);
261     m_ScoreLineChart->setScoreTickCount(5);
262     m_ScoreLineChart->setVisible(true);
263 
264     m_ScoreLineChart->setShowYAxisLine(false);
265     m_ScoreLineChart->setShowLegend(false);
266     m_ScoreLineChart->setShowTitle(false);
267     m_ScoreLineChart->setShowVGridLine(false);
268     m_ScoreLineChart->setShowPoint(false);
269 
270     m_ScoreLineChart->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
271 
272     QVector<float> values;
273 
274     int maxValue = 10;
275     for(int indxe = 0; indxe < maxValue; ++indxe)
276     {
277         m_ScoreLineChart->appendCourseLabel(QString::number(indxe));
278         values << indxe*10;
279     }
280     m_ScoreLineChart->appendScoresItem(values, "1", "#547DFF");
281 }
282 
283 void TChartShowWidget::initStructureChart()
284 {
285 //    QJsonParseError jsonError;
286 //    QString s = "{\"root\":\"Qt\",\"From\":\"1991\",\"Cross Platform\":\"true\"}";
287 //    QJsonDocument o = QJsonDocument::fromJson(s.toStdString().data(), &jsonError);
288 //    if(jsonError.error == QJsonParseError::NoError)
289 //    {
290 //        QJsonObject json = o.object();
291 //        m_pStructureChart = new StructureChart(json, this);
292 //        m_pStructureChart->setGeometry(m_ScoreLineChart->x() + m_ScoreLineChart->width(), m_ScoreLineChart->y(),500,300);
293 //        m_pStructureChart->setVisible(true);
294 //        m_pStructureChart->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
295 //    }
296     QJsonObject oTipValue;
297     oTipValue["tip"] = "TipValue";
298 
299     QJsonObject oValue;
300     oValue["1"] = QJsonValue(oTipValue);
301     oValue["2"] = "22";
302     oValue["tip"] = "tip";
303     QJsonValue oV(oValue);
304     QJsonObject json;
305     json["root"] = oV;
306 
307     m_pStructureChart = new StructureChart(json, this);
308     m_pStructureChart->setGeometry(m_ScoreLineChart->x() + m_ScoreLineChart->width(), m_ScoreLineChart->y(),500,300);
309     m_pStructureChart->setVisible(true);
310     m_pStructureChart->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
311 
312 
313 }
View Code
复制代码

AbstractChart.h

复制代码
 1 /*
 2  * 功能:简单封装图表抽象类
 3  * 作者:朱建强
 4  * 创建时间:2025-1-28
 5 */
 6 #ifndef ABSTRACTCHART_H
 7 #define ABSTRACTCHART_H
 8 
 9 #include <QWidget>
10 #include <QtCharts>
11 #include <QLabel>
12 
13 #include "chartdefine.h"
14 
15 class AbstractChart : public QWidget
16 {
17     Q_OBJECT
18 public:
19     explicit AbstractChart(QWidget* parent = nullptr);
20     virtual ~AbstractChart();
21 
22     virtual void setLabel(QString name);
23 signals:
24 
25 public slots:
26 
27 protected:
28     void initUI();
29 
30     virtual void initChartView();
31     virtual void initChart();
32     virtual void initAxis();
33     virtual void initSeries();
34 
35     virtual void combine();
36 
37 protected:
38     QLabel *m_pLabel = NULL;
39     QChart* m_chart;
40     QChartView* m_chartView;
41 };
42 
43 #endif // ABSTRACTCHART_H
View Code
复制代码

AbstractChart.cpp

复制代码
 1 #include "AbstractChart.h"
 2 #include <QVBoxLayout>
 3 
 4 AbstractChart::AbstractChart(QWidget* parent) : QWidget(parent)
 5 {
 6     m_pLabel = new QLabel(this);
 7     m_pLabel->setText(QStringLiteral("AbstractChart"));
 8     m_pLabel->setFont(C_legendFont);
 9     m_chart = new QChart;
10     m_chartView = new QChartView;    
11 }
12 
13 AbstractChart::~AbstractChart()
14 {
15 
16 }
17 
18 void AbstractChart::setLabel(QString name)
19 {
20     m_pLabel->setText(name);
21 }
22 
23 void AbstractChart::initUI()
24 {
25     initChartView();
26     initChart();
27     initAxis();
28     initSeries();
29 
30     combine();
31 
32     QVBoxLayout* layout = new QVBoxLayout;
33     layout->setContentsMargins(0, 0, 0, 0);
34     layout->setMargin(0);
35     layout->addWidget(m_pLabel);
36     layout->addWidget(m_chartView);
37 
38     this->setLayout(layout);
39     // zhujq
40     const QString C_AbstractChartQS = "QWidget{color:rgb(255, 255, 255); background:rgba(0, 0, 0, 0);}";
41     this->setStyleSheet(C_AbstractChartQS);
42 }
43 
44 void AbstractChart::initChartView()
45 {
46     m_chartView->setBackgroundBrush(C_chartBackgroundColor);// zhujq
47     m_chartView->setRenderHint(QPainter::Antialiasing);
48     m_chartView->setRenderHint(QPainter::NonCosmeticDefaultPen);
49 }
50 
51 void AbstractChart::initChart()
52 {
53     m_chart->setMargins(QMargins(0, 0, 0, 0));
54     m_chart->setBackgroundRoundness(0);
55     m_chart->setBackgroundBrush(C_chartBackgroundColor);// zhujq
56 //    m_chart->setBackgroundPen(
57 //    QPen pen;
58 //    pen.setColor(C_BackgroundColor);
59 //    pen.setWidthF(0.0);// zhujq
60 //    m_chart->setBackgroundPen(pen);// zhujq
61 //    m_chart->setBackgroundRoundness(8);//
62 
63 
64     m_chart->setAnimationOptions(QChart::NoAnimation);
65 
66     m_chart->legend()->setVisible(true);
67     m_chart->legend()->setAlignment(Qt::AlignBottom);
68     m_chart->legend()->setFont(C_legendFont);
69     m_chart->legend()->setLabelColor(C_legendFontColor);
70     m_chart->legend()->setBackgroundVisible(false);
71 }
72 
73 void AbstractChart::initAxis()
74 {
75 
76 }
77 
78 void AbstractChart::initSeries()
79 {
80 
81 }
82 
83 void AbstractChart::combine()
84 {
85 
86 }
View Code
复制代码

BarChart.h

复制代码
 1 /*
 2  * 功能:简单封装条形图(再次使用时可快速修改)
 3  * 作者:朱建强
 4  * 创建时间:2025-1-28
 5 */
 6 #ifndef BARCHART_H
 7 #define BARCHART_H
 8 
 9 #include "QObject"
10 #include "AbstractChart.h"
11 
12 class BarChart : public AbstractChart
13 {
14     Q_OBJECT
15 public:
16     enum BarDirection {horizontal, vertical};
17     explicit BarChart(BarDirection dir = vertical, QWidget* parent = nullptr);
18     void setCategoryAxisLabel(const QStringList& list);
19     void clearData();
20     void appendData(const QString& sliceName, QList<qreal> values, const QColor& color);
21     void setYAxisMaxValue(qreal max);
22     void toStyle1();
23     void toStyle2();
24 signals:
25 public slots:
26 private:
27     void initAxis() override;
28     void initSeries() override;
29     void combine() override;
30 private:
31     QAbstractBarSeries* m_series;
32     QBarCategoryAxis* m_axisX;
33     QValueAxis* m_axisY;
34     BarDirection m_barDirection;
35 };
36 
37 #endif // BARCHART_H
View Code
复制代码

BarChart.cpp

复制代码
  1 #include "BarChart.h"
  2 
  3 BarChart::BarChart(BarDirection dir, QWidget* parent) : AbstractChart(parent)
  4 {
  5     m_axisX = nullptr;
  6     m_axisY = nullptr;
  7     m_series = nullptr;
  8     m_barDirection = dir;
  9 
 10     initUI();
 11 }
 12 
 13 void BarChart::setCategoryAxisLabel(const QStringList& list)
 14 {
 15     Q_ASSERT(m_axisX);
 16     m_axisX->clear();
 17     m_axisX->append(list);
 18 }
 19 
 20 void BarChart::clearData()
 21 {
 22     Q_ASSERT(m_series);
 23     m_series->clear();
 24 }
 25 
 26 void BarChart::appendData(const QString& sliceName, QList<qreal> values, const QColor& color)
 27 {
 28     Q_ASSERT(m_series);
 29     QBarSet* barset = new QBarSet(sliceName);
 30     barset->append(values);
 31     barset->setColor(color);
 32     barset->setLabelFont(C_valueLabelFont);
 33     barset->setLabelColor(C_valueFontColor);
 34     m_series->append(barset);
 35 }
 36 
 37 void BarChart::setYAxisMaxValue(qreal max)
 38 {
 39     Q_ASSERT(m_axisX);
 40     int nStep = ceil(max / 4);
 41     m_axisY->setRange(0, nStep * 4);
 42 }
 43 
 44 void BarChart::toStyle1()
 45 {
 46     Q_ASSERT(m_chart);
 47     m_chart->legend()->setVisible(false);
 48 }
 49 
 50 void BarChart::toStyle2()
 51 {
 52     Q_ASSERT(m_axisY);
 53     m_axisY->setLabelsVisible(false);
 54 }
 55 
 56 void BarChart::initAxis()
 57 {
 58     m_axisY = new QValueAxis;
 59     m_axisY->setLineVisible(false);
 60     m_axisY->setLinePenColor(C_yAxisLineColor);
 61     m_axisY->setGridLineVisible(true);
 62     m_axisY->setGridLineColor(C_yAxisGridLineColor);
 63     m_axisY->setLabelsFont(C_yAxislabelsFont);
 64     m_axisY->setLabelsColor(C_yAxislabelsFontColor);
 65     m_axisY->setRange(0, 100);
 66     m_axisY->setTickCount(5);
 67     m_axisY->setLabelFormat("%d");
 68 
 69     m_axisX = new QBarCategoryAxis;
 70     m_axisX->setLineVisible(true);
 71     m_axisX->setLinePenColor(C_xAxisLineColor);
 72     m_axisX->setGridLineVisible(false);
 73     m_axisX->setGridLineColor(C_xAxisGridLineColor);
 74     m_axisX->setLabelsFont(C_xAxislabelsFont);
 75     m_axisX->setLabelsColor(C_xAxislabelsFontColor);
 76 }
 77 
 78 void BarChart::initSeries()
 79 {
 80     switch(m_barDirection)
 81     {
 82         case vertical:
 83             m_series = new QBarSeries;
 84             break;
 85         case horizontal:
 86             m_series = new QHorizontalBarSeries;
 87             break;
 88         default:
 89             m_series = new QBarSeries;
 90             break;
 91     }
 92 
 93     m_series->setLabelsVisible(true);
 94     m_series->setLabelsPosition(QAbstractBarSeries::LabelsCenter);
 95 }
 96 
 97 void BarChart::combine()
 98 {
 99     Q_ASSERT(m_axisX);
100     Q_ASSERT(m_axisY);
101     Q_ASSERT(m_series);
102     Q_ASSERT(m_chart);
103     Q_ASSERT(m_chartView);
104 
105     m_chartView->setChart(m_chart);
106 
107     m_chart->addSeries(m_series);
108 
109     switch(m_barDirection)
110     {
111         case vertical:
112             m_chart->addAxis(m_axisX, Qt::AlignBottom);
113             m_chart->addAxis(m_axisY, Qt::AlignLeft);
114             break;
115         case horizontal:
116             m_chart->addAxis(m_axisX, Qt::AlignLeft);
117             m_chart->addAxis(m_axisY, Qt::AlignBottom);
118             break;
119         default:
120             m_chart->addAxis(m_axisX, Qt::AlignBottom);
121             m_chart->addAxis(m_axisY, Qt::AlignLeft);
122             break;
123     }
124 
125     m_series->attachAxis(m_axisX);
126     m_series->attachAxis(m_axisY);
127 }
View Code
复制代码

C_V_AreaChart.h

复制代码
 1 /*
 2  * 功能:简单封装X轴为文本-Y轴为数值的面积图
 3  * 作者:朱建强
 4  * 创建时间:2025-1-28
 5 */
 6 #ifndef CVAREACHART_H
 7 #define CVAREACHART_H
 8 
 9 #include "AbstractChart.h"
10 
11 class C_V_AreaChart : public AbstractChart
12 {
13     Q_OBJECT
14 public:
15     explicit C_V_AreaChart(QWidget* parent = nullptr);
16 
17     /*
18      * 功能:清空数据项
19     */
20     void clearData();
21 
22     /*
23      * 功能:添加数据
24      * 输入:
25      *  sliceName:数据项名称
26      *  values:值
27      *  color:数据项代表色
28     */
29     void appendData(const QString& sliceName, QVector<float> values, const QColor& color);
30 
31     /*
32      * 功能:设置标签
33      * 输入:
34      *  sliceName:数据项名称
35      *  values:值
36      *  color:数据项代表色
37     */
38     void setCategoryLabels(const QStringList& categorys);
39 
40     /*
41      * 功能:设置Y轴最大值
42      * 输入:
43      *  max:最大值
44     */
45     void setMaxValue(int max);
46 
47 signals:
48 
49 public slots:
50 
51 private:
52     void initAxis() override;
53 
54     void combine() override;
55 
56 private:
57     QCategoryAxis* m_axisX;
58     QValueAxis* m_axisY;
59 };
60 
61 #endif // AREACHART_H
View Code
复制代码

C_V_AreaChart.cpp

复制代码
  1 #include "C_V_AreaChart.h"
  2 
  3 C_V_AreaChart::C_V_AreaChart(QWidget* parent) : AbstractChart(parent)
  4 {
  5     m_axisX = nullptr;
  6     m_axisY = nullptr;
  7 
  8     initUI();
  9 }
 10 
 11 void C_V_AreaChart::clearData()
 12 {
 13     Q_ASSERT(m_chart);
 14     m_chart->removeAllSeries();
 15 }
 16 
 17 void C_V_AreaChart::appendData(const QString& sliceName, QVector<float> values, const QColor& color)
 18 {
 19     QList<QPointF> upperPoints;
 20     QList<QPointF> lowerPoints;
 21 
 22     for(int i = 0; i < values.size(); ++i)
 23     {
 24         upperPoints << QPointF(i * 2 + 1, values[i]);
 25         lowerPoints << QPointF(i * 2 + 1, 0);
 26     }
 27 
 28     auto upperLineSeries = new QLineSeries;
 29     upperLineSeries->append(upperPoints);
 30 
 31     auto lowerLineSeries = new QLineSeries;
 32     lowerLineSeries->append(lowerPoints);
 33 
 34     auto series = new QAreaSeries;
 35     series->setName(sliceName);
 36     series->setColor(color);
 37     series->setUpperSeries(upperLineSeries);
 38     series->setLowerSeries(lowerLineSeries);
 39 
 40     Q_ASSERT(m_chart);
 41     m_chart->addSeries(series);
 42 
 43     series->attachAxis(m_axisX);
 44     series->attachAxis(m_axisY);
 45 }
 46 
 47 void C_V_AreaChart::setCategoryLabels(const QStringList& categorys)
 48 {
 49     int categorysCount = categorys.size();
 50 
 51     Q_ASSERT(m_axisX);
 52     m_axisX->append(" ", 0);
 53     for(int i = 0; i < categorysCount; ++i)
 54     {
 55         m_axisX->append(categorys[i], i * 2 + 1);
 56     }
 57     m_axisX->append(" ", categorysCount * 2);
 58 
 59     m_axisX->setRange(0, categorysCount * 2);
 60     m_axisX->setTickCount(categorysCount * 2);
 61 }
 62 
 63 void C_V_AreaChart::setMaxValue(int max)
 64 {
 65     m_axisY->setRange(0, max);
 66 }
 67 
 68 void C_V_AreaChart::initAxis()
 69 {
 70     m_axisX = new QCategoryAxis;
 71     m_axisX->setLineVisible(false);
 72     m_axisX->setLinePenColor(C_xAxisLineColor);
 73     m_axisX->setGridLineVisible(true);
 74     m_axisX->setGridLineColor(C_xAxisGridLineColor);
 75     m_axisX->setLabelsFont(C_xAxislabelsFont);
 76     m_axisX->setLabelsColor(C_xAxislabelsFontColor);
 77     m_axisX->setLabelsPosition(QCategoryAxis::AxisLabelsPositionOnValue);
 78     m_axisX->setTickCount(5);
 79 
 80     m_axisY = new QValueAxis;
 81     m_axisY->setLineVisible(false);
 82     m_axisY->setLinePenColor(C_yAxisLineColor);
 83     m_axisY->setGridLineVisible(true);
 84     m_axisY->setGridLineColor(C_yAxisGridLineColor);
 85     m_axisY->setLabelsFont(C_yAxislabelsFont);
 86     m_axisY->setLabelsColor(C_yAxislabelsFontColor);
 87     m_axisY->setRange(0, 1);
 88     m_axisY->setTickCount(5);
 89 }
 90 
 91 void C_V_AreaChart::combine()
 92 {
 93     Q_ASSERT(m_chart);
 94     Q_ASSERT(m_chartView);
 95 
 96     m_chart->addAxis(m_axisX, Qt::AlignBottom);
 97     m_chart->addAxis(m_axisY, Qt::AlignLeft);
 98 
 99     m_chartView->setChart(m_chart);
100 }
View Code
复制代码

CustomWidget.h

复制代码
 1 /*
 2  * 功能:将m_widget上放在另一个Widget中,使得m_widget可以实现顶级Widget不能实现的功能。如部分QSS在顶层widget不生效。
 3 */
 4 #ifndef CUSTOMWIDGET_H
 5 #define CUSTOMWIDGET_H
 6 
 7 #include <QWidget>
 8 
 9 class CustomWidget : public QWidget
10 {
11     Q_OBJECT
12 public:
13     explicit CustomWidget(QWidget* parent = nullptr);
14 
15     void setLayout(QLayout* layout);
16 
17 signals:
18 
19 public slots:
20 
21 private:
22     QWidget* m_widget;
23 };
24 
25 #endif // CUSTOMWIDGET_H
View Code
复制代码

CustomWidget.cpp

复制代码
 1 #include "CustomWidget.h"
 2 #include <QHBoxLayout>
 3 
 4 CustomWidget::CustomWidget(QWidget* parent) : QWidget(parent)
 5 {
 6     m_widget = new QWidget;
 7 
 8     QHBoxLayout* layout = new QHBoxLayout();
 9     layout->addWidget(m_widget);
10     layout->setContentsMargins(0, 0, 0, 0);
11     layout->setSpacing(0);
12 
13     QWidget::setLayout(layout);
14     this->setWindowFlags(Qt::FramelessWindowHint);
15     this->setAttribute(Qt::WA_TranslucentBackground);
16 }
17 
18 void CustomWidget::setLayout(QLayout* layout)
19 {
20     m_widget->setLayout(layout);
21 }
View Code
复制代码

LineChart.h

复制代码
 1 /*
 2  * 功能:简单封装线性图(再次使用时可快速修改)
 3  * 作者:朱建强
 4  * 创建时间:2025-1-28
 5 */
 6 #ifndef LINECHART_H
 7 #define LINECHART_H
 8 
 9 #include "AbstractChart.h"
10 
11 class LineChart : public AbstractChart
12 {
13     Q_OBJECT
14 public:
15     explicit LineChart(QWidget* parent = nullptr);
16     void clearData();
17     void appendData(const QString& sliceName, const QVector<float>& data, const QColor& color);
18     void setCategoryLabels(const QStringList& categorys);
19 signals:
20 public slots:
21 private:
22     void initChart() override;
23     void initAxis() override;
24 
25     void combine() override;
26 private:
27     QCategoryAxis* m_axisX;
28     QValueAxis* m_axisY;
29 };
30 
31 #endif // LINECHART_H
View Code
复制代码

LineChart.cpp

复制代码
  1 #include "LineChart.h"
  2 #include <QString>
  3 
  4 LineChart::LineChart(QWidget* parent) : AbstractChart(parent)
  5 {
  6     m_axisX = nullptr;
  7     m_axisY = nullptr;
  8 
  9     initUI();
 10 }
 11 
 12 void LineChart::clearData()
 13 {
 14     Q_ASSERT(m_chart);
 15     m_chart->removeAllSeries();
 16 }
 17 
 18 void LineChart::appendData(const QString& sliceName, const QVector<float>& data, const QColor& color)
 19 {
 20     QList<QPointF> upperPoints;
 21 
 22     for(int i = 0; i < data.size(); ++i)
 23     {
 24         QString str = QString::number(data[i], 'f', 2);
 25         upperPoints << QPointF(i * 2 + 1, str.toDouble());
 26     }
 27 
 28     QPen pen;
 29     pen.setWidth(2);
 30     pen.setColor(C_BackgroundColor);
 31 
 32     auto upperLineSeries = new QLineSeries;
 33     upperLineSeries->setName(sliceName);
 34     upperLineSeries->append(upperPoints);
 35     upperLineSeries->setPen(pen);
 36     upperLineSeries->setPointLabelsFormat("(@yPoint)");
 37     upperLineSeries->setPointsVisible(true);
 38     upperLineSeries->setPointLabelsVisible(true);
 39     upperLineSeries->setPointLabelsFont(C_valueLabelFont);
 40     upperLineSeries->setPointLabelsColor(C_BackgroundColor);
 41     upperLineSeries->setColor(C_BackgroundColor);// zhujq-a
 42 //    upperLineSeries->setb
 43 
 44     Q_ASSERT(m_chart);
 45     m_chart->addSeries(upperLineSeries);
 46 
 47     upperLineSeries->attachAxis(m_axisX);
 48     upperLineSeries->attachAxis(m_axisY);
 49 }
 50 
 51 void LineChart::setCategoryLabels(const QStringList& categorys)
 52 {
 53     int categorysCount = categorys.size();
 54 
 55     Q_ASSERT(m_axisX);
 56     m_axisX->append(" ", 0);
 57     for(int i = 0; i < categorysCount; ++i)
 58     {
 59         m_axisX->append(categorys[i], i * 2 + 1);
 60     }
 61     m_axisX->append(" ", categorysCount * 2);
 62 
 63     m_axisX->setRange(0, categorysCount * 2);
 64     m_axisX->setTickCount(categorysCount * 2);
 65 }
 66 
 67 void LineChart::initChart()
 68 {
 69     AbstractChart::initChart();
 70 
 71     m_chart->legend()->setVisible(false);
 72 }
 73 
 74 void LineChart::initAxis()
 75 {
 76     m_axisX = new QCategoryAxis;
 77     m_axisX->setLineVisible(false);
 78     m_axisX->setLinePenColor(C_xAxisLineColor);
 79     m_axisX->setGridLineVisible(false);
 80     m_axisX->setGridLineColor(C_xAxisGridLineColor);
 81     m_axisX->setLabelsFont(C_xAxislabelsFont);
 82     m_axisX->setLabelsColor(C_xAxislabelsFontColor);
 83     m_axisX->setLabelsPosition(QCategoryAxis::AxisLabelsPositionOnValue);
 84     m_axisX->setTickCount(5);
 85 
 86     m_axisY = new QValueAxis;
 87     m_axisY->setLineVisible(false);
 88     m_axisY->setLinePenColor(C_yAxisLineColor);
 89     m_axisY->setGridLineVisible(true);
 90     m_axisY->setGridLineColor(C_yAxisGridLineColor);
 91     m_axisY->setLabelsFont(C_yAxislabelsFont);
 92     m_axisY->setLabelsColor(C_yAxislabelsFontColor);
 93     m_axisY->setRange(0, 1);
 94     m_axisY->setTickCount(5);
 95 }
 96 
 97 void LineChart::combine()
 98 {
 99     Q_ASSERT(m_chart);
100     Q_ASSERT(m_chartView);
101 
102     m_chart->addAxis(m_axisX, Qt::AlignBottom);
103     m_chart->addAxis(m_axisY, Qt::AlignLeft);
104 
105     m_chartView->setChart(m_chart);
106 }
View Code
复制代码

PieChart.h

复制代码
 1 /*
 2  * 功能:简单封装饼状图(再次使用时可快速修改)
 3  * 作者:朱建强
 4  * 创建时间:2025-1-28
 5 */
 6 
 7 #ifndef PIECHART_H
 8 #define PIECHART_H
 9 
10 #include "AbstractChart.h"
11 
12 class PieChart : public AbstractChart
13 {
14     Q_OBJECT
15 public:
16     explicit PieChart(QWidget* parent = nullptr);
17     void clearData();
18     void appendData(const QString& sliceName, float value, const QColor& color);
19 signals:
20 public slots:
21 private:
22     void initChart() override;
23     void initSeries() override;
24     void combine() override;
25 private:
26     QPieSeries* m_series;
27 };
28 
29 #endif // PIECHART_H
View Code
复制代码

PieChart.cpp

复制代码
 1 #include "PieChart.h"
 2 #include <QVBoxLayout>
 3 
 4 PieChart::PieChart(QWidget* parent) : AbstractChart(parent)
 5 {
 6     m_series = nullptr;
 7 
 8     initUI();
 9 }
10 
11 void PieChart::clearData()
12 {
13     Q_ASSERT(m_series);
14     m_series->clear();
15 }
16 
17 void PieChart::appendData(const QString& sliceName, float value, const QColor& color)
18 {
19     QPieSlice* slice = new QPieSlice();
20     slice->setValue(value);
21 
22     slice->setColor(color);
23 
24     slice->setLabel(QString("%1%2%").arg(sliceName).arg(value));
25     slice->setLabelFont(C_valueLabelFont);
26     slice->setLabelColor(C_valueFontColor);
27     slice->setLabelVisible(true);
28     slice->setLabelPosition(QPieSlice::LabelInsideHorizontal);
29 
30     slice->setBorderColor(C_BackgroundColor);
31     slice->setBorderWidth(1);
32 
33     Q_ASSERT(m_series);
34     m_series->append(slice);
35 }
36 
37 void PieChart::initChart()
38 {
39     AbstractChart::initChart();
40 
41     m_chart->legend()->setVisible(false);
42 }
43 
44 void PieChart::initSeries()
45 {
46     m_series = new QPieSeries();
47     m_series->setPieSize(1);
48     m_series->setHoleSize(0.4);
49 }
50 
51 void PieChart::combine()
52 {
53     Q_ASSERT(m_chart);
54     Q_ASSERT(m_chartView);
55 
56     m_chart->addSeries(m_series);
57     m_chartView->setChart(m_chart);
58 }
View Code
复制代码

RadarChart.h

复制代码
  1 /*
  2  * 功能:雷达图
  3  * 作者:朱建强
  4  * 创建时间:2025-1-28
  5 */
  6 #ifndef RADARCHART_H
  7 #define RADARCHART_H
  8 
  9 #include <QWidget>
 10 #include <QList>
 11 #include <QMap>
 12 
 13 #define  PI M_PI
 14 
 15 class RadarChart : public QWidget
 16 {
 17     Q_OBJECT
 18 public:
 19     enum RadarType
 20     {
 21         Radar,              // 雷达图
 22         Spider              // 蛛网图
 23     };
 24 
 25     enum PointTpye
 26     {
 27         ScatterPoints,      // 散点图
 28         LinePoints          // 连线图
 29     };
 30 
 31     struct RadarChartItem
 32     {
 33         QString legendName;         // 数据名称(id)
 34         QVector<float> values;      // 数值
 35         QColor color;               // 绘制颜色
 36     };
 37 
 38 
 39     typedef QMap<QString, RadarChartItem> RadarChartItems;
 40 
 41     explicit RadarChart(QWidget* parent = nullptr);
 42 
 43     void setRadarType(RadarType radartype);                 // 设置图形类型
 44     void setPointTpye(PointTpye pointstype);                // 设置点类型
 45     void setRadialAxisLabelVisible(bool visible);           // 设置是否显示刻度标签
 46     void setPeripheryLabelVisible(bool visible);            // 设置是否显示外围标签
 47     void setLegendVisible(bool visible);                    // 设置是否显示图例
 48 
 49     void setTitle(const QString& title);                    // 设置标题
 50     void setTitleFont(const QFont& titleFont);              // 设置标题字体
 51     void setTitleColor(const QColor& titleColor);           // 设置标题字体颜色
 52 
 53     void setPeripheryLabel(const QStringList& labels);      // 设置外围标签(其数量代表放射线数量)
 54     void setPeripheryLabelFont(const QFont& font);          // 设置外围标签字体
 55     void setPeripheryLabelColor(const QColor& color);       // 设置外围标签字体颜色
 56 
 57     void setData(const RadarChartItem& item);               // 设置单个数据
 58     void setData(const RadarChartItems& items);             // 设置多个数据
 59 
 60     void setTickCount(int tickCount);                       // 设置刻度数量
 61     void setAxisRange(float min, float max);                // 设置刻度范围
 62     void setRadialLineColor(const QColor& color);           // 设置刻度线颜色
 63     void setRadialLabelColor(const QColor& color);          // 设置刻度标签字体颜色
 64     void setLoopLineColor(const QColor& color);             // 设置环线颜色
 65 
 66     void setbackgroundColor(const QColor& color);           // 设置背景颜色
 67 
 68 protected:
 69     void paintEvent(QPaintEvent* event) override;
 70     void mouseMoveEvent(QMouseEvent* e) override;
 71     void mousePressEvent(QMouseEvent* event) override;
 72 
 73 private:
 74     void init();
 75     void drawTitle(QPainter* painter);
 76     void drawRadarBase(QPainter* painter);      // 绘制雷达基础图
 77     void drawRadar(QPainter* painter);          // 绘制基础图(圆形)
 78     void drawSpider(QPainter* painter);         // 绘制基础图(多边形)
 79     void drawRadialLabel(QPainter* painter);    // 绘制刻度
 80     void drawAngularLabel(QPainter* painter);   // 绘制转角标签
 81     void drawData(QPainter* painter);           // 绘制数据
 82     void drawScatterPoints(QPainter* painter);  // 绘制数据(散点)
 83     void drawLinePoints(QPainter* painter);     // 绘制数据(连线)
 84     void drawLegends(QPainter* painter);        // 绘制图例
 85 
 86 private:
 87     RadarType m_radartype;                      // 图形类型
 88     PointTpye m_pointstype;                     // 点类型
 89     bool m_showRadialAxisLabel;                 // 是否显示刻度标签
 90     bool m_showPeripheryLabel;                  // 是否显示外围标签
 91     bool m_showLegend;                          // 是否显示图例
 92 
 93     int m_radialCount;                          // 外围标签数量(放射线数量)
 94     QStringList m_peripheryLabels;              // 外围标签
 95     RadarChartItems m_itemVec;                  // 数据项集合
 96     QMap<QString, QPainterPath> m_PainterPathMap;// 每项数据所构成的闭合路径集(判断鼠标位置是否在其范围内)
 97     QString m_currentSelected;                   // 当前选中的项名称
 98 
 99     QString m_title;                            // 标题
100     float m_titleHeight;                        // 标题高度
101 
102     int m_tickCount;                            // 刻度数量(环形圈数量)
103     float m_axis_min;                           // 刻度最小值
104     float m_axis_max;                           // 刻度最大值
105 
106     QFont m_titleFont;                          // 标题字体
107     QFont m_peripherylabelFont;                 // 外围标签字体
108     QFont m_legendFont;                         // 图例字体
109 
110     QColor m_backgroundColor;                   // 背景颜色
111     QColor m_titleColor;                        // 标题字体颜色
112     QColor m_loopLineColor;                     // 放射线颜色
113     QColor m_radialLineColor;                   // 环形线颜色
114     QColor m_peripheryLabelColor;               // 刻度标签颜色
115     QColor m_radialLabelColor;                  // 外围标签字体颜色
116     QColor m_legendColor;                       // 图例字体颜色
117 
118     QPointF m_centerPoint;                      // 中心点坐标
119     float m_maxRadius;                          // 最大半径
120     float m_step;                               // 环间距(单位:像素)
121     float m_rayAngle;                           // 放射线之间的夹角
122 };
123 
124 #endif // RADARCHART_H
View Code
复制代码

RadarChart.cpp

复制代码
  1 #include "RadarChart.h"
  2 #include "chartdefine.h"
  3 #include <QMouseEvent>
  4 #include <QPainter>
  5 #include <qmath.h>
  6 
  7 const float C_lineWidth = 0.4;      // 线宽
  8 const int C_colorAlpha = 100;       // 数据颜色透明度(高亮时是其200)
  9 
 10 RadarChart::RadarChart(QWidget* parent) : QWidget(parent)
 11 {
 12     init();
 13 }
 14 
 15 void RadarChart::setRadarType(RadarType radartype)
 16 {
 17     m_radartype = radartype;
 18     this->update();
 19 }
 20 
 21 void RadarChart::setPointTpye(PointTpye pointstype)
 22 {
 23     m_pointstype = pointstype;
 24     this->update();
 25 }
 26 
 27 void RadarChart::setRadialAxisLabelVisible(bool visible)
 28 {
 29     m_showRadialAxisLabel = visible;
 30     this->update();
 31 }
 32 
 33 void RadarChart::setPeripheryLabelVisible(bool visible)
 34 {
 35     m_showPeripheryLabel = visible;
 36     this->update();
 37 }
 38 
 39 void RadarChart::setLegendVisible(bool visible)
 40 {
 41     m_showLegend = visible;
 42     this->update();
 43 }
 44 
 45 void RadarChart::setTitle(const QString& title)
 46 {
 47     m_title = title;
 48 
 49     this->update();
 50 }
 51 
 52 void RadarChart::setTitleFont(const QFont& titleFont)
 53 {
 54     m_titleFont = titleFont;
 55     QFontMetricsF fontMetrics(m_titleFont);
 56     float titleHeight = fontMetrics.height();
 57     float w = width();
 58     float h = height() - titleHeight;
 59 
 60     m_maxRadius = qMin(w, h) / 2;
 61     m_centerPoint = QPointF(w / 2, h / 2 + m_titleHeight);
 62     this->update();
 63 }
 64 
 65 void RadarChart::setTitleColor(const QColor& titleColor)
 66 {
 67     m_titleColor = titleColor;
 68     this->update();
 69 }
 70 
 71 void RadarChart::setPeripheryLabel(const QStringList& labels)
 72 {
 73     m_peripheryLabels = labels;
 74     m_radialCount = labels.count();
 75     m_rayAngle = PI * 2 / m_radialCount;
 76     this->update();
 77 }
 78 
 79 void RadarChart::setPeripheryLabelFont(const QFont& font)
 80 {
 81     m_peripherylabelFont = font;
 82     this->update();
 83 }
 84 
 85 void RadarChart::setPeripheryLabelColor(const QColor& color)
 86 {
 87     m_peripheryLabelColor = color;
 88     this->update();
 89 }
 90 
 91 void RadarChart::setData(const RadarChart::RadarChartItem& item)
 92 {
 93     m_itemVec.clear();
 94     m_itemVec[item.legendName] = item;
 95     m_currentSelected = item.legendName;
 96     this->update();
 97 }
 98 
 99 void RadarChart::setData(const RadarChartItems& items)
100 {
101     m_itemVec = items;
102     this->update();
103 }
104 
105 void RadarChart::setTickCount(int tickCount)
106 {
107     m_tickCount = tickCount;
108     this->update();
109 }
110 
111 void RadarChart::setAxisRange(float min, float max)
112 {
113     m_axis_min = min;
114     m_axis_max = max;
115     this->update();
116 }
117 
118 void RadarChart::setLoopLineColor(const QColor& color)
119 {
120     m_loopLineColor = color;
121     this->update();
122 }
123 
124 void RadarChart::setRadialLineColor(const QColor& color)
125 {
126     m_radialLineColor = color;
127     this->update();
128 }
129 
130 void RadarChart::setRadialLabelColor(const QColor& color)
131 {
132     m_radialLabelColor = color;
133     this->update();
134 }
135 
136 void RadarChart::setbackgroundColor(const QColor& color)
137 {
138     m_backgroundColor = color;
139     this->update();
140 }
141 
142 void RadarChart::paintEvent(QPaintEvent* event)
143 {
144     QPainter painter(this);
145     // 防止“锯齿”现象的出现
146     painter.setRenderHint(QPainter::Antialiasing);          // 告诉绘图引擎应该在可能的情况下进行边的反锯齿绘制
147     painter.setRenderHint(QPainter::SmoothPixmapTransform); // 使用平滑的pixmap变换算法(双线性插值算法),而不是近邻插值算
148     painter.setRenderHint(QPainter::TextAntialiasing);      // 尽可能的情况下文字的反锯齿绘制
149 
150     // 添加背景颜色
151     painter.save();
152     painter.setBrush(m_backgroundColor);
153     QPen pen;
154     pen.setColor(m_backgroundColor);
155     painter.setPen(pen);
156     painter.drawRect(this->rect());
157     painter.restore();
158 
159     QFontMetricsF fontMetrics(m_peripherylabelFont);
160     float labelHeight = fontMetrics.height();
161 
162     m_maxRadius = qMin(width(), height()) / 2 - labelHeight * 2;
163     m_centerPoint = QPointF(width() / 2, height() / 2);
164     m_step = m_maxRadius / (m_tickCount + 1);       // 需要空出外围标签位置
165 
166     // 绘制图
167     drawTitle(&painter);
168     drawRadarBase(&painter);
169     drawRadialLabel(&painter);
170     drawAngularLabel(&painter);
171     drawData(&painter);
172     drawLegends(&painter);
173 
174     QWidget::paintEvent(event);
175 }
176 
177 void RadarChart::mouseMoveEvent(QMouseEvent* e)
178 {
179     //    bool hoverItem = false;
180     //    for(auto it=m_PainterPathMap.begin(); it!=m_PainterPathMap.end(); ++it)
181     //    {
182     //        if(it.value().contains(e->pos()))
183     //        {
184     //            hoverItem = true;
185     //            currentSelected = it.key();
186     //            break;
187     //        }
188     //    }
189 
190     //    if(!hoverItem)
191     //    {
192     //        currentSelected = "";
193     //    }
194     //    this->update();
195 
196     QWidget::mouseMoveEvent(e);
197 }
198 
199 void RadarChart::mousePressEvent(QMouseEvent* event)
200 {
201     bool hoverItem = false;
202     //左击
203     if(event->button() == Qt::LeftButton)
204     {
205         for(auto it = m_PainterPathMap.begin(); it != m_PainterPathMap.end(); ++it)
206         {
207             if(it.value().contains(event->pos()))
208             {
209                 hoverItem = true;
210                 m_currentSelected = it.key();
211                 break;
212             }
213         }
214 
215         if(!hoverItem && m_PainterPathMap.size() > 1)
216         {
217             m_currentSelected = "";
218         }
219         this->update();
220     }
221 
222     QWidget::mousePressEvent(event);
223 }
224 
225 void RadarChart::init()
226 {
227     m_radartype = Spider;
228     m_pointstype = LinePoints;
229     m_showRadialAxisLabel = true;
230     m_showPeripheryLabel = true;
231     m_showLegend = true;
232 
233     m_radialCount = 0;
234 
235     m_tickCount = 5;
236     m_axis_min = 0;
237     m_axis_max = 10;
238 
239     m_titleFont = QFont("Arial", 12, QFont::Bold);
240     m_peripherylabelFont = QFont("Arial", 9);
241     m_legendFont = QFont("Arial", 9, QFont::Bold, true);
242 
243     m_backgroundColor = Qt::white;
244     m_titleColor = Qt::white;
245     m_loopLineColor = Qt::white;
246     m_radialLineColor = Qt::white;
247     m_peripheryLabelColor = Qt::white;
248     m_radialLabelColor = Qt::white;
249     m_legendColor = Qt::white;
250 
251     m_titleFont = C_titleFont;
252     m_peripherylabelFont = C_yAxislabelsFont;
253     m_legendFont = C_legendFont;
254 
255     m_backgroundColor = C_chartBackgroundColor;
256     m_titleColor = C_titleFontColor;
257     m_loopLineColor = C_xAxisGridLineColor;
258     m_radialLineColor = C_yAxisLineColor;
259 }
260 
261 void RadarChart::drawTitle(QPainter* painter)
262 {
263     painter->save();
264 
265     QPen pen;
266     pen.setColor(m_titleColor);
267     painter->setPen(pen);
268     painter->setFont(m_titleFont);
269 
270     QFontMetricsF fontMetrics(m_titleFont);
271     float strWidth = fontMetrics.width(m_title);
272     float strHeight = fontMetrics.height();
273 
274     float cur_x = width() / 2 - strWidth / 2;
275     float cur_y = 10 + strHeight;
276 
277     painter->drawText(cur_x, cur_y, m_title);
278 
279     painter->restore();
280 }
281 
282 void RadarChart::drawRadarBase(QPainter* painter)
283 {
284     switch(m_radartype)
285     {
286         case Radar:
287             drawRadar(painter);
288             break;
289         case Spider:
290             drawSpider(painter);
291             break;
292         default:
293             drawRadar(painter);
294             break;
295     }
296 }
297 
298 void RadarChart::drawRadar(QPainter* painter)
299 {
300     painter->save();
301 
302     // 绘制环线
303     QPen pen;
304     pen.setWidthF(C_lineWidth);
305     pen.setColor(m_loopLineColor);
306     painter->setPen(pen);
307 
308     QPainterPath loopPath;
309     float curRadius = 0;
310     for(int i = 0; i < m_tickCount; ++i)
311     {
312         curRadius = m_step * (i + 1);
313         loopPath.addEllipse(m_centerPoint, curRadius, curRadius);
314     }
315     painter->drawPath(loopPath);
316 
317     // 绘制中心到边缘的直线(直线数量等于外环标签数量)
318     pen.setColor(m_radialLineColor);
319     painter->setPen(pen);
320 
321     QPointF point;
322     float angle = PI * 2 / m_radialCount;       // 线之间的夹角
323     float currentAngle = 0;                     // 当前线夹角
324     for(int i = 0 ; i < m_radialCount; ++i)
325     {
326         currentAngle = angle * i;
327         point.setX(m_centerPoint.x() + m_maxRadius * qSin(currentAngle));
328         point.setY(m_centerPoint.y() - m_maxRadius * qCos(currentAngle));
329         painter->drawLine(m_centerPoint, point);
330     }
331 
332     painter->restore();
333 }
334 
335 void RadarChart::drawSpider(QPainter* painter)
336 {
337     painter->save();
338 
339     // 绘制环线
340     QPen pen;
341     QPointF point;
342     QPointF* points = new QPointF[m_radialCount + 1];  // 转角点集合
343 
344     pen.setWidthF(C_lineWidth);
345     pen.setColor(m_loopLineColor);
346     painter->setPen(pen);
347 
348     float curRadius = 0;                        // 当前半径
349     float currentAngle = 0;                     // 当前线夹角
350     for(int j = 0 ; j < m_tickCount; ++j)
351     {
352         curRadius = m_step * (j + 1);
353         for(int i = 0 ; i < m_radialCount; ++i)
354         {
355             currentAngle = m_rayAngle * i;
356             point.setX(m_centerPoint.x() + curRadius * qSin(currentAngle));
357             point.setY(m_centerPoint.y() - curRadius * qCos(currentAngle));
358             points[i] = point;
359         }
360         points[m_radialCount] = points[0];
361         painter->drawPolyline(points, m_radialCount + 1);
362     }
363 
364     // 绘制中心点到边缘的直线
365     pen.setColor(m_radialLineColor);
366     painter->setPen(pen);
367 
368     for(int i = 0 ; i < m_radialCount; ++i)
369     {
370         painter->drawLine(m_centerPoint, points[i]);
371     }
372 
373     delete []points;
374     painter->restore();
375 }
376 
377 void RadarChart::drawRadialLabel(QPainter* painter)
378 {
379     if(!m_showRadialAxisLabel)
380     {
381         return;
382     }
383 
384     painter->save();
385 
386     // 绘制刻度
387     QPen pen;
388     pen.setColor(m_radialLabelColor);
389     painter->setPen(pen);
390     painter->setFont(m_peripherylabelFont);
391 
392     float dY = (m_axis_max - m_axis_min) / (m_tickCount);   // 刻度间隔值
393     float offset = 20;
394     if(m_step < 40)
395     {
396         offset = m_step / 2;
397     }
398     QPointF point;
399     float curRadius = 0;
400     QString strlabel;
401     for(int j = 0 ; j < m_tickCount + 1; ++j)
402     {
403         curRadius = m_step * j;
404         point.setX(m_centerPoint.x() + 2);
405         point.setY(m_centerPoint.y() - curRadius + offset);
406         strlabel = QString::number((m_axis_min + dY * j), 'f', 2);
407         painter->drawText(point, strlabel);
408     }
409 
410     painter->restore();
411 }
412 
413 void RadarChart::drawAngularLabel(QPainter* painter)
414 {
415     if(!m_showPeripheryLabel)
416     {
417         return;
418     }
419 
420     painter->save();
421 
422     QPen pen;
423     pen.setColor(m_peripheryLabelColor);
424     painter->setPen(pen);
425     painter->setFont(m_peripherylabelFont);
426 
427     QFontMetricsF fontMetrics(m_peripherylabelFont);     // 计算给定字体的字符和字符串的大小
428     float textWidth, textHeight;
429 
430     float cur_x, cur_y;
431     float currentAngle = 0;
432     float radius = m_maxRadius * m_tickCount / (m_tickCount + 1) + 5;
433     QString text;
434     for(int i = 0; i < m_radialCount; ++i)
435     {
436         currentAngle = m_rayAngle * i;
437         text = m_peripheryLabels.at(i);
438         // 标签文本绘制起始位置
439         cur_x = m_centerPoint.x() + radius * qSin(currentAngle);
440         cur_y = m_centerPoint.y() - radius * qCos(currentAngle);
441 
442         textWidth = fontMetrics.width(text);
443         textHeight = fontMetrics.height();
444         // 若选中则添加值
445         if(!m_currentSelected.isEmpty() && i < m_itemVec.value(m_currentSelected).values.size())
446         {
447             textHeight *= 2;
448             text += QString("\n(%1)").arg(m_itemVec.value(m_currentSelected).values.at(i));
449         }
450 
451         // 根据当前点与中心点的坐标调整标签绘制的位置
452         if(abs(cur_x - m_centerPoint.x()) < 0.0001)           //cur_x == x0
453         {
454             cur_x -= textWidth / 2;
455         }
456         else
457         {
458             if(cur_x < m_centerPoint.x())
459             {
460                 cur_x = cur_x - textWidth;
461             }
462         }
463 
464         if(abs(cur_y - m_centerPoint.y()) < 0.0001)           //cur_y == y0
465         {
466             cur_y += textHeight / 2;
467         }
468         else
469         {
470             if(cur_y > m_centerPoint.y())
471             {
472                 cur_y -= textHeight * qCos(currentAngle);
473             }
474             else if(cur_y < m_centerPoint.y())
475             {
476                 cur_y = cur_y - textHeight * qCos(currentAngle) + textHeight;
477             }
478         }
479         cur_y -= textHeight;
480 
481         QRectF labelRect = QRectF(cur_x, cur_y, textWidth, textHeight);
482         painter->drawText(labelRect, Qt::AlignCenter | Qt::TextWordWrap, text);
483     }
484 
485     painter->restore();
486 }
487 
488 void RadarChart::drawData(QPainter* painter)
489 {
490     switch(m_pointstype)
491     {
492         case ScatterPoints:
493             drawScatterPoints(painter);
494             break;
495         case LinePoints:
496             drawLinePoints(painter);
497             break;
498         default:
499             drawLinePoints(painter);
500             break;
501     }
502 }
503 
504 void RadarChart::drawScatterPoints(QPainter* painter)
505 {
506     painter->save();
507 
508     QPointF point;
509     QVector<float> values;
510     QColor color;
511     float currentAngle = 0;                                         // 当前线与x轴夹角
512     float radius = m_tickCount * m_maxRadius / (m_tickCount + 1);
513     float step = 1.0 * radius / (m_axis_max - m_axis_min);
514     float valueLength = 0;
515 
516     for(auto it = m_itemVec.begin(); it != m_itemVec.end(); ++it)
517     {
518         RadarChartItem item = it.value();
519         values = item.values;
520         color = it->color;
521         color.setAlpha(150);
522 
523         painter->setPen(color);
524         painter->setBrush(color);
525 
526         for(int i = 0; i < values.size() && i < m_radialCount; i++)
527         {
528             currentAngle = m_rayAngle * i;
529             valueLength = values.at(i) * step;
530             point.setX(m_centerPoint.x() + valueLength * qSin(currentAngle));
531             point.setY(m_centerPoint.y() - valueLength * qCos(currentAngle));
532 
533             painter->drawEllipse(point, 4, 4);
534         }
535     }
536 
537     painter->restore();
538 }
539 
540 void RadarChart::drawLinePoints(QPainter* painter)
541 {
542     painter->save();
543 
544     QPen pen;
545     QPointF point;
546     QVector<float> values;
547     QColor color;
548     float currentAngle = 0;
549     float radius = m_tickCount * m_maxRadius / (m_tickCount + 1);
550     float step = 1.0 * radius / (m_axis_max - m_axis_min);
551 
552     for(auto it = m_itemVec.begin(); it != m_itemVec.end(); ++it)
553     {
554         QPolygonF polygon;
555         RadarChartItem item = it.value();
556         values = item.values;
557         color = it->color;
558 
559         for(int i = 0; i < values.size() && i < m_radialCount; i++)
560         {
561             currentAngle = m_rayAngle * i;
562             float valueLength = values.at(i) * step;
563 
564             if(i <= m_radialCount)
565             {
566                 point.setX(m_centerPoint.x() + valueLength * qSin(currentAngle));
567                 point.setY(m_centerPoint.y() - valueLength * qCos(currentAngle));
568             }
569             else
570             {
571                 point = m_centerPoint;
572             }
573 
574             polygon.append(point);
575 
576             pen.setWidthF(C_lineWidth * 2);
577             color.setAlpha(150);
578             pen.setColor(color);
579 
580             painter->setPen(pen);
581             painter->drawPoint(point);
582         }
583 
584         QPainterPath painterPath;
585         painterPath.addPolygon(polygon);
586         painterPath.closeSubpath();
587         m_PainterPathMap[it->legendName] = painterPath;
588 
589         pen.setWidthF(C_lineWidth);
590         if(it->legendName == m_currentSelected)
591         {
592             pen.setWidthF(C_lineWidth * 10);
593         }
594         //线条色
595         color.setAlpha(255);
596         pen.setColor(color);
597         painter->setPen(pen);
598         painter->drawPath(painterPath);
599 
600         //填充色
601         if(it->legendName == m_currentSelected)
602         {
603             color.setAlpha(C_colorAlpha * 2);
604         }
605         else
606         {
607             color.setAlpha(C_colorAlpha);
608         }
609         painter->fillPath(painterPath, color);
610     }
611 
612     painter->restore();
613 }
614 
615 void RadarChart::drawLegends(QPainter* painter)
616 {
617     if(!m_showLegend)
618     {
619         return;
620     }
621     painter->save();
622 
623     QPen pen;
624     pen.setColor(m_legendColor);
625     painter->setPen(pen);
626     painter->setFont(m_legendFont);
627     QFontMetrics fm = painter->fontMetrics();
628 
629     // 获取最大名称长度
630     int maxWidth = 0;
631     for(auto it = m_itemVec.begin(); it != m_itemVec.end(); ++it)
632     {
633         int w = fm.width(it.value().legendName);
634         if(w > maxWidth)
635         {
636             maxWidth = w;
637         }
638     }
639     maxWidth += 5;
640 
641     // 绘制图例
642     int distance = 10;             // 与边界的距离
643     int distance2 = 5;             // 文字与颜色块距离
644     int distance3 = 5;             // 图例与图例距离
645     int colorSide = fm.height();   // 颜色块边长
646     float cur_x = 0;
647     float cur_y = distance;
648     int textWidth = 0;
649     int textHeight = 0;
650     for(auto it = m_itemVec.begin(); it != m_itemVec.end(); ++it)
651     {
652         RadarChartItem item = it.value();
653         textWidth = fm.width(item.legendName) + 5;
654         textHeight = fm.height();
655         cur_x = width() - distance - maxWidth - distance2;
656 
657         QRectF legendNameRect = QRectF(cur_x, cur_y, textWidth, textHeight);
658         cur_x -= colorSide;
659         QRectF legendColorRect = QRectF(cur_x, cur_y, colorSide, colorSide);
660         cur_y += textHeight + distance3;
661 
662         painter->drawText(legendNameRect, Qt::AlignLeft | Qt::AlignTop, it->legendName);
663         painter->fillRect(legendColorRect, it->color);
664     }
665 
666     painter->restore();
667 }
View Code
复制代码

ScatterChart.h

复制代码
 1 /*
 2  * 功能:简单封装散点图(再次使用时可快速修改)
 3  * 作者:朱建强
 4  * 创建时间:2025-1-28
 5 */
 6 #ifndef SCATTERCHART_H
 7 #define SCATTERCHART_H
 8 
 9 #include "AbstractChart.h"
10 
11 class ScatterChart : public AbstractChart
12 {
13     Q_OBJECT
14 public:
15     explicit ScatterChart(QWidget* parent = nullptr);
16     void clearData();
17     void appendData(const QString& sliceName, QVector<float> values, const QColor& color);
18     void setCategoryLabels(const QStringList& categorys);
19 signals:
20 public slots:
21 private:
22     void initChart() override;
23     void initAxis() override;
24     void combine() override;
25 private:
26     QCategoryAxis* m_axisX;
27     QValueAxis* m_axisY;
28 };
29 
30 #endif // SCATTERCHART_H
View Code
复制代码

ScatterChart.cpp

复制代码
  1 #include "ScatterChart.h"
  2 
  3 ScatterChart::ScatterChart(QWidget* parent) : AbstractChart(parent)
  4 {
  5     m_axisX = nullptr;
  6     m_axisY = nullptr;
  7 
  8     initUI();
  9 }
 10 
 11 void ScatterChart::clearData()
 12 {
 13     Q_ASSERT(m_chart);
 14     m_chart->removeAllSeries();
 15 }
 16 
 17 void ScatterChart::appendData(const QString& sliceName, QVector<float> values, const QColor& color)
 18 {
 19     QList<QPointF> points;
 20     for(int i = 0; i < values.size(); ++i)
 21     {
 22         points << QPointF(i * 2 + 1, values[i]);
 23     }
 24 
 25     auto* series = new QScatterSeries;
 26 
 27     series->setName(sliceName);
 28     series->setColor(color);
 29 
 30     series->setMarkerSize(10);
 31     series->setPointLabelsFormat("(@yPoint)");
 32     series->setPointsVisible(true);
 33     series->setPointLabelsVisible(true);
 34 
 35     Q_ASSERT(m_chart);
 36     m_chart->addSeries(series);
 37 
 38     series->append(points);
 39     series->attachAxis(m_axisX);
 40     series->attachAxis(m_axisY);
 41 }
 42 
 43 void ScatterChart::setCategoryLabels(const QStringList& categorys)
 44 {
 45     int categorysCount = categorys.size();
 46 
 47     Q_ASSERT(m_axisX);
 48     m_axisX->append(" ", 0);
 49     for(int i = 0; i < categorysCount; ++i)
 50     {
 51         m_axisX->append(categorys[i], i * 2 + 1);
 52     }
 53     m_axisX->append(" ", categorysCount * 2);
 54 
 55     m_axisX->setRange(0, categorysCount * 2);
 56     m_axisX->setTickCount(categorysCount * 2);
 57 }
 58 
 59 void ScatterChart::initChart()
 60 {
 61     AbstractChart::initChart();
 62 
 63     m_chart->legend()->setVisible(false);
 64 }
 65 
 66 void ScatterChart::initAxis()
 67 {
 68     m_axisX = new QCategoryAxis;
 69     m_axisX->setLineVisible(false);
 70     m_axisX->setLinePenColor(C_xAxisLineColor);
 71     m_axisX->setGridLineVisible(false);
 72     m_axisX->setGridLineColor(C_xAxisGridLineColor);
 73     m_axisX->setLabelsFont(C_xAxislabelsFont);
 74     m_axisX->setLabelsColor(C_xAxislabelsFontColor);
 75     m_axisX->setLabelsPosition(QCategoryAxis::AxisLabelsPositionOnValue);
 76     m_axisX->setRange(0, 100);
 77     m_axisX->setTickCount(5);
 78 
 79     m_axisY = new QValueAxis;
 80     m_axisY->setLineVisible(false);
 81     m_axisY->setLinePenColor(C_yAxisLineColor);
 82     m_axisY->setGridLineVisible(true);
 83     m_axisY->setGridLineColor(C_yAxisGridLineColor);
 84     m_axisY->setLabelsFont(C_yAxislabelsFont);
 85     m_axisY->setLabelsColor(C_yAxislabelsFontColor);
 86     m_axisY->setRange(0, 5);
 87     m_axisY->setTickCount(5);
 88     m_axisY->setLabelFormat("%d");
 89 }
 90 
 91 void ScatterChart::combine()
 92 {
 93     Q_ASSERT(m_chart);
 94     Q_ASSERT(m_chartView);
 95 
 96     m_chartView->setChart(m_chart);
 97 
 98     m_chart->addAxis(m_axisX, Qt::AlignBottom);
 99     m_chart->addAxis(m_axisY, Qt::AlignLeft);
100 }
View Code
复制代码

ScoreLineChart.h

复制代码
  1 #ifndef SCORELINECHART_H
  2 #define SCORELINECHART_H
  3 
  4 #include <QWidget>
  5 #include <QMap>
  6 #include <QVector>
  7 #include <QList>
  8 #include "chartdefine.h"
  9 #include "ZJQChart/ToolTip.h"
 10 
 11 class ScoreLineChart : public QWidget
 12 {
 13     Q_OBJECT
 14 public:
 15     struct ScoresItem
 16     {
 17         QString name;
 18         QVector<float> scores;
 19         QColor color;
 20         QList<QPointF> scorePoints;     // 成绩对应的位置点
 21         QPainterPath linePath;          // 各点连线路径
 22         QPainterPath itemArea;          // 对象选择区域
 23     };
 24 
 25     explicit ScoreLineChart(QWidget* parent = nullptr);
 26 
 27     void setShowLegend(bool showLegend);
 28     void setShowTitle(bool showTitle);
 29     void setShowXAxisLine(bool showXAxisLine);
 30     void setShowYAxisLine(bool showYAxisLine);
 31     void setShowHGridLine(bool showHGridLine);
 32     void setShowVGridLine(bool showVGridLine);
 33     void setShowPoint(bool showPoint);
 34     void setShowPointLabel(bool showPointLabel);
 35 
 36     void setBackgroundColor(const QColor& backgroundColor);
 37     void setBaseLineColor(const QColor& baseLineColor);
 38     void setGridLineColor(const QColor& gridLineColor);
 39 
 40     void setTitle(const QString& title);
 41     void setTitleFont(const QFont& titleFont);
 42     void setTitleColor(const QColor& titleColor);
 43 
 44     void setScoreTickCount(int tickCount);                   // 设置刻度数量
 45     void setScoreRange(float min, float max);                // 设置刻度范围
 46     void setScoreLabelFont(const QFont& scoreLabelFont);
 47     void setScoreLabelColor(const QColor& scoreLabelColor);
 48 
 49     void setCourseLabel(const QStringList& labels);         // 设置课程标签
 50     void appendCourseLabel(const QString& course);          // 添加课程标签
 51     void setCourseLabelFont(const QFont& font);             // 设置课程标签字体
 52     void setCourseLabelColor(const QColor& color);          // 设置课程标签字体颜色
 53 
 54     void appendScoresItem(QVector<float> scores, QString name, QColor color);
 55 
 56 signals:
 57 
 58 public slots:
 59 
 60 protected:
 61     void paintEvent(QPaintEvent* event) override;
 62     void mousePressEvent(QMouseEvent* event) override;
 63     void mouseMoveEvent(QMouseEvent* event) override;
 64     void wheelEvent(QWheelEvent* event) override;
 65 
 66 private:
 67     void init();
 68 
 69     void updatePoints();                       // 更新成绩点
 70     void updateCurveLine();                    // 更新曲线
 71     void updateItemArea();                     // 更新对象区域
 72     void updateCourseArea();                   // 更新课程区域
 73 
 74     void showScore(int i);                     // 显示提示框
 75 
 76     void drawTitle(QPainter* painter);         // 绘制标题
 77     void drawBaseLine(QPainter* painter);      // 绘制基线(X轴与Y轴)
 78     void drawGridLine(QPainter* painter);      // 绘制网格线
 79     void drawScoreLabel(QPainter* painter);    // 绘制分数标签
 80     void drawCourseLabel(QPainter* painter);   // 绘制课程标签
 81     void drawScorePoint(QPainter* painter);    // 绘制成绩点
 82     void drawScoreLine(QPainter* painter);     // 绘制成绩线
 83     void drawLegends(QPainter* painter);       // 绘制图例
 84     void drawSelectedArea(QPainter* painter);  // 绘制被选择的区域
 85     void drawSelectedPoint(QPainter* painter); // 绘制被选择的课程点
 86     void drawPointLabel(QPainter* painter);    // 绘制所有点标签
 87 
 88 private:
 89     bool m_showTitle;                           // 是否显示标题
 90     bool m_showLegend;                          // 是否显示图例
 91     bool m_showXAxisLine;                       // 是否显示X轴基线
 92     bool m_showYAxisLine;                       // 是否显示Y轴基线
 93     bool m_showHGridLine;                       // 是否显示水平网格线
 94     bool m_showVGridLine;                       // 是否显示垂直网格线
 95     bool m_showPoint;                           // 是否显示点
 96     bool m_showPointLabel;                      // 是否显示点标签
 97 
 98     int m_courseCount;                          // 课程标签数量
 99     int m_scoreMarkCount;                       // 成绩标签数量
100     float m_minScore;                           // 最小成绩
101     float m_maxScore;                           // 最大成绩
102 
103     QStringList m_courseLabels;                 // 课程标签
104     QMap<QString, ScoresItem*> m_itemMap;
105 
106     QString m_title;                            // 标题
107 
108     QFont m_titleFont;                          // 标题字体
109     QFont m_scoreLabelFont;                     // 成绩标签字体
110     QFont m_courseLabelFont;                    // 课程标签字体
111     QFont m_legendFont;                         // 图例字体
112     QFont m_pointLabelFont;                     // 点标签字体
113 
114     QColor m_backgroundColor;                   // 背景颜色
115     QColor m_baseLineColor;                     // 基线颜色
116     QColor m_gridLineColor;                     // 网格线颜色
117     QColor m_titleColor;                        // 标题字体颜色
118     QColor m_scoreLabelColor;                   // 分数标签字体颜色
119     QColor m_courseLabelColor;                  // 课程标签字体颜色
120     QColor m_legendColor;                       // 图例字体颜色
121     QColor m_pointLabelColor;                   // 点标签字体颜色
122 
123     float m_gridWidth;                          // 网线宽度
124     float m_gridHeight;                         // 网线高度
125     QPointF m_gridPosition;                     // 网格位置
126 
127     QString m_selectedItemName;                 // 被选择的对象
128     QVector<QPainterPath> m_courseAreas;        // 每个课程所在的柱状区域(鼠标移动时判断是在哪个课程区域内)
129     int m_selectedCourseID;                     // 被选择的课程ID
130 
131     bool m_selectPointExist;                    // 选择的点是否存在
132     ToolTip* m_tip;
133 };
134 
135 #endif // SCORELINECHART_H
View Code
复制代码

ScoreLineChart.cpp

复制代码
#include "ScoreLineChart.h"
#include <QPainter>
#include <QFontMetricsF>
#include <QtMath>
#include <QLinearGradient>
#include "SmoothCurveGenerator.h"
#include <QToolTip>
#include <QMouseEvent>


const int C_edgeDistance = 5;      // 图像与边的距离
const int C_lableDistance = 5;     // 标签与网格线距离
const int C_lineWidth = 1;          // 线宽
const int C_colorAlpha = 100;       // 数据颜色透明度(高亮时是其200)

ScoreLineChart::ScoreLineChart(QWidget* parent) : QWidget(parent)
{
    init();

    m_tip = new ToolTip(this);
    m_tip->hide();
}

void ScoreLineChart::setShowLegend(bool showLegend)
{
    m_showLegend = showLegend;
}

void ScoreLineChart::setShowTitle(bool showTitle)
{
    m_showTitle = showTitle;
}

void ScoreLineChart::setShowXAxisLine(bool showXAxisLine)
{
    m_showXAxisLine = showXAxisLine;
}

void ScoreLineChart::setShowYAxisLine(bool showYAxisLine)
{
    m_showYAxisLine = showYAxisLine;
}

void ScoreLineChart::setShowHGridLine(bool showHGridLine)
{
    m_showHGridLine = showHGridLine;
}

void ScoreLineChart::setShowVGridLine(bool showVGridLine)
{
    m_showVGridLine = showVGridLine;
}

void ScoreLineChart::setShowPoint(bool showPoint)
{
    m_showPoint = showPoint;
}

void ScoreLineChart::setShowPointLabel(bool showPointLabel)
{
    m_showPointLabel = showPointLabel;
}

void ScoreLineChart::setBackgroundColor(const QColor& backgroundColor)
{
    m_backgroundColor = backgroundColor;
}

void ScoreLineChart::setBaseLineColor(const QColor& baseLineColor)
{
    m_baseLineColor = baseLineColor;
}

void ScoreLineChart::setGridLineColor(const QColor& gridLineColor)
{
    m_gridLineColor = gridLineColor;
}

void ScoreLineChart::setTitle(const QString& title)
{
    m_title = title;
}

void ScoreLineChart::setTitleFont(const QFont& titleFont)
{
    m_titleFont = titleFont;
}

void ScoreLineChart::setTitleColor(const QColor& titleColor)
{
    m_titleColor = titleColor;
}

void ScoreLineChart::setScoreTickCount(int tickCount)
{
    m_scoreMarkCount = tickCount;
}

void ScoreLineChart::setScoreRange(float min, float max)
{
    m_minScore = min;
    m_maxScore = max;
}

void ScoreLineChart::setScoreLabelFont(const QFont& scoreLabelFont)
{
    m_scoreLabelFont = scoreLabelFont;
}

void ScoreLineChart::setScoreLabelColor(const QColor& scoreLabelColor)
{
    m_scoreLabelColor = scoreLabelColor;
}

void ScoreLineChart::setCourseLabel(const QStringList& labels)
{
    m_courseLabels = labels;
    m_courseCount = m_courseLabels.size();
}

void ScoreLineChart::appendCourseLabel(const QString& course)
{
    if(course.isEmpty())
    {
        return;
    }
    m_courseLabels.append(course);
    m_courseCount = m_courseLabels.size();
    this->update();
}

void ScoreLineChart::setCourseLabelFont(const QFont& font)
{
    m_courseLabelFont = font;
}

void ScoreLineChart::setCourseLabelColor(const QColor& color)
{
    m_courseLabelColor = color;
}

void ScoreLineChart::appendScoresItem(QVector<float> scores, QString name, QColor color)
{
    ScoresItem* pItem = new ScoresItem();
    pItem->name = name;
    pItem->scores = scores;
    pItem->color = color;
    if(pItem->name.isEmpty())
    {
        pItem->name = "ScoresItem";
    }
    m_itemMap[pItem->name] = pItem;
    m_selectedItemName = pItem->name;
    this->update();
}

void ScoreLineChart::paintEvent(QPaintEvent* event)
{
    QPainter painter(this);
    // 防止“锯齿”现象的出现
    painter.setRenderHint(QPainter::Antialiasing);          // 告诉绘图引擎应该在可能的情况下进行边的反锯齿绘制
    painter.setRenderHint(QPainter::SmoothPixmapTransform); // 使用平滑的pixmap变换算法(双线性插值算法),而不是近邻插值算
    painter.setRenderHint(QPainter::TextAntialiasing);      // 尽可能的情况下文字的反锯齿绘制

    // 添加背景颜色
    painter.save();
    painter.setBrush(m_backgroundColor);
    QPen pen;
    pen.setColor(m_backgroundColor);
    painter.setPen(pen);
    painter.drawRect(this->rect());
    painter.restore();

    // 更新数据
    QFontMetricsF fontMetrics(m_titleFont);
    float titleHeight = fontMetrics.height();
    if(m_title.isEmpty())
    {
        titleHeight = 0;
    }
    QFontMetricsF fontMetrics2(m_courseLabelFont);
    float courseLabelHeight = fontMetrics2.height();
    QFontMetricsF fontMetrics3(m_scoreLabelFont);
    float scoreLabelWidth = fontMetrics3.width(QString::number(m_maxScore, 'f', 2));

    m_gridWidth = width() - 2 * C_edgeDistance - scoreLabelWidth - C_lableDistance;
    m_gridHeight = height() - 2 * C_edgeDistance - titleHeight - courseLabelHeight - 2 * C_lableDistance;
    m_gridPosition.setX(C_edgeDistance + scoreLabelWidth + C_lableDistance);
    m_gridPosition.setY(C_edgeDistance + titleHeight + C_lableDistance);

    updateCourseArea();
    updatePoints();
    updateCurveLine();
    updateItemArea();
    // 绘制
    drawTitle(&painter);
    drawBaseLine(&painter);
    drawGridLine(&painter);
    drawScoreLabel(&painter);
    drawCourseLabel(&painter);
    drawLegends(&painter);
    drawScoreLine(&painter);
    drawSelectedArea(&painter);
    drawScorePoint(&painter);
    drawSelectedPoint(&painter);
    drawPointLabel(&painter);

    QWidget::paintEvent(event);
}

void ScoreLineChart::mousePressEvent(QMouseEvent* event)
{
    //左击
    if(event->button() == Qt::LeftButton)
    {
        ScoresItem* item;
        for(auto it = m_itemMap.begin(); it != m_itemMap.end(); ++it)
        {
            item = it.value();
            if(item->itemArea.contains(event->pos()))
            {
                m_selectedItemName = it.key();
            }
        }

        this->update();
    }

    QWidget::mousePressEvent(event);
}

void ScoreLineChart::mouseMoveEvent(QMouseEvent* event)
{
    m_selectedCourseID = -1;
    if(!m_selectedItemName.isEmpty())
    {
        for(int i = 0; i < m_courseAreas.size(); ++i)
        {
            if(m_courseAreas[i].contains(event->pos()) && m_itemMap[m_selectedItemName]->itemArea.contains(event->pos()))
            {
                m_selectedCourseID = i;
                break;
            }
        }
    }

    if(m_selectedCourseID != -1)
    {
        showScore(m_selectedCourseID);
        this->update();
    }
    else
    {
        if(m_tip != nullptr)
        {
            m_tip->hide();
        }
        if(m_selectPointExist)
        {
            this->update();
        }
    }

    QWidget::mouseMoveEvent(event);
}

void ScoreLineChart::wheelEvent(QWheelEvent* event)
{
    m_selectedCourseID = -1;
    if(m_tip != nullptr)
    {
        m_tip->hide();
    }
    if(m_selectPointExist)
    {
        this->update();
    }
    QWidget::wheelEvent(event);
}

void ScoreLineChart::init()
{
    m_showTitle = true;
    m_showLegend = true;
    m_showXAxisLine = true;
    m_showYAxisLine = true;
    m_showHGridLine = true;
    m_showVGridLine = true;
    m_showPoint = true;
    m_selectPointExist = false;
    m_showPointLabel = false;

    m_courseCount = 0;

    m_scoreMarkCount = 6;
    m_minScore = 0;
    m_maxScore = 100;

    m_titleFont = C_titleFont;
    m_scoreLabelFont = C_yAxislabelsFont;
    m_courseLabelFont = C_xAxislabelsFont;
    m_legendFont = C_legendFont;
    m_pointLabelFont = C_valueLabelFont;

    m_backgroundColor = C_chartBackgroundColor;
    m_baseLineColor = C_xAxisLineColor;
    m_gridLineColor = C_yAxisGridLineColor;
    m_titleColor = C_titleFontColor;
    m_scoreLabelColor = C_yAxislabelsFontColor;
    m_courseLabelColor = C_xAxislabelsFontColor;
    m_legendColor = C_legendFontColor;
    m_pointLabelColor = C_legendFontColor;

    this->setMouseTracking(true);
}

void ScoreLineChart::updateCourseArea()
{
    m_courseAreas.clear();

    float xStep = m_gridWidth / m_courseCount;
    for(int i = 0; i < m_courseCount; ++i)
    {
        QPainterPath painterPath;
        painterPath.addRect(m_gridPosition.x() + i * xStep, m_gridPosition.y(), xStep, m_gridHeight);
        m_courseAreas.append(painterPath);
    }
}

void ScoreLineChart::updatePoints()
{
    float xStep = m_gridWidth / m_courseCount;
    float startXPos = m_gridPosition.x() + xStep / 2;
    float startYPos = m_gridPosition.y() + m_gridHeight;
    float cur_x = 0;
    float cur_y = 0;

    ScoresItem* item;
    for(auto it = m_itemMap.begin(); it != m_itemMap.end(); ++it)
    {
        item = it.value();

        item->scorePoints.clear();
        for(int i = 0; i < m_courseCount && i < item->scores.size(); ++i)
        {
            cur_x = startXPos + i * xStep;
            cur_y = startYPos - (item->scores[i] - m_minScore) / (m_maxScore - m_minScore) * m_gridHeight;
            QPointF point(cur_x, cur_y);
            item->scorePoints.append(point);
        }
    }
}

void ScoreLineChart::updateCurveLine()
{
    ScoresItem* item;
    for(auto it = m_itemMap.begin(); it != m_itemMap.end(); ++it)
    {
        item = it.value();
        item->linePath = SmoothCurveGenerator::generateSmoothCurve(item->scorePoints, false, 0.25, 16);
    }
}

void ScoreLineChart::updateItemArea()
{
    ScoresItem* item;
    QPainterPath path;
    float xStep = m_gridWidth / m_courseCount;
    for(auto it = m_itemMap.begin(); it != m_itemMap.end(); ++it)
    {
        item = it.value();
        path = item->linePath;
        path.lineTo(QPointF(m_gridPosition.x() + m_courseCount * xStep - xStep / 2, m_gridPosition.y() + m_gridHeight));
        path.lineTo(QPointF(m_gridPosition.x() + xStep / 2, m_gridPosition.y() + m_gridHeight));
        item->itemArea = path;
    }
}

void ScoreLineChart::showScore(int i)
{
    if(i < 0 || i >= m_courseCount)
    {
        return;
    }

    if(m_itemMap.contains(m_selectedItemName))
    {
        ScoresItem* item = m_itemMap[m_selectedItemName];
        float score = 0;
        if(i < item->scores.size())
        {
            score = item->scores[i];
        }
        QString strScore;
        if(abs(score - int(score)) < 1e-8)
        {
            strScore = QString::number(int(score));
        }
        else
        {
            strScore = QString::number(score, 'f', 2);
        }

        QString tipText = QString("%1\n%2").arg(m_courseLabels[i]).arg(strScore);

        float xStep = m_gridWidth / m_courseCount;
        float x = m_gridPosition.x() + i * xStep + xStep / 2;
        float y = m_gridPosition.y() + m_gridHeight - (score - m_minScore) / (m_maxScore - m_minScore) * m_gridHeight;
        QPoint point(x + 5, y + 5);

        QFont font("微软雅黑", -1, 700);
        font.setPixelSize(14);
        m_tip->setTextFont(font);
        m_tip->showText(point, tipText);
    }
}

void ScoreLineChart::drawTitle(QPainter* painter)
{
    painter->save();

    QPen pen;
    pen.setColor(m_titleColor);
    painter->setPen(pen);
    painter->setFont(m_titleFont);

    QFontMetricsF fontMetrics(m_titleFont);
    float strWidth = fontMetrics.width(m_title);
    float strHeight = fontMetrics.height();

    float cur_x = width() / 2 - strWidth / 2;
    float cur_y = C_edgeDistance + strHeight;

    painter->drawText(cur_x, cur_y, m_title);

    painter->restore();
}

void ScoreLineChart::drawBaseLine(QPainter* painter)
{
    painter->save();

    QPen pen;
    pen.setWidthF(C_lineWidth);
    pen.setColor(m_baseLineColor);
    painter->setPen(pen);

    if(m_showXAxisLine)
    {
        float XAxisYpos = m_gridPosition.y() + m_gridHeight;
        painter->drawLine(m_gridPosition.x(), XAxisYpos, m_gridPosition.x() + m_gridWidth, XAxisYpos);
    }

    if(m_showYAxisLine)
    {
        painter->drawLine(m_gridPosition.x(), m_gridPosition.y(), m_gridPosition.x(), m_gridPosition.y() + m_gridHeight);
    }

    painter->restore();
}

void ScoreLineChart::drawGridLine(QPainter* painter)
{
    painter->save();

    QPen pen;
    pen.setWidthF(C_lineWidth);
    pen.setColor(m_gridLineColor);
    painter->setPen(pen);

    if(m_showHGridLine)
    {
        float yStep = m_gridHeight / (m_scoreMarkCount - 1);
        float cur_y = m_gridPosition.y();
        for(int i = 0; i < m_scoreMarkCount - 1; ++i)
        {
            painter->drawLine(m_gridPosition.x(), cur_y, m_gridPosition.x() + m_gridWidth, cur_y);
            cur_y += yStep;
        }
    }

    if(m_showVGridLine)
    {
        float xStep = m_gridWidth / m_courseCount;
        float cur_x = m_gridPosition.x() + xStep / 2;       // 开始位置右移动半个步进长度
        for(int i = 0; i < m_courseCount; ++i)
        {
            painter->drawLine(cur_x, m_gridPosition.y(), cur_x, m_gridPosition.y() + m_gridHeight);
            cur_x += xStep;
        }
    }

    painter->restore();
}

void ScoreLineChart::drawScoreLabel(QPainter* painter)
{
    painter->save();

    QPen pen;
    pen.setColor(m_scoreLabelColor);
    painter->setPen(pen);

    painter->setFont(m_scoreLabelFont);

    QString strScore;
    QPointF point;
    QFontMetricsF fontMetrics(m_scoreLabelFont);

    float strHeight = fontMetrics.height();
    float startPos = m_gridPosition.y() + m_gridHeight;
    float yStep = m_gridHeight / (m_scoreMarkCount - 1);
    float dY = (m_maxScore - m_minScore) / (m_scoreMarkCount - 1); // 刻度间隔值
    float score = 0;

    for(int i = 0; i < m_scoreMarkCount; ++i)
    {
        score = m_minScore + dY * i;
        if(abs(score - int(score)) < 1e-8)
        {
            strScore = QString::number(int(score));
        }
        else
        {
            strScore = QString::number(score, 'f', 2);
        }

        point.setX(m_gridPosition.x() - fontMetrics.width(strScore) - C_lableDistance);
        point.setY(startPos - i * yStep + strHeight / 4);

        painter->drawText(point, strScore);
    }

    painter->restore();
}

void ScoreLineChart::drawCourseLabel(QPainter* painter)
{
    painter->save();

    QPen pen;
    pen.setColor(m_courseLabelColor);
    painter->setPen(pen);

    painter->setFont(m_courseLabelFont);

    QFontMetricsF fontMetrics(m_courseLabelFont);
    float strHeight = fontMetrics.height();
    float xStep = m_gridWidth / m_courseCount;
    float startXPos = m_gridPosition.x();
    float yPos = m_gridPosition.y() + m_gridHeight + C_lableDistance;
    float currentXPos = 0;
    for(int i = 0; i < m_courseCount; ++i)
    {
        currentXPos = startXPos + i * xStep;
        QRectF labelRect = QRectF(currentXPos, yPos, xStep, 2 * strHeight);

        painter->drawText(labelRect, Qt::AlignHCenter | Qt::AlignTop | Qt::TextWordWrap, m_courseLabels.at(i));
    }

    painter->restore();
}

void ScoreLineChart::drawScorePoint(QPainter* painter)
{
    if(!m_showPoint)
    {
        return;
    }
    painter->save();

    QPen pen;
    ScoresItem* item;
    for(auto it = m_itemMap.begin(); it != m_itemMap.end(); ++it)
    {
        item = it.value();
        pen.setWidth(C_lineWidth * 3);
        pen.setColor(item->color);
        painter->setPen(pen);

        for(int i = 0; i < item->scorePoints.size(); ++i)
        {
            QPainterPath path;
            float x = item->scorePoints[i].x() - 4;
            float y = item->scorePoints[i].y() - 4;

            path.addRoundedRect(QRectF(x, y, 8, 8), 4, 4);
            painter->fillPath(path, Qt::white);

            painter->drawEllipse(item->scorePoints[i], 4, 4);
        }
    }

    painter->restore();
}

void ScoreLineChart::drawScoreLine(QPainter* painter)
{
    painter->save();

    QPen pen;
    QColor color;
    ScoresItem* item;
    for(auto it = m_itemMap.begin(); it != m_itemMap.end(); ++it)
    {
        item = it.value();

        color = item->color;
        color.setAlpha(200);

        pen.setWidthF(C_lineWidth * 2);
        pen.setColor(color);

        painter->setPen(pen);
        painter->drawPath(item->linePath);
    }

    painter->restore();
}

void ScoreLineChart::drawLegends(QPainter* painter)
{
    if(!m_showLegend)
    {
        return;
    }
    painter->save();

    QPen pen;
    pen.setColor(m_legendColor);
    painter->setPen(pen);
    painter->setFont(m_legendFont);
    QFontMetrics fm = painter->fontMetrics();

    // 获取最大名称长度
    int maxWidth = 0;
    ScoresItem* item;
    for(auto it = m_itemMap.begin(); it != m_itemMap.end(); ++it)
    {
        item = it.value();
        int w = fm.width(item->name);
        if(w > maxWidth)
        {
            maxWidth = w;
        }
    }
    maxWidth += 5;

    // 绘制图例
    int distance = 10;             // 与边界的距离
    int distance2 = 5;             // 文字与颜色块距离
    int distance3 = 5;             // 图例与图例距离
    int colorSide = fm.height();   // 颜色块边长
    float cur_x = 0;
    float cur_y = distance;
    int textWidth = 0;
    int textHeight = 0;
    for(auto it = m_itemMap.begin(); it != m_itemMap.end(); ++it)
    {
        ScoresItem* item = it.value();
        textWidth = fm.width(item->name) + 5;
        textHeight = fm.height();
        cur_x = width() - distance - maxWidth - distance2;

        QRectF legendNameRect = QRectF(cur_x, cur_y, textWidth, textHeight);
        cur_x -= colorSide;
        QRectF legendColorRect = QRectF(cur_x, cur_y, colorSide, colorSide);
        cur_y += textHeight + distance3;

        painter->drawText(legendNameRect, Qt::AlignLeft | Qt::AlignTop, item->name);
        painter->fillRect(legendColorRect, item->color);
    }

    painter->restore();
}

void ScoreLineChart::drawSelectedArea(QPainter* painter)
{
    painter->save();

    // 被选中则填充颜色
    if(m_itemMap.contains(m_selectedItemName))
    {
        ScoresItem* item = m_itemMap[m_selectedItemName];
        Q_ASSERT(item);

        QColor color = item->color;
        QLinearGradient linearGradient(m_gridPosition.x(), m_gridPosition.y(), m_gridPosition.x(), m_gridPosition.y() + m_gridHeight);
        color.setAlpha(C_colorAlpha * 2);
        linearGradient.setColorAt(0.0, color);
        color.setAlpha(0);
        linearGradient.setColorAt(1.0, color);

        painter->fillPath(item->itemArea, linearGradient);
    }

    painter->restore();
}

void ScoreLineChart::drawSelectedPoint(QPainter* painter)
{
    painter->save();

    ScoresItem* item = m_itemMap[m_selectedItemName];
    if(m_selectedCourseID >= 0 && m_selectedCourseID < item->scorePoints.size())
    {
        QPen pen;
        pen.setWidth(C_lineWidth * 3);
        pen.setColor(item->color);
        painter->setPen(pen);

        QPainterPath path;
        QPointF point = item->scorePoints[m_selectedCourseID];
        float x = point.x() - 4;
        float y = point.y() - 4;

        path.addRoundedRect(QRectF(x, y, 8, 8), 4, 4);
        painter->fillPath(path, Qt::white);

        painter->drawEllipse(point, 4, 4);
        m_selectPointExist = true;
    }
    else
    {
        m_selectPointExist = false;
    }

    painter->restore();
}

void ScoreLineChart::drawPointLabel(QPainter* painter)
{
    if(!m_showPointLabel)
    {
        return;
    }

    painter->save();

    QPen pen;
    pen.setColor(m_pointLabelColor);
    painter->setPen(pen);
    painter->setFont(m_pointLabelFont);

    QFontMetricsF fontMetrics(m_pointLabelFont);
    float strHeight = fontMetrics.height();

    ScoresItem* item = m_itemMap[m_selectedItemName];

    for(int i = 0; i < item->scorePoints.size(); ++i)
    {
        float score = 0;
        if(i < item->scores.size())
        {
            score = item->scores[i];
        }
        QString strScore;
        if(abs(score - int(score)) < 1e-8)
        {
            strScore = QString::number(int(score));
        }
        else
        {
            strScore = QString::number(score, 'f', 2);
        }

        QString tipText = QString("%1").arg(strScore);

        float xStep = m_gridWidth / m_courseCount;
        float x = m_gridPosition.x() + i * xStep + xStep / 2;
        float y = m_gridPosition.y() + m_gridHeight - (score - m_minScore) / (m_maxScore - m_minScore) * m_gridHeight;

        QRectF labelRect = QRectF(x - 10, y + 20, fontMetrics.width(tipText), 2 * strHeight);

        painter->drawText(labelRect, Qt::AlignHCenter | Qt::AlignTop | Qt::TextWordWrap, tipText);
    }
    painter->restore();
}
View Code
复制代码

SmoothCurveGenerator.h

复制代码
 1 #ifndef SMOOTHCURVEGENERATOR_H
 2 #define SMOOTHCURVEGENERATOR_H
 3 
 4 #include <QList>
 5 #include <QPainterPath>
 6 
 7 class SmoothCurveGenerator
 8 {
 9 
10 public:
11     /**
12     * @brief generateSmoothCurve 的重载函数
13     */
14     static QPainterPath generateSmoothCurve(QList<QPointF> points, bool closed = false, double tension = 0.5, int numberOfSegments = 16);
15 
16     /**
17     * @brief 使用传入的曲线顶点坐标创建平滑曲线。
18     *
19     * @param points 曲线顶点坐标数组,
20     * points[i+0] 是第 i 个点的 x 坐标,
21     * points[i+1] 是第 i 个点的 y 坐标
22     * @param closed 曲线是否封闭,默认不封闭
23     * @param tension 密集程度,默认为 0.5
24     * @param numberOfSegments 平滑曲线 2 个顶点间的线段数,默认为 16
25     * @return 平滑曲线的 QPainterPath
26     */
27     static QPainterPath generateSmoothCurve(QList<double>points, bool closed = false, double tension = 0.5, int numberOfSegments = 16);
28 };
29 
30 #endif // SMOOTHCURVEGENERATOR_H
View Code
复制代码

SmoothCurveGenerator.cpp

复制代码
 1 #include "SmoothCurveGenerator.h"
 2 #include <QtMath>
 3 
 4 QPainterPath SmoothCurveGenerator::generateSmoothCurve(QList<QPointF> points, bool closed, double tension, int numberOfSegments)
 5 {
 6     QList<double> ps;
 7 
 8     foreach(QPointF p, points)
 9     {
10         ps << p.x() << p.y();
11     }
12 
13     return SmoothCurveGenerator::generateSmoothCurve(ps, closed, tension, numberOfSegments);
14 }
15 
16 QPainterPath SmoothCurveGenerator::generateSmoothCurve(QList<double> points, bool closed, double tension, int numberOfSegments)
17 {
18     QList<double> ps(points); // clone array so we don't change the original points
19     QList<double> result; // generated smooth curve coordinates
20     double x, y;
21     double t1x, t2x, t1y, t2y;
22     double c1, c2, c3, c4;
23     double st;
24 
25     // The algorithm require a previous and next point to the actual point array.
26     // Check if we will draw closed or open curve.
27     // If closed, copy end points to beginning and first points to end
28     // If open, duplicate first points to befinning, end points to end
29     if(closed)
30     {
31         ps.prepend(points[points.length() - 1]);
32         ps.prepend(points[points.length() - 2]);
33         ps.prepend(points[points.length() - 1]);
34         ps.prepend(points[points.length() - 2]);
35         ps.append(points[0]);
36         ps.append(points[1]);
37     }
38     else
39     {
40         ps.prepend(points[1]); // copy 1st point and insert at beginning
41         ps.prepend(points[0]);
42         ps.append(points[points.length() - 2]); // copy last point and append
43         ps.append(points[points.length() - 1]);
44     }
45 
46     // 1. loop goes through point array
47     // 2. loop goes through each segment between the 2 points + 1e point before and after
48     for(int i = 2; i < (ps.length() - 4); i += 2)
49     {
50         // calculate tension vectors
51         t1x = (ps[i + 2] - ps[i - 2]) * tension;
52         t2x = (ps[i + 4] - ps[i - 0]) * tension;
53         t1y = (ps[i + 3] - ps[i - 1]) * tension;
54         t2y = (ps[i + 5] - ps[i + 1]) * tension;
55 
56         for(int t = 0; t <= numberOfSegments; t++)
57         {
58             // calculate step
59             st = (double)t / (double)numberOfSegments;
60 
61             // calculate cardinals
62             c1 = 2 * qPow(st, 3) - 3 * qPow(st, 2) + 1;
63             c2 = -2 * qPow(st, 3) + 3 * qPow(st, 2);
64             c3 = qPow(st, 3) - 2 * qPow(st, 2) + st;
65             c4 = qPow(st, 3) - qPow(st, 2);
66 
67             // calculate x and y cords with common control vectors
68             x = c1 * ps[i] + c2 * ps[i + 2] + c3 * t1x + c4 * t2x;
69             y = c1 * ps[i + 1] + c2 * ps[i + 3] + c3 * t1y + c4 * t2y;
70 
71             //store points in array
72             result << x << y;
73         }
74     }
75 
76     // 浣跨敤鐨勫钩婊戞洸绾跨殑鍧愭爣鍒涘缓 QPainterPath
77     QPainterPath path;
78     path.moveTo(result[0], result[1]);
79     for(int i = 2; i < result.length(); i += 2)
80     {
81         path.lineTo(result[i + 0], result[i + 1]);
82     }
83 
84     if(closed)
85     {
86         path.closeSubpath();
87     }
88 
89     return path;
90 }
View Code
复制代码

SplineChart.h

复制代码
 1 /*
 2  * 功能:简单封装曲线图(再次使用时可快速修改)
 3  * 作者:朱建强
 4  * 创建时间:2025-1-28
 5 */
 6 #ifndef SPLINECHART_H
 7 #define SPLINECHART_H
 8 
 9 #include "AbstractChart.h"
10 
11 class SplineChart : public AbstractChart
12 {
13     Q_OBJECT
14 public:
15     explicit SplineChart(QWidget* parent = nullptr);
16     void clearData();
17     void appendData(const QString& sliceName, QList<QPointF> points, const QColor& color);
18 public slots:
19 private:
20     void initAxis() override;
21     void combine() override;
22 private:
23     QValueAxis* m_axisX;
24     QValueAxis* m_axisY;
25 };
26 
27 #endif // SPLINECHART_H
View Code
复制代码

SplineChart.cpp

复制代码
 1 #include "SplineChart.h"
 2 
 3 SplineChart::SplineChart(QWidget* parent) : AbstractChart(parent)
 4 {
 5     m_axisX = nullptr;
 6     m_axisY = nullptr;
 7 
 8     initUI();
 9 }
10 
11 void SplineChart::clearData()
12 {
13     Q_ASSERT(m_chart);
14     m_chart->removeAllSeries();
15 }
16 
17 void SplineChart::appendData(const QString& sliceName, QList<QPointF> points, const QColor& color)
18 {
19     auto* series = new QSplineSeries;
20 
21     series->setName(sliceName);
22     series->setColor(color);
23 
24     Q_ASSERT(m_chart);
25     m_chart->addSeries(series);
26 
27     series->append(points);
28     series->attachAxis(m_axisX);
29     series->attachAxis(m_axisY);
30 }
31 
32 void SplineChart::initAxis()
33 {
34     m_axisX = new QValueAxis;
35     m_axisX->setLineVisible(false);
36     m_axisX->setLinePenColor(C_xAxisLineColor);
37     m_axisX->setGridLineVisible(true);
38     m_axisX->setGridLineColor(C_xAxisGridLineColor);
39     m_axisX->setLabelsFont(C_xAxislabelsFont);
40     m_axisX->setLabelsColor(C_xAxislabelsFontColor);
41     m_axisX->setRange(0, 100);
42     m_axisX->setTickCount(5);
43 
44     m_axisY = new QValueAxis;
45     m_axisY->setLineVisible(false);
46     m_axisY->setLinePenColor(C_yAxisLineColor);
47     m_axisY->setGridLineVisible(true);
48     m_axisY->setGridLineColor(C_yAxisGridLineColor);
49     m_axisY->setLabelsFont(C_yAxislabelsFont);
50     m_axisY->setLabelsColor(C_yAxislabelsFontColor);
51     m_axisY->setRange(0, 100);
52     m_axisY->setTickCount(5);
53 }
54 
55 void SplineChart::combine()
56 {
57     Q_ASSERT(m_chart);
58     Q_ASSERT(m_chartView);
59 
60     m_chartView->setChart(m_chart);
61 
62     m_chart->addAxis(m_axisX, Qt::AlignBottom);
63     m_chart->addAxis(m_axisY, Qt::AlignLeft);
64 }
View Code
复制代码

StructureChart.h

复制代码
 1 /*
 2  * 功能:结构图
 3  * 作者:朱建强
 4  * 创建时间:2025-1-30
 5 */
 6 #ifndef STRUCTURECHART_H
 7 #define STRUCTURECHART_H
 8 
 9 #include <QWidget>
10 #include <QJsonObject>
11 #include <QJsonArray>
12 #include "StructureNode.h"
13 
14 class StructureChart : public QWidget
15 {
16     Q_OBJECT
17 
18 public:
19     explicit StructureChart(const QJsonObject& data, QWidget* parent = 0);
20     ~StructureChart();
21 
22 private:
23     void initWidget(const QJsonObject& data);
24 
25     void createStructure(StructureNode* parent, const QString& nodeName, const QJsonObject& data);
26 };
27 
28 #endif // STRUCTURECHART_H
View Code
复制代码

StructureChart.cpp

复制代码
 1 #include "StructureChart.h"
 2 #include <QBoxLayout>
 3 #include <QMap>
 4 
 5 StructureChart::StructureChart(const QJsonObject& data, QWidget* parent) :
 6     QWidget(parent)
 7 {
 8     initWidget(data);
 9 }
10 
11 StructureChart::~StructureChart()
12 {
13 }
14 
15 void StructureChart::initWidget(const QJsonObject& data)
16 {
17     StructureNodeData nodeData;
18 
19     if(data["root"].isObject())
20     {
21         auto rootObject = data["root"].toObject();
22         auto keys = rootObject.keys();
23 
24         if(keys.size() > 0)
25         {
26             nodeData.labelText = keys[0];
27             nodeData.tipText = rootObject[keys[0]].toObject()["tip"].toString();
28         }
29     }
30 
31     StructureNode* root = new StructureNode(nodeData);
32     createStructure(root, nodeData.labelText, data);
33     root->setCurrentNodeStyleSheet("font:700 18px;color: #FFFFFF;background-color: #547DFF;");
34     root->setCurrentNodeSize(QSize(200, 100));
35     root->updateWidget();
36     root->setMinimumSize(root->size());
37 
38     auto hLayout = new QHBoxLayout;
39     hLayout->addStretch();
40     hLayout->addWidget(root);
41     hLayout->addStretch();
42 
43     auto vLayout = new QVBoxLayout;
44     vLayout->addStretch();
45     vLayout->addLayout(hLayout);
46     vLayout->addStretch();
47 
48     this->setLayout(vLayout);
49 }
50 
51 void StructureChart::createStructure(StructureNode* parent, const QString& nodeName, const QJsonObject& data)
52 {
53     Q_ASSERT(parent);
54 
55     QJsonObject currentNode;
56     if(data[nodeName].isObject())
57     {
58         currentNode = data[nodeName].toObject();
59     }
60     else
61     {
62         return;
63     }
64 
65     QJsonObject temp;
66     StructureNode* node = nullptr;
67     QMap<int, StructureNode*> nodes;
68 
69     for(auto it = currentNode.begin(); it != currentNode.end(); it++)
70     {
71         StructureNodeData nodeData;
72         nodeData.labelText = it.key();
73         temp = it.value().toObject();
74         nodeData.tipText = temp["tip"].toString();
75 
76         node = new StructureNode(nodeData, parent);
77         nodes[temp["order"].toInt()] = node;
78 
79         createStructure(node, it.key(), data);
80     }
81 
82     for(auto it = nodes.begin(); it != nodes.end(); it++)
83     {
84         parent->addChildNode(it.value());
85     }
86 }
View Code
复制代码

StructureNode.h

复制代码
  1 /*
  2  * 功能:结构图-结点
  3  * 作者:朱建强
  4  * 创建时间:2025-1-30
  5 */
  6 #ifndef STRUCTURENODE_H
  7 #define STRUCTURENODE_H
  8 
  9 #include <QWidget>
 10 #include <QVector>
 11 #include <QLabel>
 12 
 13 struct StructureNodeData
 14 {
 15     QString labelText;
 16     QString tipText;
 17 };
 18 
 19 class StructureNode: public QWidget
 20 {
 21 public:
 22     StructureNode(const StructureNodeData& nodeData, QWidget* parent = nullptr);
 23     ~StructureNode();
 24 
 25     /*
 26      * 功能:添加子节点
 27      * 输入:
 28      *  node:子节点指针
 29     */
 30     void addChildNode(StructureNode* node);
 31 
 32     /*
 33      * 功能:是否存在孩子节点(根据是否有孩子节点来决定其与兄弟节点的间距)
 34      * 输出:
 35      *  bool:是否存在孩子节点
 36     */
 37     bool hasChild();
 38 
 39     /*
 40      * 功能:更新界面
 41     */
 42     void updateWidget();
 43 
 44     /*
 45      * 功能:设置当前节点样式(若当前为末尾节点则会覆盖全局末尾节点样式)
 46      * 输入:
 47      *      style:qss样式
 48     */
 49     void setCurrentNodeStyleSheet(const QString& style);
 50 
 51     /*
 52      * 功能:设置当前节点尺寸
 53      * 输入:
 54      *      size:末尾节点尺寸
 55     */
 56     void setCurrentNodeSize(const QSize& size);
 57 
 58     /*
 59      * 功能:设置末尾节点尺寸
 60      * 输入:
 61      *      size:末尾节点尺寸
 62     */
 63     static void setEndNodeSize(const QSize& size);
 64 
 65 private:
 66 
 67     /*
 68      * 功能:更新当前节点界面尺寸大小
 69     */
 70     void updateSize();
 71 
 72     /*
 73      * 功能:绘制当前结构树
 74     */
 75     void paint();
 76 
 77     /*
 78      * 功能:绘制当前结点数据标签
 79     */
 80     void paintLabel();
 81 
 82     /*
 83      * 功能:绘制子节点
 84     */
 85     void paintChildren();
 86 
 87     /*
 88      * 功能:节点连接线
 89     */
 90     void paintLine();
 91 
 92     /*
 93      * 功能:获取子节点界面宽度和
 94      * 输出:
 95      *     int:子节点界面宽度和(包含子节点之间的间隔)
 96     */
 97     int getChildWidthSum();
 98 
 99     /*
100      * 功能:获取子节点中最大高度
101      * 输出:
102      *      int:最大高度
103     */
104     int getChildMaxHeight();
105 
106     /*
107      * 功能:提示框
108     */
109     bool eventFilter(QObject* obj, QEvent* event) override;
110 
111 private:
112     static QSize M_endNodeSize;                 // 末尾节点尺寸
113 
114     StructureNodeData m_data;                   // 当前节点文本信息
115     QString m_currentNodeStyleSheet;            // 当前节点样式
116     QSize m_currentNodeSize;                    // 当前节点尺寸
117     QLabel* m_currentLabel;                     // 当前标签
118     QVector<StructureNode*> m_childNodes;       // 子节点集合
119 };
120 
121 #endif // STRUCTURENODE_H
View Code
复制代码

StructureNode.cpp

复制代码
  1 #include "StructureNode.h"
  2 #include <QToolTip>
  3 #include <QEvent>
  4 
  5 const QSize c_nodeSize(154, 48);
  6 const int c_brotherNodeMinGap = 12;
  7 const int c_fatherNodeMinGap = 64;
  8 const QString c_labelStyleSheet = "QLabel{font-size: 16px;color: #1A1A1A;font-weight: 400;background: #F1F5FC;border-radius: 4px;}";
  9 const QString c_lineStyleSheet = "border: 1px solid #DDE2ED;";
 10 const QString c_arrowStyleSheet = "background: #547DFF;";
 11 const QString c_endNodeStyleSheet = "padding:5px";
 12 QSize StructureNode::M_endNodeSize(48, 178);
 13 
 14 StructureNode::StructureNode(const StructureNodeData& nodeData, QWidget* parent)
 15     : QWidget(parent)
 16 {
 17     m_data = nodeData;
 18 
 19     this->setStyleSheet(c_labelStyleSheet);
 20     m_currentLabel = nullptr;
 21     m_currentNodeStyleSheet = "";
 22     m_currentNodeSize = QSize(0, 0);
 23 }
 24 
 25 StructureNode::~StructureNode()
 26 {
 27 
 28 }
 29 
 30 void StructureNode::addChildNode(StructureNode* node)
 31 {
 32     m_childNodes.append(node);
 33     node->setParent(this);
 34 }
 35 
 36 bool StructureNode::hasChild()
 37 {
 38     return (m_childNodes.size() != 0);
 39 }
 40 
 41 void StructureNode::updateWidget()
 42 {
 43     updateSize();
 44     paint();
 45 }
 46 
 47 void StructureNode::setCurrentNodeStyleSheet(const QString& style)
 48 {
 49     m_currentNodeStyleSheet = style;
 50 }
 51 
 52 void StructureNode::setCurrentNodeSize(const QSize& size)
 53 {
 54     m_currentNodeSize = size;
 55 }
 56 
 57 void StructureNode::setEndNodeSize(const QSize& size)
 58 {
 59     M_endNodeSize = size;
 60 }
 61 
 62 void StructureNode::updateSize()
 63 {
 64     // 更新子节点尺寸
 65     for(int i = 0; i < m_childNodes.size(); i++)
 66     {
 67         m_childNodes[i]->updateSize();
 68     }
 69 
 70     // 计算当前节点大小
 71     int childWidthSum = getChildWidthSum();
 72     int width = 0;
 73     int height = 0;
 74     if(childWidthSum == 0)
 75     {
 76         width = M_endNodeSize.width();
 77         height = M_endNodeSize.height();
 78     }
 79     else
 80     {
 81         width = childWidthSum;
 82         if(width < c_nodeSize.width())
 83         {
 84             width = c_nodeSize.width();
 85         }
 86 
 87         height = c_nodeSize.height() + c_fatherNodeMinGap + getChildMaxHeight();
 88     }
 89 
 90     this->resize(width, height);
 91 }
 92 
 93 void StructureNode::paint()
 94 {
 95     paintLabel();
 96 
 97     if(m_childNodes.size() != 0)
 98     {
 99         paintChildren();
100         paintLine();
101     }
102 }
103 
104 void StructureNode::paintLabel()
105 {
106     m_currentLabel = new QLabel(m_data.labelText, this);
107     m_currentLabel->installEventFilter(this);
108     if(m_childNodes.size() == 0)
109     {
110         m_currentLabel->setWordWrap(true);
111         m_currentLabel->setAlignment(Qt::AlignCenter);
112         m_currentLabel->setStyleSheet(c_endNodeStyleSheet);
113         if(m_currentNodeSize != QSize(0, 0))
114         {
115             m_currentLabel->resize(m_currentNodeSize);
116         }
117         else
118         {
119             m_currentLabel->resize(M_endNodeSize);
120         }
121 
122         m_currentLabel->move(0, 0);
123     }
124     else
125     {
126         m_currentLabel->setAlignment(Qt::AlignCenter);
127         if(m_currentNodeSize != QSize(0, 0))
128         {
129             m_currentLabel->resize(m_currentNodeSize);
130         }
131         else
132         {
133             m_currentLabel->resize(c_nodeSize);
134         }
135 
136         if(this->width() == m_currentLabel->width())
137         {
138             m_currentLabel->move(0, 0);
139         }
140         else
141         {
142             m_currentLabel->move((this->width() - m_currentLabel->width()) / 2, 0);
143         }
144     }
145 
146     if(!m_currentNodeStyleSheet.isEmpty())
147     {
148         m_currentLabel->setStyleSheet(m_currentNodeStyleSheet);
149     }
150 }
151 
152 void StructureNode::paintChildren()
153 {
154     Q_ASSERT(m_currentLabel);
155 
156     int currentPaintXPos = 0;
157     int currentPaintYPos = 0;
158     if(this->width() == m_currentLabel->width())
159     {
160         currentPaintXPos = (m_currentLabel->width() - getChildWidthSum()) / 2;
161     }
162     currentPaintYPos = m_currentLabel->height() + c_fatherNodeMinGap;
163 
164     StructureNode* node = nullptr;
165     for(int i = 0; i < m_childNodes.size(); i++)
166     {
167         node = m_childNodes[i];
168         node->paint();
169         node->move(currentPaintXPos, currentPaintYPos);
170 
171         currentPaintXPos += node->width() + c_brotherNodeMinGap * (node->hasChild() ? 2 : 1);
172     }
173 }
174 
175 void StructureNode::paintLine()
176 {
177     Q_ASSERT(m_currentLabel);
178 
179     // 绘制连接线
180     int currentPaintYPos = m_currentLabel->height();
181     QFrame* line = nullptr;
182     // 母线
183     line = new QFrame(this);
184     line->setStyleSheet(c_lineStyleSheet);
185     line->resize(1, c_fatherNodeMinGap / 2);
186     line->move(m_currentLabel->pos().x() + m_currentLabel->width() / 2, currentPaintYPos);
187     currentPaintYPos += line->height();
188 
189     // 水平线
190     StructureNode* node1 = m_childNodes.first();
191     StructureNode* node2 = m_childNodes.last();
192     if(node1 != node2)
193     {
194         line = new QFrame(this);
195         line->setStyleSheet(c_lineStyleSheet);
196         line->resize(node2->pos().x() + (node2->width() - node1->width()) / 2, 1);
197         line->move(node1->width() / 2, currentPaintYPos);
198     }
199 
200     // 子线
201     StructureNode* node = nullptr;
202     for(int i = 0; i < m_childNodes.size(); i++)
203     {
204         node = m_childNodes[i];
205         line = new QFrame(this);
206         line->setStyleSheet(c_lineStyleSheet);
207         line->resize(1, c_fatherNodeMinGap / 2 - 9);
208         line->move(node->pos().x() + node->width() / 2, currentPaintYPos);
209     }
210 
211     // 子线箭头
212     currentPaintYPos += line->height();
213     for(int i = 0; i < m_childNodes.size(); i++)
214     {
215         node = m_childNodes[i];
216         line = new QFrame(this);
217         line->setStyleSheet(c_arrowStyleSheet);
218         line->resize(8, 8);
219         line->move(node->pos().x() + (node->width() - 8) / 2, currentPaintYPos);
220     }
221 }
222 
223 int StructureNode::getChildWidthSum()
224 {
225     int childWidthSum = 0;
226 
227     int chileCount = m_childNodes.size();
228     for(int i = 0; i < chileCount - 1; i++)
229     {
230         childWidthSum += m_childNodes[i]->width() + c_brotherNodeMinGap  * (m_childNodes[i]->hasChild() ? 2 : 1);
231     }
232     if(chileCount > 1)
233     {
234         childWidthSum += m_childNodes.last()->width();
235     }
236 
237     return childWidthSum;
238 }
239 
240 int StructureNode::getChildMaxHeight()
241 {
242     int childMaxHeight = 0;
243 
244     for(int i = 0; i < m_childNodes.size(); i++)
245     {
246         if(childMaxHeight < m_childNodes[i]->height())
247         {
248             childMaxHeight = m_childNodes[i]->height();
249         }
250     }
251 
252     return childMaxHeight;
253 }
254 
255 bool StructureNode::eventFilter(QObject* obj, QEvent* event)
256 {
257     switch(event->type())
258     {
259         case QEvent::Enter:
260             if(obj == m_currentLabel)
261             {
262                 if(!m_data.tipText.isEmpty())
263                 {
264                     QFont font;
265                     font.setPixelSize(16);
266                     QToolTip::setFont(font);  //设置ToolTip字体
267 
268                     QToolTip::showText(QCursor::pos(), m_data.tipText);
269                 }
270             }
271             break;
272         default:
273             break;
274     }
275 
276     return QWidget::eventFilter(obj, event);
277 }
View Code
复制代码

ToolTip.h

复制代码
 1 #ifndef TOOLTIP_H
 2 #define TOOLTIP_H
 3 
 4 #include <QWidget>
 5 #include <QLabel>
 6 #include "CustomWidget.h"
 7 
 8 class ToolTip : public CustomWidget
 9 {
10     Q_OBJECT
11 public:
12     explicit ToolTip(QWidget* parent = nullptr);
13 
14     static ToolTip* getInstance();
15     void setTextFont(const QFont& font);
16     void showText(const QPoint& point, const QString& text);
17 
18 signals:
19 
20 public slots:
21 
22 private:
23     /*
24      * 功能:根据换行符拆分字符串,计算字符串最大宽度
25     */
26     QSize getTextMaxSize(const QString& text, const QFont& font);
27 
28     static ToolTip* m_tipLabel;
29     QLabel* m_label;
30 };
31 
32 #endif // TOOLTIP_H
View Code
复制代码

ToolTip.cpp

复制代码
 1 #include "ToolTip.h"
 2 #include <QHBoxLayout>
 3 #include <QFontMetricsF>
 4 
 5 ToolTip* ToolTip::m_tipLabel = nullptr;
 6 const QString C_tipStyleSheet = "background-color: #FFFFFF;border:none; border-radius: 8px;";
 7 
 8 ToolTip* ToolTip::getInstance()
 9 {
10     if(m_tipLabel == nullptr)
11     {
12         m_tipLabel = new ToolTip;
13     }
14 
15     return m_tipLabel;
16 }
17 
18 void ToolTip::setTextFont(const QFont& font)
19 {
20     if(m_label != nullptr)
21     {
22         m_label->setFont(font);
23     }
24 }
25 
26 void ToolTip::showText(const QPoint& point, const QString& text)
27 {
28     if(!text.isEmpty())
29     {
30         this->move(point);
31         auto size = getTextMaxSize(text, m_label->font());
32         m_label->setText(text);
33         m_label->setFixedSize(size);
34 
35         this->resize(size.width() + 20, size.height() + 20);
36         this->show();
37         this->raise();
38     }
39 }
40 
41 QSize ToolTip::getTextMaxSize(const QString& text, const QFont& font)
42 {
43     QFontMetricsF fontMetrics(font);
44     QStringList strs = text.split("\n");
45 
46     float maxWidth = 0;
47     float height = fontMetrics.height() * strs.size();
48     for(int i = 0; i < strs.size(); ++i)
49     {
50         float width = fontMetrics.width(strs[i]);
51         if(width > maxWidth)
52         {
53             maxWidth = width;
54         }
55     }
56 
57     QSize size(maxWidth, height);
58 
59     return size;
60 }
61 
62 ToolTip::ToolTip(QWidget* parent) : CustomWidget(parent)
63 {
64     m_label = new QLabel();
65     m_label->setAlignment(Qt::AlignTop | Qt::AlignLeft);
66     m_label->setWordWrap(true);
67 
68     QHBoxLayout* layout = new QHBoxLayout;
69     layout->addWidget(m_label);
70     layout->setContentsMargins(10, 10, 10, 10);
71     layout->setSpacing(10);
72 
73     setLayout(layout);
74     setStyleSheet(C_tipStyleSheet);
75 }
View Code
复制代码

 

 

posted on   疯狂delphi  阅读(11)  评论(0编辑  收藏  举报

相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)

导航

统计

点击右上角即可分享
微信分享提示