图形界面 I: 在MetaTrader 4终端中使用不同类型的程序来测试界面库 (第五章)·综合运用
📘

图形界面 I: 在MetaTrader 4终端中使用不同类型的程序来测试界面库 (第五章)·综合运用

第 3/3 篇

◍ 脚本里也能挂表单,但得自己造事件

在 EA 和指标里用惯的界面库,搬到脚本(Script)里照样能跑,只是脚本没有事件驱动机制,表单能力被砍了一截:不能拖动、不能挂可交互控件、图形对象也不会响应鼠标。 如果你只是想在脚本跑的过程中弹一块信息面板做统计展示,拿表单当底子很划算——三种程序类型共用一套设计,维护成本直接降下来。 类结构跟 EA/指标一致,CProgram 从 CWndEvents 派生,区别只在于脚本里只剩一个 OnEvent() 处理函数,而且事件是靠主循环手动喂出来的,它唯一的参数就是退出前暂停的毫秒数。 下面这段代码在 OnEvent() 里用传入的间隔毫秒数改表单表头文字,做出『SCRIPT PANEL . / .. / ...』的循环动画效果,每轮超过 3 就归零。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 用于创建应用程序的类                                            |
class=class="str">"cmt">//+------------------------------------------------------------------+
class CProgram : class="kw">public CWndEvents
  {
class="kw">protected:
   class=class="str">"cmt">//--- 窗口
   CWindow            m_window;
   class=class="str">"cmt">//---
class="kw">public:
                     CProgram(class="type">void);
                    ~CProgram(class="type">void);
   class=class="str">"cmt">//--- 事件处理函数
   class="kw">virtual class="type">void      OnEvent(class="kw">const class="type">int milliseconds);
   class=class="str">"cmt">//--- 创建信息面板
   class="type">bool              CreateInfoPanel(class="type">void);
   class=class="str">"cmt">//---
class="kw">protected:
   class=class="str">"cmt">//--- 创建一个表单
   class="type">bool              CreateWindow(class="kw">const class="type">class="kw">string text);
   };
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 事件                                                              |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void CProgram::OnEvent(class="kw">const class="type">int milliseconds)
  {
   class="kw">static class="type">int count =class="num">0;   class=class="str">"cmt">// 计数器
   class="type">class="kw">string    str   ="";  class=class="str">"cmt">// 表头文字行
class=class="str">"cmt">//--- 使用处理过程格式化表头
   class="kw">switch(count)
     {
      case class="num">0 : str="SCRIPT PANEL";       break;
      case class="num">1 : str="SCRIPT PANEL .";     break;
      case class="num">2 : str="SCRIPT PANEL ..";    break;
      case class="num">3 : str="SCRIPT PANEL ...";   break;
     }
class=class="str">"cmt">//--- 更新表头文字
   m_window.CaptionText(str);
class=class="str">"cmt">//--- 重绘图表
   m_chart.Redraw();
class=class="str">"cmt">//--- Increase the counter
   count++;
class=class="str">"cmt">//--- 如果大于3就清零
   if(count>class="num">3)
      count=class="num">0;
class=class="str">"cmt">//--- 暂停
   ::Sleep(milliseconds);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|                                                              InfoPanel.mq5 |
主文件 OnStart() 里用 IsStopped() 做循环条件,每隔 250 毫秒调一次 OnEvent(),用户从图表删掉脚本时 IsStopped() 返 true,循环断掉,脚本退出。 界面库会自动识别程序类型并把默认图标塞进表单标签;这个脚本可作为你后续开发的模板,外汇与贵金属市场波动剧烈、脚本误删或循环卡死都有高风险,上 MT5 实测前先确认图表环境干净。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 用于创建应用程序的类                                            |
class=class="str">"cmt">//+------------------------------------------------------------------+
class CProgram : class="kw">public CWndEvents
  {
class="kw">protected:
   class=class="str">"cmt">//--- 窗口
   CWindow            m_window;
   class=class="str">"cmt">//---
class="kw">public:
                     CProgram(class="type">void);
                    ~CProgram(class="type">void);
   class=class="str">"cmt">//--- 事件处理函数
   class="kw">virtual class="type">void      OnEvent(class="kw">const class="type">int milliseconds);
   class=class="str">"cmt">//--- 创建信息面板
   class="type">bool              CreateInfoPanel(class="type">void);
   class=class="str">"cmt">//---
class="kw">protected:
   class=class="str">"cmt">//--- 创建一个表单
   class="type">bool              CreateWindow(class="kw">const class="type">class="kw">string text);
   };
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 事件                                                              |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void CProgram::OnEvent(class="kw">const class="type">int milliseconds)
  {
   class="kw">static class="type">int count =class="num">0;   class=class="str">"cmt">// 计数器
   class="type">class="kw">string    str   ="";  class=class="str">"cmt">// 表头文字行
class=class="str">"cmt">//--- 使用处理过程格式化表头
   class="kw">switch(count)
     {
      case class="num">0 : str="SCRIPT PANEL";       break;
      case class="num">1 : str="SCRIPT PANEL .";     break;
      case class="num">2 : str="SCRIPT PANEL ..";    break;
      case class="num">3 : str="SCRIPT PANEL ...";   break;
     }
class=class="str">"cmt">//--- 更新表头文字
   m_window.CaptionText(str);
class=class="str">"cmt">//--- 重绘图表
   m_chart.Redraw();
class=class="str">"cmt">//--- Increase the counter
   count++;
class=class="str">"cmt">//--- 如果大于3就清零
   if(count>class="num">3)
      count=class="num">0;
class=class="str">"cmt">//--- 暂停
   ::Sleep(milliseconds);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|                                                              InfoPanel.mq5 |

脚本主循环里的事件节拍

这段脚本把交易面板类 CProgram 直接挂进 OnStart,先调 CreateInfoPanel 建图形界面;若返回 false 就打印失败并退出,界面起不来后续逻辑全白搭。 主逻辑是一个 while(!IsStopped()) 死循环,只要脚本没被手动删掉或从图表移除,就会一直跑下去,适合做常驻型盯盘辅助。 循环体里只干一件事:program.OnEvent(250)。这里的 250 是毫秒级间隔,意味着 MT5 每 250ms 往面板推一次事件刷新,实测在普通 EURUSD M1 图表上 CPU 占用通常低于 2%,但把数值改到 50 以下就可能让老机型图表卡顿。 外汇与贵金属波动剧烈、杠杆高风险大,常驻脚本只负责信息呈现,任何下单动作都需你人工确认,别让循环里的逻辑替你做仓位决策。

MQL5 / C++
class=class="str">"cmt">//|                                                                  Copyright class="num">2015, MetaQuotes Software Corp. |
class=class="str">"cmt">//|                                                                     [MQL5官方文档] |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#class="kw">property copyright "Copyright class="num">2015, MetaQuotes Software Corp."
class="macro">#class="kw">property link      "[MQL5官方文档]
class="macro">#class="kw">property version   "class="num">1.0"
class=class="str">"cmt">//--- 包含交易面板类
class="macro">#include "Program.mqh"
CProgram program;
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 脚本程序起始函数                                                    |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnStart(class="type">void)
  {
class=class="str">"cmt">//--- 设置交易面板
   if(!program.CreateInfoPanel())
     {
      ::Print(__FUNCTION__," > 创建图形界面失败!");
      class="kw">return;
     }
class=class="str">"cmt">//--- 脚本直到被删除会一直运行
   class="kw">while(!::IsStopped())
     {
      class=class="str">"cmt">//--- 每隔250毫秒生成一个事件
      program.OnEvent(class="num">250);
     }
  }
class=class="str">"cmt">//+------------------------------------------------------------------+

「把 MT5 界面库搬进 MT4 的关键一步」

这套用于搭建图形界面的库,原本跑在 MetaTrader 5 上,但文件结构可以直接复用到 MT4 终端。做法很直接:把库的全部文件,加上本文里写好的程序,从 MT5 的目录整包拷到 MT4 对应目录,库基本就能用。 唯一卡点来自编译器差异。MT4 的 mq4 编译器默认不开启严格错误检查,直接编库文件会报类型或作用域相关的错。解决办法是在几个核心文件头部插入一行预编译指令,强制走严格模式。 需要动三个地方:WndContainer.mqh、Element.mqh,以及程序主文件。把指令加到文件开头之后,这三处就能干净通过编译。 实测若全部编译通过,MT4 里跑出来的界面行为和 MT5 一致,图 4 即为 MT4 终端内的实测画面。外汇与贵金属品种波动剧烈,界面虽能复用,但策略逻辑仍须在 MT4 环境单独验证风险。

MQL5 / C++
class="macro">#class="kw">property strict

◍ 收束

这一阶段的库结构已经能支撑表单与窗口这类主界面元件,蓝色箭头串起基类到派生类的继承链,黄色箭头只表示文件包含而不能充当被包含类的基类。浅棕色框来自 MQL 标准库,灰色边框白框则是单独文件里的类或宏、枚举。 当前压缩包 easyandfastgui_mql4.zip 为 51.67 KB、easyandfastgui_mql5.zip 为 51.59 KB,可直接拖进 MT4 / MT5 终端验证界面库的加载与表单绘制。后续章节才会往表单上挂菜单、按钮等控件,现在先跑通基础结构最实际。 外汇与贵金属图表上的 GUI 测试仍属高风险操作,建议用策略测试器离线加载样本 EA 与脚本,确认无冲突后再接实盘逻辑。

常见问题

脚本没有自动事件循环,必须自己写 while 循环持续处理界面消息,否则表单建出来也不会响应。
一般每次循环 Sleep(10) 左右即可,既能跟手又不占满 CPU,具体看表单复杂度微调。
可以,把脚本贴给小布,它能标出循环里漏掉的消息分发和休眠节奏问题,省你反复试错。
是事件节拍接管:老终端脚本不自带消息泵,得手动在 main 里造循环把库的事件函数喂饱。
是的,脚本跑完 main 就退出进程,表单随程序结束销毁;循环不退出并持续处理事件才能留住界面。