首页 / 交易学院 / 小布知识库 / 用 CChartObject 扩展 MQL5 图形控件:进度条、微调器、表格
用 CChartObject 扩展 MQL5 图形控件:进度条、微调器、表格
🧩

用 CChartObject 扩展 MQL5 图形控件:进度条、微调器、表格

MQL5 编程 难度 · 高阶 2013 13 分钟阅读
CChartObjectGUI 控件MQL5

一、为什么要从裸 ObjectCreate 迁移到 CChartObject

在上一篇用 GUI 做半自动 EA 之后,作者发现更复杂的指标和 EA 还需要进度条、数字微调框、表格这类组件。如果不碰标准库,这些只能靠 ObjectCreate 加一堆 ObjectSet* 字符串属性硬搭,繁琐且极易拼错。MQL5 的 OOP 让我们可以“继承 CChartObjectEdit 写自己的控件”:声明实例、调方法改属性,编辑器还能补全,代码既好记又快。

二、三个组件,都派生自 CChartObjectEdit

本文实施了三个派生类,全部继承自 CChartObjectEdit:CChartObjectProgressBar(进度条)、CChartObjectSpinner(带 +/− 的数字框)、CChartObjectEditTable(二维表格)。它们共用同一套路——在 Create 里先调基类建好 OBJ_EDIT 对象,再叠加自己的子对象与逻辑。理解一个,其余两个都是它的变体。

三、CChartObjectProgressBar:进度条

ProgressBar 的 Create(long chart_id, string name, int window, int X, int Y, int sizeX, int sizeY, int direction=0) 多了一个 direction 参数:当 direction!=0 表示竖向,构造函数会把 Y 改成 Y−sizeY,让条从下往上长。内部条 m_bar 是另一个 CChartObjectEdit,设成白底、不可选、只读。SetValue(int val) 时,横向按 stepSize = sizeX/(max−min) 重算 m_bar 的宽度,竖向则重算高度并调整 Y 锚点。Type() 固定返回 OBJ_EDIT,这样它仍是一个编辑框对象,只是被当成进度条用。

四、CChartObjectSpinner:带加减按钮的数字框

Spinner 在编辑框右侧叠两个 CChartObjectButton:上半高 m_up(文字“+”)、下半高 m_down(文字“−”)。Create 里还接了初值 val、步长 stepSize 和精度 precision(默认 8 位)。Inc()/Dec() 用 NormalizeDouble 按精度比较,越界就停在 [min,max],然后改 Description 显示新值。精度参数很关键——不直接用浮点比较,避免 0.1+0.1+0.1≠0.3 之类的显示抖动。

五、CChartObjectEditTable:二维动态对象数组表格

表格是最复杂的一个:用 CArrayObj 做“列数组里再套行编辑框”的二维动态结构,每个单元格命名为 name+i+":"+j 便于定位。Create(long chart_id, string name, int window, int rows, int cols, int startX, int startY, int sizeX, int sizeY, color Bg, int deltaX, int deltaY) 按行列和间距铺满单元格。SetText/GetText(row,col) 通过 array2D.At(col).At(row) 找到对应编辑框再读写。Delete() 要递归清掉列数组并释放内存,否则留下一堆孤立对象。

六、用 OnChartEvent 让 Spinner 真正动起来

让 Spinner 能响应点击靠 OnChartEvent:当 id==CHARTEVENT_OBJECT_CLICK 时,用 sparam 比对按钮名(如 spinner1_up / spinner1_down),命中就调对应 spinner 的 Inc()/Dec(),再把最新值写进某个标签的 Description 并 ChartRedraw()。这是标准库对象事件最典型的用法,也是把“静态控件”变成“可交互控件”的桥梁。

七、接入与几个坑

八、CChartObject 继承体系

MQL5 所有图表对象都继承自 CChartObject,之上是 CChartObjectTrend(趋势线)、CChartObjectHLine(水平线)等,以及可交互的控件基类。理解这条继承链,你就能自己派生「标准库没有的控件」。

派生时关键是实现虚函数:绘制(OnDraw 或 Refresh)、命中测试(哪个像素点被点中)、事件处理。标准库的控件已经实现好这些,你的子类只需填具体内容。

九、画 ProgressBar 与 Spinner

进度条本质是「按百分比在矩形里填充一段」。继承 CChartObject 后,在绘制里用 Canvas 画底色矩形加前景填充;Spinner(旋转等待)则是按帧旋转一个扇形或文字。两者都靠定时刷新重绘。

class CProgressBar : public CChartObject
{
   int m_percent;
   virtual bool OnDraw()
   {
      CanvasErase();                 // 清画布
      CanvasFillRect(0,0,W,H, clrDimGray);          // 底
      CanvasFillRect(0,0, W*m_percent/100, H, clrLime); // 填充
      return true;
   }
};
✦ 绘制即状态映射
控件绘制 = 把内部状态画到 Canvas。进度条就是「百分比映射到矩形宽度」,逻辑极简,难在坐标与重绘时机。

十、事件交互:OnChartEvent 点击

让控件可点,要在 OnChartEvent 里判断点击坐标是否落在本控件矩形内,命中就触发回调(如按钮逻辑)。图表对象通过 CHARTEVENT_OBJECT_CLICK 或鼠标坐标事件拿到点击,再用控件自己的命中测试分流。

做交互面板时,把「坐标→控件→动作」这一步写清楚,否则点了没反应或点错控件,体验就很差。

十一、小结

✦ 小结
CChartObject 是所有图表对象的基类,派生它可实现标准库没有的控件(进度条、Spinner)。绘制=把状态映射到 Canvas 矩形,交互靠 OnChartEvent 做坐标命中测试再触发回调。逻辑不复杂,难在坐标管理与重绘时机。

常见问题

MQL5 图表本身没有现成进度条、微调器,需要手动组合图形对象。封装成类后复用性更强。
CArrayObj 可以嵌套:每列是一个 CArrayObj,里面再放单元格对象。这样实现行和列的动态增删。
Type() 返回对象类型标识,用于在 OnChartEvent 中识别哪个控件触发了事件,以及做类型检查。
少量控件没问题。如果表格单元格很多,刷新时要注意只更新变化的部分,避免每个 tick 都重绘全部对象。