QCustomPlot的使用介绍

目录

一、项目背景

二、常用的绘图以及注意事项

三、曲线图绘制实例

3.1 绘制主代码

3.2点击显示值功能

3.3右键导出功能


一、项目背景


        在停车项目中Qt自带的QChart模块在可视化数据方面有着很好的应用,但是在开发道路车流数据可视化大屏项目时由于数据量大,相机和雷达数据实时刷新频繁,QChart性能上有点跟不上;后续更换QCustomPlot库后解决该问题,促进项目落地验收。
        使用避坑:确保正确添加 printsupport 模块;绘图画板用QWidget并准确提升控件QCustomPlot;下载稳定的版本注意适用的Qt版本。

二、常用的绘图以及注意事项

        以下是关于 QCustomPlot 绘制不同类型图表的核心方法、常用函数和核心类的详细表格。所有条目基于典型用法,并已按图表类型分类说明:

图表类型核心类/对象核心方法/步骤常用函数注意事项与关键特性
折线图QCPGraph

1. 调用 addGraph() 创建图形

2. 准备 QVector 类型的 x、y 数据

3. 使用 graph->setData(x, y) 设置数据

4. 可选设置线型、颜色、图例名称等

addGraph()

setData()

setPen()

setName()

rescaleAxes()

确保 x、y 向量长度一致;大数据量时优先使用 setData(QCPGraph::lsNone) 关闭插值以提高性能
散点图QCPGraph

1. 使用 addGraph() 添加图形

2. 关闭线条显示setLineStyle(QCPGraph::lsNone)

3. 设置 setScatterStyle() 定义点的形状、大小、颜色

setScatterStyle()

setLineStyle()

可搭配误差棒使用;支持自定义形状(如 QCPScatterStyle::ssCircle
柱状图QCPBars

1. 使用 addPlottable(new QCPBars(xAxis, yAxis))

2. 设置柱宽、偏移量、填充颜色等

3. 调用 setData() 传入 x(位置)和 y(高度)数据

QCPBars()

setWidth()

setData()

setBrush()

x 值为分类位置(如 1,2,3…);支持堆叠显示;需手动设置 x 轴标签
曲线图(平滑)QCPGraph

1. 创建图形(addGraph()

2. 设置 setSmooth(true) 启用平滑插值3. 使用 setData() 传入数据后刷新

setSmooth()

setData()

平滑处理会增加计算量,大数据量慎用;可通过 setSmoothScaler() 调整平滑度
等高线/热图QCPColorMap

1. 实例化 QCPColorMap(xAxis, yAxis) 并 addPlottable()

2. 使用 setData() 传入二维矩阵(QCPColorMapData)

3. 设置色阶(setColorScale()

QCPColorMap()

setData()

setColorScale()setGradient()

数据需为规整网格;支持交互缩放时动态更新色阶范围
误差棒图QCPErrorBars

1. 创建误差棒对象(new QCPErrorBars(xAxis, yAxis)

2. 使用 setDataPlottable() 关联已有图形(如折线图)

3. 调用 setData() 设置误差值

QCPErrorBars()

setDataPlottable()

setData()

误差数据需与关联图形的数据点一一对应;支持只显示正误差、负误差或两者同时
饼图无内置类(需自定义)

1. 通常继承 QCPAbstractItem 或自定义绘制

2. 使用 draw() 重写绘制逻辑

3. 计算扇形角度、颜色并调用 QPainter 绘制

自定义绘制函数QCustomPlot 未原生支持,需结合 QPainter 实现;可参考社区扩展或示例代码
金融图(K线)QCPFinancial

1. 使用 addPlottable(new QCPFinancial(xAxis, yAxis))

2. 设置开盘、最高、最低、收盘数据(OHLC)

3. 调用 setData() 传入时间序列和价格数据

QCPFinancial()

setData()

setChartStyle()

支持蜡烛图、美国线等样式;时间序列需转换为 double 类型(如 QDateTime::toTime_t())

通用流程与核心类说明

  • QCustomPlot:主控件类,负责管理所有元素(坐标轴、图层、图例等)。
  • QCPAxisRect:坐标轴矩形区域,可创建多个实现多图表布局。
  • QCPLegend:图例管理,通过 legend->setVisible(true) 显示。
  • 通用步骤
    1. 初始化 QCustomPlot 控件(如在 UI 中提升或代码创建)。
    2. 添加图形/图表对象(addGraph() 或 addPlottable())。
    3. 准备数据(通常为 QVector<double> 或专用数据类如 QCPGraphData)。
    4. 设置数据并调用 rescaleAxes() 自动调整坐标范围。
    5. 定制样式(线型、颜色、标签等)。
    6. 调用 replot() 刷新显示。

常见问题提醒

  • 数据不显示:检查数据范围是否在坐标轴可见区间内,或调用 rescaleAxes()
  • 性能差:大数据量时关闭抗锯齿(setAntialiasedElements())或使用 setData(QCPGraphDataContainer) 优化。
  • 编译错误:确保 pro 文件包含 QT += widgets printsupport,并正确包含头文件。

三、曲线图绘制实例

3.1 绘制主代码

void Qmultiwidgettest::on_pB1_clicked()
{
	auto& customPlot = ui.customePlotWgt;
	connect(customPlot, &QCustomPlot::mousePress, this, &Qmultiwidgettest::on_lineMouseClick);//鼠标点击显示附近点坐标
	connect(customPlot, &QCustomPlot::mouseRelease, this, &Qmultiwidgettest::on_lineMouseRelease);//鼠标释放清除功能

	// 启用自定义Plot的右键菜单交互
	customPlot->setContextMenuPolicy(Qt::CustomContextMenu);

	// 连接右键菜单信号
	connect(customPlot, &QCustomPlot::customContextMenuRequested,
		this, &Qmultiwidgettest::showContextMenu);

	// 为了精确捕获鼠标事件,我们需要启用轨迹追踪
	customPlot->setMouseTracking(true);

	customPlot->clearGraphs();
	customPlot->addGraph();
	customPlot->graph(0)->setPen(QPen(Qt::blue)); 
	//曲线与X轴之间蓝色透明度20
	//customPlot->graph(0)->setBrush(QBrush(QColor(0, 0, 255, 20))); 

	customPlot->addGraph();
	customPlot->graph(1)->setPen(QPen(Qt::red)); 
	//两条曲线之间的填充红色透明度50
	customPlot->graph(0)->setBrush(QBrush(QColor(255, 0, 0, 50)));
	customPlot->graph(0)->setChannelFillGraph(customPlot->graph(1));

	// 显示图例
	customPlot->legend->setVisible(true);
	// 设置曲线名称(将显示在图例中)
	customPlot->graph(0)->setName("曲线1");
	// 可选:设置图例位置
	customPlot->axisRect()->insetLayout()->setInsetAlignment(0, Qt::AlignTop | Qt::AlignRight);

	customPlot->graph(1)->setName("曲线2");
	customPlot->axisRect()->insetLayout()->setInsetAlignment(1, Qt::AlignTop | Qt::AlignRight);

	//添加高亮散点图作为双击提示
	QCPGraph* highlightGraph = customPlot->addGraph();
	highlightGraph->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssCircle, Qt::red, 8)); // 红色圆点,稍大
	highlightGraph->setLineStyle(QCPGraph::lsNone);
	highlightGraph->setName("HighLight"); // 可选的,方便识别
	highlightGraph->setVisible(false); 
	highlightGraph->removeFromLegend();
	QVector<double> x(251), y0(251), y1(251);
	for (int i = 0; i<251; ++i)
	{
		x[i] = i;
		y0[i] = 5*qExp(-i / 150.0)*qCos(i / 10.0); 
		y1[i] = 5*qExp(-i / 150.0);              
	}

	//顶上和右边的xy副座标显示但隐藏刻度标签
	customPlot->xAxis2->setVisible(true);
	customPlot->xAxis2->setTickLabels(false);
	customPlot->yAxis2->setVisible(true);
	customPlot->yAxis2->setTickLabels(false);

	//主坐标轴范围变化时自动同步到辅助坐标轴
	//确保拖动 / 缩放时四个坐标轴保持联动
	connect(customPlot->xAxis, SIGNAL(rangeChanged(QCPRange)), customPlot->xAxis2, SLOT(setRange(QCPRange)));
	connect(customPlot->yAxis, SIGNAL(rangeChanged(QCPRange)), customPlot->yAxis2, SLOT(setRange(QCPRange)));

	// 将数据绑定到对应图形
	customPlot->graph(0)->setData(x, y0);
	customPlot->graph(1)->setData(x, y1);

	//rescaleAxes():坐标轴范围会被设置为刚好包围图形的所有数据点。如果之前坐标轴的范围比数据范围大,它会被缩小;如果之前范围小,则会被扩大。
	//rescaleAxes(true):确保坐标轴范围至少能容纳该图形的所有数据,但如果当前范围已经比数据范围大,则会保持不变
	customPlot->graph(0)->rescaleAxes();
	customPlot->graph(1)->rescaleAxes(true);
	
	//交互设置 启用鼠标拖动缩放、滚轮缩放和图形选择功能 选中后默认在原有基础上添加半透明蓝色
	//customPlot->graph(0)->setSelectionDecorator(nullptr);禁用选中高亮
	customPlot->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom | QCP::iSelectPlottables);

	//自定义选中效果默认叠加半透明蓝色 现在改为半透明橙色
	QCPSelectionDecorator *decorator = customPlot->graph(0)->selectionDecorator();
	// 如果装饰器不存在,先创建一个
	if (!decorator)
	{
		decorator = new QCPSelectionDecorator;
		customPlot->graph(0)->setSelectionDecorator(decorator);
	}
	// 设置选中时的笔刷(如填充色)和画笔(如边框色)
	decorator->setBrush(QBrush(QColor(255, 200, 0, 100))); // 半透明橙色填充
	decorator->setPen(QPen(Qt::red, 2)); // 红色边框,宽度为2
	customPlot->replot();  // 强制重绘图表
}

3.2点击显示值功能

void Qmultiwidgettest::on_lineMouseClick(QMouseEvent* event)
{
	auto& customPlot = ui.customePlotWgt;
	// 获取鼠标双击处的x, y坐标(相对于QCustomPlot绘图区域的坐标)
	//pixelToCoord 将像素位置转换为图表坐标轴值
	double x = customPlot->xAxis->pixelToCoord(event->pos().x());
	double y = customPlot->yAxis->pixelToCoord(event->pos().y());

	// 初始化一个变量来存储找到的数据点索引
	int dataIndex = -1;
	QCPGraph* targetGraph = nullptr;
	double minDistance = std::numeric_limits<double>::max();

	//遍历所有图形,寻找距离双击点最近的曲线和数据点
	for (int graphIndex = 0; graphIndex < customPlot->graphCount(); ++graphIndex)
	{
		QCPGraph* graph = customPlot->graph(graphIndex);
		if (!graph || graph->data()->isEmpty()) continue; // 跳过空图形

		// 在图形的数据中寻找与鼠标x坐标最接近的数据点
		QSharedPointer<QCPGraphDataContainer> data = graph->data();
		auto it = std::lower_bound(data->begin(), data->end(), x,
			[](const QCPGraphData& data, double value) { return data.key < value; });
		// 检查找到的点及其前一个点,计算与鼠标点的实际距离
		if (it != data->end())
		{
			double distance = calculateDistance(x, y, it->key, it->value);
			if (distance < minDistance)
			{
				minDistance = distance;
				dataIndex = std::distance(data->begin(), it);
				targetGraph = graph;
			}
		}
		if (it != data->begin())
		{
			--it;
			double distance = calculateDistance(x, y, it->key, it->value);
			if (distance < minDistance)
			{
				minDistance = distance;
				dataIndex = std::distance(data->begin(), it);
				targetGraph = graph;
			}
		}
	}

	// 如果找到了一个足够近的数据点(例如距离小于2像素的容差),则显示其坐标
	double toleranceInPixels = 2.0;
	double toleranceInX = customPlot->xAxis->pixelToCoord(toleranceInPixels) - customPlot->xAxis->pixelToCoord(0);
	double toleranceInY = customPlot->yAxis->pixelToCoord(0) - customPlot->yAxis->pixelToCoord(toleranceInPixels); // 注意Y轴方向

	if (targetGraph && minDistance < sqrt(toleranceInX*toleranceInX + toleranceInY*toleranceInY))
	{
		const QCPGraphData* dataPoint = targetGraph->data()->at(dataIndex);
		QString coordinateText = QString("曲线%1: X=%2, Y=%3")
			.arg(targetGraph->name())
			.arg(dataPoint->key, 0, 'f', 2) // 保留两位小数
			.arg(dataPoint->value, 0, 'f', 2);



		// 显示坐标信息(使用QToolTip或自定义QLabel)
		QToolTip::showText(event->globalPos(), coordinateText, customPlot);
		// 或者自定义的QLabel显示:
		// label->setText(coordinateText);
		// label->move(event->globalPos());
		// label->show();

		// 在显示坐标信息的同时,添加一个高亮的数据点(散点图)
		auto*highlightGraph = customPlot->graph(2);
		highlightGraph->data()->clear();
		highlightGraph->addData(dataPoint->key, dataPoint->value);
		highlightGraph->setVisible(true);
		customPlot->replot(); // 重绘图表以显示高亮点
	}
	else
	{
		// 如果点击在曲线之外,可以隐藏提示或显示其他信息
		QToolTip::hideText(); 
		auto*highlightGraph = customPlot->graph(2);
		highlightGraph->setVisible(false);
		customPlot->replot();
	}
}

void Qmultiwidgettest::on_lineMouseRelease(QMouseEvent* event)
{
	auto*highlightGraph = ui.customePlotWgt->graph(2);
	if (highlightGraph) {
		highlightGraph->setVisible(false);
		ui.customePlotWgt->replot();
	}
}

3.3右键导出功能

void Qmultiwidgettest::showContextMenu(const QPoint &pos)
{
	QMenu *menu = new QMenu(ui.customePlotWgt);

	// 添加导出动作
	QAction *exportPngAction = menu->addAction("导出为PNG");
	QAction *exportJpgAction = menu->addAction("导出为JPG");
	QAction *exportPdfAction = menu->addAction("导出为PDF");
	QAction *exportBmpAction = menu->addAction("导出为BMP");

	// 连接动作到槽函数
	connect(exportPngAction, &QAction::triggered, this, [this]() { exportPlot("PNG"); });
	connect(exportJpgAction, &QAction::triggered, this, [this]() { exportPlot("JPG"); });
	connect(exportPdfAction, &QAction::triggered, this, [this]() { exportPlot("PDF"); });
	connect(exportBmpAction, &QAction::triggered, this, [this]() { exportPlot("BMP"); });

	// 显示菜单
	menu->popup(ui.customePlotWgt->mapToGlobal(pos));
}

void Qmultiwidgettest::exportPlot(const QString &format, int factor)
{
	auto& customPlot = ui.customePlotWgt;
	QString fileName = QFileDialog::getSaveFileName(
		this,
		"保存图表",
		QDir::homePath() + "/chart." + format.toLower(),
		format + " Files (*." + format.toLower() + ")"
	);

	if (fileName.isEmpty()) return;

	bool success = false;
	//分辨率调整 对PDF清晰度无效矢量
	int width = customPlot->width()*factor;
	int height = customPlot->height()*factor;

	if (format == "PNG") {
		// PNG 是无损压缩格式,不支持“质量参数”
		success = customPlot->savePng(fileName, width, height, factor);
	}
	else if (format == "JPG") {
		//quality 1-100 质量好压缩率小
		success = customPlot->saveJpg(fileName, width, height, factor, 100);
	}
	else if (format == "PDF") {
		// 计算尺寸:以300 DPI的A4纸为例 (8.27 x 11.69英寸)
		int widthA4 = 8.27 * 300;  // 2481 pixels
		int heightA4 = 11.69 * 300; // 3507 pixels
									// 保存PDF,使用适合打印的pen模式
		success = customPlot->savePdf(fileName, widthA4, heightA4, QCP::epNoCosmetic);
	}
	else if (format == "BMP") {//BMP无压缩像素数据直接存储不考虑quality 只考虑插值
		success = customPlot->saveBmp(fileName, width, height, factor);
	}

	if (success) {
		QMessageBox::information(this, "导出成功",
			QString("图表已成功导出为%1格式").arg(format));
	}
	else {
		QMessageBox::warning(this, "导出失败",
			QString("导出%1格式失败,请检查文件路径和权限").arg(format));
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值