DoEasy 函数库中的图形(第七十四部分):由 CCanvas 类提供强力支持的基本图形元素(基础篇)
📘

DoEasy 函数库中的图形(第七十四部分):由 CCanvas 类提供强力支持的基本图形元素(基础篇)

第 1/3 篇

◍ 正文

DoEasy 函数库中的图形(第七十四部分):由 CCanvas 类提供强力支持的基本图形元素 MetaTrader 5 — 示例 | 26 七月 2021, 09:41

  • 724

3 Artyom Trishkin 概述 改进库类 基于画布的所有函数库图形对象的基准对象 测试 下一步是什么?

把图形对象拆成「元素」再搭基类

上一节里基于 CCanvas 做的交互窗对象,细看下来比函数库基准对象复杂太多,概念也对不上。于是这里换思路:在画布上引入“元素(Element)”作为最底层的图形构件,后面所有图形对象都从它长出来。 具体层次是这样:基准图形对象派生自 CObject,带终端图形对象的固有属性;Element 对象属性全建立在画布对象之上;Form 对象给元素加外观属性与功能;Window 则是元素加交互窗的复合体。 按这个新概念,CGBaseObj 这个图形基类要重做,同时新建一个“图形元素”对象,把基准对象的整套构建逻辑完整复刻一遍。后面就能直接按对象类型快速检索、排序、管行为和渲染,MT5 里打开类视图对照这套继承链就能验证。

「给图形元素对象打地基」

要在 MT5 里用函数库统一管理画布类图形,第一步是扩展 DoEasy 的底层枚举与消息。Data.mqh 中新增了 MSG_LIB_SYS_FAILED_CREATE_ELM_OBJ 这条索引,对应俄英双语文本 "Failed to create graphic element object",专门捕获图形元素对象创建失败的运行期错误。 Defines.mqh 里补了 ENUM_GRAPH_ELEMENT_TYPE 枚举,目前含 GRAPH_ELEMENT_TYPE_ELEMENT、FORM、WINDOW 三个成员,把“图形元素”从普通 OBJ_LABEL 之类剥离成独立类型族。由于基于画布的对象在标准库中没有实数型属性,而构造函数库对象需要统一属性接口,作者塞了一个 stub 实数属性进去占位——这点在回看 CCanvas 派生结构时会发现它目前只是空壳。 GBaseObj.mqh 被整体重写而非修补:删空后只留图表 ID、子窗口索引、名称前缀、Y 偏移这几个公共字段,所有具体图形对象后续都从它继承。m_name_prefix 默认拼上程序名加下划线,完整对象名 = 前缀 + 用户给的唯一名,这样多 EA 同图运行时不会互相误删对方的图形。 类构造函数初始化列表里把 m_type 默认置 0,并重写标准库 CObject::Type() 虚函数返回 m_type 而非写死值。想验证的话,开 MT5 在 Include\DoEasy 路径下比对上述枚举与文本是否就位,缺一条编译图形元素模块就会报未定义标识符。外汇与贵金属图表加载此类 EA 属高风险操作,图形对象泄漏可能拖慢终端响应。

MQL5 / C++
  MSG_LIB_SYS_FAILED_CREATE_STORAGE_FOLDER,          class=class="str">"cmt">// Failed to create folder for storing files. Error: 
  MSG_LIB_SYS_FAILED_ADD_ACC_OBJ_TO_LIST,              class=class="str">"cmt">// Error. Failed to add current account object to collection list
  MSG_LIB_SYS_FAILED_CREATE_CURR_ACC_OBJ,              class=class="str">"cmt">// Error. Failed to create account object with current account data
  MSG_LIB_SYS_FAILED_OPEN_FILE_FOR_WRITE,              class=class="str">"cmt">// Could not open file for writing
  MSG_LIB_SYS_INPUT_ERROR_NO_SYMBOL,                   class=class="str">"cmt">// Input error: no symbol
  MSG_LIB_SYS_FAILED_CREATE_SYM_OBJ,                   class=class="str">"cmt">// Failed to create symbol object
  MSG_LIB_SYS_FAILED_ADD_SYM_OBJ,                      class=class="str">"cmt">// Failed to add symbol
  MSG_LIB_SYS_FAILED_CREATE_ELM_OBJ,                   class=class="str">"cmt">// Failed to create the graphical element object
  {"Не удалось создать папку хранения файлов. Ошибка: ","Could not create file storage folder. Error: "},
  {"Ошибка. Не удалось добавить текущий объект-аккаунт в список-коллекцию","Error. Failed to add current account object to collection list"},
  {"Ошибка. Не удалось создать объект-аккаунт с данными текущего счёта","Error. Failed to create account object with current account data"},
  {"Не удалось открыть для записи файл ","Could not open file for writing: "},
  {"Ошибка входных данных: нет символа ","Input error: no "},
  {"Не удалось создать объект-символ ","Failed to create symbol object "},
  {"Не удалось добавить символ ","Failed to add "},
  {"Не удалось создать объект-графический элемент ","Failed to create graphic element object "},
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| The list of graphical element types                              |
class=class="str">"cmt">//+------------------------------------------------------------------+
enum ENUM_GRAPH_ELEMENT_TYPE
  {
   GRAPH_ELEMENT_TYPE_ELEMENT,                        class=class="str">"cmt">// Element
   GRAPH_ELEMENT_TYPE_FORM,                            class=class="str">"cmt">// Form
   GRAPH_ELEMENT_TYPE_WINDOW,                          class=class="str">"cmt">// Window
   };
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Integer properties of the graphical element on the canvas         |
class=class="str">"cmt">//+------------------------------------------------------------------+
enum ENUM_CANV_ELEMENT_PROP_INTEGER
  {

◍ 画布表单的整数属性枚举

在 MT5 的 Canvas 界面框架里,一个表单(Form)被拆成 23 个整数属性来管理,从 ID、类型、所属图表子窗口到坐标宽高全在其中。 下面这段枚举定义了每个属性的语义,开 MT5 把代码贴进 MQ5 头文件就能直接引用,不用自己重排顺序。 注意 CANV_ELEMENT_PROP_INTEGER_TOTAL 被宏定义为 23,和枚举里实际列出的属性条数一致;若你往后增删属性,这个宏必须同步改,否则遍历属性数组会越界。 外汇与贵金属图表上挂这类自定义画布属于高风险操作,属性算错可能导致面板漂移到不可见区域,建议在策略测试器里先验证坐标逻辑。

MQL5 / C++
CANV_ELEMENT_PROP_ID = class="num">0,                                              class=class="str">"cmt">// Form ID
CANV_ELEMENT_PROP_TYPE,                                                  class=class="str">"cmt">// Graphical element type
CANV_ELEMENT_PROP_NUM,                                                   class=class="str">"cmt">// Element index in the list
CANV_ELEMENT_PROP_CHART_ID,                                              class=class="str">"cmt">// Chart ID
CANV_ELEMENT_PROP_WND_NUM,                                               class=class="str">"cmt">// Chart subwindow index
CANV_ELEMENT_PROP_COORD_X,                                               class=class="str">"cmt">// Form&class="macro">#x27;s X coordinate on the chart
CANV_ELEMENT_PROP_COORD_Y,                                               class=class="str">"cmt">// Form&class="macro">#x27;s Y coordinate on the chart
CANV_ELEMENT_PROP_WIDTH,                                                 class=class="str">"cmt">// Form width
CANV_ELEMENT_PROP_HEIGHT,                                                class=class="str">"cmt">// Form height
CANV_ELEMENT_PROP_RIGHT,                                                 class=class="str">"cmt">// Form right border
CANV_ELEMENT_PROP_BOTTOM,                                                class=class="str">"cmt">// Form bottom border
CANV_ELEMENT_PROP_ACT_SHIFT_LEFT,                                        class=class="str">"cmt">// Active area offset from the left edge of the form
CANV_ELEMENT_PROP_ACT_SHIFT_TOP,                                         class=class="str">"cmt">// Active area offset from the top edge of the form
CANV_ELEMENT_PROP_ACT_SHIFT_RIGHT,                                       class=class="str">"cmt">// Active area offset from the right edge of the form
CANV_ELEMENT_PROP_ACT_SHIFT_BOTTOM,                                      class=class="str">"cmt">// Active area offset from the bottom edge of the form
CANV_ELEMENT_PROP_OPACITY,                                               class=class="str">"cmt">// Form opacity
CANV_ELEMENT_PROP_COLOR_BG,                                              class=class="str">"cmt">// Form background class="type">color
CANV_ELEMENT_PROP_MOVABLE,                                               class=class="str">"cmt">// Form moveability flag
CANV_ELEMENT_PROP_ACTIVE,                                                class=class="str">"cmt">// Form activity flag
CANV_ELEMENT_PROP_COORD_ACT_X,                                           class=class="str">"cmt">// X coordinate of the form&class="macro">#x27;s active area
CANV_ELEMENT_PROP_COORD_ACT_Y,                                           class=class="str">"cmt">// Y coordinate of the form&class="macro">#x27;s active area
CANV_ELEMENT_PROP_ACT_RIGHT,                                             class=class="str">"cmt">// Right border of the form&class="macro">#x27;s active area
CANV_ELEMENT_PROP_ACT_BOTTOM,                                            class=class="str">"cmt">// Bottom border of the form&class="macro">#x27;s active area
};
class="macro">#define CANV_ELEMENT_PROP_INTEGER_TOTAL(class="num">23)                              class=class="str">"cmt">// Total number of integer properties

画布元素属性与排序枚举的底层定义

在 MT5 自定义图形界面开发里,画布元素被拆成整数、实数、字符串三类属性分别管理。整数类跳过了 0 个不参与排序的字段,实数类只定义了 1 个占位属性(CANV_ELEMENT_PROP_DUMMY)且全部跳过排序,字符串类则有对象名与资源名共 2 个属性。 排序起始偏移由宏算出:整数可用排序起点 = 总数减跳过数;字符串起点还要再叠加上实数部分。代码里 FIRST_CANV_ELEMENT_STR_PROP 正是用 INTEGER_TOTAL 减 INTEGER_SKIP 再加 DOUBLE_TOTAL 减 DOUBLE_SKIP 推出来的,改这两个 SKIP 宏会直接挪动字符串排序号。 ENUM_SORT_CANV_ELEMENT_MODE 从 0 开始枚举,SORT_BY_CANV_ELEMENT_ID 到 SORT_BY_CANV_ELEMENT_RIGHT 覆盖了 ID、类型、列表序号、图表 ID、窗口序号、X/Y 坐标、宽高及右边框等 10 种整数排序依据。实盘写面板重绘逻辑时,按坐标或宽度排序可能显著改变图层叠放顺序,外汇与贵金属图表多窗口场景下误排会导致点击命中错位,属高风险操作,建议开 MT5 用这两段宏现场改 SKIP 值验证偏移。

MQL5 / C++
class="macro">#define CANV_ELEMENT_PROP_INTEGER_SKIP(class="num">0)        class=class="str">"cmt">// Number of integer properties not used in sorting
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Real properties of the graphical element on the canvas            |
class=class="str">"cmt">//+------------------------------------------------------------------+
enum ENUM_CANV_ELEMENT_PROP_DOUBLE
  {
   CANV_ELEMENT_PROP_DUMMY = CANV_ELEMENT_PROP_INTEGER_TOTAL, class=class="str">"cmt">// DBL stub
  };
class="macro">#define CANV_ELEMENT_PROP_DOUBLE_TOTAL(class="num">1)        class=class="str">"cmt">// Total number of real properties
class="macro">#define CANV_ELEMENT_PROP_DOUBLE_SKIP(class="num">1)        class=class="str">"cmt">// Number of real properties not used in sorting
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| String properties of the graphical element on the canvas          |
class=class="str">"cmt">//+------------------------------------------------------------------+
enum ENUM_CANV_ELEMENT_PROP_STRING
  {
   CANV_ELEMENT_PROP_NAME_OBJ = (CANV_ELEMENT_PROP_INTEGER_TOTAL+CANV_ELEMENT_PROP_DOUBLE_TOTAL), class=class="str">"cmt">// Form object name
   CANV_ELEMENT_PROP_NAME_RES,                                                    class=class="str">"cmt">// Graphical resource name
  };
class="macro">#define CANV_ELEMENT_PROP_STRING_TOTAL(class="num">2)        class=class="str">"cmt">// Total number of class="type">class="kw">string properties
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Possible sorting criteria of graphical elements on the canvas    |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#define FIRST_CANV_ELEMENT_DBL_PROP(CANV_ELEMENT_PROP_INTEGER_TOTAL-CANV_ELEMENT_PROP_INTEGER_SKIP)
class="macro">#define FIRST_CANV_ELEMENT_STR_PROP(CANV_ELEMENT_PROP_INTEGER_TOTAL-CANV_ELEMENT_PROP_INTEGER_SKIP+CANV_ELEMENT_PROP_DOUBLE_TOTAL-CANV_ELEMENT_PROP_DOUBLE_SKIP)
enum ENUM_SORT_CANV_ELEMENT_MODE
  {
class=class="str">"cmt">//--- Sort by integer properties
   SORT_BY_CANV_ELEMENT_ID = class="num">0,                      class=class="str">"cmt">// Sort by form ID
   SORT_BY_CANV_ELEMENT_TYPE,                        class=class="str">"cmt">// Sort by graphical element type
   SORT_BY_CANV_ELEMENT_NUM,                         class=class="str">"cmt">// Sort by form index in the list
   SORT_BY_CANV_ELEMENT_CHART_ID,                    class=class="str">"cmt">// Sort by chart ID
   SORT_BY_CANV_ELEMENT_WND_NUM,                     class=class="str">"cmt">// Sort by chart window index
   SORT_BY_CANV_ELEMENT_COORD_X,                     class=class="str">"cmt">// Sort by the form X coordinate on the chart
   SORT_BY_CANV_ELEMENT_COORD_Y,                     class=class="str">"cmt">// Sort by the form Y coordinate on the chart
   SORT_BY_CANV_ELEMENT_WIDTH,                       class=class="str">"cmt">// Sort by the form width
   SORT_BY_CANV_ELEMENT_HEIGHT,                      class=class="str">"cmt">// Sort by the form height
   SORT_BY_CANV_ELEMENT_RIGHT,                       class=class="str">"cmt">// Sort by the form right border
  };

「画布元素排序枚举的收尾定义」

上面这组枚举值把画布表单对象能拿来排序的维度列干净了:从外框底边 SORT_BY_CANV_ELEMENT_BOTTOM,到活动区相对四边的偏移、透明度、背景色、是否可拖动、是否激活,再到活动区坐标与右/下边界,一共 14 个整型枚举项。 字符串类排序单独从 FIRST_CANV_ELEMENT_STR_PROP 起算,目前挂了表单对象名 SORT_BY_CANV_ELEMENT_NAME_OBJ 和图形资源名 SORT_BY_CANV_ELEMENT_NAME_RES 两项;注释里留了『按真实属性』『按字符串属性』的分组空行,说明后续可能继续扩。 下方 GBaseObj.mqh 头文件标注 2021 年 MetaQuotes 版权、version 1.00 与 strict 模式(兼顾 mql4 编译)。在 MT5 里新建 EA 引用该枚举时,可直接用 SORT_BY_CANV_ELEMENT_ACT_SHIFT_LEFT 这类常量给自定义面板做 z-order 或命中测试排序,省去手写比较函数。

MQL5 / C++
  SORT_BY_CANV_ELEMENT_BOTTOM,                                            class=class="str">"cmt">// Sort by the form bottom border
  SORT_BY_CANV_ELEMENT_ACT_SHIFT_LEFT,                                     class=class="str">"cmt">// Sort by the active area offset from the left edge of the form
  SORT_BY_CANV_ELEMENT_ACT_SHIFT_TOP,                                      class=class="str">"cmt">// Sort by the active area offset from the top edge of the form
  SORT_BY_CANV_ELEMENT_ACT_SHIFT_RIGHT,                                    class=class="str">"cmt">// Sort by the active area offset from the right edge of the form
  SORT_BY_CANV_ELEMENT_ACT_SHIFT_BOTTOM,                                   class=class="str">"cmt">// Sort by the active area offset from the bottom edge of the form
  SORT_BY_CANV_ELEMENT_OPACITY,                                            class=class="str">"cmt">// Sort by the form opacity
  SORT_BY_CANV_ELEMENT_COLOR_BG,                                           class=class="str">"cmt">// Sort by the form background class="type">color
  SORT_BY_CANV_ELEMENT_MOVABLE,                                            class=class="str">"cmt">// Sort by the form moveability flag
  SORT_BY_CANV_ELEMENT_ACTIVE,                                             class=class="str">"cmt">// Sort by the form activity flag
  SORT_BY_CANV_ELEMENT_COORD_ACT_X,                                        class=class="str">"cmt">// Sort by X coordinate of the form active area
  SORT_BY_CANV_ELEMENT_COORD_ACT_Y,                                        class=class="str">"cmt">// Sort by Y coordinate of the form active area
  SORT_BY_CANV_ELEMENT_ACT_RIGHT,                                          class=class="str">"cmt">// Sort by the right border of the form active area
  SORT_BY_CANV_ELEMENT_ACT_BOTTOM,                                         class=class="str">"cmt">// Sort by the bottom border of the form active area
class=class="str">"cmt">//--- Sort by real properties
class=class="str">"cmt">//--- Sort by class="type">class="kw">string properties
  SORT_BY_CANV_ELEMENT_NAME_OBJ = FIRST_CANV_ELEMENT_STR_PROP,             class=class="str">"cmt">// Sort by the form object name
  SORT_BY_CANV_ELEMENT_NAME_RES,                                           class=class="str">"cmt">// Sort by the graphical resource name
  };
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|                                                                 GBaseObj.mqh |
class=class="str">"cmt">//|                                                Copyright class="num">2021, MetaQuotes Ltd. |
class=class="str">"cmt">//|                                         [MQL5官方文档] |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#class="kw">property copyright "Copyright class="num">2021, MetaQuotes Ltd."
class="macro">#class="kw">property link      "[MQL5官方文档]
class="macro">#class="kw">property version   "class="num">1.00"
class="macro">#class="kw">property strict    class=class="str">"cmt">// Necessary for mql4
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Include files                                                            |
class=class="str">"cmt">//+------------------------------------------------------------------+

常见问题

先定义图形元素基类,把位置、大小、可见性等公共属性抽出来,再让具体形状继承,避免每个图形都重写一遍底层逻辑。
至少覆盖宽度、高度、背景色、边框状态等整数类配置,用枚举统一管理比散落变量更安全,也方便后期扩展。
可以。把元素枚举和基类结构贴给小布,它能对照常见画布表单属性清单帮你标出缺失项,并给出补充建议。
收尾只需补完层级关系和绘制优先级常量,确保数值不重复且预留扩展位,后续新增元素就不会破坏原排序。
先定属性相当于打地基,具体元素只填差异部分,改样式时不用动结构,调试和复用都更直接。