自定义图形控件。第一部分:创建简单控件·综合运用
「控件读写接口与告警切换的内部逻辑」
这段封装把矩形标签类的基础存取器一次性摊开:Left()、Top()、Width()、Height() 直接回传成员变量,给外部定位用;SetTag / Tag 配对管理字符串标识,方便在图表上区分多个实例。 SetWarning 是重点,不是简单赋值。它先判断 m_Visible,只有控件可见才动 g.Attach(m_NameEdit) 重新绑定文本区,再按 aValue 在 m_WarningColor 与 m_BgColor 之间切背景色——也就是说隐藏状态下改告警标志不会触发重绘,能省一次图形刷新。 SetDigits 设了非负值就调 SetValue(ValueDouble()) 按新精度重排显示,Digits() 回读当前小数位。外汇与贵金属报价跳动快,这类控件若挂在实时面板,背景色切换延迟可能让告警慢一拍,属于高风险环境下的视觉辅助而非执行信号。 开 MT5 把上面代码贴进自定义类,建两个 CLabel 实例分别置 Warning(true/false),切 m_Visible 前后各调一次,能直接看到 Attach 是否重复触发。
class="kw">return(m_Left); } class=class="str">"cmt">// 获取Y轴坐标 class="type">int Top() { class="kw">return(m_Top); } class=class="str">"cmt">// 获取宽度 class="type">int Width() { class="kw">return(m_Width); } class=class="str">"cmt">// 获取高度 class="type">int Height() { class="kw">return(m_Height); } class=class="str">"cmt">// 设置标签 class="type">void SetTag(class="type">class="kw">string aValue) { m_Tag=aValue; } class=class="str">"cmt">// 获取标签 class="type">class="kw">string Tag() { class="kw">return(m_Tag); } class=class="str">"cmt">// 设置告警模式 class="type">void SetWarning(class="type">bool aValue) { if(m_Visible) { class=class="str">"cmt">// 启用可见性 if(aValue) { class=class="str">"cmt">// 我们需开启告警模式 if(!m_Warning) { class=class="str">"cmt">// 告警模式未启用 g.Attach(m_NameEdit); class=class="str">"cmt">// 分配用于控制的文本区域 g.SetBgColor(m_WarningColor); class=class="str">"cmt">// 设置文本区域的告警颜色背景 } } else { class=class="str">"cmt">// 我们需要禁用告警模式 if(m_Warning) { class=class="str">"cmt">// 告警模式启用 g.Attach(m_NameEdit); class=class="str">"cmt">// 分配用于控制的文本区域 g.SetBgColor(m_BgColor); class=class="str">"cmt">// 设置正常的字体颜色 } } } m_Warning=aValue; class=class="str">"cmt">// 注册当前模式 } class=class="str">"cmt">// 获取告警模式 class="type">bool Warning() { class="kw">return(m_Warning); } class=class="str">"cmt">// 设置小数位数 class="type">void SetDigits(class="type">int aValue) { m_Digits=aValue; class=class="str">"cmt">// 设置新值 if(m_Digits>=class="num">0) { class=class="str">"cmt">// 数字模式 SetValue(ValueDouble()); class=class="str">"cmt">// 重设当前值 } } class=class="str">"cmt">// 获取m_Digits的值 class="type">int Digits() { class="kw">return(m_Digits); }
给控件套上可切换的配色
GUI 控件的配色集中交给 CColorSchemes 类(实例名 ClrScheme)管理,不写死在各自控件里。调用 SetScheme(int) 传入方案编号即可切换整体观感;不调用时默认走编号 0 的方案,避免空指针式的初始化遗漏。 取色统一走 Color(int) 方法,参数是方案内的颜色序号。底层用二维数组存色:第一维是方案号,第二维是方案内颜色号。当前写了 3 套方案(0/1/2),每套 4 个颜色,其中序号 1 和 2 在代码里被赋了相同值,相当于边框与字体同色,省一个冗余定义。 下面这段是类里最核心的两段,直接拆开看: // 设置颜色方案编号 void SetScheme(int aShemeIndex) { m_ShemeIndex=aShemeIndex; // 把传入的方案号存进私有变量,后续 Color() 靠它选行 } color Color(int aColorIndex) { color m_Color[3][4]; // 第一维 - 色彩方案编号,第二维 - 色彩方案中的颜色数量 // 默认 m_Color[0][0]=clrSnow; m_Color[0][1]=clrDimGray; m_Color[0][2]=clrDimGray; m_Color[0][3]=clrPink; // 黄 黑 m_Color[1][0]=clrLightYellow; m_Color[1][1]=clrBrown; m_Color[1][2]=clrBrown; m_Color[1][3]=clrPink; // 蓝色 m_Color[2][0]=clrAliceBlue; m_Color[2][1]=clrNavy; m_Color[2][2]=clrNavy; m_Color[2][3]=clrPink; return(m_Color[m_ShemeIndex][aColorIndex]); // 根据色彩方案和方案中的颜色数量返回一个值 } 在 CInputBox 的 Init() 里把写死的颜色换成 ClrScheme 的取值,控件就活了: m_BgColor=ClrScheme.Color(0); // 文本区域的背景颜色 field m_TxtColor=ClrScheme.Color(1); // 字体的颜色和文本区的框架颜色 m_LblColor=ClrScheme.Color(2); // 标题颜色 m_WarningColor=ClrScheme.Color(3); // 告警颜色 调试阶段别靠脑补颜色。类里提供了 Show() 在图表上画出色板、Hide() 撤掉,附带的 ColorSchemesView.mq5 这个 EA 就是干这事的——挂上 MT5 跑一下,三套方案 12 个色块直接可见,要加第四套或补第五个颜色,先在这里眼验再改数组。外汇贵金属面板高频刷新,配色对比度不够可能在波动剧烈时看不清告警色,属于实操里容易栽的小坑。
class=class="str">"cmt">// 设置颜色方案编号 class="type">void SetScheme(class="type">int aShemeIndex) { m_ShemeIndex=aShemeIndex; } class="type">color Color(class="type">int aColorIndex) { class="type">color m_Color[class="num">3][class="num">4]; class=class="str">"cmt">// 第一维 - 色彩方案编号,第二维 - 色彩方案中的颜色数量 class=class="str">"cmt">// 默认 m_Color[class="num">0][class="num">0]=clrSnow; m_Color[class="num">0][class="num">1]=clrDimGray; m_Color[class="num">0][class="num">2]=clrDimGray; m_Color[class="num">0][class="num">3]=clrPink; class=class="str">"cmt">// 黄 黑 m_Color[class="num">1][class="num">0]=clrLightYellow; m_Color[class="num">1][class="num">1]=clrBrown; m_Color[class="num">1][class="num">2]=clrBrown; m_Color[class="num">1][class="num">3]=clrPink; class=class="str">"cmt">// 蓝色 m_Color[class="num">2][class="num">0]=clrAliceBlue; m_Color[class="num">2][class="num">1]=clrNavy; m_Color[class="num">2][class="num">2]=clrNavy; m_Color[class="num">2][class="num">3]=clrPink; class="kw">return(m_Color[m_ShemeIndex][aColorIndex]); class=class="str">"cmt">// 根据色彩方案和方案中的颜色数量返回一个值 } m_BgColor=ClrScheme.Color(class="num">0); class=class="str">"cmt">// 文本区域的背景颜色 field m_TxtColor=ClrScheme.Color(class="num">1); class=class="str">"cmt">// 字体的颜色和文本区的框架颜色 m_LblColor=ClrScheme.Color(class="num">2); class=class="str">"cmt">// 标题颜色 m_WarningColor=ClrScheme.Color(class="num">3); class=class="str">"cmt">// 告警颜色
◍ 在 EA 里挂上输入框与配色切换
做一套带界面的 EA,先起名 GUITest,头部引 IncGUI.mqh,用 ib 声明 CInputBox 实例。OnInit 里调 ib.Init("InpytBox",50,4,"input") 把输入框宽度设 50、高度 4、默认文本 input,再用 ib.Show(10,20) 把控件摆到坐标 (10,20);OnDeinit 里 ib.Hide() 释放,编译挂图就能看到原生输入框(原文图 7)。 配色别写死。用 enum 列 DefaultScheme=0、YellowBrownScheme=1、BlueScheme=2,外部变量 input eColorScheme ColorScheme=DefaultScheme 让用户在 EA 属性窗直接挑方案,OnInit 开头 ClrScheme.SetScheme(ColorScheme) 生效,实测能切出三套色板(原文图 8)。 输入框改值要有反馈。OnChartEvent 里判 ib.Event(id,lparam,dparam,sparam)==1 就 Alert("Entered value "+ib.ValueStrind()),录完新值弹窗报字符串,方便你当场核对录入。 子窗口逻辑看指标在不在。OnChartEvent 捕 CHARTEVENT_CHART_CHANGE 后 ip.SetSubWindow("TestSubWindow"):指标没挂主图,控件留主图;指标挂主图,控件跳子窗(原文图 9);删指标又弹回主图。外汇贵金属脚本改界面参数属高频操作,MT5 环境崩界面可能丢事件,先小规模验证再上实盘。
class="macro">#include <IncGUI.mqh> CInputBox ib; ib.Init("InpytBox",class="num">50,class="num">4,"input"); ib.Show(class="num">10,class="num">20); ib.Hide(); enum eColorScheme { DefaultScheme=class="num">0, YellowBrownScheme=class="num">1, BlueScheme=class="num">2 }; input eColorScheme ColorScheme=DefaultScheme; ClrScheme.SetScheme(ColorScheme); if(ib.Event(id,lparam,dparam,sparam)==class="num">1) { Alert("Entered value "+ib.ValueStrind()); } if(CHARTEVENT_CHART_CHANGE) { ip.SetSubWindow("TestSubWindow"); }
「把四个类接进EA的正确顺序」
IncGUI.mqh 里落了四个东西:CGraphicObjectShell 管图形对象的创建与生命周期,CWorkPiece 用参数批量生成对象,CColorSchemes 管配色,外加一个控件类 CInputBox。前三个在文件里分别用 g、w、ClrScheme 做了声明,挂上 IncGUI.mqh 之后就能直接点名用,不用再自己 new 一套。 CInputBox 的接法有固定套路:先 include,再声明实例,调 Init();坐标用 SetPos() 定,或者直接在 Show() 里带坐标一起亮出来。不想要了就 Hide(),EA 收尾时也得记得藏干净。 图表事件不走 Event() 就等于控件聋了——OnChartEvent() 里必须塞进 Event() 调用。要是控件打算扔子窗口,还得在 CHARTEVENT_CHART_CHANGE 里补一句 SetSubWindow(),否则坐标会算错位置。 配色想在初始化前就定调,就在 Init() 之前调 ClrScheme.SetScheme();顺序反了就只能是默认色。外汇与贵金属图表挂这类自定义控件存在平台兼容性风险,建议在 MT5 策略测试器先跑一遍再上真实图。
一点提醒
这套自定义图形控件库以 IncGUI.mqh 为核心,需置于 MQL5/Include 下;GUITest.mq5、ColorSchemesView.mq5 放 Experts,TestSubWindow.mq5 放 Indicators,随附的 chm 文档供查阅接口。压缩包 custom_graphic_controls_mql5.zip 体积约 252.19 KB,下载后按目录拖入即可在 MT5 里跑起控件样例。 下面这段俄文示例给出了三种创建垂直基准线 OBJ_VLINE 的写法,差异只在 ObjectCreate 与 Attach 的先后以及图表 ID 传参方式,实际用哪种都行,但 Attach 必须在对象创建后调用才绑得稳。 外汇与贵金属图表加载这类自定义控件时波动跳空频繁,图形对象可能瞬间错位,属于高风险环境下的界面实验,建议先在模拟盘验证再谈实盘。
Для примера, один метод создания графического объекта вертикальная линия (OBJ_VLINE): class="type">void CreateVLine(class="type">class="kw">string aName,class="type">int aSubWindow=class="num">0,class="type">long aChartID=class="num">0) { ObjectCreate(m_id,m_name,OBJ_VLINE,aSubWindow,class="num">0,class="num">0); Attach(aName,aChartID); } Для примера, один метод создания графического объекта вертикальная линия (OBJ_VLINE): class="type">void CreateVLine(class="type">class="kw">string aName,class="type">int aSubWindow=class="num">0,class="type">long aChartID=class="num">0) { Attach(aName,aChartID); ObjectCreate(m_id,m_name,OBJ_VLINE,aSubWindow,class="num">0,class="num">0); } Для примера, один метод создания графического объекта вертикальная линия (OBJ_VLINE): class="type">void CreateVLine(class="type">class="kw">string aName,class="type">int aSubWindow=class="num">0,class="type">long aChartID=class="num">0) { ObjectCreate(aChartID,aName,OBJ_VLINE,aSubWindow,class="num">0,class="num">0); Attach(aName,aChartID); } По умолчанию будет устанавливаться диапазон допустимых значений от -DBL_MAX до DBL_MAX