主要功能可用
This commit is contained in:
@@ -1,162 +0,0 @@
|
||||
#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); // 按新宽度重新计算点数
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
#ifndef CURVEPLOTWIDGET_H
|
||||
#define CURVEPLOTWIDGET_H
|
||||
|
||||
#include <QWidget>
|
||||
#include <QVector>
|
||||
#include <QColor>
|
||||
#include <QMap>
|
||||
#include <QElapsedTimer>
|
||||
|
||||
struct CurveCfg
|
||||
{
|
||||
QString name; // 图例
|
||||
double yMax; // y 满量程
|
||||
QColor normal; // 绿色(正常)
|
||||
QColor warning; // 橙色(警告)
|
||||
QColor fault; // 红色(故障)
|
||||
double thWarning; // 警告阈值
|
||||
double thFault; // 故障阈值
|
||||
};
|
||||
|
||||
class CurvePlotWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit CurvePlotWidget(QWidget *parent = nullptr);
|
||||
|
||||
/* x 轴总时长(秒) */
|
||||
void setTimeRange(double secs);
|
||||
|
||||
/* 添加一条曲线 */
|
||||
void initCurve(int id, const CurveCfg &cfg);
|
||||
|
||||
/* 清空所有曲线 */
|
||||
void clearAllCurves();
|
||||
|
||||
/* 追加数据,yValue 为物理量,timeStamp 为 QDateTime::toMSecsSinceEpoch()/1000.0 */
|
||||
void appendPoint(int curveId, double timeStamp, double yValue);
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *event) override;
|
||||
void resizeEvent(QResizeEvent *event) override;
|
||||
|
||||
private:
|
||||
struct Curve
|
||||
{
|
||||
CurveCfg cfg;
|
||||
QVector<double> tBuf;
|
||||
QVector<double> yBuf;
|
||||
int head = 0;
|
||||
};
|
||||
|
||||
double m_tRange = 10.0; // 总时长(秒)
|
||||
int m_xMax = 72000; // 内部缓冲区点数
|
||||
int m_topMargin = 40; // 顶部留图例
|
||||
int m_bottomMargin = 25; // X 轴
|
||||
int m_leftMargin = 35; // Y 轴
|
||||
int m_rightMargin = 10;
|
||||
|
||||
QMap<int, Curve> m_curves;
|
||||
double m_t0 = 0; // 第一条数据的时间基准
|
||||
bool m_t0set = false;
|
||||
|
||||
QPointF mapToWidget(double t, double y, const Curve &c) const;
|
||||
};
|
||||
|
||||
#endif // CURVEPLOTWIDGET_H
|
||||
Reference in New Issue
Block a user