在MQL5中创建交互式图形用户界面(第1部分):制作面板(基础篇)
📘

在MQL5中创建交互式图形用户界面(第1部分):制作面板(基础篇)

第 1/3 篇

用 MQL5 在 MT5 里搭一块可交互面板

想在 MT5 上做自己的交易面板,第一步不是画按钮,而是先理解 MQL5 的 GUI 载体:它不靠系统窗体,而是用 OBJ_RECTANGLE_LABEL、OBJ_BUTTON 这类图形对象直接贴在图表上。2025 年 1 月发布的这套思路,已经被 1932 次阅读验证过基础可行性,说明手动堆对象这条路在实盘图表里走得通。 核心动作是创建一个矩形标签当底板,再往上面叠按钮和文本。下面这段代码演示了最基础的面板底板生成,复制到 MT5 的 EA 或指标 OnInit 里就能看到一块 300×200 的灰色区域浮在图表左上角。外汇与贵金属品种波动剧烈,这类悬浮 UI 不影响 tick 接收,但遮挡 K 线区域时可能干扰你的价格行为判断,建议靠边放。 真正的交互靠的是 ChartEvent 回调:鼠标点击按钮会触发 CHARTEVENT_OBJECT_CLICK,你在 OnChartEvent 里用 sparam 拿到对象名,再分流处理逻辑。这一步不做,面板就只是块死图。

MQL5 / C++
class="type">class="kw">string panelName = "MyPanel";
class="type">int OnInit()
{
   ObjectCreate(class="num">0, panelName, OBJ_RECTANGLE_LABEL, class="num">0, class="num">0, class="num">0);
   ObjectSetInteger(class="num">0, panelName, OBJPROP_XDISTANCE, class="num">10);
   ObjectSetInteger(class="num">0, panelName, OBJPROP_YDISTANCE, class="num">10);
   ObjectSetInteger(class="num">0, panelName, OBJPROP_XSIZE, class="num">300);
   ObjectSetInteger(class="num">0, panelName, OBJPROP_YSIZE, class="num">200);
   ObjectSetInteger(class="num">0, panelName, OBJPROP_BGCOLOR, C&class="macro">#x27;class="num">230,class="num">230,class="num">230&class="macro">#x27;);
   ObjectSetInteger(class="num">0, panelName, OBJPROP_BACK, true);
   class="kw">return(INIT_SUCCEEDED);
}

「从空项目到第一块面板」

在 MT5 里写 GUI 面板,第一步不是画控件,而是把 MQL5 工程结构搭对。MetaEditor 是唯一的官方 IDE,脚本、指标、EA 三个类型的工程都能挂界面,但面板逻辑通常跑在 EA 或指标里,纯脚本无法常驻接收事件。 布局设计先于代码。先在纸上或脑内确定面板锚点(左上/右下)、控件行列与预留间距,再进 MQL5 用 CWndContainer 等基类去堆。新手常犯的错是一上来就塞 Button,结果坐标乱跳、重绘卡顿。 本篇只做静态骨架:建工程、放窗口容器、加几个基础控件(标签、按钮、编辑框)。下一篇才接消息映射让它们动起来。MT5 终端版本需为当前官方版,旧 build 可能缺 CAppDialog 新成员函数,编译直接报错。 验证方式很简单:新建一个 EA 工程,包含 #include <Controls/Dialog.mqh>,能编译出空面板挂到图表上,就算过了第一关。外汇与贵金属交易本身高风险,界面只是工具,不代表任何收益可能。

◍ 面板该长什么样

这一轮要做的 GUI 面板,目标是把交易者日常最常用的操作塞进一个可拖动的窗口里,而不是散落在多个脚本。我们计划用 4 类基础元素搭出整套界面:交易按钮、锐利矩形框、实时刷新模块和带悬停反馈的可移动区块。 面板内会直接放一键交易按钮,配合表情符号和多种字体样式做状态区分;标签用来标价格与持仓,矩形框负责切分功能区域。MT5 上这类面板若不做悬停效果,鼠标移上去毫无反馈,实战里很容易点错。 下面给一个雏形示例:左侧交易按钮区、右上实时标签、底部可拖动条,整体用矩形框包边。你开 MT5 新建一个空 EA,先把这 4 类元素占位画出来,就能知道后续每篇要填哪块。

用四个函数搭出MT5面板骨架

在 MT5 里做 GUI 面板,最省事的办法是先写四个可复用的创建函数:矩形标签、按钮、编辑字段、文本标签。函数化之后,同类对象随时调,不用每次把 ObjectCreate 到 ObjectSetInteger 整套重写一遍。 矩形标签函数 createRecLabel 吃 10 个参数,返回 true/false 表示成败。关键参数里,xD/yD 是相对图表角落的像素偏移,xS/yS 是宽高,clrBg 管背景色,widthBorder 为 0 即无边框,borderType 用 BORDER_FLAT、borderStyle 用 STYLE_SOLID 最干净。函数签名里带默认值的参数(如默认 clrNONE),调用时省略就自动补上。 函数体内先用 ResetLastError 清掉旧错误码,再跑 ObjectCreate(chart_id=0, objName, OBJ_RECTANGLE_LABEL, 0, 0, 0)——时间和价格坐标都填 0,意思是锚定窗口像素而不是图表价格。失败就 Print 错误并 return false;成功则用 ObjectSetInteger 逐个写 OBJPROP_XDISTANCE / YDISTANCE / XSIZE / YSIZE / CORNER 等。CORNER 只能取四个角之一,比如 CORNER_LEFT_UPPER 就是左上角起算。最后 ChartRedraw 强制重绘,不用等报价。 按钮用 OBJ_BUTTON,比矩形多 text、文字色、字体大小、字体名这几个参数,边框只留颜色;编辑框用 OBJ_EDIT,额外加水平对齐和 OBJPROP_READONLY=false 才能输入;文本标签用 OBJ_LABEL,连背景和边框参数都省了。 落到实建:用 #define MAIN_REC "MAIN_REC" 存主框名,在 OnInit 里调 createRecLabel(MAIN_REC,10,30,250,400,clrWhite,1,clrBlack,...) 得到 250×400 的白底黑边主面板(左上角偏移 10,30 像素)。子框再减 10 像素边距变成 240×390,背景改 RGB(245,245,245) 接近白,边框设 clrNONE 融合更好。标题用 OBJ_LABEL 放 "DashBoard v1.0",字体 Cooper Black、大小 14、色蓝;换 Wingdings/Webdings 字体后,字符 'Y' 直接渲染成心形图标,大小写对应不同 glyph。 别把正态当圣经 像素距离靠肉眼估容易歪。把图表缩到缩放级别 0,十字光标两列之间的 K 线数约等于水平像素数,先用这招标定 10/250 这类值,再编译看效果,比盲填参数稳。

「把交易量按钮和下拉箭头做出来」

面板头部区域搭好之后,下一步是按钮工具。先啃交易量按钮:它由标签加一个下拉箭头组成,宏定义写完就调按钮函数生成实体。 按钮坐标用 x/y 传入,宽 130 像素、高 25 像素。文本故意留空(传 null),因为我们要自己控制文字位置,不靠系统默认标签。背景和边框色用文字颜色记号来设,随后另建一个文本标签贴着按钮放,函数你前面已经熟了。 下拉箭头用的字体是能出图标的类型(比如 Webdings)。文本参数里把字符转成 string,这样若你已知符号代码就能直接喂代码而非手写字符串,编辑字段按钮也走同一条路。 编辑按钮宽 60 高 25,默认文本 0.01、黑色字、Calibri 14 号,背景白、边框近黑。交易量加减按钮用 Webdings,文本值分别设 "5" 和 "6"。编译后面板出现第一排可交互控件。 止损止盈按钮逻辑完全复用,只改了显示内容;买/卖/价差按钮调用自定义 Bid()、Ask()、Spread() 填值。bid/ask 返回 double,需用 DoubleToString 转字符串,spread 是整型直接转。EA 卸载时要清场,写个 void 函数调 ObjectDelete(0, name) 在 OnDeinit 里删对象,避免图表留垃圾。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Function to create rectangle label |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool createRecLabel(class="type">class="kw">string objName, class="type">int xD, class="type">int yD, class="type">int xS, class="type">int yS,
 class="type">class="kw">color clrBg, class="type">int widthBorder, class="type">class="kw">color clrBorder = clrNONE,
 ENUM_BORDER_TYPE borderType = BORDER_FLAT, ENUM_LINE_STYLE borderStyle = STYLE_SOLID) {
...
}
 class=class="str">"cmt">// Create a rectangle label object
 if (!ObjectCreate(class="num">0, objName, OBJ_RECTANGLE_LABEL, class="num">0, class="num">0, class="num">0)) {
 Print(__FUNCTION__, ": failed to create rec label! Error code = ", _LastError);
 class="kw">return (false); class=class="str">"cmt">// Return false if object creation fails
 }
 ResetLastError(); class=class="str">"cmt">// Reset any previous error codes
 class=class="str">"cmt">// Set properties for the rectangle label
 ObjectSetInteger(class="num">0, objName, OBJPROP_XDISTANCE, xD); class=class="str">"cmt">// X distance from the corner
 ObjectSetInteger(class="num">0, objName, OBJPROP_YDISTANCE, yD); class=class="str">"cmt">// Y distance from the corner
 ObjectSetInteger(class="num">0, objName, OBJPROP_XSIZE, xS); class=class="str">"cmt">// Width of the rectangle
 ObjectSetInteger(class="num">0, objName, OBJPROP_YSIZE, yS); class=class="str">"cmt">// Height of the rectangle
 ObjectSetInteger(class="num">0, objName, OBJPROP_CORNER, CORNER_LEFT_UPPER); class=class="str">"cmt">// Positioning corner
 ObjectSetInteger(class="num">0, objName, OBJPROP_BGCOLOR, clrBg); class=class="str">"cmt">// Rectangle background class="type">class="kw">color
 ObjectSetInteger(class="num">0, objName, OBJPROP_BORDER_TYPE, borderType); class=class="str">"cmt">// Border type
 ObjectSetInteger(class="num">0, objName, OBJPROP_STYLE, borderStyle); class=class="str">"cmt">// Border style(only if borderType is flat)
 ObjectSetInteger(class="num">0, objName, OBJPROP_WIDTH, widthBorder); class=class="str">"cmt">// Border width(only if borderType is flat)

◍ 用矩形标签搭可定位的盘面浮层

在 MT5 里做价格行为标注,常需要把提示框钉在图表固定角落而不是跟着 K 线漂。OBJ_RECTANGLE_LABEL 就是干这个的:它不绑定时间轴,靠 CORNER_LEFT_UPPER 加 XDISTANCE / YDISTANCE 决定屏幕坐标,xD、yD 以像素计,和图表缩放无关。 上面这段先把对象设为非背景、不可选中、未选中状态,再把边框色 clrBorder 通过 OBJPROP_COLOR 写进去——注意注释里写明这只在 borderType 为 BORDER_FLAT 时生效,若你传了 BORDER_NONE 这行就是空操作。最后 ChartRedraw(0) 强制重绘,函数返回 true 代表属性全部落定。 createRecLabel 把创建和赋值收进一个布尔函数:ResetLastError 清掉旧错误码,ObjectCreate 失败就 Print 出 _LastError 并返回 false,成功后才批量写 XSIZE / YSIZE / BGCOLOR / BORDER_TYPE / STYLE。宽度 xS、高度 yS 同样按像素算,比如传 xS=120、yS=40 大约就是个能塞下短文字的标签块。 开 MT5 新建脚本把这两段拼上,调 xD=10、yD=10 就能在左上角看到一块矩形;把 borderType 改成 BORDER_NONE 再编译,边框色那行便不再起作用,这是验证属性优先级最直接的办法。

MQL5 / C++
ObjectSetInteger(class="num">0, objName, OBJPROP_COLOR, clrBorder); class=class="str">"cmt">// Border class="type">class="kw">color (only if borderType is flat)
ObjectSetInteger(class="num">0, objName, OBJPROP_BACK, false); class=class="str">"cmt">// Not a background object
ObjectSetInteger(class="num">0, objName, OBJPROP_STATE, false); class=class="str">"cmt">// Not selectable
ObjectSetInteger(class="num">0, objName, OBJPROP_SELECTABLE, false); class=class="str">"cmt">// Not selectable
ObjectSetInteger(class="num">0, objName, OBJPROP_SELECTED, false); class=class="str">"cmt">// Not selected
ObjectSetInteger(class="num">0, objName, OBJPROP_XDISTANCE, xD); class=class="str">"cmt">// X distance from the corner
ObjectSetInteger(class="num">0, objName, OBJPROP_CORNER, CORNER_LEFT_UPPER); class=class="str">"cmt">// Positioning corner
ChartRedraw(class="num">0); class=class="str">"cmt">// Redraw the chart
class="kw">return (true); class=class="str">"cmt">// Return true if object creation and class="kw">property settings are successful
class="type">bool createRecLabel(class="type">class="kw">string objName, class="type">int xD, class="type">int yD, class="type">int xS, class="type">int yS,
                      class="type">class="kw">color clrBg, class="type">int widthBorder, class="type">class="kw">color clrBorder = clrNONE,
                      ENUM_BORDER_TYPE borderType = BORDER_FLAT, ENUM_LINE_STYLE borderStyle = STYLE_SOLID) {
   ResetLastError(); class=class="str">"cmt">// Reset any previous error codes
   
   class=class="str">"cmt">// Create a rectangle label object
   if (!ObjectCreate(class="num">0, objName, OBJ_RECTANGLE_LABEL, class="num">0, class="num">0, class="num">0)) {
      Print(__FUNCTION__, ": failed to create rec label! Error code = ", _LastError);
      class="kw">return (false); class=class="str">"cmt">// Return false if object creation fails
   }
   
   class=class="str">"cmt">// Set properties for the rectangle label
   ObjectSetInteger(class="num">0, objName, OBJPROP_XDISTANCE, xD); class=class="str">"cmt">// X distance from the corner
   ObjectSetInteger(class="num">0, objName, OBJPROP_YDISTANCE, yD); class=class="str">"cmt">// Y distance from the corner
   ObjectSetInteger(class="num">0, objName, OBJPROP_XSIZE, xS); class=class="str">"cmt">// Width of the rectangle
   ObjectSetInteger(class="num">0, objName, OBJPROP_YSIZE, yS); class=class="str">"cmt">// Height of the rectangle
   ObjectSetInteger(class="num">0, objName, OBJPROP_CORNER, CORNER_LEFT_UPPER); class=class="str">"cmt">// Positioning corner
   ObjectSetInteger(class="num">0, objName, OBJPROP_BGCOLOR, clrBg); class=class="str">"cmt">// Rectangle background class="type">class="kw">color
   ObjectSetInteger(class="num">0, objName, OBJPROP_BORDER_TYPE, borderType); class=class="str">"cmt">// Border type
   ObjectSetInteger(class="num">0, objName, OBJPROP_STYLE, borderStyle); class=class="str">"cmt">// Border style(only if borderType is flat)

常见问题

先建空项目,用四个基础函数搭出面板骨架,再往里放按钮和标签,具体代码可参考基础篇示例逐行改。
用矩形标签(Rectangle Label)类控件,按图表坐标定位,就能做出不随滚动乱跑的浮层。
可以,小布能根据你说的中文需求直接产出面板骨架代码,你复制到编辑器里调参数即可用。
用按钮控件加箭头符号文本,或下拉控件绑定数组,基础篇里有具体的创建与事件写法。
多数漏了事件处理函数绑定,按钮画出来但没接鼠标点击逻辑,补上回调就能正常触发。