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 }
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
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 }
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>
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
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 }
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
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 }
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
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 }
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
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 }
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
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 }
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
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 }
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
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 }
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
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 }
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
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 }
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
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(); }
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
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 }
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
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 }
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
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 }
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
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 }
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
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 }
作者:疯狂Delphi
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.
欢迎关注我,一起进步!扫描下方二维码即可加我


分类:
《21天学通Qt5》
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)