图形界面 XI: 集成标准图形库 (统合构建 16)
内容
概述
首篇文章 图形界面 I: 函数库结构的准备 (第 1 章) 详细解释了这个函数库的用处。您可在每章结尾处找到相关文章列表的链接。在那里, 您还可以下载当前开发阶段的完整版本函数库。文件必须位于与存档中相同的目录下。
系列第九部分的第二章, 图形界面 IX: 进度条和线图表控件 (第 2 章), 展示了如何将一个创建折线图的类集成到 函数库。那是一个临时解决方案, 因为函数库的这部分能力严重不足。创建科学图表 (CGraphic 类) 的新版本图形库最近已发布了。这个类的一些函数说明已发表在 可视化!类似于 R 语言 "PLOT (绘图)" 的 MQL5 图形库。创建图形界面的开发中函数库在本次更新中将引入创建图表的新版本控件。不同类型数据的可视化现在更加容易了。
函数库规划的变动
早前, 开发中函数库曾用过为绘图而设计的 CCanvas 类的副本。由于最近全局重构了函数库的代码, 此副本不再需要, 可将其替换为标准库中的原始版本。相对于重构之前在文章 图形界面 XI: 重构函数库代码 (统合构建 14.1) 和 图形界面 XI: 全新粉饰的控件 (统合构建 14.2) 中提到的版本, 新版体积减少了大约 10% 和近乎 40%。
CGraphic 类现在将用于创建图表, 因此, 在 Objects.mqh 文件中包含 Graphic.mqh 文件。由于 CCanvas 类的文件已经在 Graphic.mqh 包含文件其一之内, 所以对于整个函数库它也可用。
//+------------------------------------------------------------------+ //| Objects.mqh | //| 版权所有 2015, MetaQuotes 软件公司 | //| https://www.mql5.com | //+------------------------------------------------------------------+ #include "Enums.mqh" #include "Defines.mqh" #include "Fonts.mqh" #include "Colors.mqh" #include <Graphics\Graphic.mqh> #include <ChartObjects\ChartObjectSubChart.mqh> ...
CLineChart 类已经改名为 CGraph。其内部内容也发生了变化。现在这个类只包含用于管理控件一般属性和状态的方法。
class CGraph : public CElement { public: //--- 图表事件处理程序 virtual void OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam); //--- 移动控件 virtual void Moving(const bool only_visible=true); //--- 管理 virtual void Show(void); virtual void Hide(void); virtual void Reset(void); virtual void Delete(void); //--- 应用最新的变化 virtual void Update(const bool redraw=false); //--- private: //--- 调整大小 void Resize(const int width,const int height); //--- 更改窗口右边缘的宽度 virtual void ChangeWidthByRightWindowSide(void); //--- 更改窗口底部边缘的高度 virtual void ChangeHeightByBottomWindowSide(void); };
图表属性的控制可使用 CGraphic::GetGraphicPointer() 方法, 以便获取指向 CGraphic 类实例的指针:
class CGraph : public CElement { private: //--- 用于创建控件的对象 CGraphic m_graph; //--- public: //--- 返回指向图表的指针 CGraphic *GetGraphicPointer(void) { return(::GetPointer(m_graph)); } };
用于管理图表的坐标轴 (CAxis) 和曲线 (CCurve) 的附加类已经包含在 CGraphic 类中。CColorGenerator 类设计用于生成曲线颜色。所有这些类都包括在 Graphic.mqh 文件所包含的单独文件中:
//+------------------------------------------------------------------+ //| Graphic.mqh | //| 版权所有 2016-2017, MetaQuotes 软件公司 | //| https://www.mql5.com | //+------------------------------------------------------------------+ #include <Arrays\ArrayObj.mqh> #include "Curve.mqh" #include "Axis.mqh" #include "ColorGenerator.mqh" ...
CCanvas 类的文件已包含在 Curve.mqh 文件中, 且从这里它将对于整个函数库可用。
//+------------------------------------------------------------------+ //| Curve.mqh | //| 版权所有 2016-2017, MetaQuotes 软件公司 | //| https://www.mql5.com | //+------------------------------------------------------------------+ #include <Object.mqh> #include <Canvas\Canvas.mqh> ...
上述所有文件和类之间的交互联系如下图所示:
图例. 1. 标准和开发中函数库之间类的交互联系。
因此, 标准库中操纵数组和文件的类在已用到它们的应用程序文件和函数里自动可用。本文将进一步展示几个 MQL 测试应用程序, 以便帮助您了解现有的新功能。
测试图表属性的应用程序
第一个 MQL 测试应用程序将实现一个图形界面, 所含控件用于管理 CGraphic 类型图表的某些属性。一个 CTabs 类型的控件位于窗体的顶部。在此情况下, 它是一组四个选项卡。含有两条随机生成数值曲线的图表将位于选项卡的工作区域下方。
第一个选项卡 (背景) 将具有用来管理以下图表属性的控件:
- 背景颜色。
- 图表的主要文本 (显示在顶部)。
- 图表的辅助文本 (显示在底部)。
- 正文的颜色。
- 辅助文字的颜色。
- 正文字号。
- 辅助文字的字号。
要设置并获取这些属性, CGraphic 类提供了相应的公共方法:
//+------------------------------------------------------------------+ //| 结构 CBackground | //| 用法: 二维图形上的背景 | //+------------------------------------------------------------------+ struct CBackground { uint clr; uint clr_main; uint clr_sub; string main; string sub; int size_main; int size_sub; }; //+------------------------------------------------------------------+ //| 类 CGraphic | //| 用法: 绘制二维图形的类 | //+------------------------------------------------------------------+ class CGraphic { protected: //--- 图形元素 CBackground m_background; // 背景 //--- public: //--- 获取背景属性 uint BackgroundColor(void) const { return(m_background.clr); } uint BackgroundMainColor(void) const { return(m_background.clr_main); } uint BackgroundSubColor(void) const { return(m_background.clr_sub); } string BackgroundMain(void) const { return(m_background.main); } string BackgroundSub(void) const { return(m_background.sub); } int BackgroundMainSize(void) const { return(m_background.size_main); } int BackgroundSubSize(void) const { return(m_background.size_sub); } //--- 设置背景属性 void BackgroundColor(const uint clr) { m_background.clr=clr; } void BackgroundMainColor(const uint clr) { m_background.clr_main=clr; } void BackgroundSubColor(const uint clr) { m_background.clr_sub=clr; } void BackgroundMain(const string main) { m_background.main=main; } void BackgroundSub(const string sub) { m_background.sub=sub; } void BackgroundMainSize(const int size) { m_background.size_main=size; } void BackgroundSubSize(const int size) { m_background.size_sub=size; } };
这是它的样子:
图例. 2. MQL 测试应用程序的第一个选项卡 (背景) 的控件。
第二个选项卡 (缩进和历史) 将包含用于设置以下属性的控件:
- 缩进 (左, 右, 顶, 底)。
- 图例的宽度。
- 图例的字号。
- 图例标记的大小。
- 图表所有元素的公用缩进。
- 图表坐标轴标尺标记的大小。
下面列出的 CGraphic 方法可用于获取和设置这些属性:
//+------------------------------------------------------------------+ //| 结构 CCurveHistory | //| 用法: 二维图形上的历史曲线 | //+------------------------------------------------------------------+ struct CCurveHistory { int name_width; int name_size; int symbol_size; int count_total; int count_points; int count_lines; int count_histogram; int count_custom; }; //+------------------------------------------------------------------+ //| 类 CGraphic | //| 用法: 绘制二维图形的类 | //+------------------------------------------------------------------+ class CGraphic { protected: //--- 图形元素 CCurveHistory m_history; // 历史 //--- public: //--- 获取或是设置缩进 int IndentUp(void) const { return(m_up0); } void IndentUp(const int up) { m_up0=up; } int IndentDown(void) const { return(m_down0); } void IndentDown(const int down) { m_down0=down; } int IndentLeft(void) const { return(m_left0); } void IndentLeft(const int left) { m_left0=left; } int IndentRight(void) const { return(m_right0); } void IndentRight(const int right) { m_right0=right; } //--- 获取或是设置空白 int GapSize(void) const { return(m_gap); } void GapSize(const int size) { m_gap=size; } //--- 获取或是设置主要标记大小 int MajorMarkSize(void) const { return(m_mark_size); } void MajorMarkSize(const int size) { m_mark_size=size; } //--- 获取曲线历史属性 int HistoryNameWidth(void) const { return(m_history.name_width); } int HistoryNameSize(void) const { return(m_history.name_size); } int HistorySymbolSize(void) const { return(m_history.symbol_size); } //--- 设置曲线历史属性 void HistoryNameWidth(const int width) { m_history.name_width=width; } void HistoryNameSize(const int size) { m_history.name_size=size; } void HistorySymbolSize(const int size) { m_history.symbol_size=size; } };
这是 MQL 测试应用程序的图形界面的样子:
图例. 3. MQL 测试应用程序的第二个选项卡 (缩进和历史) 的控件。
第三个选项卡 (网格) 包含用于设置下面列出的网格属性的控件:
- 网格线的颜色。
- 坐标轴零线颜色。
- 网格背景颜色。
- 在网格节点中绘制圆点。
- 圆点半径。
- 圆点颜色。
相应的方法体现在 CGraphic 类中, 以便获取和设置这些属性 (参见下面的代码清单):
//+------------------------------------------------------------------+ //| 结构 CGrid | //| 用法: 二维图形上的网格 | //+------------------------------------------------------------------+ struct CGrid { uint clr_line; uint clr_background; uint clr_circle; uint clr_axis_line; uint clr_frame; int r_circle; bool has_circle; }; //+------------------------------------------------------------------+ //| 类 CGraphic | //| 用法: 绘制二维图形的类 | //+------------------------------------------------------------------+ class CGraphic { protected: //--- 图形元素 CGrid m_grid; // 网格 //--- public: //--- 获取网格属性 uint GridLineColor(void) const { return(m_grid.clr_line); } uint GridAxisLineColor(void) const { return(m_grid.clr_axis_line); } uint GridBackgroundColor(void) const { return(m_grid.clr_background); } int GridCircleRadius(void) const { return(m_grid.r_circle); } uint GridCircleColor(void) const { return(m_grid.clr_circle); } bool GridHasCircle(void) const { return(m_grid.has_circle); } //--- 设置网格属性 void GridLineColor(const uint clr) { m_grid.clr_line=clr; } void GridAxisLineColor(const uint clr) { m_grid.clr_axis_line=clr; } void GridBackgroundColor(const uint clr) { m_grid.clr_background=clr; } void GridCircleRadius(const int r) { m_grid.r_circle=r; } void GridCircleColor(const uint clr) { m_grid.clr_circle=clr; } void GridHasCircle(const bool has) { m_grid.has_circle=has; } };
这是最后的样子:
图例. 4. MQL 测试应用程序的第三个选项卡 (网格) 的控件。
用于管理图表坐标轴属性的控件将放置在第四个选项卡 (坐标轴) 上。位于选项卡工作区左侧的单选按钮允许在某个配置中坐标轴之间进行切换。这些按钮与 坐标轴 选项卡的其它控件通过分隔线分离。
以下是可用于修改的属性:
- 自动缩放。
- 最小坐标轴数值。
- 最大坐标轴数值。
- 坐标轴最小公差值。
- 坐标轴最大公差值。
- 坐标轴数字的大小。
- 坐标轴数字的最大显示长度。
- 坐标轴名称的字号。
- 坐标轴的初始步长值。
- 坐标轴上的数字最大数量。
- 坐标轴名称。
- 坐标轴名称的文字颜色。
下面的列表显示了获取和设置上述属性的 CAxis 类方法的名称:
//+------------------------------------------------------------------+ //| 类 CAxis | //| 用法: 二维图形上创建坐标轴的类 | //+------------------------------------------------------------------+ class CAxis { private: double m_min; double m_max; uint m_clr; string m_name; int m_name_size; int m_values_size; int m_values_width; bool m_auto_scale; double m_default_step; // 默认步长的长度 double m_max_labels; // 最大标记数量 double m_min_grace; // 应用于最小数据范围的 "grace" 值 double m_max_grace; // 应用于最大数据范围的 "grace" 值 //--- public: CAxis(void); ~CAxis(void); //--- 属性 double Min(void) const { return(m_min); } void Min(const double min) { m_min=min; } double Max(void) const { return(m_max); } void Max(const double max) { m_max=max; } string Name(void) const { return(m_name); } void Name(const string name) { m_name=name; } //--- 默认属性 uint Color(void) const { return(m_clr); } void Color(const uint clr) { m_clr=clr; } bool AutoScale(void) const { return(m_auto_scale); } void AutoScale(const bool auto) { m_auto_scale=auto; } int ValuesSize(void) const { return(m_values_size); } void ValuesSize(const int size) { m_values_size=size; } int ValuesWidth(void) const { return(m_values_width); } void ValuesWidth(const int width) { m_values_width=width; } int NameSize(void) const { return(m_name_size); } void NameSize(const int size) { m_name_size=size; } double DefaultStep(void) const { return(m_default_step); } void DefaultStep(const double value) { m_default_step=value; } double MaxLabels(void) const { return(m_max_labels); } void MaxLabels(const double value) { m_max_labels=value; } double MinGrace(void) const { return(m_min_grace); } void MinGrace(const double value) { m_min_grace=value; } double MaxGrace(void) const { return(m_max_grace); } void MaxGrace(const double value) { m_max_grace=value; } };
结果如下所示:
图例. 5. MQL 测试应用程序的第四个选项卡 (坐标轴) 的控件。
文章中列出的测试应用程序可从以下链接进行下载, 以供进一步学习。
测试图表曲线属性的应用程序
已经编写了一个单独的 MQL 应用程序来测试图表上 CGraphic 类型曲线的某些属性。管理图表曲线属性的控件将位于此应用程序窗体的顶部, 在它之下有两个 CGraphic 类型 (CGraph 控件) 的图表。第一个图表将显示随机数据的序列, 第二个将绘制其导数, 它们是基于 动量 指标的公式计算的, 如示例。
此处是用于管理图表曲线属性的控件:
- 动画 复选框 - 开始数据自动输入到图表。
- 数组大小 旋转编辑框 – 数据数组中显示在图表上的当前元素数量。
- 随机 按钮 – 在图表上顺序生成随机数据序列。
- 周期 旋转编辑框 – 用来计算 动量 指标的变量值。
- 曲线类型 组合框 – 图表上的曲线类型。
- 圆点类型 组合框 – 用来在图表上绘制曲线的数据圆点类型。
应用程序的自定义类 (CProgram) 实现了上述控件相关的方法, 并执行以下任务:
- 设置要在图表上显示的数据数组的大小。
- 用数据初始化数组。
- 刷新图表以反映最近的变化。
- 将一个元素添加到数组的末尾。
- 删除数组末尾的一个元素。
- 依据计时器刷新图表。
- 自动输入新数据的动画图表。
以下是实现这些功能的所有方法的代码清单。下载文章末尾的文件, 以便找到有关这些方法代码的更多细节。
class CProgram : public CWndEvents { protected: //--- 用于在图表上输出的数据数组 double data1[]; double data2[]; //--- double data3[]; double data4[]; //--- private: //--- 调整数组的大小 void ResizeGraph1Arrays(void); void ResizeGraph2Arrays(void); void ResizeGraph1Arrays(const int new_size); void ResizeGraph2Arrays(const int new_size); //--- 数组初始化 void InitGraph1Arrays(void); void InitGraph2Arrays(void); //--- 数组清零 void ZeroGraph1Arrays(void); void ZeroGraph2Arrays(void); //--- 在指定的索引处设置随机值 void SetGraph1Value(const int index); void SetGraph2Value(const int index); //--- 更新图表上的序列 void UpdateGraph(void); void UpdateGraph1(void); void UpdateGraph2(void); //--- 重新计算图表上的序列 void RecalculatingSeries(void); //--- 添加一个或多个值到数组的末尾 void AddValue(void); //--- 删除数组末尾的一个值 void DeleteValue(void); //--- 按计时器刷新图表 void UpdateGraphByTimer(void); //--- 图表序列动画 void AnimateGraphSeries(void); };
这是它的样子:
图例. 6. 用于测试图表曲线属性的应用程序图形界面。
文章中列出的测试应用程序可从以下链接进行下载, 以供进一步学习。
动态内摆线图表应用程序
在 John Walkenbach 的一本关于 VBA 编程的 Microsoft Excel 书中, 他为读者提供了带有测试文件的光盘。其中一个文件实现了一个示意图, 其中生成了无限数量的内摆线。
参考: 维基百科给出以下定义:
一条 内摆线 (来自希腊语 ὑπό — 下边, 低于和 κύκλος — 圆, 圆圈) 是由在较大圆圈内滚动的小圆上的固定点的迹线产生的特殊平面曲线。
John Walkenbach 在他的书中给出的定义:
让我们在 MQL 中实现类似的应用, 并添加一个用于管理参数的图形界面。我们来看看它是如何工作的细节。
三个参数用于生成一个新的内摆线, 按照指定步长初始化数字序列。然后根据这些序列中的值进行计算, 以获得图表上的点坐标。之后, 将所得结果常规化。
在自定义类中, 我们声明多个数组来计算序列和字段, 并计算平均值和标准偏差。
//+------------------------------------------------------------------+ //| Program.mqh | //| 版权所有 2017, MetaQuotes 软件公司 | //| https://www.mql5.com | //+------------------------------------------------------------------+ #include <Math\Stat\Stat.mqh> #include <EasyAndFastGUI\WndEvents.mqh> #include <EasyAndFastGUI\TimeCounter.mqh> //+------------------------------------------------------------------+ //| 创建应用程序的类 | //+------------------------------------------------------------------+ class CProgram : public CWndEvents { protected: ... //--- 用于计算的数据数组 double a_inc[]; double b_inc[]; double t_inc[]; double x_source[]; double y_source[]; //--- 用于输出到图表的数据数组 double x_norm[]; double y_norm[]; //--- 计算平均值和标准差 double x_mean; double y_mean; double x_sdev; double y_sdev; ... }; //+------------------------------------------------------------------+ //| 构造器 | //+------------------------------------------------------------------+ CProgram::CProgram(void) : x_mean(0), y_mean(0), x_sdev(0), y_sdev(0) { ... }
这些值将在 CProgram::InitArrays() 方法里计算。在此, 第一个循环计算初始数据。然后获得平均值和标准偏差, 第二个循环对数据进行常规化。设置数组大小则使用 CProgram::ResizeArrays() 方法。数组大小的值取自应用程序图形界面的文本框控件 (CTextEdit)。
class CProgram : public CWndEvents { private: //--- 调整数组的大小 void ResizeArrays(void); //--- 初始化辅助数组进行计算 void InitArrays(void); }; //+------------------------------------------------------------------+ //| 调整数组大小 | //+------------------------------------------------------------------+ void CProgram::ResizeArrays(void) { int array_size =::ArraySize(x_norm); int new_size =(int)m_array_size.GetValue(); //--- 如果大小没有改变, 离开 if(array_size==new_size) return; //--- 设置新的大小 ::ArrayResize(a_inc,new_size); ::ArrayResize(b_inc,new_size); ::ArrayResize(t_inc,new_size); ::ArrayResize(x_source,new_size); ::ArrayResize(y_source,new_size); ::ArrayResize(x_norm,new_size); ::ArrayResize(y_norm,new_size); } //+------------------------------------------------------------------+ //| 数组初始化 | //+------------------------------------------------------------------+ void CProgram::InitArrays(void) { //--- 调整数组的大小 ResizeArrays(); //--- 使用公式计算值 int total=(int)m_array_size.GetValue(); for(int i=0; i<total; i++) { if(i<1) { a_inc[i] =1+(double)m_animate.GetValue(); b_inc[i] =1+(double)m_animate.GetValue(); t_inc[i] =1+(double)m_animate.GetValue(); } else { a_inc[i] =a_inc[i-1]+(double)m_a_inc.GetValue(); b_inc[i] =b_inc[i-1]+(double)m_b_inc.GetValue(); t_inc[i] =t_inc[i-1]+(double)m_t_inc.GetValue(); } //--- double a=a_inc[i]; double b=b_inc[i]; double t=t_inc[i]; //--- x_source[i] =(a-b)*cos(t)+b*cos((a/b-1)*t); y_source[i] =(a-b)*sin(t)+b*sin((a/b-1)*t); } //--- 计算平均值 x_mean=MathMean(x_source); y_mean=MathMean(y_source); //--- 计算标准偏差 x_sdev=MathStandardDeviation(x_source); y_sdev=MathStandardDeviation(y_source); //--- 调整以避免除零 x_sdev =(x_sdev==0)? 1 : x_sdev; y_sdev =(y_sdev==0)? 1 : y_sdev; //--- 常规化数据 for(int i=0; i<total; i++) { x_norm[i] =(x_source[i]-x_mean)/x_sdev; y_norm[i] =(y_source[i]-y_mean)/y_sdev; } }
CGraphic 类包含的方法, 允许在已创建的图表工作区域内向坐标轴标尺上添加额外缺口, 线和文本。
在此情况下, CProgram::TextAdd() 方法将在示意图的左上角输出 X 和 Y 序列的平均值和标准偏差。CGraphic::ScaleX() 和 CGraphic::ScaleY() 方法用于获取示意图的极值点坐标 (左上角)。它们设计用于将实际图表值变换为像素坐标。此处, X 轴当中的最小值和 Y 轴当中的最大值 作为真实数值。
class CProgram : public CWndEvents { private: //--- 向图表添加文本 void TextAdd(void); }; //+------------------------------------------------------------------+ //| 向图表添加文本 | //+------------------------------------------------------------------+ void CProgram::TextAdd(void) { //--- 获取指向图表的指针 CGraphic *graph=m_graph1.GetGraphicPointer(); //--- int x =graph.ScaleX(graph.XAxis().Min())+50; int y =graph.ScaleY(graph.YAxis().Max())+10; int y2 =y+20; uint clr =::ColorToARGB(clrBlack); uint align =TA_RIGHT; //--- string str[8]; str[0] ="x mean:"; str[1] ="y mean:"; str[2] =::DoubleToString(x_mean,2); str[3] =::DoubleToString(y_mean,2); str[4] ="x sdev:"; str[5] ="y sdev:"; str[6] =::DoubleToString(x_sdev,2); str[7] =::DoubleToString(y_sdev,2); //--- Calculate the coordinates and output the text on the chart int l_x=0,l_y=0; for(int i=0; i<8; i++) { if(i<2) l_x=x; else if(i<6) l_x=(i%2==0)? l_x+50 : l_x; else l_x=(i%2==0)? l_x+60 : l_x; //--- l_y=(i%2==0)? y : y2; //--- graph.TextAdd(l_x,l_y,str[i],clr,align); } }
在图表上设置所有必要的数据之后, 需要刷新以反映最新的变化。这是通过 CProgram::UpdateSeries() 方法完成的。此处, 它首先检查图表上是否有序列。如果有, 那么它 设置最后计算的数据。此外, 使用图形界面的控件设置曲线的属性。这里, (1) 线的平滑, (2) 点的类型和 (3) 曲线的类型。应注意的是, 只有在所有其它属性和数据已被设置和渲染之后再将文本应用到图表上。在最靠后的地方, 有必要刷新图表以便看到结果。
class CProgram : public CWndEvents { private: //--- 在图表上设置并更新序列 void UpdateSeries(void); }; //+------------------------------------------------------------------+ //| 在图表上设置并更新序列 | //+------------------------------------------------------------------+ void CProgram::UpdateSeries(void) { //--- 获取指向图表的指针 CGraphic *graph=m_graph1.GetGraphicPointer(); //--- 更新图表的所有序列 int total=graph.CurvesTotal(); if(total>0) { //--- 获取曲线指针 CCurve *curve=graph.CurveGetByIndex(0); //--- 设置数据数组 curve.Update(x_norm,y_norm); //--- 获取曲线属性的值 ENUM_CURVE_TYPE curve_type =(ENUM_CURVE_TYPE)m_curve_type.GetListViewPointer().SelectedItemIndex(); ENUM_POINT_TYPE point_type =(ENUM_POINT_TYPE)m_point_type.GetListViewPointer().SelectedItemIndex(); //--- 设置属性 curve.LinesSmooth(m_line_smooth.IsPressed()); curve.PointsType(point_type); curve.Type(curve_type); } //--- 应用 graph.Redraw(true); //--- 输出文本 TextAdd(); //--- 刷新图表 graph.Update(); }
CProgram::RecalculatingSeries() 方法用于在单次调用中计算并应用获得的结果:
class CProgram : public CWndEvents { private: //--- 重新计算图表上的序列 void RecalculatingSeries(void); }; //+------------------------------------------------------------------+ //| 重新计算图表上的序列 | //+------------------------------------------------------------------+ void CProgram::RecalculatingSeries(void) { //--- 计算值并初始化数组 InitArrays(); //--- 更新序列 UpdateSeries(); }
基于这些公式绘制的示意图如果变成动画, 那么看上去会更加有趣, 。为了将计算的序列设置为动感, 需要改变这些序列的初始值。这可以通过旋转编辑框输入值, 或以自动模式运行进程来实现。在自动模式下, 该编辑框中的值在 CProgram::AnimateGraphSeries() 方法里递增或递减。该方法是在 CProgram::UpdateGraphByTimer() 方法中被调用, 而后者是在应用程序的定时器中调用。
class CProgram : public CWndEvents { private: //--- 按计时器刷新图表 void UpdateGraphByTimer(void); //--- 图表序列动画 void AnimateGraphSeries(void); }; //+------------------------------------------------------------------+ //| 定时器 | //+------------------------------------------------------------------+ void CProgram::OnTimerEvent(void) { CWndEvents::OnTimerEvent(); //--- 按计时器刷新图表 if(m_counter1.CheckTimeCounter()) { UpdateGraphByTimer(); } ... } //+------------------------------------------------------------------+ //| 按计时器刷新图表 | //+------------------------------------------------------------------+ void CProgram::UpdateGraphByTimer(void) { //--- 如果 (1) 窗体最小化或 (2) 动画被禁用, 离开 if(m_window.IsMinimized() || !m_animate.IsPressed()) return; //--- 图表序列动画 AnimateGraphSeries(); //--- 更新图表上的数组和序列 RecalculatingSeries(); } //+------------------------------------------------------------------+ //| 将图表序列变为动画 | //+------------------------------------------------------------------+ void CProgram::AnimateGraphSeries(void) { //--- 指定调整数组大小的方向 static bool counter_direction=false; //--- 如果达到最小值, 则切换方向 if((double)m_animate.GetValue()<=(double)m_animate.MinValue()) counter_direction=false; //--- 如果达到最大值, 则切换方向 if((double)m_animate.GetValue()>=(double)m_animate.MaxValue()) counter_direction=true; //--- 按指定的方向调整数组的大小 string value=""; if(!counter_direction) value=string((double)m_animate.GetValue()+m_animate.StepValue()); else value=string((double)m_animate.GetValue()-m_animate.StepValue()); //--- 设置新值并更新文本框 m_animate.SetValue(value,false); m_animate.GetTextBoxPointer().Update(true); }
得到的结果如下所示:
图例. 7. 动态内摆线演示。
文章中列出的测试应用程序可从以下链接进行下载, 以供进一步学习。
来自前次更新的测试应用程序新版本
在文章 图形界面 IX: 进度条和折线图表控件 (第 2 章) 中的测试应用程序已根据此次更新的变化进行了更新。
更新后的 MQL 应用程序新版本的图形界面如下所示:
图例. 8. 来自前次更新的测试应用程序新版本。
文章中列出的测试应用程序可从以下链接进行下载, 以供进一步学习。
结束语
在本文中, 绘制科学图表的标准库的一部分已经集成到开发中的用于创建图形界面的函数库里。所有演示的示例可以从本文附带的文件中下载, 以供更深入地学习源代码。
当前开发阶段的函数库如下图所示:
图例. 9. 处于当前开发阶段的函数库结构
所提供的函数库代码是免费的。您可以在您的项目中使用它, 包括商业项目, 撰写文章和履行订单。
本文由MetaQuotes Ltd译自俄文
原文地址: https://www.mql5.com/ru/articles/3527
thank you!so good GUI!
I am sorry!
Element.mqh have some errors!
can you correct them! thank you!@Anatoli Kazharski
class CWindow; //have no body
bool CElement::CheckMainPointer(void)
{
//--- Если нет указателя
if(::CheckPointer(m_main)==POINTER_INVALID)
{
//--- Вывести сообщение в журнал терминала
::Print(__FUNCTION__,
" > Перед созданием элемента... \n...нужно передать указатель на главный элемент: "+
ClassName()+"::MainPointer(CElementBase &object)");
//--- Прервать построение графического интерфейса приложения
return(false);
}
//--- Сохранение указателя на форму
m_wnd=m_main.WindowPointer();
//--- Если нет указателя на форму
if(::CheckPointer(m_wnd)==POINTER_INVALID)
{
//--- Вывести сообщение в журнал терминала
::Print(__FUNCTION__,
" > У элемента "+ClassName()+" нет указателя на форму!...\n"+
"...Элементы должны создаваться в порядке своей вложенности!");
//--- Прервать построение графического интерфейса приложения
return(false);
}
//--- Сохранение указателя на курсор мыши
m_mouse=m_main.MousePointer();
//--- Сохранение свойств
m_id =m_wnd.LastId()+1; // 'LastId' - undeclared identifier Element.mqh 842 22
m_chart_id =m_wnd.ChartId(); // 'ChartId' - undeclared identifier Element.mqh 843 22
m_subwin =m_wnd.SubwindowNumber(); // 'SubwindowNumber' - undeclared identifier Element.mqh 844 22
m_corner =(ENUM_BASE_CORNER)m_wnd.Corner(); // 'Corner' - undeclared identifier Element.mqh 845 40
m_anchor =(ENUM_ANCHOR_POINT)m_wnd.Anchor(); // 'Anchor' - undeclared identifier Element.mqh 846 41
//--- Отправить признак наличия указателя
return(true);
}