用 CChartObject 扩展 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()。这是标准库对象事件最典型的用法,也是把“静态控件”变成“可交互控件”的桥梁。
七、接入与几个坑
- 这些组件不是凭空能用——EA 或指标里必须 #include <ChartObjectsExtControls.mqh>
- 继承自编辑框的控件记得设 ReadOnly(true) 和 Selectable(false),否则用户能乱改或选中背景
- 坐标一律用 X_Distance/Y_Distance 的像素距离定位,而不是价格/时间锚点,这样控件不会随行情滚动
- 进度条/表格内部子对象多,销毁时必须统一清理,否则重复创建会越积越多
八、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;
}
};
十、事件交互:OnChartEvent 点击
让控件可点,要在 OnChartEvent 里判断点击坐标是否落在本控件矩形内,命中就触发回调(如按钮逻辑)。图表对象通过 CHARTEVENT_OBJECT_CLICK 或鼠标坐标事件拿到点击,再用控件自己的命中测试分流。
做交互面板时,把「坐标→控件→动作」这一步写清楚,否则点了没反应或点错控件,体验就很差。