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++

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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