DoEasyライブラリのグラフィックス(第81部): ライブラリオブジェクトへのグラフィックの統合
内容
概念
今回は、グラフィックの新しい構造や改善を実装するつもりはありません。代わりに、作成済みのグラフィック要素クラスをライブラリオブジェクトに統合し始めます。これにより、グラフィック要素のさらなる開発と改善が可能になります。後で、複合グラフィカルオブジェクトの有効性を制御するために、チャートに沿ったオブジェクトの移動を実装する必要があります。これを実現するには、これらのオブジェクトをライブラリオブジェクトに統合することを事前に検討して、グラフィカルオブジェクトを管理するためのクラスとそのコレクションクラスを作成できるようにする必要があります。
グラフィカルオブジェクト管理クラスには、フォームとそれに続くグラフィカルオブジェクトを作成するためのメソッドが含まれています。これらのメソッドは、作成されたグラフィカルオブジェクトへのポインタを返し、後で操作できるようにします。さまざまなライブラリオブジェクトに関連するすべてのビルドされたグラフィカルオブジェクトのリストを作成するために、グラフィック要素コレクションクラスが必要になります。これにより、オブジェクト同士およびプログラムユーザーとの相互作用のメソッドを作成できます。
現在の記事では、グラフィカルオブジェクトをライブラリオブジェクトの1つであるバーオブジェクトにのみ統合します。作成したコンセプトをデバッグするのに少し時間がかかります。次に、開発およびデバッグされたメカニズムを使用して、残りのライブラリオブジェクトに追加します。その後、ライブラリのグラフィカルオブジェクトの開発に戻ります。
現在のコンセプトは次のとおりです。
- さまざまな描画メソッドを持つグラフィック要素オブジェクトがあります。
- グラフィカルオブジェクトについてまだ何も知らないバーオブジェクトがあります。
- グラフィカルオブジェクト管理クラスを作成して、それらを作成し、作成されたオブジェクトへのポインタを返すことができるようにする必要があります。
- この管理クラスをバーオブジェクトコンポーネントの1つにする必要があります。
これらの4つの手順により、以前に作成されたライブラリオブジェクト(現在、これはバーオブジェクト)を取得できます。グラフィカルオブジェクトを管理するためのオブジェクトを使用すると、必要なオブジェクトを作成してそのオブジェクトへのポインタを取得できるため、以前の記事で検討した通常のライブラリグラフィカルオブジェクトと同様に、さらに操作することができます。
コンセプトをデバッグした後は、ライブラリのグラフィカルコンポーネントを統合するために、バーオブジェクトに対して現在行っているすべてのことをすべてのライブラリオブジェクトに実装します。すべてのオブジェクトは新しい「視覚的」次元を取得し、ライブラリと対話するための新しいツールを取得します。
ライブラリクラスの改善
ライブラリオブジェクトによって作成された各グラフィカルオブジェクトは、それを認識している必要があります。もちろん、それ自体でグラフィカルオブジェクトを作成できる単一のオブジェクトがある場合(現在、これはバーオブジェクトです)、新しく作成されたグラフィカルオブジェクトは、どのオブジェクトに作成されたかを知る必要はありません。ただし、各ライブラリオブジェクトがそれ自体のグラフィカルオブジェクトを作成できる場合、作成されたすべてのグラフィカルオブジェクトは、作成元のオブジェクトを参照してそこからデータを取得するために、どのオブジェクトの内部に作成されたのかを認識している必要があります。これは、グラフィカルオブジェクトにデータを表示したり、異なるオブジェクト間のより複雑な関係を実現したりする場合に役立ちます。
もちろん、1つの記事内ですべてを行うことは不可能です。最も単純なことから始めます。グラフィカルオブジェクトの作成元であるオブジェクトのタイプの説明を知る必要があります。これを実現するために、オブジェクトコレクションIDを使用しましょう(オブジェクトタイプに対応するコレクションのIDがオブジェクトごとに設定されます)。IDを使用すると、ライブラリオブジェクト(グラフィカルオブジェクトの作成元)が属するオブジェクトタイプを定義できます。もちろん、これは特定のオブジェクトを正確に示すには不十分です。しかし、すでに述べたように、私は単純なことから始めています。
さらに、以前に作成されたすべてのライブラリオブジェクトに対して同じタイプのオブジェクトの説明を表示するためのメソッドが必要になります。これらは、オブジェクトプロパティの完全な説明と短い説明を表示するPrint()メソッドとPrintShort()メソッドです。これらのメソッドを仮想化し、すべてのCBaseObjライブラリオブジェクトの親クラスで宣言しましょう。仮想化が機能するためには、これらのメソッドの引数はすべてのクラスでまったく同じにする必要があります。現時点では、これらのメソッドのパラメータはクラスによって違います。それらを単一の形式にし、メソッド引数でのパラメータの変更に従ってメソッド呼び出しを修正する必要があります。
\MQL5\Include\DoEasy\Objects\BaseObj.mqhのCBaseObjクラスで、2つの仮想メソッドと必要なパラメータを宣言します。
//--- Return an object type virtual int Type(void) const { return this.m_type; } //--- 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) { return; } //--- Display a short description of the object in the journal virtual void PrintShort(const bool dash=false,const bool symbol=false){ return; } //--- Constructor
メソッド引数のパラメータは、派生クラスで以前に実装したすべてのメソッドで使用できるように選択されています。
たとえば、COrderクラス(ライブラリの注文システム全体の基本クラス)には、次の変更が含まれています。
//--- Return order/position direction string DirectionDescription(void) const; //--- 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); //--- }; //+------------------------------------------------------------------+
ここで、さらに別の引数をPrint()メソッドに追加し、 PrintShort()メソッドを宣言しました。
クラス本体の外部でメソッドを実装する際に、追加のメソッド引数も追加しました。
//+------------------------------------------------------------------+ //| Send order properties to the journal | //+------------------------------------------------------------------+ void COrder::Print(const bool full_prop=false,const bool dash=false) { ::Print("============= ",CMessage::Text(MSG_LIB_PARAMS_LIST_BEG),": \"",this.StatusDescription(),"\" ============="); int beg=0, end=ORDER_PROP_INTEGER_TOTAL; for(int i=beg; i<end; i++) { ENUM_ORDER_PROP_INTEGER prop=(ENUM_ORDER_PROP_INTEGER)i; if(!full_prop && !this.SupportProperty(prop)) continue; ::Print(this.GetPropertyDescription(prop)); } ::Print("------"); beg=end; end+=ORDER_PROP_DOUBLE_TOTAL; for(int i=beg; i<end; i++) { ENUM_ORDER_PROP_DOUBLE prop=(ENUM_ORDER_PROP_DOUBLE)i; if(!full_prop && !this.SupportProperty(prop)) continue; ::Print(this.GetPropertyDescription(prop)); } ::Print("------"); beg=end; end+=ORDER_PROP_STRING_TOTAL; for(int i=beg; i<end; i++) { ENUM_ORDER_PROP_STRING prop=(ENUM_ORDER_PROP_STRING)i; if(!full_prop && !this.SupportProperty(prop)) continue; ::Print(this.GetPropertyDescription(prop)); } ::Print("================== ",CMessage::Text(MSG_LIB_PARAMS_LIST_END),": \"",this.StatusDescription(),"\" ==================\n"); } //+------------------------------------------------------------------+
以下は、引数に実装されたパラメータを使用してメソッド呼び出しを改善する例です。
//+------------------------------------------------------------------+ //| Display complete collection description to the journal | //+------------------------------------------------------------------+ void CMBookSeriesCollection::Print(const bool full_prop=false,const bool dash=false) { ::Print(CMessage::Text(MSG_MB_COLLECTION_TEXT_MBCOLLECTION),":"); for(int i=0;i<this.m_list.Total();i++) { CMBookSeries *bookseries=this.m_list.At(i); if(bookseries==NULL) continue; bookseries.Print(false,true); } } //+------------------------------------------------------------------+ //| Display the short collection description in the journal | //+------------------------------------------------------------------+ void CMBookSeriesCollection::PrintShort(const bool dash=false,const bool symbol=false) { ::Print(CMessage::Text(MSG_MB_COLLECTION_TEXT_MBCOLLECTION),":"); for(int i=0;i<this.m_list.Total();i++) { CMBookSeries *bookseries=this.m_list.At(i); if(bookseries==NULL) continue; bookseries.PrintShort(true); } } //+------------------------------------------------------------------+
以前はここに単一のパラメータがあり、メソッドはbookseries.Print(true);として呼び出されました。CMBookSeriesクラスのPrint()メソッドは、必要なパラメータの前にさらに別のパラメータを備えています。したがって、最初に追加されたパラメータに対してfalseを渡し、次に必要なもの(メソッドを呼び出すときに以前に存在したもの)に対してtrueを渡します。
同様の変更は、以前に作成されたライブラリオブジェクトのほぼすべてのクラスに影響を与え、これらのメソッドとすべてのライブラリオブジェクトの基本オブジェクトから継承されたメソッドを含むすべてのクラスにすでに実装されています。
BookSeriesCollection.mqh、ChartObjCollection.mqh、MQLSignalsCollection.mqh、TickSeriesCollection.mqh、TimeSeriesCollection.mqh
Account.mqh、MarketBookOrd.mqh、MarketBookSnapshot.mqh、MBookSeries.mqh、ChartObj.mqh、ChartWnd.mqh、MQLSignal.mqh、Order.mqh
Buffer.mqh、BufferArrow.mqh、BufferBars.mqh、BufferCalculate.mqh、BufferCandles.mqh、BufferFilling.mqh、BufferHistogram.mqh、BufferHistogram2.mqh、BufferLine.mqh、BufferSection.mqh、BufferZigZag.mqh、DataInd.mqh、IndicatorDE.mqh
PendReqClose.mqh、PendReqModify.mqh、PendReqOpen.mqh、PendReqPlace.mqh、PendReqRemove.mqh、PendReqSLTP.mqh、PendRequest.mqh
Bar.mqh、SeriesDE.mqh、TimeSeriesDE.mqh、DataTick.mqh、TickSeries.mqh
Symbol.mqh、SymbolBonds.mqh、SymbolCFD.mqh、SymbolCollateral.mqh、SymbolCommodity.mqh、SymbolCommon.mqh、SymbolCrypto.mqh、SymbolCustom.mqh、SymbolExchange.mqh、SymbolFutures.mqh、SymbolFX.mqh、SymbolFXExotic.mqh、SymbolFXMajor.mqh、SymbolFXMinor.mqh、SymbolFXRub.mqh、SymbolIndex.mqh、SymbolIndicative.mqh、SymbolMetall.mqh、SymbolOption.mqh、SymbolStocks.mqh
BaseObj.mqh
ライブラリの一部のクラスでは、メッセージは標準のPrint()関数に置き換えられ、たとえば、CMessageクラスのToLog()メソッドを使用してメッセージを表示します。 例は、イベントコレクションクラスの次のメソッドです。
//+------------------------------------------------------------------+ //| Select only market pending orders from the list | //+------------------------------------------------------------------+ CArrayObj* CEventsCollection::GetListMarketPendings(CArrayObj* list) { if(list.Type()!=COLLECTION_MARKET_ID) { CMessage::ToLog(DFUN,MSG_LIB_SYS_ERROR_NOT_MARKET_LIST); return NULL; } CArrayObj* list_orders=CSelect::ByOrderProperty(list,ORDER_PROP_STATUS,ORDER_STATUS_MARKET_PENDING,EQUAL); return list_orders; } //+------------------------------------------------------------------+
次の文字列は、以前はメッセージを表示するために使用されていました。
Print(DFUN,CMessage::Text(MSG_LIB_SYS_ERROR_NOT_MARKET_LIST));
次はそのような編集を特徴とするファイルのリストです。
EventsCollection.mqh, HistoryCollection.mqh, TimeSeriesCollection.mqh.
変更は添付ファイルで参照できます。
グラフに作成済みのフォームオブジェクトが含まれている場合は、指定した時間枠で表示フラグを指定することで、グラフを非表示または表示できます。これを使用して、BringToTop()メソッドを使用して、オブジェクトを他のすべてのオブジェクトよりも前景に「移動」します。
グラフィカルオブジェクトを表示/非表示にするメソッドはありません。
\MQL5\Include\DoEasy\Objects\Graph\<b0>GCnvElement.mqh</b0>CGCnvElementのグラフィック要素クラスで2つの仮想メソッドを作成しましょう。
//--- Set the object above all void BringToTop(void) { CGBaseObj::SetVisible(false); CGBaseObj::SetVisible(true); } //--- (1) Show and (2) hide the element virtual void Show(void) { CGBaseObj::SetVisible(true); } virtual void Hide(void) { CGBaseObj::SetVisible(false); }
メソッドは、ライブラリのグラフィカルオブジェクトの基本ブジェクトのすべての時間枠でオブジェクトを表示するための適切なフラグを設定するだけです。
UCFormフォームオブジェクトクラスはグラフィック要素オブジェクトの子孫であり、フォームオブジェクトは複数のグラフィック要素オブジェクトで構成できます。したがって、これらのメソッドを個別に実装する必要があります。
\MQL5\Include\DoEasy\Objects\Graph\Form.mqhを開き、publicセクションで2つの仮想メソッドを宣言します。
//+------------------------------------------------------------------+ //| Visual design methods | //+------------------------------------------------------------------+ //--- (1) Show and (2) hide the form virtual void Show(void); virtual void Hide(void); //+------------------------------------------------------------------+ //| Methods of simplified access to object properties | //+------------------------------------------------------------------+
クラス本体の外側で実装しましょう。
//+------------------------------------------------------------------+ //| Show the form | //+------------------------------------------------------------------+ void CForm::Show(void) { //--- If the object has a shadow, display it if(this.m_shadow_obj!=NULL) this.m_shadow_obj.Show(); //--- Display the main form CGCnvElement::Show(); //--- In the loop by all bound graphical objects, for(int i=0;i<this.m_list_elements.Total();i++) { //--- get the next graphical element CGCnvElement *elment=this.m_list_elements.At(i); if(elment==NULL) continue; //--- and display it elment.Show(); } //--- Update the form CGCnvElement::Update(); } //+------------------------------------------------------------------+ //| Hide the form | //+------------------------------------------------------------------+ void CForm::Hide(void) { //--- If the object has a shadow, hide it if(this.m_shadow_obj!=NULL) this.m_shadow_obj.Hide(); //--- In the loop by all bound graphical objects, for(int i=0;i<this.m_list_elements.Total();i++) { //--- get the next graphical element CGCnvElement *elment=this.m_list_elements.At(i); if(elment==NULL) continue; //--- and hide it elment.Hide(); } //--- Hide the main form and update the object CGCnvElement::Hide(); CGCnvElement::Update(); } //+------------------------------------------------------------------+
どちらのメソッドも、コードリストで詳細にコメントされています。簡単に言うと、オブジェクトを非表示にする場合、非表示にする順序に大きな違いはありません。ただし、オブジェクトを表示するときは、メインフォーム上のすべてのバインドされたオブジェクトの場所のシーケンス全体を復元することが重要です。したがって、表示は層で行われます。最初に、最も低いオブジェクトが表示されます(フォームの影)。メインフォームが影の上に表示されます。その後、メインフォームにバインドされているすべてのグラフィック要素が表示されます。この実装では、表示順序は、バインドされたオブジェクトのリストに追加される順序に対応しています。
複雑な(複合)フォームオブジェクトを作成する際のアルゴリズムを確認する必要があります。
次に、グラフィカルオブジェクトのライブラリオブジェクトへの統合を開始します。
グラフィックオブジェクト管理クラス
では、どのようにして各ライブラリオブジェクトにそれ自体のグラフィカルオブジェクトを作成する機能を与えることができるでしょうか。
ほとんどのライブラリオブジェクトは、すべてのCBaseObjライブラリオブジェクトの基本ブジェクトから派生しています。可能なすべてのグラフィカルオブジェクト(利用可能であり、今後の開発が計画されている)を作成する機能を持つクラスのインスタンスを追加し、作成されたオブジェクトへのポインタへのアクセスを提供すると、そのすべての子孫がグラフィカルオブジェクトを操作できるようになります。
多数の異なるグラフィカルオブジェクトがあるため、そのような各オブジェクトを「認識」し、それらを作成および管理できるクラスが必要です。これは、グラフィカルオブジェクトを管理するためのクラスになります。
\MQL5\Include\DoEasy\Objects\Graph\で、CGraphElmControlクラスの新しいファイルGraphElmControl.mqhを作成します。クラスは、CObjectMQL5標準ライブラリを構築するための基本クラスから派生する必要があります。クラスには、3つのファイル(CObjectクラスインスタンスとその子孫へのポインタの動的配列のクラスのファイル、サービス関数ファイルとフォームオブジェクトクラスのファイル)を含める必要があります。 )
//+------------------------------------------------------------------+ //| GraphElmControl.mqh | //| Copyright 2021, MetaQuotes Ltd. | //| https://mql5.com/en/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2021, MetaQuotes Ltd." #property link "https://mql5.com/en/users/artmedia70" #property version "1.00" #property strict // Necessary for mql4 //+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include <Arrays\ArrayObj.mqh> #include "..\..\Services\DELib.mqh" #include "Form.mqh" //+------------------------------------------------------------------+ //| Class for managing graphical elements | //+------------------------------------------------------------------+ class CGraphElmControl : public CObject { private: int m_type_node; // Type of the object the graphics is constructed for public: //--- Return itself CGraphElmControl *GetObject(void) { return &this; } //--- Set a type of the object the graphics is constructed for void SetTypeNode(const int type_node) { this.m_type_node=type_node; } //--- Create a form object CForm *CreateForm(const int form_id,const long chart_id,const int wnd,const string name,const int x,const int y,const int w,const int h); CForm *CreateForm(const int form_id,const int wnd,const string name,const int x,const int y,const int w,const int h); CForm *CreateForm(const int form_id,const string name,const int x,const int y,const int w,const int h); //--- Constructors CGraphElmControl(){;} CGraphElmControl(int type_node); }; //+------------------------------------------------------------------+
m_type_node変数は、クラスオブジェクトを含むオブジェクトのタイプを格納します。新しいオブジェクト(現在はバーオブジェクト)を作成するとき、そのコンストラクタは SetTypeNode()メソッドを呼び出し、バーオブジェクトセットのタイプをm_type変数( バーの場合はバーオブジェクトコレクションID)に受け取ります。したがって、グラフィカルオブジェクトを管理するオブジェクトは、そのオブジェクトを構築するクラスを認識しています。今のところ、コレクションIDのみを使用します。将来的には、グラフィックを構成するオブジェクトへのポインタを渡すことを検討します。
クラスメソッドについて考えてみましょう。
クラスのパラメトリックコンストラクタで、メソッド引数で渡されたオブジェクトタイプをm_type_node変数に設定します。
//+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CGraphElmControl::CGraphElmControl(int type_node) { this.m_type_node=m_type_node; } //+------------------------------------------------------------------+
以下は、指定されたサブウィンドウの指定されたチャートにフォームオブジェクトを作成するメソッドです。
//+----------------------------------------------------------------------+ //| Create the form object on a specified chart in a specified subwindow | //+----------------------------------------------------------------------+ CForm *CGraphElmControl::CreateForm(const int form_id,const long chart_id,const int wnd,const string name,const int x,const int y,const int w,const int h) { CForm *form=new CForm(chart_id,wnd,name,x,y,w,h); if(form==NULL) return NULL; form.SetID(form_id); form.SetNumber(0); return form; } //+------------------------------------------------------------------+
このメソッドは、作成されたフォームオブジェクトの一意のID、チャートID、チャートサブウィンドウインデックス、フォームオブジェクト名、フォームを作成するためのX/Y座標、およびフォームの幅と高さを受け取ります。
次に、メソッドに渡されたパラメータを使用して新しいフォームオブジェクトを作成します。正常に作成された場合は、オブジェクトのフォームIDとオブジェクトリストのインデックスを設定します(現在、オブジェクトには他のバインドされたフォームオブジェクトが含まれていないためメインフォームオブジェクトで、ゼロになります)。新しく作成されたオブジェクトへのポインタを返します。
以下は、指定されたサブウィンドウの現在のチャートにフォームオブジェクトを作成するメソッドです。
//+----------------------------------------------------------------------+ //| Create the form object on the current chart in a specified subwindow | //+----------------------------------------------------------------------+ CForm *CGraphElmControl::CreateForm(const int form_id,const int wnd,const string name,const int x,const int y,const int w,const int h) { return this.CreateForm(form_id,::ChartID(),wnd,name,x,y,w,h); } //+------------------------------------------------------------------+
このメソッドは、作成されたフォームオブジェクトの一意のID、チャートサブウィンドウインデックス、フォームオブジェクト名、フォームを作成するためのX / Y座標、およびフォームの幅と高さを受け取ります。メソッドは、 現在のチャートIDを明示的に示してメソッドを呼び出すための上記のフォームの操作結果を返します。
以下は、チャートのメインウィンドウで現在のチャートにフォームオブジェクトを作成するメソッドです。
//+----------------------------------------------------------------------+ //| Create the form object on the current chart in the chart main window | //+----------------------------------------------------------------------+ CForm *CGraphElmControl::CreateForm(const int form_id,const string name,const int x,const int y,const int w,const int h) { return this.CreateForm(form_id,::ChartID(),0,name,x,y,w,h); } //+------------------------------------------------------------------+
このメソッドは、作成されたフォームオブジェクトの一意のID、フォームオブジェクト名、フォームを作成するためのX / Y座標、およびフォームの幅と高さを受け取ります。このメソッドは、現在のチャートIDとチャートのメインウィンドウインデックスを明示的に示して、メソッドを呼び出す最初の形式の操作結果を返します。
フォームオブジェクトを作成するためには、ここでは他に何も必要ありません。作成されたフォームオブジェクトでさまざまなアニメーションを作成するためのすべての作業は、オブジェクトへのポインタによって実行されます。ポインタは、上記で検討したメソッドによって返されます。
すべてのCBaseObjライブラリオブジェクトの基本ブジェクトの子孫である、すべてのライブラリオブジェクトへのグラフィックス処理の統合を開始する準備が整いました。
ライブラリへのグラフィックの統合
各ライブラリオブジェクトは、グラフィカルオブジェクトのクラスを「表示」し、これらのオブジェクトを自分で作成できるようにする必要があります。これを実現するには、すべてのライブラリオブジェクトの基本ブジェクトのクラス内でグラフィカルオブジェクトを管理するためのクラスのインスタンスを宣言するだけです。そのすべての子孫には、今検討したCGraphElmControlクラスインスタンスを介して作業することでグラフィックを作成する機能がすぐに与えられます。
\MQL5\Include\DoEasy\Objects\BaseObj.mqhを開き、グラフィカルオブジェクト管理クラスのファイルをそれにインクルードします。
//+------------------------------------------------------------------+ //| BaseObj.mqh | //| Copyright 2019, MetaQuotes Software Corp. | //| https://mql5.com/en/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2019, MetaQuotes Software Corp." #property link "https://mql5.com/en/users/artmedia70" #property version "1.00" #property strict // Necessary for mql4 //+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include <Arrays\ArrayObj.mqh> #include "..\Services\DELib.mqh" #include "..\Objects\Graph\GraphElmControl.mqh" //+------------------------------------------------------------------+
CBaseObjクラスのprotectedセクションで、グラフィカルオブジェクト管理クラスオブジェクトのインスタンスを宣言します)。
//+------------------------------------------------------------------+ //| Base object class for all library objects | //+------------------------------------------------------------------+ class CBaseObj : public CObject { protected: CGraphElmControl m_graph_elm; // Instance of the class for managing graphical elements ENUM_LOG_LEVEL m_log_level; // Logging level ENUM_PROGRAM_TYPE m_program; // Program type bool m_first_start; // First launch flag bool m_use_sound; // Flag of playing the sound set for an object bool m_available; // Flag of using a descendant object in the program int m_global_error; // Global error code long m_chart_id_main; // Control program chart ID long m_chart_id; // Chart ID string m_name; // Object name string m_folder_name; // Name of the folder storing CBaseObj descendant objects string m_sound_name; // Object sound file name int m_type; // Object type (corresponds to the collection IDs) public:
Write the methods for creating a form object in the public section of the class:
//--- Return an object type virtual int Type(void) const { return this.m_type; } //--- 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) { return; } //--- Display a short description of the object in the journal virtual void PrintShort(const bool dash=false,const bool symbol=false){ return; } //--- Create a form object on a specified chart in a specified subwindow CForm *CreateForm(const int form_id,const long chart_id,const int wnd,const string name,const int x,const int y,const int w,const int h) { return this.m_graph_elm.CreateForm(form_id,chart_id,wnd,name,x,y,w,h); } //--- Create a form object on the current chart in a specified subwindow CForm *CreateForm(const int form_id,const int wnd,const string name,const int x,const int y,const int w,const int h) { return this.m_graph_elm.CreateForm(form_id,wnd,name,x,y,w,h); } //--- Create the form object on the current chart in the main window CForm *CreateForm(const int form_id,const string name,const int x,const int y,const int w,const int h) { return this.m_graph_elm.CreateForm(form_id,name,x,y,w,h); } //--- Constructor
これらのメソッドは、上記で検討したグラフィカルオブジェクト管理クラスの3つの同じ名前のメソッドの操作結果を単純化します。
これで、CBaseObjクラスの各子孫オブジェクトは、メソッドを呼び出すときにフォームオブジェクトを作成できるようになりました。
今日は、「Bar」オブジェクトクラスを使用してグラフィカルオブジェクトの処理を確認します。
クラス\MQL5\Include\DoEasy\Objects\Series\Bar.mqhのファイルを開き、SetProperties()メソッドでグラフィカルオブジェクトを管理するクラスにバーオブジェクトタイプを渡すようにします。
//+------------------------------------------------------------------+ //| Set bar object parameters | //+------------------------------------------------------------------+ void CBar::SetProperties(const MqlRates &rates) { this.SetProperty(BAR_PROP_SPREAD,rates.spread); this.SetProperty(BAR_PROP_VOLUME_TICK,rates.tick_volume); this.SetProperty(BAR_PROP_VOLUME_REAL,rates.real_volume); this.SetProperty(BAR_PROP_TIME,rates.time); this.SetProperty(BAR_PROP_TIME_YEAR,this.TimeYear()); this.SetProperty(BAR_PROP_TIME_MONTH,this.TimeMonth()); this.SetProperty(BAR_PROP_TIME_DAY_OF_YEAR,this.TimeDayOfYear()); this.SetProperty(BAR_PROP_TIME_DAY_OF_WEEK,this.TimeDayOfWeek()); this.SetProperty(BAR_PROP_TIME_DAY,this.TimeDay()); this.SetProperty(BAR_PROP_TIME_HOUR,this.TimeHour()); this.SetProperty(BAR_PROP_TIME_MINUTE,this.TimeMinute()); //--- this.SetProperty(BAR_PROP_OPEN,rates.open); this.SetProperty(BAR_PROP_HIGH,rates.high); this.SetProperty(BAR_PROP_LOW,rates.low); this.SetProperty(BAR_PROP_CLOSE,rates.close); this.SetProperty(BAR_PROP_CANDLE_SIZE,this.CandleSize()); this.SetProperty(BAR_PROP_CANDLE_SIZE_BODY,this.BodySize()); this.SetProperty(BAR_PROP_CANDLE_BODY_TOP,this.BodyHigh()); this.SetProperty(BAR_PROP_CANDLE_BODY_BOTTOM,this.BodyLow()); this.SetProperty(BAR_PROP_CANDLE_SIZE_SHADOW_UP,this.ShadowUpSize()); this.SetProperty(BAR_PROP_CANDLE_SIZE_SHADOW_DOWN,this.ShadowDownSize()); //--- this.SetProperty(BAR_PROP_TYPE,this.BodyType()); //--- Set the object type to the object of the graphical object management class this.m_graph_elm.SetTypeNode(this.m_type); } //+------------------------------------------------------------------+
ほとんどすべてがテストできる状態ですが、1つ注意点があります。グラフィカルオブジェクトの操作を開始したとき、グラフィック要素クラスを「そのまま」使用するだけで、それらをメインライブラリに含めませんでした。ここではすべてを正しく行う必要があります。すべてのライブラリオブジェクトには、そのメインオブジェクトであるCEngineクラスからアクセスできます。CEngineクラスには、すべてのオブジェクトのコレクションファイルが接続されています。ただし、すべてのオブジェクトが作成されるわけではないため、グラフィカルオブジェクトにはまだコレクションのクラスがありません。ただし、グラフィカルオブジェクトの予備的なクラスコレクションを作成することはできます。これには、すべてのオブジェクトを作成した後で戻ってきます。
これらの考慮事項を念頭に置いて、グラフィカルオブジェクトコレクションクラスの暫定バージョンを作成します。\MQL5\Include\DoEasy\Defines.mqhでグラフィカルオブジェクトコレクションリストのIDを指定します。
//--- Parameters of the chart collection timer #define COLLECTION_CHARTS_PAUSE (500) // Chart collection timer pause in milliseconds #define COLLECTION_CHARTS_COUNTER_STEP (16) // Chart timer counter increment #define COLLECTION_CHARTS_COUNTER_ID (9) // Chart timer counter ID //--- Collection list IDs #define COLLECTION_HISTORY_ID (0x777A) // Historical collection list ID #define COLLECTION_MARKET_ID (0x777B) // Market collection list ID #define COLLECTION_EVENTS_ID (0x777C) // Event collection list ID #define COLLECTION_ACCOUNT_ID (0x777D) // Account collection list ID #define COLLECTION_SYMBOLS_ID (0x777E) // Symbol collection list ID #define COLLECTION_SERIES_ID (0x777F) // Timeseries collection list ID #define COLLECTION_BUFFERS_ID (0x7780) // Indicator buffer collection list ID #define COLLECTION_INDICATORS_ID (0x7781) // Indicator collection list ID #define COLLECTION_INDICATORS_DATA_ID (0x7782) // Indicator data collection list ID #define COLLECTION_TICKSERIES_ID (0x7783) // Tick series collection list ID #define COLLECTION_MBOOKSERIES_ID (0x7784) // DOM series collection list ID #define COLLECTION_MQL5_SIGNALS_ID (0x7785) // MQL5 signals collection list ID #define COLLECTION_CHARTS_ID (0x7786) // Chart collection list ID #define COLLECTION_CHART_WND_ID (0x7787) // Chart window list ID #define COLLECTION_GRAPH_OBJ_ID (0x7788) // Graphical object collection list ID //--- Pending request type IDs
\MQL5\Include\DoEasy\Collections\ライブラリフォルダに、CGraphElementsCollectionクラスの新しいファイルGraphElementsCollection.mqhを作成します。
//+------------------------------------------------------------------+ //| GraphElementsCollection.mqh | //| Copyright 2021, MetaQuotes Ltd. | //| https://mql5.com/en/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2021, MetaQuotes Ltd." #property link "https://mql5.com/en/users/artmedia70" #property version "1.00" //+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include "ListObj.mqh" #include "..\Services\Select.mqh" #include "..\Objects\Graph\Form.mqh" //+------------------------------------------------------------------+ //| Collection of graphical objects | //+------------------------------------------------------------------+ class CGraphElementsCollection : public CBaseObj { private: CListObj m_list_all_graph_obj; // List of all 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 in the list of graphical objects bool IsPresentGraphElmInList(const int id,const ENUM_GRAPH_ELEMENT_TYPE type_obj); public: //--- Return itself CGraphElementsCollection *GetObject(void) { return &this; } //--- Return the full collection list 'as is' CArrayObj *GetList(void) { return &this.m_list_all_graph_obj; } //--- Return the list by 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.GetList(),property,value,mode); } CArrayObj *GetList(ENUM_CANV_ELEMENT_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode=EQUAL) { return CSelect::ByGraphCanvElementProperty(this.GetList(),property,value,mode); } CArrayObj *GetList(ENUM_CANV_ELEMENT_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode=EQUAL) { return CSelect::ByGraphCanvElementProperty(this.GetList(),property,value,mode); } //--- Return the number of new graphical objects int NewObjects(void) const { return this.m_delta_graph_obj; } //--- 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); }; //+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CGraphElementsCollection::CGraphElementsCollection() { ::ChartSetInteger(::ChartID(),CHART_EVENT_MOUSE_MOVE,true); ::ChartSetInteger(::ChartID(),CHART_EVENT_MOUSE_WHEEL,true); this.m_list_all_graph_obj.Sort(SORT_BY_CANV_ELEMENT_ID); this.m_list_all_graph_obj.Clear(); this.m_list_all_graph_obj.Type(COLLECTION_GRAPH_OBJ_ID); } //+------------------------------------------------------------------+
クラスの構造は、他のライブラリオブジェクトのコレクションクラスに似ています。ここで重要なのはクラスコンストラクタだけです。他のすべてのメソッドは、他のライブラリオブジェクトコレクションのメソッドと一致するので後で実装します。今は、ライブラリベースのプログラムがグラフィカルオブジェクトを表示できるように、クラスにフォームオブジェクトクラスファイルが含まれていることが重要です。現在のチャートのクラスコンストラクタを使用すると、マウスの動きとマウスホイールのスクロールイベントを追跡できます。
準備完了です。残りは、すべてのライブラリグラフィカルオブジェクトを作成した後でやります。
最後に、\MQL5\Include\DoEasy\Engine.mqhにあるCEngineライブラリのメインオブジェクトファイルにグラィカルオブジェクトコレクションクラスのファイルをインクルードします。
//+------------------------------------------------------------------+ //| Engine.mqh | //| Copyright 2020, MetaQuotes Software Corp. | //| https://mql5.com/en/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2020, MetaQuotes Software Corp." #property link "https://mql5.com/en/users/artmedia70" #property version "1.00" //+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include "Services\TimerCounter.mqh" #include "Collections\HistoryCollection.mqh" #include "Collections\MarketCollection.mqh" #include "Collections\EventsCollection.mqh" #include "Collections\AccountsCollection.mqh" #include "Collections\SymbolsCollection.mqh" #include "Collections\ResourceCollection.mqh" #include "Collections\TimeSeriesCollection.mqh" #include "Collections\BuffersCollection.mqh" #include "Collections\IndicatorsCollection.mqh" #include "Collections\TickSeriesCollection.mqh" #include "Collections\BookSeriesCollection.mqh" #include "Collections\MQLSignalsCollection.mqh" #include "Collections\ChartObjCollection.mqh" #include "Collections\GraphElementsCollection.mqh" #include "TradingControl.mqh" //+------------------------------------------------------------------+ //| Library basis class | //+------------------------------------------------------------------+ class CEngine {
これで、統合されたグラフィカルオブジェクトをバーオブジェクトクラスにテストする準備が整いました。
検証
現在の銘柄とチャート期間の時系列リストを作成しましょう。リストにはバーオブジェクトが格納されます。これらのオブジェクトは、グラフィカルオブジェクトを管理するためのクラスを備えているので、バーごとに個別のフォームオブジェクトを作成できます。
Ctrlキーを押しながらチャートの上にマウスを置くと、マウスカーソルが置かれているバーに対して、影とバータイプの説明(強気/弱気/同事)を特徴とするフォームオブジェクトが作成されるようにします。Ctrlキーを押したままにすると、マウスとホイールでチャートをスクロールする機能が無効になり、バーの説明が記載されたフォームがすぐに表示されます。Ctrlキーを離した後は、Ctrlキーの保持/解放モーメントを追跡する必要がないため、新しいティックが到着するまたはチャートをシフトすると、作成されたフォームオブジェクトのリストがクリアされます。作成されたオブジェクトのリストをクリアすることでさえ、チャートのスケールを変更するときに発生するいくつかの問題を「隠す」ためにのみ必要です。以前に作成されたフォームオブジェクトは古い場所に表示されます。つまり、変更されたチャートスケールでは現在のローソク足の位置に対応しなくなります。テストの速度のためには、グラフのスケールを変更するときには、オブジェクトの座標を再計算するよりもリストをクリアする方が簡単です。
テストを実行するには、前の記事のEAを\MQL5\Experts\TestDoEasy\Part81\でTestDoEasyPart81.mq5として保存します。
グローバルの領域では、ファイルをインクルードする代わりに、
#include <Arrays\ArrayObj.mqh>
#include <DoEasy\Services\Select.mqh>
#include <DoEasy\Objects\Graph\Form.mqh>
メインライブラリオブジェクトのファイルをインクルードしてインスタンスを宣言します。
//+------------------------------------------------------------------+ //| TestDoEasyPart81.mq5 | //| Copyright 2021, MetaQuotes Ltd. | //| https://mql5.com/en/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2021, MetaQuotes Ltd." #property link "https://mql5.com/en/users/artmedia70" #property version "1.00" //--- includes #include <DoEasy\Engine.mqh> //--- defines #define FORMS_TOTAL (4) // Number of created forms #define START_X (4) // Initial X coordinate of the shape #define START_Y (4) // Initial Y coordinate of the shape //--- input parameters sinput bool InpMovable = true; // Movable forms flag sinput ENUM_INPUT_YES_NO InpUseColorBG = INPUT_YES; // Use chart background color to calculate shadow color sinput color InpColorForm3 = clrCadetBlue; // Third form shadow color (if not background color) //--- global variables CEngine engine; CArrayObj list_forms; color array_clr[]; //+------------------------------------------------------------------+
EAのOnInit()ハンドラから、フォームオブジェクトを作成するコードを削除します。ライブラリで使用されている銘柄を指定し、現在の銘柄と期間の時系列を作成するだけです。その結果、ハンドラは次のようになります。
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- Set the permissions to send cursor movement and mouse scroll events ChartSetInteger(ChartID(),CHART_EVENT_MOUSE_MOVE,true); ChartSetInteger(ChartID(),CHART_EVENT_MOUSE_WHEEL,true); //--- Set EA global variables 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); } //+------------------------------------------------------------------+
FigureType()関数とFigureProcessing()関数をEAから削除します。これらの関数はEAコードのボリュームのほぼ全体を占領しますが、このテストでは必要ありません。
それらを3つの関数に置き換えます。
以下は、指定された名前のフォームの存在を示すフラグを返す関数です。
//+-------------------------------------------------------------------------------+ //| Return the flag that indicates the existence of a form with a specified name | //+-------------------------------------------------------------------------------+ bool IsPresentForm(const string name) { //--- In the loop by the list of form objects, for(int i=0;i<list_forms.Total();i++) { //--- get the next form object CForm *form=list_forms.At(i); if(form==NULL) continue; //--- form the desired object name as "Program name_" + the form name passed to the function string nm=MQLInfoString(MQL_PROGRAM_NAME)+"_"+name; //--- If the current form object has such a name, return 'true' if(form.NameObj()==nm) return true; } //--- Upon the loop completion, return 'false' return false; } //+------------------------------------------------------------------+
以下は、指定された名前のフォームを除くすべてのフォームを非表示にする関数です。
//+------------------------------------------------------------------+ //| Hide all forms except the one with the specified name | //+------------------------------------------------------------------+ void HideFormAllExceptOne(const string name) { //--- In the loop by the list of form objects, for(int i=0;i<list_forms.Total();i++) { //--- get the next form object CForm *form=list_forms.At(i); if(form==NULL) continue; //--- form the desired object name as "Program name_" + the form name passed to the function string nm=MQLInfoString(MQL_PROGRAM_NAME)+"_"+name; //--- If the current form object has such a name, display it, if(form.NameObj()==nm) form.Show(); //--- otherwise - hide else form.Hide(); } } //+------------------------------------------------------------------+
以下は、Ctrl押下フラグを返す関数です。
//+------------------------------------------------------------------+ //| Return the flag of holding Ctrl | //+------------------------------------------------------------------+ bool IsCtrlKeyPressed(void) { return((TerminalInfoInteger(TERMINAL_KEYSTATE_CONTROL)&0x80)!=0); } //+------------------------------------------------------------------+
すべての関数は非常にシンプルなので、説明は必要ないと思います。
OnChartEvent()ハンドラからキーストロークとオブジェクトクリックの処理を削除します。ここで必要になります。マウスの動きの処理を追加します。
//+------------------------------------------------------------------+ //| ChartEvent function | //+------------------------------------------------------------------+ void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { //--- 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 and allow scrolling a chart with the mouse list_forms.Clear(); ChartSetInteger(ChartID(),CHART_MOUSE_SCROLL,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 ChartSetInteger(ChartID(),CHART_MOUSE_SCROLL,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,76,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(); } } } //+------------------------------------------------------------------+
ハンドラコードには詳細なコメントが含まれているので、コメントですべてが明確であることを願っています。質問がある場合は、コメント欄でお気軽にお問い合わせください。
EAをコンパイルし、チャート上で起動します。Ctrlキーを押しながら、マウスをチャート上で移動します。各バーには、バータイプの説明(弱気/強気/同事)を含むフォームオブジェクトが必要です。Ctrlを離すと、作成されたすべてのオブジェクトが削除されます。
次の段階
次の記事では、引き続きグラフィカルオブジェクトをライブラリオブジェクトに統合していきます。
ライブラリの現在のバージョンのすべてのファイルは、テストおよびダウンロードできるように、MQL5のテストEAファイルと一緒に以下に添付されています。
質問や提案はコメント欄にお願いします。
**連載のこれまでの記事:
DoEasyライブラリのグラフィックス(第73部): グラフィック要素のフォームオブジェクト
DoEasyライブラリのグラフィックス(第74部): CCanvasクラスを使用した基本的グラフィック要素
DoEasyライブラリのグラフィックス(第75部): 基本的なグラフィック要素でプリミティブとテキストを処理するメソッド
DoEasyライブラリのグラフィックス(第76部): フォームオブジェクトと事前定義されたカラースキーム
DoEasyライブラリのグラフィックス(第77部): 影オブジェクトクラス
DoEasyライブラリのグラフィックス(第78部): ライブラリのアニメーションの原則イメージスライス
DoEasyライブラリのグラフィックス(第79部): 「アニメーションフレーム」オブジェクトクラスとその子孫オブジェクト
DoEasyライブラリのグラフィックス(第80部): 「幾何学的アニメーションフレーム」オブジェクトクラス
MetaQuotes Ltdによってロシア語から翻訳されました。
元の記事: https://www.mql5.com/ru/articles/9751
- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索
新しい記事「DoEasyライブラリのグラフィックス(第81部): ライブラリオブジェクトへのグラフィックの統合」はパブリッシュされました:
作者: Artyom Trishkin
全く意味が理解出来なかった自分がいます。宜しければアドバイスや指導をしていただけたら幸いです。