|
|
#pragma once
|
|
|
|
|
|
#include <QDateTime>
|
|
|
|
|
|
#include "ZxAxisHelper.h"
|
|
|
|
|
|
// ============================================================================
|
|
|
// ZxAxisHelperDate —— 日期/时间类坐标轴的绘制辅助类
|
|
|
// ----------------------------------------------------------------------------
|
|
|
// 【约定】
|
|
|
// 1. 外部值 / m_dStart / m_dEnd / m_vecMainValues 的单位都是"小时"
|
|
|
// (与 ZxSubAxisBase 的现有约定保持一致)
|
|
|
//
|
|
|
// 2. 本 Helper 只是 把值翻译成时间字符串显示出来
|
|
|
// 它不修改 m_vecMainValues / m_dStart / m_dEnd,
|
|
|
// 不影响数据点的位置(数据点位置由 getPosForValue 决定,与本类无关)
|
|
|
//
|
|
|
// 3. 刻度的对齐、间隔的选择,全部在 Helper 内部完成,只用于"显示"
|
|
|
// 即使 m_dtBase 不是整点,刻度也落在 00:00 / 06:00 这类整时间上
|
|
|
//
|
|
|
// 4. m_dtBase 只做"值 ↔ 绝对时间"的映射:
|
|
|
// 绝对时间 = m_dtBase + 值(小时) × 3600 秒
|
|
|
// 它不参与刻度对齐。m_dtBase 是什么时间,都影响不了刻度的对齐
|
|
|
//
|
|
|
// 【使用场景】
|
|
|
// - 用户在现有 Plot 上画完图(用线性轴)
|
|
|
// - 调用 pAxis->setAxisType(AXIS_Date) 切换
|
|
|
// - ZxSubAxisBase::resetHelper 创建本类,并把 m_dtBase 传进来
|
|
|
// - 下一次 paint 时,getTickInfos / drawGrids 自动按"整时间"显示
|
|
|
//
|
|
|
// 【单位换算】
|
|
|
// 本类内部需要 QDateTime 时,临时 ×3600 换成秒;
|
|
|
// 对外(getTickInfos 返回的 key)仍然是"小时",与 m_vecMainValues 一致
|
|
|
// ============================================================================
|
|
|
class I_PLOTBASE_EXPORT ZxAxisHelperDate : public ZxAxisHelper
|
|
|
{
|
|
|
Q_OBJECT
|
|
|
|
|
|
public:
|
|
|
|
|
|
// 刻度文本显示样式
|
|
|
enum TimeTextFormat
|
|
|
{
|
|
|
TTF_Relative_TIU = 0, // 相对时间,带单位(如 "+1.50 h")
|
|
|
TTF_Relative_Normal, // 相对时间,时分秒模式(如 "+1:30:00")
|
|
|
TTF_Absolute_Time // 绝对时间,格式由 setTtfFormat 指定,未指定则自动选择
|
|
|
};
|
|
|
|
|
|
public:
|
|
|
|
|
|
explicit ZxAxisHelperDate(ZxSubAxisBase* p);
|
|
|
~ZxAxisHelperDate();
|
|
|
|
|
|
public:
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////
|
|
|
// 时间格式相关
|
|
|
|
|
|
// 显示模式:时间格式——相对时间、还是绝对时间
|
|
|
void setTtf(TimeTextFormat o);
|
|
|
TimeTextFormat getTtf();
|
|
|
|
|
|
// 针对 相对时间,其 单位
|
|
|
void setTtfUnit(QString s);
|
|
|
QString getTtfUnit();
|
|
|
|
|
|
// 针对 绝对时间,其显示样式,(如 "yyyy-MM-dd HH:mm:ss"),如果为空则自动
|
|
|
void setTtfFormat(QString s);
|
|
|
QString getTtfFormat();
|
|
|
|
|
|
public:
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////
|
|
|
// 增加 基准时间(值 = 0 对应的绝对时间)
|
|
|
// 注意:m_dtBase 只做"值 ↔ 绝对时间"的映射,不参与刻度对齐
|
|
|
|
|
|
void setBaseDt(QDateTime& dt);
|
|
|
QDateTime getBaseDt();
|
|
|
|
|
|
// 该函数暂未启用
|
|
|
QString getTimeBaseUnit();
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////
|
|
|
|
|
|
protected:
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////
|
|
|
// 刻度计算相关
|
|
|
|
|
|
// 取得待显示的刻度文本标识
|
|
|
// 本类不读 m_vecMainValues,而是根据 m_dStart/m_dEnd/m_dtBase
|
|
|
// 自己算"整时间刻度",只用于显示
|
|
|
virtual QMap<double, QString> getTickInfos();
|
|
|
|
|
|
// 按范围(秒)选"整时间"间隔(秒)
|
|
|
double chooseDateTimeInterval(double dScaleInSecond);
|
|
|
|
|
|
/// @brief 选"单位 + 步长",借鉴了 Chart.js 风格算法
|
|
|
/// @param dScaleInSecond 范围(秒)
|
|
|
/// @param nTargetTickCount 目标刻度数
|
|
|
/// @param nArrIndex kIntervals 索引
|
|
|
/// @param nArrStep 倍数
|
|
|
/// @return:是否成功
|
|
|
/// @note 流程:
|
|
|
/// 1) 从 millisecond 到 year 遍历 gArrTimeScaleDefines
|
|
|
/// 2) 对每个 common 单位,算 rough = range / unitSize
|
|
|
/// 3) 用 _pickOutStep 选一个 1/2/5/10... 步长
|
|
|
/// 4) 用 steps % step == 0 约束修正步长
|
|
|
/// 5) 检查刻度数是否合理
|
|
|
/// 6) 找到第一个有效的,返回
|
|
|
bool _chooseTimeInterval(double dScaleInSecond, int nTargetTickCount, \
|
|
|
int& nArrIndex, int& nArrStep);
|
|
|
|
|
|
// 从 rough 里选一个 1/2/5/10... 的步长
|
|
|
int _pickOutStep(double dRough, int nTargetTickCount);
|
|
|
|
|
|
// 找 ≤ maxStep 且能整除 steps 的最大值
|
|
|
int _findLargestDivisor(int nSteps, int nMaxStep);
|
|
|
|
|
|
// 把 dt 对齐到自然时间边界
|
|
|
// dIntervalInSecond 决定对齐粒度:< 60 → 整秒;< 3600 → 整分;< 86400 → 整时;
|
|
|
// < 28 天 → 当天 00:00;< 365 天 → 当月 1 号;
|
|
|
// ≥ 365 天 → 当年 1 月 1 日
|
|
|
// bDown = true → 向下对齐(地板)
|
|
|
// bDown = false → 向上对齐(天花板)
|
|
|
QDateTime _alignToNatural(const QDateTime& dt, \
|
|
|
double dIntervalInSecond, \
|
|
|
bool bDown);
|
|
|
|
|
|
// 格式化绝对时间(根据范围 + 跨级自动选格式)
|
|
|
// 跨天/月/年时,用 "\r\n" 分隔两行(上一行日期,下一行时间)
|
|
|
QString tickTimeAbsoluteOf(const QDateTime& t, \
|
|
|
double dScaleInSecond, \
|
|
|
bool bFirst, \
|
|
|
const QDate& dtLastDate, \
|
|
|
int nLastMonth, \
|
|
|
int nLastYear);
|
|
|
|
|
|
// 格式化相对时间(相对于 m_dtBase 的偏移)
|
|
|
QString tickTimeRelativeOf(double dSec, \
|
|
|
TimeTextFormat ttf, \
|
|
|
const QString& sUnit) const;
|
|
|
|
|
|
// 按"自然单位"递增
|
|
|
// 天/月/年用 addDays / addMonths / addYears,
|
|
|
// 避免"加固定秒数"在闰年、月长上产生误差
|
|
|
QDateTime advanceDateTime(const QDateTime& dt, \
|
|
|
double dIntervalInSecond);
|
|
|
|
|
|
protected:
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////
|
|
|
// 刻度绘制相关
|
|
|
|
|
|
// 绘制刻度线和标签
|
|
|
virtual void drawTickAndTags(QPainter* painter, QRectF& rt);
|
|
|
|
|
|
// 绘制单个刻度文字(支持两行:日期\r\n时间)
|
|
|
virtual void drawOneTickTag(QPainter* painter, double dPos, QString s);
|
|
|
|
|
|
// 获取刻度文字的位置信息
|
|
|
virtual void locateOneTickTag(QPainter* painter, double dPos, QString s,
|
|
|
QRectF& rt, int& flag);
|
|
|
|
|
|
// 文本尺寸:支持两行
|
|
|
virtual void metricStr(QPainter* painter, QString s, double& w, double& h);
|
|
|
|
|
|
// "时间 + 日期"两行文本的解析
|
|
|
void _parseToDateTime(QString s, QString& s1, QString& s2);
|
|
|
|
|
|
// 是否允许显示该刻度(防重叠)
|
|
|
virtual bool canTickShow(QRectF rt);
|
|
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
// 网格线绘制(覆盖基类)
|
|
|
// 主网格线:来自 getTickInfos 的 key → 整时间
|
|
|
// 次网格线:Helper 内部按"整时间"的次间隔生成 → 也是整时间
|
|
|
// ------------------------------------------------------------------------
|
|
|
virtual void drawGrids(QPainter* painter, QRectF& rtInner);
|
|
|
|
|
|
protected:
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////
|
|
|
// 其它
|
|
|
|
|
|
// 裁剪区
|
|
|
virtual void clipBounds(QPainter* painter, QRectF& rt);
|
|
|
|
|
|
// 预估大小(用于布局)
|
|
|
virtual QRectF getPreferedTickSumRect(QPainter* painter);
|
|
|
virtual void adjustPreferedSize(double& w, double& h);
|
|
|
|
|
|
public:
|
|
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
// 外部若调用,dMin/dMax 单位是"小时"
|
|
|
// 本类实现中不会修改 m_vecMainValues,只返回"次刻度细分数"(5.0)
|
|
|
// ------------------------------------------------------------------------
|
|
|
double adjustScaleAndTicks(double& dMin, double& dMax,
|
|
|
QVector<double>* pVecTicks = nullptr,
|
|
|
bool bConsiderRectZoom = false);
|
|
|
|
|
|
public:
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////
|
|
|
// 外部显示提取接口
|
|
|
|
|
|
// 取某个值的显示字符串
|
|
|
QString getLabelStr(double d);
|
|
|
|
|
|
// 格式化相对时间(相对于 m_dtBase 的偏移)
|
|
|
QString _labelRelativeTime(double dSec, \
|
|
|
TimeTextFormat ttf, \
|
|
|
const QString& sUnit) const;
|
|
|
|
|
|
// 格式化绝对时间(优先使用用户指定格式)
|
|
|
QString _labelAbsoluteTime(const QDateTime& t, double dScaleInSecond) const;
|
|
|
|
|
|
public slots:
|
|
|
|
|
|
public:
|
|
|
|
|
|
// Chart.js 风格的时间间隔表
|
|
|
struct TimeInterval
|
|
|
{
|
|
|
const char* name; // "second", "minute", ...
|
|
|
double size; // 单位:秒
|
|
|
int steps; // 到上一级的换算步数(-1 表示无)
|
|
|
bool common; // 是否参与自动选择
|
|
|
};
|
|
|
|
|
|
protected:
|
|
|
|
|
|
// 显示模式
|
|
|
TimeTextFormat m_oTtf;
|
|
|
|
|
|
// 相对时间的单位(如 "sec" / "min" / "hour" / "day")
|
|
|
QString m_sTtfUnit;
|
|
|
|
|
|
// 绝对时间的格式(如 "yyyy-MM-dd HH:mm:ss"),空 = 自动
|
|
|
QString m_sTtfFormat;
|
|
|
|
|
|
// 基准时间(值 = 0 对应的绝对时间)
|
|
|
QDateTime m_dtBase;
|
|
|
|
|
|
// 缓存当前范围(秒),用于格式选择
|
|
|
mutable double m_dCurScaleInSecond;
|
|
|
};
|