You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
nmWTAI-Platform/Include/iPlot/iPlotBase/Helper/ZxAxisHelperDate.h

249 lines
9.3 KiB
C

#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;
};