DoEasy 函数库中的图形(第一百部分):改进扩展标准图形对象的处理·进阶篇
🧩

DoEasy 函数库中的图形(第一百部分):改进扩展标准图形对象的处理·进阶篇

(2/3)· 当 GUI 控件被标准图形对象盖住、点击总落在错误层,这篇给出库内排序与排错的落地改法

含代码示例实战向 第 2/3 篇

在图表上堆了画布 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,分别回传元素类型、归属、物种和具体图形对象枚举,调用成本接近零。

MQL5 / C++
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 掩码若没设全,对象会静默消失,属于高概率踩坑点。

MQL5 / C++
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 属性,图表上对象叠放顺序会随之变化。外汇与贵金属图表波动剧烈,对象层级错乱可能导致信号被遮,实盘前务必在策略测试器里验证。

MQL5 / C++
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 可能导致信号框点不动,属高风险操作环境,调参前建议在策略测试器里验证。

MQL5 / C++
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 响应,属于高风险自定义开发。

MQL5 / C++
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 属高风险操作,参数误设可能导致交互失效。

MQL5 / C++
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
把层级巡检交给小布
这些对象层级的异常诊断,小布盯盘的 AIGC 已内置,打开对应品种页即可看到画布对象是否被意外覆盖的提示,你只管决定要不要手动调序。

常见问题

标准对象创建时 ZOrder 默认零值,若不主动提升 GUI 的排序属性,它们会落在后加对象之下,需要库内统一前置。
本篇把它扩展为画布元素之间及相对其它图形对象的排列顺序指示,通过基类整数属性统一读写。
可以,小布盯盘的 AIGC 诊断会标出被覆盖的控件元素,省去你挨个翻对象属性的麻烦。
用文末测试节里的 EA 挂载方式,在真实图表加混合对象并点选,观察前景移动是否符合预期。
不会,本篇收尾这部分,下一篇开启模拟 MS Visual Studio 窗体的新章节,关于复合对象的完整讨论见《DoEasy 函数库中的图形·实战篇》。