DoEasyライブラリのグラフィックス(第98部):拡張された標準グラフィカルオブジェクトのピボットポイントの移動
内容
概念
第93部で、複合グラフィカルオブジェクトの開発を開始しましたが、CCanvas クラスに基づくフォームオブジェクトの機能を改善する必要性に気を取られました。拡張されたされた標準グラフィカルオブジェクトに含まれるグラフィカルオブジェクトのピボットポイントコントロールを作成するために、複合グラフィカルオブジェクトのフォームオブジェクトを使用します。そのため、新しいフォームオブジェクト機能が必要でした。
前回の記事では、CCanvasクラスに基づくオブジェクトの改善を完了しました。今日は、複合グラフィカルオブジェクトで構成される、拡張された標準グラフィカルオブジェクトの開発を続けます。
この記事の目的は、新しいクラスの開発ではありません。代わりに、標準のグラフィカルオブジェクトのピボットポイントを再配置するための便利なツールを作成するために、すでに準備されている機能の改善を検討します。本稿では、複合グラフィカルオブジェクトのプロトタイプの開発について説明します。以前の記事ですでに作成しました。これは通常のトレンドラインであり、最後に追加の価格ラベルオブジェクトがあります。
今日は、トレンドラインの再配置された側に付けられた価格ラベルと一緒にトレンドラインのピボットポイントを移動する問題を扱います。ラインのピボットポイントには、再配置用のフォームオブジェクトが含まれています。オブジェクトをつかんで移動することにより、適切なトレンドライン側も移動します。カーソルがトレンドラインのピボットポイントから離れている間、フォームオブジェクトは表示されません。ただし、カーソルが特定の距離でピボットポイントに近づくと(完全に透明なフォームの領域に入るとき)、円の付いたポイントがフォームに表示されます。
トレンドラインのピボットポイントコントロールはこのようにチャートに表示されます。フォームのサイズはアクティブな領域よりも大きくなっています。フォームのアクティブな領域は、ドラッグしてフォームを移動できる領域です。対照的に、フォーム自体の領域は、マウスボタンとマウスホイールを使用して、フォームとの他の種類の対話を調整するために使用できます。
したがって、マウスカーソルがフォーム内にあるがアクティブ領域の外にある場合は、たとえば、マウスの右ボタンを押したときに複合グラフィカルオブジェクトのコンテキストメニューをアクティブ化するように実装することができます。カーソルがアクティブな領域にある場合は、コンテキストメニューに加えて、このフォームをマウスでつかんで移動できます。この場合、フォームが添付されているラインの終わりも移動します。
もちろん、これは新しい機能を試すために使用される単なるテスト複合グラフィカルオブジェクトです。複合グラフィカルオブジェクトの作成とフォームオブジェクトの処理に必要なすべてのツールが作成されたら、カスタムオブジェクトの開発に使用する標準ライブラリ複合グラフィカルオブジェクトの小さなセットを作成します。それらの作成は、この種の独自のオブジェクトを作成する方法の例と説明として役立ちます。
今のところ、私はライブラリの機能を開発し、カスタムオブジェクトを作成するために必要な「レンガ」を作成しているだけです。私の記事で説明、分析、および実装された手順は、すべてを最初から実装する必要なしに、「そのまま」使用するための基礎として提供します。
ライブラリクラスの改善
\MQL5\Include\DoEasy\Defines.mqhを開き、いくつかの変更を実装します。
ピボットポイント制御フォームには、ポイントと円が含まれます。適用された色は、以前にコードで指定されています。デフォルトの色を特徴とするマクロ置換を追加しましょう。
//--- Pending request type IDs #define PENDING_REQUEST_ID_TYPE_ERR (1) // Type of a pending request created based on the server return code #define PENDING_REQUEST_ID_TYPE_REQ (2) // Type of a pending request created by request //--- Timeseries parameters #define SERIES_DEFAULT_BARS_COUNT (1000) // Required default amount of timeseries data #define PAUSE_FOR_SYNC_ATTEMPTS (16) // Amount of pause milliseconds between synchronization attempts #define ATTEMPTS_FOR_SYNC (5) // Number of attempts to receive synchronization with the server //--- Tick series parameters #define TICKSERIES_DEFAULT_DAYS_COUNT (1) // Required number of days for tick data in default series #define TICKSERIES_MAX_DATA_TOTAL (200000) // Maximum number of stored tick data of a single symbol //--- Parameters of the DOM snapshot series #define MBOOKSERIES_DEFAULT_DAYS_COUNT (1) // The default required number of days for DOM snapshots in the series #define MBOOKSERIES_MAX_DATA_TOTAL (200000) // Maximum number of stored DOM snapshots of a single symbol //--- Canvas parameters #define PAUSE_FOR_CANV_UPDATE (16) // Canvas update frequency #define CLR_CANV_NULL (0x00FFFFFF) // Zero for the canvas with the alpha channel #define OUTER_AREA_SIZE (16) // Size of one side of the outer area around the form workspace //--- Graphical object parameters #define PROGRAM_OBJ_MAX_ID (10000) // Maximum value of an ID of a graphical object belonging to a program #define CTRL_POINT_RADIUS (5) // Radius of the control point on the form for managing graphical object pivot points #define CTRL_POINT_COLOR (clrDodgerBlue) // Radius of the control point on the form for managing graphical object pivot points #define CTRL_FORM_SIZE (40) // Size of the control point form for managing graphical object pivot points //+------------------------------------------------------------------+ //| Enumerations | //+------------------------------------------------------------------+
CTRL_POINT_SIZEマクロ置換の名前をCTRL_POINT_RADIUSに変更します。これは、円のフルサイズではなく、半径であるためです。フォームオブジェクトのアクティブ領域を計算するとき、マクロ置換名は少し誤解を招くものでした。
グラフィカル要素オブジェクトクラスのファイル\MQL5\Include\DoEasy\Objects\Graph\GCnvElement.mqhで、グラフィカル要素オブジェクトを作成するメソッドを少し改善します。残念ながら、CCanvasクラスのCreateBitmapLabel()メソッドを呼び出しても、エラーコードは返されません。したがって、メソッドを呼び出す前に、最後のエラーコードをリセットします。グラフィカルラベルの作成に失敗した場合は、操作ログにエラーコードを表示します。これにより、デバッグがわずかに改善されます。
//+------------------------------------------------------------------+ //| Create the graphical element object | //+------------------------------------------------------------------+ bool CGCnvElement::Create(const long chart_id, // Chart ID const int wnd_num, // Chart subwindow const string name, // Element name const int x, // X coordinate const int y, // Y coordinate const int w, // Width const int h, // Height const color colour, // Background color const uchar opacity, // Opacity const bool redraw=false) // Flag indicating the need to redraw { ::ResetLastError(); if(this.m_canvas.CreateBitmapLabel(chart_id,wnd_num,name,x,y,w,h,COLOR_FORMAT_ARGB_NORMALIZE)) { this.Erase(CLR_CANV_NULL); this.m_canvas.Update(redraw); this.m_shift_y=(int)::ChartGetInteger(chart_id,CHART_WINDOW_YDISTANCE,wnd_num); return true; } CMessage::ToLog(DFUN,::GetLastError(),true); return false; } //+------------------------------------------------------------------+
なぜやらなければならなかったのでしょうか。フォームオブジェクトを作成するとき、グラフィカルリソースを作成できない理由を理解するために多くの時間を費やしました。最後に、作成されたグラフィカルリソースの名前が63文字を超えていることが判明しました。CCanvasクラスのグラフィカルラベルを作成するためのメソッドでエラーが報告された場合、何も検索する必要はありません。代わりに、すぐにエラーコードを取得するので、さまざまなクラスのさまざまなメソッドを呼び出すすべてのループを渡す必要はありません。
ただし、リソース名の長さが63文字を超える場合、この改善は役に立ちません。
ERR_RESOURCE_NAME_IS_TOO_LONG 4018 The resource name exceeds 63 characters
エラーコードを返します。
ERR_RESOURCE_NOT_FOUND 4016 Resource with such a name is not found in EX5
しかし、これはすぐに、グラフィカルリソースがなぜ作成されないのかという質問を引き起こすので、まだ良い方です。
拡張された標準グラフィカルオブジェクトツールキットクラスのファイル、 \MQL5\Include\DoEasy\Objects\Graph\Extend\CGStdGraphObjExtToolkit.mqhを改善してみましょう。
各グラフィカルオブジェクトは、チャート上にオブジェクトを配置するために使用される1つまたは複数のピボットポイントを備えています。各ポイントにはフォームオブジェクトが接続されています。標準グラフィカルオブジェクトには、再配置のための独自のポイントがあり、グラフィカルオブジェクトを選択すると表示されます。ライブラリ内の拡張さえrた標準グラフィカルオブジェクトを管理するつもりはありません。この機能を実装するには、フォームオブジェクトを使用してグラフィカルオブジェクトのピボットポイントを移動する方が便利です。そのようなフォームオブジェクトはコントロールポイントより多く存在する可能性があるため、グラフィカルオブジェクトのピボットポイントの数でフォームオブジェクトの数を定義することはできません。ただし、この数を知っている必要があります。したがって、クラスのpublicセクションに、ピボットポイントを管理するために作成されたフォームオブジェクトの数を返すメソッドを追加し、完全に透過的なフォームオブジェクトにピボットポイントを管理するポイントを描画するメソッドを宣言します。
//--- (1) Set and (2) return the size of the form of pivot point management control points void SetControlFormSize(const int size); int GetControlFormSize(void) const { return this.m_ctrl_form_size; } //--- Return the pointer to the pivot point form by (1) index and (2) name CForm *GetControlPointForm(const int index) { return this.m_list_forms.At(index); } CForm *GetControlPointForm(const string name,int &index); //--- Return the number of (1) base object pivot points and (2) newly created form objects for managing reference points int GetNumPivotsBaseObj(void) const { return this.m_base_pivots; } int GetNumControlPointForms(void) const { return this.m_list_forms.Total(); } //--- Create form objects on the base object pivot points bool CreateAllControlPointForm(void); //--- Draw a reference point on the form void DrawControlPoint(CForm *form,const uchar opacity,const color clr); //--- Remove all form objects from the list void DeleteAllControlPointForm(void);
基本オブジェクトのピボットポイントにフォームオブジェクトを作成するメソッドで、作成したオブジェクトの名前を短くします—「_TKPP_」を「_CP_」に置き換えます。
//+------------------------------------------------------------------+ //| Create a form object on a base object reference point | //+------------------------------------------------------------------+ CForm *CGStdGraphObjExtToolkit::CreateNewControlPointForm(const int index) { string name=this.m_base_name+"_CP_"+(index<this.m_base_pivots ? (string)index : "X"); CForm *form=this.GetControlPointForm(index); if(form!=NULL) return NULL; int x=0, y=0; if(!this.GetControlPointCoordXY(index,x,y)) return NULL; return new CForm(this.m_base_chart_id,this.m_base_subwindow,name,x-this.m_shift,y-this.m_shift,this.GetControlFormSize(),this.GetControlFormSize()); } //+------------------------------------------------------------------+
ここ(およびテストEAファイル)では、グラフィカルリソースの名前が63文字を超えてオブジェクトが作成されなかったため、作成されたフォームオブジェクトの名前を短くする必要がありました。その理由は、CCanvasクラスの動的リソース作成メソッドにあります。作成されたリソースの名前は、「::」記号+メソッドに渡された名前(および上記のメソッドで指定された名前)+チャートID+システム起動から経過したミリ秒の数+疑似乱数で構成されます。
//+------------------------------------------------------------------+ //| Create dynamic resource | //+------------------------------------------------------------------+ bool CCanvas::Create(const string name,const int width,const int height,ENUM_COLOR_FORMAT clrfmt) { Destroy(); //--- prepare data array if(width>0 && height>0 && ArrayResize(m_pixels,width*height)>0) { //--- generate resource name m_rcname="::"+name+(string)ChartID()+(string)(GetTickCount()+MathRand()); //--- initialize data with zeros ArrayInitialize(m_pixels,0); //--- create dynamic resource if(ResourceCreate(m_rcname,m_pixels,width,height,0,0,0,clrfmt)) { //--- successfully created //--- complete initialization m_width =width; m_height=height; m_format=clrfmt; //--- succeed return(true); } } //--- error - destroy object Destroy(); return(false); } //+------------------------------------------------------------------+
残念ながら、これは、作成されたオブジェクトでわかりやすい名前を選択するのに深刻な制限となります。
基本オブジェクトのピボットポイントにフォームオブジェクトを作成するメソッドでは、フォームの中央に配置されるフォームのアクティブ領域の位置とサイズを指定するために、フォームオブジェクトの両側からインデントを計算します。 そのサイズは、CTRL_POINT_RADIUSマクロ置換で設定された2つの値と等しくなければなりません。半径を扱っているので、2つの半径値を使用し、それらをフォームの幅から減算し(フォームの高さはその幅に等しい)、取得した値を2で割って、フォームのアクティブ領域がその中心に描かれている円に等しくなるようにする必要があります。
SetActiveAreaShift()メソッドで、フォームの端からアクティブ領域の境界線のインデントを指定します。
//+------------------------------------------------------------------+ //| Create form objects on the base object pivot points | //+------------------------------------------------------------------+ bool CGStdGraphObjExtToolkit::CreateAllControlPointForm(void) { bool res=true; //--- In the loop by the number of base object pivot points for(int i=0;i<this.m_base_pivots;i++) { //--- Create a new form object on the current pivot point corresponding to the loop index CForm *form=this.CreateNewControlPointForm(i); //--- If failed to create the form, inform of that and add 'false' to the final result if(form==NULL) { CMessage::ToLog(DFUN,MSG_GRAPH_OBJ_EXT_FAILED_CREATE_CTRL_POINT_FORM); res &=false; } //--- If failed to add the form to the list, inform of that, remove the created form and add 'false' to the final result if(!this.m_list_forms.Add(form)) { CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST); delete form; res &=false; } //--- Set all the necessary properties for the created form object form.SetBelong(GRAPH_OBJ_BELONG_PROGRAM); // Object is created programmatically form.SetActive(true); // Form object is active form.SetMovable(true); // Movable object int x=(int)::floor((form.Width()-CTRL_POINT_RADIUS*2)/2);// Active area shift from the form edge form.SetActiveAreaShift(x,x,x,x); // Object active area is located in the center of the form, its size is equal to the two CTRL_POINT_RADIUS values form.SetFlagSelected(false,false); // Object is not selected form.SetFlagSelectable(false,false); // Object cannot be selected by mouse form.Erase(CLR_CANV_NULL,0); // Fill in the form with transparent color and set the full transparency //form.DrawRectangle(0,0,form.Width()-1,form.Height()-1,clrSilver); // Draw an outlining rectangle for visual display of the form location //form.DrawRectangle(x,x,form.Width()-x-1,form.Height()-x-1,clrSilver);// Draw an outlining rectangle for visual display of the form active area location this.DrawControlPoint(form,0,CTRL_POINT_COLOR); // Draw a circle and a point in the form center form.Done(); // Save the initial form object state (its appearance) } //--- Redraw the chart for displaying changes (if successful) and return the final result if(res) ::ChartRedraw(this.m_base_chart_id); return res; } //+------------------------------------------------------------------+
フォームを作成するときに、フォームのサイズとデバッグ用のアクティブな領域を表示する長方形を描画できます— これらの文字列はコメント化されています。以下で検討する新しいメソッドを使用して、フォームの中央に完全に透明な円を描画します(ただし、それらを描画することをお勧めしますか?)。
以下は、フォームに参照点を描画するメソッドです。
//+------------------------------------------------------------------+ //| Draw a reference point on the form | //+------------------------------------------------------------------+ void CGStdGraphObjExtToolkit::DrawControlPoint(CForm *form,const uchar opacity,const color clr) { if(form==NULL) return; form.DrawCircle((int)::floor(form.Width()/2),(int)::floor(form.Height()/2),CTRL_POINT_RADIUS,clr,opacity);// Draw a circle at the center of the form form.DrawCircleFill((int)::floor(form.Width()/2),(int)::floor(form.Height()/2),2,clr,opacity); // Draw a point at the center of the form } //+------------------------------------------------------------------+
このメソッドには、上記で検討したメソッドから取得した2つの文字列が含まれています。なぜでしょうか。フォームの中央点はさまざまな時点で円で表示または非表示にする必要があります。これを実現するためには、描画する図形の必要な非透明度と色を指定してメソッドを呼び出します。
不要になったため、マウスカーソル移動の処理をイベントハンドラから削除します。
//+------------------------------------------------------------------+ //| Event handler | //+------------------------------------------------------------------+ void CGStdGraphObjExtToolkit::OnChartEvent(const int id,const long& lparam,const double& dparam,const string& sparam) { if(id==CHARTEVENT_CHART_CHANGE) { for(int i=0;i<this.m_list_forms.Total();i++) { CForm *form=this.m_list_forms.At(i); if(form==NULL) continue; int x=0, y=0; if(!this.GetControlPointCoordXY(i,x,y)) continue; form.SetCoordX(x-this.m_shift); form.SetCoordY(y-this.m_shift); form.Update(); } ::ChartRedraw(this.m_base_chart_id); } if(id==CHARTEVENT_MOUSE_MOVE) { for(int i=0;i<this.m_list_forms.Total();i++) { CForm *form=this.m_list_forms.At(i); if(form==NULL) continue; form.OnChartEvent(id,lparam,dparam,sparam); } ::ChartRedraw(this.m_base_chart_id); } } //+------------------------------------------------------------------+
\MQL5\Include\DoEasy\Objects\Graph\Standard\GStdGraphObj.mqhの抽象標準グラフィカルオブジェクトクラスを改善します。
クラスのpublicセクションで、グラフィカルオブジェクトのピボットポイントとそれにバインドされているオブジェクトの座標を同時に変更できるようにするメソッドを宣言します。
//--- Return (1) the list of dependent objects, (2) dependent graphical object by index and (3) the number of dependent objects CArrayObj *GetListDependentObj(void) { return &this.m_list; } CGStdGraphObj *GetDependentObj(const int index) { return this.m_list.At(index); } int GetNumDependentObj(void) { return this.m_list.Total(); } //--- Return the name of the dependent object by index string NameDependent(const int index); //--- Add the dependent graphical object to the list bool AddDependentObj(CGStdGraphObj *obj); //--- Change X and Y coordinates of the current and all dependent objects bool ChangeCoordsExtendedObj(const int x,const int y,const int modifier,bool redraw=false); //--- Return the object of data on pivot points CLinkedPivotPoint*GetLinkedPivotPoint(void) { return &this.m_linked_pivots; }
グラフィカルオブジェクトのピボットポイントを管理するためのフォームにアクセスするための3つのメソッドを宣言します。
//--- Return the number of base object pivot points for calculating the coordinates in the (1) current and (2) specified object int GetLinkedCoordsNum(void) const { return this.m_linked_pivots.GetNumLinkedCoords(); } int GetLinkedPivotsNum(CGStdGraphObj *obj) const { return(obj!=NULL ? obj.GetLinkedCoordsNum() : 0); } //--- Return the form for managing an object pivot point CForm *GetControlPointForm(const int index); //--- Return the number of form objects for managing reference points int GetNumControlPointForms(void); //--- Redraw the form for managing a reference point of an extended standard graphical object void RedrawControlPointForms(const uchar opacity,const color clr); private:
画面座標で時間と価格を設定するメソッドを追加します。
//--- Symbol for the Chart object string ChartObjSymbol(void) const { return this.GetProperty(GRAPH_OBJ_PROP_CHART_OBJ_SYMBOL,0); } bool SetChartObjSymbol(const string symbol) { if(!::ObjectSetString(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_SYMBOL,symbol)) return false; this.SetProperty(GRAPH_OBJ_PROP_CHART_OBJ_SYMBOL,0,symbol); return true; } //--- Set the time and price by screen coordinates bool SetTimePrice(const int x,const int y,const int modifier) { bool res=true; ENUM_OBJECT type=this.GraphObjectType(); if(type==OBJ_LABEL || type==OBJ_BUTTON || type==OBJ_BITMAP_LABEL || type==OBJ_EDIT || type==OBJ_RECTANGLE_LABEL) { res &=this.SetXDistance(x); res &=this.SetYDistance(y); } else { int subwnd=0; datetime time=0; double price=0; if(::ChartXYToTimePrice(this.ChartID(),x,y,subwnd,time,price)) { res &=this.SetTime(time,modifier); res &=this.SetPrice(price,modifier); } } return res; } //--- Return the flags indicating object visibility on timeframes
画面のXY座標でグラフィカルオブジェクトを処理できるようにするには、画面座標を時間/価格座標に再計算する必要があります。このメソッドは、まず現在のオブジェクトタイプをチェックします。画面座標に基づいて構築されている場合、画面座標はすぐに変更されます。グラフィカルオブジェクトが時間/価格座標に基づいている場合、まずメソッドに渡された画面座標を時間と価格の値に変換する必要があります。取得した値は、グラフィカルオブジェクトパラメータに設定されます。
以下は、オブジェクトのピボットポイントを管理するためのフォームを返すメソッドです。
//+------------------------------------------------------------------+ //| Return the form for managing an object pivot point | //+------------------------------------------------------------------+ CForm *CGStdGraphObj::GetControlPointForm(const int index) { return(this.ExtToolkit!=NULL ? this.ExtToolkit.GetControlPointForm(index) : NULL); } //+------------------------------------------------------------------+
ここではすべてが簡単です。 拡張標準グラフィカルオブジェクトツールキットのオブジェクトが存在する場合、インデックスによってフォームオブジェクトが返されます。それ以外の場合、NULLを返します。
以下は、参照点を管理するためのフォームオブジェクトの数を返すメソッドです。
//+------------------------------------------------------------------+ //| Return the number of form objects | //| for managing control points | //+------------------------------------------------------------------+ int CGStdGraphObj::GetNumControlPointForms(void) { return(this.ExtToolkit!=NULL ? this.ExtToolkit.GetNumControlPointForms() : 0); } //+------------------------------------------------------------------+
このメソッドは、上記のメソッドと似ています。拡張された標準グラフィカルオブジェクトツールキットのオブジェクトが存在する場合,、フォームオブジェクトの数が返されます。それ以外の場合、0を返します。
以下は、拡張された標準グラフィカルオブジェクトの参照点を管理するためにフォームを再描画するメソッドです。
//+------------------------------------------------------------------+ //| Redraw the form for managing the control point | //| of the extended standard graphical object | //+------------------------------------------------------------------+ void CGStdGraphObj::RedrawControlPointForms(const uchar opacity,const color clr) { //--- If the object has no extended standard graphical object toolkit, exit if(this.ExtToolkit==NULL) return; //--- Get the number of pivot point handling forms int total_form=this.GetNumControlPointForms(); //--- In the loop by the number of pivot point management forms for(int i=0;i<total_form;i++) { //--- get the next form object CForm *form=this.ExtToolkit.GetControlPointForm(i); if(form==NULL) continue; //--- draw a point and a circle with the specified non-transparency and color this.ExtToolkit.DrawControlPoint(form,opacity,clr); } //--- Get the total number of bound graphical objects int total_dep=this.GetNumDependentObj(); //--- In the loop by all bound graphical objects, for(int i=0;i<total_dep;i++) { //--- get the next graphical object from the list CGStdGraphObj *dep=this.GetDependentObj(i); if(dep==NULL) continue; //--- call the method for it dep.RedrawControlPointForms(opacity,clr); } } //+------------------------------------------------------------------+
メソッドには詳細なコメントが付いています。簡単に言えば、アイデアは次のとおりです。まず、現在のオブジェクトにバインドされているすべてのフォームオブジェクトを再描画します。ご存知のように、依存するグラフィカルオブジェクトをオブジェクトに接続できます。これらの依存オブジェクトには、独自のフォームオブジェクトもあります。すべての依存オブジェクトによって、ループ内の依存オブジェクトごとにこのメソッドを呼び出しましょう。次に、依存オブジェクトで、それら自身の依存オブジェクトのリストをループしそれらに対して同じメソッドを呼び出します。これは、リンクされているすべてのグラフィカルオブジェクトのすべてのフォームオブジェクトが再描画されるまでおこなわれます。
以下は、現在のオブジェクトとすべての依存オブジェクトのX座標とY座標を変更するメソッドです。
//+----------------------------------------------------------------------+ //| Change X and Y coordinates of the current and all dependent objects | //+----------------------------------------------------------------------+ bool CGStdGraphObj::ChangeCoordsExtendedObj(const int x,const int y,const int modifier,bool redraw=false) { //--- Set the new coordinates for the pivot point set in 'modifier' if(!this.SetTimePrice(x,y,modifier)) return false; //--- If the object is not a composite graphical object //--- or dependent graphical objects are not attached to the object, //--- there is nothing we can do here, return 'true' if(this.ExtToolkit==NULL || this.m_list.Total()==0) return true; //--- Get the graphical object bound to the 'modifier' point CGStdGraphObj *dep=this.GetDependentObj(modifier); if(dep==NULL) return false; //--- Get the object of the pivot point data of the attached graphical object CLinkedPivotPoint *pp=dep.GetLinkedPivotPoint(); if(pp==NULL) return false; //--- get the number of coordinate points the object is attached to int num=pp.GetNumLinkedCoords(); //--- In the loop by the object coordinate points, for(int j=0;j<num;j++) { //--- get the number of coordinate points of the base object for setting the X coordinate int numx=pp.GetBasePivotsNumX(j); //--- In the loop by each coordinate point for setting the X coordinate, for(int nx=0;nx<numx;nx++) { //--- get the property for setting the X coordinate, its modifier //--- and set it to the dependent graphical object attached to the 'modifier' point int prop_from=pp.GetPropertyX(j,nx); int modifier_from=pp.GetPropertyModifierX(j,nx); this.SetCoordXToDependentObj(dep,prop_from,modifier_from,nx); } //--- Get the number of coordinate points of the base object for setting the Y coordinate int numy=pp.GetBasePivotsNumY(j); //--- In the loop by each coordinate point for setting the Y coordinate, for(int ny=0;ny<numy;ny++) { //--- get the property for setting the Y coordinate, its modifier //--- and set it to the dependent graphical object attached to the 'modifier' point int prop_from=pp.GetPropertyY(j,ny); int modifier_from=pp.GetPropertyModifierY(j,ny); this.SetCoordYToDependentObj(dep,prop_from,modifier_from,ny); } } //--- Save the current properties of the dependent graphical object as the previous ones dep.PropertiesCopyToPrevData(); //--- Move the management control point to new coordinates this.ExtToolkit.SetBaseObjTimePrice(this.Time(modifier),this.Price(modifier),modifier); this.ExtToolkit.SetBaseObjCoordXY(this.XDistance(),this.YDistance()); //--- Redraw the chart, if the flag is enabled if(redraw) ::ChartRedraw(m_chart_id); //--- If all is successful, return 'true' return true; } //+------------------------------------------------------------------+
メソッドには詳細なコメントが付いています。つまり、まず、現在のオブジェクトの指定されたピボットポイントの座標を変更します。オブジェクトに依存するグラフィカルオブジェクトがバインドされている場合は、新しい座標に移動します。これはまさにメソッドでおこなわれていることです。
不要になったため、マウス移動の処理をイベントハンドラから削除します。
//+------------------------------------------------------------------+ //| Event handler | //+------------------------------------------------------------------+ void CGStdGraphObj::OnChartEvent(const int id,const long &lparam,const double &dparam,const string &sparam) { if(GraphElementType()!=GRAPH_ELEMENT_TYPE_STANDARD_EXTENDED) return; string name=this.Name(); if(id==CHARTEVENT_CHART_CHANGE) { if(ExtToolkit==NULL) return; for(int i=0;i<this.Pivots();i++) { ExtToolkit.SetBaseObjTimePrice(this.Time(i),this.Price(i),i); } ExtToolkit.SetBaseObjCoordXY(this.XDistance(),this.YDistance()); ExtToolkit.OnChartEvent(id,lparam,dparam,name); } if(id==CHARTEVENT_MOUSE_MOVE) { if(ExtToolkit!=NULL) ExtToolkit.OnChartEvent(id,lparam,dparam,name); } } //+------------------------------------------------------------------+
\MQL5\Include\DoEasy\Collections\GraphElementsCollection.mqhでグラフィカル要素のコレクションクラスを改善しましょう。
チャート上にカーソルを移動すると、チャートにはオブジェクトが表示されますが、マウスカーソルが合わせられているオブジェクトはイベントハンドラで定義されます。次に、オブジェクトに関連するマウスステータスのハンドラが起動されます。拡張された標準グラフィカルオブジェクトのピボットポイントを処理するためにオブジェクトにカーソルを合わせると、フォーム自体、およびフォームが接続されているインデックスとグラフィカルオブジェクトがハンドラに表示されます。このフォームとグラフィカルオブジェクトをハンドラの外部で再度検索しないようにするには、フォームが接続されているグラフィカルオブジェクトのIDと、カーソルが合わせられているフォームのインデックスを変数に保存する必要があります。このデータにより、リストから必要なオブジェクトをすばやく選択し、これらの変数の値によって、カーソルがフォームの上にあることを理解できます。
メソッドの宣言にこれらの変数を挿入 して、カーソルの下にあるフォームへのポインタを返します。
//--- Return the pointer to the form located under the cursor CForm *GetFormUnderCursor(const int id, const long &lparam, const double &dparam, const string &sparam, ENUM_MOUSE_FORM_STATE &mouse_state, long &obj_ext_id, int &form_index);
変数はリンクによってメソッドに渡されます。つまり、メソッド内に必要な値を設定するだけで、後で使用できるような適切な変数に保存されます。
クラスのpublicセクションで、標準および拡張グラフィカルオブジェクトのリストを返す2つのメソッドを宣言します。
//--- Return an (1) existing and (2) removed graphical object by chart name and ID CGStdGraphObj *GetStdGraphObject(const string name,const long chart_id); CGStdGraphObj *GetStdDelGraphObject(const string name,const long chart_id); //--- Return the existing (1) extended and (2) standard graphical object by its ID CGStdGraphObj *GetStdGraphObjectExt(const long id,const long chart_id); CGStdGraphObj *GetStdGraphObject(const long id,const long chart_id); //--- Return the list of (1) chart management objects and (2) removed graphical objects CArrayObj *GetListChartsControl(void) { return &this.m_list_charts_control; } CArrayObj *GetListDeletedObj(void) { return &this.m_list_deleted_obj; }
これらのメソッドは、IDでグラフィカルオブジェクトへのポインタを受け取るために必要です。これらのメソッドの実装を見てみましょう。
以下は、IDによって既存の拡張された標準グラフィカルオブジェクトを返すメソッドです。
//+------------------------------------------------------------------+ //| Return the existing extended standard | //| graphical object by its ID | //+------------------------------------------------------------------+ CGStdGraphObj *CGraphElementsCollection::GetStdGraphObjectExt(const long id,const long chart_id) { CArrayObj *list=this.GetListStdGraphObjectExt(); list=CSelect::ByGraphicStdObjectProperty(list,GRAPH_OBJ_PROP_CHART_ID,0,chart_id,EQUAL); list=CSelect::ByGraphicStdObjectProperty(list,GRAPH_OBJ_PROP_ID,0,id,EQUAL); return(list!=NULL && list.Total()>0 ? list.At(0) : NULL); } //+------------------------------------------------------------------+
ここに、拡張された標準グラフィカルオブジェクトのリストがあります。リストには指定されたチャートIDを持つオブジェクトのみが残ります。
取得したリストから、指定したオブジェクトIDを持つオブジェクトを選択します。取得したリストが有効で空でない場合、リストに含まれている必要なオブジェクトへのポインタを返します。それ以外の場合、NULLを返します。
以下は、IDによって既存の標準グラフィカルオブジェクトを返すメソッドです。
//+------------------------------------------------------------------+ //| Return the existing standard | //| graphical object by its ID | //+------------------------------------------------------------------+ CGStdGraphObj *CGraphElementsCollection::GetStdGraphObject(const long id,const long chart_id) { CArrayObj *list=this.GetList(GRAPH_OBJ_PROP_CHART_ID,0,chart_id); list=CSelect::ByGraphicStdObjectProperty(list,GRAPH_OBJ_PROP_ID,0,id,EQUAL); return(list!=NULL && list.Total()>0 ? list.At(0) : NULL); } //+------------------------------------------------------------------+
ここでは、チャートIDごとのグラフィカルオブジェクトのリストを受け取ります。取得したリストに、指定したオブジェクトIDのオブジェクトを残します。
取得したリストが有効で空でない場合、リストに含まれている必要なオブジェクトへのポインタを返します。それ以外の場合、NULLを返します。
カーソルの下にあるフォームへのポインタを返すメソッドを改善しましょう。拡張された標準グラフィカルオブジェクトのIDとフォームが処理するアンカーポイントのインデックスを格納するための2つの新しい変数を追加して初期化し、拡張された標準グラフィカルオブジェクトを処理するためのブロックを挿入します。これらのオブジェクトに添付されている(およびマウスカーソルが合わせられている)フォームの検索は次の通りです。
//+------------------------------------------------------------------+ //| Return the pointer to the form located under the cursor | //+------------------------------------------------------------------+ CForm *CGraphElementsCollection::GetFormUnderCursor(const int id, const long &lparam, const double &dparam, const string &sparam, ENUM_MOUSE_FORM_STATE &mouse_state, long &obj_ext_id, int &form_index) { //--- Set the ID of the extended standard graphical object to -1 //--- and the index of the anchor point managed by the form to -1 obj_ext_id=WRONG_VALUE; form_index=WRONG_VALUE; //--- Initialize the mouse status relative to the form mouse_state=MOUSE_FORM_STATE_NONE; //--- Declare the pointers to graphical element collection class objects CGCnvElement *elm=NULL; CForm *form=NULL; //--- Get the list of objects the interaction flag is set for (there should be only one object) CArrayObj *list=CSelect::ByGraphCanvElementProperty(GetListCanvElm(),CANV_ELEMENT_PROP_INTERACTION,true,EQUAL); //--- If managed to obtain the list and it is not empty if(list!=NULL && list.Total()>0) { //--- Get the only graphical element there elm=list.At(0); //--- If the element is a form object if(elm.TypeGraphElement()==GRAPH_ELEMENT_TYPE_FORM) { //--- Assign the pointer to the element for the form object pointer form=elm; //--- Get the mouse status relative to the form mouse_state=form.MouseFormState(id,lparam,dparam,sparam); //--- If the cursor is within the form, return the pointer to the form if(mouse_state>MOUSE_FORM_STATE_OUTSIDE_FORM_WHEEL) return form; } } //--- If there is no a single form object with a specified interaction flag, //--- in the loop by all graphical element collection class objects int total=this.m_list_all_canv_elm_obj.Total(); for(int i=0;i<total;i++) { //--- get the next element elm=this.m_list_all_canv_elm_obj.At(i); if(elm==NULL) continue; //--- if the obtained element is a form object if(elm.TypeGraphElement()==GRAPH_ELEMENT_TYPE_FORM) { //--- Assign the pointer to the element for the form object pointer form=elm; //--- Get the mouse status relative to the form mouse_state=form.MouseFormState(id,lparam,dparam,sparam); //--- If the cursor is within the form, return the pointer to the form if(mouse_state>MOUSE_FORM_STATE_OUTSIDE_FORM_WHEEL) return form; } } //--- If there is no a single form object from the collection list //--- Get the list of extended standard graphical objects list=this.GetListStdGraphObjectExt(); if(list!=NULL) { //--- in the loop by all extended standard graphical objects for(int i=0;i<list.Total();i++) { //--- get the next graphical object, CGStdGraphObj *obj_ext=list.At(i); if(obj_ext==NULL) continue; //--- get the object of its toolkit, CGStdGraphObjExtToolkit *toolkit=obj_ext.GetExtToolkit(); if(toolkit==NULL) continue; //--- handle the event of changing the chart for the current graphical object obj_ext.OnChartEvent(CHARTEVENT_CHART_CHANGE,lparam,dparam,sparam); //--- Get the total number of form objects created for the current graphical object total=toolkit.GetNumControlPointForms(); //--- In the loop by all form objects for(int j=0;j<total;j++) { //--- get the next form object, form=toolkit.GetControlPointForm(j); if(form==NULL) continue; //--- get the mouse status relative to the form mouse_state=form.MouseFormState(id,lparam,dparam,sparam); //--- If the cursor is inside the form, if(mouse_state>MOUSE_FORM_STATE_OUTSIDE_FORM_WHEEL) { //--- set the object ID and form index //--- and return the pointer to the form obj_ext_id=obj_ext.ObjectID(); form_index=j; return form; } } } } //--- Nothing is found - return NULL return NULL; } //+------------------------------------------------------------------+
追加されたコードブロックのロジックはすべてコメントで説明されています。簡単に言うと、マウスカーソルが合わせられているフォームオブジェクトを見つける必要があります。まず、コレクションクラスのグラフィカル要素のリストに格納されているフォームオブジェクトを見つけます。単一のフォームが見つからない場合は、フォームを検索するときにすべての拡張標準グラフィカルオブジェクトを渡す必要があります— カーソルをそれらの1つに置くことができます。この場合、フォームが接続されている拡張された標準グラフィカルオブジェクトのIDは、リンクによってメソッドに渡される変数に設定されます。フォームインデックスも設定されているため、フォームによって管理されるピボットポイントとグラフィカルオブジェクトを知ることができます。
次に、イベントハンドラーで、マウスカーソルと拡張された標準グラフィカルオブジェクトのフォームオブジェクトとの相互作用を処理する必要があります。さらに、フォームオブジェクトの再配置に制御を実装して、ワンクリック取引モードのアクティブ化ボタンが配置されている右上隅のチャート領域に入らないようにします。このボタンは常にすべてのオブジェクトの上に表示されているため、ボタンが誤ってクリックされるのを防ぐために、再配置されたフォームがその領域に入らないようにする必要があります。ワンクリックパネルがすでにアクティブになっている場合は、フォームの処理時に不便を引き起こす可能性があるため、フォームがその領域に入らないようにします。
イベントハンドラの改善点と変更点を見てみましょう。
//+------------------------------------------------------------------+ //| Event handler | //+------------------------------------------------------------------+ void CGraphElementsCollection::OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { CGStdGraphObj *obj_std=NULL; // Pointer to the standard graphical object CGCnvElement *obj_cnv=NULL; // Pointer to the graphical element object on canvas ushort idx=ushort(id-CHARTEVENT_CUSTOM); if(id==CHARTEVENT_OBJECT_CHANGE || id==CHARTEVENT_OBJECT_DRAG || id==CHARTEVENT_OBJECT_CLICK || idx==CHARTEVENT_OBJECT_CHANGE || idx==CHARTEVENT_OBJECT_DRAG || idx==CHARTEVENT_OBJECT_CLICK) { //--- Calculate the chart ID //--- If the event ID corresponds to an event from the current chart, the chart ID is received from ChartID //--- If the event ID corresponds to a user event, the chart ID is received from lparam //--- Otherwise, the chart ID is assigned to -1 long param=(id==CHARTEVENT_OBJECT_CLICK ? ::ChartID() : idx==CHARTEVENT_OBJECT_CLICK ? lparam : WRONG_VALUE); long chart_id=(param==WRONG_VALUE ? (lparam==0 ? ::ChartID() : lparam) : param); //--- Get the object, whose properties were changed or which was relocated, //--- from the collection list by its name set in sparam obj_std=this.GetStdGraphObject(sparam,chart_id); //--- If failed to get the object by its name, it is not on the list, //--- which means its name has been changed if(obj_std==NULL) { //--- Let's search the list for the object that is not on the chart obj_std=this.FindMissingObj(chart_id); //--- If failed to find the object here as well, exit if(obj_std==NULL) return; //--- Get the name of the renamed graphical object on the chart, which is not in the collection list string name_new=this.FindExtraObj(chart_id); //--- set a new name for the collection list object, which does not correspond to any graphical object on the chart, //--- and send an event with the new name of the object to the control program chart if(obj_std.SetNamePrev(obj_std.Name()) && obj_std.SetName(name_new)) ::EventChartCustom(this.m_chart_id_main,GRAPH_OBJ_EVENT_RENAME,obj_std.ChartID(),obj_std.TimeCreate(),obj_std.Name()); } //--- Update the properties of the obtained object //--- and check their change obj_std.PropertiesRefresh(); obj_std.PropertiesCheckChanged(); } //--- Handle standard graphical object events in the collection list for(int i=0;i<this.m_list_all_graph_obj.Total();i++) { //--- Get the next graphical object and obj_std=this.m_list_all_graph_obj.At(i); if(obj_std==NULL) continue; //--- call its event handler obj_std.OnChartEvent((id<CHARTEVENT_CUSTOM ? id : idx),lparam,dparam,sparam); } //--- Handle chart changes for extended standard objects if(id==CHARTEVENT_CHART_CHANGE || idx==CHARTEVENT_CHART_CHANGE) { CArrayObj *list=this.GetListStdGraphObjectExt(); if(list!=NULL) { for(int i=0;i<list.Total();i++) { obj_std=list.At(i); if(obj_std==NULL) continue; obj_std.OnChartEvent(CHARTEVENT_CHART_CHANGE,lparam,dparam,sparam); } } } //--- Handling mouse events of graphical objects on canvas //--- If the event is not a chart change else { //--- Check whether the mouse button is pressed bool pressed=(this.m_mouse.ButtonKeyState(id,lparam,dparam,sparam)==MOUSE_BUTT_KEY_STATE_LEFT ? true : false); ENUM_MOUSE_FORM_STATE mouse_state=MOUSE_FORM_STATE_NONE; //--- Declare static variables for the active form and status flags static CForm *form=NULL; static bool pressed_chart=false; static bool pressed_form=false; static bool move=false; //--- Declare static variables for the form index of managing an extended standard graphical object and its ID static int form_index=WRONG_VALUE; static long graph_obj_id=WRONG_VALUE; //--- If the button is not pressed on the chart and the movement flag is not set, get the form, above which the cursor is located if(!pressed_chart && !move) form=this.GetFormUnderCursor(id,lparam,dparam,sparam,mouse_state,graph_obj_id,form_index); //--- If the button is not pressed, reset all flags and enable the chart tools if(!pressed) { pressed_chart=false; pressed_form=false; move=false; this.SetChartTools(::ChartID(),true); } //--- If this is a mouse movement event and the movement flag is active, move the form, above which the cursor is located (if the pointer to it is valid) if(id==CHARTEVENT_MOUSE_MOVE && move) { if(form!=NULL) { //--- calculate the cursor movement relative to the form coordinate origin int x=this.m_mouse.CoordX()-form.OffsetX(); int y=this.m_mouse.CoordY()-form.OffsetY(); //--- get the width and height of the chart the form is located at int chart_width=(int)::ChartGetInteger(form.ChartID(),CHART_WIDTH_IN_PIXELS,form.SubWindow()); int chart_height=(int)::ChartGetInteger(form.ChartID(),CHART_HEIGHT_IN_PIXELS,form.SubWindow()); //--- If the form is outside the extended standard graphical object if(form_index==WRONG_VALUE) { //--- Adjust the calculated form coordinates if the form is out of the chart range if(x<0) x=0; if(x>chart_width-form.Width()) x=chart_width-form.Width(); if(y<0) y=0; if(y>chart_height-form.Height()) y=chart_height-form.Height(); //--- If the chart has no one-click trading panel, if(!::ChartGetInteger(form.ChartID(),CHART_SHOW_ONE_CLICK)) { //--- calculate the form coordinate so that the form does not enter the one-click trading button during relocation if(y<17 && x<41) y=17; } //--- If the chart has the one-click trading panel enabled, else { //--- calculate the form coordinate so that the form does not enter the one-click trading panel area during relocation if(y<80 && x<192) y=80; } } //--- If the form is included into the extended standard graphical object else { if(graph_obj_id>WRONG_VALUE) { //--- Get the list of objects by the object ID (there should be one object) CArrayObj *list_ext=CSelect::ByGraphicStdObjectProperty(GetListStdGraphObjectExt(),GRAPH_OBJ_PROP_ID,0,graph_obj_id,EQUAL); //--- If managed to obtain the list and it is not empty, if(list_ext!=NULL && list_ext.Total()>0) { //--- get the graphical object from the list CGStdGraphObj *ext=list_ext.At(0); //--- If the pointer to the object is received, if(ext!=NULL) { //--- get the object type ENUM_OBJECT type=ext.GraphObjectType(); //--- If the object is constructed based on screen coordinates, set the coordinates to the object if(type==OBJ_LABEL || type==OBJ_BUTTON || type==OBJ_BITMAP_LABEL || type==OBJ_EDIT || type==OBJ_RECTANGLE_LABEL) { ext.SetXDistance(x); ext.SetYDistance(y); } //--- otherwise, if the object is based on time/price coordinates else { //--- calculate the coordinate shift and limit the coordinates so that they are not out of the chart range int shift=(int)::ceil(form.Width()/2)+1; if(x+shift<0) x=-shift; if(x+shift>chart_width) x=chart_width-shift; if(y+shift<0) y=-shift; if(y+shift>chart_height) y=chart_height-shift; //--- set calculated coordinates in the object ext.ChangeCoordsExtendedObj(x+shift,y+shift,form_index); } } } } } //--- Move the form by the obtained coordinates form.Move(x,y,true); } } //--- Display debugging comments on the chart Comment ( (form!=NULL ? form.Name()+":" : ""),"\n", EnumToString((ENUM_CHART_EVENT)id),"\n", EnumToString(this.m_mouse.ButtonKeyState(id,lparam,dparam,sparam)), "\n",EnumToString(mouse_state), "\npressed=",pressed,", move=",move,(form!=NULL ? ", Interaction="+(string)form.Interaction() : ""), "\npressed_chart=",pressed_chart,", pressed_form=",pressed_form, "\nform_index=",form_index,", graph_obj_id=",graph_obj_id ); //--- If the cursor is not above the form if(form==NULL) { //--- If the mouse button is pressed if(pressed) { //--- If the button is still pressed and held on the form, exit if(pressed_form) { return; } //--- If the button hold flag is not enabled yet, set the flags and enable chart tools if(!pressed_chart) { pressed_chart=true; // Button is held on the chart pressed_form=false; // Cursor is not above the form move=false; // movement disabled this.SetChartTools(::ChartID(),true); } } //--- If the mouse button is not pressed else { //--- Get the list of extended standard graphical objects CArrayObj *list_ext=GetListStdGraphObjectExt(); //--- In the loop by all extended graphical objects, int total=list_ext.Total(); for(int i=0;i<total;i++) { //--- get the next graphical object CGStdGraphObj *obj=list_ext.At(i); if(obj==NULL) continue; //--- and redraw it without a point and a circle obj.RedrawControlPointForms(0,CTRL_POINT_COLOR); } } } //--- If the cursor is above the form else { //--- If the button is still pressed and held on the chart, exit if(pressed_chart) { return; } //--- If the flag of holding the button on the form is not set yet if(!pressed_form) { pressed_chart=false; // The button is not pressed on the chart this.SetChartTools(::ChartID(),false); //--- 'The cursor is inside the form, no mouse buttons are clicked' event handler if(mouse_state==MOUSE_FORM_STATE_INSIDE_FORM_NOT_PRESSED) { //--- If the cursor is above the extended graphical object pivot point control form, if(graph_obj_id>WRONG_VALUE) { //--- get the object by the object and chart IDs CGStdGraphObj *graph_obj=this.GetStdGraphObjectExt(graph_obj_id,form.ChartID()); if(graph_obj!=NULL) { //--- Get the toolkit of the extended standard graphical object CGStdGraphObjExtToolkit *toolkit=graph_obj.GetExtToolkit(); if(toolkit!=NULL) { //--- Draw a circle with a point on the form toolkit.DrawControlPoint(form,255,CTRL_POINT_COLOR); } } } } //--- 'The cursor is inside the form, a mouse button is clicked (any)' event handler if(mouse_state==MOUSE_FORM_STATE_INSIDE_FORM_PRESSED) { this.SetChartTools(::ChartID(),false); //--- If the flag of holding the form is not set yet if(!pressed_form) { pressed_form=true; // set the flag of pressing on the form pressed_chart=false; // remove the flag of pressing on the chart } } //--- 'The cursor is inside the form, the mouse wheel is being scrolled' event handler workpiece if(mouse_state==MOUSE_FORM_STATE_INSIDE_FORM_WHEEL) { } //--- 'The cursor is inside the active area, the mouse buttons are not clicked' event handler if(mouse_state==MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_NOT_PRESSED) { //--- Set the cursor shift relative to the form initial coordinates form.SetOffsetX(this.m_mouse.CoordX()-form.CoordX()); form.SetOffsetY(this.m_mouse.CoordY()-form.CoordY()); //--- If the cursor is above the active area of the extended graphical object pivot point control form if(graph_obj_id>WRONG_VALUE) { //--- get the object by the object and chart IDs CGStdGraphObj *graph_obj=this.GetStdGraphObjectExt(graph_obj_id,form.ChartID()); if(graph_obj!=NULL) { //--- Get the toolkit of the extended standard graphical object CGStdGraphObjExtToolkit *toolkit=graph_obj.GetExtToolkit(); if(toolkit!=NULL) { //--- Draw a circle with a point on the form toolkit.DrawControlPoint(form,255,CTRL_POINT_COLOR); } } } } //--- 'The cursor is inside the active area, any mouse button is clicked' event handler if(mouse_state==MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_PRESSED && !move) { pressed_form=true; // the flag of holding the mouse button on the form //--- If the left mouse button is pressed if(this.m_mouse.IsPressedButtonLeft()) { //--- Set flags and form parameters move=true; // movement flag form.SetInteraction(true); // flag of the form interaction with the environment form.BringToTop(); // form on the background - above all others this.ResetAllInteractionExeptOne(form); // Reset interaction flags for all forms except the current one form.SetOffsetX(this.m_mouse.CoordX()-form.CoordX()); // Cursor shift relative to the X coordinate form.SetOffsetY(this.m_mouse.CoordY()-form.CoordY()); // Cursor shift relative to the Y coordinate } } //--- 'The cursor is inside the active area, the mouse wheel is being scrolled' event handler workpiece if(mouse_state==MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_WHEEL) { } //--- 'The cursor is inside the window scrolling area, no mouse buttons are clicked' event handler workpiece if(mouse_state==MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_NOT_PRESSED) { } //--- 'The cursor is inside the window scrolling area, a mouse button is clicked (any)' event handler workpiece if(mouse_state==MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_PRESSED) { } //--- 'The cursor is inside the window scrolling area, the mouse wheel is being scrolled' event handler workpiece if(mouse_state==MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_WHEEL) { } } } } } //+------------------------------------------------------------------+
メソッドのすべての改善には詳細なコメントが付いています。質問がある場合は、下のコメント欄でお気軽にお問い合わせください。
私がここで実装しようとしていた改善はこれですべてです。
検証
テストを実行するには、前の記事のEAを使用して、\MQL5\Experts\TestDoEasy\Part98\\TestDoEasyPart98.mq5として保存します。
実装する唯一の変更は、3つのフォームオブジェクトの作成です。
//+------------------------------------------------------------------+ //| TestDoEasyPart98.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 (3) // Number of created forms #define START_X (4) // Initial X coordinate of the shape #define START_Y (4) // Initial Y coordinate of the shape #define KEY_LEFT (188) // Left #define KEY_RIGHT (190) // Right #define KEY_ORIGIN (191) // Initial properties //--- 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; color array_clr[]; //+------------------------------------------------------------------+
この点で、作成された各フォームの座標の計算を少し調整してみましょう。
フォームオブジェクトの宣言は、ループの外側に設定されます。
最初のフォームは100のY座標で作成されていますが、残りのフォームには前のフォームの下端から20ピクセルのインデントがあります。
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- Set EA global variables ArrayResize(array_clr,2); // Array of gradient filling colors array_clr[0]=C'26,100,128'; // Original ≈Dark-azure color array_clr[1]=C'35,133,169'; // Lightened original color //--- Create the array with the current symbol and set it to be used in the library string array[1]={Symbol()}; engine.SetUsedSymbols(array); //--- Create the timeseries object for the current symbol and period, and show its description in the journal engine.SeriesCreate(Symbol(),Period()); engine.GetTimeSeriesCollection().PrintShort(false); // Short descriptions //--- Create form objects CForm *form=NULL; for(int i=0;i<FORMS_TOTAL;i++) { //--- When creating an object, pass all the required parameters to it form=new CForm("Form_0"+string(i+1),30,(form==NULL ? 100 : form.BottomEdge()+20),100,30); if(form==NULL) continue; //--- Set activity and moveability flags for the form form.SetActive(true); form.SetMovable(true); //--- Set the form ID and the index in the list of objects form.SetID(i); form.SetNumber(0); // (0 - main form object) Auxiliary objects may be attached to the main one. The main object is able to manage them //--- Set the opacity of 200 form.SetOpacity(245); //--- The form background color is set as the first color from the color array form.SetColorBackground(array_clr[0]); //--- Form outlining frame color form.SetColorFrame(clrDarkBlue); //--- Draw the shadow drawing flag form.SetShadow(false); //--- Calculate the shadow color as the chart background color converted to the monochrome one color clrS=form.ChangeColorSaturation(form.ColorBackground(),-100); //--- If the settings specify the usage of the chart background color, replace the monochrome color with 20 units //--- Otherwise, use the color specified in the settings for drawing the shadow color clr=(InpUseColorBG ? form.ChangeColorLightness(clrS,-20) : InpColorForm3); //--- Draw the form shadow with the right-downwards offset from the form by three pixels along all axes //--- Set the shadow opacity to 200, while the blur radius is equal to 4 form.DrawShadow(3,3,clr,200,4); //--- Fill the form background with a vertical gradient form.Erase(array_clr,form.Opacity(),true); //--- Draw an outlining rectangle at the edges of the form form.DrawRectangle(0,0,form.Width()-1,form.Height()-1,form.ColorFrame(),form.Opacity()); form.Done(); //--- Display the text describing the gradient type and update the form //--- Text parameters: the text coordinates and the anchor point in the form center //--- Create a new text animation frame with the ID of 0 and display the text on the form form.TextOnBG(0,TextByLanguage("Тест 0","Test 0")+string(i+1),form.Width()/2,form.Height()/2,FRAME_ANCHOR_CENTER,C'211,233,149',255,true,true); //--- Add the form to the list if(!engine.GraphAddCanvElmToCollection(form)) delete form; } //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+
OnChartEvent()ハンドラで、マウスクリックによって作成されるグラフィカルオブジェクト名の長さを短くして(前の名前は「TrendLineExt」でした)、グラフィカルリソースを作成するときに63文字を超えないようにします。
if(id==CHARTEVENT_CLICK) { if(!IsCtrlKeyPressed()) return; //--- Get the chart click coordinates datetime time=0; double price=0; int sw=0; if(ChartXYToTimePrice(ChartID(),(int)lparam,(int)dparam,sw,time,price)) { //--- Get the right point coordinates for a trend line datetime time2=iTime(Symbol(),PERIOD_CURRENT,1); double price2=iOpen(Symbol(),PERIOD_CURRENT,1); //--- Create the "Trend line" object string name_base="TLineExt"; engine.CreateLineTrend(name_base,0,true,time,price,time2,price2); //--- Get the object from the list of graphical objects by chart name and ID CGStdGraphObj *obj=engine.GraphGetStdGraphObjectExt(name_base,ChartID()); //--- Create the "Left price label" object string name_dep="PriceLeftExt"; engine.CreatePriceLabelLeft(name_dep,0,false,time,price); //--- Get the object from the list of graphical objects by chart name and ID and CGStdGraphObj *dep=engine.GraphGetStdGraphObject(name_dep,ChartID()); //--- add it to the list of graphical objects bound to the "Trend line" object obj.AddDependentObj(dep); //--- Set its pivot point by X and Y axis to the trend line left point dep.AddNewLinkedCoord(GRAPH_OBJ_PROP_TIME,0,GRAPH_OBJ_PROP_PRICE,0); //--- Create the "Right price label" object name_dep="PriceRightExt"; engine.CreatePriceLabelRight(name_dep,0,false,time2,price2); //--- Get the object from the list of graphical objects by chart name and ID and dep=engine.GraphGetStdGraphObject(name_dep,ChartID()); //--- add it to the list of graphical objects bound to the "Trend line" object obj.AddDependentObj(dep); //--- Set its pivot point by X and Y axis to the trend line right point dep.AddNewLinkedCoord(GRAPH_OBJ_PROP_TIME,1,GRAPH_OBJ_PROP_PRICE,1); } } //--- Handling graphical element collection events
EAをコンパイルし、チャート上で起動します。
ご覧のとおり、フォームはワンクリック取引パネルボタンが配置されている領域には入力されません。また、フォームがアクティブになっている場合はパネル領域にも入力されません。拡張された標準グラフィカルオブジェクトのピボットポイントを処理するためのフォームは、意図したとおりに機能し、チャートの制限を超えません。
ただし、欠点もあります。複合グラフィカルオブジェクトを作成してそのピボットポイントを移動すると、フォームオブジェクトの上に配置されます。場合によっては、これが正しくないことが分かります。たとえば、パネルがある場合、カーソルによってドラッグされた線は、パネルの上に描画されるのではなくパネルの下を通過する必要があります。各フォームを1つずつクリックすると、それらは複合グラフィカルオブジェクトの上に設定され、再配置中にはこれらのフォームの上に描画されません。3つのフォームを部分的に重ねて2番目のフォームにカーソルを合わせると、最初のフォームがアクティブになります。これを修正します。ここでは、相互およびチャート上の他のオブジェクトに対するすべてのフォームの位置の「深さ」を使用する必要があります。
次の段階
次の記事では、複合グラフィカルオブジェクトとその機能に関する作業を続けます。
**連載のこれまでの記事
DoEasyライブラリのグラフィックス(第93部):複合グラフィカルオブジェクトを作成するための機能の準備
DoEasyライブラリのグラフィックス(第94部):複合グラフィカルオブジェクトの移動と削除
DoEasyライブラリのグラフィックス(第95部):複合グラフィカルオブジェクトコントロール
DoEasyライブラリのグラフィックス(第96部):フォームオブジェクトのグラフィックとマウスイベントの処理
DoEasyライブラリのグラフィックス(第97部):フォームオブジェクトの移動の独立した処理
MetaQuotes Ltdによってロシア語から翻訳されました。
元の記事: https://www.mql5.com/ru/articles/10521
- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索