English Русский 中文 Español Deutsch Português
DoEasyライブラリのグラフィックス(第86部): グラフィカルオブジェクトコレクション - プロパティ変更の管理

DoEasyライブラリのグラフィックス(第86部): グラフィカルオブジェクトコレクション - プロパティ変更の管理

MetaTrader 5 | 19 11月 2021, 10:22
482 0
Artyom Trishkin
Artyom Trishkin

内容


概念

前の記事では、新しく作成された標準のグラフィカルオブジェクトをグラフィカルオブジェクトのコレクションクラスに格納する機能を実装しました。新しいチャートオブジェクトを検出し、チャート上のオブジェクトタイプに対応するクラスのオブジェクトを作成してコレクションリストに追加します。ただし、これはグラフィカルオブジェクトを本格的に管理するには不十分です。チャート上のグラフィカルオブジェクトのプロパティのすべての変更、削除、名前変更を管理する必要があります。

ObjectGetXXXシリーズの関数はグラフィカルオブジェクトのプロパティの読み取りに使用されますが、タイマー内の各グラフィカルオブジェクトの値を常に確認することはできません。これらの関数は同期されているためです。つまり、これらの関数はコマンドが実行されるのを待ちます。グラフィカルオブジェクトが多数ある場合、非常に多くのリソースを消費する可能性があります。

ここで私たちは選択に直面しています。タイマーを使用して各グラフィカルオブジェクトの各プロパティを調査し、プロパティリクエスト関数の同期性の結果をすべて調査するか、OnChartEvent()ハンドラで応答してイベントモデルを適用することです。後者は残念ながら、ストラテジーテスターでは機能しません(覚えていらっしゃるかもしれませんが、テスターのタイマー操作は、ライブラリでOnTick()およびOnCalculate()ハンドラを使用して処理されます。 )。

すべての長所と短所を比較検討した後、チャートイベントハンドラでグラフィカルオブジェクトプロパティの変更を追跡することにしました。つまり、コードを単純化するが、テスターでの作業に制限を課すイベントモデルを使用します。テスターでは、グラフィカルオブジェクトの処理はできません(少なくとも今のところ)。それらをテスターウィンドウに追加して、後でプロパティを変更することはできません。これは、イベントハンドラが機能する「ライブ」チャートでのみグラフィカルオブジェクトを処理する必要があることを意味します。

本稿では、現在のチャート(プログラムが起動されたチャート)のみのグラフィカルオブジェクトイベントを処理するテストバージョンを実装します。すべてが正しく機能することを確認したらすぐに、開いているチャートごとに本格的なイベントハンドラを開発します。ハンドラは、プログラムのメインチャートにイベントを送信し、ライブラリがイベントを収集して、グラフィカルオブジェクトコレクションで処理します。


グラフィカルオブジェクトプロパティの変更の追跡

OnChartEvent()ハンドラでは以下のイベントに興味があります。

  • CHARTEVENT_OBJECT_CREATE — グラフィカルオブジェクトを作成する(チャートの CHART_EVENT_OBJECT_CREATE=trueの場合)
  • CHARTEVENT_OBJECT_CHANGE — プロパティダイアログでオブジェクトのプロパティを変更する
  • CHARTEVENT_OBJECT_DELETE — グラフィカルオブジェクトを削除する(チャートの CHART_EVENT_OBJECT_DELETE=trueの場合)
  • CHARTEVENT_OBJECT_DRAG — グラフィカルオブジェクトをマウスでドラッグする

前の記事で、OnChartEvent()ハンドラを呼び出さずにグラフィカルオブジェクト作成イベントを準備しました。
オブジェクトプロパティの変更を手動で制御するには、ターミナルプロパティダイアログを介してグラフィカルオブジェクトプロパティを変更するイベントが必要です。
グラフィカルオブジェクト削除イベントはすでに存在します。ライブラリはすべてのターミナルチャート上のグラフィカルオブジェクトの数を追跡し、開いている各チャートのイベントフラグを持っています。チャートオブジェクトの数が減少した場合、チャートから削除されたオブジェクトの数を見つけて対処します。
グラフィカルオブジェクトの場所全体、特にその個々のアンカーポイントの変更を制御するには、グラフィカルオブジェクト移動イベントが必要です。

移動イベントは、オブジェクトが手動で作成されたときにもアクティブになります。チャートをクリックしてオブジェクトを設定し、マウスボタンがまだ離されていない瞬間、オブジェクトはすでに作成されており、ライブラリは適切なクラスオブジェクトを作成してコレクションに追加していることを確認できます。すべてのオブジェクトプロパティ値が正しく設定されているわけではありません。マウスボタンはまだ離されていません。オブジェクトが複数のポイントを使用している場合は、オブジェクトを移動したり、残りのアンカーポイントを設定したりできます。しかし、マウスボタンを離すと、すべてのオブジェクトアンカーポイントがすでに設定されていれば、グラフィカルなオブジェクト移動イベントが作成されます。イベントを追跡し、作成されたグラフィカルオブジェクトの完全に設定されたパラメーターに従って、作成済みのクラスオブジェクトのプロパティ値を変更することにより、新しく作成されたオブジェクトのすべてのプロパティの正しい値を設定します。

オブジェクト名の変更には、オブジェクトの削除、作成、プロパティ変更という3つのイベントが同時に発生します。これらの3つのイベントを追跡すれば、既存オブジェクトの名前の変更を検出できます。しかし、私はもっと単純なアプローチを使用することにします。オブジェクト名を変更すると、CHARTEVENT_OBJECT_CHANGEイベントが常に最後に処理されます。すべてのターミナルオブジェクトは名前とチャートIDで選択されているため、チャートに存在するオブジェクトのうち、コレクションリストに含まれなくなったオブジェクトを確認できます。次に、コレクションにクラスオブジェクトが存在しないチャート上のオブジェクト名を見つけ(1)、適切な名前のチャートオブジェクトを持たないオブジェクトを見つけ(2)、その名前を(1)コレクションリストで検出されたクラスオブジェクトに追加します。かなり複雑に見えるかもしれませんが、実際にはすべてが単純です。

どのオブジェクトプロパティが変更されたかを理解するために(イベントでは変更されたオブジェクトの名前はあるが変更されたプロパティが示されないため)、オブジェクトのプロパティを変更するイベントを受信する前に、すべてのオブジェクトプロパティの現在の値を以前に存在していた値と比較する必要があります したがって、「前の」オブジェクトプロパティを格納するために、さらに3つの配列を作成する必要があります。

抽象標準グラフィカルオブジェクトクラスのファイル\MQL5\Include\DoEasy\Objects\Graph\Standard\GStdGraphObj.mqhを開き、privateセクションに「以前の」オブジェクトプロパティを格納するための新しい配列を追加します

//+------------------------------------------------------------------+
//| The class of the abstract standard graphical object              |
//+------------------------------------------------------------------+
class CGStdGraphObj : public CGBaseObj
  {
private:
   long              m_long_prop[GRAPH_OBJ_PROP_INTEGER_TOTAL];         // Integer properties
   double            m_double_prop[GRAPH_OBJ_PROP_DOUBLE_TOTAL];        // Real properties
   string            m_string_prop[GRAPH_OBJ_PROP_STRING_TOTAL];        // String properties
   
   long              m_long_prop_prev[GRAPH_OBJ_PROP_INTEGER_TOTAL];    // Integer properties before change
   double            m_double_prop_prev[GRAPH_OBJ_PROP_DOUBLE_TOTAL];   // Real properties before change
   string            m_string_prop_prev[GRAPH_OBJ_PROP_STRING_TOTAL];   // String properties before change

//--- Return the index of the array the (1) double and (2) string properties are actually located at
   int               IndexProp(ENUM_GRAPH_OBJ_PROP_DOUBLE property)  const { return(int)property-GRAPH_OBJ_PROP_INTEGER_TOTAL;                              }
   int               IndexProp(ENUM_GRAPH_OBJ_PROP_STRING property)  const { return(int)property-GRAPH_OBJ_PROP_INTEGER_TOTAL-GRAPH_OBJ_PROP_DOUBLE_TOTAL;  }

public:

クラスのprivateセクションに、「前の」オブジェクトプロパティを設定するメソッドと返すメソッドを設定します。

public:
//--- Set object's (1) integer, (2) real and (3) string properties
   void              SetProperty(ENUM_GRAPH_OBJ_PROP_INTEGER property,long value)   { this.m_long_prop[property]=value;                         }
   void              SetProperty(ENUM_GRAPH_OBJ_PROP_DOUBLE property,double value)  { this.m_double_prop[this.IndexProp(property)]=value;       }
   void              SetProperty(ENUM_GRAPH_OBJ_PROP_STRING property,string value)  { this.m_string_prop[this.IndexProp(property)]=value;       }
//--- Return object’s (1) integer, (2) real and (3) string property from the properties array
   long              GetProperty(ENUM_GRAPH_OBJ_PROP_INTEGER property)        const { return this.m_long_prop[property];                        }
   double            GetProperty(ENUM_GRAPH_OBJ_PROP_DOUBLE property)         const { return this.m_double_prop[this.IndexProp(property)];      }
   string            GetProperty(ENUM_GRAPH_OBJ_PROP_STRING property)         const { return this.m_string_prop[this.IndexProp(property)];      }
   
//--- Set object's previous (1) integer, (2) real and (3) string properties
   void              SetPropertyPrev(ENUM_GRAPH_OBJ_PROP_INTEGER property,long value) { this.m_long_prop_prev[property]=value;                  }
   void              SetPropertyPrev(ENUM_GRAPH_OBJ_PROP_DOUBLE property,double value){ this.m_double_prop_prev[this.IndexProp(property)]=value;}
   void              SetPropertyPrev(ENUM_GRAPH_OBJ_PROP_STRING property,string value){ this.m_string_prop_prev[this.IndexProp(property)]=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)    const { return this.m_long_prop_prev[property];                   }
   double            GetPropertyPrev(ENUM_GRAPH_OBJ_PROP_DOUBLE property)     const { return this.m_double_prop_prev[this.IndexProp(property)]; }
   string            GetPropertyPrev(ENUM_GRAPH_OBJ_PROP_STRING property)     const { return this.m_string_prop_prev[this.IndexProp(property)]; }
   
//--- Return itself
   CGStdGraphObj    *GetObject(void)                                                { return &this;}


グラフィカルオブジェクトを記述するクラスオブジェクトの「オブジェクトID」プロパティがあります。このプロパティを使用すると、一意のオブジェクトラベルを設定して、識別できます。
このプロパティは、オブジェクトイベントの定義に関与してはいけません。したがって、オブジェクトIDを設定するメソッドでは、メソッドに渡された値を現在(開発済み)前のプロパティ(今追加予定)の両方のプロパティに同時に設定します。

public:
//+--------------------------------------------------------------------+ 
//|Methods of simplified access and setting graphical object properties|
//+--------------------------------------------------------------------+
//--- Object index in the list
   int               Number(void)                  const { return (int)this.GetProperty(GRAPH_OBJ_PROP_NUM);                              }
   void              SetNumber(const int number)         { this.SetProperty(GRAPH_OBJ_PROP_NUM,number);                                   }
//--- Object ID
   long              ObjectID(void)                const { return this.GetProperty(GRAPH_OBJ_PROP_ID);                                    }
   void              SetObjectID(const long obj_id)
                       {
                        CGBaseObj::SetObjectID(obj_id);
                        this.SetProperty(GRAPH_OBJ_PROP_ID,obj_id);
                        this.SetPropertyPrev(GRAPH_OBJ_PROP_ID,obj_id);
                       }
//--- Graphical object type


イベント応答アルゴリズムは次のとおりです。イベントを受信した後、グラフィカルオブジェクトを説明するクラスオブジェクトのすべてのデータを更新して、そのすべてのプロパティが関連する値を持つようにする必要があります。どのプロパティが変更されたかわからないため、すべてのオブジェクトプロパティを更新し、現在のプロパティを、オブジェクト変更イベントを受信する前にオブジェクトが持っていたプロパティと比較します。3つのループで、3つのオブジェクトプロパティ配列すべてを以前のプロパティの適切な配列と比較します。比較すると、現在のプロパティ値が前のプロパティ値と等しくない場合、プロパティの変更に関するメッセージが一時的に(これはこれまでのテストバージョンであるため)操作ログに送信されます。同じことが、各オブジェクトプロパティ配列で比較された値の検出された各差異にも当てはまります。後で、開いている各チャートのプロパティ変更の制御を実装するときに、別の方法を紹介します。変更されたすべてのプロパティは、イベントオブジェクトに送信されます。このような各オブジェクトはコレクションクラスで受信され、開いている各チャート上の各オブジェクトのプロパティの変更が通知されます。

クラスのpublicセクションで、すべてのオブジェクトプロパティを上書きするメソッドを宣言します。グラフィカルオブジェクトのすべてのプロパティを一度に調べて、それらをクラスオブジェクトのプロパティに入力するには、プロパティ変更イベントを検出する必要があります。
オブジェクトプロパティの変更をチェックするメソッドは、現在のすべてのオブジェクトプロパティを以前の状態と比較します。

クラスの privateセクションで、グラフィカルオブジェクトからすべてのプロパティを受け取り、それらをクラスオブジェクトのプロパティに設定するための3つのメソッドを宣言します
現在のプロパティを前のプロパティにコピーするメソッドでは、イベントが検出された次のチェック中に、プロパティを変更されたプロパティと比較できます。

//--- Return the description of the object visibility on timeframes
   string            VisibleOnTimeframeDescription(void);

//--- Re-write all graphical object properties
   void              PropertiesRefresh(void);
//--- Check object property changes
   void              PropertiesCheckChanged(void);
   
private:
//--- Get and save (1) integer, (2) real and (3) string properties
   void              GetAndSaveINT(void);
   void              GetAndSaveDBL(void);
   void              GetAndSaveSTR(void);
//--- Copy the current data to the previous one
   void              PropertiesCopyToPrevData(void);
   
  };
//+------------------------------------------------------------------+

protectedパラメトリックコンストラクタを単純化します。以前は、オブジェクトからすべてのグラフィカルオブジェクトに固有のすべてのプロパティを受け取り、それらをクラスオブジェクトに設定していました

//+------------------------------------------------------------------+
//| Protected parametric constructor                                 |
//+------------------------------------------------------------------+
CGStdGraphObj::CGStdGraphObj(const ENUM_OBJECT_DE_TYPE obj_type,
                             const ENUM_GRAPH_OBJ_BELONG belong,
                             const ENUM_GRAPH_OBJ_GROUP group,
                             const long chart_id,const string name)
  {
//--- 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;
   CGBaseObj::SetChartID(chart_id);
   CGBaseObj::SetTypeGraphObject(CGBaseObj::GraphObjectType(obj_type));
   CGBaseObj::SetTypeElement(GRAPH_ELEMENT_TYPE_STANDARD);
   CGBaseObj::SetBelong(belong);
   CGBaseObj::SetGroup(group);
   CGBaseObj::SetSubwindow(chart_id,name);
   CGBaseObj::SetDigits((int)::SymbolInfoInteger(::ChartSymbol(chart_id),SYMBOL_DIGITS));
   
//--- Save integer properties
   //--- properties inherent in all graphical objects but not present in a graphical object
   this.m_long_prop[GRAPH_OBJ_PROP_CHART_ID]    = CGBaseObj::ChartID();          // Chart ID
   this.m_long_prop[GRAPH_OBJ_PROP_WND_NUM]     = CGBaseObj::SubWindow();        // Chart subwindow index
   this.m_long_prop[GRAPH_OBJ_PROP_TYPE]        = CGBaseObj::TypeGraphObject();  // Graphical object type (ENUM_OBJECT)
   this.m_long_prop[GRAPH_OBJ_PROP_ELEMENT_TYPE]= CGBaseObj::TypeGraphElement(); // Graphical element type (ENUM_GRAPH_ELEMENT_TYPE)
   this.m_long_prop[GRAPH_OBJ_PROP_BELONG]      = CGBaseObj::Belong();           // Graphical object affiliation
   this.m_long_prop[GRAPH_OBJ_PROP_GROUP]       = CGBaseObj::Group();            // Graphical object group
   this.m_long_prop[GRAPH_OBJ_PROP_ID]          = 0;                             // Object ID
   this.m_long_prop[GRAPH_OBJ_PROP_NUM]         = 0;                             // Object index in the list
   //--- Properties inherent in all graphical objects and present in a graphical object
   this.m_long_prop[GRAPH_OBJ_PROP_CREATETIME]  = ::ObjectGetInteger(chart_id,name,OBJPROP_CREATETIME);  // Object creation time
   this.m_long_prop[GRAPH_OBJ_PROP_TIMEFRAMES]  = ::ObjectGetInteger(chart_id,name,OBJPROP_TIMEFRAMES);  // Object visibility on timeframes
   this.m_long_prop[GRAPH_OBJ_PROP_BACK]        = ::ObjectGetInteger(chart_id,name,OBJPROP_BACK);        // Background object
   this.m_long_prop[GRAPH_OBJ_PROP_ZORDER]      = ::ObjectGetInteger(chart_id,name,OBJPROP_ZORDER);      // Priority of a graphical object for receiving the event of clicking on a chart
   this.m_long_prop[GRAPH_OBJ_PROP_HIDDEN]      = ::ObjectGetInteger(chart_id,name,OBJPROP_HIDDEN);      // Disable displaying the name of a graphical object in the terminal object list
   this.m_long_prop[GRAPH_OBJ_PROP_SELECTED]    = ::ObjectGetInteger(chart_id,name,OBJPROP_SELECTED);    // Object selection
   this.m_long_prop[GRAPH_OBJ_PROP_SELECTABLE]  = ::ObjectGetInteger(chart_id,name,OBJPROP_SELECTABLE);  // Object availability
   this.m_long_prop[GRAPH_OBJ_PROP_TIME]        = ::ObjectGetInteger(chart_id,name,OBJPROP_TIME);        // First point time coordinate
   this.m_long_prop[GRAPH_OBJ_PROP_COLOR]       = ::ObjectGetInteger(chart_id,name,OBJPROP_COLOR);       // Color
   this.m_long_prop[GRAPH_OBJ_PROP_STYLE]       = ::ObjectGetInteger(chart_id,name,OBJPROP_STYLE);       // Style
   this.m_long_prop[GRAPH_OBJ_PROP_WIDTH]       = ::ObjectGetInteger(chart_id,name,OBJPROP_WIDTH);       // Line width
   //--- Properties belonging to different graphical objects
   this.m_long_prop[GRAPH_OBJ_PROP_FILL]                          = 0;  // Object color filling
   this.m_long_prop[GRAPH_OBJ_PROP_READONLY]                      = 0;  // Ability to edit text in the Edit object
   this.m_long_prop[GRAPH_OBJ_PROP_LEVELS]                        = 0;  // Number of levels
   this.m_long_prop[GRAPH_OBJ_PROP_LEVELCOLOR]                    = 0;  // Level line color
   this.m_long_prop[GRAPH_OBJ_PROP_LEVELSTYLE]                    = 0;  // Level line style
   this.m_long_prop[GRAPH_OBJ_PROP_LEVELWIDTH]                    = 0;  // Level line width
   this.m_long_prop[GRAPH_OBJ_PROP_ALIGN]                         = 0;  // Horizontal text alignment in the Edit object (OBJ_EDIT)
   this.m_long_prop[GRAPH_OBJ_PROP_FONTSIZE]                      = 0;  // Font size
   this.m_long_prop[GRAPH_OBJ_PROP_RAY_LEFT]                      = 0;  // Ray goes to the left
   this.m_long_prop[GRAPH_OBJ_PROP_RAY_RIGHT]                     = 0;  // Ray goes to the right
   this.m_long_prop[GRAPH_OBJ_PROP_RAY]                           = 0;  // Vertical line goes through all windows of a chart
   this.m_long_prop[GRAPH_OBJ_PROP_ELLIPSE]                       = 0;  // Display the full ellipse of the Fibonacci Arc object
   this.m_long_prop[GRAPH_OBJ_PROP_ARROWCODE]                     = 0;  // Arrow code for the "Arrow" object
   this.m_long_prop[GRAPH_OBJ_PROP_ANCHOR]                        = 0;  // Position of the binding point of the graphical object
   this.m_long_prop[GRAPH_OBJ_PROP_XDISTANCE]                     = 0;  // Distance from the base corner along the X axis in pixels
   this.m_long_prop[GRAPH_OBJ_PROP_YDISTANCE]                     = 0;  // Distance from the base corner along the Y axis in pixels
   this.m_long_prop[GRAPH_OBJ_PROP_DIRECTION]                     = 0;  // Gann object trend
   this.m_long_prop[GRAPH_OBJ_PROP_DEGREE]                        = 0;  // Elliott wave marking level
   this.m_long_prop[GRAPH_OBJ_PROP_DRAWLINES]                     = 0;  // Display lines for Elliott wave marking
   this.m_long_prop[GRAPH_OBJ_PROP_STATE]                         = 0;  // Button state (pressed/released)
   this.m_long_prop[GRAPH_OBJ_PROP_CHART_OBJ_CHART_ID]            = 0;  // Chart object ID (OBJ_CHART).
   this.m_long_prop[GRAPH_OBJ_PROP_CHART_OBJ_PERIOD]              = 0;  // Chart object period<
   this.m_long_prop[GRAPH_OBJ_PROP_CHART_OBJ_DATE_SCALE]          = 0;  // Time scale display flag for the Chart object
   this.m_long_prop[GRAPH_OBJ_PROP_CHART_OBJ_PRICE_SCALE]         = 0;  // Price scale display flag for the Chart object
   this.m_long_prop[GRAPH_OBJ_PROP_CHART_OBJ_CHART_SCALE]         = 0;  // Chart object scale
   this.m_long_prop[GRAPH_OBJ_PROP_XSIZE]                         = 0;  // Object width along the X axis in pixels.
   this.m_long_prop[GRAPH_OBJ_PROP_YSIZE]                         = 0;  // Object height along the Y axis in pixels.
   this.m_long_prop[GRAPH_OBJ_PROP_XOFFSET]                       = 0;  // X coordinate of the upper-left corner of the visibility area.
   this.m_long_prop[GRAPH_OBJ_PROP_YOFFSET]                       = 0;  // Y coordinate of the upper-left corner of the visibility area.
   this.m_long_prop[GRAPH_OBJ_PROP_BGCOLOR]                       = 0;  // Background color for OBJ_EDIT, OBJ_BUTTON, OBJ_RECTANGLE_LABEL
   this.m_long_prop[GRAPH_OBJ_PROP_CORNER]                        = 0;  // Chart corner for binding a graphical object
   this.m_long_prop[GRAPH_OBJ_PROP_BORDER_TYPE]                   = 0;  // Border type for "Rectangle border"
   this.m_long_prop[GRAPH_OBJ_PROP_BORDER_COLOR]                  = 0;  // Border color for OBJ_EDIT and OBJ_BUTTON
   
//--- Save real properties
   this.m_double_prop[this.IndexProp(GRAPH_OBJ_PROP_PRICE)]       = ::ObjectGetDouble(chart_id,name,OBJPROP_PRICE);  // Price coordinate
   this.m_double_prop[this.IndexProp(GRAPH_OBJ_PROP_LEVELVALUE)]  = 0;                                               // Level value
   this.m_double_prop[this.IndexProp(GRAPH_OBJ_PROP_SCALE)]       = 0;                                               // Scale (property of Gann objects and Fibonacci Arcs objects)
   this.m_double_prop[this.IndexProp(GRAPH_OBJ_PROP_ANGLE)]       = 0;                                               // Angle
   this.m_double_prop[this.IndexProp(GRAPH_OBJ_PROP_DEVIATION)]   = 0;                                               // Deviation of the standard deviation channel
   
//--- Save string properties
   this.m_string_prop[this.IndexProp(GRAPH_OBJ_PROP_NAME)]        = name;                                            // Object name
   this.m_string_prop[this.IndexProp(GRAPH_OBJ_PROP_TEXT)]        = ::ObjectGetString(chart_id,name,OBJPROP_TEXT);   // Object description (the text contained in the object)
   this.m_string_prop[this.IndexProp(GRAPH_OBJ_PROP_TOOLTIP)]     = ::ObjectGetString(chart_id,name,OBJPROP_TOOLTIP);// Tooltip text
   this.m_string_prop[this.IndexProp(GRAPH_OBJ_PROP_LEVELTEXT)]   = "";                                              // Level description
   this.m_string_prop[this.IndexProp(GRAPH_OBJ_PROP_FONT)]        = "";                                              // Font
   this.m_string_prop[this.IndexProp(GRAPH_OBJ_PROP_BMPFILE)]     = "";                                              // BMP file name for the "Bitmap Level" object
   this.m_string_prop[this.IndexProp(GRAPH_OBJ_PROP_CHART_OBJ_SYMBOL)]= "";                                          // Chart object symbol 
   
//--- Save basic properties in the parent object
   this.m_create_time=(datetime)this.GetProperty(GRAPH_OBJ_PROP_CREATETIME);
   this.m_back=(bool)this.GetProperty(GRAPH_OBJ_PROP_BACK);
   this.m_selected=(bool)this.GetProperty(GRAPH_OBJ_PROP_SELECTED);
   this.m_selectable=(bool)this.GetProperty(GRAPH_OBJ_PROP_SELECTABLE);
   this.m_hidden=(bool)this.GetProperty(GRAPH_OBJ_PROP_HIDDEN);
   this.m_name=this.GetProperty(GRAPH_OBJ_PROP_NAME);

  }
//+-------------------------------------------------------------------+

これらすべての文字列は、PropertiesRefresh()メソッドから一度に呼び出される個別のメソッドに移動されます。
したがって、これらの文字列を削除しましょう。コンストラクタは次のようになります。

//+------------------------------------------------------------------+
//| Protected parametric constructor                                 |
//+------------------------------------------------------------------+
CGStdGraphObj::CGStdGraphObj(const ENUM_OBJECT_DE_TYPE obj_type,
                             const ENUM_GRAPH_OBJ_BELONG belong,
                             const ENUM_GRAPH_OBJ_GROUP group,
                             const long chart_id,const string name)
  {
//--- 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(GRAPH_ELEMENT_TYPE_STANDARD);
   CGBaseObj::SetBelong(belong);
   CGBaseObj::SetGroup(group);
   CGBaseObj::SetSubwindow(chart_id,name);
   CGBaseObj::SetDigits((int)::SymbolInfoInteger(::ChartSymbol(chart_id),SYMBOL_DIGITS));
   
//--- Save integer properties
   //--- properties inherent in all graphical objects but not present in a graphical object
   this.m_long_prop[GRAPH_OBJ_PROP_CHART_ID]    = CGBaseObj::ChartID();          // Chart ID
   this.m_long_prop[GRAPH_OBJ_PROP_WND_NUM]     = CGBaseObj::SubWindow();        // Chart subwindow index
   this.m_long_prop[GRAPH_OBJ_PROP_TYPE]        = CGBaseObj::TypeGraphObject();  // Graphical object type (ENUM_OBJECT)
   this.m_long_prop[GRAPH_OBJ_PROP_ELEMENT_TYPE]= CGBaseObj::TypeGraphElement(); // Graphical element type (ENUM_GRAPH_ELEMENT_TYPE)
   this.m_long_prop[GRAPH_OBJ_PROP_BELONG]      = CGBaseObj::Belong();           // Graphical object affiliation
   this.m_long_prop[GRAPH_OBJ_PROP_GROUP]       = CGBaseObj::Group();            // Graphical object group
   this.m_long_prop[GRAPH_OBJ_PROP_ID]          = 0;                             // Object ID
   this.m_long_prop[GRAPH_OBJ_PROP_NUM]         = 0;                             // Object index in the list

//--- 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);
   this.m_back=(bool)this.GetProperty(GRAPH_OBJ_PROP_BACK);
   this.m_selected=(bool)this.GetProperty(GRAPH_OBJ_PROP_SELECTED);
   this.m_selectable=(bool)this.GetProperty(GRAPH_OBJ_PROP_SELECTABLE);
   this.m_hidden=(bool)this.GetProperty(GRAPH_OBJ_PROP_HIDDEN);

//--- Save the current properties to the previous ones
   this.PropertiesCopyToPrevData();
   
  }
//+-------------------------------------------------------------------+

PropertiesRefresh()メソッドが正しく機能するためには、データを取得する必要のあるグラフィカルオブジェクトの名前を知っている必要があります。以前は、名前は文字列パラメータを読み取るためのブロックのほぼ最後に書かれていました。これで、コンストラクタに入るとすぐに、グラフィカルオブジェクト名が読み取られ、クラスオブジェクトプロパティに設定されます。すべてのプロパティをクラスオブジェクトに追加した後、PropertiesCopyToPrevData()メソッドを呼び出します。このメソッドは、保存されているすべてのオブジェクトプロパティを「前の」プロパティの配列にすでに設定しており 、変更を制御します。

以下は、グラフィカルオブジェクトから整数実数文字列プロパティを受け取ってクラスオブジェクトに保存するメソッドです。

//+------------------------------------------------------------------+
//| Get and save the integer properties                              |
//+------------------------------------------------------------------+
void CGStdGraphObj::GetAndSaveINT(void)
  {
   //--- Properties inherent in all graphical objects and present in a graphical object
   this.m_long_prop[GRAPH_OBJ_PROP_CREATETIME]  = ::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_CREATETIME); // Object creation time
   this.m_long_prop[GRAPH_OBJ_PROP_TIMEFRAMES]  = ::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_TIMEFRAMES); // Object visibility on timeframes
   this.m_long_prop[GRAPH_OBJ_PROP_BACK]        = ::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_BACK);       // Background object
   this.m_long_prop[GRAPH_OBJ_PROP_ZORDER]      = ::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_ZORDER);     // Priority of a graphical object for receiving the event of clicking on a chart
   this.m_long_prop[GRAPH_OBJ_PROP_HIDDEN]      = ::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_HIDDEN);     // Disable displaying the name of a graphical object in the terminal object list
   this.m_long_prop[GRAPH_OBJ_PROP_SELECTED]    = ::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_SELECTED);   // Object selection
   this.m_long_prop[GRAPH_OBJ_PROP_SELECTABLE]  = ::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_SELECTABLE); // Object availability
   this.m_long_prop[GRAPH_OBJ_PROP_TIME]        = ::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_TIME);       // First point time coordinate
   this.m_long_prop[GRAPH_OBJ_PROP_COLOR]       = ::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_COLOR);      // Color
   this.m_long_prop[GRAPH_OBJ_PROP_STYLE]       = ::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_STYLE);      // Style
   this.m_long_prop[GRAPH_OBJ_PROP_WIDTH]       = ::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_WIDTH);      // Line width
   //--- Properties belonging to different graphical objects
   this.m_long_prop[GRAPH_OBJ_PROP_FILL]        = ::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_FILL);       // Fill an object with color
   this.m_long_prop[GRAPH_OBJ_PROP_READONLY]    = ::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_READONLY);   // Ability to edit text in the Edit object
   this.m_long_prop[GRAPH_OBJ_PROP_LEVELS]      = ::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_LEVELS);     // Number of levels
   this.m_long_prop[GRAPH_OBJ_PROP_LEVELCOLOR]  = ::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_LEVELCOLOR); // Level line color
   this.m_long_prop[GRAPH_OBJ_PROP_LEVELSTYLE]  = ::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_LEVELSTYLE); // Level line style
   this.m_long_prop[GRAPH_OBJ_PROP_LEVELWIDTH]  = ::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_LEVELWIDTH); // Level line width
   this.m_long_prop[GRAPH_OBJ_PROP_ALIGN]       = ::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_ALIGN);      // Horizontal text alignment in the Edit object (OBJ_EDIT)
   this.m_long_prop[GRAPH_OBJ_PROP_FONTSIZE]    = ::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_FONTSIZE);   // Font size
   this.m_long_prop[GRAPH_OBJ_PROP_RAY_LEFT]    = ::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_RAY_LEFT);   // Ray goes to the left
   this.m_long_prop[GRAPH_OBJ_PROP_RAY_RIGHT]   = ::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_RAY_RIGHT);  // Ray goes to the right
   this.m_long_prop[GRAPH_OBJ_PROP_RAY]         = ::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_RAY);        // Vertical line goes through all windows of a chart
   this.m_long_prop[GRAPH_OBJ_PROP_ELLIPSE]     = ::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_ELLIPSE);    // Display the full ellipse of the Fibonacci Arc object
   this.m_long_prop[GRAPH_OBJ_PROP_ARROWCODE]   = ::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_ARROWCODE);  // Arrow code for the "Arrow" object
   this.m_long_prop[GRAPH_OBJ_PROP_ANCHOR]      = ::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_ANCHOR);     // Position of the binding point of the graphical object
   this.m_long_prop[GRAPH_OBJ_PROP_XDISTANCE]   = ::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_XDISTANCE);  // Distance from the base corner along the X axis in pixels
   this.m_long_prop[GRAPH_OBJ_PROP_YDISTANCE]   = ::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_YDISTANCE);  // Distance from the base corner along the Y axis in pixels
   this.m_long_prop[GRAPH_OBJ_PROP_DIRECTION]   = ::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_DIRECTION);  // Gann object trend
   this.m_long_prop[GRAPH_OBJ_PROP_DEGREE]      = ::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_DEGREE);     // Elliott wave marking level
   this.m_long_prop[GRAPH_OBJ_PROP_DRAWLINES]   = ::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_DRAWLINES);  // Display lines for Elliott wave marking
   this.m_long_prop[GRAPH_OBJ_PROP_STATE]       = ::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_STATE);      // Button state (pressed/released)
   this.m_long_prop[GRAPH_OBJ_PROP_CHART_OBJ_CHART_ID]   = ::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_CHART_ID);   // Chart object ID (OBJ_CHART).
   this.m_long_prop[GRAPH_OBJ_PROP_CHART_OBJ_PERIOD]     = ::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_PERIOD);     // Chart object period
   this.m_long_prop[GRAPH_OBJ_PROP_CHART_OBJ_DATE_SCALE] = ::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_DATE_SCALE); // Time scale display flag for the Chart object
   this.m_long_prop[GRAPH_OBJ_PROP_CHART_OBJ_PRICE_SCALE]= ::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_PRICE_SCALE);// Price scale display flag for the Chart object
   this.m_long_prop[GRAPH_OBJ_PROP_CHART_OBJ_CHART_SCALE]= ::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_CHART_SCALE);// Chart object scale
   this.m_long_prop[GRAPH_OBJ_PROP_XSIZE]       = ::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_XSIZE);      // Object width along the X axis in pixels.
   this.m_long_prop[GRAPH_OBJ_PROP_YSIZE]       = ::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_YSIZE);      // Object height along the Y axis in pixels.
   this.m_long_prop[GRAPH_OBJ_PROP_XOFFSET]     = ::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_XOFFSET);    // X coordinate of the upper-left corner of the visibility area.
   this.m_long_prop[GRAPH_OBJ_PROP_YOFFSET]     = ::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_YOFFSET);    // Y coordinate of the upper-left corner of the visibility area.
   this.m_long_prop[GRAPH_OBJ_PROP_BGCOLOR]     = ::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_BGCOLOR);    // Background color for OBJ_EDIT, OBJ_BUTTON, OBJ_RECTANGLE_LABEL
   this.m_long_prop[GRAPH_OBJ_PROP_CORNER]      = ::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_CORNER);     // Chart corner for binding a graphical object
   this.m_long_prop[GRAPH_OBJ_PROP_BORDER_TYPE] = ::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_BORDER_TYPE);// Border type for "Rectangle border"
   this.m_long_prop[GRAPH_OBJ_PROP_BORDER_COLOR]= ::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_BORDER_COLOR);// Border color for OBJ_EDIT and OBJ_BUTTON
  }
//+------------------------------------------------------------------+
//| Get and save the real properties                                 |
//+------------------------------------------------------------------+
void CGStdGraphObj::GetAndSaveDBL(void)
  {
   this.m_double_prop[this.IndexProp(GRAPH_OBJ_PROP_PRICE)]       = ::ObjectGetDouble(this.ChartID(),this.Name(),OBJPROP_PRICE);       // Price coordinate
   this.m_double_prop[this.IndexProp(GRAPH_OBJ_PROP_LEVELVALUE)]  = ::ObjectGetDouble(this.ChartID(),this.Name(),OBJPROP_LEVELVALUE);  // Level value
   this.m_double_prop[this.IndexProp(GRAPH_OBJ_PROP_SCALE)]       = ::ObjectGetDouble(this.ChartID(),this.Name(),OBJPROP_SCALE);       // Scale (property of Gann objects and Fibonacci Arcs objects)
   this.m_double_prop[this.IndexProp(GRAPH_OBJ_PROP_ANGLE)]       = ::ObjectGetDouble(this.ChartID(),this.Name(),OBJPROP_ANGLE);       // Corner
   this.m_double_prop[this.IndexProp(GRAPH_OBJ_PROP_DEVIATION)]   = ::ObjectGetDouble(this.ChartID(),this.Name(),OBJPROP_DEVIATION);   // Deviation of the standard deviation channel
  }
//+------------------------------------------------------------------+
//| Get and save the string properties                               |
//+------------------------------------------------------------------+
void CGStdGraphObj::GetAndSaveSTR(void)
  {
   this.m_string_prop[this.IndexProp(GRAPH_OBJ_PROP_TEXT)]              = ::ObjectGetString(this.ChartID(),this.Name(),OBJPROP_TEXT);     // Object description (the text contained in the object)
   this.m_string_prop[this.IndexProp(GRAPH_OBJ_PROP_TOOLTIP)]           = ::ObjectGetString(this.ChartID(),this.Name(),OBJPROP_TOOLTIP);  // Tooltip text
   this.m_string_prop[this.IndexProp(GRAPH_OBJ_PROP_LEVELTEXT)]         = ::ObjectGetString(this.ChartID(),this.Name(),OBJPROP_LEVELTEXT);// Level description
   this.m_string_prop[this.IndexProp(GRAPH_OBJ_PROP_FONT)]              = ::ObjectGetString(this.ChartID(),this.Name(),OBJPROP_FONT);     // Font
   this.m_string_prop[this.IndexProp(GRAPH_OBJ_PROP_BMPFILE)]           = ::ObjectGetString(this.ChartID(),this.Name(),OBJPROP_BMPFILE);  // BMP file name for the "Bitmap Level" object
   this.m_string_prop[this.IndexProp(GRAPH_OBJ_PROP_CHART_OBJ_SYMBOL)]  = ::ObjectGetString(this.ChartID(),this.Name(),OBJPROP_SYMBOL);   // Chart object symbol 
  }
//+------------------------------------------------------------------+

すべてのグラフィカルオブジェクトプロパティを上書きするメソッドに置き換えられたクラスコンストラクタ文字列は、次の3つのメソッドに移動されました。

//+------------------------------------------------------------------+
//| Overwrite all graphical object properties                        |
//+------------------------------------------------------------------+
void CGStdGraphObj::PropertiesRefresh(void)
  {
   this.GetAndSaveINT();
   this.GetAndSaveDBL();
   this.GetAndSaveSTR();
  }
//+------------------------------------------------------------------+

上記で検討した3つのメソッドはすべて、ここでは1つずつ呼び出されます。

以下は、現在のクラスオブジェクトのプロパティを前のプロパティにコピーするメソッドです。

//+------------------------------------------------------------------+
//| Copy the current data to the previous one                        |
//+------------------------------------------------------------------+
void CGStdGraphObj::PropertiesCopyToPrevData(void)
  {
   ::ArrayCopy(this.m_long_prop_prev,this.m_long_prop);
   ::ArrayCopy(this.m_double_prop_prev,this.m_double_prop);
   ::ArrayCopy(this.m_string_prop_prev,this.m_string_prop);
  }
//+------------------------------------------------------------------+

ここでは、配列コピー関数を使用して、整数、実数、文字列のプロパティの配列を前のプロパティの適切な配列に1つずつコピーします。

以下は、オブジェクトプロパティの変更を確認するメソッドです。

//+------------------------------------------------------------------+
//| Check object property changes                                    |
//+------------------------------------------------------------------+
void CGStdGraphObj::PropertiesCheckChanged(void)
  {
   bool changed=false;
   int beg=0, end=GRAPH_OBJ_PROP_INTEGER_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_GRAPH_OBJ_PROP_INTEGER prop=(ENUM_GRAPH_OBJ_PROP_INTEGER)i;
      if(!this.SupportProperty(prop)) continue;
      if(this.GetProperty(prop)!=this.GetPropertyPrev(prop))
        {
         changed=true;
         ::Print(DFUN,this.Name(),": ",TextByLanguage(" Изменённое свойство: "," Modified property: "),GetPropertyDescription(prop));
        }
     }

   beg=end; end+=GRAPH_OBJ_PROP_DOUBLE_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_GRAPH_OBJ_PROP_DOUBLE prop=(ENUM_GRAPH_OBJ_PROP_DOUBLE)i;
      if(!this.SupportProperty(prop)) continue;
      if(this.GetProperty(prop)!=this.GetPropertyPrev(prop))
        {
         changed=true;
         ::Print(DFUN,this.Name(),": ",TextByLanguage(" Изменённое свойство: "," Modified property: "),GetPropertyDescription(prop));
        }
     }

   beg=end; end+=GRAPH_OBJ_PROP_STRING_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_GRAPH_OBJ_PROP_STRING prop=(ENUM_GRAPH_OBJ_PROP_STRING)i;
      if(!this.SupportProperty(prop)) continue;
      if(this.GetProperty(prop)!=this.GetPropertyPrev(prop))
        {
         changed=true;
         ::Print(DFUN,this.Name(),": ",TextByLanguage(" Изменённое свойство: "," Modified property: "),GetPropertyDescription(prop));
        }
     }
   if(changed)
      PropertiesCopyToPrevData();
  }
//+------------------------------------------------------------------+

ここでは、3つのループ(整数、実数、文字列のプロパティを個別に)で、適切な配列から次のプロパティを取得し、前のプロパティの配列の同じプロパティと比較します。現在のプロパティと以前のプロパティの比較値が等しくない場合、プロパティは変更されています。オブジェクトプロパティの変更フラグを設定し、操作ログにメッセージを表示します

これは、オブジェクトプロパティの変更を検索するという概念を確認することのみを目的としたテストメソッドです。今後の記事では、概念を改善して完全に機能するようにします。開いているすべてのチャートのすべてのグラフィカルオブジェクトの変更を追跡し、オブジェクトプロパティの変更イベントを制御プログラムのグラフに送信して、ライブラリでさらに処理できるようにします。


グラフィカルオブジェクト削除の追跡

グラフィカルオブジェクトプロパティはオブジェクトに属しているため、グラフィカルオブジェクトプロパティの変更はオブジェクトクラスで追跡されます。それらはオブジェクトクラスに設定され、そこで確認できます。ただし、上記で作成されたオブジェクトプロパティの変更と変更の検証のすべてのメソッドは、グラフィカルオブジェクトコレクションクラスから呼び出されます。対照的に、チャートへのグラフィカルオブジェクトの追加と削除は、グラフィカルオブジェクトコレクションクラスでのみ追跡できます。このクラスは、開いているすべてのチャート上のすべてのオブジェクトの完全なリストを管理し、独自のコレクションリストでそれらを追跡します。

グラフィカルオブジェクトコレクションクラスのファイル(\MQL5\Include\DoEasy\Collections\GraphElementsCollection.mqh)を開き、必要なすべての改善を行います。

同じファイルにあるチャートオブジェクト管理クラスで、マウスとグラフィカルオブジェクトのイベントを追跡する権限を設定するプライベートメソッドを宣言します。これにより、開いているグラフごとにそのようなイベントを追跡するための権限を設定できます。
クラスコンストラクタで、これらのメソッドを呼び出して、コントロールオブジェクトが作成されるチャートの権限を設定します。クラスの最後で、イベントハンドラを宣言します(次の記事で実装します)。

//+------------------------------------------------------------------+
//| Chart object management class                                    |
//+------------------------------------------------------------------+
class CChartObjectsControl : public CObject
  {
private:
   CArrayObj         m_list_new_graph_obj;      // List of added graphical objects
   ENUM_TIMEFRAMES   m_chart_timeframe;         // Chart timeframe
   long              m_chart_id;                // Chart ID
   string            m_chart_symbol;            // Chart symbol
   bool              m_is_graph_obj_event;      // Event flag in the list of graphical objects
   int               m_total_objects;           // Number of graphical objects
   int               m_last_objects;            // Number of graphical objects during the previous check
   int               m_delta_graph_obj;         // Difference in the number of graphical objects compared to the previous check
   
//--- Return the name of the last graphical object added to the chart
   string            LastAddedGraphObjName(void);
//--- Set the permission to track mouse events and graphical objects
   void              SetMouseEvent(void);
   
public:
//--- Return the variable values
   ENUM_TIMEFRAMES   Timeframe(void)                           const { return this.m_chart_timeframe;    }
   long              ChartID(void)                             const { return this.m_chart_id;           }
   string            Symbol(void)                              const { return this.m_chart_symbol;       }
   bool              IsEvent(void)                             const { return this.m_is_graph_obj_event; }
   int               TotalObjects(void)                        const { return this.m_total_objects;      }
   int               Delta(void)                               const { return this.m_delta_graph_obj;    }
//--- Create a new standard graphical object
   CGStdGraphObj    *CreateNewGraphObj(const ENUM_OBJECT obj_type,const long chart_id, const string name);
//--- Return the list of newly added objects
   CArrayObj        *GetListNewAddedObj(void)                        { return &this.m_list_new_graph_obj;}
//--- Check the chart objects
   void              Refresh(void);
//--- Constructors
                     CChartObjectsControl(void)
                       { 
                        this.m_list_new_graph_obj.Clear();
                        this.m_list_new_graph_obj.Sort();
                        this.m_chart_id=::ChartID();
                        this.m_chart_timeframe=(ENUM_TIMEFRAMES)::ChartPeriod(this.m_chart_id);
                        this.m_chart_symbol=::ChartSymbol(this.m_chart_id);
                        this.m_is_graph_obj_event=false;
                        this.m_total_objects=0;
                        this.m_last_objects=0;
                        this.m_delta_graph_obj=0;
                        this.SetMouseEvent();
                       }
                     CChartObjectsControl(const long chart_id)
                       { 
                        this.m_list_new_graph_obj.Clear();
                        this.m_list_new_graph_obj.Sort();
                        this.m_chart_id=chart_id;
                        this.m_chart_timeframe=(ENUM_TIMEFRAMES)::ChartPeriod(this.m_chart_id);
                        this.m_chart_symbol=::ChartSymbol(this.m_chart_id);
                        this.m_is_graph_obj_event=false;
                        this.m_total_objects=0;
                        this.m_last_objects=0;
                        this.m_delta_graph_obj=0;
                        this.SetMouseEvent();
                       }
                     
//--- Compare CChartObjectsControl objects by a chart ID (for sorting the list by an object property)
   virtual int       Compare(const CObject *node,const int mode=0) const
                       {
                        const CChartObjectsControl *obj_compared=node;
                        return(this.ChartID()>obj_compared.ChartID() ? 1 : this.ChartID()<obj_compared.ChartID() ? -1 : 0);
                       }

//--- Event handler
   void              OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam);

  };
//+------------------------------------------------------------------+

クラス本体の外で、マウスおよびグラフィックオブジェクトのイベントを追跡するためのアクセス許可を設定するメソッドを実装します。

//+------------------------------------------------------------------+
//| Set the permission                                               |
//| to track mouse and graphical object events for the chart         |
//+------------------------------------------------------------------+
void CChartObjectsControl::SetMouseEvent(void)
  {
   ::ChartSetInteger(this.ChartID(),CHART_EVENT_MOUSE_MOVE,true);
   ::ChartSetInteger(this.ChartID(),CHART_EVENT_MOUSE_WHEEL,true);
   ::ChartSetInteger(this.ChartID(),CHART_EVENT_OBJECT_CREATE,true);
   ::ChartSetInteger(this.ChartID(),CHART_EVENT_OBJECT_DELETE,true);
  }
//+------------------------------------------------------------------+


次に、グラフィカルオブジェクトコレクションクラスに焦点を当てます。

クラスのprivateセクションで、新しいメソッドを宣言し、その機能を説明に明確に記載します。

//+------------------------------------------------------------------+
//| Collection of graphical objects                                  |
//+------------------------------------------------------------------+
class CGraphElementsCollection : public CBaseObj
  {
private:
   CArrayObj         m_list_charts_control;     // List of chart management objects
   CListObj          m_list_all_canv_elm_obj;   // List of all graphical elements on canvas
   CListObj          m_list_all_graph_obj;      // List of all graphical objects
   bool              m_is_graph_obj_event;      // Event flag in the list of graphical objects
   int               m_total_objects;           // Number of graphical objects
   int               m_delta_graph_obj;         // Difference in the number of graphical objects compared to the previous check
   
//--- Return the flag indicating the graphical element object presence in the collection list of graphical elements
   bool              IsPresentGraphElmInList(const int id,const ENUM_GRAPH_ELEMENT_TYPE type_obj);
//--- Return the flag indicating the graphical element object presence in the collection list of graphical objects
   bool              IsPresentGraphObjInList(const long chart_id,const string name);
//--- Return the flag indicating the presence of a graphical object on a chart by name
   bool              IsPresentGraphObjOnChart(const long chart_id,const string name);
//--- Return the pointer to the object of managing objects of the specified chart
   CChartObjectsControl *GetChartObjectCtrlObj(const long chart_id);
//--- Create a new object of managing graphical objects of a specified chart and add it to the list
   CChartObjectsControl *CreateChartObjectCtrlObj(const long chart_id);
//--- Update the list of graphical objects by chart ID
   CChartObjectsControl *RefreshByChartID(const long chart_id);
//--- Return the first free ID of the graphical (1) object and (2) element on canvas
   long              GetFreeGraphObjID(void);
   long              GetFreeCanvElmID(void);
//--- Add a graphical object to the collection
   bool              AddGraphObjToCollection(const string source,CChartObjectsControl *obj_control);
//--- Find an object present in the collection but not on a chart
   CGStdGraphObj    *FindMissingObj(const long chart_id);
//--- Find the graphical object present on a chart but not in the collection
   string            FindExtraObj(const long chart_id);
//--- Remove the graphical object from the graphical object collection list
   bool              DeleteGraphObjFromList(CGStdGraphObj *obj);
   
public:

クラスのpublicセクションで、チャート名とIDでグラフィカルオブジェクトを返すメソッドおよびチャートイベントハンドラを宣言します。

public:
//--- Return itself
   CGraphElementsCollection *GetObject(void)                                                             { return &this;                        }
//--- Return the full collection list of standard graphical objects "as is"
   CArrayObj        *GetListGraphObj(void)                                                               { return &this.m_list_all_graph_obj;   }
//--- Return the full collection list of graphical elements on canvas "as is"
   CArrayObj        *GetListCanvElm(void)                                                                { return &this.m_list_all_canv_elm_obj;}
//--- Return the list of graphical elements by a selected (1) integer, (2) real and (3) string properties meeting the compared criterion
   CArrayObj        *GetList(ENUM_CANV_ELEMENT_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode=EQUAL) { return CSelect::ByGraphCanvElementProperty(this.GetListCanvElm(),property,value,mode);  }
   CArrayObj        *GetList(ENUM_CANV_ELEMENT_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode=EQUAL){ return CSelect::ByGraphCanvElementProperty(this.GetListCanvElm(),property,value,mode);  }
   CArrayObj        *GetList(ENUM_CANV_ELEMENT_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode=EQUAL){ return CSelect::ByGraphCanvElementProperty(this.GetListCanvElm(),property,value,mode);  }
//--- Return the list of graphical objects by a selected (1) integer, (2) real and (3) string properties meeting the compared criterion
   CArrayObj        *GetList(ENUM_GRAPH_OBJ_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode=EQUAL)    { return CSelect::ByGraphicStdObjectProperty(this.GetListGraphObj(),property,value,mode); }
   CArrayObj        *GetList(ENUM_GRAPH_OBJ_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode=EQUAL)   { return CSelect::ByGraphicStdObjectProperty(this.GetListGraphObj(),property,value,mode); }
   CArrayObj        *GetList(ENUM_GRAPH_OBJ_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode=EQUAL)   { return CSelect::ByGraphicStdObjectProperty(this.GetListGraphObj(),property,value,mode); }
//--- Return the number of new graphical objects, (3) the flag of the occurred change in the list of graphical objects
   int               NewObjects(void)   const                                                            { return this.m_delta_graph_obj;       }
   bool              IsEvent(void) const                                                                 { return this.m_is_graph_obj_event;    }
//--- Return a graphical object by chart name and ID
   CGStdGraphObj    *GetStdGraphObject(const string name,const long chart_id);
//--- Constructor
                     CGraphElementsCollection();
//--- Display the description of the object properties in the journal (full_prop=true - all properties, false - supported ones only - implemented in descendant classes)
   virtual void      Print(const bool full_prop=false,const bool dash=false);
//--- Display a short description of the object in the journal
   virtual void      PrintShort(const bool dash=false,const bool symbol=false);

//--- Create the list of chart management objects and return the number of charts
   int               CreateChartControlList(void);
//--- Update the list of (1) all graphical objects, (2) on the specified chart, fill in the data on the number of new ones and set the event flag
   void              Refresh(void);
   void              Refresh(const long chart_id);
//--- Event handler
   void              OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam);
  };
//+------------------------------------------------------------------+

すべてのグラフィカルオブジェクトのリストを更新するメソッドで、チャートからのグラフィカルオブジェクトの削除を処理するブロックを追加します

//+------------------------------------------------------------------+
//| Update the list of all graphical objects                         |
//+------------------------------------------------------------------+
void CGraphElementsCollection::Refresh(void)
  {
//--- 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(!AddGraphObjToCollection(DFUN_ERR_LINE,obj_ctrl))
               continue;
           }
         //--- If the graphical object has been removed
         else if(obj_ctrl.Delta()<0)
           {
            // Find an extra object in the list
            CGStdGraphObj *obj=this.FindMissingObj(chart_id);
            if(obj!=NULL)
              {
               //--- Display a short description of a detected object deleted from a chart in the journal
               obj.PrintShort();
               //--- Remove the class object of a removed graphical object from the collection list
               if(!this.DeleteGraphObjFromList(obj))
                  CMessage::ToLog(DFUN,MSG_GRAPH_OBJ_FAILED_DETACH_OBJ_FROM_LIST);
              }
           }
         //--- otherwise
         else
           {
            
           }
        }
      //--- Increase the loop index
      i++;
     }
  }
//+------------------------------------------------------------------+

このメソッドでは、チャートコントロールオブジェクトの総数によって、ループ内の各コントロールオブジェクトにイベントが存在するかどうかを確認します。イベントが存在する場合は、チャート上のオブジェクト(チャート制御オブジェクトによって管理される)の数が変更された値を確認してください。前回の記事でオブジェクトの追加処理を実装しました。ここでは、チャートオブジェクト番号の変更の負の値の処理を紹介しました。
ここではすべて簡単です。まず、グラフ上にグラフィカルオブジェクトがないコレクションリストオブジェクトを検索し、コレクションリストから削除します。

以下は、コレクションには存在するがチャートには存在しないオブジェクトを検索するメソッドです。

//+------------------------------------------------------------------+
//|Find an object present in the collection but not on a chart       |
//+------------------------------------------------------------------+
CGStdGraphObj *CGraphElementsCollection::FindMissingObj(const long chart_id)
  {
   CArrayObj *list=CSelect::ByGraphicStdObjectProperty(this.GetListGraphObj(),GRAPH_OBJ_PROP_CHART_ID,chart_id,EQUAL);
   if(list==NULL)
      return NULL;
   for(int i=0;i<list.Total();i++)
     {
      CGStdGraphObj *obj=list.At(i);
      if(obj==NULL)
         continue;
      if(!this.IsPresentGraphObjOnChart(obj.ChartID(),obj.Name()))
         return obj;
     }
   return NULL;
  }
//+------------------------------------------------------------------+

ここで、メソッドパラメータで指定されたものと等しいチャートIDを持つすべてのオブジェクトのリストを取得します
取得したリストによるループで、標準のグラフィカルオブジェクトクラスの次のオブジェクトを取得しますチャートにそのような名前のオブジェクトがない場合は、オブジェクトへのポインタを返します
ループが完了すると、NULL が返されます

以下は、チャートには存在するがコレクションには存在しないオブジェクトを検索するメソッドです。

//+------------------------------------------------------------------+
//|Find an object present on a chart but not in the collection       |
//+------------------------------------------------------------------+
string CGraphElementsCollection::FindExtraObj(const long chart_id)
  {
   int total=::ObjectsTotal(chart_id);
   for(int i=0;i<total;i++)
     {
      string name=::ObjectName(chart_id,i);
      if(!this.IsPresentGraphObjInList(chart_id,name))
         return name;
     }
   return NULL;
  }
//+------------------------------------------------------------------+

ここで、ターミナルリスト内のすべてのオブジェクトによるループで、次のオブジェクトの名前を取得します。コレクションリストにそのような名前とチャートIDを持つオブジェクトがない場合は、グラフィカルオブジェクト名を返します。ループが完了すると、 NULLが返されます

以下は、グラフィカルオブジェクトコレクションリストにグラフィカルオブジェクトクラスが存在することを示すフラグを返すメソッドです。

//+------------------------------------------------------------------------------+
//| Return the flag indicating the presence of the graphical object class object |
//| in the graphical object collection list                                      |
//+------------------------------------------------------------------------------+
bool CGraphElementsCollection::IsPresentGraphObjInList(const long chart_id,const string name)
  {
   CArrayObj *list=CSelect::ByGraphicStdObjectProperty(this.GetListGraphObj(),GRAPH_OBJ_PROP_CHART_ID,chart_id,EQUAL);
   list=CSelect::ByGraphicStdObjectProperty(list,GRAPH_OBJ_PROP_NAME,name,EQUAL);
   return(list==NULL || list.Total()==0 ? false : true);
  }
//+------------------------------------------------------------------+

指定されたチャートIDを特徴とするオブジェクトのリストを取得します取得したリストから、必要な名前と一致する名前のオブジェクトへのポインタを取得しますリストの取得に失敗した場合、またはリストが空の場合は、falseを返します。オブジェクトが見つかりません。それ以外の場合はtrue を返します 。

以下は、名前でチャート上にグラフィカルオブジェクトが存在することを示すフラグを返すメソッドです。

//+----------------------------------------------------------------------------------+
//| Return the flag indicating the presence of a graphical object on a chart by name |
//+----------------------------------------------------------------------------------+
bool CGraphElementsCollection::IsPresentGraphObjOnChart(const long chart_id,const string name)
  {
   int total=::ObjectsTotal(chart_id);
   for(int i=0;i<total;i++)
      if(::ObjectName(chart_id,i)==name)
         return true;
   return false;
  }
//+-------------------------------------------------------------------+

すべてのグラフィカルオブジェクトの総数によるループで、IDで指定されたチャートpの次のオブジェクトの名前を取得します。名前が必要な名前と一致する場合は、trueが返されますループが完了したら、falseが返されます

以下は、グラフィカルオブジェクトコレクションリストからグラフィカルオブジェクトを削除するメソッドです。

//+---------------------------------------------------------------------+
//|Remove the graphical object from the graphical object collection list|
//+---------------------------------------------------------------------+
bool CGraphElementsCollection::DeleteGraphObjFromList(CGStdGraphObj *obj)
  {
   this.m_list_all_graph_obj.Sort();
   int index=this.m_list_all_graph_obj.Search(obj);
   return(index==WRONG_VALUE ? false : this.m_list_all_graph_obj.Delete(index));
  }
//+------------------------------------------------------------------+

メソッドは、リストから削除されるオブジェクトへのポインタを受け取ります
並び替え済みリストフラグをリストに設定
(検索は並び替え済みリストでのみ実行されます)し、標準ライブラリのSearch()メソッドを使用してオブジェクトインデックスを取得します
オブジェクトインデックスが見つからない場合はfalseを返し
、それ以外の場合は標準ライブラリのDelete()メソッドを使用してリストからオブジェクトを削除した結果を返します。

以下は、チャート名とIDでグラフィカルオブジェクトへのポインタを返すメソッドです。

//+------------------------------------------------------------------+
//| Return a graphical object by chart name and ID                   |
//+------------------------------------------------------------------+
CGStdGraphObj *CGraphElementsCollection::GetStdGraphObject(const string name,const long chart_id)
  {
   CArrayObj *list=this.GetList(GRAPH_OBJ_PROP_CHART_ID,chart_id);
   list=CSelect::ByGraphicStdObjectProperty(list,GRAPH_OBJ_PROP_NAME,name,EQUAL);
   return(list!=NULL && list.Total()>0 ? list.At(0) : NULL);
  }
//+------------------------------------------------------------------+

チャートIDがメソッドに渡されたものと等しいオブジェクトのリストを取得します。取得したリストから、必要な名前と一致する名前のオブジェクトを含むリストを取得します(そのようなオブジェクトは1つだけです)。リストの取得に失敗し、リストが空でない場合は、リストの最初の(そして唯一の)オブジェクトへのポインタを返しますそれ以外の場合は、NULLを返します

イベントハンドラ

本稿では、現在のチャートでのみグラフィカルオブジェクトイベントを処理するイベントハンドラのテストバージョンを実装します。ハンドラは、グラフィカルオブジェクトの変更または再配置のイベントを処理します。このメソッドは、上記のイベントを定義するのに十分です。さらに、マウスの1回のクリックで作成されないオブジェクトプロパティの不完全な入力の問題をさらに修正します。チャートを最初にクリックするとオブジェクト作成イベントが作成され、ライブラリが適切なイベントをすぐに作成することはすでに説明しました。

同時に、オブジェクトは複数回のマウスクリックで作成されるため、すべてのプロパティが正しく設定されているわけではありません。オブジェクトの構築が完了すると、移動イベントが作成されます。これに対する応答により、オブジェクトプロパティが書き換えられます(イベントをリアルタイムで追跡したいのですが、複数のアンカーポイントを持つオブジェクトを作成すると、オブジェクトプロパティの更新につながる移動イベントも取得されるため、次のようになります。 正しいデータで書き直されました)。

ロジック全体は、メソッドコードのコメントで説明されています。メソッドについて考察してみましょう。

//+------------------------------------------------------------------+
//| Event handler                                                    |
//+------------------------------------------------------------------+
void CGraphElementsCollection::OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
  {
   CGStdGraphObj *obj=NULL;
   if(id==CHARTEVENT_OBJECT_CHANGE || id==CHARTEVENT_OBJECT_DRAG)
     {
      //--- If the object, whose properties were changed or which was relocated,
      //--- is successfully received from the collection list by its name set in sparam
      obj=this.GetStdGraphObject(sparam,::ChartID());
      if(obj!=NULL)
        {
         //--- Update the properties of the obtained object
         //--- and check their change
         obj.PropertiesRefresh();
         obj.PropertiesCheckChanged();
        }
      //--- If failed to get the object by its name, it is not on the list,
      //--- which means its name has been changed
      else
        {
         //--- Let's search the list for the object that is not on the chart
         obj=this.FindMissingObj(::ChartID());
         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(::ChartID());
         //--- Set a new name for the collection list object, which does not correspond to any graphical object on the chart,
         //--- update the chart properties and check their change
         obj.SetName(name_new);
         obj.PropertiesRefresh();
         obj.PropertiesCheckChanged();
        }
     }
  }
//+------------------------------------------------------------------+


さらに、ライブラリベースのプログラムからグラフィカルオブジェクトコレクションにアクセスする必要があります。
これを行うには、\MQL5\Include\DoEasy\Engine.mqh内のメインライブラリオブジェクトで、ライブラリグラフィカルオブジェクトのコレクションクラスへのポインタを返すメソッドを作成します。

//--- Launch the new pause countdown
   void                 Pause(const ulong pause_msc,const datetime time_start=0)
                          {
                           this.PauseSetWaitingMSC(pause_msc);
                           this.PauseSetTimeBegin(time_start*1000);
                           while(!this.PauseIsCompleted() && !::IsStopped()){}
                          }

//--- Return the graphical object collection
   CGraphElementsCollection *GetGraphicObjCollection(void)              { return &this.m_graph_objects; }

//--- Constructor/destructor
                        CEngine();
                       ~CEngine();

private:


検証

テストを実行するには、前の記事のEAを使用して、\MQL5\Experts\TestDoEasy\Part86\TestDoEasyPart86.mq5として保存します。

OnInit()ハンドラから、マウスイベントを追跡するためのアクセス許可を設定する文字列を削除します

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Set the permissions to send cursor movement and mouse scroll events
   ChartSetInteger(ChartID(),CHART_EVENT_MOUSE_MOVE,true);
   ChartSetInteger(ChartID(),CHART_EVENT_MOUSE_WHEEL,true);
//--- Set EA global variables
   ArrayResize(array_clr,2);        // Array of gradient filling colors
   array_clr[0]=C'246,244,244';     // Original ≈pale gray
   array_clr[1]=C'249,251,250';     // Final ≈pale gray-green
//--- 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
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+

これで、マウスとグラフィカルオブジェクトのイベントを追跡する権限がチャート管理クラスで設定されています。権限は開いているすべてのチャートについて設定されます。

OnChartEvent()ハンドラの最後に、グラフィカルオブジェクトコレクションクラスイベントのハンドラの呼び出しを追加します

//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//--- If working in the tester, exit
   if(MQLInfoInteger(MQL_TESTER))
      return;
//--- If the mouse is moved
   if(id==CHARTEVENT_MOUSE_MOVE)
     {
      CForm *form=NULL;
      datetime time=0;
      double price=0;
      int wnd=0;
      
      //--- If Ctrl is not pressed,
      if(!IsCtrlKeyPressed())
        {
         //--- clear the list of created form objects, allow scrolling a chart with the mouse and show the context menu
         list_forms.Clear();
         ChartSetInteger(ChartID(),CHART_MOUSE_SCROLL,true);
         ChartSetInteger(ChartID(),CHART_CONTEXT_MENU,true);
         return;
        }
      
      //--- If X and Y chart coordinates are successfully converted into time and price,
      if(ChartXYToTimePrice(ChartID(),(int)lparam,(int)dparam,wnd,time,price))
        {
         //--- get the bar index the cursor is hovered over
         int index=iBarShift(Symbol(),PERIOD_CURRENT,time);
         if(index==WRONG_VALUE)
            return;
         
         //--- Get the bar index by index
         CBar *bar=engine.SeriesGetBar(Symbol(),Period(),index);
         if(bar==NULL)
            return;
         
         //--- Convert the coordinates of a chart from the time/price representation of the bar object to the X and Y coordinates
         int x=(int)lparam,y=(int)dparam;
         if(!ChartTimePriceToXY(ChartID(),0,bar.Time(),(bar.Open()+bar.Close())/2.0,x,y))
            return;
         
         //--- Disable moving a chart with the mouse and showing the context menu
         ChartSetInteger(ChartID(),CHART_MOUSE_SCROLL,false);
         ChartSetInteger(ChartID(),CHART_CONTEXT_MENU,false);
         
         //--- Create the form object name and hide all objects except one having such a name
         string name="FormBar_"+(string)index;
         HideFormAllExceptOne(name);
         
         //--- If the form object with such a name does not exist yet,
         if(!IsPresentForm(name))
           {
            //--- create a new form object
            form=bar.CreateForm(index,name,x,y,114,16);   
            if(form==NULL)
               return;
            
            //--- Set activity and unmoveability flags for the form
            form.SetActive(true);
            form.SetMovable(false);
            //--- Set the opacity of 200
            form.SetOpacity(200);
            //--- 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(C'47,70,59');
            //--- Draw the shadow drawing flag
            form.SetShadow(true);
            //--- 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(2,2,clr,200,3);
            //--- Fill the form background with a vertical gradient
            form.Erase(array_clr,form.Opacity());
            //--- Draw an outlining rectangle at the edges of the form
            form.DrawRectangle(0,0,form.Width()-1,form.Height()-1,form.ColorFrame(),form.Opacity());
            //--- If failed to add the form object to the list, remove the form and exit the handler
            if(!list_forms.Add(form))
              {
               delete form;
               return;
              }
            //--- Capture the form appearance
            form.Done();
           }
         //--- If the form object exists,
         if(form!=NULL)
           {
            //--- draw a text with the bar type description on it and show the form. The description corresponds to the mouse cursor position
            form.TextOnBG(0,bar.BodyTypeDescription(),form.Width()/2,form.Height()/2-1,FRAME_ANCHOR_CENTER,C'7,28,21');
            form.Show();
           }
         //--- Redraw the chart
         ChartRedraw();
        }
     }
   
   engine.GetGraphicObjCollection().OnChartEvent(id,lparam,dparam,sparam);
   
  }
//+------------------------------------------------------------------+

改善点はこれで全部です。EAをコンパイルし、チャート上で起動します。オブジェクトを作成/削除したり、そのプロパティを変更したりすると、適切なイベントエントリがクライアントターミナルの操作ログに表示されます。


これまでのところ、これらは単なる操作ログエントリですが、これは後で変更されます。

次の段階

次の記事では、開いているチャートごとにオブジェクトイベントハンドラを作成し、これらのイベントを制御プログラムチャートに送信してプログラムが完全に処理できるようにします。

ライブラリの現在のバージョンのすべてのファイルは、テストおよびダウンロードできるように、MQL5のテストEAファイルと一緒に以下に添付されています。

質問や提案はコメント欄にお願いします。

目次に戻る

**連載のこれまでの記事:

DoEasyライブラリのグラフィックス(第83部): 抽象標準グラフィカルオブジェクトのクラス
DoEasyライブラリのグラフィックス(第84部): 抽象標準グラフィカルオブジェクトの子孫クラス
DoEasyライブラリのグラフィックス(第85部): グラフィカルオブジェクトコレクション - 新規作成オブジェクトの追加

MetaQuotes Ltdによってロシア語から翻訳されました。
元の記事: https://www.mql5.com/ru/articles/10018

添付されたファイル |
MQL5.zip (4153.33 KB)
DoEasyライブラリのグラフィックス(第87部): グラフィカルオブジェクトコレクション - プロパティ変更の管理 DoEasyライブラリのグラフィックス(第87部): グラフィカルオブジェクトコレクション - プロパティ変更の管理
本稿では、標準のグラフィカルオブジェクトイベントの追跡作業を継続し、ユーザがターミナルで開いたチャートに配置されたグラフィカルオブジェクトのプロパティの変更を制御できる機能を作成します。
多層パーセプトロンとバックプロパゲーションアルゴリズム(第II部): Pythonでの実装とMQL5との統合 多層パーセプトロンとバックプロパゲーションアルゴリズム(第II部): Pythonでの実装とMQL5との統合
MQLとの統合を開発するために利用できるPythonパッケージが存在し、データの探索、作成、機械学習モデルの使用などのさまざまな機会がもたらされます。MQL5に組み込まれているPython統合により、単純な線形回帰から深層学習モデルまで、さまざまなソリューションを作成できます。開発環境を設定して準備する方法と、いくつかの機械学習ライブラリを使用する方法を見てみましょう。
MQL5.communityでのチャネルとグループチャットの使用 MQL5.communityでのチャネルとグループチャットの使用
MQL5.com Webサイトには、世界中のトレーダーが集まっています。ユーザーは記事を公開し、無料コードを共有し、市場で製品を販売し、フリーランスの注文を実行し、取引シグナルをコピーできます。フォーラム、トレーダーチャット、MetaTraderチャネルでは彼らとコミュニケーションをとることができます。
より優れたプログラマー(第05部): より速い開発者になる方法 より優れたプログラマー(第05部): より速い開発者になる方法
すべての開発者は、コードをより速く書くことを望んでいます。より速く効果的にコードを書けることは、少数の人々だけが生まれつき持っているような特別な能力ではありません。これは、すべてのコーダーが習得できるスキルです。この記事ではそれを教えようと思います。