#pragma once #include #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 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* 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; };