DoEasyライブラリのグラフィックス(第95部):複合グラフィカルオブジェクトコントロール
目次
概念
本稿では、複合グラフィカルオブジェクトの開発を続けます。これらは、いくつかのオブジェクトで構成され、単一のグラフィカルオブジェクトに結合された標準グラフィカルオブジェクトです。ライブラリでは、複合オブジェクトに含まれるグラフィカルオブジェクトは、拡張された標準グラフィカルオブジェクトとして定義されます。このようなオブジェクトには、いくつかの追加のプロパティと機能があり、他のグラフィカルオブジェクトを組み込んだり組み込まれたりすることができます。
複合グラフィカルオブジェクトの概念には、別のオブジェクトに接続されたポイントでオブジェクトを保持し、親オブジェクトを変更または再配置するときにその位置を調整する機能が必要です。
前回の記事では、複合グラフィカルオブジェクトイベントハンドラの作成を開始し、複合グラフィカルオブジェクトの削除の処理を実装し、その再配置ハンドラの開発を開始しました。
今日は、複合グラフィカルオブジェクトの再配置から少し脱線して、複合グラフィカルオブジェクトを特徴とするチャートに変更イベントのハンドラを実装します。さらに、複合グラフィカルオブジェクトを管理するためのコントロールに焦点を当てます。
なぜでしょうか。複合グラフィカルオブジェクトのリアルタイム作成を実装します。従属オブジェクトを基本オブジェクトにドラッグして基本オブジェクトに接続します。基本グラフィカルオブジェクトでは、別のオブジェクトがマウスでドラッグされているかどうかを追跡します。オブジェクト接続メカニズムは、チャートアンカーポイントから一定の距離で有効になります。接続されたオブジェクトのアンカーポイントと基本オブジェクトのアンカーポイントを結ぶ線が視覚的に表示され、ドラッグされたオブジェクトを基本オブジェクトに接続する準備ができたことを示します。これを実現するには、グラフィカルオブジェクトの各アンカーポイントが特定のサイズのフォームオブジェクトを備えている必要があります。フォームオブジェクトの領域に入ると、接続メカニズムがアクティブになり、オブジェクトが対話の準備ができていることを示す線がフォーム自体に表示されます。このようなフォームは、グラフィカルオブジェクトの各ピボットポイントに目に見えない形で存在します。領域のサイズは、フォームのエッジに沿って長方形を描画できるようにすることで、デバッグ目的でのみ表示できます。
さらに、フォームには、マウスカーソルがフォームのアクティブ領域に合わせられたときにのみ表示されるグラフィカルオブジェクトのアンカーポイントが表示されます。マウスクリックで強調表示するのではなくフォーム領域にマウスカーソルを合わせることによって、拡張グラフィカルオブジェクトを移動および変更できます。フォームのアクティブな領域(上の画像の長方形)にカーソルを合わせるとすぐに、ラベルがグラフィカルオブジェクトのアンカーポイント(円の中心にある青い点)に表示されます。マウスでフォームのドラッグを開始すると、グラフィカルオブジェクトの適切なピボットポイントがカーソルに追従し、複合グラフィカルオブジェクトとともにオブジェクト自体を変更します。
マウスボタンが押された状態でマウスカーソルがフォームのアクティブ領域に入った場合、これは(確認された場合)別のグラフィカルオブジェクトをフォームに適用して、あるオブジェクトを別のオブジェクトにバインドするメカニズムをアクティブにすることを意味します。したがって、フォームを使用すると、一度に複数の目的を達成できます。
準備がまだ終わっていないので、ここではオブジェクトの別のオブジェクトへの接続は実装しません。代わりに、フォームを作成してグラフィカルオブジェクトアンカーポイントに接続し、チャートを変更するときにオブジェクトのピボットポイント座標に沿ってフォームを移動するメカニズムを実装します。これには、チャートを再配置するか、表示スケールを変更します。これを行うのは、フォームオブジェクトの座標は画面のピクセル単位であるのに対し、ほとんどのグラフィカルオブジェクトは時間/価格の値で表示されるためです。
ライブラリクラスの改善
\MQL5\Include\DoEasy\Data.mqhに、新しいメッセージインデックスを追加します。
//--- CLinkedPivotPoint MSG_GRAPH_OBJ_EXT_NOT_ANY_PIVOTS_X, // Not a single pivot point is set for the object along the X axis MSG_GRAPH_OBJ_EXT_NOT_ANY_PIVOTS_Y, // Not a single pivot point is set for the object along the Y axis MSG_GRAPH_OBJ_EXT_NOT_ATACHED_TO_BASE, // The object is not attached to the basic graphical object MSG_GRAPH_OBJ_EXT_FAILED_CREATE_PP_DATA_OBJ, // Failed to create a data object for the X and Y pivot points MSG_GRAPH_OBJ_EXT_NUM_BASE_PP_TO_SET_X, // Number of base object pivot points for calculating the X coordinate: MSG_GRAPH_OBJ_EXT_NUM_BASE_PP_TO_SET_Y, // Number of base object pivot points for calculating the Y coordinate: //--- CGStdGraphObjExtToolkit MSG_GRAPH_OBJ_EXT_FAILED_ARR_RESIZE_TIME_DATA, // Failed to change the size of the pivot point time data array MSG_GRAPH_OBJ_EXT_FAILED_ARR_RESIZE_PRICE_DATA, // Failed to change the size of the pivot point price data array MSG_GRAPH_OBJ_EXT_FAILED_CREATE_CTRL_POINT_FORM, // Failed to create a form object to manage a pivot point }; //+------------------------------------------------------------------+
新しく追加したインデックスに対応するメッセージテキストも追加します。
//--- CLinkedPivotPoint {"Для объекта не установлено ни одной опорной точки по оси X","The object does not have any pivot points set along the x-axis"}, {"Для объекта не установлено ни одной опорной точки по оси Y","The object does not have any pivot points set along the y-axis"}, {"Объект не привязан к базовому графическому объекту","The object is not attached to the base graphical object"}, {"Не удалось создать объект данных опорной точки X и Y.","Failed to create X and Y reference point data object"}, {"Количество опорных точек базового объекта для расчёта координаты X: ","Number of reference points of the base object to set the X coordinate: "}, {"Количество опорных точек базового объекта для расчёта координаты Y: ","Number of reference points of the base object to set the Y coordinate: "}, //--- CGStdGraphObjExtToolkit {"Не удалось изменить размер массива данных времени опорной точки","Failed to resize pivot point time data array"}, {"Не удалось изменить размер массива данных цены опорной точки","Failed to resize pivot point price data array"}, {"Не удалось создать объект-форму для контроля опорной точки","Failed to create form object to control pivot point"}, }; //+---------------------------------------------------------------------+
\MQL5\Include\DoEasy\Defines.mqhでマクロ置換
#define CLR_DEFAULT (0xFF000000) // Default symbol background color in the navigator
を、よりわかりやすい
#define CLR_MW_DEFAULT (0xFF000000) // Default symbol background color in the Market Watch
で置き換えます。マクロ置き換え
#define NULL_COLOR (0x00FFFFFF) // Zero for the canvas with the alpha channel
を、よりわかりやすい
#define CLR_CANV_NULL (0x00FFFFFF) // Zero for the canvas with the alpha channel
で置き換えます。また、新しいマクロ置換を追加して、ここで作成するフォームオブジェクトのデフォルト値を設定します。
//--- Graphical object parameters #define PROGRAM_OBJ_MAX_ID (10000) // Maximum value of an ID of a graphical object belonging to a program #define CTRL_POINT_SIZE (5) // Radius of the control point on the form for managing graphical object pivot points #define CTRL_FORM_SIZE (40) // Size of the control point form for managing graphical object pivot points //+------------------------------------------------------------------+ //| Enumerations | //+------------------------------------------------------------------+
すべてのファイルの古いマクロ置換名を置き換えます。
単にCtrl+Shift+Hを押し、次の値を入力して、以下に示すチェックボックスをオンにします。
[Replace in Files]をクリックしすると、すべてのライブラリファイルで置き換えが行われます。
NULL_COLOR with CLR_CANV_NULLを同じように置き換えます。
新しいマクロ置換名はそれらの機能をよりわかりやすく示すため、潜在的なエラーの量が減ります(たとえば、私は透明なキャンバスの背景を設定するためにCLR_DEFAULTを入力し、それが機能しない理由を理解するために多くの時間を費やしました)。
さらに、基本グラフィカルオブジェクトの子孫のすべてのクラスファイルにいくつかの小さな変更を加えました(操作ログで短いオブジェクトの説明を表示するメソッドのテキストにコンマを追加するだけです )。
//+------------------------------------------------------------------+ //| Display a short description of the object in the journal | //+------------------------------------------------------------------+ void CGStdArrowBuyObj::PrintShort(const bool dash=false,const bool symbol=false) { ::Print ( (dash ? " - " : "")+this.Header(symbol)," \"",CGBaseObj::Name(),"\": ID ",(string)this.GetProperty(GRAPH_OBJ_PROP_ID,0), ", ",::TimeToString(CGBaseObj::TimeCreate(),TIME_DATE|TIME_MINUTES|TIME_SECONDS) ); } //+------------------------------------------------------------------+
これは純粋に「見栄えのする」改善です。
\MQL5\Include\DoEasy\Objects\Graph\Standard\のすべてのファイルに実装されています。
拡張された標準グラフィカルオブジェクトツールキットクラス
拡張グラフィカルオブジェクトを処理するためのツールキットの作成を始めましょう。これは、フォームオブジェクトを作成して操作するために必要なすべてのメソッドを備えたクラスになります。各拡張グラフィカルオブジェクトは、適切なクラスのオブジェクトへのポインタを持つ必要があります。必要に応じて(これが複合グラフィカルオブジェクト内の基本オブジェクトである場合)、クラスオブジェクトは、拡張オブジェクトの作成時に動的に作成され、拡張オブジェクトが削除されると削除されます。
オブジェクトは、基本グラフィカルオブジェクトの必要なパラメータ(座標、タイプ、名前など)を受け取ります。基本オブジェクトの座標が追跡され、フォームオブジェクトの座標がオブジェクト内で調整されます。最終的に、フォームオブジェクトは基本オブジェクトの管理を可能にします。
ただし、まず最初に...
\MQL5\Include\DoEasy\Objects\Graph\で、新しいExtend\フォルダを作成して、 MQL5標準ライブラリを構築する基本CObjectクラスから継承されたCGStdGraphObjExtToolkitクラスのCGStdGraphObjExtToolkit.mqhファイルを含めます。
//+------------------------------------------------------------------+ //| CGStdGraphObjExtToolkit.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 "..\..\Graph\Form.mqh" //+------------------------------------------------------------------+ //| Extended standard graphical | //| object toolkit class | //+------------------------------------------------------------------+ class CGStdGraphObjExtToolkit : public CObject { }
フォームオブジェクトクラスファイルをクラスファイルに含める必要があります。
クラスのprivateセクションで、必要なすべての基本オブジェクトプロパティ、フォームを作成するためのプロパティ、それらを保存するためのリストを格納する変数およびフォームオブジェクトを作成するためのメソッドとその画面座標を返すためのメソッドを宣言します。
//+------------------------------------------------------------------+ //| Extended standard graphical | //| object toolkit class | //+------------------------------------------------------------------+ class CGStdGraphObjExtToolkit : public CObject { private: long m_base_chart_id; // Base graphical object chart ID int m_base_subwindow; // Base graphical object chart subwindow ENUM_OBJECT m_base_type; // Base object type string m_base_name; // Base object name int m_base_pivots; // Number of base object reference points datetime m_base_time[]; // Time array of base object reference points double m_base_price[]; // Price array of base object reference points int m_base_x; // Base object X coordinate int m_base_y; // Base object Y coordinate int m_ctrl_form_size; // Size of forms for managing reference points int m_shift; // Shift coordinates for adjusting the form location CArrayObj m_list_forms; // List of form objects for managing reference points //--- Create a form object on a base object reference point CForm *CreateNewControlPointForm(const int index); //--- Return X and Y screen coordinates of the specified reference point of the graphical object bool GetControlPointCoordXY(const int index,int &x,int &y); public:
1つのグラフィカルオブジェクトに複数のピボットポイントがある場合があるため、配列を使用して価格と時間の座標を格納しましょう。各ポイントの座標は適切な配列セルに格納されます。最初のポイントの座標は配列の0インデックス、2番目はインデックス1、3番目はインデックス2などです。
フォームの座標をシフトすると、オブジェクトのピボットポイントの中心にフォームを正確に配置できます。このシフトは、ポイントサイズの半分をカバーします。フォームサイズが2の倍数の場合は1を足して調整されます(例: 10の場合は1を足して11)。これにより、フォームをグラフィカルオブジェクトのピボットポイントの中心に正確に配置できるため、1ピクセルも他の辺を超えないようになります。
フォームのリストは、作成されたすべてのフォームを保存するためのものです。それらへのアクセスは、ポインタによって許可されます。画面の座標を計算するメソッドにより、フォームが配置される画面の座標を知ることができます。これにより、グラフィカルオブジェクトのピボットポイントに正確に配置できます。
クラスのpublicセクションで、クラスを処理するために必要なすべてのメソッドを宣言します。
public: //--- Set the parameters of the base object of a composite graphical object void SetBaseObj(const ENUM_OBJECT base_type,const string base_name, const long base_chart_id,const int base_subwindow, const int base_pivots,const int ctrl_form_size, const int base_x,const int base_y, const datetime &base_time[],const double &base_price[]); //--- Set the base object (1) time, (2) price, (3) time and price coordinates void SetBaseObjTime(const datetime time,const int index); void SetBaseObjPrice(const double price,const int index); void SetBaseObjTimePrice(const datetime time,const double price,const int index); //--- Set the base object (1) X, (2) Y, (3) X and Y screen coordinates void SetBaseObjCoordX(const int value) { this.m_base_x=value; } void SetBaseObjCoordY(const int value) { this.m_base_y=value; } void SetBaseObjCoordXY(const int value_x,const int value_y) { this.m_base_x=value_x; this.m_base_y=value_y; } //--- (1) Set and (2) return the size of the form of pivot point management control points void SetControlFormSize(const int size); int GetControlFormSize(void) const { return this.m_ctrl_form_size; } //--- Return the pointer to the pivot point form by (1) index and (2) name CForm *GetControlPointForm(const int index) { return this.m_list_forms.At(index); } CForm *GetControlPointForm(const string name,int &index); //--- Return the number of the base object pivot points int GetNumPivotsBaseObj(void) const { return this.m_base_pivots; } //--- Create form objects on the base object pivot points bool CreateAllControlPointForm(void); //--- Remove all form objects from the list void DeleteAllControlPointForm(void); //--- Event handler void OnChartEvent(const int id,const long& lparam,const double& dparam,const string& sparam); //--- Constructor/destructor CGStdGraphObjExtToolkit(const ENUM_OBJECT base_type,const string base_name, const long base_chart_id,const int base_subwindow, const int base_pivots,const int ctrl_form_size, const int base_x,const int base_y, const datetime &base_time[],const double &base_price[]) { this.m_list_forms.Clear(); this.SetBaseObj(base_type,base_name,base_chart_id,base_subwindow,base_pivots,ctrl_form_size,base_x,base_y,base_time,base_price); this.CreateAllControlPointForm(); } CGStdGraphObjExtToolkit(){;} ~CGStdGraphObjExtToolkit(){;} }; //+------------------------------------------------------------------+
クラスコンストラクタで、フォームオブジェクトのリストをクリアし、必要なすべての基本オブジェクト値(コンストラクタパラメータで渡される)をクラス変数に設定し、基本グラフィカルオブジェクトの各ピボットポイントで基本オブジェクトを管理するフォームオブジェクトを作成します。
以下は、複合グラフィカルオブジェクトの基本オブジェクトのパラメータを設定するメソッドです。
//+------------------------------------------------------------------+ //| Set the base object parameters of the | //| composite graphical object | //+------------------------------------------------------------------+ void CGStdGraphObjExtToolkit::SetBaseObj(const ENUM_OBJECT base_type,const string base_name, const long base_chart_id,const int base_subwindow, const int base_pivots,const int ctrl_form_size, const int base_x,const int base_y, const datetime &base_time[],const double &base_price[]) { this.m_base_chart_id=base_chart_id; // Base graphical object chart ID this.m_base_subwindow=base_subwindow; // Base graphical object chart subwindow this.m_base_type=base_type; // Base object type this.m_base_name=base_name; // Base object name this.m_base_pivots=base_pivots; // Number of base object reference points this.m_base_x=base_x; // Base object X coordinate this.m_base_y=base_y; // Base object Y coordinate this.SetControlFormSize(ctrl_form_size); // Size of forms for managing reference points if(this.m_base_type==OBJ_LABEL || this.m_base_type==OBJ_BUTTON || this.m_base_type==OBJ_BITMAP_LABEL || this.m_base_type==OBJ_EDIT || this.m_base_type==OBJ_RECTANGLE_LABEL || this.m_base_type==OBJ_CHART) return; if(::ArraySize(base_time)==0) { CMessage::ToLog(DFUN+"base_time: ",MSG_CANV_ELEMENT_ERR_EMPTY_ARRAY); return; } if(::ArraySize(base_price)==0) { CMessage::ToLog(DFUN+"base_price: ",MSG_CANV_ELEMENT_ERR_EMPTY_ARRAY); return; } if(::ArrayResize(this.m_base_time,this.m_base_pivots)!=this.m_base_pivots) { CMessage::ToLog(DFUN,MSG_GRAPH_OBJ_EXT_FAILED_ARR_RESIZE_TIME_DATA); return; } if(::ArrayResize(this.m_base_price,this.m_base_pivots)!=this.m_base_pivots) { CMessage::ToLog(DFUN,MSG_GRAPH_OBJ_EXT_FAILED_ARR_RESIZE_PRICE_DATA); return; } for(int i=0;i<this.m_base_pivots;i++) { this.m_base_time[i]=base_time[i]; // Time (i) of the base object pivot point this.m_base_price[i]=base_price[i]; // Price (i) of the base object pivot point } } //+------------------------------------------------------------------+
このメソッドは、必要なすべての基本グラフィカルオブジェクトプロパティ値を受け取ります。次に、基本オブジェクトタイプを確認します。これが価格/時間座標に基づかないオブジェクトである場合は、メソッドを終了します。そのようなオブジェクトはまだ処理していません。次に、メソッドに渡される基本オブジェクト座標の配列のサイズを確認します。サイズがゼロの場合(メソッドが空の配列を受け取った場合)、そのことを通知してメソッドを終了します。次に、渡されたものに応じて座標内部配列のサイズを変更します。配列の変更に失敗した場合は、そのことを通知して終了します。最後に、単純に入力配列を要素ごとに内部の配列にコピーします。
以下は、ピボットポイントを管理するための参照ポイントのサイズを設定するメソッドです。
//+------------------------------------------------------------------+ //|Set the size of reference points for managing pivot points | //+------------------------------------------------------------------+ void CGStdGraphObjExtToolkit::SetControlFormSize(const int size) { this.m_ctrl_form_size=(size>254 ? 255 : size<5 ? 5 : size%2==0 ? size+1 : size); this.m_shift=(int)ceil(m_ctrl_form_size/2)+1; } //+------------------------------------------------------------------+
メソッドは必要なフォームサイズを受け取ります。サイズが254を超える場合は、255に設定します(奇数値)。渡されたサイズが5未満の場合は、5に設定します(これは最小フォームサイズになります) 価値)。それ以外の場合、渡されたサイズが2に等しい場合は、それに1を追加して使用します。確認された値のいずれもtrueでない場合は、メソッドに渡されたサイズを使用します。
次に、グラフィカルオブジェクトのピボットポイントが正確に中心に配置されるようにフォームを設定するために、座標シフトを計算します。これを実現するには、座標値からシフト値を差し引く必要があります。計算フォームサイズを2つに分割して、最も近い整数値を取得して1を追加します。
以下は、基本オブジェクトの時間座標を設定するメソッドです。
//+------------------------------------------------------------------+ //| Set the time coordinate of the base object | //+------------------------------------------------------------------+ void CGStdGraphObjExtToolkit::SetBaseObjTime(const datetime time,const int index) { if(index>this.m_base_pivots-1) { CMessage::ToLog(DFUN,MSG_LIB_SYS_REQUEST_OUTSIDE_ARRAY); return; } this.m_base_time[index]=time; } //+------------------------------------------------------------------+
メソッドは、ピボットポイント時間とオブジェクトピボットポイントインデックスを受け取ります。インデックスがオブジェクト内のピボットポイントの数を超えている場合は、配列範囲外のリクエストを通知して終了します。その結果、メソッドに渡された時間値は、時間配列のインデックスに対応するセルに設定されます。
このメソッドは、オブジェクトが変更されたときにクラスオブジェクトで基本オブジェクトの時間を指定するために必要です。
以下は、基本オブジェクトの価格座標を設定するメソッドです。
//+------------------------------------------------------------------+ //| Set the coordinate of the base object price | //+------------------------------------------------------------------+ void CGStdGraphObjExtToolkit::SetBaseObjPrice(const double price,const int index) { if(index>this.m_base_pivots-1) { CMessage::ToLog(DFUN,MSG_LIB_SYS_REQUEST_OUTSIDE_ARRAY); return; } this.m_base_price[index]=price; } //+------------------------------------------------------------------+
このメソッドは、インデックスで指定された基本オブジェクトのピボットポイントの価格をクラスの価格配列に追加することを除いて、上記で検討したメソッドと同じです。
以下は、基本オブジェクトの時間と価格の座標を設定するメソッドです。
//+------------------------------------------------------------------+ //| Set the time and price coordinates of the base object | //+------------------------------------------------------------------+ void CGStdGraphObjExtToolkit::SetBaseObjTimePrice(const datetime time,const double price,const int index) { if(index>this.m_base_pivots-1) { CMessage::ToLog(DFUN,MSG_LIB_SYS_REQUEST_OUTSIDE_ARRAY); return; } this.m_base_time[index]=time; this.m_base_price[index]=price; } //+------------------------------------------------------------------+
このメソッドは、価格と時間の両方がクラス配列に設定されていることを除いて、上記の2つのメソッドと同じです。
以下は、グラフィカルオブジェクトの指定されたピボットポイントのX座標とY座標を画面座標で返すメソッドです。
//+------------------------------------------------------------------+ //| 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) { switch(this.m_base_type) { case OBJ_LABEL : case OBJ_BUTTON : case OBJ_BITMAP_LABEL : case OBJ_EDIT : case OBJ_RECTANGLE_LABEL : case OBJ_CHART : x=this.m_base_x; y=this.m_base_y; break; default: if(!::ChartTimePriceToXY(this.m_base_chart_id,this.m_base_subwindow,this.m_base_time[index],this.m_base_price[index],x,y)) { x=0; y=0; return false; } } return true; } //+------------------------------------------------------------------+
このメソッドは、基本グラフィカルオブジェクトの必要なピボットポイントのインデックスを受け取ります。これは、画面座標(画面の左上隅からのピクセル単位)のピボットポイントであり、フォーム画面座標を受け取る2つの変数(リンクを介して)を受け取る必要があります。オブジェクトがすでに画面座標内にある場合、これらの座標が返されます。
オブジェクトが価格/時間座標内にある場合は、 ChartTimePriceToXY()関数を使用してそれらを計算します。座標を画面の座標に変換できなかった場合は、座標をゼロに設定し、falseを返します。
結果としてtrueを返します。
以下は、名前でピボットポイントフォームへのポインタを返すメソッドです。
//+------------------------------------------------------------------+ //| Return the pointer to the pivot point form by name | //+------------------------------------------------------------------+ CForm *CGStdGraphObjExtToolkit::GetControlPointForm(const string name,int &index) { index=WRONG_VALUE; for(int i=0;i<this.m_list_forms.Total();i++) { CForm *form=this.m_list_forms.At(i); if(form==NULL) continue; if(form.Name()==name) { index=i; return form; } } return NULL; } //+------------------------------------------------------------------+
このメソッドは、フォームオブジェクトのリストで検出されたフォームのインデックスを特徴とするリンクを介して、目的のフォームの名前と変数を受け取ります。
フォームオブジェクトのリストによるループで、次のオブジェクトを取得します。その名前が目的の名前と一致する場合は、ループインデックスを変数に書き込み、検出されたオブジェクトへのポインタを返します。ループが完了したら、NULLを返します。フォームが見つからなかったため、インデックスは-1になります。この値は、ループ開始前に設定されています。
以下は、基本オブジェクト参照ポイントにフォームオブジェクトを作成するメソッドです。
//+------------------------------------------------------------------+ //| Create a form object on a base object reference point | //+------------------------------------------------------------------+ CForm *CGStdGraphObjExtToolkit::CreateNewControlPointForm(const int index) { string name=this.m_base_name+"_TKPP_"+(index<this.m_base_pivots ? (string)index : "X"); CForm *form=this.GetControlPointForm(index); if(form!=NULL) return NULL; int x=0, y=0; if(!this.GetControlPointCoordXY(index,x,y)) return NULL; return new CForm(this.m_base_chart_id,this.m_base_subwindow,name,x-this.m_shift,y-this.m_shift,this.GetControlFormSize(),this.GetControlFormSize()); } //+------------------------------------------------------------------+
このメソッドは、フォームオブジェクトを作成する必要のあるピボットポイントのインデックスを受け取ります。
基本オブジェクト名+「ToolKitPivotPoint」」(_TKPP)の省略形+ピボットポイントインデックスで構成されるフォームオブジェクト名を作成します。インデックスの説明を作成するときは、その値を確認します。基本オブジェクトのピボットポイントの数より少ない場合(計算はゼロから開始)、メソッドに渡されるインデックスの文字列表現を使用します。それ以外の場合は、Xアイコンを使用します。これは何故必要なのでしょうか。後に、従属オブジェクトをそのピボットポイントだけでなくポイント間でも基本オブジェクトに接続できるようになります。さらに、オブジェクト全体を再配置するために、基本オブジェクトラインの中心にコントロールフォームを作成する必要があります。したがって、フォーム名には、ピボットポイントだけでなく、他のポイントにもフォームを作成する機能が含まれている必要があります。
次に、メソッドに渡されたインデックスによってリスト内のフォームの存在を確認します。フォームオブジェクトがリスト内のインデックスにすでに存在する場合(ポインタがNULLと等しくない場合)、NULLを返します。
次に、ピボットポイントの座標をインデックスで画面座標に変換し、受信した座標にフォームオブジェクトを作成した結果を返します。フォームの中心をピボットポイントに正確に配置するために、シフト値は両方の座標から引かれます
フォームのアンカーポイントの値を設定するだけでもかまいませんが、ライブラリのルールでは、すべてのフォームのアンカーポイントは左上隅にあって変更されないままであると規定されています。したがって、フォームオブジェクトの配置のシフトは必要に応じて使用されます。
以下は、基本オブジェクトのピボットポイントにフォームオブジェクトを作成するメソッドです。
//+------------------------------------------------------------------+ //| Create form objects on the base object pivot points | //+------------------------------------------------------------------+ bool CGStdGraphObjExtToolkit::CreateAllControlPointForm(void) { bool res=true; //--- In the loop by the number of base object pivot points for(int i=0;i<this.m_base_pivots;i++) { //--- Create a new form object on the current pivot point corresponding to the loop index CForm *form=this.CreateNewControlPointForm(i); //--- If failed to create the form, inform of that and add 'false' to the final result if(form==NULL) { CMessage::ToLog(DFUN,MSG_GRAPH_OBJ_EXT_FAILED_CREATE_CTRL_POINT_FORM); res &=false; } //--- If failed to add the form to the list, inform of that, remove the created form and add 'false' to the final result if(!this.m_list_forms.Add(form)) { CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST); delete form; res &=false; } //--- Set all the necessary properties for the created form object form.SetBelong(GRAPH_OBJ_BELONG_PROGRAM); // Object is created programmatically form.SetActive(true); // Form object is active form.SetMovable(true); // Movable object form.SetActiveAreaShift(0,0,0,0); // Object active area - the entire form form.SetFlagSelected(false,false); // Object is not selected form.SetFlagSelectable(false,false); // Object cannot be selected by mouse form.Erase(CLR_CANV_NULL,0); // Fill in the form with transparent color and set the full transparency //form.DrawRectangle(0,0,form.Width()-1,form.Height()-1,clrSilver); // Draw an outlining rectangle for visual display of the form location form.DrawCircle((int)floor(form.Width()/2),(int)floor(form.Height()/2),CTRL_POINT_SIZE,clrDodgerBlue); // Draw a circle in the form center form.DrawCircleFill((int)floor(form.Width()/2),(int)floor(form.Height()/2),2,clrDodgerBlue); // Draw a point in the form center form.Done(); // Save the initial form object state (its appearance) } //--- Redraw the chart for displaying changes (if successful) and return the final result if(res) ::ChartRedraw(this.m_base_chart_id); return res; } //+------------------------------------------------------------------+
ここでのロジック全体は、コードコメントで説明されています。簡単に言うと、基本オブジェクトのピボットポイントの数によるループで、ピボットポイントごとに新しいフォームオブジェクトを作成してフォームオブジェクトのリストに追加して、適切な基本オブジェクトのピボットポイントの座標を各フォームに設定します。各フォームには、中央の円と、これが基本オブジェクトのピボットポイントを管理するオブジェクトであることを指定するポイントがあります。
このようなオブジェクトは、最初は非表示になり、マウスカーソルがフォーム領域に合わせられたときにのみ表示されます。今のところ、チャートの変更中の動作をテストするために、それらを表示することにします。以降の記事では自分の計画に固執して、オブジェクトは最初は非表示で、マウスカーソルがアクティブな領域(つまり、フォームオブジェクト全体のサイズ)にを合わせられたときにのみ表示されるようにします。
以下は、リストからすべてのフォームオブジェクトを削除するメソッドです。
//+------------------------------------------------------------------+ //| Remove all form objects from the list | //+------------------------------------------------------------------+ void CGStdGraphObjExtToolkit::DeleteAllControlPointForm(void) { this.m_list_forms.Clear(); } //+------------------------------------------------------------------+
Clear()メソッドを使用するだけで、リスト全体が完全にクリアされます。
イベントハンドラで、発生したイベントに従ってフォームオブジェクトイベントを処理します。
//+------------------------------------------------------------------+ //| Event handler | //+------------------------------------------------------------------+ void CGStdGraphObjExtToolkit::OnChartEvent(const int id,const long& lparam,const double& dparam,const string& sparam) { if(id==CHARTEVENT_CHART_CHANGE) { for(int i=0;i<this.m_list_forms.Total();i++) { CForm *form=this.m_list_forms.At(i); if(form==NULL) continue; int x=0, y=0; if(!this.GetControlPointCoordXY(i,x,y)) continue; form.SetCoordX(x-this.m_shift); form.SetCoordY(y-this.m_shift); form.Update(); } ::ChartRedraw(this.m_base_chart_id); } } //+------------------------------------------------------------------+
現在、チャート変更イベントのみを処理しています。すべてのフォームオブジェクトによるループで、リストから次のフォームを取得します。描画されたピボットポイントに従って画面座標を受信できなかった場合は、次のフォームに進みます。新しい画面座標をフォームに設定し、フォームを更新します。ループが完了したら、変更を表示するためにチャートを再描画します。
拡張された標準グラフィカルオブジェクトのツールキットオブジェクトは標準グラフィカルオブジェクトクラスのオブジェクトに格納されているため、\MQL5\Include\DoEasy\Objects\Graph\Standard\GStdGraphObj.mqhでクラスを改善する必要があります。
まず、フォームオブジェクトのファイルと、拡張された標準グラフィカルオブジェクトの新しく作成されたツールキットオブジェクトをファイルに含めます。
//+------------------------------------------------------------------+ //| GStdGraphObj.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" #include "..\..\..\Services\Properties.mqh" #include "..\..\Graph\Form.mqh" #include "..\..\Graph\Extend\CGStdGraphObjExtToolkit.mqh" //+------------------------------------------------------------------+ //| Class of the dependent object pivot point data | //+------------------------------------------------------------------+
抽象標準グラフィカルオブジェクトのクラスのprivateセクションで、拡張標準グラフィカルオブジェクトのツールキットオブジェクトへのポインタを宣言します。
//+------------------------------------------------------------------+ //| The class of the abstract standard graphical object | //+------------------------------------------------------------------+ class CGStdGraphObj : public CGBaseObj { private: CArrayObj m_list; // List of subordinate graphical objects CProperties *Prop; // Pointer to the property object CLinkedPivotPoint m_linked_pivots; // Linked pivot points CGStdGraphObjExtToolkit *ExtToolkit; // Pointer to the extended graphical object toolkit int m_pivots; // Number of object reference points //--- Read and set (1) the time and (2) the price of the specified object pivot point void SetTimePivot(const int index); void SetPricePivot(const int index); //--- Read and set (1) color, (2) style, (3) width, (4) value, (5) text of the specified object level void SetLevelColor(const int index); void SetLevelStyle(const int index); void SetLevelWidth(const int index); void SetLevelValue(const int index); void SetLevelText(const int index); //--- Read and set the BMP file name for the "Bitmap Level" object. Index: 0 - ON, 1 - OFF void SetBMPFile(const int index); public:
publicセクションで、ツールキットオブジェクトへのポインタを返すメソッドを記述します。
public: //--- Set object's (1) integer, (2) real and (3) string properties void SetProperty(ENUM_GRAPH_OBJ_PROP_INTEGER property,int index,long value) { this.Prop.Curr.SetLong(property,index,value); } void SetProperty(ENUM_GRAPH_OBJ_PROP_DOUBLE property,int index,double value) { this.Prop.Curr.SetDouble(property,index,value); } void SetProperty(ENUM_GRAPH_OBJ_PROP_STRING property,int index,string value) { this.Prop.Curr.SetString(property,index,value); } //--- Return object’s (1) integer, (2) real and (3) string property from the properties array long GetProperty(ENUM_GRAPH_OBJ_PROP_INTEGER property,int index) const { return this.Prop.Curr.GetLong(property,index); } double GetProperty(ENUM_GRAPH_OBJ_PROP_DOUBLE property,int index) const { return this.Prop.Curr.GetDouble(property,index); } string GetProperty(ENUM_GRAPH_OBJ_PROP_STRING property,int index) const { return this.Prop.Curr.GetString(property,index); } //--- Set object's previous (1) integer, (2) real and (3) string properties void SetPropertyPrev(ENUM_GRAPH_OBJ_PROP_INTEGER property,int index,long value) { this.Prop.Prev.SetLong(property,index,value); } void SetPropertyPrev(ENUM_GRAPH_OBJ_PROP_DOUBLE property,int index,double value){ this.Prop.Prev.SetDouble(property,index,value); } void SetPropertyPrev(ENUM_GRAPH_OBJ_PROP_STRING property,int index,string value){ this.Prop.Prev.SetString(property,index,value); } //--- Return object’s (1) integer, (2) real and (3) string property from the previous properties array long GetPropertyPrev(ENUM_GRAPH_OBJ_PROP_INTEGER property,int index) const { return this.Prop.Prev.GetLong(property,index); } double GetPropertyPrev(ENUM_GRAPH_OBJ_PROP_DOUBLE property,int index) const { return this.Prop.Prev.GetDouble(property,index); } string GetPropertyPrev(ENUM_GRAPH_OBJ_PROP_STRING property,int index) const { return this.Prop.Prev.GetString(property,index); } //--- Return (1) itself, (2) properties and (3) the change history CGStdGraphObj *GetObject(void) { return &this; } CProperties *Properties(void) { return this.Prop; } CChangeHistory *History(void) { return this.Prop.History;} CGStdGraphObjExtToolkit *GetExtToolkit(void) { return this.ExtToolkit; } //--- Return the flag of the object supporting this property virtual bool SupportProperty(ENUM_GRAPH_OBJ_PROP_INTEGER property) { return true; } virtual bool SupportProperty(ENUM_GRAPH_OBJ_PROP_DOUBLE property) { return true; } virtual bool SupportProperty(ENUM_GRAPH_OBJ_PROP_STRING property) { return true; }
クラスのpublicセクションで、グラフィカルオブジェクトイベントのハンドラを宣言します。コンストラクタでは、ツールキットオブジェクトへのポインタにデフォルト値のNULLを設定し、クラスデストラクタでは、ポインタの有効性を確認します。まず、ツールキットオブジェクトからすべてのフォームを削除してからオブジェクト自体を削除します。
private: //--- Set the X coordinate (1) from the specified property of the base object to the specified subordinate object, (2) from the base object void SetCoordXToDependentObj(CGStdGraphObj *obj,const int prop_from,const int modifier_from,const int modifier_to); void SetCoordXFromBaseObj(const int prop_from,const int modifier_from,const int modifier_to); //--- Set the Y coordinate (1) from the specified property of the base object to the specified subordinate object, (2) from the base object void SetCoordYToDependentObj(CGStdGraphObj *obj,const int prop_from,const int modifier_from,const int modifier_to); void SetCoordYFromBaseObj(const int prop_from,const int modifier_from,const int modifier_to); //--- Set the (1) integer, (2) real and (3) string property to the specified subordinate property void SetDependentINT(CGStdGraphObj *obj,const ENUM_GRAPH_OBJ_PROP_INTEGER prop,const long value,const int modifier); void SetDependentDBL(CGStdGraphObj *obj,const ENUM_GRAPH_OBJ_PROP_DOUBLE prop,const double value,const int modifier); void SetDependentSTR(CGStdGraphObj *obj,const ENUM_GRAPH_OBJ_PROP_STRING prop,const string value,const int modifier); public: //--- Event handler void OnChartEvent(const int id,const long& lparam,const double& dparam,const string& sparam); //--- Default constructor CGStdGraphObj(){ this.m_type=OBJECT_DE_TYPE_GSTD_OBJ; this.m_species=WRONG_VALUE; this.ExtToolkit=NULL; } //--- Destructor ~CGStdGraphObj() { if(this.Prop!=NULL) delete this.Prop; if(this.ExtToolkit!=NULL) { this.ExtToolkit.DeleteAllControlPointForm(); delete this.ExtToolkit; } } protected: //--- Protected parametric constructor CGStdGraphObj(const ENUM_OBJECT_DE_TYPE obj_type, const ENUM_GRAPH_ELEMENT_TYPE elm_type, const ENUM_GRAPH_OBJ_BELONG belong, const ENUM_GRAPH_OBJ_SPECIES species, const long chart_id, const int pivots, const string name); public: //+--------------------------------------------------------------------+ //|Methods of simplified access and setting graphical object properties| //+--------------------------------------------------------------------+
簡略化されたアクセスとグラフィカルオブジェクトプロパティの設定のためのメソッドのブロックで、グラフィカルオブジェクトのピボットポイントの数を返すメソッドを記述します。
public: //+--------------------------------------------------------------------+ //|Methods of simplified access and setting graphical object properties| //+--------------------------------------------------------------------+ //--- Number of object reference points int Pivots(void) const { return this.m_pivots; } //--- Object index in the list int Number(void) const { return (int)this.GetProperty(GRAPH_OBJ_PROP_NUM,0); } void SetNumber(const int number) { this.SetProperty(GRAPH_OBJ_PROP_NUM,0,number); }
protectedパラメトリックコンストラクタで、グラフィカル要素タイプを確認します。これが拡張グラフィカルオブジェクトの場合は、新しいツールキットオブジェクトを作成し、そのオブジェクトへのポインタを ExtToolkit 変数に保存します。コンストラクタの最後で、ツールキットオブジェクトを初期化します。
//+------------------------------------------------------------------+ //| Protected parametric constructor | //+------------------------------------------------------------------+ CGStdGraphObj::CGStdGraphObj(const ENUM_OBJECT_DE_TYPE obj_type, const ENUM_GRAPH_ELEMENT_TYPE elm_type, const ENUM_GRAPH_OBJ_BELONG belong, const ENUM_GRAPH_OBJ_SPECIES species, const long chart_id,const int pivots, const string name) { //--- Create the property object with the default values this.Prop=new CProperties(GRAPH_OBJ_PROP_INTEGER_TOTAL,GRAPH_OBJ_PROP_DOUBLE_TOTAL,GRAPH_OBJ_PROP_STRING_TOTAL); this.ExtToolkit=(elm_type==GRAPH_ELEMENT_TYPE_STANDARD_EXTENDED ? new CGStdGraphObjExtToolkit() : NULL); //--- Set the number of pivot points and object levels this.m_pivots=pivots; int levels=(int)::ObjectGetInteger(chart_id,name,OBJPROP_LEVELS); //--- Set the property array dimensionalities according to the number of pivot points and levels this.Prop.SetSizeRange(GRAPH_OBJ_PROP_TIME,this.m_pivots); this.Prop.SetSizeRange(GRAPH_OBJ_PROP_PRICE,this.m_pivots); this.Prop.SetSizeRange(GRAPH_OBJ_PROP_LEVELCOLOR,levels); this.Prop.SetSizeRange(GRAPH_OBJ_PROP_LEVELSTYLE,levels); this.Prop.SetSizeRange(GRAPH_OBJ_PROP_LEVELWIDTH,levels); this.Prop.SetSizeRange(GRAPH_OBJ_PROP_LEVELVALUE,levels); this.Prop.SetSizeRange(GRAPH_OBJ_PROP_LEVELTEXT,levels); this.Prop.SetSizeRange(GRAPH_OBJ_PROP_BMPFILE,2); //--- Set the object (1) type, type of graphical (2) object, (3) element, (4) subwindow affiliation and (5) index, as well as (6) chart symbol Digits this.m_type=obj_type; this.SetName(name); CGBaseObj::SetChartID(chart_id); CGBaseObj::SetTypeGraphObject(CGBaseObj::GraphObjectType(obj_type)); CGBaseObj::SetTypeElement(elm_type); CGBaseObj::SetBelong(belong); CGBaseObj::SetSpecies(species); CGBaseObj::SetSubwindow(chart_id,name); CGBaseObj::SetDigits((int)::SymbolInfoInteger(::ChartSymbol(chart_id),SYMBOL_DIGITS)); //--- Save the integer properties inherent in all graphical objects but not present in the current one this.SetProperty(GRAPH_OBJ_PROP_CHART_ID,0,CGBaseObj::ChartID()); // Chart ID this.SetProperty(GRAPH_OBJ_PROP_WND_NUM,0,CGBaseObj::SubWindow()); // Chart subwindow index this.SetProperty(GRAPH_OBJ_PROP_TYPE,0,CGBaseObj::TypeGraphObject()); // Graphical object type (ENUM_OBJECT) this.SetProperty(GRAPH_OBJ_PROP_ELEMENT_TYPE,0,CGBaseObj::TypeGraphElement()); // Graphical element type (ENUM_GRAPH_ELEMENT_TYPE) this.SetProperty(GRAPH_OBJ_PROP_BELONG,0,CGBaseObj::Belong()); // Graphical object affiliation this.SetProperty(GRAPH_OBJ_PROP_SPECIES,0,CGBaseObj::Species()); // Graphical object species this.SetProperty(GRAPH_OBJ_PROP_GROUP,0,0); // Graphical object group this.SetProperty(GRAPH_OBJ_PROP_ID,0,0); // Object ID this.SetProperty(GRAPH_OBJ_PROP_BASE_ID,0,0); // Base object ID this.SetProperty(GRAPH_OBJ_PROP_NUM,0,0); // Object index in the list this.SetProperty(GRAPH_OBJ_PROP_CHANGE_HISTORY,0,false); // Flag of storing the change history this.SetProperty(GRAPH_OBJ_PROP_BASE_NAME,0,this.Name()); // Base object name //--- Save the properties inherent in all graphical objects and present in a graphical object this.PropertiesRefresh(); //--- Save basic properties in the parent object this.m_create_time=(datetime)this.GetProperty(GRAPH_OBJ_PROP_CREATETIME,0); this.m_back=(bool)this.GetProperty(GRAPH_OBJ_PROP_BACK,0); this.m_selected=(bool)this.GetProperty(GRAPH_OBJ_PROP_SELECTED,0); this.m_selectable=(bool)this.GetProperty(GRAPH_OBJ_PROP_SELECTABLE,0); this.m_hidden=(bool)this.GetProperty(GRAPH_OBJ_PROP_HIDDEN,0); //--- Initialize the extended graphical object toolkit if(this.GraphElementType()==GRAPH_ELEMENT_TYPE_STANDARD_EXTENDED) { datetime times[]; double prices[]; if(::ArrayResize(times,this.Pivots())!=this.Pivots()) CMessage::ToLog(DFUN,MSG_GRAPH_OBJ_EXT_FAILED_ARR_RESIZE_TIME_DATA); if(::ArrayResize(prices,this.Pivots())!=this.Pivots()) CMessage::ToLog(DFUN,MSG_GRAPH_OBJ_EXT_FAILED_ARR_RESIZE_PRICE_DATA); for(int i=0;i<this.Pivots();i++) { times[i]=this.Time(i); prices[i]=this.Price(i); } this.ExtToolkit.SetBaseObj(this.TypeGraphObject(),this.Name(),this.ChartID(),this.SubWindow(),this.Pivots(),CTRL_FORM_SIZE,this.XDistance(),this.YDistance(),times,prices); this.ExtToolkit.CreateAllControlPointForm(); this.SetFlagSelected(false,false); this.SetFlagSelectable(false,false); } //--- Save the current properties to the previous ones this.PropertiesCopyToPrevData(); } //+-------------------------------------------------------------------+
ツールキットオブジェクトを初期化するときは、まず時間と価格のプロパティの配列を宣言し、グラフィカルオブジェクトのピボットポイントの数に応じてサイズを調整し、ループインデックスに対応するオブジェクトのピボットポイントから価格と時間の値を設定します。
次に、ツールキットオブジェクトの初期化メソッドを呼び出し、必要なグラフィカルオブジェクトパラメータを、新しく入力された価格と時間のプロパティ配列とともに渡します。初期化後、グラフィカルオブジェクトのピボットポイントにフォームオブジェクトを作成するためのメソッドを呼び出します。最後に、グラフィカルオブジェクトの選択されていないオブジェクトのステータスを設定し、マウスで選択する機能を無効にします。
オブジェクトプロパティの変更を確認するメソッドの拡張標準グラフィカルオブジェクトを処理するコードブロックで、拡張された標準グラフィカルオブジェクトのピボットポイントの位置を変更するときに、コントロールポイント(フォームオブジェクト)を移動するためのコードブロックを新しい画面座標に追加します。
//+------------------------------------------------------------------+ //| Check object property changes | //+------------------------------------------------------------------+ void CGStdGraphObj::PropertiesCheckChanged(void) { CGBaseObj::ClearEventsList(); bool changed=false; int begin=0, end=GRAPH_OBJ_PROP_INTEGER_TOTAL; for(int i=begin; i<end; i++) { ENUM_GRAPH_OBJ_PROP_INTEGER prop=(ENUM_GRAPH_OBJ_PROP_INTEGER)i; if(!this.SupportProperty(prop)) continue; for(int j=0;j<Prop.CurrSize(prop);j++) { if(this.GetProperty(prop,j)!=this.GetPropertyPrev(prop,j)) { changed=true; this.CreateAndAddNewEvent(GRAPH_OBJ_EVENT_CHANGE,this.ChartID(),prop,this.Name()); } } } begin=end; end+=GRAPH_OBJ_PROP_DOUBLE_TOTAL; for(int i=begin; i<end; i++) { ENUM_GRAPH_OBJ_PROP_DOUBLE prop=(ENUM_GRAPH_OBJ_PROP_DOUBLE)i; if(!this.SupportProperty(prop)) continue; for(int j=0;j<Prop.CurrSize(prop);j++) { if(this.GetProperty(prop,j)!=this.GetPropertyPrev(prop,j)) { changed=true; this.CreateAndAddNewEvent(GRAPH_OBJ_EVENT_CHANGE,this.ChartID(),prop,this.Name()); } } } begin=end; end+=GRAPH_OBJ_PROP_STRING_TOTAL; for(int i=begin; i<end; i++) { ENUM_GRAPH_OBJ_PROP_STRING prop=(ENUM_GRAPH_OBJ_PROP_STRING)i; if(!this.SupportProperty(prop)) continue; for(int j=0;j<Prop.CurrSize(prop);j++) { if(this.GetProperty(prop,j)!=this.GetPropertyPrev(prop,j) && prop!=GRAPH_OBJ_PROP_NAME) { changed=true; this.CreateAndAddNewEvent(GRAPH_OBJ_EVENT_CHANGE,this.ChartID(),prop,this.Name()); } } } if(changed) { for(int i=0;i<this.m_list_events.Total();i++) { CGBaseEvent *event=this.m_list_events.At(i); if(event==NULL) continue; ::EventChartCustom(::ChartID(),event.ID(),event.Lparam(),event.Dparam(),event.Sparam()); } if(this.AllowChangeHistory()) { int total=HistoryChangesTotal(); if(this.CreateNewChangeHistoryObj(total<1)) ::Print ( DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_SUCCESS_CREATE_SNAPSHOT)," #",(total==0 ? "0-1" : (string)total), ": ",this.HistoryChangedObjTimeChangedToString(total-1) ); } //--- If subordinate objects are attached to the base one (in a composite graphical object) if(this.m_list.Total()>0) { //--- In the loop by the number of added graphical objects, for(int i=0;i<this.m_list.Total();i++) { //--- get the next graphical object, CGStdGraphObj *dep=m_list.At(i); if(dep==NULL) continue; //--- get the data object of its pivot points, CLinkedPivotPoint *pp=dep.GetLinkedPivotPoint(); if(pp==NULL) continue; //--- get the number of coordinate points the object is attached to int num=pp.GetNumLinkedCoords(); //--- In the loop by the object coordinate points, for(int j=0;j<num;j++) { //--- get the number of coordinate points of the base object for setting the X coordinate int numx=pp.GetBasePivotsNumX(j); //--- In the loop by each coordinate point for setting the X coordinate, for(int nx=0;nx<numx;nx++) { //--- get the property for setting the X coordinate, its modifier //--- and set it in the object selected as the current one in the main loop int prop_from=pp.GetPropertyX(j,nx); int modifier_from=pp.GetPropertyModifierX(j,nx); this.SetCoordXToDependentObj(dep,prop_from,modifier_from,nx); } //--- Get the number of coordinate points of the base object for setting the Y coordinate int numy=pp.GetBasePivotsNumY(j); //--- In the loop by each coordinate point for setting the Y coordinate, for(int ny=0;ny<numy;ny++) { //--- get the property for setting the Y coordinate, its modifier //--- and set it in the object selected as the current one in the main loop int prop_from=pp.GetPropertyY(j,ny); int modifier_from=pp.GetPropertyModifierY(j,ny); this.SetCoordYToDependentObj(dep,prop_from,modifier_from,ny); } } dep.PropertiesCopyToPrevData(); } //--- Move reference control points to new coordinates if(ExtToolkit!=NULL) { for(int i=0;i<this.Pivots();i++) { ExtToolkit.SetBaseObjTimePrice(this.Time(i),this.Price(i),i); } ExtToolkit.SetBaseObjCoordXY(this.XDistance(),this.YDistance()); long lparam=0; double dparam=0; string sparam=""; ExtToolkit.OnChartEvent(CHARTEVENT_CHART_CHANGE,lparam,dparam,sparam); } //--- Upon completion of the loop of handling all bound objects, redraw the chart to display all the changes ::ChartRedraw(m_chart_id); } //--- Save the current properties as the previous ones this.PropertiesCopyToPrevData(); } } //+------------------------------------------------------------------+
グラフィカルオブジェクトのピボットポイントの1つまたはオブジェクト全体を移動すると、そのピボットポイントの画面座標が変化するため、ツールキットクラスのフォームオブジェクトも新しい画面座標に移動する必要があります。したがって、まず新しいグラフィカルオブジェクト座標をツールキットオブジェクトに渡します(ループ内で、価格/時間座標のピボットポイントの数とピクセル単位の個別の座標による) 。次に、チャート変更イベントのIDを渡すことにより、ツールキットオブジェクトイベントのハンドラを呼び出します。ツールキットオブジェクトイベントのハンドラですべてのフォームの画面座標を再計算し、グラフィカルオブジェクトの新しい価格と時間の座標に従って新しい場所に再配置します。
下位の標準グラフィカルオブジェクトをリストに追加する方法で、小さなエラーを修正します。下位のグラフィカルオブジェクトではそれ自体のプロパティを変更するため、クリックしたときにこれらのグラフィカルオブジェクトを変更する新しいイベントが生成されないように、新しいプロパティは、すぐに前のプロパティとして保存する必要があります。
//+------------------------------------------------------------------+ //| Add a subordinate standard graphical object to the list | //+------------------------------------------------------------------+ bool CGStdGraphObj::AddDependentObj(CGStdGraphObj *obj) { //--- If the current object is not an extended one, inform of that and return 'false' if(this.TypeGraphElement()!=GRAPH_ELEMENT_TYPE_STANDARD_EXTENDED) { CMessage::ToLog(MSG_GRAPH_OBJ_NOT_EXT_OBJ); return false; } //--- If failed to add the pointer to the passed object into the list, inform of that and return 'false' if(!this.m_list.Add(obj)) { CMessage::ToLog(DFUN,MSG_GRAPH_OBJ_FAILED_ADD_DEP_EXT_OBJ_TO_LIST); return false; } //--- Object added to the list - set its number in the list, //--- name and ID of the current object as the base one, //--- set the flags of object availability and selection //--- and the graphical element type - standard extended graphical object obj.SetNumber(this.m_list.Total()-1); obj.SetBaseName(this.Name()); obj.SetBaseObjectID(this.ObjectID()); obj.SetFlagSelected(false,false); obj.SetFlagSelectable(false,false); obj.SetTypeElement(GRAPH_ELEMENT_TYPE_STANDARD_EXTENDED); obj.PropertiesCopyToPrevData(); return true; } //+------------------------------------------------------------------+
以下は、抽象標準グラフィカルオブジェクトイベントのハンドラです。
//+------------------------------------------------------------------+ //| Event handler | //+------------------------------------------------------------------+ void CGStdGraphObj::OnChartEvent(const int id,const long &lparam,const double &dparam,const string &sparam) { if(GraphElementType()!=GRAPH_ELEMENT_TYPE_STANDARD_EXTENDED) return; if(id==CHARTEVENT_CHART_CHANGE) { if(ExtToolkit==NULL) return; for(int i=0;i<this.Pivots();i++) { ExtToolkit.SetBaseObjTimePrice(this.Time(i),this.Price(i),i); } ExtToolkit.SetBaseObjCoordXY(this.XDistance(),this.YDistance()); ExtToolkit.OnChartEvent(id,lparam,dparam,sparam); } } //+------------------------------------------------------------------+
今のところ、ハンドラはチャート変更イベントのみを処理します。
オブジェクトが拡張されていない場合は、ハンドラを終了します。チャート変更イベントが検出された場合、拡張標準グラフィカルオブジェクトのツールキットオブジェクトへのポインタの有効性を確認します。ツールキットが作成されていない場合は、終了します。次に、グラフィカルオブジェクトのピボットポイント数のループで、新しいグラフィカルオブジェクトの価格/時間座標をツールキットオブジェクトに設定します。次に、新しい画面座標を設定し、ツールキットオブジェクトイベントのハンドラを呼び出します。ここで、すべてのフォームが、新しくツールキットオブジェクトに渡された価格/時間座標に基づいて新しい画面座標に再配置されます。
拡張された標準グラフィカルオブジェクトをチャートから削除する場合、そのようなオブジェクトがグラフィカルオブジェクト用に作成された場合に備えて、ツールキットオブジェクトのフォームオブジェクトも削除する必要があります。チャートからのグラフィカルオブジェクトの削除は、\MQL5\Include\DoEasy\Collections\GraphElementsCollection.mqhのグラフィカル要素のコレクションクラスで処理されます。
拡張グラフィカルオブジェクトの削除を処理するメソッドで、ツールキットオブジェクトからすべてのフォームオブジェクトを削除するためのコードブロックを追加します。
//+------------------------------------------------------------------+ //| Handle the removal of extended graphical objects | //+------------------------------------------------------------------+ void CGraphElementsCollection::DeleteExtendedObj(CGStdGraphObj *obj) { if(obj==NULL) return; //--- Save the ID of the graphical object chart and the number of subordinate objects in its list long chart_id=obj.ChartID(); int total=obj.GetNumDependentObj(); //--- If the list of subordinate objects is not empty (this is the base object) if(total>0) { CGStdGraphObjExtToolkit *toolkit=obj.GetExtToolkit(); if(toolkit!=NULL) { toolkit.DeleteAllControlPointForm(); } //--- In the loop, move along all dependent objects and remove them for(int n=total-1;n>WRONG_VALUE;n--) { //--- Get the next graphical object CGStdGraphObj *dep=obj.GetDependentObj(n); if(dep==NULL) continue; //--- If failed to remove it from the chart, display the appropriate message in the journal if(!::ObjectDelete(dep.ChartID(),dep.Name())) CMessage::ToLog(DFUN+dep.Name()+": ",MSG_GRAPH_OBJ_FAILED_DELETE_OBJ_FROM_CHART); } //--- Upon the loop completion, update the chart to display the changes and exit the method ::ChartRedraw(chart_id); return; } //--- If this is a subordinate object else if(obj.BaseObjectID()>0) { //--- Get the base object name and its ID string base_name=obj.BaseName(); long base_id=obj.BaseObjectID(); //--- Get the base object from the graphical object collection list CGStdGraphObj *base=GetStdGraphObject(base_name,chart_id); if(base==NULL) return; //--- get the number of dependent objects in its list int count=base.GetNumDependentObj(); //--- In the loop, move along all its dependent objects and remove them for(int n=count-1;n>WRONG_VALUE;n--) { //--- Get the next graphical object CGStdGraphObj *dep=base.GetDependentObj(n); //--- If failed to get the pointer or the object has already been removed from the chart, move on to the next one if(dep==NULL || !this.IsPresentGraphObjOnChart(dep.ChartID(),dep.Name())) continue; //--- If failed to delete the graphical object from the chart, //--- display the appropriate message in the journal and move on to the next one if(!::ObjectDelete(dep.ChartID(),dep.Name())) { CMessage::ToLog(DFUN+dep.Name()+": ",MSG_GRAPH_OBJ_FAILED_DELETE_OBJ_FROM_CHART); continue; } } //--- Remove the base object from the chart and from the list if(!::ObjectDelete(base.ChartID(),base.Name())) CMessage::ToLog(DFUN+base.Name()+": ",MSG_GRAPH_OBJ_FAILED_DELETE_OBJ_FROM_CHART); } //--- Update the chart for displaying the changes ::ChartRedraw(chart_id); } //+------------------------------------------------------------------+
ここでは、拡張グラフィカルオブジェクトのツールキットオブジェクトへのポインタを取得するだけです。ポインタが有効な場合は、以前に検討した拡張標準グラフィカルオブジェクトのツールキットオブジェクトの作成されたすべてのフォームを削除するためのメソッドを呼び出します。
グラフィカル要素コレクションクラスイベントのハンドラで、拡張された標準グラフィカルオブジェクトのチャート変更の処理を追加します。
//+------------------------------------------------------------------+ //| Event handler | //+------------------------------------------------------------------+ void CGraphElementsCollection::OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { CGStdGraphObj *obj=NULL; ushort idx=ushort(id-CHARTEVENT_CUSTOM); if(id==CHARTEVENT_OBJECT_CHANGE || id==CHARTEVENT_OBJECT_DRAG || id==CHARTEVENT_OBJECT_CLICK || idx==CHARTEVENT_OBJECT_CHANGE || idx==CHARTEVENT_OBJECT_DRAG || idx==CHARTEVENT_OBJECT_CLICK) { //--- Calculate the chart ID //--- If the event ID corresponds to an event from the current chart, the chart ID is received from ChartID //--- If the event ID corresponds to a user event, the chart ID is received from lparam //--- Otherwise, the chart ID is assigned to -1 long param=(id==CHARTEVENT_OBJECT_CLICK ? ::ChartID() : idx==CHARTEVENT_OBJECT_CLICK ? lparam : WRONG_VALUE); long chart_id=(param==WRONG_VALUE ? (lparam==0 ? ::ChartID() : lparam) : param); //--- Get the object, whose properties were changed or which was relocated, //--- from the collection list by its name set in sparam obj=this.GetStdGraphObject(sparam,chart_id); //--- If failed to get the object by its name, it is not on the list, //--- which means its name has been changed if(obj==NULL) { //--- Let's search the list for the object that is not on the chart obj=this.FindMissingObj(chart_id); //--- If failed to find the object here as well, exit if(obj==NULL) return; //--- Get the name of the renamed graphical object on the chart, which is not in the collection list string name_new=this.FindExtraObj(chart_id); //--- set a new name for the collection list object, which does not correspond to any graphical object on the chart, //--- and send an event with the new name of the object to the control program chart if(obj.SetNamePrev(obj.Name()) && obj.SetName(name_new)) ::EventChartCustom(this.m_chart_id_main,GRAPH_OBJ_EVENT_RENAME,obj.ChartID(),obj.TimeCreate(),obj.Name()); } //--- Update the properties of the obtained object //--- and check their change obj.PropertiesRefresh(); obj.PropertiesCheckChanged(); } //--- Handle chart changes for extended standard objects if(id==CHARTEVENT_CHART_CHANGE || idx==CHARTEVENT_CHART_CHANGE) { CArrayObj *list=this.GetListStdGraphObjectExt(); if(list!=NULL) { for(int i=0;i<list.Total();i++) { obj=list.At(i); if(obj==NULL) continue; obj.OnChartEvent(CHARTEVENT_CHART_CHANGE,lparam,dparam,sparam); } } } } //+------------------------------------------------------------------+
ここで、チャート変更イベントが検出された場合は、すべての拡張された標準グラフィカルオブジェクトのリストを取得します。番号によるループで、次のオブジェクトを取得し、そのイベントハンドラを呼び出して、「Chart changed」イベント値を渡します。
テスト
テストを実行するには、前の記事のEAを\MQL5\Experts\TestDoEasy\Part95\でTestDoEasyPart95.mq5として保存します。
唯一の変更点は、基本トレンドラインオブジェクトに付加された価格ラベルオブジェクトの名前がわずかに異なるです。EAイベントハンドラの複合グラフィカルオブジェクトを作成するブロック内の従属オブジェクト名は、名前が拡張グラフィカルオブジェクトタイプに対応するように「Ext」接辞を備えています。
if(id==CHARTEVENT_CLICK) { if(!IsCtrlKeyPressed()) return; //--- Get the chart click coordinates datetime time=0; double price=0; int sw=0; if(ChartXYToTimePrice(ChartID(),(int)lparam,(int)dparam,sw,time,price)) { //--- Get the right point coordinates for a trend line datetime time2=iTime(Symbol(),PERIOD_CURRENT,1); double price2=iOpen(Symbol(),PERIOD_CURRENT,1); //--- Create the "Trend line" object string name_base="TrendLineExt"; engine.CreateLineTrend(name_base,0,true,time,price,time2,price2); //--- Get the object from the list of graphical objects by chart name and ID and pass its properties to the journal CGStdGraphObj *obj=engine.GraphGetStdGraphObjectExt(name_base,ChartID()); //--- Create the "Left price label" object string name_dep="PriceLeftExt"; engine.CreatePriceLabelLeft(name_dep,0,false,time,price); //--- Get the object from the list of graphical objects by chart name and ID and CGStdGraphObj *dep=engine.GraphGetStdGraphObject(name_dep,ChartID()); //--- add it to the list of graphical objects bound to the "Trend line" object obj.AddDependentObj(dep); //--- Set its pivot point by X and Y axis to the trend line left point dep.AddNewLinkedCoord(GRAPH_OBJ_PROP_TIME,0,GRAPH_OBJ_PROP_PRICE,0); //--- Create the "Right price label" object name_dep="PriceRightExt"; engine.CreatePriceLabelRight(name_dep,0,false,time2,price2); //--- Get the object from the list of graphical objects by chart name and ID and dep=engine.GraphGetStdGraphObject(name_dep,ChartID()); //--- add it to the list of graphical objects bound to the "Trend line" object obj.AddDependentObj(dep); //--- Set its pivot point by X and Y axis to the trend line right point dep.AddNewLinkedCoord(GRAPH_OBJ_PROP_TIME,1,GRAPH_OBJ_PROP_PRICE,1); } } engine.GetGraphicObjCollection().OnChartEvent(id,lparam,dparam,sparam);
複合グラフィカルオブジェクトを作成しましょう。フォームオブジェクトは、作成中にそのピボットポイントに設定されます。
これらのフォームオブジェクトの座標は、画面の左上隅からピクセル単位で表示されます。したがって、チャートを移動する場合は、これらの画面座標を再計算して、オブジェクトが適切なグラフィカルオブジェクトのピボットポイントに設定されるようにする必要があります。これがここで確認しようとしていることです。
EAをコンパイルし、チャート上で起動します。
チャートが変わると、オブジェクトが指定された場所に移動することがわかります。ただし、これは遅れて発生します。
グラフィカルオブジェクトを削除すると、適切なフォームオブジェクトも削除されます。
遅れについて何ができるでしょうか?実際、動きをライブで確認する必要はありません。これらの動きは、チャートを移動するときに常に非表示のままになります(イベント応答を処理するために表示されるようになりました)。フォームオブジェクトをマウスでドラッグすると、グラフィカルオブジェクトの線自体が移動します。フォームとのやり取りはすべて、固定チャートで実行されます。特に、チャートは各ループの反復ではなくループの完了後にのみ更新されることを考慮すると、この結果は非常に十分です。負荷を軽減するために、変更を表示し、後でオブジェクトを表示するチャート変更の完了を制御できます(これは、カーソルがフォームオブジェクトのアクティブ領域に合わせられたときに表示される場合にのみ可能です)。
次の段階
次の記事では、複合グラフィカルオブジェクトイベントの作業を続けます。
*連載のこれまでの記事:
DoEasyライブラリのグラフィックス(第93部): 複合グラフィカルオブジェクトを作成するための機能の準備
DoEasyライブラリのグラフィックス(第94部): 複合グラフィカルオブジェクトの移動と削除
MetaQuotes Ltdによってロシア語から翻訳されました。
元の記事: https://www.mql5.com/ru/articles/10387
- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索