在 MQL 应用程序中运用 CCanvas 类(基础篇)
「用 CCanvas 类在 MQL 里画自己的图」
MQL5 标准库里的 CCanvas 类,能让你在指标或 EA 里脱离默认图表对象,直接往一张内存画布上绘点、线、文字和位图,再把结果贴回图表窗口。对做自定义面板、信号热区、或把 AIGC 输出可视化到 MT5 的交易者来说,这是绕不开的底层接口。 原文示例发布于 2022 年 4 月 21 日,截至收录时公开页显示浏览量 1492、评论 6 条,说明这个类在量化社群里属于低频但刚需的硬知识,不是花哨噱头。 想验证它到底能画多细,直接开 MT5 新建一个指标,include <Canvas/Canvas.h> 后实例化 CCanvas,调 Resize 和 Fill 就能看到一张空白画布覆盖在副图上,比读文档快得多。
程序里的画布与像素权限
想在 MT5 自定义指标或 EA 里画出自己的面板、按钮甚至从文件贴图,第一步不是学绘图函数,而是确认你拿到了屏幕像素的操作权。没有这块“画布”,你连改一个像素颜色都做不到。 在基于 C 语系的开发环境里,这套机制几乎都封装在名字带 Canvas 的类里,MQL5 对应的是 CCanvas。它相当于应用向图形子系统申请到的一块显存区域,之后所有界面元素、输入域、窗口都得在这块区域上叠。 实际验证很简单:新建一个 EA,在 OnInit 里实例化 CCanvas 并调用 Resize,若能编译通过且运行时不报 4014 类错误,说明你的程序已经具备图形访问基础。外汇与贵金属交易本身杠杆高、风险大,自动化图形界面只是辅助观察,别把它当成信号保真度的保证。
◍ CCanvas 类怎么把像素搬上 MT5 图表
MQL5 里做自定义绘图的核心是 CCanvas 类:它内部维护一块像素数组,提供改像素的方法,再借助 OBJ_BITMAP 或 OBJ_BITMAP_LABEL 图形对象把数据投到终端图表上。也就是说,图形对象本身不画,它只从图形资源取数,而资源又直接从像素数组接数据。 这套机制不只能搭桌面级 UI,还能直接画指标缓冲区、OHLC 柱线,甚至更复杂的叠加层。对做价格行为可视化的交易者来说,这意味着你可以在 MT5 原生图表里塞进自己算的供需区色块,而不依赖第三方指标。 外汇和贵金属杠杆高、滑点随机,任何自绘层都只是辅助判形,信号失效要及时砍仓。开 MT5 新建一个 EA 把下面代码跑一遍,就能看到一张 200x100 的红色位图被贴到图表左上角。
class="macro">#include <Canvas/Canvas.h> CCanvas canvas; class="type">int OnInit() { if(!canvas.CreateBitmapLabel("my_canvas", class="num">0, class="num">0, class="num">200, class="num">100, COLOR_FORMAT_ARGB_RAW)) class="kw">return INIT_FAILED; canvas.Fill(0xFFFF0000); class=class="str">"cmt">// 填红色 canvas.Update(); class=class="str">"cmt">// 推到图表 class="kw">return INIT_SUCCEEDED; }
「拆开 CCanvas 的数据与方法骨架」
在 MT5 里用 CCanvas 自绘图形,先得弄清它内部怎么存数据、怎么暴露接口。这个类明显分成两半:数据成员和方法成员,理解这两块才能避免调错参数还不知道错在哪。 数据侧有五组字段。像素层是 m_pixels 数组加 m_width、m_height,所有绘图方法改的都是这块内存,Update 时再整体推给图形资源;字体层 m_fontname、m_fontsize、m_fontflags、m_fontangle 控制 TextOut 出字效果,调文本前必须设好;线条层 m_style 和 m_style_idx 是 AA/Wu/Thick 类画线方法的必需输入;m_default_colors 只是调色板备用,基类不用;图表交互层 m_chart_id、m_objname、m_objtype、m_rcname、m_format 专门喂给 Destroy、Update、Resize,其中 m_format 在 TextOut 时也参与运算。 方法侧最容易被忽略的是生命周期。CreateBitmap / CreateBitmapLabel 建对象并绑资源,Attach 能挂已有 OBJ_BITMAP_LABEL,Destroy 才释放对象和 m_pixels 缓冲区——CCanvas 析构不会自动清,跑完不调 Destroy 就会漏图形资源。 绘图方法按平滑等级分了多族:裸线有 Line/Polygon/Circle 等,AA 族带抗锯齿,Wu 族走 Wu 算法,Thick 族可调线宽,还有 PolylineSmooth 用 Bézier 做曲线。TextOut 虽归文本组,但本质也是改像素数组。LoadFromFile 要求 m_format 等于 COLOR_FORMAT_ARGB_RAW 才能把 bmp 读进 m_pixels,这点不对照源码容易踩坑。 私有区域字段和内部方法不在本文范围,MetaEditor 打开 Canvas.mqh 能直接看。外汇与贵金属图表上做这类自绘叠加,属于高风险环境下的辅助显示,参数误设只影响视觉不影响下单,但别把它当信号源。
CCanvas 画图的标准动作链
在 MT5 里用 CCanvas 往图表上塞自定义图形,流程是固定的:先建或挂一个 OBJ_BITMAP / OBJ_BITMAP_LABEL 对象,设好字体和线型,再用类方法往 m_pixels 数组里画,最后调资源更新,图表才显形。跳步通常只会得到一片空白或残影。 建对象有三条路。CreateBitmap 生成绑定时间+价格坐标的位图,适合贴在主图做背景层;CreateBitmapLabel 用 x/y 像素坐标定位,不占价格轴,做悬浮标签更顺手。若图表里已有 OBJ_BITMAP_LABEL,直接 Attach 上去即可,省去重建。 字体和线型要在画之前定。FontSet 系列管name/size/flags/angle,LineStyleSet 写 m_style 默认值——后面画简单图元时省略 style 参数就会吃这个值。文本用 TextOut 在指定 x/y 以某色和对齐方式输出,像素级可用 PixelSet 直接改 m_pixels。 图元方法分三档:基础版线宽锁死 1 像素、实线 STYLE_SOLID;加 style 参数的版本能从 ENUM_LINE_STYLE 挑虚线点线;再带 size 和 end_style 的版本才能改线宽和线头样式。贝塞尔系列多两个参数 tension(平滑度)和 step(近似步长),画出来的是曲线不是折线,位图做了抗锯齿。 下面这段是 CreateBitmap 与 CreateBitmapLabel 的声明原型,注意重载版把 chart_id 和 subwin 写死成 0(当前图主窗口),少传两个参数更省事:
class="type">bool CreateBitmap(class="kw">const class="type">long chart_id, class="kw">const class="type">int subwin, class="kw">const class="type">class="kw">string name, class="kw">const class="type">class="kw">datetime time, class="kw">const class="type">class="kw">double price, class="kw">const class="type">int width, class="kw">const class="type">int height, ENUM_COLOR_FORMAT clrfmt=COLOR_FORMAT_XRGB_NOALPHA); class="type">bool CreateBitmap(class="kw">const class="type">class="kw">string name, class="kw">const class="type">class="kw">datetime time, class="kw">const class="type">class="kw">double price, class="kw">const class="type">int width, class="kw">const class="type">int height,ENUM_COLOR_FORMAT clrfmt=COLOR_FORMAT_XRGB_NOALPHA); class="type">bool CreateBitmapLabel(class="kw">const class="type">long chart_id, class="kw">const class="type">int subwin, class="kw">const class="type">class="kw">string name, class="kw">const class="type">int x, class="kw">const class="type">int y, class="kw">const class="type">int width, class="kw">const class="type">int height, ENUM_COLOR_FORMAT clrfmt=COLOR_FORMAT_XRGB_NOALPHA);
◍ 在图表上落地自定义画布与字体
CCanvas 类提供了两套创建位图对象的入口:CreateBitmap 把资源锚定在指定时间与价格的坐标上,适合画随 K 线移动的分析图层;CreateBitmapLabel 则用像素坐标 (x,y) 定位,更适合做固定位置的 HUD 式标注。两者底层都转调了带偏移量的重载版本,前两个参数传 0 表示相对图表原点不额外偏移。 Attach 方法有两个虚函数重载,一个显式传入宽高与色彩格式,另一个仅用 objname 和 chart_id 让画布接管已有对象——后者要求目标对象已存在且尺寸匹配,否则返回失败。色彩格式默认 COLOR_FORMAT_XRGB_NOALPHA,即 32 位不含透明通道,处理效率比带 Alpha 的更高。 字体控制是一组对称的 Set/Get:FontNameSet 收字符串、FontSizeSet 收 int 像素值、FontFlagsSet 收 uint 属性位、FontAngleSet 收 uint 斜率角(单位 1/10 度)。FontSet 是一次性封装,flags 与 angle 缺省为 0 表示常规正字。 [CODE] bool CreateBitmapLabel(const string name,const int x,const int y, const int width,const int height,ENUM_COLOR_FORMAT clrfmt=COLOR_FORMAT_XRGB_NOALPHA); //+------------------------------------------------------------------+
| // | Create object on chart with attached dynamic resource |
|---|
//+------------------------------------------------------------------+ bool CCanvas::CreateBitmap(const string name,const datetime time,const double price, const int width,const int height,ENUM_COLOR_FORMAT clrfmt) { return(CreateBitmap(0,0,name,time,price,width,height,clrfmt)); } //+------------------------------------------------------------------+
| // | Create object on chart with attached dynamic resource |
|---|
//+------------------------------------------------------------------+ bool CCanvas::CreateBitmapLabel(const string name,const int x,const int y, const int width,const int height,ENUM_COLOR_FORMAT clrfmt) { return(CreateBitmapLabel(0,0,name,x,y,width,height,clrfmt)); } virtual bool Attach(const long chart_id,const string objname,const int width,const int height,ENUM_COLOR_FORMAT clrfmt=COLOR_FORMAT_XRGB_NOALPHA); virtual bool Attach(const long chart_id,const string objname,ENUM_COLOR_FORMAT clrfmt=COLOR_FORMAT_XRGB_NOALPHA); bool FontNameSet(string name); // Set the font name bool FontSizeSet(int size); // Set the font size bool FontFlagsSet(uint flags); // Set the font attributes bool FontAngleSet(uint angle); // Set the font slope angle string FontNameGet(void) const; // Return the font name int FontSizeGet(void) const; // Return the font size uint FontFlagsGet(void) const;// Return the font attributes uint FontAngleGet(void) const;// Return the font slope angle bool FontSet(const string name,const int size,const uint flags=0,const uint angle=0); // Set the font properties [/CODE] 逐行拆解:第 1 行声明 CreateBitmapLabel 独立函数,参数含名称、像素坐标、宽高与色彩格式,默认无透明通道。CCanvas::CreateBitmap 实现里把 0,0 作为子图偏移传入重载,等价于图表基准点起绘。CreateBitmapLabel 同理,用 0,0 表示不相对对象再平移。两个 Attach 虚函数分别应对「新建尺寸」与「绑定现成」两种场景。Font*Set 与 Font*Get 成对出现,angle 以 uint 存 0.1 度步进;FontSet 用 flags=0、angle=0 的默认值快速设正字常规体。 开 MT5 新建 EA 时 include <Canvas/Canvas.h>,先 Attach 一个 OBJ_BITMAP_LABEL 再 FontSet("Consolas",10),即可在左上角稳定绘制帧率类监控文字,外汇与贵金属图表波动剧烈,此类自绘层需注意重绘频率以免占用主线程。
class="type">bool CreateBitmapLabel(class="kw">const class="type">class="kw">string name,class="kw">const class="type">int x,class="kw">const class="type">int y, class="kw">const class="type">int width,class="kw">const class="type">int height,ENUM_COLOR_FORMAT clrfmt=COLOR_FORMAT_XRGB_NOALPHA); class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Create object on chart with attached dynamic resource | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool CCanvas::CreateBitmap(class="kw">const class="type">class="kw">string name,class="kw">const class="type">class="kw">datetime time,class="kw">const class="type">class="kw">double price, class="kw">const class="type">int width,class="kw">const class="type">int height,ENUM_COLOR_FORMAT clrfmt) { class="kw">return(CreateBitmap(class="num">0,class="num">0,name,time,price,width,height,clrfmt)); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Create object on chart with attached dynamic resource | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool CCanvas::CreateBitmapLabel(class="kw">const class="type">class="kw">string name,class="kw">const class="type">int x,class="kw">const class="type">int y, class="kw">const class="type">int width,class="kw">const class="type">int height,ENUM_COLOR_FORMAT clrfmt) { class="kw">return(CreateBitmapLabel(class="num">0,class="num">0,name,x,y,width,height,clrfmt)); } class="kw">virtual class="type">bool Attach(class="kw">const class="type">long chart_id,class="kw">const class="type">class="kw">string objname,class="kw">const class="type">int width,class="kw">const class="type">int height,ENUM_COLOR_FORMAT clrfmt=COLOR_FORMAT_XRGB_NOALPHA); class="kw">virtual class="type">bool Attach(class="kw">const class="type">long chart_id,class="kw">const class="type">class="kw">string objname,ENUM_COLOR_FORMAT clrfmt=COLOR_FORMAT_XRGB_NOALPHA); class="type">bool FontNameSet(class="type">class="kw">string name); class=class="str">"cmt">// Set the font name class="type">bool FontSizeSet(class="type">int size); class=class="str">"cmt">// Set the font size class="type">bool FontFlagsSet(class="type">uint flags); class=class="str">"cmt">// Set the font attributes class="type">bool FontAngleSet(class="type">uint angle); class=class="str">"cmt">// Set the font slope angle class="type">class="kw">string FontNameGet(class="type">void) class="kw">const; class=class="str">"cmt">// Return the font name class="type">int FontSizeGet(class="type">void) class="kw">const; class=class="str">"cmt">// Return the font size class="type">uint FontFlagsGet(class="type">void) class="kw">const;class=class="str">"cmt">// Return the font attributes class="type">uint FontAngleGet(class="type">void) class="kw">const;class=class="str">"cmt">// Return the font slope angle class="type">bool FontSet(class="kw">const class="type">class="kw">string name,class="kw">const class="type">int size,class="kw">const class="type">uint flags=class="num">0,class="kw">const class="type">uint angle=class="num">0); class=class="str">"cmt">// Set the font properties