01、重点内容公布
| Qt | windows Qt6.5.3安装&安卓环境搭建&虚拟机调试和真机调试完美版(保姆级教程) | | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | 第二章 Ubuntu22.04 Linux磁盘扩容/硬盘扩展教程 | | 基于Qt 音乐播放器mp3(进阶) |
02、Qt6.5.3 配置下快捷键
一般我将ctrl+alt+L或者shift+alt+L来展开和折叠所有代码块,方便快速定位函数位置。(这个需要自定义)
演示gif
03、前言
【1】如果之前安装前忘记安装Qt Charts,打开这个重新安装,不然我的代码你打开会显示灰色。
【2】目前我这个代码是支持Qt5.14.2,如果Qt6运行不了,记得将endl改为Qt::endl, 还有头文件改下。
04、.pro文件
requires(qtConfig(combobox)) 是一个条件预处理器指令,它用于在编译时检查 Qt 库是否支持 QComboBox 类。qtConfig() 是一个宏,它返回一个包含 Qt 配置信息的字符串。在这个例子中,combobox 是 Qt 库中的一个特性,如果 Qt 支持 QComboBox 类,那么这个条件就会为真,否则为假。通过使用 requires() 宏,可以确保只有在 Qt 支持 QComboBox 类的情况下,才会编译包含 QComboBox 相关代码的部分。这有助于避免因为缺少必要的 Qt 功能而导致编译错误。
必须添加:
QT += charts
QT += chartsrequires(qtConfig(combobox))HEADERS += \ themewidget.hSOURCES += \ main.cpp \ themewidget.cpp# $$[QT_INSTALL_EXAMPLES] = D:/Qt6/install/Examples/Qt-6.5.3target.path = $$[QT_INSTALL_EXAMPLES]/charts/chartthemes# 查看内容# message($$[QT_INSTALL_EXAMPLES])INSTALLS += targetFORMS += \ themewidget.ui
05、头文件
#ifndef THEMEWIDGET_H#define THEMEWIDGET_H#include <QtWidgets/QWidget>#include <QtCharts/QChartGlobal>QT_BEGIN_NAMESPACEclass QComboBox;class QCheckBox;class Ui_ThemeWidgetForm;QT_END_NAMESPACE#include <QChartView>#include <QtCharts>typedef QPair<QPointF, QString> Data;typedef QList<Data> DataList;typedef QList<DataList> DataTable;class ThemeWidget: public QWidget{ Q_OBJECTpublic: explicit ThemeWidget(QWidget *parent = 0); ~ThemeWidget();private Q_SLOTS: void updateUI();private: DataTable generateRandomData(int listCount, int valueMax, int valueCount) const; void populateThemeBox(); void populateAnimationBox(); void populateLegendBox(); void connectSignals(); QChart *createAreaChart() const; QChart *createBarChart(int valueCount) const; QChart *createPieChart() const; QChart *createLineChart() const; QChart *createSplineChart() const; QChart *createScatterChart() const;private: int m_listCount; int m_valueMax; int m_valueCount; QList<QChartView *> m_charts; // 存放多个视图图标 DataTable m_dataTable; Ui_ThemeWidgetForm *m_ui;};#endif /* THEMEWIDGET_H */
06、源文件
#include "themewidget.h"#include "ui_themewidget.h"#include <QtCharts/QPieSeries> // 饼图#include <QtCharts/QPieSlice>#include <QtCharts/QAbstractBarSeries>#include <QtCharts/QPercentBarSeries>#include <QtCharts/QStackedBarSeries>#include <QtCharts/QBarSeries>#include <QtCharts/QBarSet>#include <QtCharts/QLineSeries>#include <QtCharts/QSplineSeries>#include <QtCharts/QScatterSeries>#include <QtCharts/QAreaSeries>#include <QtCharts/QLegend>#include <QtWidgets/QGridLayout>#include <QtWidgets/QFormLayout>#include <QtWidgets/QComboBox>#include <QtWidgets/QSpinBox>#include <QtWidgets/QCheckBox>#include <QtWidgets/QGroupBox>#include <QtWidgets/QLabel>#include <QtCore/QRandomGenerator>#include <QtCharts/QBarCategoryAxis>#include <QtWidgets/QApplication>#include <QtCharts/QValueAxis>#include <QDebug>#include <QToolTip>// 学习官方代码规范的书写方式ThemeWidget::ThemeWidget(QWidget *parent) : QWidget(parent), m_ui(new Ui_ThemeWidgetForm){ m_ui->setupUi(this); m_listCount = 3; m_valueMax = 10; m_valueCount = 7; // 生成随机数据 m_dataTable.append(generateRandomData(m_listCount, m_valueMax, m_valueCount)); // 填充主题框 populateThemeBox(); // 填充动画框 populateAnimationBox(); // 填充图例框 populateLegendBox(); // 创建图表 QChartView *chartView; // 创建面积图 chartView = new QChartView(createAreaChart()); m_ui->gridLayout->addWidget(chartView, 1, 0); m_charts << chartView; // 创建饼图 chartView = new QChartView(createPieChart()); // 如果饼片标签不适合屏幕,就会发生有趣的事情,所以我们忽略了大小策略 chartView->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored); m_ui->gridLayout->addWidget(chartView, 1, 1); m_charts << chartView; // 创建折线图 chartView = new QChartView(createLineChart()); m_ui->gridLayout->addWidget(chartView, 1, 2); m_charts << chartView; // 创建条线图 chartView = new QChartView(createBarChart(m_valueCount)); m_ui->gridLayout->addWidget(chartView, 2, 0); m_charts << chartView; // 创建样条曲线图 chartView = new QChartView(createSplineChart()); m_ui->gridLayout->addWidget(chartView, 2, 1); m_charts << chartView; // 创建散点图 chartView = new QChartView(createScatterChart()); m_ui->gridLayout->addWidget(chartView, 2, 2); m_charts << chartView; // 设置默认值 m_ui->antialiasCheckBox->setChecked(true); // 将明亮主题中的颜色设置为默认颜色 QPalette pal = qApp->palette(); // 一般背景颜色 pal.setColor(QPalette::Window, QRgb(0xf0f0f0)); // 使用窗口文本来代替 pal.setColor(QPalette::WindowText, QRgb(0x404044)); qApp->setPalette(pal); // 更新图表 updateUI();}ThemeWidget::~ThemeWidget(){ delete m_ui;}// 生成随机数据[0]DataTable ThemeWidget::generateRandomData(int listCount, int valueMax, int valueCount) const{ // listCount = 3 valueMax = 10 valueCount = 7 qDebug () << "[generateRandomData]->[start]->listCount = " << listCount << "valueMax = " << valueMax << "valueCount = " << valueCount; DataTable dataTable; // 生成随机数据 for (int i(0); i < listCount; i++) { // 3组 DataList dataList; qreal yValue(0); qreal xValue(0); for (int j(0); j < valueCount; j++) { // 每组7个数据 // 随机数范围 = 10 / 7 ≈ 1.428571... = [0, 1.428571...) qreal yRandomNum = QRandomGenerator::global()->bounded(valueMax / (qreal) valueCount); // 范围是 [0, 7) yValue的后一个值一定大于等于前一个值(纵坐标⬆) yValue = yValue + yRandomNum; // 随机数范围 = [0, 1) qreal xRandomNum = QRandomGenerator::global()->generateDouble(); // 范围在 [0, 10) xValue的后一个值一定大于等于前一个值(横坐标->) xValue = (j + xRandomNum) * ((qreal) m_valueMax / (qreal) valueCount); qDebug() << "j = " << j << "[yValue = " << yValue << "] yRandomNum = " << yRandomNum<< " [xValue = " << xValue << "] xRandomNum = " << xRandomNum; // 坐标(x,y) QPointF value(xValue,yValue); QString label = "Slice " + QString::number(i) + ":" + QString::number(j); dataList << Data(value, label); // 传入((x,y),切片) } qDebug() << "dataList-size = " << dataList.size() << Qt::endl; dataTable << dataList; } //qDebug () << " dataTable = " << dataTable << " size = " << dataTable.size(); qDebug () << "[generateRandomData]->[end]->listCount = " << listCount << "valueMax = " << valueMax << "valueCount = " << valueCount << Qt::endl; return dataTable;}// 填充主题框[1]void ThemeWidget::populateThemeBox(){ // 向主题组合框中添加项目 m_ui->themeComboBox->addItem("明亮色", QChart::ChartThemeLight); m_ui->themeComboBox->addItem("天蓝色", QChart::ChartThemeBlueCerulean); m_ui->themeComboBox->addItem("黑暗色", QChart::ChartThemeDark); m_ui->themeComboBox->addItem("沙棕色", QChart::ChartThemeBrownSand); m_ui->themeComboBox->addItem("自然色彩系统(ncs)的蓝色", QChart::ChartThemeBlueNcs); m_ui->themeComboBox->addItem("高对比度", QChart::ChartThemeHighContrast); m_ui->themeComboBox->addItem("冰冷的蓝色", QChart::ChartThemeBlueIcy); m_ui->themeComboBox->addItem("Qt", QChart::ChartThemeQt);}// 填充动画框[2]void ThemeWidget::populateAnimationBox(){ // 将项目添加到动画组合框中 m_ui->animatedComboBox->addItem("动画在图表中被禁用", QChart::NoAnimation); m_ui->animatedComboBox->addItem("图表中启用网格轴动画", QChart::GridAxisAnimations); m_ui->animatedComboBox->addItem("图表中启用系列动画", QChart::SeriesAnimations); m_ui->animatedComboBox->addItem("图表中启用所有动画类型", QChart::AllAnimations);}// 填充图例框[3]void ThemeWidget::populateLegendBox(){ // 向图例组合框中添加项目 m_ui->legendComboBox->addItem("图例 无", 0); m_ui->legendComboBox->addItem("图例顶部", Qt::AlignTop); m_ui->legendComboBox->addItem("图例底部", Qt::AlignBottom); m_ui->legendComboBox->addItem("图例左部", Qt::AlignLeft); m_ui->legendComboBox->addItem("图例右部", Qt::AlignRight);}// 面积图[4]QChart *ThemeWidget::createAreaChart() const{ qDebug () << "[createAreaChart]->[start]->m_dataTable.count = " << m_dataTable.count(); QChart *chart = new QChart(); chart->setTitle("面积图(Area chart)"); // 下级数 QLineSeries *lowerSeries = 0; QString name("Series "); int nameIndex = 0; for (int i(0); i < m_dataTable.count(); i++) { // 3组 // 上级数 QLineSeries *upperSeries = new QLineSeries(chart); qDebug () << "m_dataTable[i].count = " << m_dataTable[i].count(); for (int j(0); j < m_dataTable[i].count(); j++) { // 每组7个数据 Data data = m_dataTable[i].at(j); if (lowerSeries) { const QVector<QPointF>& points = lowerSeries->pointsVector(); upperSeries->append(QPointF(j, points[i].y() + data.first.y())); qDebug () << "[2] j = " << j <<" data.first.y = " << data.first.y(); } else { qDebug () << "[1] j = " << j <<" data.first.y = " << data.first.y(); upperSeries->append(QPointF(j, data.first.y())); } } // 以面积图的形式显示数据 QAreaSeries *area = new QAreaSeries(upperSeries, lowerSeries); area->setName(name + QString::number(nameIndex)); nameIndex++; // 将系列添加到图表中 chart->addSeries(area); lowerSeries = upperSeries; qDebug () << "lowerSeries = " << lowerSeries->pointsVector() << Qt::endl; } // 根据已添加到图表中的系列为图表创建轴。先前添加到图表中的任何轴都将被删除。 chart->createDefaultAxes(); chart->axes(Qt::Horizontal).first()->setRange(0, m_valueCount - 1); chart->axes(Qt::Vertical).first()->setRange(0, m_valueMax); // 添加要添加标签的空间以添加标签和轴之间的空间 QValueAxis *axisY = qobject_cast<QValueAxis*>(chart->axes(Qt::Vertical).first()); Q_ASSERT(axisY); // 判断指针是否为空 axisY->setLabelFormat("%.1f "); return chart;}// 创建条形图[5]QChart *ThemeWidget::createBarChart(int valueCount) const{ qDebug () << "[createBarChart]->[start]->valueCount = " << valueCount << Qt::endl; Q_UNUSED(valueCount); QChart *chart = new QChart(); chart->setTitle("条形图(Bar chart)"); QStackedBarSeries *series = new QStackedBarSeries(chart); for (int i(0); i < m_dataTable.count(); i++) { // 表示条形图中的一组条形图 QBarSet *set = new QBarSet("Bar set " + QString::number(i)); for (const Data &data : m_dataTable[i]) { *set << data.first.y(); } series->append(set); } // 将系列系列添加到图表中 chart->addSeries(series); chart->createDefaultAxes(); chart->axes(Qt::Vertical).first()->setRange(0, m_valueMax * 2); // 添加要添加标签的空间以添加标签和轴之间的空间 QValueAxis *axisY = qobject_cast<QValueAxis*>(chart->axes(Qt::Vertical).first()); Q_ASSERT(axisY); axisY->setLabelFormat("%.1f "); return chart;}// 创建折线图[6]QChart *ThemeWidget::createLineChart() const{ QChart *chart = new QChart(); chart->setTitle("线形图(Line chart)"); QString name("Series "); int nameIndex = 0; for (const DataList &list : m_dataTable) { QLineSeries *series = new QLineSeries(chart); for (const Data &data : list) { //series->append(data.first); QPointF point(data.first.x(), data.first.y()); series->append(point); } // 显示点 series->setPointsVisible(true); // 显示坐标值,只显示最后一组的(x,y) series->setPointLabelsVisible(true); series->setPointLabelsColor(QColor(255, 0, 255)); series->setPointLabelsFormat("(@xPoint, @yPoint)"); series->setPointLabelsFont(QFont("微软雅黑", 5)); series->setPointLabelsClipping(false); // 此属性默认为true。当启用剪裁时,将在打印区域的边缘切割标签。 // 显示鼠标悬停的坐标值 connect(series, &QSplineSeries::hovered, this, [&](const QPointF &point, bool state){ if (state) { qDebug() << "point = " << point << " state = " << state; QToolTip::showText(QCursor::pos(), QString("(%1,%2)").arg(point.x()).arg(point.y()), nullptr, QRect(), 5000); } }); series->setName(name + QString::number(nameIndex)); nameIndex++; chart->addSeries(series); } // 根据已添加到图表中的系列为图表创建轴。先前添加到图表中的任何轴都将被删除。 chart->createDefaultAxes(); chart->axes(Qt::Horizontal).first()->setRange(0, m_valueMax); chart->axes(Qt::Vertical).first()->setRange(0, m_valueCount); chart->axes(Qt::Horizontal).first()->setMin(0); chart->axes(Qt::Horizontal).first()->setMax(m_valueMax); // 添加要添加标签的空间以添加标签和轴之间的空间 QValueAxis *axisY = qobject_cast<QValueAxis*>(chart->axes(Qt::Vertical).first()); Q_ASSERT(axisY); axisY->setLabelFormat("%.1f "); // 设置刻度计数 刻度越大 纵坐标间距越小 axisY->setTickCount(5); QValueAxis *axisX = qobject_cast<QValueAxis*>(chart->axes(Qt::Horizontal).first()); Q_ASSERT(axisX); axisX->setLabelFormat("%.1f "); // 设置刻度计数 刻度越大 横坐标间距越小 axisX->setTickCount(5); // 设置坐标轴的颜色,粗细,设置网格 axisY->setLinePenColor(QColor(Qt::darkCyan)); axisX->setLinePenColor(QColor(Qt::darkCyan)); return chart;}// 创建饼图[7]QChart *ThemeWidget::createPieChart() const{ qDebug () << "[createPieChart]->[start]->m_dataTable.count = " << m_dataTable.count(); QChart *chart = new QChart(); chart->setTitle("饼图(Pie chart)"); QPieSeries *series = new QPieSeries(chart); // 这里只是以第一组数据作为基准 for (const Data &data : m_dataTable[0]) { QPieSlice *slice = series->append(data.second, data.first.y()); qDebug () << "[createPieChart]->[start]->data.second = " << data.first.y(); if (data == m_dataTable[0].first()) { // 标签可见。 slice->setLabelVisible(true); // 此属性用于保存切片是否与饼图分离true slice->setExploded(true); // 确定切片离饼的距离 slice->setExplodeDistanceFactor(0.5); } } // 设置饼图大小 series->setPieSize(0.4); // 将系列系列添加到图表中 chart->addSeries(series); qDebug () << "[createPieChart]->[start]->m_dataTable.count = " << m_dataTable.count() << Qt::endl; return chart;}// 创建样条曲线图[8]QChart *ThemeWidget::createSplineChart() const{ QChart *chart = new QChart(); chart->setTitle("样条曲线图(Spline chart)"); QString name("Series "); int nameIndex = 0; for (const DataList &list : m_dataTable) { QSplineSeries *series = new QSplineSeries(chart); for (const Data &data : list) { series->append(data.first); } // 显示点 series->setPointsVisible(true); // 显示坐标值,只显示最后一组的(x,y) series->setPointLabelsVisible(true); series->setPointLabelsColor(QColor(Qt::black)); series->setPointLabelsFormat("(@xPoint, @yPoint)"); series->setPointLabelsFont(QFont("微软雅黑", 5)); series->setPointLabelsClipping(false); // 此属性默认为true。当启用剪裁时,将在打印区域的边缘切割标签。 // 显示鼠标悬停的坐标值 connect(series, &QSplineSeries::hovered, this, [&](const QPointF &point, bool state){ if (state) { qDebug() << "point = " << point << " state = " << state; QToolTip::showText(QCursor::pos(), QString("(%1,%2)").arg(QString::number(point.x(),'f',2)).arg(QString::number(point.y(),'f',2)), nullptr, QRect(), 5000); } }); series->setName(name + QString::number(nameIndex)); nameIndex++; // 将系列系列添加到图表中(addSeries必须写在坐标设置setAxisX、setAxisY之前,否则会引起坐标异常。) chart->addSeries(series); } // 根据已添加到图表中的系列为图表创建轴。先前添加到图表中的任何轴都将被删除。 chart->createDefaultAxes(); chart->axes(Qt::Horizontal).first()->setRange(0, m_valueMax); chart->axes(Qt::Vertical).first()->setRange(0, m_valueCount); // 添加要添加标签的空间以添加标签和轴之间的空间 QValueAxis *axisY = qobject_cast<QValueAxis*>(chart->axes(Qt::Vertical).first()); Q_ASSERT(axisY); axisY->setLabelFormat("%.1f "); return chart;}// 创建散点图[9]QChart *ThemeWidget::createScatterChart() const{ QChart *chart = new QChart(); chart->setTitle("散点图(Scatter chart)"); QString name("Series "); int nameIndex = 0; for (const DataList &list : m_dataTable) { // 3组 QScatterSeries *series = new QScatterSeries(chart); for (const Data &data : list) { // 每组7个数据 series->append(data.first); //qDebug()<<"data.first = " << data.first << " data.second = " << data.second; } series->setName(name + QString::number(nameIndex)); nameIndex++; chart->addSeries(series); } chart->createDefaultAxes(); chart->axes(Qt::Horizontal).first()->setRange(0, m_valueMax); chart->axes(Qt::Vertical).first()->setRange(0, m_valueCount); // 添加要添加标签的空间以添加标签和轴之间的空间 QValueAxis *axisY = qobject_cast<QValueAxis*>(chart->axes(Qt::Vertical).first()); Q_ASSERT(axisY); axisY->setLabelFormat("%.1f "); return chart;}// 更新图表void ThemeWidget::updateUI(){ QChart::ChartTheme theme = static_cast<QChart::ChartTheme>( m_ui->themeComboBox->itemData(m_ui->themeComboBox->currentIndex()).toInt()); qDebug () << "[updateUI]->[start]->themeComboBox = " << m_ui->themeComboBox->itemData(m_ui->themeComboBox->currentIndex()) << Qt::endl; // 设置主题颜色 const auto charts = m_charts; if (!m_charts.isEmpty() && m_charts.at(0)->chart()->theme() != theme) { for (QChartView *chartView : charts) { chartView->chart()->setTheme(theme); } // 根据选定的主题设置调色板的颜色 //![8] QPalette pal = window()->palette(); if (theme == QChart::ChartThemeLight) { pal.setColor(QPalette::Window, QRgb(0xf0f0f0)); pal.setColor(QPalette::WindowText, QRgb(0x404044)); } else if (theme == QChart::ChartThemeDark) { pal.setColor(QPalette::Window, QRgb(0x121218)); pal.setColor(QPalette::WindowText, QRgb(0xd6d6d6)); } else if (theme == QChart::ChartThemeBlueCerulean) { pal.setColor(QPalette::Window, QRgb(0x40434a)); pal.setColor(QPalette::WindowText, QRgb(0xd6d6d6)); } else if (theme == QChart::ChartThemeBrownSand) { pal.setColor(QPalette::Window, QRgb(0x9e8965)); pal.setColor(QPalette::WindowText, QRgb(0x404044)); } else if (theme == QChart::ChartThemeBlueNcs) { pal.setColor(QPalette::Window, QRgb(0x018bba)); pal.setColor(QPalette::WindowText, QRgb(0x404044)); } else if (theme == QChart::ChartThemeHighContrast) { pal.setColor(QPalette::Window, QRgb(0xffab03)); pal.setColor(QPalette::WindowText, QRgb(0x181818)); } else if (theme == QChart::ChartThemeBlueIcy) { pal.setColor(QPalette::Window, QRgb(0xcee7f0)); pal.setColor(QPalette::WindowText, QRgb(0x404044)); } else { pal.setColor(QPalette::Window, QRgb(0xf0f0f0)); pal.setColor(QPalette::WindowText, QRgb(0x404044)); } window()->setPalette(pal); } // [1]更新反锯齿 bool checked = m_ui->antialiasCheckBox->isChecked(); for (QChartView *chart : charts) chart->setRenderHint(QPainter::Antialiasing, checked); // [2]更新动画选项 QChart::AnimationOptions options( m_ui->animatedComboBox->itemData(m_ui->animatedComboBox->currentIndex()).toInt()); qDebug () << "[updateUI]->[start]->animatedComboBox = " << m_ui->animatedComboBox->itemData(m_ui->animatedComboBox->currentIndex()) << Qt::endl; if (!m_charts.isEmpty() && m_charts.at(0)->chart()->animationOptions() != options) { for (QChartView *chartView : charts) chartView->chart()->setAnimationOptions(options); } // [3]更新图例对齐 Qt::Alignment alignment( m_ui->legendComboBox->itemData(m_ui->legendComboBox->currentIndex()).toInt()); qDebug () << "[updateUI]->[start]->legendComboBox = " << m_ui->legendComboBox->itemData(m_ui->legendComboBox->currentIndex()) << Qt::endl; if (!alignment) { for (QChartView *chartView : charts) chartView->chart()->legend()->hide(); } else { for (QChartView *chartView : charts) { chartView->chart()->legend()->setAlignment(alignment); chartView->chart()->legend()->show(); } }}
07、main.cpp
#include "themewidget.h"#include <QtWidgets/QApplication>#include <QtWidgets/QMainWindow>int main(int argc, char *argv[]){ QApplication a(argc, argv); QMainWindow window; ThemeWidget *widget = new ThemeWidget(); window.setCentralWidget(widget); window.resize(900, 600); window.show(); return a.exec();}
08、themewidget.ui
09、运行演示
10、运行截图
总结
【1】遇到问题逐一排查;
【2】这个图表其实挺通用的,修改参数即可;
【3】支持跨平台运行(嵌入式、安卓)。
喜欢的支持一下。