工程配置
CMake文件
find_package(Qt5 COMPONENTS Charts REQUIRED)
target_link_libraries(zhd-desktop PRIVATE Qt5::Charts)
包含头文件以及名称空间(这个很重要,没有包含名称空间编译器会提示找不到相关的类型)
#include <QtCharts>
using namespace QtCharts;
初始化
初始化Chart
// 创建图表
QChart *chart = new QChart();
chart->setTitle("Valve Data");
// 隐藏图例
chart->legend()->setVisible(false);
// 创建图表视图
QChartView *chartView = new QChartView(chart);
chartView->setRenderHint(QPainter::Antialiasing);
QHBoxLayout *hLayout = new QHBoxLayout(ui->view);
hLayout->addWidget(chartView);
hLayout->setContentsMargins(0, 0, 0, 0);
波形
每一道波形就是一个QLineSeries
,每个QLineSeries
都要和X轴和Y轴进行绑定
m_temperature = new QLineSeries();
m_temperature->setColor(Qt::red);
m_temperature->setPointsVisible(true);
m_pressure = new QLineSeries();
m_pressure->setColor(Qt::blue);
m_pressure->setPointsVisible(true);
m_position = new QLineSeries();
m_position->setColor(Qt::black);
m_position->setPointsVisible(true);
m_pressureSetpt = new QLineSeries();
m_pressureSetpt->setColor(Qt::darkYellow);
m_pressureSetpt->setPointsVisible(true);
为了使波形更有区分度,需要配置不同的颜色,建议跟Y轴刻度的颜色保持一致,特别是轴比较多的情况下
void setAxisColor(QAbstractAxis *axis, const QColor &color)
{
axis->setGridLineColor(color);
axis->setLinePenColor(color);
axis->setLabelsColor(color);
axis->setTitleBrush(color);
}
X轴
X轴一般是时间轴,时分秒格式:hh:mm:ss
QDateTimeAxis *timeAxis = new QDateTimeAxis();
timeAxis->setTitleText("Time");
timeAxis->setFormat("hh:mm:ss");
chart->addAxis(timeAxis, Qt::AlignBottom);
series1->attachAxis(timeAxis);
series2->attachAxis(timeAxis);
Y轴
Y轴:左侧和右侧都可以添加刻度尺
// 左侧刻度尺
QValueAxis *pressureSetpt = new QValueAxis();
pressureSetpt->setTitleText("Pressure Setpt (Torr)");
pressureSetpt->setRange(0, 25);
chart->addAxis(pressureSetpt, Qt::AlignLeft);
series1->attachAxis(pressureSetpt);
QValueAxis *pressure = new QValueAxis();
pressure->setTitleText("Pressure (Torr)");
pressure->setRange(0, 25);
chart->addAxis(pressure, Qt::AlignLeft);
series1->attachAxis(pressure);
// 右侧刻度尺
QValueAxis *driverTemperature = new QValueAxis();
driverTemperature->setTitleText("Driver temperature");
driverTemperature->setRange(0, 100);
chart->addAxis(driverTemperature, Qt::AlignRight);
series2->attachAxis(driverTemperature);
QValueAxis *positionAxis = new QValueAxis();
positionAxis->setTitleText("Position (%)");
positionAxis->setRange(0, 100);
chart->addAxis(positionAxis, Qt::AlignRight);
series2->attachAxis(positionAxis);
绘图
实时绘制波形点:往QLineSeries里边添加数据,用append接口
QDateTime currentTime = QDateTime::currentDateTime();
if(m_resumePause) {
m_timeAxis->setRange(currentTime.addSecs(-10), currentTime);
}
if(m_channels[0]) {
m_pressure->append(currentTime.toMSecsSinceEpoch(), pressure);
if (m_pressure->count() > m_max) {
m_pressure->remove(0);
}
}
if(m_channels[1]) {
m_position->append(currentTime.toMSecsSinceEpoch(), position);
if (m_position->count() > m_max) {
m_position->remove(0);
}
}
if(m_channels[2]) {
m_pressureSetpt->append(currentTime.toMSecsSinceEpoch(), pressureSetpt);
if (m_pressureSetpt->count() > m_max) {
m_pressureSetpt->remove(0);
}
}
if(m_channels[3]) {
m_temperature->append(currentTime.toMSecsSinceEpoch(), valveTemperature);
if (m_temperature->count() > m_max) {
m_temperature->remove(0);
}
}
注意事项:
1.时间轴要实时移动到正确的时间窗口范围
2.QLineSeries
波形点之后到达一定的数据量需要删除一些点以确保buffer不会写爆(频繁申请内存导致卡顿)
进阶
QtCharts自带的时间轴比较丑陋,如果想要定制的话,可以参考以下方法进行样式修改
- 自定义
QValueAxis
作为X轴 - 捕获
QValueAxis::rangeChanged
信号,获取QChartView
里边的scene
,同时也可以获取到刻度的左右边界min
和max
- 删除
scene
(QGraphicsScene)里边的所有items
,即原有的刻度不要了 - 在
QGraphicsScene
即基础上画刻度,每一个刻度就是一个QGraphicsLineItem
代码参考
// 动态修改 X 轴标签为时间格式,并绘制刻度线
QObject::connect(axisX, &QValueAxis::rangeChanged, [axisX, chartView](qreal min, qreal max) {
QGraphicsScene *scene = chartView->scene();
// 检查 min 和 max 是否有效
if (qIsNaN(min) || qIsNaN(max) || qIsInf(min) || qIsInf(max)) {
// qWarning() << "Invalid min or max value:" << min << max;
return;
}
// 检查 plotArea 是否有效
if (chartView->chart()->plotArea().width() <= 0 || chartView->chart()->plotArea().height() <= 0) {
// qWarning() << "Invalid plot area size";
return;
}
// 清除旧的标签和刻度线
for (QGraphicsItem *item : scene->items()) {
if (item->data(0).toString() == "custom_label" || item->data(0).toString() == "custom_line") {
scene->removeItem(item);
delete item;
}
}
// 生成 10 个刻度
int tickCount = 10; // 总共 10 个刻度
qreal tickInterval = (max - min) / (tickCount - 1); // 计算刻度间隔
for (int i = 0; i < tickCount; ++i) {
qreal value = min + i * tickInterval;
// 检查 value 是否有效
if (qIsNaN(value) || qIsInf(value)) {
// qWarning() << "Invalid value:" << value;
continue;
}
QDateTime dateTime = QDateTime::fromSecsSinceEpoch(value);
// 计算刻度线的位置
qreal x = chartView->chart()->plotArea().left() +
(value - min) / (max - min) * chartView->chart()->plotArea().width();
// 检查 x 是否有效
if (qIsNaN(x) || qIsInf(x)) {
// qWarning() << "Invalid x coordinate:" << x;
continue;
}
// 只在偶数刻度绘制时间标签和黑线
if (i % 2 == 0) {
// 绘制时间标签
QString label = dateTime.toString("hh:mm:ss");
QGraphicsTextItem *textItem = scene->addText(label);
qreal textWidth = textItem->boundingRect().width(); // 获取标签宽度
qreal textHeight = textItem->boundingRect().height(); // 获取标签高度
// 调整标签位置,使其居中对齐刻度线
textItem->setPos(x - textWidth / 2, chartView->chart()->plotArea().bottom() + 10);
textItem->setData(0, "custom_label"); // 标记为自定义标签
// 绘制黑线
qreal lineLength = 10; // 刻度线长度
QGraphicsLineItem *lineItem = scene->addLine(x, chartView->chart()->plotArea().bottom(),
x, chartView->chart()->plotArea().bottom() + lineLength,
QPen(Qt::black));
lineItem->setData(0, "custom_line"); // 标记为自定义刻度线
}
}
});
打包
软件打包,需要添加Qt5Charts这个dll
Qt5Charts.dll
版权
版权问题,商用的话可能会被Qt请喝茶