DoEasy 函数库中的图形(第八十二部分):函数库对象重构和图形对象集合(基础篇)
◍ 把图形对象收进统一集合
DoEasy 函数库这一轮改造的核心,是把原先散落的图形对象类归并到一个集合里管理,同时顺手重构了底层库对象。2021 年 10 月 25 日发布的示例里,文章页面显示阅读量 1,496、评论 0,说明这套改法偏内部架构,不是冲着策略信号去的。 对于做价格行为分析的交易者,意义在于:以后在 MT5 里挂趋势线、矩形、斐波那契这类对象,可以用同一套接口遍历和批量改属性,不用为每个类型写一遍重复代码。 外汇和贵金属市场高波动,用图形对象做辅助判读时,建议先在小周期回看对象集合的刷新是否跟得上 tick,避免视觉滞后。
「把图形对象纳入统一管控」
函数库里每个对象现在都要带独有 ID,靠 Type() 区分身份。对象生成图形后,图形必须反向持有父对象指针,父对象也要记着自己产出的图形指针,双向绑定才方便后续遍历与回收。 图形对象集合列表是核心容器:用程序创建的对象立刻入表;在 MT5 终端里手动画的趋势线、矩形等标准图形,也要被集合类实时跟踪,新建或删除都同步进表,并为它们单独建程序对象以便统一管理。 集合类需轮询所有已打开图表窗口,捕捉标准图形对象的出现与消失。这样函数库最终能控制图表上全部标准图形,把手工画的也当成“自己人”,同时保留其来源标记(程序建 or 终端建)。外汇与贵金属图表对象繁杂,手工与程序混用是高概率场景,这套机制能降低对象泄漏风险。
给库对象打类型和隶属标签
在 DoEasy 框架里给每个非辅助对象挂上类型值,核心改动落在 Defines.mqh。先在计时器参数宏末尾补图形对象集合的计时器宏,再在已有集合 ID 后追加一个标签常量,把它作为 ENUM_OBJECT_DE_TYPE 的起点——标签值 +1 就是第一个库对象类型常量,标准图形对象枚举结束后紧接着排库对象类型,保证不与标准类型撞值。 图形对象属性也要动:在从属关系枚举里加“程序创建 / 终端创建”两种来源,并塞进整数型属性枚举,属性总数从 21 调到 22;排序标准枚举补一个按该属性排序的入口。Data.mqh 里同步加图形对象集合的消息索引和文本。 派生自基准或扩展基准类的对象最简单,构造函数里给 m_type 写死类型即可,虚拟 Type() 自动返回。账户对象构造函数只写 m_type=account;市场深度那几个类(买、卖、市价单、快照、序列)默认和参数型构造函数都要设类型;抽象事件类没继承基准类,得自己加 m_type 变量和 Type() 方法,再在子类构造函数填值。 GBaseObj.mqh 这个图形基准对象除了设类型,还带了程序/终端隶属属性,用来区分对象是 EA 管控建的还是手动拖上图的。集合类文件(AccountsCollection、SymbolsCollection 等 13 个)做了同构改进。 改完的对象类覆盖 Chart、Indicators、Standart 下 40+ 指标文件、Orders、Symbols 下 20+ 品种文件等,打开 MT5 看 Include\DoEasy 目录,对比构造函数里 m_type 赋值就能验证类型体系是否接通。外汇和贵金属品种对象也走这套,实盘加载前先在策略测试器跑一遍避免类型冲突引发高风险报错。
class=class="str">"cmt">//--- Parameters of the chart collection timer
◍ 采集模块的定时器与列表标识常量
在 MT5 的底层采集框架里,图表与图形对象的轮询不是连续抢占主线程,而是靠宏定义出来的定时器节奏走。图表采集暂停间隔设为 500 毫秒,图形对象采集则为 250 毫秒,前者比后者慢一倍,说明图形对象状态变化更频繁、需要更密地兜底。
两个定时器各自带一个计数器步进值 16,以及独立的计数器 ID(图表用 9,图形对象用 10)。这种分离设计让你可以单独观测某类资源的采集触发次数,比如在调试时只打印 ID=10 的计数,就能判断图形对象回调是否漏跑。
真正落库的数据被分到一组十六进制列表 ID 里:从历史 0x777A 到盘口深度 0x7784,共 14 类。这些 ID 是后续 CCollect 系列类定位内存列表的钥匙,改掉任意一个都可能让对应采集器写错地方。开 MT5 把下面宏贴进头文件,搜一下工程里谁引用了 0x777E,就能看清符号表是怎么被接管的。
class="macro">#define COLLECTION_CHARTS_PAUSE(class="num">500) class=class="str">"cmt">// Chart collection timer pause in milliseconds class="macro">#define COLLECTION_CHARTS_COUNTER_STEP(class="num">16) class=class="str">"cmt">// Chart timer counter increment class="macro">#define COLLECTION_CHARTS_COUNTER_ID(class="num">9) class=class="str">"cmt">// Chart timer counter ID class=class="str">"cmt">//--- Parameters of the graphical objects collection timer class="macro">#define COLLECTION_GRAPH_OBJ_PAUSE(class="num">250) class=class="str">"cmt">// Graphical objects collection timer pause in milliseconds class="macro">#define COLLECTION_GRAPH_OBJ_COUNTER_STEP(class="num">16) class=class="str">"cmt">// Graphical objects timer counter increment class="macro">#define COLLECTION_GRAPH_OBJ_COUNTER_ID(class="num">10) class=class="str">"cmt">// Graphical objects timer counter ID class=class="str">"cmt">//--- Collection list IDs class=class="str">"cmt">//--- Collection list IDs class="macro">#define COLLECTION_HISTORY_ID(0x777A) class=class="str">"cmt">// Historical collection list ID class="macro">#define COLLECTION_MARKET_ID(0x777B) class=class="str">"cmt">// Market collection list ID class="macro">#define COLLECTION_EVENTS_ID(0x777C) class=class="str">"cmt">// Event collection list ID class="macro">#define COLLECTION_ACCOUNT_ID(0x777D) class=class="str">"cmt">// Account collection list ID class="macro">#define COLLECTION_SYMBOLS_ID(0x777E) class=class="str">"cmt">// Symbol collection list ID class="macro">#define COLLECTION_SERIES_ID(0x777F) class=class="str">"cmt">// Timeseries collection list ID class="macro">#define COLLECTION_BUFFERS_ID(0x7780) class=class="str">"cmt">// Indicator buffer collection list ID class="macro">#define COLLECTION_INDICATORS_ID(0x7781) class=class="str">"cmt">// Indicator collection list ID class="macro">#define COLLECTION_INDICATORS_DATA_ID(0x7782) class=class="str">"cmt">// Indicator data collection list ID class="macro">#define COLLECTION_TICKSERIES_ID(0x7783) class=class="str">"cmt">// Tick series collection list ID class="macro">#define COLLECTION_MBOOKSERIES_ID(0x7784) class=class="str">"cmt">// DOM series collection list ID
「库对象的集合ID与类型枚举怎么排」
在自绘 UI 库里,集合(collection)用一组 16 进制常量做身份标识。MQL5 信号列表占 0x7785,图表集合 0x7786,图表窗口 0x7787,图形对象集合 0x7788,最后一个有效 ID 用 COLLECTION_ID_LIST_END 指向图形对象集合,后续枚举从它 +1 接着排。 画布参数也在这里固化:画布刷新间隔 PAUSE_FOR_CANV_UPDATE 设为 16 毫秒,透明色 NULL_COLOR 用 0x00FFFFFF(alpha 通道为零),工作区外留白 OUTER_AREA_SIZE 为 16 像素。直接抄进 include 头文件就能用。 对象类型枚举 ENUM_OBJECT_DE_TYPE 从 COLLECTION_ID_LIST_END+1 起算,首项是所有图形对象的基类 OBJECT_DE_TYPE_GBASE,其后 GELEMENT(图元)、GFORM(表单)、GSHADOW(阴影)到 GFRAME(单帧动画)依次递增。改这个枚举时务必保持连续,跳号会让后续 Cast 出错。
class="macro">#define COLLECTION_MQL5_SIGNALS_ID(0x7785) class=class="str">"cmt">// MQL5 signals collection list ID class="macro">#define COLLECTION_CHARTS_ID(0x7786) class=class="str">"cmt">// Chart collection list ID class="macro">#define COLLECTION_CHART_WND_ID(0x7787) class=class="str">"cmt">// Chart window list ID class="macro">#define COLLECTION_GRAPH_OBJ_ID(0x7788) class=class="str">"cmt">// Graphical object collection list ID class="macro">#define COLLECTION_ID_LIST_END(COLLECTION_GRAPH_OBJ_ID) class=class="str">"cmt">// End of collection ID list class=class="str">"cmt">//--- Pending request type IDs class=class="str">"cmt">//--- Canvas parameters class="macro">#define PAUSE_FOR_CANV_UPDATE(class="num">16) class=class="str">"cmt">// Canvas update frequency class="macro">#define NULL_COLOR(0x00FFFFFF) class=class="str">"cmt">// Zero for the canvas with the alpha channel class="macro">#define OUTER_AREA_SIZE(class="num">16) class=class="str">"cmt">// Size of one side of the outer area around the workspace class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Enumerations | class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| List of library object types | class=class="str">"cmt">//+------------------------------------------------------------------+ enum ENUM_OBJECT_DE_TYPE { class=class="str">"cmt">//--- Graphics OBJECT_DE_TYPE_GBASE = COLLECTION_ID_LIST_END+class="num">1, class=class="str">"cmt">// "Base object of all library graphical objects" object type OBJECT_DE_TYPE_GELEMENT, class=class="str">"cmt">// "Graphical element" object type OBJECT_DE_TYPE_GFORM, class=class="str">"cmt">// Form object type OBJECT_DE_TYPE_GSHADOW, class=class="str">"cmt">// Shadow object type class=class="str">"cmt">//--- Animation OBJECT_DE_TYPE_GFRAME, class=class="str">"cmt">// "Single animation frame" object type
对象类型枚举里的图形控件偏移规律
在 MT5 的标准图形对象体系里,所有可见画线都不是从 0 直接编号,而是挂在 OBJECT_DE_TYPE_GELEMENT_CONTROL 这个「管理图形对象」基类之上再做偏移。 具体做法是 CONTROL 基值加 1 之后,再叠加上 OBJ_VLINE、OBJ_HLINE、OBJ_TREND 等原生对象常量。例如垂直线的枚举值为 OBJECT_DE_TYPE_GELEMENT_CONTROL+1+OBJ_VLINE,水平线则是 CONTROL+1+OBJ_HLINE,趋势线对应 CONTROL+1+OBJ_TREND。 这种 +1 的间隙,本质上把「管理类」和「标准绘制类」在枚举空间上隔开,避免和前面的动画帧类型(如 OBJECT_DE_TYPE_GFRAME_TEXT、OBJECT_DE_TYPE_GANIMATIONS)撞号。 开 MT5 切到 MetaEditor,搜 OBJECT_DE_TYPE_GELEMENT_CONTROL 就能看到完整偏移链;自己写面板时若想遍历所有标准线,直接从这个基值 +1 起循环 OBJ_ 前缀常量即可,比硬编码数字稳。
OBJECT_DE_TYPE_GFRAME_TEXT, class=class="str">"cmt">// "Single text animation frame" object type OBJECT_DE_TYPE_GFRAME_QUAD, class=class="str">"cmt">// "Single rectangular animation frame" object type OBJECT_DE_TYPE_GFRAME_GEOMETRY, class=class="str">"cmt">// "Single geometric animation frame" object type OBJECT_DE_TYPE_GANIMATIONS, class=class="str">"cmt">// "Animations" object type class=class="str">"cmt">//--- Managing graphical objects OBJECT_DE_TYPE_GELEMENT_CONTROL, class=class="str">"cmt">// "Managing graphical objects" object type class=class="str">"cmt">//--- Standard graphical objects OBJECT_DE_TYPE_GSTD_VLINE = OBJECT_DE_TYPE_GELEMENT_CONTROL+class="num">1+OBJ_VLINE, class=class="str">"cmt">// "Vertical line" object type OBJECT_DE_TYPE_GSTD_HLINE = OBJECT_DE_TYPE_GELEMENT_CONTROL+class="num">1+OBJ_HLINE, class=class="str">"cmt">// "Horizontal line" object type OBJECT_DE_TYPE_GSTD_TREND = OBJECT_DE_TYPE_GELEMENT_CONTROL+class="num">1+OBJ_TREND, class=class="str">"cmt">// "Trend line" object type OBJECT_DE_TYPE_GSTD_TRENDBYANGLE = OBJECT_DE_TYPE_GELEMENT_CONTROL+class="num">1+OBJ_TRENDBYANGLE, class=class="str">"cmt">// "Trend line by angle" object type OBJECT_DE_TYPE_GSTD_CYCLES = OBJECT_DE_TYPE_GELEMENT_CONTROL+class="num">1+OBJ_CYCLES, class=class="str">"cmt">// "Cyclic lines" object type OBJECT_DE_TYPE_GSTD_ARROWED_LINE = OBJECT_DE_TYPE_GELEMENT_CONTROL+class="num">1+OBJ_ARROWED_LINE, class=class="str">"cmt">// "Arrowed line" object type OBJECT_DE_TYPE_GSTD_CHANNEL = OBJECT_DE_TYPE_GELEMENT_CONTROL+class="num">1+OBJ_CHANNEL, class=class="str">"cmt">// "Equidistant channel" object type OBJECT_DE_TYPE_GSTD_STDDEVCHANNEL = OBJECT_DE_TYPE_GELEMENT_CONTROL+class="num">1+OBJ_STDDEVCHANNEL, class=class="str">"cmt">// "Standard deviation channel" object type OBJECT_DE_TYPE_GSTD_REGRESSION = OBJECT_DE_TYPE_GELEMENT_CONTROL+class="num">1+OBJ_REGRESSION, class=class="str">"cmt">// "Linear regression channel" object type OBJECT_DE_TYPE_GSTD_PITCHFORK = OBJECT_DE_TYPE_GELEMENT_CONTROL+class="num">1+OBJ_PITCHFORK, class=class="str">"cmt">// "Andrews&class="macro">#x27; pitchfork" object type OBJECT_DE_TYPE_GSTD_GANNLINE = OBJECT_DE_TYPE_GELEMENT_CONTROL+class="num">1+OBJ_GANNLINE, class=class="str">"cmt">// "Gann line" object type OBJECT_DE_TYPE_GSTD_GANNFAN = OBJECT_DE_TYPE_GELEMENT_CONTROL+class="num">1+OBJ_GANNFAN, class=class="str">"cmt">// "Gann fan" object type