
DoEasy 函数库中的图形(第七十四部分):由 CCanvas 类提供强力支持的基本图形元素
内容
概述
在前一篇文章中,我开始研究处理图形的大型函数库章节。 即,我着手开发交互窗对象,它基于 CCanvas 标准库类,将作为所有函数库图形对象的主对象。 我还测试了一些机制,并为进一步开发做好了准备。 然而,经过仔细分析发现,所选概念与构建函数库对象概念不同,交互窗对象比基准对象复杂得多。
我将针对画布上的基准图形对象引入“元素”概念。 这个概念将用于构建其余的图形对象。 例如,交互窗对象也是在程序中绘制图形结构的最低限度对象,但它业已能够作为一个独立的对象参与设计。 它已经拥有绘制对象框架、各种形状和文本的能力。 对比之下,元素对象作为创建函数库“图形”层次结构中所有后续对象的基础,例如:
- 基准图形对象 是 CObject 的衍生后代。 在终端中可用于构建的图形对象包含一些固有属性;
- 画布上的 Element 对象所拥有的对象属性均基于画布对象;
- Form 对象为设计元素对象外观提供了附加属性和功能;
- Window 对象 是基于元素和交互窗对象的复合对象;
- 诸如此类。
基于这个新概念,我将重新设计 CGBaseObj 函数库图形对象的基类,并创建一个新的“图形元素”对象,它完全重复了构建基准函数库对象的整个概念。 稍后,这种方式将令我们能够快速搜索所需的图形对象,针对它们进行排序,管理它们的行为,并渲染。
改进库类
在 MQL5\Include\DoEasy\Data.mqh 里,加入新的消息索引:
MSG_LIB_SYS_FAILED_CREATE_STORAGE_FOLDER, // Failed to create folder for storing files. Error: MSG_LIB_SYS_FAILED_ADD_ACC_OBJ_TO_LIST, // Error. Failed to add current account object to collection list MSG_LIB_SYS_FAILED_CREATE_CURR_ACC_OBJ, // Error. Failed to create account object with current account data MSG_LIB_SYS_FAILED_OPEN_FILE_FOR_WRITE, // Could not open file for writing MSG_LIB_SYS_INPUT_ERROR_NO_SYMBOL, // Input error: no symbol MSG_LIB_SYS_FAILED_CREATE_SYM_OBJ, // Failed to create symbol object MSG_LIB_SYS_FAILED_ADD_SYM_OBJ, // Failed to add symbol MSG_LIB_SYS_FAILED_CREATE_ELM_OBJ, // Failed to create the graphical element object
以及与新添加的索引对应的文本:
{"Не удалось создать папку хранения файлов. Ошибка: ","Could not create file storage folder. Error: "}, {"Ошибка. Не удалось добавить текущий объект-аккаунт в список-коллекцию","Error. Failed to add current account object to collection list"}, {"Ошибка. Не удалось создать объект-аккаунт с данными текущего счёта","Error. Failed to create account object with current account data"}, {"Не удалось открыть для записи файл ","Could not open file for writing: "}, {"Ошибка входных данных: нет символа ","Input error: no "}, {"Не удалось создать объект-символ ","Failed to create symbol object "}, {"Не удалось добавить символ ","Failed to add "}, {"Не удалось создать объект-графический элемент ","Failed to create graphic element object "},
对于 \MQL5\Include\DoEasy\Defines.mqh 中的新“图形元素”对象,将其类型添加到图形对象类型的枚举列表之中,以及其整数型和字符串型属性:
//+------------------------------------------------------------------+ //| The list of graphical element types | //+------------------------------------------------------------------+ enum ENUM_GRAPH_ELEMENT_TYPE { GRAPH_ELEMENT_TYPE_ELEMENT, // Element GRAPH_ELEMENT_TYPE_FORM, // Form GRAPH_ELEMENT_TYPE_WINDOW, // Window }; //+------------------------------------------------------------------+ //| Integer properties of the graphical element on the canvas | //+------------------------------------------------------------------+ enum ENUM_CANV_ELEMENT_PROP_INTEGER { CANV_ELEMENT_PROP_ID = 0, // Form ID CANV_ELEMENT_PROP_TYPE, // Graphical element type CANV_ELEMENT_PROP_NUM, // Element index in the list CANV_ELEMENT_PROP_CHART_ID, // Chart ID CANV_ELEMENT_PROP_WND_NUM, // Chart subwindow index CANV_ELEMENT_PROP_COORD_X, // Form's X coordinate on the chart CANV_ELEMENT_PROP_COORD_Y, // Form's Y coordinate on the chart CANV_ELEMENT_PROP_WIDTH, // Form width CANV_ELEMENT_PROP_HEIGHT, // Form height CANV_ELEMENT_PROP_RIGHT, // Form right border CANV_ELEMENT_PROP_BOTTOM, // Form bottom border CANV_ELEMENT_PROP_ACT_SHIFT_LEFT, // Active area offset from the left edge of the form CANV_ELEMENT_PROP_ACT_SHIFT_TOP, // Active area offset from the top edge of the form CANV_ELEMENT_PROP_ACT_SHIFT_RIGHT, // Active area offset from the right edge of the form CANV_ELEMENT_PROP_ACT_SHIFT_BOTTOM, // Active area offset from the bottom edge of the form CANV_ELEMENT_PROP_OPACITY, // Form opacity CANV_ELEMENT_PROP_COLOR_BG, // Form background color CANV_ELEMENT_PROP_MOVABLE, // Form moveability flag CANV_ELEMENT_PROP_ACTIVE, // Form activity flag CANV_ELEMENT_PROP_COORD_ACT_X, // X coordinate of the form's active area CANV_ELEMENT_PROP_COORD_ACT_Y, // Y coordinate of the form's active area CANV_ELEMENT_PROP_ACT_RIGHT, // Right border of the form's active area CANV_ELEMENT_PROP_ACT_BOTTOM, // Bottom border of the form's active area }; #define CANV_ELEMENT_PROP_INTEGER_TOTAL (23) // Total number of integer properties #define CANV_ELEMENT_PROP_INTEGER_SKIP (0) // Number of integer properties not used in sorting //+------------------------------------------------------------------+ //| Real properties of the graphical element on the canvas | //+------------------------------------------------------------------+ enum ENUM_CANV_ELEMENT_PROP_DOUBLE { CANV_ELEMENT_PROP_DUMMY = CANV_ELEMENT_PROP_INTEGER_TOTAL, // DBL stub }; #define CANV_ELEMENT_PROP_DOUBLE_TOTAL (1) // Total number of real properties #define CANV_ELEMENT_PROP_DOUBLE_SKIP (1) // Number of real properties not used in sorting //+------------------------------------------------------------------+ //| String properties of the graphical element on the canvas | //+------------------------------------------------------------------+ enum ENUM_CANV_ELEMENT_PROP_STRING { CANV_ELEMENT_PROP_NAME_OBJ = (CANV_ELEMENT_PROP_INTEGER_TOTAL+CANV_ELEMENT_PROP_DOUBLE_TOTAL), // Form object name CANV_ELEMENT_PROP_NAME_RES, // Graphical resource name }; #define CANV_ELEMENT_PROP_STRING_TOTAL (2) // Total number of string properties //+------------------------------------------------------------------+
由于基于画布的对象还没有实数型属性,而构造函数库对象的概念需要它们的存在,故此我添加了实数型属性 stub,作为唯一的实数型属性。
为了能够按属性对图形元素对象进行排序,添加可能的排序标准枚举:
//+------------------------------------------------------------------+ //| Possible sorting criteria of graphical elements on the canvas | //+------------------------------------------------------------------+ #define FIRST_CANV_ELEMENT_DBL_PROP (CANV_ELEMENT_PROP_INTEGER_TOTAL-CANV_ELEMENT_PROP_INTEGER_SKIP) #define FIRST_CANV_ELEMENT_STR_PROP (CANV_ELEMENT_PROP_INTEGER_TOTAL-CANV_ELEMENT_PROP_INTEGER_SKIP+CANV_ELEMENT_PROP_DOUBLE_TOTAL-CANV_ELEMENT_PROP_DOUBLE_SKIP) enum ENUM_SORT_CANV_ELEMENT_MODE { //--- Sort by integer properties SORT_BY_CANV_ELEMENT_ID = 0, // Sort by form ID SORT_BY_CANV_ELEMENT_TYPE, // Sort by graphical element type SORT_BY_CANV_ELEMENT_NUM, // Sort by form index in the list SORT_BY_CANV_ELEMENT_CHART_ID, // Sort by chart ID SORT_BY_CANV_ELEMENT_WND_NUM, // Sort by chart window index SORT_BY_CANV_ELEMENT_COORD_X, // Sort by the form X coordinate on the chart SORT_BY_CANV_ELEMENT_COORD_Y, // Sort by the form Y coordinate on the chart SORT_BY_CANV_ELEMENT_WIDTH, // Sort by the form width SORT_BY_CANV_ELEMENT_HEIGHT, // Sort by the form height SORT_BY_CANV_ELEMENT_RIGHT, // Sort by the form right border SORT_BY_CANV_ELEMENT_BOTTOM, // Sort by the form bottom border SORT_BY_CANV_ELEMENT_ACT_SHIFT_LEFT, // Sort by the active area offset from the left edge of the form SORT_BY_CANV_ELEMENT_ACT_SHIFT_TOP, // Sort by the active area offset from the top edge of the form SORT_BY_CANV_ELEMENT_ACT_SHIFT_RIGHT, // Sort by the active area offset from the right edge of the form SORT_BY_CANV_ELEMENT_ACT_SHIFT_BOTTOM, // Sort by the active area offset from the bottom edge of the form SORT_BY_CANV_ELEMENT_OPACITY, // Sort by the form opacity SORT_BY_CANV_ELEMENT_COLOR_BG, // Sort by the form background color SORT_BY_CANV_ELEMENT_MOVABLE, // Sort by the form moveability flag SORT_BY_CANV_ELEMENT_ACTIVE, // Sort by the form activity flag SORT_BY_CANV_ELEMENT_COORD_ACT_X, // Sort by X coordinate of the form active area SORT_BY_CANV_ELEMENT_COORD_ACT_Y, // Sort by Y coordinate of the form active area SORT_BY_CANV_ELEMENT_ACT_RIGHT, // Sort by the right border of the form active area SORT_BY_CANV_ELEMENT_ACT_BOTTOM, // Sort by the bottom border of the form active area //--- Sort by real properties //--- Sort by string properties SORT_BY_CANV_ELEMENT_NAME_OBJ = FIRST_CANV_ELEMENT_STR_PROP,// Sort by the form object name SORT_BY_CANV_ELEMENT_NAME_RES, // Sort by the graphical resource name }; //+------------------------------------------------------------------+
所有这些列举在首发文章中都有讲述,且研究过多次,故我不会在此赘述。
在创建“图形元素”对象之前,修改 MQL5\Include\DoEasy\Objects\Graph\GBaseObj.mqh 中所有函数库图形对象的基准对象类。
该对象是为了存储任意图形对象的所有公共属性,例如所创建对象类型、图表 ID、和子窗口索引,子窗口内已设置了图形对象、及其名称和名称前缀。 函数库的任意图形对象都将从该类继承。
全新创建这个类比修补现有的类更方便。 因此,简单地从文件中删除所有内容,并加入必要的内容:
//+------------------------------------------------------------------+ //| GBaseObj.mqh | //| Copyright 2021, MetaQuotes Ltd. | //| https://mql5.com/en/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2021, MetaQuotes Ltd." #property link "https://mql5.com/en/users/artmedia70" #property version "1.00" #property strict // Necessary for mql4 //+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include "..\..\Services\DELib.mqh" #include <Graphics\Graphic.mqh> //+------------------------------------------------------------------+ //| Class of the base object of the library graphical objects | //+------------------------------------------------------------------+ class CGBaseObj : public CObject { private: int m_type; // Object type protected: string m_name_prefix; // Object name prefix string m_name; // Object name long m_chart_id; // Chart ID int m_subwindow; // Subwindow index int m_shift_y; // Subwindow Y coordinate shift public: //--- Return the values of class variables string Name(void) const { return this.m_name; } long ChartID(void) const { return this.m_chart_id; } int SubWindow(void) const { return this.m_subwindow; } //--- The virtual method returning the object type virtual int Type(void) const { return this.m_type; } //--- Constructor/destructor CGBaseObj(); ~CGBaseObj(); }; //+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CGBaseObj::CGBaseObj() : m_shift_y(0), m_type(0), m_name_prefix(::MQLInfoString(MQL_PROGRAM_NAME)+"_") { } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CGBaseObj::~CGBaseObj() { } //+------------------------------------------------------------------+
函数库服务函数文件和标准库 CGraphic 类文件即刻包含在文件当中。 CCanvas 类文件已经包含在 CGraphic 之中。 与此同时,CGraphic 类拥有广泛的绘制各种图形的方法。 我们将来也需要这个。
该类继承自标准库的基类,允许我们将图形元素创建为 CObject 类对象,并遵照相同的方式把所有对象存储在相应图形对象的列表集合中。
m_type 私有变量存储我上面讨论的 ENUM_GRAPH_ELEMENT_TYPE 枚举中的对象类型。
默认情况下,对象类型为零,并由标准库基类的 Type() 虚方法返回:
//--- method of identifying the object virtual int Type(void) const { return(0); }
在此,我已重新定义了该方法,从而它可根据所创建图形对象的时间返回 m_type 变量。
受保护的类变量:
- m_name_prefix — 在此,我将存储对象的名称前缀,依据它们识别图形对象与程序的从属关系。 相应地,在此我将存储基于函数库的程序名称。
- m_name 存储图形对象名称。 完整的对象名称是由前缀和名称相加而创建的。 因此,在创建对象时,我们只需为新创建的对象指定唯一的一个名称,而“图形元素”对象类则会自行为名称添加前缀。 前缀允许用创建它的程序标识对象。
- m_chart_id — 在此,我设置要在其上创建图形对象的图表 ID。
- m_subwindow — 构建图形对象所在的图表子窗口。
- m_shift_y — 在图表子窗口中创建对象的 Y 坐标偏移。
公开方法简单地返回相应类的变量值:
public: //--- Return the values of class variables string Name(void) const { return this.m_name; } long ChartID(void) const { return this.m_chart_id; } int SubWindow(void) const { return this.m_subwindow; } //--- The virtual method returning the object type virtual int Type(void) const { return this.m_type; }
在类构造函数的初始化清单中,设置 Y 坐标偏移量、对象类型(默认为0),以及由程序名称和下划线组成的名称前缀:
//+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CGBaseObj::CGBaseObj() : m_shift_y(0), m_type(0), m_name_prefix(::MQLInfoString(MQL_PROGRAM_NAME)+"_") { } //+------------------------------------------------------------------+
基于画布的所有函数库图形对象的基准对象
我们开始基于 CCanvas 类开发“图形元素”对象类。
在 \MQL5\Include\DoEasy\Objects\Graph\ 里,创建含 CGCnvElement 类的新文件 GCnvElement.mqh。
包含函数库基准图形对象文件,该类需继承自基类:
//+------------------------------------------------------------------+ //| GCnvElement.mqh | //| Copyright 2021, MetaQuotes Ltd. | //| https://mql5.com/en/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2021, MetaQuotes Ltd." #property link "https://mql5.com/en/users/artmedia70" #property version "1.00" #property strict // Necessary for mql4 //+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include "GBaseObj.mqh" //+------------------------------------------------------------------+ //| Class of the base object of the library graphical objects | //+------------------------------------------------------------------+ class CGCnvElement : public CGBaseObj { }
在类的受保护部分,声明 CCanvas 和 CPause 类的对象,以及返回指定坐标相对于元素和其活动区域位置的两个方法:
protected: CCanvas m_canvas; // CCanvas class object CPause m_pause; // Pause class object //--- Return the cursor position relative to the (1) entire element and (2) the element's active area bool CursorInsideElement(const int x,const int y); bool CursorInsideActiveArea(const int x,const int y); private:
在类的私密部分,声明存储对象属性的数组,并编写两个方法,在相应的数组中返回指定属性的实际索引:
private: long m_long_prop[ORDER_PROP_INTEGER_TOTAL]; // Integer properties double m_double_prop[ORDER_PROP_DOUBLE_TOTAL]; // Real properties string m_string_prop[ORDER_PROP_STRING_TOTAL]; // String properties //--- Return the index of the array the order's (1) double and (2) string properties are located at int IndexProp(ENUM_CANV_ELEMENT_PROP_DOUBLE property) const { return(int)property-CANV_ELEMENT_PROP_INTEGER_TOTAL; } int IndexProp(ENUM_CANV_ELEMENT_PROP_STRING property) const { return(int)property-CANV_ELEMENT_PROP_INTEGER_TOTAL-CANV_ELEMENT_PROP_DOUBLE_TOTAL; } public:
该类的公开部分提供了函数库类对象的标准方法,可将属性设置到数组,并从数组中返回属性值,返回对象支持指定属性标志的方法,以及比较两个对象的方法:
public: //--- Set object's (1) integer, (2) real and (3) string properties void SetProperty(ENUM_CANV_ELEMENT_PROP_INTEGER property,long value) { this.m_long_prop[property]=value; } void SetProperty(ENUM_CANV_ELEMENT_PROP_DOUBLE property,double value) { this.m_double_prop[this.IndexProp(property)]=value; } void SetProperty(ENUM_CANV_ELEMENT_PROP_STRING property,string value) { this.m_string_prop[this.IndexProp(property)]=value; } //--- Return object’s (1) integer, (2) real and (3) string property from the properties array long GetProperty(ENUM_CANV_ELEMENT_PROP_INTEGER property) const { return this.m_long_prop[property]; } double GetProperty(ENUM_CANV_ELEMENT_PROP_DOUBLE property) const { return this.m_double_prop[this.IndexProp(property)]; } string GetProperty(ENUM_CANV_ELEMENT_PROP_STRING property) const { return this.m_string_prop[this.IndexProp(property)]; } //--- Return the flag of the object supporting this property virtual bool SupportProperty(ENUM_CANV_ELEMENT_PROP_INTEGER property) { return true; } virtual bool SupportProperty(ENUM_CANV_ELEMENT_PROP_DOUBLE property) { return true; } virtual bool SupportProperty(ENUM_CANV_ELEMENT_PROP_STRING property) { return true; } //--- Compare CGCnvElement objects with each other by all possible properties (for sorting the lists by a specified object property) virtual int Compare(const CObject *node,const int mode=0) const; //--- Compare CGCnvElement objects with each other by all properties (to search equal objects) bool IsEqual(CGCnvElement* compared_obj) const; //--- Creates the control
所有这些方法对于函数库对象都是标准化的。 我在第一篇文章中已研究过它们。
类的公开部分提供了在画布上创建“图形元素”对象的方法、返回指向所创建画布对象指针的方法、设置画布更新频率的方法、在图表上移动画布的方法、以及简化访问对象属性的方法:
//--- Creates the control bool Create(const long chart_id, const int wnd_num, const string name, const int x, const int y, const int w, const int h, const color colour, const uchar opacity, const bool redraw=false); //--- Return the pointer to a canvas object CCanvas *CanvasObj(void) { return &this.m_canvas; } //--- Set the canvas update frequency void SetFrequency(const ulong value) { this.m_pause.SetWaitingMSC(value); } //--- Update the coordinates (shift the canvas) bool Move(const int x,const int y,const bool redraw=false); //--- Constructors/Destructor CGCnvElement(const ENUM_GRAPH_ELEMENT_TYPE element_type, const int element_id, const int element_num, const long chart_id, const int wnd_num, const string name, const int x, const int y, const int w, const int h, const color colour, const uchar opacity, const bool movable=true, const bool activity=true, const bool redraw=false); CGCnvElement(){;} ~CGCnvElement(); //+------------------------------------------------------------------+ //| Methods of simplified access to object properties | //+------------------------------------------------------------------+ //--- Set the (1) X, (2) Y coordinates, (3) element width and (4) height, bool SetCoordX(const int coord_x); bool SetCoordY(const int coord_y); bool SetWidth(const int width); bool SetHeight(const int height); //--- Set the shift of the (1) left, (2) top, (3) right, (4) bottom edge of the active area relative to the element, //--- (5) all shifts of the active area edges relative to the element and (6) the element opacity void SetActiveAreaLeftShift(const int value) { this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_LEFT,fabs(value)); } void SetActiveAreaRightShift(const int value) { this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_RIGHT,fabs(value)); } void SetActiveAreaTopShift(const int value) { this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_TOP,fabs(value)); } void SetActiveAreaBottomShift(const int value) { this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_BOTTOM,fabs(value)); } void SetActiveAreaShift(const int left_shift,const int bottom_shift,const int right_shift,const int top_shift); void SetOpacity(const uchar value,const bool redraw=false); //--- Return the shift (1) of the left, (2) right, (3) top and (4) bottom edge of the element active area int ActiveAreaLeftShift(void) const { return (int)this.GetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_LEFT); } int ActiveAreaRightShift(void) const { return (int)this.GetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_RIGHT); } int ActiveAreaTopShift(void) const { return (int)this.GetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_TOP); } int ActiveAreaBottomShift(void) const { return (int)this.GetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_BOTTOM); } //--- Return the coordinate (1) of the left, (2) right, (3) top and (4) bottom edge of the element active area int ActiveAreaLeft(void) const { return int(this.CoordX()+this.ActiveAreaLeftShift()); } int ActiveAreaRight(void) const { return int(this.RightEdge()-this.ActiveAreaRightShift()); } int ActiveAreaTop(void) const { return int(this.CoordY()+this.ActiveAreaTopShift()); } int ActiveAreaBottom(void) const { return int(this.BottomEdge()-this.ActiveAreaBottomShift()); } //--- Return (1) the opacity, coordinate (2) of the right and (3) bottom element edge uchar Opacity(void) const { return (uchar)this.GetProperty(CANV_ELEMENT_PROP_OPACITY); } int RightEdge(void) const { return this.CoordX()+this.m_canvas.Width(); } int BottomEdge(void) const { return this.CoordY()+this.m_canvas.Height(); } //--- Return the (1) X, (2) Y coordinates, (3) element width and (4) height, int CoordX(void) const { return (int)this.GetProperty(CANV_ELEMENT_PROP_COORD_X); } int CoordY(void) const { return (int)this.GetProperty(CANV_ELEMENT_PROP_COORD_Y); } int Width(void) const { return (int)this.GetProperty(CANV_ELEMENT_PROP_WIDTH); } int Height(void) const { return (int)this.GetProperty(CANV_ELEMENT_PROP_HEIGHT); } //--- Return the element (1) moveability and (2) activity flag bool Movable(void) const { return (bool)this.GetProperty(CANV_ELEMENT_PROP_MOVABLE); } bool Active(void) const { return (bool)this.GetProperty(CANV_ELEMENT_PROP_ACTIVE); } //--- Return (1) the object name, (2) the graphical resource name, (3) the chart ID and (4) the chart subwindow index string NameObj(void) const { return this.GetProperty(CANV_ELEMENT_PROP_NAME_OBJ); } string NameRes(void) const { return this.GetProperty(CANV_ELEMENT_PROP_NAME_RES); } long ChartID(void) const { return this.GetProperty(CANV_ELEMENT_PROP_CHART_ID); } int WindowNum(void) const { return (int)this.GetProperty(CANV_ELEMENT_PROP_WND_NUM); } }; //+------------------------------------------------------------------+
我们来详研所声明方法的实现。
参数型类构造函数:
//+------------------------------------------------------------------+ //| Parametric constructor | //+------------------------------------------------------------------+ CGCnvElement::CGCnvElement(const ENUM_GRAPH_ELEMENT_TYPE element_type, const int element_id, const int element_num, const long chart_id, const int wnd_num, const string name, const int x, const int y, const int w, const int h, const color colour, const uchar opacity, const bool movable=true, const bool activity=true, const bool redraw=false) { this.m_name=this.m_name_prefix+name; this.m_chart_id=chart_id; this.m_subwindow=wnd_num; if(this.Create(chart_id,wnd_num,this.m_name,x,y,w,h,colour,opacity,redraw)) { this.SetProperty(CANV_ELEMENT_PROP_NAME_RES,this.m_canvas.ResourceName()); // Graphical resource name this.SetProperty(CANV_ELEMENT_PROP_CHART_ID,CGBaseObj::ChartID()); // Chart ID this.SetProperty(CANV_ELEMENT_PROP_WND_NUM,CGBaseObj::SubWindow()); // Chart subwindow index this.SetProperty(CANV_ELEMENT_PROP_NAME_OBJ,CGBaseObj::Name()); // Element object name this.SetProperty(CANV_ELEMENT_PROP_TYPE,element_type); // Graphical element type this.SetProperty(CANV_ELEMENT_PROP_ID,element_id); // Element ID this.SetProperty(CANV_ELEMENT_PROP_NUM,element_num); // Element index in the list this.SetProperty(CANV_ELEMENT_PROP_COORD_X,x); // Element's X coordinate on the chart this.SetProperty(CANV_ELEMENT_PROP_COORD_Y,y); // Element's Y coordinate on the chart this.SetProperty(CANV_ELEMENT_PROP_WIDTH,w); // Element width this.SetProperty(CANV_ELEMENT_PROP_HEIGHT,h); // Element height this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_LEFT,0); // Active area offset from the left edge of the element this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_TOP,0); // Active area offset from the upper edge of the element this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_RIGHT,0); // Active area offset from the right edge of the element this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_BOTTOM,0); // Active area offset from the bottom edge of the element this.SetProperty(CANV_ELEMENT_PROP_OPACITY,opacity); // Element opacity this.SetProperty(CANV_ELEMENT_PROP_COLOR_BG,colour); // Element color this.SetProperty(CANV_ELEMENT_PROP_MOVABLE,movable); // Element moveability flag this.SetProperty(CANV_ELEMENT_PROP_ACTIVE,activity); // Element activity flag this.SetProperty(CANV_ELEMENT_PROP_RIGHT,this.RightEdge()); // Element right border this.SetProperty(CANV_ELEMENT_PROP_BOTTOM,this.BottomEdge()); // Element bottom border this.SetProperty(CANV_ELEMENT_PROP_COORD_ACT_X,this.ActiveAreaLeft()); // X coordinate of the element active area this.SetProperty(CANV_ELEMENT_PROP_COORD_ACT_Y,this.ActiveAreaTop()); // Y coordinate of the element active area this.SetProperty(CANV_ELEMENT_PROP_ACT_RIGHT,this.ActiveAreaRight()); // Right border of the element active area this.SetProperty(CANV_ELEMENT_PROP_ACT_BOTTOM,this.ActiveAreaBottom()); // Bottom border of the element active area } else { ::Print(CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_ELM_OBJ),this.m_name); } } //+------------------------------------------------------------------+
此处,我们首先创建一个对象名称,它是由父类中所创建对象名称前缀,和构造函数参数中传入的名称组成。 因此,唯一的对象名称看起来像 "Prefix_Object_Name"。
接下来,设置需在参数中传递至父类的图表 ID 和子窗口索引变量。 之后调用在画布上创建图形对象的方法。 如果对象创建成功,则将所有数据写入元素对象属性。 如果创建 CCanvas 类的图形对象失败,则在日志中通知。 带有前缀的名称已被创建,图表 ID 将与其子窗口一起随同设置。 因此,我们可以尝试调用 Create() 方法再次创建 CCanvas 类对象。 默认情况下,创建对象时,距活动区域每个边侧的偏移量设置为零,即对象活动区域与创建的图形元素的大小相匹配。 创建之后,活动区域的大小和位置始终可用以下研究的相应方法进行更改。
在类的析构函数里,销毁所创建的 CCanvas 类对象:
//+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CGCnvElement::~CGCnvElement() { this.m_canvas.Destroy(); } //+------------------------------------------------------------------+
该方法依据指定属性比较图形元素对象:
//+----------------------------------------------------------------------+ //|Compare CGCnvElement objects with each other by the specified property| //+----------------------------------------------------------------------+ int CGCnvElement::Compare(const CObject *node,const int mode=0) const { const CGCnvElement *obj_compared=node; //--- compare integer properties of two objects if(mode<CANV_ELEMENT_PROP_INTEGER_TOTAL) { long value_compared=obj_compared.GetProperty((ENUM_CANV_ELEMENT_PROP_INTEGER)mode); long value_current=this.GetProperty((ENUM_CANV_ELEMENT_PROP_INTEGER)mode); return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0); } //--- compare real properties of two objects else if(mode<CANV_ELEMENT_PROP_DOUBLE_TOTAL+CANV_ELEMENT_PROP_INTEGER_TOTAL) { double value_compared=obj_compared.GetProperty((ENUM_CANV_ELEMENT_PROP_DOUBLE)mode); double value_current=this.GetProperty((ENUM_CANV_ELEMENT_PROP_DOUBLE)mode); return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0); } //--- compare string properties of two objects else if(mode<ORDER_PROP_DOUBLE_TOTAL+ORDER_PROP_INTEGER_TOTAL+ORDER_PROP_STRING_TOTAL) { string value_compared=obj_compared.GetProperty((ENUM_CANV_ELEMENT_PROP_STRING)mode); string value_current=this.GetProperty((ENUM_CANV_ELEMENT_PROP_STRING)mode); return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0); } return 0; } //+------------------------------------------------------------------+
该方法是所有函数库对象的标准方法。 之前也曾研究过。 简而言之,该方法接收指定对象参数,并与当前对象的相应参数进行比较。 根据传递的参数,获取一个相似的参数,并返回两个对象参数的比较结果(1、-1 和 0 分别表示“更多”、“更少”和“相等”)。
该方法依据所有属性比较图形元素对象:
//+------------------------------------------------------------------+ //| Compare CGCnvElement objects with each other by all properties | //+------------------------------------------------------------------+ bool CGCnvElement::IsEqual(CGCnvElement *compared_obj) const { int beg=0, end=CANV_ELEMENT_PROP_INTEGER_TOTAL; for(int i=beg; i<end; i++) { ENUM_CANV_ELEMENT_PROP_INTEGER prop=(ENUM_CANV_ELEMENT_PROP_INTEGER)i; if(this.GetProperty(prop)!=compared_obj.GetProperty(prop)) return false; } beg=end; end+=CANV_ELEMENT_PROP_DOUBLE_TOTAL; for(int i=beg; i<end; i++) { ENUM_CANV_ELEMENT_PROP_DOUBLE prop=(ENUM_CANV_ELEMENT_PROP_DOUBLE)i; if(this.GetProperty(prop)!=compared_obj.GetProperty(prop)) return false; } beg=end; end+=CANV_ELEMENT_PROP_STRING_TOTAL; for(int i=beg; i<end; i++) { ENUM_CANV_ELEMENT_PROP_STRING prop=(ENUM_CANV_ELEMENT_PROP_STRING)i; if(this.GetProperty(prop)!=compared_obj.GetProperty(prop)) return false; } return true; } //+------------------------------------------------------------------+
该方法也是所有函数库对象的标准方法。 简而言之,该方法接收对象参数,并与当前对象进行比较。 在所有对象属性的三重循环中,比较两个对象的每个新属性。 如果存在不相等的属性,则该方法返回 false — 比较的对象不相等。 直至三个循环完成后,返回 true — 所比较的两个对象所有属性都相等。
该方法创建图形元素对象:
//+------------------------------------------------------------------+ //| Create the graphical element object | //+------------------------------------------------------------------+ bool CGCnvElement::Create(const long chart_id, // Chart ID const int wnd_num, // Chart subwindow const string name, // Element name const int x, // X coordinate const int y, // Y coordinate const int w, // Width const int h, // Height const color colour, // Background color const uchar opacity, // Opacity const bool redraw=false) // Flag indicating the need to redraw { if(this.m_canvas.CreateBitmapLabel(chart_id,wnd_num,name,x,y,w,h,COLOR_FORMAT_ARGB_NORMALIZE)) { this.m_canvas.Erase(::ColorToARGB(colour,opacity)); this.m_canvas.Update(redraw); this.m_shift_y=(int)::ChartGetInteger(chart_id,CHART_WINDOW_YDISTANCE,wnd_num); return true; } return false; } //+------------------------------------------------------------------+
该方法接收构造所需的所有参数,调用 CCanvas 类中 CreateBitmapLabel() 方法的第二种形式。 如果与图表对象绑定的图形资源创建成功,则图形元素填充颜色,并调用 Update() 方法在屏幕上显示已实现的变更。 该方法接收屏幕重绘标志。 如果我们更新一个由若干个图形元素组成的复合对象,那么应该在所有复合对象元素都进行更改之后再重新绘制图表,如此可避免单个元素变更后即刷新图表。 接下来,m_shift 父类变量接收子窗口的 Y 坐标偏移量,并返回 true。 如果没有创建 CCanvas 类对象,则返回 false。
该方法返回光标相对于元素的位置:
//+------------------------------------------------------------------+ //| Return the cursor position relative to the element | //+------------------------------------------------------------------+ bool CGCnvElement::CursorInsideElement(const int x,const int y) { return(x>=this.CoordX() && x<=this.RightEdge() && y>=this.CoordY() && y<=this.BottomEdge()); } //+------------------------------------------------------------------+
该方法接收光标的 X 和 Y 坐标整数值,以及所传递的相对于元素维度的坐标位置 — 仅当光标位于元素内部时才返回 true。
该方法返回光标相对于元素活动区域的位置:
//+------------------------------------------------------------------+ //| Return the cursor position relative to the element active area | //+------------------------------------------------------------------+ bool CGCnvElement::CursorInsideActiveArea(const int x,const int y) { return(x>=this.ActiveAreaLeft() && x<=this.ActiveAreaRight() && y>=this.ActiveAreaTop() && y<=this.ActiveAreaBottom()); } //+------------------------------------------------------------------+
方法逻辑与前面的那个方法类似。 但返回的光标坐标位置是相对于元素活动区域的边界 — 只有当光标在活动区域之内时才返回 true 。
该方法更新元素坐标:
//+------------------------------------------------------------------+ //| Update the coordinate elements | //+------------------------------------------------------------------+ bool CGCnvElement::Move(const int x,const int y,const bool redraw=false) { //--- Leave if the element is not movable or inactive if(!this.Movable()) return false; //--- If failed to set new values into graphical object properties, return 'false' if(!this.SetCoordX(x) || !this.SetCoordY(y)) return false; //--- If the update flag is activated, redraw the chart. if(redraw) ::ChartRedraw(this.ChartID()); //--- Return 'true' return true; } //+------------------------------------------------------------------+
该方法接收需放置图形元素的左上角位置新坐标,以及图表重绘标志。 接下来,检查对象可移动标志,如果对象不可移动则离开。 如果调用下面研究的方法为对象设置新坐标失败,则返回 false。 接下来,如果设置了图表重绘标志,则更新图表.。 作为结果,返回 true。
该方法设置新的 X 坐标:
//+------------------------------------------------------------------+ //| Set the new X coordinate | //+------------------------------------------------------------------+ bool CGCnvElement::SetCoordX(const int coord_x) { int x=(int)::ObjectGetInteger(this.ChartID(),this.NameObj(),OBJPROP_XDISTANCE); if(coord_x==x) { if(coord_x==GetProperty(CANV_ELEMENT_PROP_COORD_X)) return true; this.SetProperty(CANV_ELEMENT_PROP_COORD_X,coord_x); return true; } if(::ObjectSetInteger(this.ChartID(),this.NameObj(),OBJPROP_XDISTANCE,coord_x)) { this.SetProperty(CANV_ELEMENT_PROP_COORD_X,coord_x); return true; } return false; } //+------------------------------------------------------------------+
该方法接收所需的 X 坐标值。 接着,从对象中获取此坐标。 如果传递的坐标与那个对象等于,则不应移动该对象。 但是我们需要检查对象属性中是否设置了相同的值。 如果值匹配,则返回 true,否则为对象属性设置所传递的新坐标值,并返回 true。
如果传递的坐标与对象坐标不匹配,则为对象设置新坐标。 如果设置成功,则将值写入对象属性,并返回 true。 在所有其他情况下,返回 false。
该方法设置新的 Y 坐标:
//+------------------------------------------------------------------+ //| Set the new Y coordinate | //+------------------------------------------------------------------+ bool CGCnvElement::SetCoordY(const int coord_y) { int y=(int)::ObjectGetInteger(this.ChartID(),this.NameObj(),OBJPROP_YDISTANCE); if(coord_y==y) { if(coord_y==GetProperty(CANV_ELEMENT_PROP_COORD_Y)) return true; this.SetProperty(CANV_ELEMENT_PROP_COORD_Y,coord_y); return true; } if(::ObjectSetInteger(this.ChartID(),this.NameObj(),OBJPROP_YDISTANCE,coord_y)) { this.SetProperty(CANV_ELEMENT_PROP_COORD_Y,coord_y); return true; } return false; } //+------------------------------------------------------------------+
方法逻辑与上面研究过的设置 X 坐标类似。
该方法设置新对象的宽度:
//+------------------------------------------------------------------+ //| Set the new width | //+------------------------------------------------------------------+ bool CGCnvElement::SetWidth(const int width) { return this.m_canvas.Resize(width,this.m_canvas.Height()); } //+------------------------------------------------------------------+
该方法接收对象的新宽度 ,和调用 Resize() 方法调整图形资源大小的结果。
Resize() 方法传递对象的新宽度和当前高度。
该方法设置对象新高度:
//+------------------------------------------------------------------+ //| Set the new height | //+------------------------------------------------------------------+ bool CGCnvElement::SetHeight(const int height) { return this.m_canvas.Resize(this.m_canvas.Width(),height); } //+------------------------------------------------------------------+
该方法接收对象的新高度 ,和调用 Resize() 方法调整图形资源大小的结果。
Resize() 方法传递对象的当前高度和新宽度。
请注意,在调整资源大小时,会覆盖画布上先前已绘制的图像。
因此,这些方法会在稍后细化。
该方法设置活动区域相对于元素的所有偏移:
//+------------------------------------------------------------------+ //| Set all shifts of the active area relative to the element | //+------------------------------------------------------------------+ void CGCnvElement::SetActiveAreaShift(const int left_shift,const int bottom_shift,const int right_shift,const int top_shift) { this.SetActiveAreaLeftShift(left_shift); this.SetActiveAreaBottomShift(bottom_shift); this.SetActiveAreaRightShift(right_shift); this.SetActiveAreaTopShift(top_shift); } //+------------------------------------------------------------------+
该方法接收距“图形元素”对象边缘的所有必要的向内偏移值。 所有四个偏移值都是通过调用相应方法逐一设置的。
的方法该置元素不透明度:
//+------------------------------------------------------------------+ //| Set the element opacity | //+------------------------------------------------------------------+ void CGCnvElement::SetOpacity(const uchar value,const bool redraw=false) { this.m_canvas.TransparentLevelSet(value); this.SetProperty(CANV_ELEMENT_PROP_OPACITY,value); this.m_canvas.Update(redraw); } //+------------------------------------------------------------------+
该方法接收所需的对象不透明度值(0 — 完全透明,255 — 完全不透明),和图表重绘标志。
接下来,调用 CCanvas 类的 TransparentLevelSet() 方法,将新的属性值写入对象属性,并依据所传递重绘标志刷新对象。
“图形元素”对象已准备就绪。 现在,我们需要在存储它们的列表中针对这些对象进行排序。 为达此目的,我们需要 CSelect 类,在该类中我们设置针对所有函数库对象进行排序和搜索的方法。
打开 \MQL5\Include\DoEasy\Services\Select.mqh,并包含“图形元素”对象类文件,以及在类主体末尾声明依据其属性对“图形元素”对象进行排序和搜索的方法:
//+------------------------------------------------------------------+ //| Select.mqh | //| Copyright 2020, MetaQuotes Software Corp. | //| https://mql5.com/en/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2020, MetaQuotes Software Corp." #property link "https://mql5.com/en/users/artmedia70" #property version "1.00" //+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include <Arrays\ArrayObj.mqh> #include "..\Objects\Orders\Order.mqh" #include "..\Objects\Events\Event.mqh" #include "..\Objects\Accounts\Account.mqh" #include "..\Objects\Symbols\Symbol.mqh" #include "..\Objects\PendRequest\PendRequest.mqh" #include "..\Objects\Series\SeriesDE.mqh" #include "..\Objects\Indicators\Buffer.mqh" #include "..\Objects\Indicators\IndicatorDE.mqh" #include "..\Objects\Indicators\DataInd.mqh" #include "..\Objects\Ticks\DataTick.mqh" #include "..\Objects\Book\MarketBookOrd.mqh" #include "..\Objects\MQLSignalBase\MQLSignal.mqh" #include "..\Objects\Chart\ChartObj.mqh" #include "..\Objects\Graph\GCnvElement.mqh" //+------------------------------------------------------------------+
...
//+--------------------------------------------------------------------------+ //| The methods of working with data of the graphical elements on the canvas | //+--------------------------------------------------------------------------+ //--- Return the list of objects with one of (1) integer, (2) real and (3) string properties meeting a specified criterion static CArrayObj *ByGraphCanvElementProperty(CArrayObj *list_source,ENUM_CANV_ELEMENT_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode); static CArrayObj *ByGraphCanvElementProperty(CArrayObj *list_source,ENUM_CANV_ELEMENT_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode); static CArrayObj *ByGraphCanvElementProperty(CArrayObj *list_source,ENUM_CANV_ELEMENT_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode); //--- Return the chart index with the maximum value of the (1) integer, (2) real and (3) string properties static int FindGraphCanvElementMax(CArrayObj *list_source,ENUM_CANV_ELEMENT_PROP_INTEGER property); static int FindGraphCanvElementMax(CArrayObj *list_source,ENUM_CANV_ELEMENT_PROP_DOUBLE property); static int FindGraphCanvElementMax(CArrayObj *list_source,ENUM_CANV_ELEMENT_PROP_STRING property); //--- Return the chart index with the minimum value of the (1) integer, (2) real and (3) string properties static int FindGraphCanvElementMin(CArrayObj *list_source,ENUM_CANV_ELEMENT_PROP_INTEGER property); static int FindGraphCanvElementMin(CArrayObj *list_source,ENUM_CANV_ELEMENT_PROP_DOUBLE property); static int FindGraphCanvElementMin(CArrayObj *list_source,ENUM_CANV_ELEMENT_PROP_STRING property); //--- }; //+------------------------------------------------------------------+
在文件清单的末尾,添加新声明方法的实现:
//+------------------------------------------------------------------+ //+---------------------------------------------------------------------------+ //| The methods of working with data of the graphical elements on the canvas | //+---------------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Return the list of objects with one integer | //| property meeting the specified criterion | //+------------------------------------------------------------------+ CArrayObj *CSelect::ByGraphCanvElementProperty(CArrayObj *list_source,ENUM_CANV_ELEMENT_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode) { if(list_source==NULL) return NULL; CArrayObj *list=new CArrayObj(); if(list==NULL) return NULL; list.FreeMode(false); ListStorage.Add(list); int total=list_source.Total(); for(int i=0; i<total; i++) { CGCnvElement *obj=list_source.At(i); if(!obj.SupportProperty(property)) continue; long obj_prop=obj.GetProperty(property); if(CompareValues(obj_prop,value,mode)) list.Add(obj); } return list; } //+------------------------------------------------------------------+ //| Return the list of objects with one real | //| property meeting the specified criterion | //+------------------------------------------------------------------+ CArrayObj *CSelect::ByGraphCanvElementProperty(CArrayObj *list_source,ENUM_CANV_ELEMENT_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode) { if(list_source==NULL) return NULL; CArrayObj *list=new CArrayObj(); if(list==NULL) return NULL; list.FreeMode(false); ListStorage.Add(list); for(int i=0; i<list_source.Total(); i++) { CGCnvElement *obj=list_source.At(i); if(!obj.SupportProperty(property)) continue; double obj_prop=obj.GetProperty(property); if(CompareValues(obj_prop,value,mode)) list.Add(obj); } return list; } //+------------------------------------------------------------------+ //| Return the list of objects with one string | //| property meeting the specified criterion | //+------------------------------------------------------------------+ CArrayObj *CSelect::ByGraphCanvElementProperty(CArrayObj *list_source,ENUM_CANV_ELEMENT_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode) { if(list_source==NULL) return NULL; CArrayObj *list=new CArrayObj(); if(list==NULL) return NULL; list.FreeMode(false); ListStorage.Add(list); for(int i=0; i<list_source.Total(); i++) { CGCnvElement *obj=list_source.At(i); if(!obj.SupportProperty(property)) continue; string obj_prop=obj.GetProperty(property); if(CompareValues(obj_prop,value,mode)) list.Add(obj); } return list; } //+------------------------------------------------------------------+ //| Return the object index in the list | //| with the maximum integer property value | //+------------------------------------------------------------------+ int CSelect::FindGraphCanvElementMax(CArrayObj *list_source,ENUM_CANV_ELEMENT_PROP_INTEGER property) { if(list_source==NULL) return WRONG_VALUE; int index=0; CGCnvElement *max_obj=NULL; int total=list_source.Total(); if(total==0) return WRONG_VALUE; for(int i=1; i<total; i++) { CGCnvElement *obj=list_source.At(i); long obj1_prop=obj.GetProperty(property); max_obj=list_source.At(index); long obj2_prop=max_obj.GetProperty(property); if(CompareValues(obj1_prop,obj2_prop,MORE)) index=i; } return index; } //+------------------------------------------------------------------+ //| Return the object index in the list | //| with the maximum real property value | //+------------------------------------------------------------------+ int CSelect::FindGraphCanvElementMax(CArrayObj *list_source,ENUM_CANV_ELEMENT_PROP_DOUBLE property) { if(list_source==NULL) return WRONG_VALUE; int index=0; CGCnvElement *max_obj=NULL; int total=list_source.Total(); if(total==0) return WRONG_VALUE; for(int i=1; i<total; i++) { CGCnvElement *obj=list_source.At(i); double obj1_prop=obj.GetProperty(property); max_obj=list_source.At(index); double obj2_prop=max_obj.GetProperty(property); if(CompareValues(obj1_prop,obj2_prop,MORE)) index=i; } return index; } //+------------------------------------------------------------------+ //| Return the object index in the list | //| with the maximum string property value | //+------------------------------------------------------------------+ int CSelect::FindGraphCanvElementMax(CArrayObj *list_source,ENUM_CANV_ELEMENT_PROP_STRING property) { if(list_source==NULL) return WRONG_VALUE; int index=0; CGCnvElement *max_obj=NULL; int total=list_source.Total(); if(total==0) return WRONG_VALUE; for(int i=1; i<total; i++) { CGCnvElement *obj=list_source.At(i); string obj1_prop=obj.GetProperty(property); max_obj=list_source.At(index); string obj2_prop=max_obj.GetProperty(property); if(CompareValues(obj1_prop,obj2_prop,MORE)) index=i; } return index; } //+------------------------------------------------------------------+ //| Return the object index in the list | //| with the minimum integer property value | //+------------------------------------------------------------------+ int CSelect::FindGraphCanvElementMin(CArrayObj* list_source,ENUM_CANV_ELEMENT_PROP_INTEGER property) { int index=0; CGCnvElement *min_obj=NULL; int total=list_source.Total(); if(total==0) return WRONG_VALUE; for(int i=1; i<total; i++) { CGCnvElement *obj=list_source.At(i); long obj1_prop=obj.GetProperty(property); min_obj=list_source.At(index); long obj2_prop=min_obj.GetProperty(property); if(CompareValues(obj1_prop,obj2_prop,LESS)) index=i; } return index; } //+------------------------------------------------------------------+ //| Return the object index in the list | //| with the minimum real property value | //+------------------------------------------------------------------+ int CSelect::FindGraphCanvElementMin(CArrayObj* list_source,ENUM_CANV_ELEMENT_PROP_DOUBLE property) { int index=0; CGCnvElement *min_obj=NULL; int total=list_source.Total(); if(total== 0) return WRONG_VALUE; for(int i=1; i<total; i++) { CGCnvElement *obj=list_source.At(i); double obj1_prop=obj.GetProperty(property); min_obj=list_source.At(index); double obj2_prop=min_obj.GetProperty(property); if(CompareValues(obj1_prop,obj2_prop,LESS)) index=i; } return index; } //+------------------------------------------------------------------+ //| Return the object index in the list | //| with the minimum string property value | //+------------------------------------------------------------------+ int CSelect::FindGraphCanvElementMin(CArrayObj* list_source,ENUM_CANV_ELEMENT_PROP_STRING property) { int index=0; CGCnvElement *min_obj=NULL; int total=list_source.Total(); if(total==0) return WRONG_VALUE; for(int i=1; i<total; i++) { CGCnvElement *obj=list_source.At(i); string obj1_prop=obj.GetProperty(property); min_obj=list_source.At(index); string obj2_prop=min_obj.GetProperty(property); if(CompareValues(obj1_prop,obj2_prop,LESS)) index=i; } return index; } //+------------------------------------------------------------------+
这些方法在第三篇文章中我们讨论创建 CSelect 类时已进行了讲述。
我们测试一下结果。
测试
为了执行测试,我们借用上一篇文章中的 EA 并将其保存在 \MQL5\Experts\TestDoEasy\Part74\ 里,命名为 TestDoEasyPart74.mq5。
包含指向 CObject 类及其后代实例的动态指针数组的类的文件、标准库、 CSelect 和 CGCnvElement 库类文件,指定创建的“图形元素”对象的数量,并声明存储所创建图形元素的列表:
//+------------------------------------------------------------------+ //| TestDoEasyPart74.mq5 | //| Copyright 2021, MetaQuotes Ltd. | //| https://mql5.com/en/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2021, MetaQuotes Ltd." #property link "https://mql5.com/en/users/artmedia70" #property version "1.00" //--- includes #include <Arrays\ArrayObj.mqh> #include <DoEasy\Services\Select.mqh> #include <DoEasy\Objects\Graph\GCnvElement.mqh> //--- defines #define FORMS_TOTAL (2) //--- input parameters sinput bool InpMovable = true; // Movable flag //--- global variables CArrayObj list_elements; //+------------------------------------------------------------------+
在 EA 的 OnInit() 处理程序中,把所有必要的参数传递给类构造函数来创建新的图形元素对象:
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- Set the permissions to send cursor movement and mouse scroll events ChartSetInteger(ChartID(),CHART_EVENT_MOUSE_MOVE,true); ChartSetInteger(ChartID(),CHART_EVENT_MOUSE_WHEEL,true); //--- Set EA global variables //--- Create the specified number of graphical elements on the canvas int total=FORMS_TOTAL; for(int i=0;i<total;i++) { //--- When creating an object, pass all the required parameters to it CGCnvElement *element=new CGCnvElement(GRAPH_ELEMENT_TYPE_ELEMENT,i,0,ChartID(),0,"Element_0"+(string)(i+1),300,40+(i*80),100,70,clrSilver,200,InpMovable,true,true); if(element==NULL) continue; //--- Add objects to the list if(!list_elements.Add(element)) { delete element; continue; } } //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+
在 OnDeinit() 处理程序中,删除图表中的所有注释:
//+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- destroy timer EventKillTimer(); Comment(""); } //+------------------------------------------------------------------+
在 OnChartEvent() 处理程序中,捕获对象单击事件,获取名称与处理程序 sparam 参数中设置的对象名称对应的元素对象,并将其不透明度级别加 5。 在图表注释中显示含有已处理的对象名称和不透明度级别的消息:
//+------------------------------------------------------------------+ //| ChartEvent function | //+------------------------------------------------------------------+ void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { //--- If clicking on an object if(id==CHARTEVENT_OBJECT_CLICK) { //--- In the new list, get the element object with the name corresponding to the sparam string parameter value of the OnChartEvent() handler CArrayObj *obj_list=CSelect::ByGraphCanvElementProperty(GetPointer(list_elements),CANV_ELEMENT_PROP_NAME_OBJ,sparam,EQUAL); if(obj_list!=NULL && obj_list.Total()>0) { //--- Get the pointer to the object in the list CGCnvElement *obj=obj_list.At(0); //--- and set the new opacity level for it uchar opasity=obj.Opacity(); if((opasity+5)>255) opasity=0; else opasity+=5; //--- Set the new opacity to the object and display the object name and opacity level in the journal obj.SetOpacity(opasity); Comment(DFUN,"Object name: ",obj.NameObj(),", opasity=",opasity); } } } //+------------------------------------------------------------------+
编译 EA,并在品种图表上启动它。 单击任何“图形元素”对象时,其不透明度增加到 255,然后在达到最大值 (255) 时,它轮回从 0 增加到 255,同时在图表注释中显示单击对象的名称,及其不透明度:
下一步是什么?
在下一篇文章中,我将继续开发“图形元素”对象,并开始在其上添加显示图形基元和文本的方法。
以下是该函数库当前版本的所有文件,以及 MQL5 的测试 EA 文件,供您测试和下载。
请您在评论中留下问题和建议。
*该系列的前几篇文章:
本文由MetaQuotes Ltd译自俄文
原文地址: https://www.mql5.com/ru/articles/9493


