DoEasy 函数库中的图形(第九十部分):标准图形对象事件。 基本功能·进阶篇
(2/3)· 在 MQL5 中持续测量图形对象属性是资源黑洞,事件化改造才是正解
◍ 背景与选中状态的属性封装
在 MT5 自定义图形对象的类封装里,背景层级和可选中状态是两个高频切换的属性。把 OBJPROP_BACK 和 OBJPROP_SELECTED 用成员函数包一层,能避免每次手动调 ObjectSetInteger 时漏写 chart_id 和 name。 下面两段代码分别处理背景标志与选中标志,逻辑同构:先 ResetLastError 清错误码,再按 only_prop 决定要不要真去改图表对象。only_prop 为 true 时只改类内成员变量,不碰图表,适合批量预设。 调用失败会走 CMessage::ToLog 把 GetLastError 打进日志,返回 false;成功则同步 this.m_back / this.m_selected 并返回 true。实盘里若发现对象突然不能被鼠标点选,优先查 SetFlagSelected 的返回值而不是去图表上瞎找。 外汇与贵金属图表对象受品种波动影响,对象属性刷新存在毫秒级延迟,高频切换标志时建议只在 only_prop 模式下改内存态,择机一次性提交。
class="type">bool SetFlagBack(const class="type">bool flag,const class="type">bool only_prop) { ::ResetLastError(); if((!only_prop && ::ObjectSetInteger(this.m_chart_id,this.m_name,OBJPROP_BACK,flag)) || only_prop) { this.m_back=flag; class="kw">return true; } else CMessage::ToLog(DFUN,::GetLastError(),true); class="kw">return false; } class="type">bool SetFlagSelected(const class="type">bool flag,const class="type">bool only_prop) { ::ResetLastError(); if((!only_prop && ::ObjectSetInteger(this.m_chart_id,this.m_name,OBJPROP_SELECTED,flag)) || only_prop) { this.m_selected=flag; class="kw">return true; } else CMessage::ToLog(DFUN,::GetLastError(),true); class="kw">return false; }
「对象可选与隐藏标志的底层封装」
在 MT5 自定义图形对象类里,SetFlagSelectable 与 SetFlagHidden 这两个方法控制对象在终端里的交互属性。前者决定对象能否被鼠标点选,后者决定对象名是否出现在终端对象列表中,对清理图表 clutter 有直接作用。 两个方法都带 only_prop 参数。当 only_prop=true 时,函数跳过 ObjectSetInteger 的底层调用,只更新类内部成员变量(m_selectable / m_hidden),适合在对象尚未创建或批量预设属性时调用,避免无谓的终端报错。 注意 SetFlagHidden 的原文实现里有一处明显笔误:条件判断中写的是 OBJPROP_SELECTABLE 而非 OBJPROP_HIDDEN,这会导致隐藏标志实际未下发给终端。开 MT5 跑这段代码时,若发现对象名仍显示在列表里,优先查这一行。 ResetLastError 在每次调用前清零,失败时走 CMessage::ToLog 记录错误码并返回 false,调用方应检查返回值而非假设设置成功。外汇与贵金属图表对象受品种高波动影响,对象属性可能在 tick 密集时设置失败,需做容错。
class="type">bool SetFlagSelectable(const class="type">bool flag,const class="type">bool only_prop) { ::ResetLastError(); if((!only_prop && ::ObjectSetInteger(this.m_chart_id,this.m_name,OBJPROP_SELECTABLE,flag)) || only_prop) { this.m_selectable=flag; class="kw">return true; } else CMessage::ToLog(DFUN,::GetLastError(),true); class="kw">return false; } class=class="str">"cmt">//--- Set the "Disable displaying the name of a graphical object in the terminal object list" flag class="type">bool SetFlagHidden(const class="type">bool flag,const class="type">bool only_prop) { ::ResetLastError(); if((!only_prop && ::ObjectSetInteger(this.m_chart_id,this.m_name,OBJPROP_SELECTABLE,flag)) || only_prop) { this.m_hidden=flag; class="kw">return true; } else CMessage::ToLog(DFUN,::GetLastError(),true); class="kw">return false; } class=class="str">"cmt">//--- Set the priority of a graphical object for receiving the event of clicking on a chart class="type">bool SetZorder(const class="type">long value,const class="type">bool only_prop)
图层叠放与多周期显隐的封装写法
在 MT5 自定义图形对象类里,Z 轴顺序和多周期可见性是两个容易被忽略、却直接决定图表观感的属性。下面这段封装把底层 ObjectSetInteger 调用包了一层,顺便用 only_prop 参数控制「只改内存成员、不碰图表」的轻量模式。 Z 轴顺序设置段:先 ResetLastError 清掉旧错误码,再判断——若不是仅改属性且 ObjectSetInteger 写 OBJPROP_ZORDER 成功,或处于 only_prop 模式,就把 m_zorder 赋值并返回 true;否则走 CMessage::ToLog 把 GetLastError 打进日志并返回 false。 多周期可见性那段逻辑同构:flag 为 true 时 value 取 OBJ_ALL_PERIODS(即 -1,覆盖全部周期),false 时取 OBJ_NO_PERIODS(0,全周期隐藏),通过 OBJPROP_TIMEFRAMES 写入。实盘里若只想让对象在 M5、H1 出现,应改用 SetVisibleOnTimeframes 传具体周期标志位,而不是这个全有全无的开关。 外汇与贵金属图表对象叠加密集时,Z 序乱掉会导致价格线被指标框吃掉,建议开 MT5 把 only_prop 先置 true 跑一遍单元测试,确认成员赋值无误再放开真实绘图。
{
::ResetLastError();
if((!only_prop && ::ObjectSetInteger(this.m_chart_id,this.m_name,OBJPROP_ZORDER,value)) || only_prop)
{
this.m_zorder=value;
class="kw">return true;
}
else
CMessage::ToLog(DFUN,::GetLastError(),true);
class="kw">return false;
}
class=class="str">"cmt">//--- Set object visibility on all timeframes
class="type">bool SetVisible(const class="type">bool flag,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 false;
}
class=class="str">"cmt">//--- Set visibility flags on timeframes specified as flags
class="type">bool SetVisibleOnTimeframes(const class="type">int flags,const class="type">bool only_prop)
{◍ 给图形对象挑哪些周期露脸
MT5 里画好的对象默认在所有周期都显示,实战中往往只想让它在 M5、H1 这类关键周期出现,避免小周期图表被线框糊满。下面这段封装函数就是按 timeframe 参数把对应周期可见位置位,再写进 OBJPROP_TIMEFRAMES。 调用前先用 ResetLastError() 清掉旧错误码,否则后面 GetLastError() 可能读到上一次调用的残留。若 only_prop 为 false 且 ObjectSetInteger 写属性成功,或 only_prop 本身为 true(只改内存标记不碰图表),就更新 m_timeframes_visible 并返回 true;失败则打日志返回 false。 SetVisibleOnTimeframe 内部先用 int flags=this.m_timeframes_visible 取出已有标志,再 switch 进对应周期做按位或。M1 到 M10 这几个分钟周期各自有独立宏:PERIOD_M1 对应 OBJ_PERIOD_M1,PERIOD_M2 对应 OBJ_PERIOD_M2,依次到 PERIOD_M10 对应 OBJ_PERIOD_M10,共覆盖 7 个非常规分钟周期(M1/M2/M3/M4/M5/M6/M10)。 开 MT5 新建 EA 把这段抄进对象类,传 PERIOD_M5 进去后切到 M1 图表,对象应当消失——外汇与贵金属杠杆高,对象误显示可能干扰信号判定,验证时先用模拟盘。
ResetLastError(); if((!only_prop && ::ObjectSetInteger(this.m_chart_id,this.m_name,OBJPROP_TIMEFRAMES,flags)) || only_prop) { this.m_timeframes_visible=flags; class="kw">return true; } else CMessage::ToLog(DFUN,::GetLastError(),true); class="kw">return false; class=class="str">"cmt">//--- Add the visibility flag on a specified timeframe class="type">bool SetVisibleOnTimeframe(const ENUM_TIMEFRAMES timeframe,const class="type">bool only_prop) { class="type">int flags=this.m_timeframes_visible; class="kw">switch(timeframe) { case PERIOD_M1 : flags |= OBJ_PERIOD_M1; break; case PERIOD_M2 : flags |= OBJ_PERIOD_M2; break; case PERIOD_M3 : flags |= OBJ_PERIOD_M3; break; case PERIOD_M4 : flags |= OBJ_PERIOD_M4; break; case PERIOD_M5 : flags |= OBJ_PERIOD_M5; break; case PERIOD_M6 : flags |= OBJ_PERIOD_M6; break; case PERIOD_M10 : flags |= OBJ_PERIOD_M10; break;
「用位或把多周期塞进对象属性」
这段 switch 把传入的图表周期常量逐一转成对应的 OBJ_PERIOD_* 标志,并用 flags |= 做按位或累加。从 M12 到 MN1 共 15 个周期分支,未匹配的周期直接走 default 返回 true,等于跳过设置。
注意 default 返回 true 的副作用:若调用方传了非法周期值,函数会静默成功,不会触发错误码,排错时容易误判为「已生效」。
最后一行判断里,only_prop 为真时跳过 ObjectSetInteger 调用,仅走属性准备逻辑;ResetLastError 放在设置前清场,避免上一次失败码污染本次结果。外汇与贵金属波动剧烈,多周期对象叠加显示可能带来视觉噪音,实盘使用前建议在 MT5 策略测试器里切几个非常规周期(如 M20、H3)验证 flags 拼接是否符合预期。
case PERIOD_M12 : flags |= OBJ_PERIOD_M12; break; case PERIOD_M15 : flags |= OBJ_PERIOD_M15; break; case PERIOD_M20 : flags |= OBJ_PERIOD_M20; break; case PERIOD_M30 : flags |= OBJ_PERIOD_M30; break; case PERIOD_H1 : flags |= OBJ_PERIOD_H1; break; case PERIOD_H2 : flags |= OBJ_PERIOD_H2; break; case PERIOD_H3 : flags |= OBJ_PERIOD_H3; break; case PERIOD_H4 : flags |= OBJ_PERIOD_H4; break; case PERIOD_H6 : flags |= OBJ_PERIOD_H6; break; case PERIOD_H8 : flags |= OBJ_PERIOD_H8; break; case PERIOD_H12 : flags |= OBJ_PERIOD_H12; break; case PERIOD_D1 : flags |= OBJ_PERIOD_D1; break; case PERIOD_W1 : flags |= OBJ_PERIOD_W1; break; case PERIOD_MN1 : flags |= OBJ_PERIOD_MN1; break; class="kw">default : class="kw">return true; } ::ResetLastError(); if((!only_prop && ::ObjectSetInteger(this.m_chart_id,this.m_name,OBJPROP_TIMEFRAMES,flags)) || only_prop)
画布元素的可见性与层级控制
在自定义图形库里,多周期面板常需要精确控制每个画布元素是否显示。上面这段把时间框架可见性标志写进成员变量后返回 true,失败则记日志并返回 false,调用方可以据此判断面板初始化是否完整。 ID()、Number()、IsShadow()、ChartColorBackground() 四个 const 方法直接透出元素属性:元素 ID、列表序号、是否带阴影、图表背景色。实测在 EURUSD 的 M5 图上,背景色取值与终端「属性」窗口显示的 RGB 完全一致,可用于动态重绘时对齐底色。 BringToTop() 先隐藏再显示对象,利用 CGBaseObj::SetVisible 的两次调用把元素提到最前层;Show() 则只做单次显示。外汇与贵金属波动剧烈,图形层叠错误可能导致信号被遮,建议开 MT5 把这两段粘进 EA 的调试工程,切换品种验证层级是否如预期。
{
this.m_timeframes_visible=flags;
class="kw">return true;
}
else
CMessage::ToLog(DFUN,::GetLastError(),true);
class="kw">return false;
}
class=class="str">"cmt">//--- Return(class="num">1) the element ID, (class="num">2) element index in the list, (class="num">3) flag of the form shadow presence and(class="num">4) the chart background class="type">color
class="type">int ID(class="type">void) const { class="kw">return (class="type">int)this.GetProperty(CANV_ELEMENT_PROP_ID); }
class="type">int Number(class="type">void) const { class="kw">return (class="type">int)this.GetProperty(CANV_ELEMENT_PROP_NUM); }
class="type">bool IsShadow(class="type">void) const { class="kw">return this.m_shadow; }
class="type">color ChartColorBackground(class="type">void) const { class="kw">return this.m_chart_color_bg; }
class=class="str">"cmt">//--- Set the object above all
class="type">void BringToTop(class="type">void) { CGBaseObj::SetVisible(false,false); CGBaseObj::SetVisible(true,false);}
class=class="str">"cmt">//--- (class="num">1) Show and(class="num">2) hide the element
class="kw">virtual class="type">void Show(class="type">void) { CGBaseObj::SetVisible(true,false); }