请 [注册] 或 [登录]  | 返回主站

量化交易吧 /  量化策略 帖子:3353276 新帖:15

图形界面 X: 文本编辑框, 图片滑块和简单控件 (构建 5)

SCSDV_d发表于:4 月 17 日 15:39回复(1)

内容

  • 概论
  • 文本编辑框控件
  • 创建文本编辑框控件的类
  • 图片滑块控件
  • 创建图片滑块控件的类
  • 文本标签和图片控件
  • 处理字体的 CFonts 类
  • 其它的函数库更新清单
  • 测试控件的应用程序
  • 结论

概论

为了更加理解此函数库的目的, 请阅读首篇文章: 图形界面 I: 准备函数库结构 (第 1 章)。您在每一章节的末尾可找到文章清单的链接。您也可以从那里下载当前正在开发阶段的函数库完整版。这些文件必须按照与存档相同的目录存放。

本文研究新的控件: 文本编辑框, 图片滑块, 以及其它的简单控件: 文本标签和图片, 这些在各种情况下都很有用。函数库正在持续增长, 并引入了一些其它的新控件, 以前创建的那些也有所改进。由于函数库目前被许多用户使用, 从他们那里收到了许多评论和建议。这些请求的大部分已在新版函数库中实现。外加, 某些算法已经优化。这将降低 CPU 负载。有关这方面的更多细节会在文章中进一步阐述。

 

文本编辑框控件

迄今为止, 已开发的函数库包含了一个编辑框控件 (CSpinEdit 类), 但它只用于输入数值。现在函数库将补充另一个控件, 它将允许输入任何文本到字段。在不同的情况下也许都需要文本编辑框控件。例如, 可以在终端 "沙盒" 的文件中创建字符串搜索。另一个选项是为 MQL 应用程序的最终用户提供输入交易品种数组的能力。简言之, 它可以是任何处理所需的文本数据。

我们来枚举文本编辑框控件的所有组件:

  1. 背景
  2. 图标
  3. 描述
  4. 编辑框

 

图例. 1. 文本编辑框控件组件。


我们来就近看看这个控件的类。

 

创建文本编辑框控件的类

利用 CTextEdit 类创建 TextEdit.mqh 文件, 其标准方法可用于所有控件, 且已包含在函数库引擎 (WndContainer.mqh 文件)。以下是供用户自定义控件的属性:

  • 控件背景色
  • 控件激活或是阻塞状态的图标
  • 图标沿两条数轴 (x, y) 的边距
  • 控件的描述文本
  • 文本标签沿两条数轴 (x, y) 的边距
  • 控件不同状态的文本颜色
  • 编辑框大小
  • 文本编辑框沿两条数轴 (x, y) 的边距
  • 文本编辑框和文本在不同状态的颜色
  • 编辑框中的文本对齐 (左/右/中心)
  • 文本选择光标的显示模式
  • 在编辑框中重置数值的模式
//+------------------------------------------------------------------+
//| 创建文本编辑框的类                                                                |
//+------------------------------------------------------------------+
class CTextEdit : public CElement
  {
private:
   //--- 控件背景色
   color             m_area_color;
   //--- 控件激活或是阻塞状态的图标
   string            m_icon_file_on;
   string            m_icon_file_off;
   //--- 图标边距
   int               m_icon_x_gap;
   int               m_icon_y_gap;
   //--- 编辑框描述文本
   string            m_label_text;
   //--- 文本标签边距
   int               m_label_x_gap;
   int               m_label_y_gap;
   //--- 不同状态的文本颜色
   color             m_label_color;
   color             m_label_color_hover;
   color             m_label_color_locked;
   color             m_label_color_array[];
   //--- 编辑框当前数值
   string            m_edit_value;
   //--- 编辑框大小
   int               m_edit_x_size;
   int               m_edit_y_size;
   //--- 编辑框边距
   int               m_edit_x_gap;
   int               m_edit_y_gap;
   //--- 不同状态的编辑框颜色和文本
   color             m_edit_color;
   color             m_edit_color_locked;
   color             m_edit_text_color;
   color             m_edit_text_color_locked;
   color             m_edit_text_color_highlight;
   //--- 不同状态的编辑框边框颜色
   color             m_edit_border_color;
   color             m_edit_border_color_hover;
   color             m_edit_border_color_locked;
   color             m_edit_border_color_array[];
   //--- 重置数值的模式 (空字符串)
   bool              m_reset_mode;
   //--- 文本选择指针的显示模式
   bool              m_show_text_pointer_mode;
   //--- 文本对齐模式
   ENUM_ALIGN_MODE   m_align_mode;
   //---
public:
   //--- 图标边距
   void              IconXGap(const int x_gap)                      { m_icon_x_gap=x_gap;                 }
   void              IconYGap(const int y_gap)                      { m_icon_y_gap=y_gap;                 }
   //--- (1) 背景色, (2) 编辑框描述文本, (3) 文本标签边距
   void              AreaColor(const color clr)                     { m_area_color=clr;                   }
   string            LabelText(void)                          const { return(m_label.Description());      }
   void              LabelText(const string text)                   { m_label.Description(text);          }
   void              LabelXGap(const int x_gap)                     { m_label_x_gap=x_gap;                }
   void              LabelYGap(const int y_gap)                     { m_label_y_gap=y_gap;                }
   //--- 不同状态的文本标签颜色
   void              LabelColor(const color clr)                    { m_label_color=clr;                  }
   void              LabelColorHover(const color clr)               { m_label_color_hover=clr;            }
   void              LabelColorLocked(const color clr)              { m_label_color_locked=clr;           }
   //--- (1) 编辑框大小, (2) 编辑框距右侧距离
   void              EditXSize(const int x_size)                    { m_edit_x_size=x_size;               }
   void              EditYSize(const int y_size)                    { m_edit_y_size=y_size;               }
   //--- 编辑框边距
   void              EditXGap(const int x_gap)                      { m_edit_x_gap=x_gap;                 }
   void              EditYGap(const int y_gap)                      { m_edit_y_gap=y_gap;                 }
   //--- 不同状态的编辑框颜色
   void              EditColor(const color clr)                     { m_edit_color=clr;                   }
   void              EditColorLocked(const color clr)               { m_edit_color_locked=clr;            }
   //--- 不同状态的编辑框文本颜色
   void              EditTextColor(const color clr)                 { m_edit_text_color=clr;              }
   void              EditTextColorLocked(const color clr)           { m_edit_text_color_locked=clr;       }
   void              EditTextColorHighlight(const color clr)        { m_edit_text_color_highlight=clr;    }
   //--- 不同状态的编辑框边框颜色
   void              EditBorderColor(const color clr)               { m_edit_border_color=clr;            }
   void              EditBorderColorHover(const color clr)          { m_edit_border_color_hover=clr;      }
   void              EditBorderColorLocked(const color clr)         { m_edit_border_color_locked=clr;     }
   //--- (1) 处理文本标签时重置模式, (2) 文本选择指针的显示模式
   bool              ResetMode(void)                                { return(m_reset_mode);               }
   void              ResetMode(const bool mode)                     { m_reset_mode=mode;                  }
   void              ShowTextPointerMode(const bool mode)           { m_show_text_pointer_mode=mode;      }
   //--- 文本对齐模式
   void              AlignMode(ENUM_ALIGN_MODE mode)                { m_align_mode=mode;                  }
   //--- 设置按钮激活或是阻塞状态的图标
   void              IconFileOn(const string file_path);
   void              IconFileOff(const string file_path);
  };

文本选择指针的显示模式意味着当鼠标光标悬浮在编辑框上时, 将用一个附加图标作为鼠标光标的补充, 表示可以在那里输入文本。为此作用, 已在 ENUM_MOUSE_POINTER 枚举中多加了一个标识符 (MP_TEXT_SELECT)。

//+------------------------------------------------------------------+
//| 指针类型的枚举 |
//+------------------------------------------------------------------+
enum ENUM_MOUSE_POINTER
  {
   MP_CUSTOM      =0,
   MP_X_RESIZE    =1,
   MP_Y_RESIZE    =2,
   MP_XY1_RESIZE  =3,
   MP_XY2_RESIZE  =4,
   MP_X_SCROLL    =5,
   MP_Y_SCROLL    =6,
   MP_TEXT_SELECT =7
  };

CPointer 类中也相应地添加 (参阅以下代码)。文本选择时光标图标的图像也在文章末尾的存档里提供。 

//+------------------------------------------------------------------+
//|                                                      Pointer.mqh |
//|                        版权所有 2015, MetaQuotes 软件公司|
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#include "Element.mqh"
//--- 资源
...
#resource "\\Images\\EasyAndFastGUI\\Controls\\pointer_text_select.bmp"

//+------------------------------------------------------------------+
//| 基于光标类型设置光标图标                                                     |
//+------------------------------------------------------------------+
void CPointer::SetPointerBmp(void)
  {
   switch(m_type)
     {
      case MP_X_RESIZE :
         m_file_on  ="Images\\EasyAndFastGUI\\Controls\\pointer_x_rs_blue.bmp";
         m_file_off ="Images\\EasyAndFastGUI\\Controls\\pointer_x_rs.bmp";
         break;
      case MP_Y_RESIZE :
         m_file_on  ="Images\\EasyAndFastGUI\\Controls\\pointer_y_rs_blue.bmp";
         m_file_off ="Images\\EasyAndFastGUI\\Controls\\pointer_y_rs.bmp";
         break;
      case MP_XY1_RESIZE :
         m_file_on  ="Images\\EasyAndFastGUI\\Controls\\pointer_xy1_rs_blue.bmp";
         m_file_off ="Images\\EasyAndFastGUI\\Controls\\pointer_xy1_rs.bmp";
         break;
      case MP_XY2_RESIZE :
         m_file_on  ="Images\\EasyAndFastGUI\\Controls\\pointer_xy2_rs_blue.bmp";
         m_file_off ="Images\\EasyAndFastGUI\\Controls\\pointer_xy2_rs.bmp";
         break;
      case MP_X_SCROLL :
         m_file_on  ="Images\\EasyAndFastGUI\\Controls\\pointer_x_scroll_blue.bmp";
         m_file_off ="Images\\EasyAndFastGUI\\Controls\\pointer_x_scroll.bmp";
         break;
      case MP_Y_SCROLL :
         m_file_on  ="Images\\EasyAndFastGUI\\Controls\\pointer_y_scroll_blue.bmp";
         m_file_off ="Images\\EasyAndFastGUI\\Controls\\pointer_y_scroll.bmp";
         break;
      case MP_TEXT_SELECT :
         m_file_on  ="Images\\EasyAndFastGUI\\Controls\\pointer_text_select.bmp";
         m_file_off ="Images\\EasyAndFastGUI\\Controls\\pointer_text_select.bmp";
         break;

     }
//--- 如果已指定光标类型 (MP_CUSTOM)
   if(m_file_on=="" || m_file_off=="")
      ::Print(__FUNCTION__," > 必须为光标设置两个图标!");
  }

为了创建文本编辑框控件, 需要五个 private 和一个 public 方法: 

class CTextEdit : public CElement
  {
private:
   //--- 创建编辑框的对象
   CRectLabel        m_area;
   CBmpLabel         m_icon;
   CLabel            m_label;
   CEdit             m_edit;
   CPointer          m_text_select;
   //---
public:
   //--- 创建文本编辑框的方法
   bool              CreateTextEdit(const long chart_id,const int subwin,const string label_text,const int x,const int y);
   //---
private:
   bool              CreateArea(void);
   bool              CreateIcon(void);
   bool              CreateLabel(void);
   bool              CreateEdit(void);
   bool              CreateTextSelectPointer(void);
  };


CTextEdit 的其余部分不包含任何本系列之前文章未研究的内容。因此, 您可以自行试验它的能力。当前版本的文本编辑框限制在 63 个字符。 

 

图片滑块控件

图片滑块属于图形界面的信息控件。当创建快速参考指南时非常有用, 其中插图在价格图表上显示某些状况, 或有关所用 MQL 应用程序中特定图形界面控件用途的简要说明。 

我们来枚举图片滑块控件的所有组件:

  1. 背景
  2. 滑块箭头按钮
  3. 单选按钮组
  4. 与单选按钮组相关联的图像组


 

图例. 2. 图片滑块控件的组件。

 

创建图片滑块控件的类

利用其它类中存在的标准方法创建 PicturesSlider.mqh 文件, 并将其包含在 WndContainer.mqh 文件中。以下是可由函数库用户自定义的控件属性。

  • 控件背景色
  • 控件背景框颜色
  • 图片沿 Y 轴的边距
  • 滑块箭头按钮沿两个轴 (x, y) 的边距
  • 单选按钮沿两个轴 (x, y) 的边距
  • 单选按钮之间的边距
//+------------------------------------------------------------------+
//| 创建图片滑块类                                                                        |
//+------------------------------------------------------------------+
class CPicturesSlider : public CElement
  {
private:
   //--- 控件背景框颜色
   color             m_area_color;
   color             m_area_border_color;
   //--- 图片沿 Y 轴的边距
   int               m_pictures_y_gap;
   //--- 按钮边距
   int               m_arrows_x_gap;
   int               m_arrows_y_gap;
   //--- 单选按钮边距
   int               m_radio_buttons_x_gap;
   int               m_radio_buttons_y_gap;
   int               m_radio_buttons_x_offset;
   //---
public:
   //--- (1) 背景以及 (2) 背景框颜色
   void              AreaColor(const color clr)              { m_area_color=clr;                      }
   void              AreaBorderColor(const color clr)        { m_area_border_color=clr;               }
   //--- 箭头按钮边距
   void              ArrowsXGap(const int x_gap)             { m_arrows_x_gap=x_gap;                  }
   void              ArrowsYGap(const int y_gap)             { m_arrows_y_gap=y_gap;                  }
   //--- 图片沿 Y 轴边距
   void              PictureYGap(const int y_gap)            { m_pictures_y_gap=y_gap;                }
   //--- (1) 单选按钮边距, (2) 单选按钮之间的距离
   void              RadioButtonsXGap(const int x_gap)       { m_radio_buttons_x_gap=x_gap;           }
   void              RadioButtonsYGap(const int y_gap)       { m_radio_buttons_y_gap=y_gap;           }
   void              RadioButtonsXOffset(const int x_offset) { m_radio_buttons_x_offset=x_offset;     }
  };

 为了创建图片滑块控件, 需要五个 private 和一个 public 方法:

class CPicturesSlider : public CElement
  {
private:
   //--- 创建控件的对象
   CRectLabel        m_area;
   CBmpLabel         m_pictures[];
   CRadioButtons     m_radio_buttons;
   CIconButton       m_left_arrow;
   CIconButton       m_right_arrow;
   //---
public:
   //--- 创建图片滑块方法
   bool              CreatePicturesSlider(const long chart_id,const int subwin,const int x,const int y);
   //---
private:
   bool              CreateArea(void);
   bool              CreatePictures(void);
   bool              CreateRadioButtons(void);
   bool              CreateLeftArrow(void);
   bool              CreateRightArrow(void);
  };

控件的宽度将会依照用户定义的参数自动计算。这些参数包括单选按钮组距控件左边缘的边距, 相对于此的图片滑块右箭头按钮的坐标也一并计算。控件高度取决于图片的尺寸。假定图像尺寸相同, 因此计算时使用组中第一副图像的大小。

调用创建控件的主方法之前, 必须使用 CPicturesSlider::AddPicture() 方法将图片加入数组。方法唯一的参数就是图片路径, 若未设置的话将采用 省缺 路径。  

//+------------------------------------------------------------------+
//|                                               PicturesSlider.mqh |
//|                        版权所有 2016, MetaQuotes 软件公司|
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
...
//--- 省缺图片
#resource "\\Images\\EasyAndFastGUI\\Icons\\bmp64\\no_image.bmp"
//+------------------------------------------------------------------+
//| 创建图片滑块类                                                                         |
//+------------------------------------------------------------------+
class CPicturesSlider : public CElement
  {
private:
   //--- 图片数组 (图片路径)
   string            m_file_path[];
   //--- 图片的省缺路径
   string            m_default_path;
   //---
public:
   //--- 添加图片
   void              AddPicture(const string file_path="");
  };
//+------------------------------------------------------------------+
//| 构造器                                                                                        |
//+------------------------------------------------------------------+
CPicturesSlider::CPicturesSlider(void) : m_default_path("Images\\EasyAndFastGUI\\Icons\\bmp64\\no_image.bmp"),
                                         m_area_color(clrNONE),
                                         m_area_border_color(clrNONE),
                                         m_arrows_x_gap(2),
                                         m_arrows_y_gap(2),
                                         m_radio_button_width(12),
                                         m_radio_buttons_x_gap(25),
                                         m_radio_buttons_y_gap(1),
                                         m_radio_buttons_x_offset(20),
                                         m_pictures_y_gap(25)
  {
//--- 在基类中存储控件的名称
   CElement::ClassName(CLASS_NAME);
//--- 设置鼠标左键点击优先权
   m_zorder=0;
  }
//+------------------------------------------------------------------+
//| 添加图片                                                                                    |
//+------------------------------------------------------------------+
void CPicturesSlider::AddPicture(const string file_path="")
  {
//--- 为数组增加一个元素的尺寸
   int array_size=::ArraySize(m_pictures);
   int new_size=array_size+1;
   ::ArrayResize(m_pictures,new_size);
   ::ArrayResize(m_file_path,new_size);
//--- 保存传递参数的值
   m_file_path[array_size]=(file_path=="")?m_default_path : file_path;
  }

若要显示来自组中的图片, 使用 CPicturesSlider::SelectPicture() 方法。此方法会在处理箭头按钮和单选按钮的处理器类 CPicturesSlider 中调用。  

class CPicturesSlider : public CElement
  {
public:
   //--- 切换指定索引处的图片
   void              SelectPicture(const uint index);
  };
//+------------------------------------------------------------------+
//| 指定要显示的图片                                                                     |
//+------------------------------------------------------------------+
void CPicturesSlider::SelectPicture(const uint index)
  {
//--- 获取图片数量
   uint pictures_total=PicturesTotal();
//--- 如果组内无图片, 报告
   if(pictures_total<1)
     {
      ::Print(__FUNCTION__," > 方法被调用, "
              "如果组内至少包含一副图片!使用 CPicturesSlider::AddPicture() 方法");
      return;
     }
//--- 如果超出数组范围, 调整索引
   uint correct_index=(index>=pictures_total)?pictures_total-1 : index;
//--- 在此索引处选择单选按钮
   m_radio_buttons.SelectRadioButton(correct_index);
//--- 切换图片
   for(uint i=0; i<pictures_total; i++)
     {
      if(i==correct_index)
         m_pictures[i].Timeframes(OBJ_ALL_PERIODS);
      else
         m_pictures[i].Timeframes(OBJ_NO_PERIODS);
     }
  }

当处理箭头按钮时, 控件的事件处理器调用 CPicturesSlider::OnClickLeftArrow() 和 CPicturesSlider::OnClickRightArrow() 方法。下列清单显示了鼠标左键方法的代码。如果必要, 点击图片滑块按钮的事件 可在 MQL 应用程序的自定义类中跟踪。 

class CPicturesSlider : public CElement
  {
public:
private:
   //--- 点击左按钮处理器
   bool              OnClickLeftArrow(const string clicked_object);
   //--- 点击右按钮处理器
   bool              OnClickRightArrow(const string clicked_object);
  };
//+------------------------------------------------------------------+
//| 点击鼠标左键                                                                             |
//+------------------------------------------------------------------+
bool CPicturesSlider::OnClickLeftArrow(const string clicked_object)
  {
//--- 如果无按钮点击, 离开
   if(::StringFind(clicked_object,CElement::ProgramName()+"_icon_button_",0)<0)
      return(false);
//--- 从对象名获取控件标识符
   int id=CElement::IdFromObjectName(clicked_object);
//--- 从对象名获取控件索引
   int index=CElement::IndexFromObjectName(clicked_object);
//--- 如果控件标识符不匹配, 离开
   if(id!=CElement::Id())
      return(false);
//--- 如果控件索引不匹配, 离开
   if(index!=0)
      return(false);
//--- 获取当前选中单选按钮的索引
   int selected_radio_button=m_radio_buttons.SelectedButtonIndex();
//--- 切换图片
   SelectPicture(--selected_radio_button);
//--- 发送有关消息
   ::EventChartCustom(m_chart_id,ON_CLICK_BUTTON,CElement::Id(),CElement::Index(),"");
   return(true);
  }

下列清单显示图片滑块事件处理程序的缩减代码。显然, 此处也跟踪滑块单选按钮的点击事件。可以理解, 点击一个本地组当中的单选按钮使用控件的标识符, 其 等于图片滑块标识符。 

//+------------------------------------------------------------------+
//| 事件处理器                                                                                |
//+------------------------------------------------------------------+
void CPicturesSlider::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
  {
//--- 光标移动事件处理器
...
//--- 单选按钮点击事件处理器
   if(id==CHARTEVENT_CUSTOM+ON_CLICK_LABEL)
     {
      //--- 如果这是滑块的单选按钮, 切换图片
      if(lparam==CElement::Id())
         SelectPicture(m_radio_buttons.SelectedButtonIndex());
      //---
      return;
     }
//--- 在对象上点击鼠标左键处理器
   if(id==CHARTEVENT_OBJECT_CLICK)
     {
      //--- 如果点击滑块的箭头按钮, 切换图片
      if(OnClickLeftArrow(sparam))
         return;
      if(OnClickRightArrow(sparam))
         return;
      //---
      return;
     }
  }

 

文本标签和图片控件

作为补充, 函数库现在包括两个新的 CTextLabelCPicture 类用于创建简单文本标签和图片控件。它们可作为单独对象, 无需将它们与任何其它控件绑定。它们的内容很简单。在 CPicture 类中, 用户仅可改变一个属性 - 图片路径。方法 CPicture::Path() 已为此目的而实现。除非未指定自定义路径, 否则将使用 省缺图片。即使在创建 MQL 应用程序的图形界面之后, 也可以随时以编程方式更改图片。

//+------------------------------------------------------------------+
//|                                                      Picture.mqh |
//|                        版权所有 2016, MetaQuotes 软件公司|
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#include "Element.mqh"
#include "Window.mqh"
//--- 资源
#resource "\\Images\\EasyAndFastGUI\\Icons\\bmp64\\no_image.bmp"
//+------------------------------------------------------------------+
//| 创建图片的类                                                                            |
//+------------------------------------------------------------------+
class CPicture : public CElement
  {
private:
   //--- 图片路径
   string            m_path;
   //---
public:
   //--- 图片路径取值/赋值
   string            Path(void)               const { return(m_path);             }
   void              Path(const string path);
  };
//+------------------------------------------------------------------+
//| 构造器                                                                                        |
//+------------------------------------------------------------------+
CPicture::CPicture(void) : m_path("Images\\EasyAndFastGUI\\Icons\\bmp64\\no_image.bmp")

  {
//--- 在基类中保存控件类的名字
   CElement::ClassName(CLASS_NAME);
//--- 设置鼠标左键点击优先权
   m_zorder=0;
  }
//+------------------------------------------------------------------+
//| 设置图片                                                                                    |
//+------------------------------------------------------------------+
void CPicture::Path(const string path)
  {
   m_path=path;
   m_picture.BmpFileOn("::"+path);
   m_picture.BmpFileOff("::"+path);
  }

对于文本标签控件, 一切都很简单, 只有四个属性可以由用户定义:

  • 标签的文本
  • 文本颜色
  • 字体
  • 字号
//+------------------------------------------------------------------+
//| 创建文本标签的类                                                                    |
//+------------------------------------------------------------------+
class CTextLabel : public CElement
  {
public:
   //--- 标签文本取值/赋值
   string            LabelText(void)             const { return(m_label.Description()); }
   void              LabelText(const string text)      { m_label.Description(text);     }
   //--- 设置文本标签的 (1) 颜色, (2) 字体和 (3) 字号
   void              LabelColor(const color clr)       { m_label.Color(clr);            }
   void              LabelFont(const string font)      { m_label.Font(font);            }
   void              LabelFontSize(const int size)     { m_label.FontSize(size);        }
  };

 

处理字体的 CFonts 类

为了方便字体选择, 已实现了另一个 CFonts 类。它包括 187 种字体。这些是终端的系统字体, 您可能已经在某些图形对象的设置中看到过清单。

 图例. 3. 终端的系统字体。

图例. 3. 终端的系统字体。


字体文件 (Fonts.mqh) 位于 "MetaTrader 5\MQL5\Include\EasyAndFastGUI\Fonts.mqh" 目录。它已经被包含在 Objects.mqh 文件中, 函数库的整个方案当中随处均可完全访问。

//+------------------------------------------------------------------+
//|                                                      Objects.mqh |
//|                        版权所有 2015, MetaQuotes 软件公司|
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#include "Enums.mqh"
#include "Defines.mqh"
#include "..\Fonts.mqh"
#include "..\Canvas\Charts\LineChart.mqh"
#include <ChartObjects\ChartObjectSubChart.mqh>
#include <ChartObjects\ChartObjectsBmpControls.mqh>
#include <ChartObjects\ChartObjectsTxtControls.mqh>

CFonts 仅包括两个公共方法 来获取字体数组大小 以及 按照索引获取字体名称。字体数组 应在类的构造器中初始化。 

//+------------------------------------------------------------------+
//|                                                        Fonts.mqh |
//|                        版权所有 2016, MetaQuotes 软件公司|
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| 操纵字体的类                                                                            |
//+------------------------------------------------------------------+
class CFonts
  {
private:
   //--- 字体数组
   string            m_fonts[];
   //---
public:
                     CFonts(void);
                    ~CFonts(void);
   //--- 返回字体数量
   int               FontsTotal(void) const { return(::ArraySize(m_fonts)); }
   //--- 按照索引返回字体
   string            FontsByIndex(const uint index);
   //---
private:
   //--- 初始化字体数组
   void              InitializeFontsArray(void);
  };
//+------------------------------------------------------------------+
//| 构造器                                                                                         |
//+------------------------------------------------------------------+
CFonts::CFonts(void)
  {
//--- 初始化字体数组
   InitializeFontsArray();
  }
//+------------------------------------------------------------------+
//| 析构器                                                                                         |
//+------------------------------------------------------------------+
CFonts::~CFonts(void)
  {
   ::ArrayFree(m_fonts);
  }

调用 CFonts::FontsByIndex() 方法进行 调整防止超出数组范围:

//+------------------------------------------------------------------+
//| 按照索引返回                                                                            |
//+------------------------------------------------------------------+
string CFonts::FontsByIndex(const uint index)
  {
//--- 数组大小
   uint array_size=FontsTotal();
//--- 范围超出的情况下进行调整
   uint i=(index>=array_size)?array_size-1 : index;
//--- 返回字体
   return(m_fonts[i]);
  }

 

其它的函数库更新清单

1. 修复了对话框中不正确的工具提示显示。现在, 主窗口中工具提示按钮的按下状态通用于图形界面的所有窗口。按下按钮会生成一条带有新 ON_WINDOW_TOOLTIPS 事件标识符的消息 (参见 Defines.mqh 文件)。

//+------------------------------------------------------------------+
//|                                                      Defines.mqh |
//|                        版权所有 2015, MetaQuotes 软件公司|
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
...
#define ON_WINDOW_TOOLTIPS         (29) // 在工具提示按钮上点击

相应地, 方法 OnClickTooltipsButton() 已被添加到 CWindow 类中来处理工具提示按钮: 

//+------------------------------------------------------------------+
//| 创建控件表单的类                                                                     |
//+------------------------------------------------------------------+
class CWindow : public CElement
  {
private:
   //--- 处理点击工具按钮事件
   bool              OnClickTooltipsButton(const string clicked_object);
  };
//+------------------------------------------------------------------+
//| 图表事件处理器                                                                        |
//+------------------------------------------------------------------+
void CWindow::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
  {
//--- 处理对象点击事件
   if(id==CHARTEVENT_OBJECT_CLICK)
     {
      //--- 如果工具按钮已被点击
      if(OnClickTooltipsButton(sparam))
         return;
     }
  }
//+------------------------------------------------------------------+
//| 处理点击工具按钮事件                                                            |
//+------------------------------------------------------------------+
bool CWindow::OnClickTooltipsButton(const string clicked_object)
  {
//--- 如果窗口是一个对话框, 则无需此按钮
   if(m_window_type==W_DIALOG)
      return(false);
//--- 如果无按钮点击, 离开
   if(::StringFind(clicked_object,CElement::ProgramName()+"_window_tooltip_",0)<0)
      return(false);
//--- 从对象名获取控件标识符
   int id=CElement::IdFromObjectName(clicked_object);
//--- 如果控件标识符不匹配, 离开
   if(id!=CElement::Id())
      return(false);
//--- 在类字段中保存状态
   m_tooltips_button_state=m_button_tooltip.State();
//--- 发送有关消息
   ::EventChartCustom(m_chart_id,ON_WINDOW_TOOLTIPS,CElement::Id(),CElement::Index(),"");
   return(true);
  }

为了所有这些能在函数库引擎中工作 (CWndEvents 类), 已加入了处理 ON_WINDOW_TOOLTIPS 标识符的 OnWindowTooltips() 方法: 

class CWndEvents : public CWndContainer
  {
private:
   //--- 启用/禁用工具提示
   bool              OnWindowTooltips(void);
  };
//+------------------------------------------------------------------+
//| CHARTEVENT_CUSTOM 事件                                               |
//+------------------------------------------------------------------+
void CWndEvents::ChartEventCustom(void)
  {
//--- 如果信号是表单最小化
//--- 如果信号是表单最大化
//--- 如果信号是控件沿 X 轴调整大小
//--- 如果信号是控件沿 Y 轴调整大小
//--- 如果信号是启用/禁用工具提示
   if(OnWindowTooltips())
      return;

//--- 如果信号是隐藏初始项目下的上下文菜单
//--- 如果信号是隐藏所有上下文菜单
//--- 如果信号是打开对话窗口
//--- 如果信号是关闭对话窗口
//--- 如果信号是清除指定表单上所有元素的颜色
//--- 如果信号是重设鼠标左键点击的优先权
//--- 如果信号是恢复鼠标左键点击的优先权
  }
//+------------------------------------------------------------------+
//| ON_WINDOW_TOOLTIPS 事件                                        |
//+------------------------------------------------------------------+
bool CWndEvents::OnWindowTooltips(void)
  {
//--- 如果信号是 "启用/禁用工具提示"
   if(m_id!=CHARTEVENT_CUSTOM+ON_WINDOW_TOOLTIPS)
      return(false);
//--- 如果窗口标识符匹配
   if(m_lparam!=m_windows[0].Id())
      return(true);
//--- 在所有窗口中同步工具提示模式
   int windows_total=WindowsTotal();
   for(int w=0; w<windows_total; w++)
     {
      if(w>0)
         m_windows[w].TooltipButtonState(m_windows[0].TooltipButtonState());
     }
//---
   return(true);
  }

2. 以下控件创建后为其添加修改描述文本的能力: 

 

图例. 4. 控件创建后有能力更改文本的列表。


3. 现在可在所有控件中设置图标, 这可能是必要的 (请参阅下表)。此外, 添加了在控件图标创建后更改控件图标的功能:


 

图例. 5. 控件创建后有能力更改图标的列表。

 

若要替换上表所列所有控件中的图标, 存在 IconFileOn() 和 IconFileOff() 方法。


4. 添加了在按钮和选项卡创建之后以编程方式管理所有类型状态 (按下/释放) 的能力。下表显示了包含此添加的控件:

 

图例. 6. 控件创建后有能力更改状态 (按下/释放) 的列表。


5. 优化当光标悬停在以下控件时高亮显示项目的算法:

 

图例. 7. 带有优化算法的控件, 高亮显示控件项目。

 

以前, 程序遍历上述控件列表中的所有项目, 检查鼠标光标在它们上方的位置。因此, 光标下的项目以不同的颜色高亮显示, 而默认设置的颜色则用于其余项目。但此方法属于资源密集型, 所以需要优化。现在, 无需遍历整个数组, 只有两个项目参与颜色变化。仅在项目转换时进行周期搜索, 即焦点已经改变。 

进而, 作为示例, 考虑如何在 CListView 类中实现。实现上述需要添加 (1) m_prev_item_index_focus 类字段来保存最后关注项目的索引, (2) CListView::CheckItemFocus() 方法来检查关注项目, 以及 (3) 修改在 CListView::ChangeItemsColor() 方法中的算法。  

//+------------------------------------------------------------------+
//| 用于创建列表视图的类                                                            |
//+------------------------------------------------------------------+
class CListView : public CElement
  {
private:
   //--- 判断鼠标光标从一个项目转换到另一个项目的时刻
   int               m_prev_item_index_focus;
   //---
private:
   //--- 当光标悬停在列表视图项目上时更改列表视图项目的颜色
   void              ChangeItemsColor(void);
   //--- 当光标悬停时, 检查列表视图项的焦点
   void              CheckItemFocus(void);
  };

方法 CListView::CheckItemFocus() 仅在鼠标进入控件区域时才被调用 (在此情况下 – CListView), 以及鼠标从一个项目移动到另一个项目时 (参见以下代码)。一旦发现鼠标悬停的项目, 其 索引被保存。 

//+------------------------------------------------------------------+
//| 当光标悬停时, 检查列表视图项目的焦点                              |
//+------------------------------------------------------------------+
void CListView::CheckItemFocus(void)
  {
//--- 获取滚动条滑块的当前位置
   int v=m_scrollv.CurrentPos();
//--- 标识光标悬停所在项目, 并高亮显示它
   for(int i=0; i<m_visible_items_total; i++)
     {
      //--- 如果未超出列表视图范围, 计数器增长
      if(v>=0 && v<m_items_total)
         v++;
      //--- 跳过所选项目
      if(m_selected_item_index==v-1)
        {
         m_items[i].BackColor(m_item_color_selected);
         m_items[i].Color(m_item_text_color_selected);
         continue;
        }
      //--- 如果光标悬停于此项目, 将之高亮
      if(m_mouse.X()>m_items[i].X() && m_mouse.X()<m_items[i].X2() &&
         m_mouse.Y()>m_items[i].Y() && m_mouse.Y()<m_items[i].Y2())
        {
         m_items[i].BackColor(m_item_color_hover);
         m_items[i].Color(m_item_text_color_hover);
         //--- 记住此项目
         m_prev_item_index_focus=i;
         break;
        }
     }
  }

方法 CListView::CheckItemFocus() 的调用来自 CListView::ChangeItemsColor() 方法之内, 如同以前段落所描述的那样 (参见以下代码清单):

//+------------------------------------------------------------------+
//| 当光标悬停时更改列表视图项的颜色                                    |
//+------------------------------------------------------------------+
void CListView::ChangeItemsColor(void)
  {
//--- 若光标悬停禁用或滚动条已激活, 离开
   if(!m_lights_hover || m_scrollv.ScrollState())
      return;
//--- 如果不是下拉元素, 并且表单被阻塞, 离开
   if(!CElement::IsDropdown() && m_wnd.IsLocked())
      return;
//--- 如果再次进入列表视图
   if(m_prev_item_index_focus==WRONG_VALUE)
     {
      //--- 检查当前项目的焦点
      CheckItemFocus();
     }
   else
     {
      //--- 检查当前行的焦点
      int i=m_prev_item_index_focus;
      bool condition=m_mouse.X()>m_items[i].X() && m_mouse.X()<m_items[i].X2() &&
                     m_mouse.Y()>m_items[i].Y() && m_mouse.Y()<m_items[i].Y2();
      //--- 如果移至另一个项目
      if(!condition)
        {
         //--- 重置前次项目的颜色
         m_items[i].BackColor(m_item_color);
         m_items[i].Color(m_item_text_color);
         m_prev_item_index_focus=WRONG_VALUE;
         //--- 检查当前项目的焦点
         CheckItemFocus();
        }
     }
  }

CListView::OnEvent() 事件处理器中, 方法 CListView::ChangeItemsColor() 仅当鼠标光标位于控件区域内才会调用。一旦光标离开控件区域, 将设置默认颜色, 并重置 项目索引值。事件处理程序的缩短版本如下所示。 

//+------------------------------------------------------------------+
//| 事件处理器                                                                                |
//+------------------------------------------------------------------+
void CListView::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
  {
//--- 光标移动事件处理器
   if(id==CHARTEVENT_MOUSE_MOVE)
     {
      //--- 如果元素隐藏, 离开
      //--- 如果子窗口编号不匹配, 离开
      //--- 检查聚焦元素
      //--- 如果是下拉列表且已按下鼠标按键
      //--- 如果滑块管理启用则移动列表

      //--- 如果未聚焦, 重置元素颜色
      if(!CElement::MouseFocus())
        {
         //--- 如果项目已聚焦
         if(m_prev_item_index_focus!=WRONG_VALUE)
           {
            //--- 重置列表视图颜色
            ResetColors();
            m_prev_item_index_focus=WRONG_VALUE;
           }
         return;
        }
      //--- 当光标悬停于列表视图的项目上时, 更改列表视图项目的颜色
      ChangeItemsColor();
      return;
     }
  }

相同的原理已在 CTable, CCalendarCTreeView 类中实现, 但考虑到每个控件的特性略有某些差异。

6. 在双状态模式下按下 CIconButton 类型的按钮 (在单击后不释放按钮) 会显示不同的图标, 如果已设置。已按下按钮的图表可使用 CIconButton::IconFilePressedOn()CIconButton::IconFilePressedOff() 方法来设置。 

//+------------------------------------------------------------------+
//| 创建图标按钮的类 |
//+------------------------------------------------------------------+
class CIconButton : public CElement
  {
private:
   //--- 按钮激活图标, 阻塞且按下状态
   string            m_icon_file_on;
   string            m_icon_file_off;
   string            m_icon_file_pressed_on;
   string            m_icon_file_pressed_off;

   //---
public:
   //--- 设置按钮释放图标, 激活且阻塞状态
   void              IconFileOn(const string file_path);
   void              IconFileOff(const string file_path);
   void              IconFilePressedOn(const string file_path);
   void              IconFilePressedOff(const string file_path);

  };
//+------------------------------------------------------------------+
//| 设置已按下 "ON" 状态的图标                                                 |
//+------------------------------------------------------------------+
void CIconButton::IconFilePressedOn(const string file_path)
  {
//--- 如果按钮的双态模式禁用, 离开
   if(!m_two_state)
      return;
//--- 保存图片路径
   m_icon_file_pressed_on=file_path;
//--- 立刻判断按钮是否已按下
   if(m_button.State())
      m_icon.BmpFileOn("::"+file_path);
  }
//+------------------------------------------------------------------+
//| 设置已按下 "OFF" 状态的图标                                                |
//+------------------------------------------------------------------+
void CIconButton::IconFilePressedOff(const string file_path)
  {
//--- 如果按钮的双态模式禁用, 离开
   if(!m_two_state)
      return;
//--- 保存图片路径
   m_icon_file_pressed_off=file_path;
//--- 立刻判断按钮是否已按下
   if(m_button.State())
      m_icon.BmpFileOff("::"+file_path);
  }

7. 添加了在表中 (CTable) 以编程方式选择行的能力。为此, 使用 CTable::SelectRow() 方法。指定已选择行的索引并取消选择。 

//+------------------------------------------------------------------+
//| 创建编辑框表单的类 |
//+------------------------------------------------------------------+
class CTable : public CElement
  {
public:
   //--- 选择指定的表单行
   void              SelectRow(const uint row_index);
  };
//+------------------------------------------------------------------+
//| 选择指定的表单行                                                                    |
//+------------------------------------------------------------------+
void CTable::SelectRow(const uint row_index)
  {
//--- 在超出范围的情况下进行调整
   uint index=(row_index>=(uint)m_rows_total)?m_rows_total-1 : row_index;
//--- 如果已经选择此行, 则取消选择
   bool is_selected=(index==m_selected_item);
//--- 保存行索引
   m_selected_item=(is_selected)?WRONG_VALUE : (int)index;
//--- 保存单元行
   m_selected_item_text=(is_selected)?"" : m_vcolumns[0].m_vrows[index];
//--- 生成单元参数的字符串
   string cell_params=string(0)+"_"+string(index)+"_"+m_vcolumns[0].m_vrows[index];
//--- 重置焦点
   m_prev_item_index_focus=WRONG_VALUE;
//--- 更新表格
   UpdateTable();
//--- 高亮已选行
   HighlightSelectedItem();
  }

8. 修复了 CIconTabs 控件中选定选项卡显示元素的问题。问题会发生在使用此类型的选项卡打开并最大化表单时。 

 

测试控件的应用程序

我们来编写一个测试应用程序, 在此您可亲身参与所有新控件的测试, 评估它们的不同模式。应用程序的图形界面中,创建一个 Tabs 控件 (CTabs 类), 其内包括含有以下内容的四个选卡:

1. 第一个选卡:

  • 进度条 (CProgressBar)。
  • 文本编辑框 (CTextEdit)。
  • 带有一个下拉列表的复合选择框 (CCombobox)。
  • 轮转数值编辑框 (CSpinEdit)。
  • 调用选色器的按钮 (CColorButton)。
  • 文本标签 (CTextLabel)。

为所有控件设置图标, 文本标签除外。进度条和文本编辑框控件的描述将以固定的时间间隔变更, 以便演示此类现在可用的功能。在复合框列表中放置来自 CFonts 类的名称。MQL 测试应用程序的事件模型将以这样的方式构建, 即从组合框中选择一种字体, 并将之反映在文本标签中。类似地, 文本标签的字号将被绑定到轮转数字编辑框以改变字体大小, 并在选择器中选择颜色。 

用于管理文本标签控件参数的事件处理程序将如下所示:

//+------------------------------------------------------------------+
//| 图表事件处理器                                                                        |
//+------------------------------------------------------------------+
void CProgram::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
  {
//--- 从复选框列表中进行选择的事件
   if(id==CHARTEVENT_CUSTOM+ON_CLICK_COMBOBOX_ITEM)
     {
      //--- 如果控件标识符匹配
      if(lparam==m_combobox1.Id())
        {
         //--- 改变字体
         m_text_label1.LabelFont(m_combobox1.ButtonText());
        }
      //---
      return;
     }
//--- 点击轮转编辑框按钮的事件
   if(id==CHARTEVENT_CUSTOM+ON_CLICK_INC ||
      id==CHARTEVENT_CUSTOM+ON_CLICK_DEC)
     {
      //--- 如果控件标识符匹配
      if(lparam==m_spin_edit1.Id())
        {
         //--- 改变字号
         m_text_label1.LabelFontSize(int(m_spin_edit1.GetValue()));
        }
      //---
      return;
     }
//--- 使用择色器变更颜色的事件
   if(id==CHARTEVENT_CUSTOM+ON_END_EDIT)
     {
      //--- 如果控件标识符匹配
      if(lparam==m_spin_edit1.Id())
        {
         //--- 改变字号
         m_text_label1.LabelFontSize(int(m_spin_edit1.GetValue()));
        }
      //---
      return;
     }
//--- 使用择色器变更颜色的事件
   if(id==CHARTEVENT_CUSTOM+ON_CHANGE_COLOR)
     {
      //--- 如果控件标识符匹配
      if(lparam==m_color_picker.Id())
        {
         //--- 如果从第一个按钮得到响应
         if(sparam==m_color_button1.LabelText())
           {
            //--- 改变对象颜色
            m_text_label1.LabelColor(m_color_button1.CurrentColor());
            return;
           }
        }
      return;
     }
//--- 按钮按下的事件
   if(id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON)
     {
      //--- 如果调用颜色选择器的第一个按钮按下
      if(sparam==m_color_button1.LabelText())
        {
         //--- 传递按钮指针, 使用颜色选择器自动打开窗口
         m_color_picker.ColorButtonPointer(m_color_button1);
         return;
        }
      //---
      return;
     }
  }

下面的屏幕截图显示了如何使用图形界面配置文本显示。

图例. 8. 第一个选项卡上的控件组。 

图例. 8. 第一个选项卡上的控件组。


2. 仅有一个控件 – 图片滑块 (CPicturesSlider 类) 将会放置在第二个选卡上。只有三个省缺图片将被添加到组, 以便您可以自行快速测试此控件。若要令控件正常工作, 请准备相同尺寸的图片。

图例. 9. 第二个选项卡上的图片滑块控件。 

图例. 9. 第二个选项卡上的图片滑块控件。


若要以编程方式切换图片, 请使用 CPicturesSlider::SelectPicture() 方法。


3. 第三个选项卡将包含 CTable 类型的表格。若要以编程方式选择一行, 请使用 CTable::SelectRow() 方法。 

 图例. 10. 第三个选项卡上的表格控件。

图例. 10. 第三个选项卡上的表格控件。


4. 三个控件将位于第四选项卡上: (1) 日历, (2) 下拉日历, 和 (3) 带有按压/释放两个不同状态图标的按钮。

图例. 11. 第四个选项卡上的控件组。 

图例. 11. 第四个选项卡上的控件组。

 

在此文章中介绍的测试应用程序可从以下链接下载, 以供进一步学习。 


结论

用于创建图形界面的函数库, 当前开发阶段如下图所示。

 图例. 12. 当前开发阶段的函数库结构。

图例. 12. 当前开发阶段的函数库结构。


在下一个版本中, 函数库将使用其它控件进行扩展。此外, 现有的控件将会进一步开发并扩展新的功能。

如果您在使用这些文件所提供的素材时有任何疑问, 可以参考函数库开发系列文章之一的详细描述, 或在本文的评论中提出您的问题。 

全部回复

0/140

量化课程

    移动端课程