简介
标准库中提供一组新类。这些类专为 MQL5 程序中独立开发控件对话框和显示面板而设计。
新组类让每个人都能利用事件驱动模型作为底层模型,创建自定义界面组件。一切都基于终端的嵌入式图表对象及事件。
上述开发类用法如下:
- 独立图表子窗口中的显示面板;
- “EA 交易”控制面板;
- 自定义显示控制面板。
本文将向您展示,利用“标准库”类,在独立图表子窗口中创建自有显示面板有多容易。
“标准库”的标配内容有哪些?
“标准库”会为开发人员提供下述即用控件:
1. 简单控件:
控件 应用 执行所基嵌入对象 “标准库”文件 带文本按钮
确保鼠标与 MQL 程序间的交互
“按钮” <Controls/Button.mqh> 带图像按钮
确保鼠标与 MQL 程序间的交互
“图形标签” <Controls/BmpButton.mqh> 编辑
文本信息的输入或显示(“只读”模式中)
“编辑” <Controls/Edit.mqh> 说明
辅助文本信息的显示
“文本标签” <Controls/Label.mqh> 面板
辅助控制(可视控件分组)
“矩形标签” <Controls/Panel.mqh> 图像
装饰性控件
“Graphical label” (图形标识) <Controls/Picture.mqh>
2. 复杂控件:
控件 应用 执行所基控件 “标准库”文件 列表
查看列表
“矩形”、“带图像按钮”及“编辑” <Controls/List.mqh> 带下拉列表的字段
从下拉列表中选择
“编辑”、“带图像按钮”及“列表” <Controls/ComboBox.mqh> 增量/减量字段
枚举值
“编辑”和“带图像按钮” <Controls/SpinEdit.mqh> 单选按钮
开关 “带图像按钮”和“说明” <Controls/RadioButton.mqh> 单选按钮组 枚举类型字段编辑 “矩形”与“单选按钮” <Controls/RadioGroup.mqh> 复选框
选择选项
“带图像按钮”和“说明” <Controls/CheckBox.mqh> 复选框组
编辑一组标志
“矩形”和“复选框” <Controls/CheckGroup.mqh> 对话框 对话框表单 “矩形”、“带图像按钮”及“编辑” <Controls/Dialog.mqh>
创建一个显示面板
我们先对该术语进行定义。“显示面板”是我们用来描述没有任何绘图缓冲区的独立窗口自定义显示的一个术语。此类面板只是利用终端中嵌入的图表对象显示所需信息。信息的显示方式有:
- 数值,
- 文本,
- 颜色,
- 等
我们将详细地看一看每个要求步骤,并如下创建一个图形面板:
想要创建一个显示面板,我们需要两个文件:
- 包含文件,其中包含显示面板类的描述。
- 指标源代码文件。
可利用MQL5向导获取上述文件的模板。于指标目录(MQL5/Indicators)下,创建一个 MyIndicators 独立文件名和一个 MyPanel 子文件夹。创建文件夹的过程已于“帮助”中有所详述,所以这里不再赘述。
类描述
我们已经完成了工作文件夹的创建。现在,我们要在 “Navigator” (导航器)窗口中找到它并右键单击。在出现的菜单中选择 “New File” (新建文件)。于 MQL5 向导指定的选项中选出 “New Class” (新建类),再点击 “Next >” (下一步)。如下所示,完成类描述对话框的填写:
点击 “Finish” (完成)。如此一来,我们会得到下述代码:
//+------------------------------------------------------------------ //| PanelDialog.mqh | //| Copyright 2011, MetaQuotes Software Corp. | //| https://www.mql5.com | //+------------------------------------------------------------------ #property copyright "Copyright 2011, MetaQuotes Software Corp." #property link "https://www.mql5.com" #property version "1.00" //+------------------------------------------------------------------ //| | //+------------------------------------------------------------------ class CPanelDialog : public CAppDialog { private: public: CPanelDialog(); ~CPanelDialog(); }; //+------------------------------------------------------------------ //| | //+------------------------------------------------------------------ CPanelDialog::CPanelDialog() { } //+------------------------------------------------------------------ //| | //+------------------------------------------------------------------ CPanelDialog::~CPanelDialog() { } //+------------------------------------------------------------------
从“标准库”添加带有基础 CAppDialog 类描述与注释的包含文件 <Controls/Dialog.mqh>。
//+------------------------------------------------------------------ //| PanelDialog.mqh | //| Copyright 2011, MetaQuotes Software Corp. | //| https://www.mql5.com | //+------------------------------------------------------------------ #include <Controls/Dialog.mqh> //+------------------------------------------------------------------ //| CPanelDialog类 | //| 函数:主要应用程序对话框 | //+------------------------------------------------------------------ class CPanelDialog : public CAppDialog { private: public: CPanelDialog(void); ~CPanelDialog(void); }; //+------------------------------------------------------------------ //| 构造函数 | //+------------------------------------------------------------------ CPanelDialog::CPanelDialog(void) { } //+------------------------------------------------------------------ //| 析构函数 | //+------------------------------------------------------------------ CPanelDialog::~CPanelDialog(void) { } //+------------------------------------------------------------------
现在,我们有了类描述,就能够在其指标中使用对话框了。我们的对话框当前为空,但稍后我们就会向其添加控件。现在,我们来看一看指标。
指标源代码
指标亦将利用 MQL5 向导创建。要采取的动作与编写类描述时的要求类似。只有一点差异 – 我们会从 MQL5 向导提供的选项中选出 “Custom Indicator” (自定义指标)。想要创建一个指标,应完成三个对话框的填写。
每一个要求指定该指标的名称:
在第二个对话框中,勾选 “OnChartEvent” (必选)和 “OnTimer“:
勾选第三个对话框中的”Separate Window Indicator”(必选,独立窗口指标):
再点击 “Finish”(完成)。生成如下代码:
//+------------------------------------------------------------------ //| PanelIndicator.mq5 | //| Copyright 2011, MetaQuotes Software Corp. | //| https://www.mql5.com | //+------------------------------------------------------------------ #property copyright "Copyright 2011, MetaQuotes Software Corp." #property link "https://www.mql5.com" #property version "1.00" #property indicator_separate_window //+------------------------------------------------------------------ //| 自定义指标初始化函数 | //+------------------------------------------------------------------ int OnInit() { //--- 指标缓冲映射 //--- return(0); } //+------------------------------------------------------------------ //| 自定义指标迭代函数 | //+------------------------------------------------------------------ int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) { //--- //--- 返回prev_calculated值用于下一次调用 return(rates_total); } //+------------------------------------------------------------------ //| 定时器函数 | //+------------------------------------------------------------------ void OnTimer() { //--- } //+------------------------------------------------------------------ //| ChartEvent函数 | //+------------------------------------------------------------------ void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { //--- } //+------------------------------------------------------------------
现在通过添加下述内容,即可完成我们的模板:
- 指标属性的缺失描述;
- 包含我们对话框的类描述的包含文件;
- 一个全局变量 – 我们对话框的类对象;
- 创建对话框、启用应用程序和创建计时器到 OnInit() 函数体的代码;
- OnDeinit() 函数包含一个可销毁对话框并关掉计时器的代码;
- OnChartEvent(…) 函数事件处理程序调用代码;
- 注释。
我们已经完成了即用型指标:
//+------------------------------------------------------------------ //| PanelIndicator.mq5 | //| Copyright 2011, MetaQuotes Software Corp. | //| https://www.mql5.com | //+------------------------------------------------------------------ #property copyright "Copyright 2011, MetaQuotes Software Corp." #property link "https://www.mql5.com" #property version "1.00" #property indicator_separate_window #property indicator_plots 0 #property indicator_buffers 0 #property indicator_minimum 0.0 #property indicator_maximum 0.0 //+------------------------------------------------------------------ //| 包含文件 | //+------------------------------------------------------------------ #include "PanelDialog.mqh" //+------------------------------------------------------------------ //| 全局变量 | //+------------------------------------------------------------------ CPanelDialog ExtDialog; //+------------------------------------------------------------------ //| 初始化 | //+------------------------------------------------------------------ int OnInit() { //--- 创建应用对话框 if(!ExtDialog.Create(0,"Panel Indicator",0,0,0,0,130)) return(-1); //--- 开始应用 if(!ExtDialog.Run()) return(-2); //--- 创建定时器 EventSetTimer(1); //--- 成功 return(0); } //+------------------------------------------------------------------ //| 去初始化 | //+------------------------------------------------------------------ int OnDeinit() { //--- 销毁对话框 ExtDialog.Destroy(); //--- 杀掉 定时器 EventKillTimer(); } //+------------------------------------------------------------------ //| 迭代 | //+------------------------------------------------------------------ int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) { //--- 返回 prev_calculated数值用于下次调用 return(rates_total); } //+------------------------------------------------------------------ //| 定时器事件处理函数 | //+------------------------------------------------------------------ void OnTimer() { //--- } //+------------------------------------------------------------------ //| 图表事件处理函数 | //+------------------------------------------------------------------ void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { //--- 处理事件 ExtDialog.ChartEvent(id,lparam,dparam,sparam); } //+------------------------------------------------------------------
上方表单中的指标至此未显示任何内容。编译完成并从“导航器”拖放至图表后,它会在一个独立的窗口中作为一个空对话框显示。
尽管对话框是空的,但我们的指标已经获取到了某些特性:
- 子窗口高度已于创建期间由指标调整为对话框高度;
- 对话框宽度始终等于图表宽度;
- 指标可以实现其自有子窗口的最小化和最大化。
令其显示
想要我们的面板开始显示任何信息,我们应根据对于下述三个问题的回答来决定:
- 我们想要显示哪类信息?
- 我们的对话框上需要放置哪些附加显示元素和(或)控件?
- 这些附加元素/控件会如何交互?
我们的对话框要有视觉吸引力且人性化,这也是一大重要因素。它不会影响到对话框的功能,但却能显示出我们对于未来 MQL5 程序用户的关怀。
步骤 1. 我们想要显示哪类信息?
因为本文是充当一种学习工具,所以我们来仔细讲讲指标的可用性。颜色会根据三个参数显示。我们不会让参数太过复杂 – 分别为“红色”、“绿色”和“蓝色”色阶。
参数值将如下设置:
顺便提一下,上述色阶的值也会显示于我们的指标中。
步骤 2. 需要哪些附加控件?
- 颜色将利用“面板”控件显示。
- “红色”和“绿色”色阶会利用“只读”模式中的“编辑”控件显示。
- 而“蓝色”色阶则会通过“微调编辑”控件进行管理。采用相同的控件帮助显示色阶值。
- “编辑”和“微调编辑”两个控件,都将通过“说明”控件的方式,增设解释性说明。
将“标准库”中的包含文件,以及存储参数值的所需控件和变量,添加到内含提供注释的类描述。
我们就会得到:
//+------------------------------------------------------------------ //| PanelDialog.mqh | //| Copyright 2011, MetaQuotes Software Corp. | //| https://www.mql5.com | //+------------------------------------------------------------------ #include <Controls/Dialog.mqh> #include <Controls/Panel.mqh> #include <Controls/Edit.mqh> #include <Controls/Label.mqh> #include <Controls/SpinEdit.mqh> //+------------------------------------------------------------------ //| CPanelDialog类 | //| 函数:主要应用程序对话框 | //+------------------------------------------------------------------ class CPanelDialog : public CAppDialog { private: //--- 附加控件 CPanel m_color; // 显示颜色对象 CLabel m_label_red; // "红" 级别标题对象 CEdit m_field_red; // "红" 数值显示对象 CLabel m_label_green; // "绿" 级别标题对象 CEdit m_field_green; // "绿"数值显示对象 CLabel m_label_blue; // "蓝" 级别标题对象 CSpinEdit m_edit_blue; // "蓝" 数值控件 //--- 参数值 int m_red; // "红" 值 int m_green; // "绿" 值 int m_blue; // "蓝" 值 public: CPanelDialog(void); ~CPanelDialog(void); }; //+------------------------------------------------------------------ //| 构造函数 | //+------------------------------------------------------------------ CPanelDialog::CPanelDialog(void) { } //+------------------------------------------------------------------ //| 析构函数 | //+------------------------------------------------------------------ CPanelDialog::~CPanelDialog(void) { } //+------------------------------------------------------------------
步骤 3. 这些附加对话框控件会如何交互?
对话框控件之间的交互原则非常简单 – “对话框应显示任何参数(“红色”、“绿色”及“蓝色”色阶)中的变化。”交互算法的实施我们稍后再谈,因为我们现在要开始创建对话框了。
浅议视觉吸引力
在讲解创建对话框的内容之前,我们简要地谈谈视觉吸引力。或者换用一种更恰当的说法:对话框控件的人性化布局,以及(可能的)未来重新布局。具名常量 (#define) 最适合此用途。
预定义具名常量具备下述一些优势:
- 无需记忆具体情况下使用的特定数值。精准选取常量名称,从而可通过“自动列表名称”获取它们;
- 进一步修改常量值时,无需搜索和替换数值中的众多内容。只更改常量描述就够了。
建议采用下述常量:
//+------------------------------------------------------------------ //| 定义 | //+------------------------------------------------------------------ //--- 缩进与空白 #define INDENT_LEFT (11) // 左缩进 (包括边框宽度) #define INDENT_TOP (11) // 顶缩进 (包括边框宽度) #define INDENT_RIGHT (11) // 右缩进 (包括边框宽度) #define INDENT_BOTTOM (11) // 底缩进 (包括边框宽度) #define CONTROLS_GAP_X (10) // 空格 X-轴 #define CONTROLS_GAP_Y (10) // 空格 Y-轴 //--- 标签 #define LABEL_WIDTH (50) // 尺寸 X-轴 //--- 编辑 #define EDIT_WIDTH (50) // 尺寸 Y-轴 #define EDIT_HEIGHT (20) // 尺寸 Y-轴 //--- 基准颜色 (RGB) #define BASE_COLOR_MIN (0) // 颜色元件最小值 #define BASE_COLOR_MAX (255) // 颜色元件最大值
填写显示面板
之前我们创建了显示面板类;现在,想要获得需要的功能,我们需要执行下述事项:
1. 重新定义父类的 Create(…) 方法我们的方法会独创性地如下显示:
//+------------------------------------------------------------------ //| 创建 | //+------------------------------------------------------------------ bool CPanelDialog::Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2) { //--- 调用父类函数 if(!CAppDialog::Create(chart,name,subwin,x1,y1,x2,y2)) return(false); //--- 附加控件将在此处创建 //--- 成功 return(true); }
2. 创建附加控件。
说点略显抒情的题外话。当然也可以将创建所有附加控件的代码直接插入 Create(…) 方法主体中,但用这种方法,我们就有得到一个臃肿且不可读的 “bulk” 的风险。
因此,我们会将创建过程划分为按方法表示的一个个自包含的部分:
- bool CreateColor(void) – 色板创建,
- bool CreateRed(void) – 创建带有解释性说明的显示元素“红色”,
- bool CreateGreen(void) – 创建带有解释性说明的显示元素“绿色”,
- bool CreateBlue(void) – 创建带有解释性说明的显示元素“蓝色”,
依次由 Create(…) 方法调用上述方法:
//+------------------------------------------------------------------ //| 创建 | //+------------------------------------------------------------------ bool CPanelDialog::Create(const long chart,const string name,const int subwin, const int x1,const int y1,const int x2,const int y2) { //--- 调用父类函数 if(!CAppDialog::Create(chart,name,subwin,x1,y1,x2,y2)) return(false); //--- 创建 附加元素 if(!CreateColor()) return(false); if(!CreateRed()) return(false); if(!CreateGreen()) return(false); if(!CreateBlue()) return(false); //--- 成功 return(true); }
控件的创建
我们不会面面俱到地讲解每一种附加控件的创建,而是精讲 bool CreateBlue(void) 方法。
如下所述:
//+------------------------------------------------------------------ //| 创建“蓝色”控件解释性标题 | //+------------------------------------------------------------------ bool CPanelDialog::CreateBlue(void) { //--- 坐标 int x1=INDENT_LEFT; int y1=INDENT_TOP+2*(EDIT_HEIGHT+CONTROLS_GAP_Y); int x2=x1+EDIT_WIDTH; int y2=y1+EDIT_HEIGHT; //--- 创建标题 if(!m_label_blue.Create(m_chart_id,m_name+"LabelBlue",m_subwin,x1,y1+1,x2,y2)) return(false); if(!m_label_blue.Text("Blue")) return(false); if(!Add(m_label_blue)) return(false); //--- 调整坐标 x1+=LABEL_WIDTH+CONTROLS_GAP_X; x2=x1+EDIT_WIDTH; //--- 创建控件 if(!m_edit_blue.Create(m_chart_id,m_name+"Blue",m_subwin,x1,y1,x2,y2)) return(false); if(!Add(m_edit_blue)) return(false); m_edit_blue.MinValue(BASE_COLOR_MIN); m_edit_blue.MaxValue(BASE_COLOR_MAX); m_edit_blue.Value(m_blue); //--- 成功 return(true); }
有两处细微差别:
- 此控件利用相对坐标创建,即,位移相对于容器(复合元素,控件创建后即添加于此)的左上角设定。
- 创建之后,有必要利用 Add(…) 方法向容器添加该控件。本例中则由对话框充当容器。
更改参数
添加 void SetColor(void) 方法以更改色板的颜色;
为了能够从外部更改参数(基色的色阶),我们会添加三个公共方法:
- void SetRed(const int value) – 更改“红色”色阶,并于指标中显示变化,
- void SetGreen(const int value) – 更改“绿色”色阶,并于指标中显示变化,
- void SetBlue(const int value) – 更改“蓝色”色阶,并于指标中显示变化。
“于指标中显示变化”一语是指:基色色阶的新值显示于对应控件的数值表单中,且色板变换其颜色。
下面是一种方法的代码示范:
//+------------------------------------------------------------------ //| 设置“红”值 | //+------------------------------------------------------------------ void CPanelDialog::SetRed(const int value) { //--- 检查 if(value<0 || value>255) return; //--- 保存 m_red=value; //--- 设置 m_field_red.Text(IntegerToString(value)); //--- 设置面板颜色 SetColor(); }
前面已经讲过:
我们将相应代码添加到原始指标中:
//+------------------------------------------------------------------ //| PanelIndicator.mq5 | //| Copyright 2011, MetaQuotes Software Corp. | //| https://www.mql5.com | //+------------------------------------------------------------------ #property copyright "Copyright 2011, MetaQuotes Software Corp." #property link "https://www.mql5.com" #property version "1.00" #property indicator_separate_window #property indicator_plots 0 #property indicator_buffers 0 #property indicator_minimum 0.0 #property indicator_maximum 0.0 //+------------------------------------------------------------------ //| 包含文件 | //+------------------------------------------------------------------ #include "PanelDialog.mqh" //+------------------------------------------------------------------ //| 全局变量 | //+------------------------------------------------------------------ CPanelDialog ExtDialog; //+------------------------------------------------------------------ //| 初始化 | //+------------------------------------------------------------------ int OnInit() { //--- 创建应用对话框 if(!ExtDialog.Create(0,"Panel Indicator",0,0,0,0,130)) return(-1); //--- 开始应用 if(!ExtDialog.Run()) return(-2); //--- 创建定时器 EventSetTimer(1); //--- 成功 return(0); } //+------------------------------------------------------------------ //| 去初始化 | //+------------------------------------------------------------------ void OnDeinit(const int reason) { //--- 销毁对话框 ExtDialog.Destroy(); //--- 杀掉定时器 EventKillTimer(); } //+------------------------------------------------------------------ //| 迭代 | //+------------------------------------------------------------------ int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) { //--- 改变对话框属性 ExtDialog.SetRed(MathRand()%256); //--- 返回 prev_calculated 数值用于下次调用 return(rates_total); } //+------------------------------------------------------------------ //| 定时器事件处理函数 | //+------------------------------------------------------------------ void OnTimer() { //--- 改变对话框属性 ExtDialog.SetGreen(MathRand()%256); } //+------------------------------------------------------------------ //| 图表事件处理函数 | //+------------------------------------------------------------------ void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { //--- 处理事件 ExtDialog.ChartEvent(id,lparam,dparam,sparam); } //+------------------------------------------------------------------
处理事件
对话框与终端间的整体交互,以及对话框控件之间的交互,均基于事件机制。我们不讲其功能,而只是简单的使用它。
按照惯例,事件可划分为两组:
- 绕过终端事件队列处理的内部事件;
- 通过终端事件队列处理的外部事件。
两种类型的事件,我们都会处理。
内部事件中,只有改变对话框尺寸的事件需要处理。为此,重新加载父类的 OnResize() 方法。如果对话框高度没有变化,我们的方法会很简单;而如果对话框宽度有变化,则我们只能修改色板的宽度:
//+------------------------------------------------------------------ //| 调整处理函数 | //+------------------------------------------------------------------ bool CPanelDialog::OnResize(void) { //--- 调用父类函数 if(!CAppDialog::OnResize()) return(false); //--- 改变颜色面板宽度 m_color.Width(ClientAreaWidth()-(INDENT_RIGHT+LABEL_WIDTH+CONTROLS_GAP_X+EDIT_WIDTH+CONTROLS_GAP_X+INDENT_LEFT)); //--- 成功 return(true); }
外部事件的列表也会被限定于一项 – 更改“蓝色”色阶的事件。对于外部事件处理程序的要求少之又少:该处理程序应为空类型的无参数类方法。
我们来描述一下此事件的处理程序:
//+------------------------------------------------------------------ //| 改变“蓝”水平事件的处理函数 | //+------------------------------------------------------------------ void CPanelDialog::OnChangeBlue(void) { //--- 保存 m_blue=m_edit_blue.Value(); //--- 设置面板颜色 SetColor(); }
看到了吧,没什么难的。
想要我们的对话框处理外部事件,必须重新加载此父类方法:
virtual bool OnEvent(const int id,const long &lparam, const double &dparam,const string &sparam);
现在,有点神秘了。如果您已经在编辑器中打开了 PanelDialog.mqh 文件,您会发现根本没有 OnEvent(…) 方法主体。
不用迷惑 – 真相就是因为已为处理外部事件的描述创建了一组宏(参见“标准库”中的 <Controls/Defines.mqh> 文件)。
我们的事件处理程序如下:
//+------------------------------------------------------------------ //| 处理事件 | //+------------------------------------------------------------------ EVENT_MAP_BEGIN(CPanelDialog) ON_EVENT(ON_CHANGE,m_edit_blue,OnChangeBlue) EVENT_MAP_END(CAppDialog)
乍然看上去,这段伪代码可能不是那么一目了然,实际上就是实现以下功能:
- 从 m_edit_blue 控件接收到 ON_CHANGE 事件时,OnChangeBlue 方法即被调用,并完成该事件的处理(返回 True);
- 收到任何其它事件时,此控件都会将其转送到父类方法。
总结
本文讲述的是利用标准库类创建显示面板的过程。
尽管其中未含超载非必要信息,而且我们也在创建时亦尽可能地覆盖过程中几乎所有独特性,但您仍然不太可能会将此作为已创建指标使用。
您终端的下述目录中,还有更复杂的标配示例:
- Experts/Examples/Controls/
- Indicators/Examples/Panels/ChartPanel/
- Indicators/Examples/Panels/SimplePanel/
本文译自 MetaQuotes Software Corp. 撰写的俄文原文
原文地址: https://www.mql5.com/ru/articles/345
MyFxtop迈投(www.myfxtop.com)-靠谱的外汇跟单社区,免费跟随高手做交易!
免责声明:本文系转载自网络,如有侵犯,请联系我们立即删除,另:本文仅代表作者个人观点,与迈投财经无关。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
著作权归作者所有。
商业转载请联系作者获得授权,非商业转载请注明出处。