使用MQL5轻松创建图形面板·综合运用
(3/3)· 新手也能照做的面板开发收尾篇,用同一案例对比裸写与调库的差异
◍ 用 ObjectSetInteger 把按钮摆上图表
在 MT5 里画一个可交互的 OBJ_EDIT 或按钮对象,光用 ObjectCreate 拿到句柄还不够,坐标、尺寸、字体、配色全得靠 ObjectSetInteger / ObjectSetString 后续写入,否则对象建出来也是个看不见的空壳。 创建失败时要立刻把错误码抛出来:Print(__FUNCTION__, ": failed to create \"Edit\" object! Error code = ", GetLastError()); 然后 return(false); 这套写法能让你在专家日志里直接定位是哪一行参数越界,不用瞎猜。 位置和大小走整型属性:OBJPROP_XDISTANCE / OBJPROP_YDISTANCE 定相对角落的偏移像素,OBJPROP_XSIZE / OBJPROP_YSIZE 定控件宽高;文字内容走 ObjectSetString 的 OBJPROP_TEXT,字体名和 OBJPROP_FONTSIZE 分开设,对齐方式用 OBJPROP_ALIGN 控。 交互细节容易漏:OBJPROP_READONLY 决定能否手敲改值,OBJPROP_SELECTABLE 配合 OBJPROP_SELECTED 管鼠标拖动,OBJPROP_ZORDER 抢鼠标点击事件优先级。角落基准 OBJPROP_CORNER 设错,x/y 就会从图表另一头算起——这是实盘面板错位的高频坑。 颜色三层分开:OBJPROP_COLOR 管文字、OBJPROP_BGCOLOR 管背景、OBJPROP_BORDER_COLOR 管边框;OBJPROP_BACK 切前后景,OBJPROP_HIDDEN 藏对象列表名。全部写完后 return(true),外汇与贵金属图表上挂这类控件属高风险界面操作,参数误设可能导致面板遮挡报价。
Print(__FUNCTION__, ": failed to create \"Edit\" object! Error code = ", GetLastError()); class="kw">return(class="kw">false); } class=class="str">"cmt">//--- Setting the object coordinates x and y by using the ObjectSetInteger ObjectSetInteger(chart_ID, name, OBJPROP_XDISTANCE, x); ObjectSetInteger(chart_ID, name, OBJPROP_YDISTANCE, y); class=class="str">"cmt">//--- Setting the object size by using the ObjectSetInteger function also ObjectSetInteger(chart_ID, name, OBJPROP_XSIZE, width); ObjectSetInteger(chart_ID, name, OBJPROP_YSIZE, height); class=class="str">"cmt">//--- Setting the text by using ObjectSetString function ObjectSetString(chart_ID, name, OBJPROP_TEXT, text); class=class="str">"cmt">//--- Setting the text font by using the ObjectSetString function also ObjectSetString(chart_ID, name, OBJPROP_FONT, font); class=class="str">"cmt">//--- Setting the font size by using the ObjectSetInteger function ObjectSetInteger(chart_ID, name, OBJPROP_FONTSIZE, font_size); class=class="str">"cmt">//--- Setting the type of text alignment in the object ObjectSetInteger(chart_ID, name, OBJPROP_ALIGN, align); class=class="str">"cmt">//--- Setting the ability to edit, enable if it is(true) or cancel(class="kw">false) if you need a read-only mode ObjectSetInteger(chart_ID, name, OBJPROP_READONLY, read_only); class=class="str">"cmt">//--- Setting the chart&class="macro">#x27;s corner, relative to which object coordinates are defined to set the location of the object. ObjectSetInteger(chart_ID, name, OBJPROP_CORNER, corner); class=class="str">"cmt">//--- Setting the text class="type">color ObjectSetInteger(chart_ID, name, OBJPROP_COLOR, clr); class=class="str">"cmt">//--- Setting the background class="type">color ObjectSetInteger(chart_ID, name, OBJPROP_BGCOLOR, back_clr); class=class="str">"cmt">//--- Setting the border class="type">color of the object ObjectSetInteger(chart_ID, name, OBJPROP_BORDER_COLOR, border_clr); class=class="str">"cmt">//--- Displaying in the foreground by(class="kw">false) or in the background by(true) ObjectSetInteger(chart_ID, name, OBJPROP_BACK, back); class=class="str">"cmt">//--- Setting(true) to enable or(class="kw">false) to disable the mode of moving the label by mouse ObjectSetInteger(chart_ID, name, OBJPROP_SELECTABLE, selection); ObjectSetInteger(chart_ID, name, OBJPROP_SELECTED, selection); class=class="str">"cmt">//--- Setting(true) if you need hiding or(class="kw">false) if you need the display of graphical object name in the object list ObjectSetInteger(chart_ID, name, OBJPROP_HIDDEN, hidden); class=class="str">"cmt">//--- Setting the priority for receiving the event of a mouse click in the chart ObjectSetInteger(chart_ID, name, OBJPROP_ZORDER, z_order); class=class="str">"cmt">//--- Returning(true) if successful execution class="kw">return(true); } class="type">bool ButtonCreate(const class="type">long chart_ID=class="num">0, const class="type">class="kw">string name="Button", const class="type">int sub_window=class="num">0, const class="type">int x=class="num">0,
「按钮对象的参数与创建落点」
在 MT5 自定义指标或 EA 里画一个可点击按钮,先得把位置、尺寸和样式参数定死。下面这段函数头把坐标原点锁在左上角(CORNER_LEFT_UPPER),按钮宽 50 像素、高 18 像素,字体用 Arial 10 号,文字写死成 "Button",背景填 RGB(236,233,216),边框设为 clrNONE 无描边。 这些常量直接写进函数签名,调用时不用再传,适合做标准控件模板。注意 hidden=true 代表该对象不参与常规对象列表显示,z_order=0 表示层级垫底,不会被后续图形盖住交互区。 真正落对象靠 ObjectCreate:图表 ID 和名字由外部传入,类型写 OBJ_BUTTON,子窗口编号 sub_window,初始 x/y 坐标先填 0,0,再由后面逻辑重定位。创建前 ResetLastError 清掉旧错误码,失败就用 Print(__FUNCTION__ 输出函数名方便排查——外汇与贵金属图表高频刷新,对象创建失败往往和图表重绘冲突有关,属于高风险环境下的常见坑。
const class="type">int y=class="num">0, const class="type">int width=class="num">50, const class="type">int height=class="num">18, const ENUM_BASE_CORNER corner=CORNER_LEFT_UPPER, const class="type">class="kw">string text="Button", const class="type">class="kw">string font="Arial", const class="type">int font_size=class="num">10, const class="type">color clr=clrBlack, const class="type">color back_clr=C&class="macro">#x27;class="num">236,class="num">233,class="num">216&class="macro">#x27;, const class="type">color border_clr=clrNONE, const class="type">bool state=class="kw">false, const class="type">bool back=class="kw">false, const class="type">bool selection=class="kw">false, const class="type">bool hidden=true, const class="type">long z_order=class="num">0) { ResetLastError(); if(!ObjectCreate(chart_ID,name,OBJ_BUTTON,sub_window,class="num">0,class="num">0)) { Print(__FUNCTION__,
用控件类拼一个下单面板
在 MT5 里手搓交易面板,最省事的办法是直接引用标准库里的控件类,而不是从头写 ObjectCreate。上面这段先引入了 Dialog、Button、Edit 和 Trade 四个头文件,用 CAppDialog 做容器,CButton 做买卖按钮,CEdit 做手数输入,CTrade 发单。 宏定义把面板尺寸钉死了:PANEL_WIDTH 是 116 像素,PANEL_HIEIGHT 写成 100(原文拼写如此,编译能过但建议改名),ROW_HEIGHT 为 20,按钮宽 50、高 20,买卖键横向间距靠 X 坐标 3 和 63 错开。 OnInit 里 panel.Create 在坐标 (10,20) 起手,宽高按宏算出来是 10+116+8=134 宽、20+100=120 高。buyBtn 和 sellBtn 分别用绿色背景配黑字“Buy”、红色背景配黑字“Sell”,字体 Arial 10 号,加进 panel 后就能在图表左上角看到两个并排按键。 外汇和贵金属杠杆高、滑点凶,这类面板只是把下单动作图形化,不替你判断方向;跑之前先在策略测试器用模拟账户验证按钮事件有没有接对 CTrade。
class="macro">#include <Controls\Dialog.mqh> class="macro">#include <Controls\Button.mqh> class="macro">#include <Controls\Edit.mqh> class="macro">#include <Trade\Trade.mqh> class="macro">#define PANEL_NAME "Order Panel" class="macro">#define PANEL_WIDTH class="num">116 class="macro">#define PANEL_HIEIGHT class="num">100 class="macro">#define ROW_HEIGHT class="num">20 class="macro">#define BUY_BTN_NAME "Buy BTN" class="macro">#define SELL_BTN_NAME "Sell BTN" class="macro">#define CLOSE_BTN_NAME "Close BTN" class="macro">#define EDIT_NAME "Lot Size" CAppDialog panel; CButton buyBtn; CButton sellBtn; CButton closeBtn; CEdit lotSize; CTrade trade; panel.Create(class="num">0,PANEL_NAME,class="num">0,class="num">10,class="num">20,class="num">10+PANEL_WIDTH+class="num">8,class="num">20+PANEL_HIEIGHT); class="type">int OnInit() { class=class="str">"cmt">//buy button panel.Create(class="num">0,PANEL_NAME,class="num">0,class="num">10,class="num">20,class="num">10+PANEL_WIDTH+class="num">8,class="num">20+PANEL_HIEIGHT); buyBtn.Create(class="num">0,BUY_BTN_NAME,class="num">0,class="num">3,class="num">40,class="num">0,class="num">0); buyBtn.Width(class="num">50); buyBtn.Height(ROW_HEIGHT); buyBtn.ColorBackground(clrGreen); buyBtn.Text("Buy"); buyBtn.Font("Arial"); buyBtn.FontSize(class="num">10); buyBtn.Color(clrBlack); buyBtn.ColorBorder(clrBlack); panel.Add(buyBtn); class=class="str">"cmt">//sell button sellBtn.Create(class="num">0,SELL_BTN_NAME,class="num">0,class="num">63,class="num">40,class="num">0,class="num">0); sellBtn.Width(class="num">50); sellBtn.Height(ROW_HEIGHT); sellBtn.ColorBackground(clrRed); sellBtn.Text("Sell"); sellBtn.Font("Arial"); sellBtn.FontSize(class="num">10); sellBtn.Color(clrBlack); sellBtn.ColorBorder(clrBlack); panel.Add(sellBtn); class=class="str">"cmt">//lotSize
◍ 把手填手数接进买卖按钮
上面这段是面板初始化收尾与图表事件联动的核心。lotSize 编辑框在坐标 (0,4)、宽 108 像素、高用 ROW_HEIGHT 常量,默认文本 "0.01",Arial 10 号字,白底黑字黑边,最后 panel.Add 挂进容器,panel.Run() 启动,Init 返回 INIT_SUCCEEDED。 OnDeinit 里只做一件事:panel.Destroy() 释放资源,避免指标/EA 重载时残留图形对象。 真正下单发生在 OnChartEvent:当 id 等于 CHARTEVENT_OBJECT_CLICK 且 sparam 是 buyBtn 名称时,把 lotSize.Text() 用 StringToDouble 转成 double,调 trade.Buy(lot);点 sellBtn 则走 trade.Sell(lot)。手数完全取决于编辑框里填的字符串,默认 0.01。 开 MT5 新建 EA 把这段贴进工程,改 EDIT_NAME 和按钮 Name(),就能在图表上点按钮按框内手数市价进出。外汇和贵金属杠杆高,实盘前务必先在策略测试器用 0.01 验证点击逻辑。
lotSize.Create(class="num">0,EDIT_NAME,class="num">0,class="num">4,class="num">10,class="num">0,class="num">0); lotSize.Width(class="num">108); lotSize.Height(ROW_HEIGHT); lotSize.Text("class="num">0.01"); lotSize.Font("Arial"); lotSize.FontSize(class="num">10); lotSize.ColorBackground(clrWhite); lotSize.Color(clrBlack); lotSize.ColorBorder(clrBlack); panel.Add(lotSize); class=class="str">"cmt">//run the panel panel.Run(); class="kw">return(INIT_SUCCEEDED); } class="type">void OnDeinit(const class="type">int reason) { panel.Destroy(); } 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) { panel.ChartEvent(id,lparam,dparam,sparam); if(id==CHARTEVENT_OBJECT_CLICK) { if(sparam==buyBtn.Name()) { class="type">class="kw">double lot=StringToDouble(lotSize.Text()); trade.Buy(lot); } else if(sparam==sellBtn.Name()) { class="type">class="kw">double lot=StringToDouble(lotSize.Text()); trade.Sell(lot); } } }
「记住这一条就够了」
回看附件里的两个 mq5 文件就能直观比对:用 Panels/Dialog 类写的 simpleOrderPanel.mq5 只有 2.91 KB,而不靠类、纯手写控件的 simpleOrderPanelWithoutClass.mq5 体积飙到 18.2 KB。省下的 15 KB 代码量,基本就是你在重复画按钮、管坐标、接事件上少掉的头发出量。 面板类真正的价值不在炫技,而是把下单、撤单这类机械活儿封装掉,让你把注意力放回价格行为和盘口本身。外汇与贵金属保证金交易波动剧烈、杠杆风险高,任何面板都只是执行辅助,不替你判断方向。 想给按钮加悬停提示,别去调控件对象没有的 ToolTip 方法,直接用原生函数最稳: ObjectSetString(0, "Your object name", OBJPROP_TOOLTIP, "Your text"); 这行在 MT5 里对图表对象一律有效,比绕进类成员碰壁省事。开 MT5 把那两个文件都编译跑一遍,手感比读十篇文都实在。
ObjectSetString(class="num">0, "Your object name", OBJPROP_TOOLTIP, "Your text");