DoEasy 函数库中的图形(第七十五部分):处理基本图形元素图元和文本的方法(基础篇)
📘

DoEasy 函数库中的图形(第七十五部分):处理基本图形元素图元和文本的方法(基础篇)

第 1/3 篇

◍ 给图元与文本加上可操作方法

在 DoEasy 函数库里,图形对象不再只是画出来就完事。第七十五部分把基本图元(线段、矩形、椭圆等)和文本对象拆成了可单独操控的基元,每个基元都能通过类方法改坐标、颜色、宽度和可见性。 文本对象的处理思路和图元一致:文字内容、锚点、字体大小都封装成方法,调用一次就能改,不用删了重画。这样在 EA 里做动态标注时,开销明显低于反复创建新对象。 想在 MT5 里验证,直接打开标准库样例,把图形对象绑定到 Chart 对象后逐方法改参数,能看到对象实时响应。外汇与贵金属市场波动剧烈,用脚本做图形标注时仍需警惕滑点和重绘风险。

给图形元素接上画布与存档能力

这一节把基础图形元素从“能设属性”推进到“能真正画图、能记住自己”。核心做法是把 CCanvas 类的绘图接口封装进元素对象方法,让派生类以后直接调用就能画基元和文本,不用每次重写底层逻辑。 除了绘图,还加了文件读写方法的雏形。GUI 元素在切换时间帧时不能丢状态,所以要把位置、属性、状态落盘,构造时再读回。不过集合类还没写,这里只先埋好保存和上传的接口,等后面统一由对象集合调度。 颜色处理单独提了个类进函数库,代码取自 Dmitry Fedoseev 在 MQL5 社区提交的公开实现。做完这步,图形元素就具备了作为复杂图表对象底座的条件,后面直接继承扩展即可。

「画布清屏与文本锚点的坑」

用 CCanvas 做带透明度的绘图时,默认 Erase(0) 其实会留下残影。原因是零值清不掉 alpha 通道,画布底层仍残留上一帧的透明信息,连续重绘就糊。正确做法是传 0x00FFFFFF——这是 ARGB 里 Alpha=0、RGB 全 255 的「全透黑」,能把透明度通道一并抹平。 在 Defines.mqh 里加一个宏替换就很省事:#define NULL_COLOR (0x00FFFFFF),之后清屏统一调 Erase(NULL_COLOR),比每次手写十六进制稳。 TextOut() 显示文本支持设锚点,但原生要用 TA_LEFT / TA_CENTER / TA_RIGHT 配 TA_TOP / TA_VCENTER / TA_BOTTOM 做位或,写错顺序就偏。干脆封一个 ENUM_TEXT_ANCHOR 枚举,把九种组合固化:TEXT_ANCHOR_LEFT_TOP=0、TEXT_ANCHOR_CENTER=5、TEXT_ANCHOR_RIGHT_BOTTOM=10 等,调用时直接传枚举值,不用管标志位先后顺序。 别把清屏零值当通用解 带 alpha 的画布若用 Erase(0),MT5 图表上叠多图层时残影概率明显升高;外汇和贵金属图表高频刷新场景下这问题会被放大,属高风险界面故障,开 MT5 自测一下就能复现。

MQL5 / C++
  class=class="str">"cmt">//--- clear/fill class="type">class="kw">color
  class="type">void                Erase(class="kw">const class="type">uint clr=class="num">0);
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=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Data for handling graphical elements                            |
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+

◍ 文字锚点与半透明混色怎么落进代码

在 MT5 自定义指标或面板里画文字标签,第一件事是定锚点。MQL5 用 ENUM_TEXT_ANCHOR 枚举把边界框的 9 个位置编码成整数,左上为 0、顶中为 1、右上为 2,左侧居中跳到 4 而非 3——中间缺的那个值保留给内部扩展,写死 3 会编译报错。 实际挂标签时,若想把「波段止损」写在 K 线右上角且不挡实体,直接用 TEXT_ANCHOR_RIGHT_TOP(=2)即可;要居中浮在框内则用 TEXT_ANCHOR_CENTER(=5)。 半透明配色走 CColors::BlendColors,入参是 ARGB 整数。下面这行把蓝(128 透明度)和红(64 透明度)按比例混出紫调,返回的值可直接喂给对象笔刷:CColors::BlendColors(ColorToARGB(clrBlue,128), ColorToARGB(clrRed,64))。外汇与贵金属图表叠加多层半透明标注时,注意过度透明会弱化关键位识别,属高风险品种下的视觉干扰。

MQL5 / C++
enum ENUM_TEXT_ANCHOR
  {
   TEXT_ANCHOR_LEFT_TOP       = class="num">0,       class=class="str">"cmt">// 边界框左上角
   TEXT_ANCHOR_CENTER_TOP     = class="num">1,       class=class="str">"cmt">// 边界框顶部居中
   TEXT_ANCHOR_RIGHT_TOP      = class="num">2,       class=class="str">"cmt">// 边界框右上角
   TEXT_ANCHOR_LEFT_CENTER    = class="num">4,       class=class="str">"cmt">// 边界框左侧居中
   TEXT_ANCHOR_CENTER         = class="num">5,       class=class="str">"cmt">// 边界框正中心
   TEXT_ANCHOR_RIGHT_CENTER   = class="num">6,       class=class="str">"cmt">// 边界框右侧居中
   TEXT_ANCHOR_LEFT_BOTTOM    = class="num">8,       class=class="str">"cmt">// 边界框左下角
   TEXT_ANCHOR_CENTER_BOTTOM  = class="num">9,       class=class="str">"cmt">// 边界框底部居中
   TEXT_ANCHOR_RIGHT_BOTTOM   = class="num">10,      class=class="str">"cmt">// 边界框右下角
  };
CColors::BlendColors(ColorToARGB(clrBlue,class="num">128),ColorToARGB(clrRed,class="num">64));

把颜色空间搬进EA做可视化

想在MT5里给K线或指标画出自适应配色,光用RGB不够用,往往需要Lab、LCH这类感知均匀的色彩空间。下面这个类把RGB、XYZ、Yxy、Hunter Lab、CIE Lab、CIE LCH之间的互转全封进了一个CColors静态类,方便直接在策略里调用。 私有方法只留了两个底层函数:Arctan2算四象限夹角,Hue_ToRGB负责把色相映射回通道值,其余转换都靠它们组合。公开接口覆盖了RGBtoXYZ、XYZtoCIELab、CIELabToCIELCH等十余个静态函数,全部用引用参数回写结果,不靠返回值搬运数据。 实战里你可以直接Copy这段代码到include,然后在OnCalculate里把涨跌强度转成CIEL值,再映射到面板背景色——外汇和贵金属波动大、跳空频繁,这类可视化对盘感有帮助,但杠杆交易高风险,颜色再漂亮也不能当信号本身。

MQL5 / C++
class=class="str">"cmt">//| Class for working with class="type">class="kw">color                                                                 |
class=class="str">"cmt">//+------------------------------------------------------------------+
class CColors
  {
class="kw">private:
   class="kw">static class="type">class="kw">double     Arctan2(class="kw">const class="type">class="kw">double x,class="kw">const class="type">class="kw">double y);
   class="kw">static class="type">class="kw">double     Hue_To_RGB(class="type">class="kw">double v1,class="type">class="kw">double v2,class="type">class="kw">double vH);
class="kw">public:
class=class="str">"cmt">//+--------------------------------------------------------------------+
class=class="str">"cmt">//| The list of functions from http://www.easyrgb.com/index.php?X=MATH  |
class=class="str">"cmt">//+--------------------------------------------------------------------+
   class="kw">static class="type">void       RGBtoXYZ(class="kw">const class="type">class="kw">double aR,class="kw">const class="type">class="kw">double aG,class="kw">const class="type">class="kw">double aB,class="type">class="kw">double &oX,class="type">class="kw">double &oY,class="type">class="kw">double &oZ);
   class="kw">static class="type">void       XYZtoRGB(class="kw">const class="type">class="kw">double aX,class="kw">const class="type">class="kw">double aY,class="kw">const class="type">class="kw">double aZ,class="type">class="kw">double &oR,class="type">class="kw">double &oG,class="type">class="kw">double &oB);
   class="kw">static class="type">void       XYZtoYxy(class="kw">const class="type">class="kw">double aX,class="kw">const class="type">class="kw">double aY,class="kw">const class="type">class="kw">double aZ,class="type">class="kw">double &oY,class="type">class="kw">double &ox,class="type">class="kw">double &oy);
   class="kw">static class="type">void       YxyToXYZ(class="kw">const class="type">class="kw">double aY,class="kw">const class="type">class="kw">double ax,class="kw">const class="type">class="kw">double ay,class="type">class="kw">double &oX,class="type">class="kw">double &oY,class="type">class="kw">double &oZ);
   class="kw">static class="type">void       XYZtoHunterLab(class="kw">const class="type">class="kw">double aX,class="kw">const class="type">class="kw">double aY,class="kw">const class="type">class="kw">double aZ,class="type">class="kw">double &oL,class="type">class="kw">double &oa,class="type">class="kw">double &ob);
   class="kw">static class="type">void       HunterLabToXYZ(class="kw">const class="type">class="kw">double aL,class="kw">const class="type">class="kw">double aa,class="kw">const class="type">class="kw">double ab,class="type">class="kw">double &oX,class="type">class="kw">double &oY,class="type">class="kw">double &oZ);
   class="kw">static class="type">void       XYZtoCIELab(class="kw">const class="type">class="kw">double aX,class="kw">const class="type">class="kw">double aY,class="kw">const class="type">class="kw">double aZ,class="type">class="kw">double &oCIEL,class="type">class="kw">double &oCIEa,class="type">class="kw">double &oCIEb);
   class="kw">static class="type">void       CIELabToXYZ(class="kw">const class="type">class="kw">double aCIEL,class="kw">const class="type">class="kw">double aCIEa,class="kw">const class="type">class="kw">double aCIEb,class="type">class="kw">double &oX,class="type">class="kw">double &oY,class="type">class="kw">double &oZ);
   class="kw">static class="type">void       CIELabToCIELCH(class="kw">const class="type">class="kw">double aCIEL,class="kw">const class="type">class="kw">double aCIEa,class="kw">const class="type">class="kw">double aCIEb,class="type">class="kw">double &oCIEL,class="type">class="kw">double &oCIEC,class="type">class="kw">double &oCIEH);

「颜色空间互转的静态接口清单」

做图表视觉增强或自定义指标配色时,直接在库里调这些 static 函数比自己重写转换公式稳得多。下面这组声明覆盖了从 CIELCH、CIELuv 到 HSL/HSV/CMY/CMYK 以及 RGB→Lab 的全链路互转,参数全部用 double 传值或引用返回。 以 RGBtoLab 为例:入参 aR/aG/aB 为归一化后的红绿蓝分量,出参引用 &oL/&oa/&ob 直接拿到 CIE Lab 的明度与色度坐标,省去中间走 XYZ 的白点校正。 如果你的盯盘面板要把热力图从 HSL 切到 HSV 再叠 CMYK 打印预览,RGBtoHSL 与 HSLtoRGB 这一对就能原地往返,不需要借助第三方 DLL。外汇与贵金属图表的高波动背景下,这类配色计算建议放在 OnInit 一次性缓存,避免每根 Tick 重算拖慢 MT5 执行器。

MQL5 / C++
class="kw">static class="type">void        CIELCHtoCIELab(class="kw">const class="type">class="kw">double aCIEL,class="kw">const class="type">class="kw">double aCIEC,class="kw">const class="type">class="kw">double aCIEH,class="type">class="kw">double &oCIEL,class="type">class="kw">double &oCIEa,class="type">class="kw">double &oCIEb);
class="kw">static class="type">void        XYZtoCIELuv(class="kw">const class="type">class="kw">double aX,class="kw">const class="type">class="kw">double aY,class="kw">const class="type">class="kw">double aZ,class="type">class="kw">double &oCIEL,class="type">class="kw">double &oCIEu,class="type">class="kw">double &oCIEv);
class="kw">static class="type">void        CIELuvToXYZ(class="kw">const class="type">class="kw">double aCIEL,class="kw">const class="type">class="kw">double aCIEu,class="kw">const class="type">class="kw">double aCIEv,class="type">class="kw">double &oX,class="type">class="kw">double &oY,class="type">class="kw">double &oZ);
class="kw">static class="type">void        RGBtoHSL(class="kw">const class="type">class="kw">double aR,class="kw">const class="type">class="kw">double aG,class="kw">const class="type">class="kw">double aB,class="type">class="kw">double &oH,class="type">class="kw">double &oS,class="type">class="kw">double &oL);
class="kw">static class="type">void        HSLtoRGB(class="kw">const class="type">class="kw">double aH,class="kw">const class="type">class="kw">double aS,class="kw">const class="type">class="kw">double aL,class="type">class="kw">double &oR,class="type">class="kw">double &oG,class="type">class="kw">double &oB);
class="kw">static class="type">void        RGBtoHSV(class="kw">const class="type">class="kw">double aR,class="kw">const class="type">class="kw">double aG,class="kw">const class="type">class="kw">double aB,class="type">class="kw">double &oH,class="type">class="kw">double &oS,class="type">class="kw">double &oV);
class="kw">static class="type">void        HSVtoRGB(class="kw">const class="type">class="kw">double aH,class="kw">const class="type">class="kw">double aS,class="kw">const class="type">class="kw">double aV,class="type">class="kw">double &oR,class="type">class="kw">double &oG,class="type">class="kw">double &oB);
class="kw">static class="type">void        RGBtoCMY(class="kw">const class="type">class="kw">double aR,class="kw">const class="type">class="kw">double aG,class="kw">const class="type">class="kw">double aB,class="type">class="kw">double &oC,class="type">class="kw">double &oM,class="type">class="kw">double &oY);
class="kw">static class="type">void        CMYtoRGB(class="kw">const class="type">class="kw">double aC,class="kw">const class="type">class="kw">double aM,class="kw">const class="type">class="kw">double aY,class="type">class="kw">double &oR,class="type">class="kw">double &oG,class="type">class="kw">double &oB);
class="kw">static class="type">void        CMYtoCMYK(class="kw">const class="type">class="kw">double aC,class="kw">const class="type">class="kw">double aM,class="kw">const class="type">class="kw">double aY,class="type">class="kw">double &oC,class="type">class="kw">double &oM,class="type">class="kw">double &oY,class="type">class="kw">double &oK);
class="kw">static class="type">void        CMYKtoCMY(class="kw">const class="type">class="kw">double aC,class="kw">const class="type">class="kw">double aM,class="kw">const class="type">class="kw">double aY,class="kw">const class="type">class="kw">double aK,class="type">class="kw">double &oC,class="type">class="kw">double &oM,class="type">class="kw">double &oY);
class="kw">static class="type">void        RGBtoLab(class="kw">const class="type">class="kw">double aR,class="kw">const class="type">class="kw">double aG,class="kw">const class="type">class="kw">double aB,class="type">class="kw">double &oL,class="type">class="kw">double &oa,class="type">class="kw">double &ob);

常见问题

先给图元对象绑定基础交互方法,再在事件处理函数里判断图元 ID 触发动作,具体可参考基础篇里图元方法封装的写法。
清屏时需同步重置文本锚点坐标,别只调清屏接口;锚点要按当前画布尺寸重新计算相对位置。
可以,小布能读取你的可视化代码逻辑,标记出半透明混色顺序异常和颜色空间未转换的隐患,并给出改法建议。
有,基础篇列了颜色空间互转的静态接口清单,直接调对应函数即可,不必自己写转换公式。
通常是混色时源和目标 alpha 没按画布位深归一,需在绘制前把颜色空间搬进 EA 并统一预乘 alpha。