163 lines
4.9 KiB
C++
163 lines
4.9 KiB
C++
#include "CurvePlotWidget.h"
|
|
#include <QPainter>
|
|
#include <QtMath>
|
|
#include <limits>
|
|
#include <QDebug>
|
|
#include <QTime>
|
|
CurvePlotWidget::CurvePlotWidget(QWidget *parent) : QWidget(parent)
|
|
{
|
|
setMinimumSize(300, 200);
|
|
}
|
|
|
|
void CurvePlotWidget::setTimeRange(double secs)
|
|
{
|
|
m_tRange = qMax(secs, 1.0);
|
|
m_xMax = qMax(200, int(width() / 2));
|
|
for (auto &c : m_curves) {
|
|
c.tBuf.resize(m_xMax);
|
|
c.yBuf.resize(m_xMax);
|
|
c.head = 0;
|
|
}
|
|
update();
|
|
}
|
|
|
|
void CurvePlotWidget::initCurve(int id, const CurveCfg &cfg)
|
|
{
|
|
Curve c;
|
|
c.cfg = cfg;
|
|
// 确保阈值有效
|
|
Q_ASSERT(cfg.thWarning < cfg.thFault);
|
|
Q_ASSERT(cfg.thFault <= cfg.yMax);
|
|
c.tBuf.resize(m_xMax);
|
|
c.yBuf.resize(m_xMax);
|
|
c.tBuf.fill(std::numeric_limits<double>::quiet_NaN());
|
|
c.yBuf.fill(std::numeric_limits<double>::quiet_NaN());
|
|
c.head = 0;
|
|
m_curves.insert(id, c);
|
|
}
|
|
|
|
void CurvePlotWidget::clearAllCurves()
|
|
{
|
|
m_curves.clear();
|
|
m_t0set = false;
|
|
update();
|
|
}
|
|
|
|
void CurvePlotWidget::appendPoint(int curveId, double timeStamp, double yValue)
|
|
{
|
|
if (!m_curves.contains(curveId)) return;
|
|
Curve &c = m_curves[curveId];
|
|
if (!m_t0set) {
|
|
m_t0 = timeStamp;
|
|
m_t0set = true;
|
|
}
|
|
double t = timeStamp - m_t0;
|
|
c.tBuf[c.head] = t;
|
|
c.yBuf[c.head] = yValue;
|
|
c.head = (c.head + 1) % m_xMax;
|
|
update();
|
|
}
|
|
|
|
void CurvePlotWidget::paintEvent(QPaintEvent *)
|
|
{
|
|
QPainter p(this);
|
|
p.setRenderHint(QPainter::Antialiasing);
|
|
|
|
QRectF plotRect(m_leftMargin, m_topMargin,
|
|
width() - m_leftMargin - m_rightMargin,
|
|
height() - m_topMargin - m_bottomMargin);
|
|
|
|
/* ---------- 1. 顶部图例 ---------- */
|
|
int legendX = m_leftMargin + 5;
|
|
int legendY = m_topMargin - 15;
|
|
for (const auto &c : m_curves) {
|
|
p.setPen(QPen(c.cfg.normal, 2));
|
|
p.drawLine(legendX, legendY, legendX + 15, legendY);
|
|
p.setPen(QPen(Qt::white));
|
|
p.drawText(legendX + 20, legendY + 4, c.cfg.name);
|
|
legendX += 20 + p.fontMetrics().horizontalAdvance(c.cfg.name) + 15;
|
|
}
|
|
|
|
/* ---------- 2. 坐标轴 ---------- */
|
|
p.setPen(Qt::white);
|
|
/* Y 轴刻度 */
|
|
if (!m_curves.isEmpty()) {
|
|
const Curve &c = *m_curves.begin();
|
|
double yMax = c.cfg.yMax;
|
|
for (int i = 0; i <= 4; ++i) {
|
|
double y = yMax * i / 4.0;
|
|
QString txt = QString::number(y, 'f', 1);
|
|
int ty = int(plotRect.bottom() - i * plotRect.height() / 4.0);
|
|
p.drawText(5, ty + 4, txt);
|
|
}
|
|
}
|
|
/* ---------- X 轴刻度(真实时钟) ---------- */
|
|
// 当前这一帧可见窗口的绝对时间范围
|
|
double tLeftEpoch = QDateTime::currentDateTime().toSecsSinceEpoch() - m_tRange;
|
|
double tRightEpoch = QDateTime::currentDateTime().toSecsSinceEpoch();
|
|
|
|
for (int i = 0; i <= 4; ++i) {
|
|
double tEpoch = tLeftEpoch + m_tRange * i / 4.0; // 绝对秒
|
|
QDateTime tm = QDateTime::fromSecsSinceEpoch(qint64(tEpoch)); // 真实时钟
|
|
QString txt = tm.toString("hh:mm:ss");
|
|
|
|
int tx = int(plotRect.left() + i * plotRect.width() / 4.0);
|
|
p.drawText(tx - 25, height() - 5, txt);
|
|
}
|
|
|
|
/* ---------- 4. 曲线 ---------- */
|
|
for (const auto &c : m_curves) {
|
|
if (m_xMax <= 1) continue;
|
|
|
|
// 改成同时缓存 QPointF 和 对应的 y 值
|
|
struct Pt {
|
|
QPointF pos;
|
|
double y;
|
|
};
|
|
QVector<Pt> pts;
|
|
pts.reserve(m_xMax);
|
|
|
|
for (int i = 0; i < m_xMax; ++i) {
|
|
int idx = (c.head + i) % m_xMax;
|
|
double t = c.tBuf[idx];
|
|
double y = c.yBuf[idx];
|
|
if (std::isnan(t) || std::isnan(y))
|
|
continue;
|
|
pts.append({mapToWidget(t, y, c), y});
|
|
}
|
|
if (pts.size() < 2) continue;
|
|
|
|
// 逐段画线,颜色直接用 pts[k].y
|
|
for (int k = 0; k < pts.size() - 1; ++k) {
|
|
double y = pts[k].y;
|
|
int level = 0;
|
|
if (y >= c.cfg.thFault) level = 2;
|
|
else if (y >= c.cfg.thWarning) level = 1;
|
|
|
|
QColor col = level == 0 ? c.cfg.normal
|
|
: level == 1 ? c.cfg.warning
|
|
: c.cfg.fault;
|
|
|
|
p.setPen(QPen(col, 2));
|
|
p.drawLine(pts[k].pos, pts[k + 1].pos);
|
|
}
|
|
}
|
|
}
|
|
|
|
QPointF CurvePlotWidget::mapToWidget(double t, double y, const Curve &c) const
|
|
{
|
|
QRectF plotRect(m_leftMargin, m_topMargin,
|
|
width() - m_leftMargin - m_rightMargin,
|
|
height() - m_topMargin - m_bottomMargin);
|
|
|
|
double x = plotRect.left() + (t / m_tRange) * plotRect.width();
|
|
double norm = qBound(0.0, y / c.cfg.yMax, 1.0);
|
|
double wy = plotRect.bottom() - norm * plotRect.height();
|
|
return QPointF(x, wy);
|
|
}
|
|
|
|
void CurvePlotWidget::resizeEvent(QResizeEvent *)
|
|
{
|
|
setTimeRange(m_tRange); // 按新宽度重新计算点数
|
|
}
|