
DoEasyライブラリのグラフィックス(第86部): グラフィカルオブジェクトコレクション - プロパティ変更の管理
内容
概念
前の記事では、新しく作成された標準のグラフィカルオブジェクトをグラフィカルオブジェクトのコレクションクラスに格納する機能を実装しました。新しいチャートオブジェクトを検出し、チャート上のオブジェクトタイプに対応するクラスのオブジェクトを作成してコレクションリストに追加します。ただし、これはグラフィカルオブジェクトを本格的に管理するには不十分です。チャート上のグラフィカルオブジェクトのプロパティのすべての変更、削除、名前変更を管理する必要があります。
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をコンパイルし、チャート上で起動します。オブジェクトを作成/削除したり、そのプロパティを変更したりすると、適切なイベントエントリがクライアントターミナルの操作ログに表示されます。
これまでのところ、これらは単なる操作ログエントリですが、これは後で変更されます。
次の段階
次の記事では、開いているチャートごとにオブジェクトイベントハンドラを作成し、これらのイベントを制御プログラムチャートに送信してプログラムが完全に処理できるようにします。
質問や提案はコメント欄にお願いします。
**連載のこれまでの記事:
DoEasyライブラリのグラフィックス(第83部): 抽象標準グラフィカルオブジェクトのクラス
DoEasyライブラリのグラフィックス(第84部): 抽象標準グラフィカルオブジェクトの子孫クラス
DoEasyライブラリのグラフィックス(第85部): グラフィカルオブジェクトコレクション - 新規作成オブジェクトの追加
MetaQuotes Ltdによってロシア語から翻訳されました。
元の記事: https://www.mql5.com/ru/articles/10018





- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索