DoEasy 函数库中的图形(第七十五部分):处理基本图形元素图元和文本的方法(基础篇)
◍ 给图元与文本加上可操作方法
在 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 自测一下就能复现。
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))。外汇与贵金属图表叠加多层半透明标注时,注意过度透明会弱化关键位识别,属高风险品种下的视觉干扰。
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值,再映射到面板背景色——外汇和贵金属波动大、跳空频繁,这类可视化对盘感有帮助,但杠杆交易高风险,颜色再漂亮也不能当信号本身。
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 执行器。
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);