MQL5语言帮助文件人话版(十九)数据类型之在用户界面安排事件模式

| 发表于 4 小时前 | 显示全部楼层 |复制链接
最后由 麻爪 于 2026-9-16 15:00 编辑

第十九章 在用户界面安排事件模式

原版手册都当读者融汇贯通的,在语言基础阶段动不动就丢出一些高级用法,新手看着云遮雾绕。我们需要先梳理一下各种概念,理解它说的到底是什么。因为牵扯到许多其他知识点还没触及,这一章我们只求理解,而不是真要实现这个操作。

一、什么是“在用户界面安排事件模式”?
1.用户界面:一般指的是GUI 界面,Graphical User Interface,图形用户界面。MT5 软件本身展示出来的整套窗口、菜单、行情窗口、K 线图,全部属于 GUI(图形用户界面),带图形、按钮、文字、复选框、输入框这种看得见、能用鼠标点的可视化界面。
2.事件模式:新手需要分清,GUI事件和我们之前写的“MQL5程序是事件驱动程序”里面的事件是两回事。OnTick、OnChartEvent、OnTimer、OnTrade这些函数所带表的事件是MT5原生事件,是所有MQL5程序运作的驱动源头,属于底层事件。 而GUI事件,就是用户在图表上手动操作的各种事件,比如点击、拖动某个按钮或者图像之类的,属于上层事件。
我们写的EA一旦打开,代码就会自动运行,遇到各种底层事件就会自动驱动相应的代码运转。但在事件模式下,各种界面控件,也就是按钮、图形那些东西,都只是展现在用户面前,你不点它,它就不动,你点了相应的按钮,系统就会执行该按钮所对应的代码。
3.“OnChartEvent”图表事件函数
各种底层事件之中,有一个“OnChartEvent”图表事件函数,它定义了一些所谓的“图表事件“:
(1) CHARTEVENT_OBJECT_CLICK:点击图表上的图形对象(OBJ_BUTTON 按钮、文字标签、矩形等)。
提示:手册中举例的CButton 板块按钮点击,底层触发的就是这个事件。`sparam` 里面存放被点击对象的名称。
(2)HARTEVENT_OBJECT_DRAG:拖动图表上的图形控件,比如把画的矩形、自定义面板拖动移位。
(3)CHARTEVENT_OBJECT_CHANGE:修改控件里面的值。例如:输入框输入文字、复选框勾选 / 取消、下拉列表切换选项。
(4)CHARTEVENT_OBJECT_CREATE:在图表上新创建图形对象时触发(很少用)。
(5)CHARTEVENT_OBJECT_DELETE:图形对象被删除的时候触发。
(6)CHARTEVENT_MOUSE_MOVE:鼠标在图表 K 线区域移动,持续触发。频繁触发,尽量少写重运算代码。
(7)CHARTEVENT_MOUSE_WHEEL:鼠标滚轮滚动(放大缩小 K 线,或者自定义滚轮逻辑)。
(8)CHARTEVENT_CHART_CHANGE:图表发生变化:切换周期、切换品种、缩放 K 线、调整图表窗口大小。
(9)CHARTEVENT_CLOSE:图表窗口被关闭。
(10)CHARTEVENT_KEYDOWN:在图表窗口激活状态下,按下键盘按键。
“OnChartEvent”图表事件函数的语法如下:
  1. void OnChartEvent(const int id,
  2.                   const long &lparam,
  3.                   const double &dparam,
  4.                   const string &sparam)
复制代码
void 代表这是一个无返回值的函数,它不做算数,而是响应各种动作;const是只读修饰符号,代表它拿到这四个参数后不可修改;id是前面说的”图表事件“的编号,但是通常我们不会输入数字,而是输入图标事件那一长串名字;后面三个是附带事件信息,&是引用符号。
关于这个函数,我们大概了解一下它长什么样子,因为本章讲的所谓“在用户界面安排事件模式”本质上就是基于这个函数实现的。
4.“OnChartEvent”图表事件函数按钮举例
  1. //写一个按钮事件处理函数,本例不用`lparam`、`dparam`两个参数,只用sparam图形控件参数
  2. void OnChartEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
  3. {
  4.    if(id == CHARTEVENT_OBJECT_CLICK)   //如果发生了点击图表图形对象的事件
  5.    {
  6.       if(sparam == "btn_buy")  //如果点击了做多按钮btn_buy
  7.       {
  8.          //做多逻辑
  9.       }
  10.       else if(sparam == "btn_sell") //如果点击了做空按钮btn_sell
  11.       {
  12.          //做空逻辑
  13.       }
  14.       else if(sparam == "btn_close")//如果点击了平仓按钮btn_close
  15.       {
  16.          //平仓逻辑
  17.       }
  18.       //每新增一个按钮,就要在这里新增一条else if
  19.    }
  20. }
复制代码
很明显,在OnChartEvent的大括号中,有一个点击动作,我们就要写一个二层的大括号,这个点击动作有一个按钮我们就要写一个三层的大括号,如果有点击、拖拽、停留、放大缩小等等很多动作,每个动作里面又有许多按钮或者操作对象,你可以想想这个函数得多复杂了?得套多少层括号?

二、CButton 按钮类标准库
要理解原手册上那一大堆”CButton版块的示例“代码,我们先要知道CButton是什么?如果点开手册上CButton链接,会看到它的标准库引用路径:
#include <Controls\Button.mqh>
#include是预处理命令,表示引用头文件,相当于把它后面路径里文件中的代码直接复制到当前位置。我们在MT5的安装目录中,可以找到Button.mqh文件,如下图所示。通过这个例子,新手可以很直截了当的理解所谓引用标准库/头文件是怎么回事,说白了就是MT5替你写好了一堆代码,安装MT5的时候就一并送给你了,你直接引用就好,复制都不需要。
ScreenShot_2026-09-16_140747_867.png
include文件夹中都是各种库的头文件,Controls文件夹是一套”对话框面板控件库“,专门用来在 MT5 图表上创建一整块窗口式 GUI 面板(类似软件里弹出的小窗口),而Button.mqh 就是专门存放面板 CButton 按钮类控件的头文件。手册 CButton 案例就是基于这个库。
CButton,C代表着它是一个“类”,Button就是按钮的意思,由此也可以学习MQL5语言标识符命名的一些规律。它虽然不是关键字,但不能改成其他写法,因为官方库里面存在各种引用,如果你改了写法,那些官方库就不认识它了。MT5 平台本身就有画按钮图形的底层能力(按钮图表对象),CButton 是在这个底层能力之上,包装出来的一套面向对象的类,方便我们在对话框面板里快速创建、管理按钮。它能实现基础功能,比如显示文字、接收鼠标点击等,但不会自带复杂高级功能(比如自定义渐变图片、复杂动画),就是面板上最基础的点击按钮。
另外,在Include//ChartObjects文件夹下还有一个ChartObjectsControls.mqh头文件,代表的是另一种CButton 按钮:独立图表按钮,也就是那种单个的,悬浮在别的窗口之上的单个按钮,功能单一,不太常用。

三、使用CButton板块实现三个按钮的代码
前面的例子显示了OnChartEvent”图表事件函数是如何和按钮互动的,在原手册中举了一个例子,如何实现三个按钮的例子,我这里也粘贴一段如何实现我前面例子中三个按钮的代码。
需要说明的是,这段代码我就不解释了,因为有许多概念还没学到,强行理解徒增负担。学代码就像学外语,学的越多生词越多,初期先做大概了解,不必深究。而前面的讲解已经足以让新手理解手册上各种概念是怎么回事了,至于怎么用,等学个七七八八的时候,再回头看吧。
  1. // 引入Controls面板库头文件
  2. #include <Controls\CAppDialog.mqh>
  3. #include <Controls\Button.mqh>
  4. // 【手册同款】定义函数指针类型 TAction
  5. // 返回int,参数:string(按钮名称),int(按钮ID)
  6. typedef int(*TAction)(string,int);
  7. // 自定义按钮子类,继承面板CButton
  8. class MyButton : public CButton
  9. {
  10. public:
  11.    TAction m_clickAction; // 函数指针,保存点击后要执行的函数
  12.    // 重写OnEvent,捕获点击事件
  13.    virtual bool OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam) override
  14.    {
  15.       // 判断事件是否属于当前按钮
  16.       if(lparam == Id())
  17.       {
  18.          if(m_clickAction != NULL)
  19.          {
  20.              // 调用绑定的回调函数,传入按钮文字、按钮ID
  21.              m_clickAction(Text(),Id());
  22.          }
  23.          return true; // 事件已处理
  24.       }
  25.       return CButton::OnEvent(id,lparam,dparam,sparam);
  26.    }
  27. };
  28. // 自定义对话框面板类,继承CAppDialog
  29. class CTradePanel : public CAppDialog
  30. {
  31. public:
  32.    MyButton m_btnBuy;    // 买入按钮
  33.    MyButton m_btnSell;   // 卖出按钮
  34.    MyButton m_btnClose;  // 平仓按钮
  35.    // 创建面板与所有控件
  36.    virtual bool Create(const long chart_id,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2) override
  37.    {
  38.       if(!CAppDialog::Create(chart_id,name,subwin,x1,y1,x2,y2))
  39.          return false;
  40.       // 创建买入按钮
  41.       m_btnBuy.Create(chart_id,"btnBuy",subwin,10,30,100,60);
  42.       m_btnBuy.Text("买入");
  43.       m_btnBuy.m_clickAction = OnBtnClick; //绑定回调函数
  44.       // 创建卖出按钮
  45.       m_btnSell.Create(chart_id,"btnSell",subwin,110,30,200,60);
  46.       m_btnSell.Text("卖出");
  47.       m_btnSell.m_clickAction = OnBtnClick;
  48.       // 创建平仓按钮
  49.       m_btnClose.Create(chart_id,"btnClose",subwin,210,30,300,60);
  50.       m_btnClose.Text("平仓");
  51.       m_btnClose.m_clickAction = OnBtnClick;
  52.       // 把按钮添加进面板统一管理
  53.       Add(&m_btnBuy);
  54.       Add(&m_btnSell);
  55.       Add(&m_btnClose);
  56.       return true;
  57.    }
  58. };
  59. // 回调函数,签名必须匹配 TAction:int 函数名(string,int)
  60. int OnBtnClick(string btnName, int btnId)
  61. {
  62.    Print("按钮被点击|按钮文字:",btnName,",按钮ID:",btnId);
  63.    if(btnName=="买入")
  64.    {
  65.       Print("执行买入逻辑");
  66.    }
  67.    else if(btnName=="卖出")
  68.    {
  69.       Print("执行卖出逻辑");
  70.    }
  71.    else if(btnName=="平仓")
  72.    {
  73.       Print("执行平仓逻辑");
  74.    }
  75.    return 1; // 返回int,匹配TAction定义
  76. }
  77. CTradePanel g_panel; //全局面板实例
  78. //EA初始化
  79. int OnInit()
  80. {
  81.    // 创建面板,坐标(10,10)到(320,100)
  82.    if(!g_panel.Create(_Symbol,"TradePanel",0,10,10,320,100))
  83.    {
  84.       Print("面板创建失败");
  85.       return INIT_FAILED;
  86.    }
  87.    g_panel.Show(); //显示面板
  88.    return INIT_SUCCEEDED;
  89. }
  90. //全局图表事件转发,Controls面板必需
  91. void OnChartEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
  92. {
  93.    g_panel.ChartEvent(id,lparam,dparam,sparam);
  94. }
  95. //EA反初始化
  96. void OnDeinit(const int reason)
  97. {
  98.    g_panel.Destroy();
  99. }
  100. //+------------------------------------------------------------------+
复制代码
最近访问 头像模式
举报

评论 使用道具

发新帖
EA交易
您需要登录后才可以评论 登录 | 立即注册

麻爪

麻爪 D

如果你想让一个人下地狱,介绍他做交易。
如果你想让一个人在地狱里永不超生,介绍他做EA交易。

Fast跟单EA