DoEasyライブラリのグラフィックス(第100部):拡張された標準グラフィックオブジェクトの処理を改善する
内容
概念
前の何稿かに渡って、拡張された標準グラフィックオブジェクトに基づいて作成された複合グラフィックオブジェクトを処理する機能を開発してきました。この機能の作成は、記事ごとに徐々に進めてきましたが、時々、トピックからトピックに移動することを余儀なくされました。まず、CCanvasクラスに基づいてオブジェクトを作成しましたが、すべてのライブラリオブジェクトへのオブジェクトには標準グラフィックオブジェクトの少なくとも部分的に機能する機能が必要だったため、次に、グラフィックオブジェクトの作成を開始することになりました。その後、高度なグラフィックオブジェクトではキャンバスに基づくクラスの改善が必要だったため、CCanvasに基づくフォームオブジェクトに戻りました。ここでは、キャンバス上のオブジェクトの開発を続行するために、もう一度進む必要があります。
したがって、現在の記事では、拡張(および標準)グラフィックオブジェクトとフォームオブジェクトをキャンバス上で同時に処理する際の明らかな欠陥を排除し、前の記事で実行したテスト中に検出されたエラーを修正します。ライブラリの説明のこのセクションは本稿で締めくくります。次の記事では、新しいセクションを開始し、MSVisualStudioでWindowsフォームを模倣するキャンバス上のグラフィックオブジェクトの開発を始めます。これらのオブジェクトは、拡張された標準グラフィックオブジェクト、およびそれらに基づく複合オブジェクトの開発を継続するために必要になります。
ライブラリクラスの改善
チャートにGUI要素(要素、形状、ウィンドウ(まだ実装されていない)または他の同様のコントロール要素)を作成するためのキャンバス上のグラフィックオブジェクトが含まれている場合、標準グラフィックオブジェクトやそれらに基づく他のライブラリオブジェクトをチャートに(手動でまたはプログラムで)配置すると、これらのオブジェクトがコントロールの上に描画されることになって不便です。したがって、チャート上の新しいグラフィックオブジェクトを追跡し、すべてのGUI要素を前面に移動するためのメカニズムを開発する必要があります。これを実現するために、ZOrderグラフィックオブジェクトプロパティ(チャートをクリックしたイベントを受信するためのグラフィックオブジェクトの優先度(CHARTEVENT_CLICK))を使用できます。
以下はヘルプ情報です。
優先度はオブジェクトの作成時にデフォルトのゼロ値が設定されますが、必要に応じて上げることができます。オブジェクトを重ねて適用すると、優先度が最も高いオブジェクト1つだけがCHARTEVENT_CLICKイベントを受け取ります。
ただし、ここではこのプロパティをより広く使用します。このプロパティの値は、GUI要素が相互に、および他のグラフィックオブジェクトに対して配置される順序を示します。
\MQL5\Include\DoEasy\Defines.mqhで、キャンバス上のグラフィック要素の整数プロパティの列挙に新しいプロパティを追加し、整数プロパティの数を23から24に増やします。
//+------------------------------------------------------------------+ //| Integer properties of the graphical element on the canvas | //+------------------------------------------------------------------+ enum ENUM_CANV_ELEMENT_PROP_INTEGER { CANV_ELEMENT_PROP_ID = 0, // Element ID CANV_ELEMENT_PROP_TYPE, // Graphical element type CANV_ELEMENT_PROP_BELONG, // Graphical element affiliation 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, // Element width CANV_ELEMENT_PROP_HEIGHT, // Element height CANV_ELEMENT_PROP_RIGHT, // Element right border CANV_ELEMENT_PROP_BOTTOM, // Element bottom border CANV_ELEMENT_PROP_ACT_SHIFT_LEFT, // Active area offset from the left edge of the element CANV_ELEMENT_PROP_ACT_SHIFT_TOP, // Active area offset from the upper edge of the element CANV_ELEMENT_PROP_ACT_SHIFT_RIGHT, // Active area offset from the right edge of the element CANV_ELEMENT_PROP_ACT_SHIFT_BOTTOM, // Active area offset from the bottom edge of the element CANV_ELEMENT_PROP_MOVABLE, // Element moveability flag CANV_ELEMENT_PROP_ACTIVE, // Element activity flag CANV_ELEMENT_PROP_INTERACTION, // Flag of interaction with the outside environment CANV_ELEMENT_PROP_COORD_ACT_X, // X coordinate of the element active area CANV_ELEMENT_PROP_COORD_ACT_Y, // Y coordinate of the element active area CANV_ELEMENT_PROP_ACT_RIGHT, // Right border of the element active area CANV_ELEMENT_PROP_ACT_BOTTOM, // Bottom border of the element active area CANV_ELEMENT_PROP_ZORDER, // Priority of a graphical object for receiving the event of clicking on a chart }; #define CANV_ELEMENT_PROP_INTEGER_TOTAL (24) // Total number of integer properties #define CANV_ELEMENT_PROP_INTEGER_SKIP (0) // Number of integer properties not used in sorting //+------------------------------------------------------------------+
キャンバス上のグラフィック要素を並べ替える可能性のある基準の列挙に新しいプロパティを追加します。
//+------------------------------------------------------------------+ //| 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 element ID SORT_BY_CANV_ELEMENT_TYPE, // Sort by graphical element type SORT_BY_CANV_ELEMENT_BELONG, // Sort by a graphical element affiliation 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 element X coordinate on the chart SORT_BY_CANV_ELEMENT_COORD_Y, // Sort by the element Y coordinate on the chart SORT_BY_CANV_ELEMENT_WIDTH, // Sort by the element width SORT_BY_CANV_ELEMENT_HEIGHT, // Sort by the element height SORT_BY_CANV_ELEMENT_RIGHT, // Sort by the element right border SORT_BY_CANV_ELEMENT_BOTTOM, // Sort by the element bottom border SORT_BY_CANV_ELEMENT_ACT_SHIFT_LEFT, // Sort by the active area offset from the left edge of the element SORT_BY_CANV_ELEMENT_ACT_SHIFT_TOP, // Sort by the active area offset from the top edge of the element SORT_BY_CANV_ELEMENT_ACT_SHIFT_RIGHT, // Sort by the active area offset from the right edge of the element SORT_BY_CANV_ELEMENT_ACT_SHIFT_BOTTOM, // Sort by the active area offset from the bottom edge of the element SORT_BY_CANV_ELEMENT_MOVABLE, // Sort by the element moveability flag SORT_BY_CANV_ELEMENT_ACTIVE, // Sort by the element activity flag SORT_BY_CANV_ELEMENT_INTERACTION, // Sort by the flag of interaction with the outside environment SORT_BY_CANV_ELEMENT_COORD_ACT_X, // Sort by X coordinate of the element active area SORT_BY_CANV_ELEMENT_COORD_ACT_Y, // Sort by Y coordinate of the element active area SORT_BY_CANV_ELEMENT_ACT_RIGHT, // Sort by the right border of the element active area SORT_BY_CANV_ELEMENT_ACT_BOTTOM, // Sort by the bottom border of the element active area SORT_BY_CANV_ELEMENT_ZORDER, // Sort by the priority of a graphical object for receiving the event of clicking on a chart //--- Sort by real properties //--- Sort by string properties SORT_BY_CANV_ELEMENT_NAME_OBJ = FIRST_CANV_ELEMENT_STR_PROP,// Sort by an element object name SORT_BY_CANV_ELEMENT_NAME_RES, // Sort by the graphical resource name }; //+------------------------------------------------------------------+
これで、このプロパティによってキャンバス上のグラフィック要素を選択して並べ替えることができるようになります。
ライブラリ内のすべてのグラフィックオブジェクトは、ライブラリのすべてのグラフィックオブジェクトの基本オブジェクト(標準グラフィックオブジェクトとキャンバス上のグラフィック要素)から派生しています。基本グラフィックオブジェクトクラスのファイル(\MQL5\Include\DoEasy\Objects\Graph\GBaseObj.mqh)では、ZOrderプロパティを処理するためのメソッドは仮想化されています。
//--- Set the "Disable displaying the name of a graphical object in the terminal object list" flag bool SetFlagHidden(const bool flag,const bool only_prop) { ::ResetLastError(); if((!only_prop && ::ObjectSetInteger(this.m_chart_id,this.m_name,OBJPROP_SELECTABLE,flag)) || only_prop) { this.m_hidden=flag; return true; } else CMessage::ToLog(DFUN,::GetLastError(),true); return false; } //--- Set the priority of a graphical object for receiving the event of clicking on a chart virtual bool SetZorder(const long value,const bool only_prop) { ::ResetLastError(); if((!only_prop && ::ObjectSetInteger(this.m_chart_id,this.m_name,OBJPROP_ZORDER,value)) || only_prop) { this.m_zorder=value; return true; } else CMessage::ToLog(DFUN,::GetLastError(),true); return false; } //--- Set object visibility on all timeframes bool SetVisible(const bool flag,const bool only_prop) { long value=(flag ? OBJ_ALL_PERIODS : OBJ_NO_PERIODS); ::ResetLastError(); if((!only_prop && ::ObjectSetInteger(this.m_chart_id,this.m_name,OBJPROP_TIMEFRAMES,value)) || only_prop) { this.m_visible=flag; return true; } else CMessage::ToLog(DFUN,::GetLastError(),true); return false; } //--- Set visibility flags on timeframes specified as flags
...
//--- Return the values of class variables ENUM_GRAPH_ELEMENT_TYPE TypeGraphElement(void) const { return this.m_type_element; } ENUM_GRAPH_OBJ_BELONG Belong(void) const { return this.m_belong; } ENUM_GRAPH_OBJ_SPECIES Species(void) const { return this.m_species; } ENUM_OBJECT TypeGraphObject(void) const { return this.m_type_graph_obj; } datetime TimeCreate(void) const { return this.m_create_time; } string Name(void) const { return this.m_name; } long ChartID(void) const { return this.m_chart_id; } long ObjectID(void) const { return this.m_object_id; } virtual long Zorder(void) const { return this.m_zorder; } int SubWindow(void) const { return this.m_subwindow; } int ShiftY(void) const { return this.m_shift_y; } int VisibleOnTimeframes(void) const { return this.m_timeframes_visible; } int Digits(void) const { return this.m_digits; } int Group(void) const { return this.m_group; } bool IsBack(void) const { return this.m_back; } bool IsSelected(void) const { return this.m_selected; } bool IsSelectable(void) const { return this.m_selectable; } bool IsHidden(void) const { return this.m_hidden; } bool IsVisible(void) const { return this.m_visible; } //--- Return the graphical object type (ENUM_OBJECT) calculated from the object type (ENUM_OBJECT_DE_TYPE) passed to the method
同じメソッドが、すべてのライブラリグラフィックオブジェクトの基本オブジェクトクラスに存在します。
それらでもメソッドが仮想化されました。
次に、\MQL5\Include\DoEasy\Objects\Graph\Standard\GStdGraphObj.mqhの抽象標準グラフィックオブジェクトトクラスを改善します。
//--- Background object bool Back(void) const { return (bool)this.GetProperty(GRAPH_OBJ_PROP_BACK,0); } bool SetFlagBack(const bool flag,const bool only_prop) { if(!CGBaseObj::SetFlagBack(flag,only_prop)) return false; this.SetProperty(GRAPH_OBJ_PROP_BACK,0,flag); return true; } //--- Priority of a graphical object for receiving the event of clicking on a chart virtual long Zorder(void) const { return this.GetProperty(GRAPH_OBJ_PROP_ZORDER,0); } virtual bool SetZorder(const long value,const bool only_prop) { if(!CGBaseObj::SetZorder(value,only_prop)) return false; this.SetProperty(GRAPH_OBJ_PROP_ZORDER,0,value); return true; } //--- Disable displaying the name of a graphical object in the terminal object list bool Hidden(void) const { return (bool)this.GetProperty(GRAPH_OBJ_PROP_HIDDEN,0); } bool SetFlagHidden(const bool flag,const bool only_prop) { if(!CGBaseObj::SetFlagHidden(flag,only_prop)) return false; this.SetProperty(GRAPH_OBJ_PROP_HIDDEN,0,flag); return true; } //--- Object selection
グラフィック要素オブジェクトクラスのファイル(MQL5\Include\DoEasy\Objects\Graph\GCnvElement.mqh)に同じ仮想メソッドを追加します。
//--- (1) Show and (2) hide the element virtual void Show(void) { CGBaseObj::SetVisible(true,false); } virtual void Hide(void) { CGBaseObj::SetVisible(false,false); } //--- Priority of a graphical object for receiving the event of clicking on a chart virtual long Zorder(void) const { return this.GetProperty(CANV_ELEMENT_PROP_ZORDER); } virtual bool SetZorder(const long value,const bool only_prop) { if(!CGBaseObj::SetZorder(value,only_prop)) return false; this.SetProperty(CANV_ELEMENT_PROP_ZORDER,value); return true; } //+------------------------------------------------------------------+ //| The methods of receiving raster data | //+------------------------------------------------------------------+
グラフィック要素のオブジェクトクラスは、保存および復元するためのオブジェクトプロパティの構造体をすでに備えているため、新しい整数プロパティのフィールドを追加する必要があります。
//--- Create (1) the object structure and (2) the object from the structure virtual bool ObjectToStruct(void); virtual void StructToObject(void); private: struct SData { //--- Object integer properties int id; // Element ID int type; // Graphical element type int number; // Element index in the list long chart_id; // Chart ID int subwindow; // Chart subwindow index int coord_x; // Form's X coordinate on the chart int coord_y; // Form's Y coordinate on the chart int width; // Element width int height; // Element height int edge_right; // Element right border int edge_bottom; // Element bottom border int act_shift_left; // Active area offset from the left edge of the element int act_shift_top; // Active area offset from the top edge of the element int act_shift_right; // Active area offset from the right edge of the element int act_shift_bottom; // Active area offset from the bottom edge of the element uchar opacity; // Element opacity color color_bg; // Element background color bool movable; // Element moveability flag bool active; // Element activity flag bool interaction; // Flag of interaction with the outside environment int coord_act_x; // X coordinate of the element active area int coord_act_y; // Y coordinate of the element active area int coord_act_right; // Right border of the element active area int coord_act_bottom; // Bottom border of the element active area long zorder; // Priority of a graphical object for receiving the event of clicking on a chart //--- Object real properties //--- Object string properties uchar name_obj[64]; // Graphical element object name uchar name_res[64]; // Graphical resource name }; SData m_struct_obj; // Object structure
オブジェクト構造体を作成するメソッドで、新しいオブジェクトプロパティを保存するようにします。
//+------------------------------------------------------------------+ //| Create the object structure | //+------------------------------------------------------------------+ bool CGCnvElement::ObjectToStruct(void) { //--- Save integer properties this.m_struct_obj.id=(int)this.GetProperty(CANV_ELEMENT_PROP_ID); // Element ID this.m_struct_obj.type=(int)this.GetProperty(CANV_ELEMENT_PROP_TYPE); // Graphical element type this.m_struct_obj.number=(int)this.GetProperty(CANV_ELEMENT_PROP_NUM); // Eleemnt ID in the list this.m_struct_obj.chart_id=this.GetProperty(CANV_ELEMENT_PROP_CHART_ID); // Chart ID this.m_struct_obj.subwindow=(int)this.GetProperty(CANV_ELEMENT_PROP_WND_NUM); // Chart subwindow index this.m_struct_obj.coord_x=(int)this.GetProperty(CANV_ELEMENT_PROP_COORD_X); // Form's X coordinate on the chart this.m_struct_obj.coord_y=(int)this.GetProperty(CANV_ELEMENT_PROP_COORD_Y); // Form's Y coordinate on the chart this.m_struct_obj.width=(int)this.GetProperty(CANV_ELEMENT_PROP_WIDTH); // Element width this.m_struct_obj.height=(int)this.GetProperty(CANV_ELEMENT_PROP_HEIGHT); // Element height this.m_struct_obj.edge_right=(int)this.GetProperty(CANV_ELEMENT_PROP_RIGHT); // Element right edge this.m_struct_obj.edge_bottom=(int)this.GetProperty(CANV_ELEMENT_PROP_BOTTOM); // Element bottom edge this.m_struct_obj.act_shift_left=(int)this.GetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_LEFT); // Active area offset from the left edge of the element this.m_struct_obj.act_shift_top=(int)this.GetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_TOP); // Active area offset from the top edge of the element this.m_struct_obj.act_shift_right=(int)this.GetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_RIGHT); // Active area offset from the right edge of the element this.m_struct_obj.act_shift_bottom=(int)this.GetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_BOTTOM);// Active area offset from the bottom edge of the element this.m_struct_obj.movable=(bool)this.GetProperty(CANV_ELEMENT_PROP_MOVABLE); // Element moveability flag this.m_struct_obj.active=(bool)this.GetProperty(CANV_ELEMENT_PROP_ACTIVE); // Element activity flag this.m_struct_obj.interaction=(bool)this.GetProperty(CANV_ELEMENT_PROP_INTERACTION); // Flag of interaction with the outside environment this.m_struct_obj.coord_act_x=(int)this.GetProperty(CANV_ELEMENT_PROP_COORD_ACT_X); // X coordinate of the element active area this.m_struct_obj.coord_act_y=(int)this.GetProperty(CANV_ELEMENT_PROP_COORD_ACT_Y); // Y coordinate of the element active area this.m_struct_obj.coord_act_right=(int)this.GetProperty(CANV_ELEMENT_PROP_ACT_RIGHT); // Right border of the element active area this.m_struct_obj.coord_act_bottom=(int)this.GetProperty(CANV_ELEMENT_PROP_ACT_BOTTOM); // Bottom border of the element active area this.m_struct_obj.color_bg=this.m_color_bg; // Element background color this.m_struct_obj.opacity=this.m_opacity; // Element opacity this.m_struct_obj.zorder=this.m_zorder; // Priority of a graphical object for receiving the on-chart mouse click event //--- Save real properties //--- Save string properties ::StringToCharArray(this.GetProperty(CANV_ELEMENT_PROP_NAME_OBJ),this.m_struct_obj.name_obj);// Graphical element object name ::StringToCharArray(this.GetProperty(CANV_ELEMENT_PROP_NAME_RES),this.m_struct_obj.name_res);// Graphical resource name //--- Save the structure to the uchar array ::ResetLastError(); if(!::StructToCharArray(this.m_struct_obj,this.m_uchar_array)) { CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_SAVE_OBJ_STRUCT_TO_UARRAY,true); return false; } return true; } //+------------------------------------------------------------------+
構造体からオブジェクトを復元するメソッドで、 プロパティの読み取りをオブジェクトに追加します。
//+------------------------------------------------------------------+ //| Create the object from the structure | //+------------------------------------------------------------------+ void CGCnvElement::StructToObject(void) { //--- Save integer properties this.SetProperty(CANV_ELEMENT_PROP_ID,this.m_struct_obj.id); // Element ID this.SetProperty(CANV_ELEMENT_PROP_TYPE,this.m_struct_obj.type); // Graphical element type this.SetProperty(CANV_ELEMENT_PROP_NUM,this.m_struct_obj.number); // Element index in the list this.SetProperty(CANV_ELEMENT_PROP_CHART_ID,this.m_struct_obj.chart_id); // Chart ID this.SetProperty(CANV_ELEMENT_PROP_WND_NUM,this.m_struct_obj.subwindow); // Chart subwindow index this.SetProperty(CANV_ELEMENT_PROP_COORD_X,this.m_struct_obj.coord_x); // Form's X coordinate on the chart this.SetProperty(CANV_ELEMENT_PROP_COORD_Y,this.m_struct_obj.coord_y); // Form's Y coordinate on the chart this.SetProperty(CANV_ELEMENT_PROP_WIDTH,this.m_struct_obj.width); // Element width this.SetProperty(CANV_ELEMENT_PROP_HEIGHT,this.m_struct_obj.height); // Element height this.SetProperty(CANV_ELEMENT_PROP_RIGHT,this.m_struct_obj.edge_right); // Element right edge this.SetProperty(CANV_ELEMENT_PROP_BOTTOM,this.m_struct_obj.edge_bottom); // Element bottom edge this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_LEFT,this.m_struct_obj.act_shift_left); // Active area offset from the left edge of the element this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_TOP,this.m_struct_obj.act_shift_top); // Active area offset from the upper edge of the element this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_RIGHT,this.m_struct_obj.act_shift_right); // Active area offset from the right edge of the element this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_BOTTOM,this.m_struct_obj.act_shift_bottom); // Active area offset from the bottom edge of the element this.SetProperty(CANV_ELEMENT_PROP_MOVABLE,this.m_struct_obj.movable); // Element moveability flag this.SetProperty(CANV_ELEMENT_PROP_ACTIVE,this.m_struct_obj.active); // Element activity flag this.SetProperty(CANV_ELEMENT_PROP_INTERACTION,this.m_struct_obj.interaction); // Flag of interaction with the outside environment this.SetProperty(CANV_ELEMENT_PROP_COORD_ACT_X,this.m_struct_obj.coord_act_x); // X coordinate of the element active area this.SetProperty(CANV_ELEMENT_PROP_COORD_ACT_Y,this.m_struct_obj.coord_act_y); // Y coordinate of the element active area this.SetProperty(CANV_ELEMENT_PROP_ACT_RIGHT,this.m_struct_obj.coord_act_right); // Right border of the element active area this.SetProperty(CANV_ELEMENT_PROP_ACT_BOTTOM,this.m_struct_obj.coord_act_bottom); // Bottom border of the element active area this.m_color_bg=this.m_struct_obj.color_bg; // Element background color this.m_opacity=this.m_struct_obj.opacity; // Element opacity this.m_zorder=this.m_struct_obj.zorder; // Priority of a graphical object for receiving the on-chart mouse click event //--- Save real properties //--- Save string properties this.SetProperty(CANV_ELEMENT_PROP_NAME_OBJ,::CharArrayToString(this.m_struct_obj.name_obj));// Graphical element object name this.SetProperty(CANV_ELEMENT_PROP_NAME_RES,::CharArrayToString(this.m_struct_obj.name_res));// Graphical resource name } //+------------------------------------------------------------------+
これらすべては、オブジェクトをディスクに正しく保存し、将来ライブラリオブジェクトをファイルに読み書きするときにディスクから復元するために必要です。 これは、ターミナルが再起動されたときに、ライブラリがプログラムとそのデータの状態を復元して通常どおり作業を再開できるようにするために必要になりますが、これはすべて後で実装されます。それまではただ、必要な機能を準備することにします。
次に、\MQL5\Include\DoEasy\Objects\Graph\Standard\GStdGraphObj.mqhの抽象標準グラフィックオブジェクトクラスを改善します。
将来、拡張された標準グラフィックオブジェクトから複合グラフィックオブジェクトを構築するときに、オブジェクト全体の最小座標と最大座標を知る必要があるため、標準グラフィックオブジェクトのX&Y座標の最小&最大値を見つけて返すメソッドを追加します。後で、これらのメソッドに基づいて、複合グラフィックオブジェクト全体の最小/最大座標を取得できるようになります。
クラスのpublicセクションで、2つの新しいメソッドを宣言します。
//--- Return (1) the list of dependent objects, (2) dependent graphical object by index and (3) the number of dependent objects CArrayObj *GetListDependentObj(void) { return &this.m_list; } CGStdGraphObj *GetDependentObj(const int index) { return this.m_list.At(index); } int GetNumDependentObj(void) { return this.m_list.Total(); } //--- Return the name of the dependent object by index string NameDependent(const int index); //--- Add the dependent graphical object to the list bool AddDependentObj(CGStdGraphObj *obj); //--- Change X and Y coordinates of the current and all dependent objects bool ChangeCoordsExtendedObj(const int x,const int y,const int modifier,bool redraw=false); //--- Set X and Y coordinates into the appropriate pivot points of a specified subordinate object bool SetCoordsXYtoDependentObj(CGStdGraphObj *dependent_obj); //--- Return (1) the minimum and (2) maximum XY screen coordinate of the specified pivot point bool GetMinCoordXY(int &x,int &y); bool GetMaxCoordXY(int &x,int &y); //--- Return the pivot point data object CLinkedPivotPoint*GetLinkedPivotPoint(void) { return &this.m_linked_pivots; }
クラス本体の外側で実装しましょう。
以下は、すべてのピボットポイントから最小のXY画面座標を返すメソッドです。
//+------------------------------------------------------------------+ //| Return the minimum XY screen coordinate | //| from all pivot points | //+------------------------------------------------------------------+ bool CGStdGraphObj::GetMinCoordXY(int &x,int &y) { //--- Declare the variables storing the minimum time and maximum price datetime time_min=LONG_MAX; double price_max=0; //--- Depending on the object type switch(this.TypeGraphObject()) { //--- Objects constructed based on screen coordinates case OBJ_LABEL : case OBJ_BUTTON : case OBJ_BITMAP_LABEL : case OBJ_EDIT : case OBJ_RECTANGLE_LABEL : case OBJ_CHART : //--- Write XY screen coordinates to x and y variables and return 'true' x=this.XDistance(); y=this.YDistance(); return true; //--- Objects constructed in time/price coordinates default : //--- In the loop by all pivot points for(int i=0;i<this.Pivots();i++) { //--- Define the minimum time if(this.Time(i)<time_min) time_min=this.Time(i); //--- Define the maximum price if(this.Price(i)>price_max) price_max=this.Price(i); } //--- Return the result of converting time/price coordinates into XY screen coordinates return(::ChartTimePriceToXY(this.ChartID(),this.SubWindow(),time_min,price_max,x,y) ? true : false); } return false; } //+------------------------------------------------------------------+
画面座標で作成されたオブジェクトの最大/最小座標を検索する必要はありません。このようなオブジェクトは単一のチャートアンカーポイントを備えており、すでに画面座標にあるため、単にそれらを返す必要があります。
逆に、時間/価格座標を使用して構築されたオブジェクトの場合、ChartTimePriceToXY()関数を介してこれらの座標を取得する必要があります。ただし、まず、オブジェクトが持つすべてのピボットポイントから最小時間座標を見つけ、同じピボットポイントから最大価格座標を見つける必要があります。チャートで必要なのは最小座標なのに、なぜ価格の最大座標を探すのでしょうか。答えは簡単です。チャートの価格は下から上に向かって増加しますが、チャートのピクセル単位の座標は左上隅から数えられるので、価格が高いほど、この価格ポイントのピクセル座標の値が小さくなるということです。
すべてのピボットポイントを反復処理して最小時間と最大価格を見つけ、これを最終的にChartTimePriceToXY()関数に渡して、チャートピクセルの座標を取得します。
同様のメソッドを使用して、すべてのピボットポイントから最大画面XY座標を取得します。
//+------------------------------------------------------------------+ //| Return the maximum XY screen coordinate | //| from all pivot points | //+------------------------------------------------------------------+ bool CGStdGraphObj::GetMaxCoordXY(int &x,int &y) { //--- Declare the variables that store the maximum time and minimum price datetime time_max=0; double price_min=DBL_MAX; //--- Depending on the object type switch(this.TypeGraphObject()) { //--- Objects constructed based on screen coordinates case OBJ_LABEL : case OBJ_BUTTON : case OBJ_BITMAP_LABEL : case OBJ_EDIT : case OBJ_RECTANGLE_LABEL : case OBJ_CHART : //--- Write XY screen coordinates to x and y variables and return 'true' x=this.XDistance(); y=this.YDistance(); return true; //--- Objects constructed in time/price coordinates default : //--- In the loop by all pivot points for(int i=0;i<this.Pivots();i++) { //--- Define the maximum time if(this.Time(i)>time_max) time_max=this.Time(i); //--- Define the maximum price if(this.Price(i)<price_min) price_min=this.Price(i); } //--- Return the result of converting time/price coordinates into XY screen coordinates return(::ChartTimePriceToXY(this.ChartID(),this.SubWindow(),time_max,price_min,x,y) ? true : false); } return false; } //+------------------------------------------------------------------+
このメソッドは前と似ていますが、ここでは最大時間と最小価格を探している点が異なります。
すでに述べたように、これらのメソッドは後で複合グラフィックオブジェクトで作業するときに必要になります。
拡張された標準グラフィックオブジェクトツールキット(\MQL5\Include\DoEasy\Objects\Graph\Extend\CGStdGraphObjExtToolkit.mqh)のクラスで、グラフィックオブジェクトの指定されたピボットポイントのX座標とY座標を画面座標で返すメソッドを少し調整します。これは、「switch」演算子のcaseで、各caseに時間/価格座標を使用して構築されたオブジェクトのピボットポイントの数に対応するグラフィックオブジェクトが含まれるように、グラフィックオブジェクトを配布する必要があるためです。それらを正しい順序で配布しましょう。
//+------------------------------------------------------------------+ //| Return the X and Y coordinates of the specified pivot point | //| of the graphical object in screen coordinates | //+------------------------------------------------------------------+ bool CGStdGraphObjExtToolkit::GetControlPointCoordXY(const int index,int &x,int &y) { //--- Declare form objects, from which we are to receive their screen coordinates CFormControl *form0=NULL, *form1=NULL; //--- Set X and Y to zero - these values will be received in case of a failure x=0; y=0; //--- Depending on the graphical object type switch(this.m_base_type) { //--- Objects drawn using screen coordinates case OBJ_LABEL : case OBJ_BUTTON : case OBJ_BITMAP_LABEL : case OBJ_EDIT : case OBJ_RECTANGLE_LABEL : case OBJ_CHART : //--- Write object screen coordinates and return 'true' x=this.m_base_x; y=this.m_base_y; return true; //--- One pivot point (price only) case OBJ_HLINE : break; //--- One pivot point (time only) case OBJ_VLINE : case OBJ_EVENT : break; //--- One reference point (time/price) case OBJ_TEXT : case OBJ_BITMAP : break; //--- Two pivot points and a central one //--- Lines case OBJ_TREND : case OBJ_TRENDBYANGLE : case OBJ_CYCLES : case OBJ_ARROWED_LINE : //--- Channels case OBJ_CHANNEL : case OBJ_STDDEVCHANNEL : case OBJ_REGRESSION : //--- Gann case OBJ_GANNLINE : case OBJ_GANNGRID : //--- Fibo case OBJ_FIBO : case OBJ_FIBOTIMES : case OBJ_FIBOFAN : case OBJ_FIBOARC : case OBJ_FIBOCHANNEL : case OBJ_EXPANSION : //--- Calculate coordinates for forms on the line pivot points if(index<this.m_base_pivots) return(::ChartTimePriceToXY(this.m_base_chart_id,this.m_base_subwindow,this.m_base_time[index],this.m_base_price[index],x,y) ? true : false); //--- Calculate the coordinates for the central form located between the line pivot points else { form0=this.GetControlPointForm(0); form1=this.GetControlPointForm(1); if(form0==NULL || form1==NULL) return false; x=(form0.CoordX()+this.m_shift+form1.CoordX()+this.m_shift)/2; y=(form0.CoordY()+this.m_shift+form1.CoordY()+this.m_shift)/2; return true; } //--- Channels case OBJ_PITCHFORK : break; //--- Gann case OBJ_GANNFAN : break; //--- Elliott case OBJ_ELLIOTWAVE5 : break; case OBJ_ELLIOTWAVE3 : break; //--- Shapes case OBJ_RECTANGLE : break; case OBJ_TRIANGLE : break; case OBJ_ELLIPSE : break; //--- Arrows case OBJ_ARROW_THUMB_UP : break; case OBJ_ARROW_THUMB_DOWN : break; case OBJ_ARROW_UP : break; case OBJ_ARROW_DOWN : break; case OBJ_ARROW_STOP : break; case OBJ_ARROW_CHECK : break; case OBJ_ARROW_LEFT_PRICE : break; case OBJ_ARROW_RIGHT_PRICE : break; case OBJ_ARROW_BUY : break; case OBJ_ARROW_SELL : break; case OBJ_ARROW : break; //--- default : break; } return false; } //+------------------------------------------------------------------+
最初に画面座標で描画されたオブジェクトは、基本オブジェクト(接続されているオブジェクト)の画面座標を返すだけです。
画面座標の戻りが実装されているオブジェクトには、2つのピボットポイントがあります。画面座標は、すべてのオブジェクトのピボットポイントと中心点に対して計算されます。
そのような機能をまだ持っていない残りのオブジェクトは、単にそれらのグループに分割されます (一部のオブジェクトは、ピボットポイントの数ではなく、型ごとにグループにまとめられます 。後で実装します)。
ここで、チャート上のすべてのGUIコントロールが、チャートに新しく追加されたグラフィックオブジェクトのいずれよりも常に上にあることを確認する必要があります。さらに、GUI要素が同じ順序で整列するように作成する必要があります。これは、グラフィックオブジェクトがチャートに追加される前の順序です。
グラフィックオブジェクトを前面に表示するには、オブジェクトを非表示にして、順番に再表示する必要があります。これは、グラフィックオブジェクト可視性フラグをリセットしてからインストールすることで実行されます。オブジェクトを非表示にするには、ObjectSetInteger()関数を使用して、OBJPROP_TIMEFRAMESプロパティのOBJ_NO_PERIODSフラグを設定する必要があります。OBJ_ALL_PERIODSフラグは、オブジェクトを表示するために使用されます。すべてのGUIオブジェクトに対して、グラフィック要素コレクションリストに配置されている順序でこれをおこなうと、チャート上のそれらの位置の順序が失われます。つまり、オブジェクトは、リストの最初がチャートの一番下になり、最後のオブジェクトが一番上になるように配置されます。ただし、これらのオブジェクトは完全に異なる順序で配置される可能性があり、これは再描画時に変更されます。ここで、今日追加した新しいプロパティであるZOrderに目を向けると、グラフィックオブジェクトのリストをZOrderプロパティの昇順で並べ替えれば、オブジェクトが正しい順序で再描画されるようになることが分かります。
グラフィックオブジェクトコレクションクラスのファイル(\MQL5\Include\DoEasy\Collections\GraphElementsCollection.mqh)を開き、必要なすべての改善をおこないます。
クラスのprivateセクションで3つの新しいメソッドを宣言します。
//--- Event handler void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam); private: //--- Move all objects on canvas to the foreground void BringToTopAllCanvElm(void); //--- (1) Return the maximum ZOrder of all CCanvas elements, //--- (2) Set ZOrde to the specified element and adjust it in all the remaining elements long GetZOrderMax(void); bool SetZOrderMAX(CGCnvElement *obj); //--- Handle the removal of extended graphical objects void DeleteExtendedObj(CGStdGraphObj *obj);
クラス本体の外側で実装しましょう。
以下は、キャンバス上のすべてのオブジェクトを前面に移動するメソッドです。
//+------------------------------------------------------------------+ //| Move all objects on canvas to the foreground | //+------------------------------------------------------------------+ void CGraphElementsCollection::BringToTopAllCanvElm(void) { //--- Create a new list, reset the flag of managing memory and sort by ZOrder CArrayObj *list=new CArrayObj(); list.FreeMode(false); list.Sort(SORT_BY_CANV_ELEMENT_ZORDER); //--- Declare the graphical element object CGCnvElement *obj=NULL; //--- In the loop by the total number of all graphical elements, for(int i=0;i<this.m_list_all_canv_elm_obj.Total();i++) { //--- get the next object obj=this.m_list_all_canv_elm_obj.At(i); if(obj==NULL) continue; //--- and insert it to the list in the order of sorting by ZOrder list.InsertSort(obj); } //--- In a loop by the created list sorted by ZOrder, for(int i=0;i<list.Total();i++) { //--- get the next object obj=list.At(i); if(obj==NULL) continue; //--- and move it to the foreground obj.BringToTop(); } //--- Remove the list sorted by 'new' delete list; } //+------------------------------------------------------------------+
ここでわかるのは、新しいCArrayObjリストオブジェクトを宣言し、そのメモリ管理フラグをリセットするということです。次に、並び替えフラグリストのZOrderプロパティを設定します。なぜこれをおこなうのでしょうか。CObjectオブジェクトを任意のプロパティで並べ替えることができるようにするには、プロパティ値を並べ替えフラグとして設定する必要があります。この場合、CObjectクラスから派生したすべてのオブジェクトに実装された仮想Compare()メソッドは、2つのオブジェクトの類似したプロパティ(並べ替えモードとして設定されたプロパティ)を比較する値を返します。これは、Sort()メソッドの操作に必要です。
コレクションオブジェクトへのポインタではなく、最終的に作成された配列のコレクションリストからオブジェクトのコピーを取得するには、メモリ管理フラグをリセットする必要があります。これは重要です。ポインタを処理している場合、これらは2つのリストにある同じコレクションリストオブジェクトへのポインタであるため、新しいリストでポインタを変更すると、コレクションリスト内のオブジェクトが自動的に変更されるからです。そのような「驚き」を避けるために、変更がオリジナルに影響を与えない独立したリストコピーを作成します。メソッドが終了したら、メモリリークを回避するために、作成したリストを削除する必要があります。これについては、標準ライブラリのヘルプ(FreeMode)にも記載されています。
メモリ管理フラグの設定はCArrayObjクラスの使用において重要な部分です。配列要素は動的オブジェクトへのポインタであるため、配列から削除するときにそれらをどのように処理するかを決定することが重要です。
フラグが設定されていて配列から要素が削除される場合、その要素は削除演算子によって自動的に削除されます。フラグが設定されていない場合、削除されたオブジェクトへのポインタはまだユーザープログラムのどこかにあり、後でプログラムによって割り当てが解除されると見なされます。
ユーザープログラムがメモリ管理のフラグをリセットする場合、ユーザーはプログラムの終了前に配列を削除する責任が自分にあることを理解する必要があります。new演算子によって要素に割り当てられたメモリはそれ以外の場合は解放されません。
データが大量の場合、これはターミナルのクラッシュにさえつながる可能性があります。ユーザーがメモリ管理フラグをリセットしない場合、別の落とし穴があります。
配列内のポインタ要素がローカル変数のどこかに格納されている場合、配列を削除すると、重大なエラーが発生しててユーザープログラムがクラッシュします。デフォルトでは、メモリ管理フラグが設定されており、配列のクラスがメモリ要素の解放を担当します。
次に、グラフィック要素のコレクションリストを反復処理し、各オブジェクトを並べ替え順に新しいリストに挿入します。
反復処理が完了すると、ZOrderプロパティの昇順で並べ替えられたすべてのグラフィック要素の新しいリストが取得されます。
リストを反復処理して、次の要素を取得して前面に移動します。後続のものは常に前のものよりも高くなります。
反復処理が完了したら、必ず新しく作成したリストを削除してください。
以下は、すべてのCCanvas要素の最大ZOrderを返すメソッドです。
//+------------------------------------------------------------------+ //| Return the maximum ZOrder of all CCanvas elements | //+------------------------------------------------------------------+ long CGraphElementsCollection::GetZOrderMax(void) { this.m_list_all_canv_elm_obj.Sort(SORT_BY_CANV_ELEMENT_ZORDER); int index=CSelect::FindGraphCanvElementMax(this.GetListCanvElm(),CANV_ELEMENT_PROP_ZORDER); CGCnvElement *obj=this.m_list_all_canv_elm_obj.At(index); return(obj!=NULL ? obj.Zorder() : WRONG_VALUE); } //+------------------------------------------------------------------+
ここではリストをZOrderプロパティで並べ替え、リスト内で最大のプロパティ値を持つ要素のインデックスを取得します。
取得したインデックスで要素へのポインタを取得し、要素のZOrderプロパティを返します。
要素が受信されていない場合は-1を返します。
たとえば、3つのGUIオブジェクトがある場合、ZOrder値は3つあるのが妥当です。すべてのオブジェクトは、最初はゼロのZOrder値を持っており、それらはすべて一番下にあります。オブジェクトをキャプチャするとすぐに、そのZOrderが1で増加します。ただし、まず、他のオブジェクトのZOrder値が1以上であるかどうかを確認する必要があります。これは、最後に選択したオブジェクトが、使用可能な最大ZOrderを1だけ超える残りのすべてのオブジェクトよりも高い必要があるためです。もちろん、最大のZOrderを取得して、単純に1増やすこともできますが、これは最も洗練された解決策ではありません。代わりに、3つのオブジェクトのうち、0〜2の範囲にあるZOrder値のみを持つことができるようにします。
したがって、最後に選択したオブジェクトについては、ZOrderを1増やすか、できるだけ高くして(ゼロから始まるすべてのオブジェクトの数だけ)、残りのオブジェクトについてはZOrderを1減らす必要があります。同時に、オブジェクトが一番下にあってそのZOrderがすでにゼロである場合は、減らしません。したがって、ZOrder値の変更は、GUIオブジェクトの数に応じて「ループ上」でおこなわれます。
以下は、ZOrderを指定された要素に設定して他の要素で調整するメソッドです。
//+------------------------------------------------------------------+ //| Set ZOrde to the specified element | //| and adjust it in other elements | //+------------------------------------------------------------------+ bool CGraphElementsCollection::SetZOrderMAX(CGCnvElement *obj) { //--- Get the maximum ZOrder of all graphical elements long max=this.GetZOrderMax(); //--- If an invalid pointer to the object has been passed or the maximum ZOrder has not been received, return 'false' if(obj==NULL || max<0) return false; //--- Declare the variable for storing the method result bool res=true; //--- If the maximum ZOrder is zero, ZOrder is equal to 1, //--- if the maximum ZOrder is less than (the total number of graphical elements)-1, ZOrder will exceed it by 1, //--- otherwise, ZOrder will be equal to (the total number of graphical elements)-1 long value=(max==0 ? 1 : max<this.m_list_all_canv_elm_obj.Total()-1 ? max+1 : this.m_list_all_canv_elm_obj.Total()-1); //--- If failed to set ZOrder for an object passed to the method, return 'false' if(!obj.SetZorder(value,false)) return false; //--- Temporarily declare a form object for drawing a text for visually displaying its ZOrder CForm *form=obj; //--- and draw a text specifying ZOrder on the form form.TextOnBG(0,TextByLanguage("Тест: ID ","Test. ID ")+(string)form.ID()+", ZOrder "+(string)form.Zorder(),form.Width()/2,form.Height()/2,FRAME_ANCHOR_CENTER,C'211,233,149',255,true,true); //--- Sort the list of graphical elements by an element ID this.m_list_all_canv_elm_obj.Sort(SORT_BY_CANV_ELEMENT_ID); //--- Get the list of graphical elements without an object whose ID is equal to the ID of the object passed to the method CArrayObj *list=CSelect::ByGraphCanvElementProperty(this.GetListCanvElm(),CANV_ELEMENT_PROP_ID,obj.ID(),NO_EQUAL); //--- If failed to obtain the list and the list size exceeds one, //--- which indicates the presence of other objects in it in addition to the one sorted by ID, return 'false' if(list==NULL && this.m_list_all_canv_elm_obj.Total()>1) return false; //--- In the loop by the obtained list of remaining graphical element objects for(int i=0;i<list.Total();i++) { //--- get the next object CGCnvElement *elm=list.At(i); //--- If failed to get the object or if this is a control form for managing pivot points of an extended standard graphical object //--- or, if the object's ZOrder is zero, skip the object since there is no need in changing its ZOrder as it is the bottom one if(elm==NULL || elm.Type()==OBJECT_DE_TYPE_GFORM_CONTROL || elm.Zorder()==0) continue; //--- If failed to set the object's ZOrder to 1 less than it already is (decreasing ZOrder by 1), add 'false' to the 'res' value if(!elm.SetZorder(elm.Zorder()-1,false)) res &=false; //--- Temporarily (for the test purpose), if the element is a form, if(elm.Type()==OBJECT_DE_TYPE_GFORM) { //--- assign the pointer to the element for the form and draw a text specifying ZOrder on the form form=elm; form.TextOnBG(0,TextByLanguage("Тест: ID ","Test. ID ")+(string)form.ID()+", ZOrder "+(string)form.Zorder(),form.Width()/2,form.Height()/2,FRAME_ANCHOR_CENTER,C'211,233,149',255,true,true); } } //--- Upon the loop completion, return the result set in 'res' return res; } //+------------------------------------------------------------------+
メソッド文字列には、メソッドロジックを明確にするためのコメントが付いています。テスト中に各フォームオブジェクトのプロパティの変化を視覚的に確認できるようにZOrder値を示すテキスト表示がテスト目的で追加されました 。
新しく構築されたグラフィックオブジェクトの上にGUIオブジェクトを移動する必要があるケースは2つあります。
- 手動で標準のグラフィックオブジェクトをグラフに追加する
- プログラムでグラフィックオブジェクト(標準または拡張複合オブジェクト)を追加する
これらのケースはライブラリで個別に処理されます。
したがって、GUIオブジェクトをさまざまな場所でより高いレベルに上げるメソッドの呼び出しを追加する必要があります。
プログラムでグラフィックオブジェクトを作成する場合は、作成された標準グラフィックオブジェクトをリストに追加するprivateメソッドでGUIオブジェクトを直接再描画するための呼び出しをおこないいます。これは、グラフィックオブジェクトの作成の成功が最終的に決定される場所ですが、すべてのGUIオブジェクトをその最後に前面に移動するためのメソッドの呼び出しを追加できます。
//--- Add a newly created standard graphical object to the list bool AddCreatedObjToList(const string source,const long chart_id,const string name,CGStdGraphObj *obj) { if(!this.m_list_all_graph_obj.Add(obj)) { CMessage::ToLog(source,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST); ::ObjectDelete(chart_id,name); delete obj; return false; } //--- Move all the forms above the created chart object and redraw the chart this.BringToTopAllCanvElm(); ::ChartRedraw(chart_id); return true; }
メソッドを呼び出した後、チャートを更新して変更をすぐに表示する必要があります。
手動でグラフィックオブジェクトを宣言する場合は、このイベントは、すべてのグラフィックオブジェクトのリストを更新するメソッドで定義されます。
新しいグラフィックオブジェクトの追加が定義されているコードブロックで、GUIオブジェクトを前面に移動するメソッドの呼び出しを追加します。
//+------------------------------------------------------------------+ //| Update the list of all graphical objects | //+------------------------------------------------------------------+ void CGraphElementsCollection::Refresh(void) { this.RefreshForExtraObjects(); //--- Declare variables to search for charts long chart_id=0; int i=0; //--- In the loop by all open charts in the terminal (no more than 100) while(i<CHARTS_MAX) { //--- Get the chart ID chart_id=::ChartNext(chart_id); if(chart_id<0) break; //--- Get the pointer to the object for managing graphical objects //--- and update the list of graphical objects by chart ID CChartObjectsControl *obj_ctrl=this.RefreshByChartID(chart_id); //--- If failed to get the pointer, move on to the next chart if(obj_ctrl==NULL) continue; //--- If the number of objects on the chart changes if(obj_ctrl.IsEvent()) { //--- If a graphical object is added to the chart if(obj_ctrl.Delta()>0) { //--- Get the list of added graphical objects and move them to the collection list //--- (if failed to move the object to the collection, move on to the next object) if(!this.AddGraphObjToCollection(DFUN_ERR_LINE,obj_ctrl)) continue; //--- Move all the forms above the created chart object and redraw the chart this.BringToTopAllCanvElm(); ::ChartRedraw(obj_ctrl.ChartID()); } //--- If the graphical object has been removed else if(obj_ctrl.Delta()<0) { int index=WRONG_VALUE; //--- In the loop by the number of removed graphical objects for(int j=0;j<-obj_ctrl.Delta();j++) { // Find an extra object in the list CGStdGraphObj *obj=this.FindMissingObj(chart_id,index); if(obj!=NULL) { //--- Get the removed object parameters long lparam=obj.ChartID(); string sparam=obj.Name(); double dparam=(double)obj.TimeCreate(); //--- If this is an extended graphical object if(obj.TypeGraphElement()==GRAPH_ELEMENT_TYPE_STANDARD_EXTENDED) { this.DeleteExtendedObj(obj); } //--- Move the graphical object class object to the list of removed objects //--- and send the event to the control program chart if(this.MoveGraphObjToDeletedObjList(index)) ::EventChartCustom(this.m_chart_id_main,GRAPH_OBJ_EVENT_DELETE,lparam,dparam,sparam); } } } } //--- Increase the loop index i++; } } //+------------------------------------------------------------------+
前回の記事でテストを実行したときに、複合グラフィックオブジェクトの座標がチャートの境界によって常に正しく制限されているとは限らないことがわかりました。オブジェクトのピボットポイントの位置が異なると、チャートの端に到達したときにオブジェクトが「変形」する可能性があります。
テストの直後に理由が明らかになりました。
...ピボットポイントのシフトは、中心点を基準にして計算されます。つまり、1つ目のポイントには正のシフトがあり、2番目のポイントには負のシフトがあります。中央のピボットポイントを基準にしてピボットポイントの位置を変更すると、制限計算エラーが発生します。
必要な変更を加えてこれを修正しましょう。中心点の座標をすぐに計算し、グラフィックオブジェクトのピボット点からその中心点にシフトします。チャートサイズで制限を計算する場合は、符号のないシフト値を使用します。シフトの符号は計算に影響しません。
拡張された標準グラフィックオブジェクトフォームを処理するためのブロックのイベントハンドラで、制限の新しい計算を追加し、チャートにデバッグデータを表示します。
//--- If the form is central for managing all pivot points of a graphical object else { //--- Get screen coordinates of all object pivot points and write them to the m_data_pivot_point structure if(this.GetPivotPointCoordsAll(ext,m_data_pivot_point)) { //--- In the loop by the number of object pivot points, for(int i=0;i<(int)this.m_data_pivot_point.Size();i++) { //--- limit the screen coordinates of the current pivot point so that they do not move beyond the chart borders //--- By X coordinate if(x+shift-::fabs(this.m_data_pivot_point[i].ShiftX)<0) x=-shift+::fabs(m_data_pivot_point[i].ShiftX); if(x+shift+::fabs(this.m_data_pivot_point[i].ShiftX)>chart_width) x=chart_width-shift-::fabs(this.m_data_pivot_point[i].ShiftX); //--- By Y coordinate if(y+shift-::fabs(this.m_data_pivot_point[i].ShiftY)<0) y=-shift+::fabs(this.m_data_pivot_point[i].ShiftY); if(y+shift+::fabs(this.m_data_pivot_point[i].ShiftY)>chart_height) y=chart_height-shift-::fabs(this.m_data_pivot_point[i].ShiftY); //--- set the calculated coordinates to the current object pivot point ext.ChangeCoordsExtendedObj(x+shift+this.m_data_pivot_point[i].ShiftX,y+shift+this.m_data_pivot_point[i].ShiftY,i); } } //--- Display debugging comments on the chart if(m_data_pivot_point.Size()>=2) { int max_x,min_x,max_y,min_y; if(ext.GetMinCoordXY(min_x,min_y) && ext.GetMaxCoordXY(max_x,max_y)) Comment ( "MinX=",min_x,", MaxX=",max_x,"\n", "MaxY=",min_y,", MaxY=",max_y ); } }
GUIオブジェクトをクリックするとすぐに、それを前面に移動し、最大ZOrderを設定する必要があります。
押されたマウスボタンのアクティブ領域内のカーソル処理ブロックのイベントハンドラで、 オブジェクトの最大ZOrderを設定するためのコードブロックを追加します。
//--- 'The cursor is inside the active area, any mouse button is clicked' event handler if(mouse_state==MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_PRESSED && !move) { pressed_form=true; // the flag of holding the mouse button on the form //--- If the left mouse button is pressed if(this.m_mouse.IsPressedButtonLeft()) { //--- Set flags and form parameters move=true; // movement flag form.SetInteraction(true); // flag of the form interaction with the environment form.BringToTop(); // form on the background - above all others form.SetOffsetX(this.m_mouse.CoordX()-form.CoordX()); // Cursor shift relative to the X coordinate form.SetOffsetY(this.m_mouse.CoordY()-form.CoordY()); // Cursor shift relative to the Y coordinate this.ResetAllInteractionExeptOne(form); // Reset interaction flags for all forms except the current one //--- Get the maximum ZOrder long zmax=this.GetZOrderMax(); //--- If the maximum ZOrder has been received and the form's ZOrder is less than the maximum one or the maximum ZOrder of all forms is equal to zero if(zmax>WRONG_VALUE && (form.Zorder()<zmax || zmax==0)) { //--- If the form is not a control point for managing an extended standard graphical object, //--- set the form's ZOrder above all others if(form.Type()!=OBJECT_DE_TYPE_GFORM_CONTROL) this.SetZOrderMAX(form); } } }
ここでは、拡張された標準グラフィックオブジェクトコントロールではないフォームオブジェクトにZOrderを設定します。
グラフィック要素コレクションクラスイベントハンドラは非常に大きいので、ここでそのコードを提供しても意味がありません。添付ファイルにはすべての変更(上記で検討)が含まれます。
各グラフィックオブジェクトのピボットポイントの画面座標を返すメソッドでのシフト計算を変更してみましょう。
次に、中央のピボットポイント(オブジェクトの移動に使用)の座標をすぐに計算し、そのポイントに基づいてシフトを計算します。より正確には、各ピボットポイントから中央のピボットポイントまでのシフトを計算します。
//+------------------------------------------------------------------+ //| Return screen coordinates | //| of each graphical object pivot point | //+------------------------------------------------------------------+ bool CGraphElementsCollection::GetPivotPointCoordsAll(CGStdGraphObj *obj,SDataPivotPoint &array_pivots[]) { //--- Central point coordinates int xc=0, yc=0; //--- If failed to increase the array of structures to match the number of pivot points, //--- inform of that in the journal and return 'false' if(::ArrayResize(array_pivots,obj.Pivots())!=obj.Pivots()) { CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_ARRAY_RESIZE); return false; } //--- In the loop by the number of graphical object pivot points for(int i=0;i<obj.Pivots();i++) { //--- Convert the object coordinates into screen ones. If failed, inform of that and return 'false' if(!::ChartTimePriceToXY(obj.ChartID(),obj.SubWindow(),obj.Time(i),obj.Price(i),array_pivots[i].X,array_pivots[i].Y)) { CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_CONV_GRAPH_OBJ_COORDS_TO_XY); return false; } } //--- Depending on the graphical object type switch(obj.TypeGraphObject()) { //--- One pivot point in screen coordinates case OBJ_LABEL : case OBJ_BUTTON : case OBJ_BITMAP_LABEL : case OBJ_EDIT : case OBJ_RECTANGLE_LABEL : case OBJ_CHART : break; //--- One pivot point (price only) case OBJ_HLINE : break; //--- One pivot point (time only) case OBJ_VLINE : case OBJ_EVENT : break; //--- Two pivot points and a central one //--- Lines case OBJ_TREND : case OBJ_TRENDBYANGLE : case OBJ_CYCLES : case OBJ_ARROWED_LINE : //--- Channels case OBJ_CHANNEL : case OBJ_STDDEVCHANNEL : case OBJ_REGRESSION : //--- Gann case OBJ_GANNLINE : case OBJ_GANNGRID : //--- Fibo case OBJ_FIBO : case OBJ_FIBOTIMES : case OBJ_FIBOFAN : case OBJ_FIBOARC : case OBJ_FIBOCHANNEL : case OBJ_EXPANSION : //--- Calculate the central point coordinates xc=(array_pivots[0].X+array_pivots[1].X)/2; yc=(array_pivots[0].Y+array_pivots[1].Y)/2; //--- Calculate the shifts of all pivot points from the central one and write them to the structure array array_pivots[0].ShiftX=array_pivots[0].X-xc; // X shift from 0 to the central point array_pivots[1].ShiftX=array_pivots[1].X-xc; // X shift from 1 to the central point array_pivots[0].ShiftY=array_pivots[0].Y-yc; // Y shift from 0 to the central point array_pivots[1].ShiftY=array_pivots[1].Y-yc; // Y shift from 1 to the central point return true; //--- Channels case OBJ_PITCHFORK : break; //--- Gann case OBJ_GANNFAN : break; //--- Elliott case OBJ_ELLIOTWAVE5 : break; case OBJ_ELLIOTWAVE3 : break; //--- Shapes case OBJ_RECTANGLE : break; case OBJ_TRIANGLE : break; case OBJ_ELLIPSE : break; //--- Arrows case OBJ_ARROW_THUMB_UP : break; case OBJ_ARROW_THUMB_DOWN : break; case OBJ_ARROW_UP : break; case OBJ_ARROW_DOWN : break; case OBJ_ARROW_STOP : break; case OBJ_ARROW_CHECK : break; case OBJ_ARROW_LEFT_PRICE : break; case OBJ_ARROW_RIGHT_PRICE : break; case OBJ_ARROW_BUY : break; case OBJ_ARROW_SELL : break; case OBJ_ARROW : break; //--- Graphical objects with time/price coordinates case OBJ_TEXT : break; case OBJ_BITMAP : break; //--- default: break; } return false; } //+------------------------------------------------------------------+
これで、すべてのシフトと制限が正しく計算されるはずなので、確認するためにテストを実行してみましょう。
検証
テストを実行するには、前の記事のEAを使用して、TestDoEasyPart100.mq5として\MQL5\Experts\TestDoEasy\Part100\にTestDoEasyPart100.mq5として保存します。
テスト中に各フォームのZOrderプロパティ値をすぐに確認したいので、OnInit()ハンドラでフォーム(一番下のオブジェクト)のプロパティのゼロ値を設定して、フォームオブジェクトIDとそのZOrderプロパティ値を表示するようにします。
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- Set EA global variables ArrayResize(array_clr,2); // Array of gradient filling colors array_clr[0]=C'26,100,128'; // Original ≈Dark-azure color array_clr[1]=C'35,133,169'; // Lightened original color //--- Create the array with the current symbol and set it to be used in the library string array[1]={Symbol()}; engine.SetUsedSymbols(array); //--- Create the timeseries object for the current symbol and period, and show its description in the journal engine.SeriesCreate(Symbol(),Period()); engine.GetTimeSeriesCollection().PrintShort(false); // Short descriptions //--- Create form objects CForm *form=NULL; for(int i=0;i<FORMS_TOTAL;i++) { //--- When creating an object, pass all the required parameters to it form=new CForm("Form_0"+string(i+1),30,(form==NULL ? 100 : form.BottomEdge()+20),100,30); if(form==NULL) continue; //--- Set activity and moveability flags for the form form.SetActive(true); form.SetMovable(true); //--- Set the form ID and the index in the list of objects form.SetID(i); form.SetNumber(0); // (0 - main form object) Auxiliary objects may be attached to the main one. The main object is able to manage them //--- Set the opacity of 200 form.SetOpacity(245); //--- The form background color is set as the first color from the color array form.SetColorBackground(array_clr[0]); //--- Form outlining frame color form.SetColorFrame(clrDarkBlue); //--- Draw the shadow drawing flag form.SetShadow(false); //--- Calculate the shadow color as the chart background color converted to the monochrome one color clrS=form.ChangeColorSaturation(form.ColorBackground(),-100); //--- If the settings specify the usage of the chart background color, replace the monochrome color with 20 units //--- Otherwise, use the color specified in the settings for drawing the shadow color clr=(InpUseColorBG ? form.ChangeColorLightness(clrS,-20) : InpColorForm3); //--- Draw the form shadow with the right-downwards offset from the form by three pixels along all axes //--- Set the shadow opacity to 200, while the blur radius is equal to 4 form.DrawShadow(3,3,clr,200,4); //--- Fill the form background with a vertical gradient form.Erase(array_clr,form.Opacity(),true); //--- Draw an outlining rectangle at the edges of the form form.DrawRectangle(0,0,form.Width()-1,form.Height()-1,form.ColorFrame(),form.Opacity()); form.Done(); //--- Set ZOrder to zero, display the text describing the gradient type and update the form //--- Text parameters: the text coordinates and the anchor point in the form center //--- Create a new text animation frame with the ID of 0 and display the text on the form form.SetZorder(0,false); form.TextOnBG(0,TextByLanguage("Тест: ID ","Test: ID ")+(string)form.ID()+", ZOrder "+(string)form.Zorder(),form.Width()/2,form.Height()/2,FRAME_ANCHOR_CENTER,C'211,233,149',255,true,true); //--- Add the form to the list if(!engine.GraphAddCanvElmToCollection(form)) delete form; } //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+
チャート上でEAをコンパイルして起動します。
ご覧のとおり、各フォームオブジェクトのZOrder値は、作成直後はゼロになりますが、グラフィックオブジェクトは「それらの下」に構築されます。各オブジェクトのZOrder値の変更は、「円で」実行されますチャート上のフォームオブジェクトの数(ゼロから数えて)が上限です。構築されたグラフィックオブジェクトは常にGUIオブジェクトの「下」に表示され、それらの相対位置は変更されません。つまり、ZOrderプロパティの値に従ってリスト内で正しく分散されます。複合グラフィックオブジェクトは画面の端に正しく制限され、ピボットポイントの位置に関係なく歪むことがなくなりました。他のグラフィックオブジェクトと同様、フォームオブジェクトの下に描画されています。
次の段階
次の記事では、Windows FormsスタイルのGUIオブジェクトの作成に関する大規模なセクションを開始します。
ただし、拡張グラフィックオブジェクトとそれらに基づく複合オブジェクトの開発が止まるわけではありません。さらなる発展のためには、本格的なコントロールが必要なだけです。ライブラリの次のセクションが進むにつれて、拡張グラフィックオブジェクトの開発と改良を徐々に続けていきます。
**連載のこれまでの記事:
DoEasyライブラリのグラフィックス(第93部):複合グラフィックオブジェクトを作成するための機能の準備
DoEasyライブラリのグラフィックス(第94部):複合グラフィックオブジェクトの移動と削除
DoEasyライブラリのグラフィックス(第95部):複合グラフィックオブジェクトコントロール
DoEasyライブラリのグラフィックス(第96部):フォームオブジェクトのグラフィックとマウスイベントの処理
DoEasyライブラリのグラフィックス(第97部):フォームオブジェクトの移動の独立した処理
DoEasyライブラリのグラフィックス(第98部):拡張された標準グラフィックオブジェクトのピボットポイントの移動
DoEasyライブラリのグラフィックス(第99部):単一のコントロールポイントを使用して拡張グラフィックオブジェクトを移動する
MetaQuotes Ltdによってロシア語から翻訳されました。
元の記事: https://www.mql5.com/ru/articles/10634
- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索