利用MQL5创建您自己的图形面板·进阶篇
(2/3)· 还在用Print塞日志看状态?标准库自带按钮、列表、复选框,子窗口面板半天就能跑起来
「用向导起一个带对话框的空指标壳」
在 MT5 里做带 UI 的自定义指标,最快的路子不是手敲框架,而是走 MQL5 向导。新建时选 Custom Indicator,连续填三个对话框:第一个给指标命名,第二个务必勾 OnChartEvent 和 OnTimer,第三个勾 Separate Window Indicator(独立窗口),点 Finish 就能拿到一套编译即可用的空模板。 向导生成的代码里,#property indicator_separate_window 已经写好,意味着指标会强制落在图表下方的独立子窗口。OnInit 返回 0、OnCalculate 直接 return(rates_total),此时拖到图上只会看到一个空对话框,没有任何画线或计算逻辑。 别小看这个空壳,它已经自带三个实用特性:子窗口高度在创建期被自动调成对话框高度,对话框宽度始终等于图表宽度,而且该子窗口能被用户最小化和最大化。外汇和贵金属波动剧烈、杠杆高风险大,先在独立窗口把面板跑稳,再往里塞信号才不容易乱。 下面这段是向导直出的骨架,注意 OnTimer 和 OnChartEvent 函数体都是空的,后面要自己补对话框创建与事件转发:
class=class="str">"cmt">//+------------------------------------------------------------------ class=class="str">"cmt">//| PanelIndicator.mq5 | class=class="str">"cmt">//| Copyright class="num">2011, MetaQuotes Software Corp. | class=class="str">"cmt">//| [MQL5官方文档] | class=class="str">"cmt">//+------------------------------------------------------------------ class="macro">#class="kw">property copyright "Copyright class="num">2011, MetaQuotes Software Corp." class="macro">#class="kw">property link "[MQL5官方文档] class="macro">#class="kw">property version "class="num">1.00" class="macro">#class="kw">property indicator_separate_window class=class="str">"cmt">//+------------------------------------------------------------------ class=class="str">"cmt">//| 自定义指标初始化函数 | class=class="str">"cmt">//+------------------------------------------------------------------ class="type">int OnInit() { class=class="str">"cmt">//--- 指标缓冲映射 class=class="str">"cmt">//--- class="kw">return(class="num">0); } class=class="str">"cmt">//+------------------------------------------------------------------ class=class="str">"cmt">//| 自定义指标迭代函数 | class=class="str">"cmt">//+------------------------------------------------------------------ class="type">int OnCalculate(const class="type">int rates_total, const class="type">int prev_calculated, const class="type">int begin, const class="type">class="kw">double &price[]) { class=class="str">"cmt">//--- class=class="str">"cmt">//--- 返回prev_calculated值用于下一次调用 class="kw">return(rates_total); } class=class="str">"cmt">//+------------------------------------------------------------------ class=class="str">"cmt">//| 定时器函数 | class=class="str">"cmt">//+------------------------------------------------------------------ class="type">void OnTimer() { class=class="str">"cmt">//--- } class=class="str">"cmt">//+------------------------------------------------------------------ class=class="str">"cmt">//| ChartEvent函数 | class=class="str">"cmt">//+------------------------------------------------------------------ class="type">void OnChartEvent(const class="type">int id, const class="type">long &lparam, const class="type">class="kw">double &dparam, const class="type">class="kw">string &sparam) { class=class="str">"cmt">//--- }
◍ 用对话框类给指标挂独立窗口面板
想在 MT5 指标里塞一个可交互的控制面板,而不是只画几条线?标准做法是把界面逻辑拆进一个对话框类,指标主体只负责创建和销毁。 下面这段源码把指标声明成独立子窗口(indicator_separate_window),但 plots 和 buffers 都设为 0——它根本不画曲线,只靠 CPanelDialog 撑起一个 130 像素宽的悬浮框。 初始化时先 ExtDialog.Create(0,"Panel Indicator",0,0,0,0,130) 拉出对话框,失败直接返回 -1;接着 Run() 启动消息循环,再 EventSetTimer(1) 挂 1 秒定时器驱动刷新。去初始化里 Destroy() 清面板、EventKillTimer() 撤定时器,避免残留句柄。 外汇与贵金属市场波动剧烈、杠杆风险高,这类面板仅作界面容器,不含任何交易信号;实盘前请在策略测试器里先跑通创建与销毁流程。
class=class="str">"cmt">//+------------------------------------------------------------------ class=class="str">"cmt">//| PanelIndicator.mq5 | class=class="str">"cmt">//| Copyright class="num">2011, MetaQuotes Software Corp. | class=class="str">"cmt">//| [MQL5官方文档] | class=class="str">"cmt">//+------------------------------------------------------------------ class="macro">#class="kw">property copyright "Copyright class="num">2011, MetaQuotes Software Corp." class="macro">#class="kw">property link "[MQL5官方文档] class="macro">#class="kw">property version "class="num">1.00" class="macro">#class="kw">property indicator_separate_window class="macro">#class="kw">property indicator_plots class="num">0 class="macro">#class="kw">property indicator_buffers class="num">0 class="macro">#class="kw">property indicator_minimum class="num">0.0 class="macro">#class="kw">property indicator_maximum class="num">0.0 class=class="str">"cmt">//+------------------------------------------------------------------ class=class="str">"cmt">//| 包含文件 | class=class="str">"cmt">//+------------------------------------------------------------------ class="macro">#include "PanelDialog.mqh" class=class="str">"cmt">//+------------------------------------------------------------------ class=class="str">"cmt">//| 全局变量 | class=class="str">"cmt">//+------------------------------------------------------------------ CPanelDialog ExtDialog; class=class="str">"cmt">//+------------------------------------------------------------------ class=class="str">"cmt">//| 初始化 | class=class="str">"cmt">//+------------------------------------------------------------------ class="type">int OnInit() { class=class="str">"cmt">//--- 创建应用对话框 if(!ExtDialog.Create(class="num">0,"Panel Indicator",class="num">0,class="num">0,class="num">0,class="num">0,class="num">130)) class="kw">return(-class="num">1); class=class="str">"cmt">//--- 开始应用 if(!ExtDialog.Run()) class="kw">return(-class="num">2); class=class="str">"cmt">//--- 创建定时器 EventSetTimer(class="num">1); class=class="str">"cmt">//--- 成功 class="kw">return(class="num">0); } class=class="str">"cmt">//+------------------------------------------------------------------ class=class="str">"cmt">//| 去初始化 | class=class="str">"cmt">//+------------------------------------------------------------------ class="type">int OnDeinit() { class=class="str">"cmt">//--- 销毁对话框 ExtDialog.Destroy(); class=class="str">"cmt">//--- 杀掉定时器 EventKillTimer(); }
指标骨架里的三个事件入口
写 MT5 自定义指标时,OnCalculate 是最先被终端调用的主循环函数。上面这段骨架里它直接 return(rates_total),等于告诉系统「本次处理的 K 线总数就是下次重算的基准」,prev_calculated 形同虚设,适合做纯静态面板类指标,不适合需要增量计算的逻辑。 OnTimer 和 OnChartEvent 在这里都是空壳。前者靠 EventSetTimer 触发,没设定时器时永远不会进;后者把图表交互(点击、拖拽、键盘)转交给 ExtDialog.ChartEvent,说明这个指标挂了一个 GUI 对话框对象,事件不自己处理而是下沉给类成员。 开 MT5 新建指标把这段代码贴进 .mq5,编译后拖到 EURUSD 图表,大概率看不到任何重绘——因为计算函数没写 Buffer 赋值。外汇与贵金属杠杆高,这类半成品只验证结构用,实盘前必须补完数据写入。
class=class="str">"cmt">//+------------------------------------------------------------------ class=class="str">"cmt">//| 迭代 | class=class="str">"cmt">//+------------------------------------------------------------------ class="type">int OnCalculate(const class="type">int rates_total, const class="type">int prev_calculated, const class="type">int begin, const class="type">class="kw">double &price[]) { class=class="str">"cmt">//--- 返回 prev_calculated数值用于下次调用 class="kw">return(rates_total); } class=class="str">"cmt">//+------------------------------------------------------------------ class=class="str">"cmt">//| 定时器事件处理函数 | class=class="str">"cmt">//+------------------------------------------------------------------ class="type">void OnTimer() { class=class="str">"cmt">//--- } class=class="str">"cmt">//+------------------------------------------------------------------ class=class="str">"cmt">//| 图表事件处理函数 | class=class="str">"cmt">//+------------------------------------------------------------------ class="type">void OnChartEvent(const class="type">int id, const class="type">long &lparam, const class="type">class="kw">double &dparam, const class="type">class="kw">string &sparam) { class=class="str">"cmt">//--- 处理事件 ExtDialog.ChartEvent(id,lparam,dparam,sparam); } class=class="str">"cmt">//+------------------------------------------------------------------
「把三通道色阶搬进面板控件」
做面板显示前先想清楚三件事:要呈现什么数据、对话框上放哪些控件、这些控件之间怎么联动。视觉美观虽不影响功能,但能降低使用者的认知负担,顺手把标准库控件include进来就行。 本例用 RGB 三个色阶当教学载体,每个分量范围都是 0–255。红色随每次 OnCalculate 事件随机变,绿色随每次定时器事件随机变,蓝色留给手动微调——这种分工能直观看出不同事件驱动机制的差异。 控件布局上,颜色块用 CPanel 画;红绿数值走只读 CEdit,蓝数值用 CSpinEdit 管理,每个字段前都挂一个 CLabel 写说明文字。下面这段类声明直接定义了全部私有成员,复制进 PanelDialog.mqh 就能编译。 别把随机当信号 红绿通道的随机赋值只是演示事件触发,没有任何市场含义;外汇和贵金属波动高风险,拿这类面板练手时别误读成行情特征。
class=class="str">"cmt">//+------------------------------------------------------------------ class=class="str">"cmt">//| PanelDialog.mqh | class=class="str">"cmt">//| Copyright class="num">2011, MetaQuotes Software Corp. | class=class="str">"cmt">//| [MQL5官方文档] | class=class="str">"cmt">//+------------------------------------------------------------------ class="macro">#include <Controls\Dialog.mqh> class="macro">#include <Controls\Panel.mqh> class="macro">#include <Controls\Edit.mqh> class="macro">#include <Controls\Label.mqh> class="macro">#include <Controls\SpinEdit.mqh> class=class="str">"cmt">//+------------------------------------------------------------------ class=class="str">"cmt">//| CPanelDialog类 | class=class="str">"cmt">//| 函数:主要应用程序对话框 | class=class="str">"cmt">//+------------------------------------------------------------------ class CPanelDialog : class="kw">public CAppDialog { class="kw">private: class=class="str">"cmt">//--- 附加控件 CPanel m_color; class=class="str">"cmt">// 显示颜色对象 CLabel m_label_red; class=class="str">"cmt">// "红" 级别标题对象 CEdit m_field_red; class=class="str">"cmt">// "红" 数值显示对象 CLabel m_label_green; class=class="str">"cmt">// "绿" 级别标题对象 CEdit m_field_green; class=class="str">"cmt">// "绿"数值显示对象 CLabel m_label_blue; class=class="str">"cmt">// "蓝" 级别标题对象 CSpinEdit m_edit_blue; class=class="str">"cmt">// "蓝" 数值控件 class=class="str">"cmt">//--- 参数值 class="type">int m_red; class=class="str">"cmt">// "红" 值 class="type">int m_green; class=class="str">"cmt">// "绿" 值 class="type">int m_blue; class=class="str">"cmt">// "蓝" 值 class="kw">public: CPanelDialog(class="type">void);
◍ 面板对话框的空构造与析构
在 MT5 自定义面板开发中,CPanelDialog 作为控制面板的基类,其构造与析构函数常被留空以延迟资源绑定。 下面这段继承自 CAppDialog 的简化定义,给出了最薄骨架:声明虚析构保证派生类释放安全,构造函数不带参数。 实际项目里,你大概率会在 OnCreate 或后续方法里才挂按钮、标签;直接放构造里反而难调试。开 MT5 新建 EA 面板时,先抄这个空壳编译通过,再逐步塞控件最稳。
~CPanelDialog(class="type">void); }; class=class="str">"cmt">//+------------------------------------------------------------------ class=class="str">"cmt">//| Constructor | class=class="str">"cmt">//+------------------------------------------------------------------ CPanelDialog::CPanelDialog(class="type">void) { } class=class="str">"cmt">//+------------------------------------------------------------------ class=class="str">"cmt">//| Destructor | class=class="str">"cmt">//+------------------------------------------------------------------ CPanelDialog::~CPanelDialog(class="type">void) { } class=class="str">"cmt">//+------------------------------------------------------------------
控件联动先定死一条规矩
做 MT5 自定义对话框时,先把交互底线卡住:红、绿、蓝三个色阶参数里任意一个被改,对话框都必须把这次变动实时反映出来,不能只刷某一个控件。 这条原则看着像废话,但落到 MQL5 面板开发里,很多人会把 OnEvent 只绑到某个 Edit 或 ColorButton 上,结果另两个色阶改了界面不跟手。 具体怎么在 CAppDialog 派生类里统一派发变更事件,留到真正建对话框那一步再拆,现在只要记住这条“全参数可见”的约束就行。