基于 CChartObject 类设计和实施新 GUI 组件·综合运用
🧩

基于 CChartObject 类设计和实施新 GUI 组件·综合运用

(3/3)· 从基类派生到动态二维数组表格,把 MQL5 标准库的 GUI 扩展能力一次性用透

含代码示例 第 3/3 篇
不少交易者在图表上堆了一堆 ObjectCreate 散函数,改个属性就要翻六七个 Set 函数,维护到第三层就乱了。直接裸写对象函数而不封装成类,复杂面板一旦要支持编辑和动态行,基本不可持续。

用二维数组搭可变色 MTF 指标表格

在多时间表 EA 里,常需要把不同周期的指标值分格显示。原生 MT5 没有现成的二维表格控件,逐个画矩形不仅繁琐,改色和填字也得重复写逻辑。 CChartObjectEditTable 这个类用指向 CArrayObj 的指针 array2D 存了一个“数组的数组”:外层每一列是一个 CArrayObj,里面再塞 CChartObjectEdit 单元格对象。m_rows、m_cols 记行列数,m_baseName 给所有单元格名加统一前缀,便于后续定位。 代码里 Create() 的默认参数是 rows=1、cols=1、sizeX=15、sizeY=15、deltaX=5、deltaY=5,也就是说不传参时只会生成一个 15×15 像素、间距 5 的小编辑框。实际做 MTF 面板时,按周期数设 cols、按指标线数设 rows 即可。 SetColor() 和 SetText() 都先按列找 array2D 里的子数组,再用行号取第 n 个 CChartObjectEdit,分别调 BackColor() 和 Description()。想让某周期某指标格变红,直接 SetColor(行, 列, Red) 就行,不用重画对象。 外汇与贵金属杠杆高、滑点跳空频繁,这类表格只做信息呈现,不构成任何方向暗示;开着 MT5 把下面类头文件和 Create 逻辑抄进脚本,挂不同品种验证行列偏移是否符合预期。

MQL5 / C++
class CChartObjectEditTable
  {
class="kw">private:
  CArrayObj          *array2D;
  class="type">int                m_rows;
  class="type">int                m_cols;
  class="type">class="kw">string             m_baseName;
class="kw">public:
  class="type">bool               Create(class="type">long chart_id,class="type">class="kw">string name,class="type">int window,class="type">int rows,class="type">int cols,class="type">int startX,class="type">int startY,class="type">int
class="type">int sizeX,class="type">int sizeY,class="type">class="kw">color Bg,class="type">int deltaX,class="type">int deltaY);
  class="type">bool               Delete();
  class="type">bool               SetColor(class="type">int row,class="type">int col,class="type">class="kw">color newColor);
  class="type">class="kw">color              GetColor(class="type">int row,class="type">int col);
  class="type">bool               SetText(class="type">int row,class="type">int col,class="type">class="kw">string newText);
  class="type">class="kw">string             GetText(class="type">int row,class="type">int col);
  };
class="type">bool CChartObjectEditTable::Create(class="type">long chart_id,class="type">class="kw">string name,class="type">int window,class="type">int rows=class="num">1,class="type">int cols=class="num">1,class="type">int
class="type">int startX=class="num">0,class="type">int startY=class="num">0,class="type">int sizeX=class="num">15,class="type">int sizeY=class="num">15,
class="type">class="kw">color Bg=White,class="type">int deltaX=class="num">5,class="type">int deltaY=class="num">5)
  {
  m_rows=rows;
  m_cols=cols;
  m_baseName=name;
  class="type">int i=class="num">0,j=class="num">0;
  array2D=new CArrayObj();
  if (array2D==NULL) class="kw">return class="kw">false;

  for(j=class="num">0; j<m_cols; j++)
    {
    CArrayObj *new_array=new CArrayObj();
    if (array2D==NULL) class="kw">return class="kw">false;

    array2D.Add(new_array);
    for(i=class="num">0; i<m_rows; i++)
      {
      CChartObjectEdit *new_edit=new CChartObjectEdit();
      new_edit.Create(chart_id, name+IntegerToString(i)+":"+IntegerToString(j), window,
startX+j*(sizeX+deltaX), startY+i*(sizeY+deltaY), sizeX, sizeY);
      new_edit.BackColor(Bg);
      new_edit.Color(White);
      new_edit.Selectable(class="kw">false);

◍ 表格控件的读写与销毁接口

上面这段把可编辑表格类收了尾:先给新建的编辑框设为只读并清空描述,再塞进列数组里,整个创建流程返回 true 表示成功。 SetColor 和 GetColor 都走同一个寻址逻辑——先用列号从 array2D 取子数组,再用行号取具体的 CChartObjectEdit 指针。越界一律返回 false 或 NULL,所以调用前得自己保证 row 在 [0, m_rows)、col 在 [0, m_cols)。 SetText 只改 Description,不改只读属性;想动态改内容又怕用户手滑,就保持 ReadOnly(true) 然后频繁调它。 Delete 里先按列清掉每个子数组再 delete,最后释放 array2D 本身,漏掉任意一层都会留内存尾巴。 实际挂表时这样落:table.Create(0,"t",0,1,10,10,10,15,15,Yellow) 建 10×10 格、左上角偏移 (1,10);table.SetColor(2,2,Red) 把第 3 行第 3 列染红,table.SetText(2,2,"2") 写个标记。外汇贵金属图表上叠这种表属于高风险辅助显示,参数错乱可能挡住价格本身。

MQL5 / C++
      new_edit.ReadOnly(true);
      new_edit.Description("");
      new_array.Add(new_edit);
      }
   }
  class="kw">return true;
  }
class="type">bool CChartObjectEditTable::SetColor(class="type">int row,class="type">int col,class="type">class="kw">color newColor)
  {
  CArrayObj *sub_array;
  CChartObjectEdit *element;
  if((row>=class="num">0 && row<m_rows) && (col>=class="num">0 && col<m_cols))
    {
     if(array2D!=NULL)
      {
      sub_array=array2D.At(col);
      element=(CChartObjectEdit*)sub_array.At(row);
      element.BackColor(newColor);
      class="kw">return true;
      }
    }
  class="kw">return class="kw">false;
  }
class="type">class="kw">color CChartObjectEditTable::GetColor(class="type">int row,class="type">int col)
  {
  CArrayObj *sub_array;
  CChartObjectEdit *element;
  if((row>=class="num">0 && row<m_rows) && (col>=class="num">0 && col<m_cols))
    {
     if(array2D!=NULL)
      {
      sub_array=array2D.At(col);
      element=(CChartObjectEdit*)sub_array.At(row);
      class="kw">return element.BackColor();
      }
    }
  class="kw">return NULL;
  }
class="type">bool CChartObjectEditTable::SetText(class="type">int row,class="type">int col,class="type">class="kw">string newText)
  {
  CArrayObj *sub_array;
  CChartObjectEdit *element;
  if((row>=class="num">0 && row<m_rows) && (col>=class="num">0 && col<m_cols))
    {
     if(array2D!=NULL)
      {
      sub_array=array2D.At(col);
      element=(CChartObjectEdit*)sub_array.At(row);
      element.Description(newText);
      class="kw">return true;
      }
    }
  class="kw">return class="kw">false;
  }
class="type">bool CChartObjectEditTable::Delete(class="type">void)
  {
  for(class="type">int j=class="num">0; j<m_cols; j++)
    {
    CArrayObj *column_array=array2D.At(j);
    column_array.Clear();
    class="kw">delete column_array;
    }
  class="kw">delete array2D;
  class="kw">return true;
  }
table.Create(class="num">0,"t",class="num">0,class="num">1,class="num">10,class="num">10,class="num">10,class="num">15,class="num">15,Yellow);
table.SetColor(class="num">2,class="num">2,Red);
table.SetText(class="num">2,class="num">2,"class="num">2");

「记住这一条就够了」

把扩展控件接进 EA 或指标,核心动作只有一件:在代码里 include 那个 ChartObjectsExtControls.mqh 头文件,控件就能像原生对象一样被调用。 随文给的 zip 里,chartobjectextcontrols.mqh 占 28.87 KB,edittabledemo.mq5、progressbarea.mq5、spinnerdemoea.mq5 三个演示分别约 8.26 KB、7.17 KB、5.49 KB,直接丢进 MT5 的 MQL5 目录就能编译跑通。 派生自 CChartObject 的组件本身不复杂,真正要留意的从来不是写法,而是你打算把它绑到哪类行情逻辑上——外汇和贵金属波动跳点频繁,用这类控件做面板交互时得先想清楚重绘触发频率,否则容易在重大数据窗口卡 UI。

把重复劳动交给小布
这些 GUI 组件的样板代码和属性绑定,小布盯盘的 AIGC 已内置常用模板,打开对应品种页即可调取片段,你专注决策逻辑而非搬砖。

常见问题

MQL5 没有原生动态二维数组,需用指针数组的数组(CChartObject* 的二维数组)配合 ArrayResize 逐级管理,析构时逐元素 Destroy 避免图表对象泄漏。
可以,小布内置了标准库派生控件的片段库,描述进度条绑定指标缓冲的需求即可拿到可编译骨架,但仍需你接好数据来源。
Spinner 要在 CChartObjectEdit 基础上拦截键盘/点击事件做增减与边界 clamp,并维护步长变量,代码量多于纯文本 Edit 但交互更稳。
Create 要传具体的 ENUM_OBJECT 并设默认属性,Type 返回派生类型值,这样标准库容器才能正确识别与序列化对象。