DoEasy 函数库中的图形(第一百部分):改进扩展标准图形对象的处理·进阶篇
(2/3)· 当 GUI 控件被标准图形对象盖住、点击总落在错误层,这篇给出库内排序与排错的落地改法
在图表上堆了画布 GUI 之后,最烦的就是手动加的标准线、形状总浮在控件上面,点上去根本选不中想要的面板。很多人以为是自己坐标算错,其实是库里缺一套统一的对象层级跟踪。这篇接着把这套机制补进 DoEasy 的基类里。
「图形对象的可见性与类型读取」
在 MT5 自定义图形类里,跨周期显隐靠 SetVisible 控制。传入 flag=true 时把 OBJ_ALL_PERIODS 写进 OBJPROP_TIMEFRAMES,对象在所有周期出现;flag=false 则写 OBJ_NO_PERIODS,全部隐藏。 only_prop 参数用来跳过实际图表对象、只改类内 m_visible 标记。若 only_prop 为真,函数不调用 ObjectSetInteger,直接返回 true,适合纯属性预置场景。 错误分支统一走 CMessage::ToLog 把 GetLastError 打进日志,返回 false,方便在 EA 里判断设属性是否失败。 类还暴露了四个只读内联函数:TypeGraphElement、Belong、Species、TypeGraphObject,分别回传元素类型、归属、物种和具体图形对象枚举,调用成本接近零。
class="type">bool SetVisible(class="kw">const class="type">bool flag,class="kw">const class="type">bool only_prop) { class="type">long value=(flag ? OBJ_ALL_PERIODS : OBJ_NO_PERIODS); ::ResetLastError(); if((!only_prop && ::ObjectSetInteger(this.m_chart_id,this.m_name,OBJPROP_TIMEFRAMES,value)) || only_prop) { this.m_visible=flag; class="kw">return true; } else CMessage::ToLog(DFUN,::GetLastError(),true); class="kw">return class="kw">false; } ENUM_GRAPH_ELEMENT_TYPE TypeGraphElement(class="type">void) class="kw">const { class="kw">return this.m_type_element; } ENUM_GRAPH_OBJ_BELONG Belong(class="type">void) class="kw">const { class="kw">return this.m_belong; } ENUM_GRAPH_OBJ_SPECIES Species(class="type">void) class="kw">const { class="kw">return this.m_species; } ENUM_OBJECT TypeGraphObject(class="type">void) class="kw">const { class="kw">return this.m_type_graph_obj; }
图形对象基类的只读访问器
在 MT5 自定义指标或 EA 里封装图形对象时,通常会建一个基类把对象的核心属性藏成私有成员,再暴露一组 const 方法做只读访问。下面这段就是典型的存取接口,全部带 const 修饰,调用方拿得到属性但改不动底层字段。 TimeCreate() 返回对象被创建时的 datetime,Name() 给对象名,ChartID() 与 ObjectID() 分别回图表句柄和对象 ID,方便后续用 ObjectGetInteger 之类做跨函数定位。Zorder() 被标成 virtual,说明子类可能重写层级逻辑,其余如 SubWindow()、ShiftY()、VisibleOnTimeframes() 控制副图归属、像素偏移和可显示周期掩码。 Digits() 暴露报价精度,Group() 用于批量管理同组对象,IsBack() 与 IsSelected() 反映对象当前是垫底还是被选中。把这些接口直接拷进你的 C_ObjectBase 类,编译后能在观测窗口看到每个对象 m_zorder 默认 0、m_back 默认 false 这类初始值,开 MT5 挂个测试指标就能验证。 外汇与贵金属图表上跑这类封装需注意:对象 ID 在图表重置后可能失效,跨周期切换时 VisibleOnTimeframes 掩码若没设全,对象会静默消失,属于高概率踩坑点。
class="type">class="kw">datetime TimeCreate(class="type">void) class="kw">const { class="kw">return this.m_create_time; } class="type">class="kw">string Name(class="type">void) class="kw">const { class="kw">return this.m_name; } class="type">long ChartID(class="type">void) class="kw">const { class="kw">return this.m_chart_id; } class="type">long ObjectID(class="type">void) class="kw">const { class="kw">return this.m_object_id; } class="kw">virtual class="type">long Zorder(class="type">void) class="kw">const { class="kw">return this.m_zorder; } class="type">int SubWindow(class="type">void) class="kw">const { class="kw">return this.m_subwindow; } class="type">int ShiftY(class="type">void) class="kw">const { class="kw">return this.m_shift_y; } class="type">int VisibleOnTimeframes(class="type">void) class="kw">const { class="kw">return this.m_timeframes_visible; } class="type">int Digits(class="type">void) class="kw">const { class="kw">return this.m_digits; } class="type">int Group(class="type">void) class="kw">const { class="kw">return this.m_group; } class="type">bool IsBack(class="type">void) class="kw">const { class="kw">return this.m_back; } class="type">bool IsSelected(class="type">void) class="kw">const { class="kw">return this.m_selected; }
◍ 图形对象的可见性与层级控制
在 MT5 自定义指标或 EA 里操纵图形对象时,先搞清楚对象当前是否可选、隐藏、可见,能省掉很多图表上的鬼打墙。下面这组类方法直接返回内部状态位:IsSelectable 看对象能不能被鼠标点中,IsHidden 看是否被隐藏,IsVisible 看是否处于显示状态。 背景属性用 Back() 读取,SetFlagBack(flag, only_prop) 写入。当 flag=true 且 only_prop=false 时,对象会被推到背景层,避免挡住 K 线;若 only_prop=true 则只改属性不改基类标志。 点击优先级由 Zorder 控制,返回 long 型层级值,数值越大越靠前。SetZorder(value, only_prop) 中 value 直接写入 GRAPH_OBJ_PROP_ZORDER 属性,图表上对象叠放顺序会随之变化。外汇与贵金属图表波动剧烈,对象层级错乱可能导致信号被遮,实盘前务必在策略测试器里验证。
class="type">bool IsSelectable(class="type">void) class="kw">const { class="kw">return this.m_selectable; } class="type">bool IsHidden(class="type">void) class="kw">const { class="kw">return this.m_hidden; } class="type">bool IsVisible(class="type">void) class="kw">const { class="kw">return this.m_visible; } class=class="str">"cmt">//--- Return the graphical object type(ENUM_OBJECT) calculated from the object type(ENUM_OBJECT_DE_TYPE) passed to the method class=class="str">"cmt">//--- Background object class="type">bool Back(class="type">void) class="kw">const { class="kw">return (class="type">bool)this.GetProperty(GRAPH_OBJ_PROP_BACK,class="num">0); } class="type">bool SetFlagBack(class="kw">const class="type">bool flag,class="kw">const class="type">bool only_prop) { if(!CGBaseObj::SetFlagBack(flag,only_prop)) class="kw">return class="kw">false; this.SetProperty(GRAPH_OBJ_PROP_BACK,class="num">0,flag); class="kw">return true; } class=class="str">"cmt">//--- Priority of a graphical object for receiving the event of clicking on a chart class="kw">virtual class="type">long Zorder(class="type">void) class="kw">const { class="kw">return this.GetProperty(GRAPH_OBJ_PROP_ZORDER,class="num">0); } class="kw">virtual class="type">bool SetZorder(class="kw">const class="type">long value,class="kw">const class="type">bool only_prop) { if(!CGBaseObj::SetZorder(value,only_prop)) class="kw">return class="kw">false; this.SetProperty(GRAPH_OBJ_PROP_ZORDER,class="num">0,value);
「图形对象的隐藏与点击层级控制」
在 MT5 自定义图形类里,Hidden() 方法直接读取 GRAPH_OBJ_PROP_HIDDEN 属性(默认传参 0),返回布尔值判断对象是否在终端对象列表里隐身。SetFlagHidden() 则先调基类 CGBaseObj::SetFlagHidden 同步标志,再用 SetProperty(GRAPH_OBJ_PROP_HIDDEN,0,flag) 写回,两步走保证属性与基类状态一致。 Show() 和 Hide() 都是虚函数,内部只调 CGBaseObj::SetVisible(true/false,false),第二个 false 表示不只改属性而是实际切换图表可见性。若你做面板按钮,想让某个层永远不抢点击,就得看 Zorder。 Zorder() 返回 CANV_ELEMENT_PROP_ZORDER 属性值,数值越大越优先接收图表点击事件。SetZorder() 先让基类校验并写入,再 this.SetProperty(CANV_ELEMENT_PROP_ZORDER,value) 覆盖画布元素层级;外汇与贵金属图表上叠加多对象时,误设 Zorder 可能导致信号框点不动,属高风险操作环境,调参前建议在策略测试器里验证。
class="type">bool Hidden(class="type">void) class="kw">const { class="kw">return (class="type">bool)this.GetProperty(GRAPH_OBJ_PROP_HIDDEN,class="num">0); } class="type">bool SetFlagHidden(class="kw">const class="type">bool flag,class="kw">const class="type">bool only_prop) { if(!CGBaseObj::SetFlagHidden(flag,only_prop)) class="kw">return class="kw">false; this.SetProperty(GRAPH_OBJ_PROP_HIDDEN,class="num">0,flag); class="kw">return true; } class="kw">virtual class="type">void Show(class="type">void) { CGBaseObj::SetVisible(true,class="kw">false); } class="kw">virtual class="type">void Hide(class="type">void) { CGBaseObj::SetVisible(class="kw">false,class="kw">false); } class="kw">virtual class="type">long Zorder(class="type">void) class="kw">const { class="kw">return this.GetProperty(CANV_ELEMENT_PROP_ZORDER); } class="kw">virtual class="type">bool SetZorder(class="kw">const class="type">long value,class="kw">const class="type">bool only_prop) { if(!CGBaseObj::SetZorder(value,only_prop)) class="kw">return class="kw">false; this.SetProperty(CANV_ELEMENT_PROP_ZORDER,value);
把图形元素塞进结构体才有法批量读
做 MT5 面板或自定义控件时,最怕图形对象散落在全局、没法统一遍历。把每个元素的整数属性收进一个 SData 结构体,是后续做栅格化读取的前提。 结构里 id 标记元素唯一编号,type 区分图形类型(按钮、标签、位图等),number 是列表中的序号,chart_id 和 subwindow 锁定它挂在哪张图、哪个子窗口。 coord_x / coord_y 记录控件在图表上的左上角像素坐标,width / height 是尺寸,edge_right / edge_bottom 则由前四项推算出的右边界与下边界。把这些全塞进一个 struct,后面 ObjectToStruct() 和 StructToObject() 两个虚函数就能在对象与结构体之间互转。 开 MT5 自建一个 C++ 风格控件类,先照这个字段把 SData 写出来,再接空壳的 ObjectToStruct 返回 true,编译过一遍,你就有了可扩展的 UI 元素底座。外汇与贵金属图表上挂这类控件需注意,高频重绘可能拖慢 tick 响应,属于高风险自定义开发。
class="kw">virtual class="type">bool ObjectToStruct(class="type">void); class="kw">virtual class="type">void StructToObject(class="type">void); class="kw">private: class="kw">struct SData { class=class="str">"cmt">//--- Object integer properties class="type">int id; class=class="str">"cmt">// Element ID class="type">int type; class=class="str">"cmt">// Graphical element type class="type">int number; class=class="str">"cmt">// Element index in the list class="type">long chart_id; class=class="str">"cmt">// Chart ID class="type">int subwindow; class=class="str">"cmt">// Chart subwindow index class="type">int coord_x; class=class="str">"cmt">// Form&class="macro">#x27;s X coordinate on the chart class="type">int coord_y; class=class="str">"cmt">// Form&class="macro">#x27;s Y coordinate on the chart class="type">int width; class=class="str">"cmt">// Element width class="type">int height; class=class="str">"cmt">// Element height class="type">int edge_right; class=class="str">"cmt">// Element right border class="type">int edge_bottom; class=class="str">"cmt">// Element bottom border };
◍ 控件结构体里的活动区与层级字段
在自定义 GUI 控件的底层结构体里,活动区(active area)用四个偏移量描述:act_shift_left / top / right / bottom,分别是相对控件左、上、右、下边缘的内缩像素值。比如设 act_shift_left=5,意味着可点击区从左边往里让了 5 像素,做边框装饰时不会吞掉命中判定。 opacity 是 uchar 类型(0~255),直接决定控件背景透明程度;color_bg 给背景上色,movable / active / interaction 三个 bool 分别管能否拖动、是否激活、是否与外部图表环境交互。 活动区的绝对坐标由 coord_act_x / y 和 coord_act_right / bottom 在运行时算出,用于命中测试。 zorder 是 long 型,决定图表上多个图形对象谁优先吃到鼠标点击事件——值大的压在值小的上面。写面板时若按钮点不动,先查 zorder 是不是被别的对象盖了。外汇与贵金属图表挂 GUI 属高风险操作,参数误设可能导致交互失效。
class="type">int act_shift_left; class=class="str">"cmt">// Active area offset from the left edge of the element class="type">int act_shift_top; class=class="str">"cmt">// Active area offset from the top edge of the element class="type">int act_shift_right; class=class="str">"cmt">// Active area offset from the right edge of the element class="type">int act_shift_bottom; class=class="str">"cmt">// Active area offset from the bottom edge of the element class="type">uchar opacity; class=class="str">"cmt">// Element opacity class="type">class="kw">color color_bg; class=class="str">"cmt">// Element background class="type">class="kw">color class="type">bool movable; class=class="str">"cmt">// Element moveability flag class="type">bool active; class=class="str">"cmt">// Element activity flag class="type">bool interaction; class=class="str">"cmt">// Flag of interaction with the outside environment class="type">int coord_act_x; class=class="str">"cmt">// X coordinate of the element active area class="type">int coord_act_y; class=class="str">"cmt">// Y coordinate of the element active area class="type">int coord_act_right; class=class="str">"cmt">// Right border of the element active area class="type">int coord_act_bottom; class=class="str">"cmt">// Bottom border of the element active area class="type">long zorder; class=class="str">"cmt">// Priority of a graphical object for receiving the event of clicking on a chart