グラフィカルインタフェース I: 種々のプログラム及びメタトレーダー4ターミナルでのライブラリのテスト(チャプター 5)

Anatoli Kazharski | 29 2月, 2016

コンテンツ


はじめに

本稿はグラフィカルインターフェイスに関するシリーズの続きです。シリーズ第一弾のグラフィカルインタフェース I: ライブラリストラクチャの準備(チャプター 1)ではライブラリの目的を詳細に考慮されます。第一部の記事へのリンクの完全なリストは各章の終わりにあります。そこではまた、開発の現段階でのライブラリの完全版をダウンロードすることができます。ファイルはアーカイブと同じディレクトリに配置される必要があります。

このグラフィカルインターフェイスに関するシリーズの第一部の前章では、フォームクラスは、そのコントロールを押すしてフォームの管理を許可するメソッドによって改善されました。これまでに、テストは Expert Advisor 型のプログラムとしてMetaTrader 5ターミナルのみで実行されました。本稿では、インディケータやスクリプトなどの異なるMQLプログラムでのテストが行われます。ライブラリはすべてのMetaTraderプラットフォームで使用できるようにクロスプラットフォームに対応するように設計されたので、MetaTrader 4でもテストが実行されます。


インディケータでのフォームの使用

MetaTrader 5 terminalのインディケータディレクトリ(<data_directory>\MQL5\Indicators)に新しいインディケータフォルダーを作成します。以前に EA でテストを実行したときのように、このフォルダにメインプログラムファイルとCProgramクラスを含むProgram.mqhファイルを作成します。Program.mqhファイルをEA のために作成されたフォルダからインディケータフォルダにコピーしてください。この段階で、このコードは、インディケータ上のコントロールフォームをテストするのに十分です。インディケータのメインファイルに以下のコードを入力して下さい。

黄色で強調表示された行は、インディケータがメインチャートウィンドウに配置されることを示します。現在は、インディケータ上のグラフィカルインターフェイスの要素のみがテストされ、価格配列との計算をスキップするので、インディケータバッファの数は、ゼロに設定することができます。

//+------------------------------------------------------------------+
//|                                                  ChartWindow.mq5 |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "2015, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property indicator_chart_window
#property indicator_buffers 0
#property indicator_plots   0
//--- 取引パネルクラスを含む
#include "Program.mqh"
CProgram program;
//+------------------------------------------------------------------+
//| カスタムインディケータ初期化関数                                      |
//+------------------------------------------------------------------+
int OnInit(void)
{
program.OnInitEvent();  
//--- 取引パネルを設定する
if(!program.CreateTradePanel())
{
::Print(__FUNCTION__," > Failed to create graphical interface!");
return(INIT_FAILED);
}
//--- 初期化が完成
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| エキスパート初期化解除関数                                           |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
program.OnDeinitEvent(reason);
}
//+------------------------------------------------------------------+
//| カスタムインディケータ反復処理関数                                    |
//+------------------------------------------------------------------+
int OnCalculate (const int    rates_total,     // price[] 配列のサイズ
const int    prev_calculated, // ひとつ前の呼び出しで処理されたバーの数
const int    begin,           // 重要データの開始点
const double &price[])        // 計算のための配列
{
return(rates_total);
}
//+------------------------------------------------------------------+
//| タイマー関数                                                       |
//+------------------------------------------------------------------+
void OnTimer(void)
{
program.OnTimerEvent();
}
//+------------------------------------------------------------------+
//| ChartEvent関数                                                    |
//+------------------------------------------------------------------+
void OnChartEvent(const int    id,
const long   &lparam,
const double &dparam,
const string &sparam)
{
program.ChartEvent(id,lparam,dparam,sparam);
}
//+------------------------------------------------------------------+

インディケータをコンパイルしてチャートに読み込みます。結果は以下のスクリーンショットのようなはずです。EAの場合と同じく、フォームの最小化、最大化、またチャート上での移動が可能です。そのコントロールは、カーソルの動きに反応し、「閉じる」ボタン(右上隅にあるバッテン)が押された場合、インディケータはチャートから削除されます。

図1。メインチャートウィンドウ内のインディケータ上のフォームのテスト

図1。メインチャートウィンドウ内のインディケータ上のフォームのテスト

左上隅のフォームのアイコンにご注意ください。プログラムが自動的にプログラムの種類を認識し、デフォルトのアイコンを設定しました。CWindow クラスの開発のこの時点では、アイコンの変更が可能です。

コントロールフォームは、コードを大規模に変更せずにインディケータ上でも動作します。しかし、メインウィンドウ以外で同じフォームを持つインディケータを作成したとすると、すべての機能が期待通りに動作するわけではありません。すなわち、(1)フォームは必要に応じてインディケータサブウインドウの代わりにメインチャートウィンドウで設定され、(2)「閉じる」ボタンを押してもインディケータがチャートから削除されません。これはなぜでしょうか。これを変更する方法を見ていこうと思います。

実際には、我々が既に行ったことから何も変える必要はありません。CWndEventsクラスに、プログラムのタイプに応じてウィンドウ番号とチャートサブウィンドウに位置するインディケータの番号を自動検出するメソッドを作成しなければならないだけです。このメソッドをDetermineSubwindow()と名付けます。メソッドの開始時には、プログラムがインディケータであるかどうかのチェックがなければなりません。MQLプログラムのうちでサブウィンドウに配置できるのはインディケータのみなので、インディケータでない場合はそれ以上進む理由がありません。

ChartWindowFind()関数を使用してインディケータウィンドウの番号を取得します。操作が失敗して関数が -1を戻した場合、対応したメッセージが操作ログに印刷されメソッドが終了します。ゼロより大きいウィンドウ番号はチャートのメインウィンドウではないことを意味します。このサブウィンドウに他のインディケータが含まれている場合、さらなる検査が必要です。このサブウインドウに他のインディケータがある場合、我々のインディケータはチャートから削除され、操作ログに削除の理由が記されます。これにはChartIndicatorsTotal()関数を使用して、指定されたサブウインドウでのインディケータの合計数を取得します。その後、リストの最後のインディケータの短縮名を取得し、 インディケータ番号が1以外の場合、チャートからプログラムを削除します。チャートからのインディケータの削除は、その短縮名が指定されている場合にのみ可能であるので、インディケータの短縮名の取得は必須です。

DetermineSubwindow()メソッドの宣言と実装を下のコードにあるようにCWndEventsクラスに加えます。

class CWndEvents : public CWndContainer
{
private:
//--- サブウィンドウ番号の検出
void              DetermineSubwindow(void);
};
//+------------------------------------------------------------------+
//| サブウィンドウ番号の検出                                             |
//+------------------------------------------------------------------+
void CWndEvents::DetermineSubwindow(void)
{
//--- プログラムがインディケータでない場合は終了する
if(PROGRAM_TYPE!=PROGRAM_INDICATOR)
return;
//--- 最後のエラーをリセットする
::ResetLastError();
//--- インディケータウィンドウの番号を検出する
m_subwin=::ChartWindowFind();
//--- 検出に失敗した場合、終了する
if(m_subwin<0)
{
::Print(__FUNCTION__," > Error when identifying the sub-window number: ",::GetLastError());
return;
}
//--- これがチャートのメインウィンドウでない場合
if(m_subwin>0)
{
//--- 指定されたサブウインドウでのインディケータの合計数を取得する
int total=::ChartIndicatorsTotal(m_chart_id,m_subwin);
//--- リストの最後のインディケータの短縮名を取得する
string indicator_name=::ChartIndicatorName(m_chart_id,m_subwin,total-1);
//--- サブウィンドウにインディケータが既存する場合、チャートからプログラムを削除する
if(total!=1)
{
::Print(__FUNCTION__," > This sub-window already contains an indicator.");
::ChartIndicatorDelete(m_chart_id,m_subwin,indicator_name);
return;
}
}
}

このメソッドはCWndEventsクラスのコンストラクタで呼び出されなければなりません。

//+------------------------------------------------------------------+
//| コンストラクタ                                                     |
//+------------------------------------------------------------------+
CWndEvents::CWndEvents(void)
{
//--- サブウィンドウ番号の検出
DetermineSubwindow();
}

ライブラリはチャートのメインウィンドウにないインディケータへの準備ができています。ここで、動作をテストし、エラーや欠陥が発見された場合、修正します。

ライブラリのサブウインドウ内に配置されるグラフィカルインタフェースには主に3つのモードがあります。

  1. 自由モードこのモードでは、インディケータサブウィンドウの高さが固定されず、ユーザによって自由に変更されることができます。<コントロールフォームが最小化された場合、インディケータサブウインドウの高さは自動的には変更されず、ユーザによる変更が可能です。
  2. 固定モードインディケータサブウィンドウの高さは、コントロールフォームの高さのサイズに固定することができます。このモードでも、フォームが最小化されてもサブウインドウの高さが同じままです。
  3. サブウインドウの最小化が可能な固定モードインディケータがチャート上に読み込まれた場合、サブウインドウサイズがコントロールフォーム高さのサイズに固定されますが、サブウィンドウが最小化されると、それは、フォームヘッダーの大きさをとります。

上記されたモードのいずれかの便利な設定のために、以前にCWindowクラスでRollUpSubwindowMode()メソッドが作成されました。これを使用する方法は、後に示されます。

それぞれのモードをデモするために3つのインディケータを作成してみましょう。物事をより面白くするために、列挙(ドロップダウンリスト)がこれらのインディケータ外部パラメータの役割を果たします。このドロップダウンリストでは、フォームを設定するための3つのオプションを選択できます。

  1. チャートウィンドウの左側
  2. チャートウィンドウの右側
  3. チャートの幅全体

3番目のオプションが選択された場合、チャートの幅が変化された場合にフォームの幅を変更する必要があります。CWindowクラスには、現在、これを行えるメソッドが含まれていません。そのようなメソッドを作成してChangeWindowWidth()と名付けましょう。メソッドの初めで、現在の幅がメソッドに渡された値と比較されます。渡された値が異なる場合には、フォームオブジェクトの幅を変更する必要があり、ボタンの座標を更新しなければなりません。

下記がCWindow::ChangeWindowWidth()メソッドの宣言と実装です。

class CWindow : public CElement
{
public:
//--- ウィンドウの幅を変更する
void              ChangeWindowWidth(const int width);
};
//+------------------------------------------------------------------+
//| ウィンドウの幅を変更する                                             |
//+------------------------------------------------------------------+
void CWindow::ChangeWindowWidth(const int width)
{
//--- 幅が変更していない場合終了する
if(width==m_bg.XSize())
return;
//--- 背景とヘッダの幅を更新する
CElement::XSize(width);
m_bg.XSize(width);
m_bg.X_Size(width);
m_caption_bg.XSize(width);
m_caption_bg.X_Size(width);
//--- 全部のボタンの座標とマージンを更新する
//--- 閉じるためのボタン
int x=CElement::X2()-CLOSE_BUTTON_OFFSET;
m_button_close.X(x);
m_button_close.XGap(x-m_x);
m_button_close.X_Distance(x);
//--- ボタンの最大化
x=CElement::X2()-ROLL_BUTTON_OFFSET;
m_button_unroll.X(x);
m_button_unroll.XGap(x-m_x);
m_button_unroll.X_Distance(x);
//--- ボタンの最小化
m_button_rollup.X(x);
m_button_rollup.XGap(x-m_x);
m_button_rollup.X_Distance(x);
//--- ツールヒントボタン(有効な場合)
if(m_tooltips_button)
{
x=CElement::X2()-TOOLTIP_BUTTON_OFFSET;
m_button_tooltip.X(x);
m_button_tooltip.XGap(x-m_x);
m_button_tooltip.X_Distance(x);
}
}

以前、チャートのメインウィンドウ内のテストのために作成されたインディケータのコピーを3部作成します。各コピーは、上記のモードのいずれかのために使われます。それぞれに固有の名前を与えます。

例:

それぞれのインディケータのメインファイルのコードで一行を変更する必要があります。インディケータをメインウィンドウに配置する命令の代わりにインディケータがサブウィンドウに作成されるよう指定されなければなりません。

//+------------------------------------------------------------------+
//|                                                   FreeHeight.mq5 |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "2015, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property indicator_separate_window
#property indicator_buffers 0
#property indicator_plots   0

その後、それぞれのProgram.mqh インクルードファイルに、列挙体(enum) と、フォーム設定モードを選択するための外部パラメータ(input)を追加します。

//--- ウィンドウ設定モードの列挙      
enum ENUM_WINDOW_MODE
{
LEFT  =0,
RIGHT =1,
FULL  =2
};
//--- 外部パラメータ
input ENUM_WINDOW_MODE WindowMode=LEFT;

外部設定でRIGHTまたはFULLオプションが選択された場合、チャートウィンドウのサイズが変更すると、プログラムは、右端がチャート境界を超えないように、フォーム座標(RIGHTモード)またはサイズ(FULLモード)を調整します。

個人的に、インディケータサブウインドウのような限られた空間でフォームを可動にすることには意味がないと思います。現在のフォームの実装では、プロパティで非可動として指定されている場合、チャートサイズが変更されたときに調整は行われません。これが、開発中のMQLアプリケーションでは、非可動のフォームの調整はチャートの内部イベントハンドラ内で個別に行わなければならない理由です。下記のコードを作成されたインディケータのすべてのProgram.mqhファイルのCProgram::OnEvent()ハンドラに追加します。

//+------------------------------------------------------------------+
//| チャートイベントハンドラ                                             |
//+------------------------------------------------------------------+
void CProgram::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
{
if(id==CHARTEVENT_CHART_CHANGE)
{
//--- チャート全幅のフォームモードが選択された場合
if(WindowMode==FULL)
m_window.ChangeWindowWidth(m_chart.WidthInPixels()-2);
//--- フォームを右に設定するモードが選択された場合
else if(WindowMode==RIGHT)
m_window.X(m_chart.WidthInPixels()-(m_window.XSize()+1));
}
}

お分かりのように、現在、作成されたインディケータのすべてのファイルの内容は完全に同一です。ついに、コントロールフォームが配置されているインディケータのサブウインドウのモードを設定するCWindow::RollUpSubwindowMode()メソッドの使用方法の質問への答えに近づいてきました。

デフォルトでは、プログラムがインディケータサブウィンドウモードを検出するのに使うCWindowクラス変数の値は、 インディケータサブウィンドウの高さ変更が許可されているときに使うモードのためにクラスコンストラクタで初期化されます。よってこのメソッドはFreeHeight インディケータでは完全にスキップできます。CProgramクラスでサブウィンドウのフォームを設定するコードは下記の通りです。外部設定で選択したオプションに応じて、フォームとその座標の幅が定義されていることを覚えておいてください。CWindow::RollUpSubwindowMode()メソッドが異なるモードを持つインディケータで使用される場所は、黄色で強調表示されています。

//+------------------------------------------------------------------+
//| コントロールフォームを作成する                                       |
//+------------------------------------------------------------------+
bool CProgram::CreateWindow(const string caption_text)
{
//--- ウィンドウ配列にウィンドウポインタを追加する
CWndContainer::AddWindow(m_window);
//--- サイズ
int x_size=(WindowMode!=FULL)?205 : m_chart.WidthInPixels()-2;
int y_size=243;
//--- 座標
int x=(WindowMode!=RIGHT)?1 : m_chart.WidthInPixels()-(x_size+1);
int y=1;
//--- プロパティ
m_window.XSize(x_size);
m_window.YSize(y_size);
// ...
//--- フォームの作成
if(!m_window.CreateWindow(m_chart_id,m_subwin,caption_text,x,y))
return(false);
//---
return(true);
}

RollUpインディケータは、インディケータサブウィンドウが固定された高さを有しており、フォームが最小化されたときに変化しないモードで使われます。これが、下記のコードが上記のコードで強調表示された場所に追加されなければならない理由です。

m_window.RollUpSubwindowMode(false,true);

最初のパラメータの値は、フォームが最小化されたときににサブウインドウが最小化されなくてもいいことを意味します。2番目のパラメータの値は、サブウインドウは、フォームの高さに等しい固定の高さを有していなければならないことを示しています。<SDownFallインディケータのCWindow::RollUpSubwindowMode()メソッドのパラメータは下記のように指定される必要があります。

m_window.RollUpSubwindowMode(true,true);

最初のパラメータの値は、フォームの最小化でサブウインドウの高さがフォームのヘッダーの高さと等しくなるように設定されるモードを設定します。

変更されたファイルとインディケータファイルをコンパイルします。それぞれをチャートでテストします。すべてが設定されたモードに応じて正しく動作しています。チャートウィンドウサイズが変更されても、座標(RIGHTモード)とフォーム幅(FULLモード)は調節されないことにご注意下さい。これは驚くべきことではありません!さて、イベントのストリームはCProgramクラスのOnEvent()チャートイベントハンドラに届きません。ライブラリの主な構造を構築したとき、チャートイベントのストリームがCWndEventsクラスのChartEvent()メソッドから CProgramクラスのOnEvent()ローカルハンドラに転送できるといいました。後者は、我々が開発しているアプリケーションのクラスです。

イベント転送CWndEvents::CheckElementsEvents()メソッドでのコントロールイベントハンドラの反復処理の直後に置きましょう。

//+------------------------------------------------------------------+
//| コントロールイベントのチェック                                       |
//+------------------------------------------------------------------+
void CWndEvents::CheckElementsEvents(void)
{
int elements_total=CWndContainer::ElementsTotal(0);
for(int e=0; e<elements_total; e++)
m_wnd[0].m_elements[e].OnEvent(m_id,m_lparam,m_dparam,m_sparam);
//--- イベントをアプリケーションファイルに転送する
OnEvent(m_id,m_lparam,m_dparam,m_sparam);
}

RIGHTモードでのフォーム座標調整は今でも間違ったものです。その理由は、CWindowクラスハンドラがCHARTEVENT_CHART_CHANGEイベントで呼び出された場合、プログラムがフォーム座標を更新しないことです。何故ならUpdateWindowXY()メソッドで座標はフォームが可動な場合のみに更新されるからです。ご存知の通り、フォームがインディケータに固定されることは以前に合意されました。すべてのコントロールの反復処理を終え、プログラムは、上記のコードで示されたようにCProgramアプリケーションのクラスのチャートイベントハンドラを呼び出し、チャートウィンドウの現在の幅に応じてX座標を更新します。

この後、プログラムは、CWndEventsクラスのCheckElementsEvents()メソッドを終了してCWndEvents::ChartEventMouseMove()メソッドを呼び出し、イベントがマウスイベントでないのですぐにメソッドを終了します。現在、フォームのプロパティでの実際の座標に応じたフォームの位置の更新はCWndEvents::ChartEventMouseMove()メソッドのみで行われます。ここで、下に黄色で強調表示されたようにCHARTEVENT_CHART_CHANGEイベント処理をCWndEvents::ChartEvent()メソッドに追加します。

//+------------------------------------------------------------------+
//| プログラムイベント処理                                              |
//+------------------------------------------------------------------+
void CWndEvents::ChartEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
{
//--- 配列が空の場合終了する
if(CWndContainer::WindowsTotal()<1)
return;
//--- イベントパラメータフィールドの初期化
InitChartEventsParams(id,lparam,dparam,sparam);
//--- インターフェースコントロールイベントの確認
CheckElementsEvents();
//--- マウス動作のイベント
ChartEventMouseMove();
//--- チャートプロパティ変更イベント
ChartEventChartChange();
}

CWndEvents::ChartEventChartChange()メソッドの本体に、実際の座標とチャートの再描画によるウィンドウの移動機能の呼び出しを追加します。

//+------------------------------------------------------------------+
//| CHARTEVENT CHART CHANGE イベント                                  |
//+------------------------------------------------------------------+
void CWndEvents::ChartEventChartChange(void)
{
//--- チャートプロパティ変更イベント
if(m_id!=CHARTEVENT_CHART_CHANGE)
return;
//--- ウィンドウの移動
MovingWindow();
//--- チャートを再描画する
m_chart.Redraw();
}

ライブラリとインディケータのファイルをコンパイルします。チャートにロードしてテストしてみてください。RIGHTモードのフォーム座標は正しく更新されます。

以前に考慮されていない別の詳細があります。現在の実装では、コントロールフォームと作成されたインディケータの1つがチャート上にロードされるとき、サブウインドウ番号はクラスのコンストラクタのCWndEvents::DetermineSubwindow()メソッドで自動的にプログラムによって定義されます。これらのインディケータの1つがすでにチャートにロードされていて、それがサブウィンドウに位置するインディケータを含む場合、このインディケータがチャート上にある間、すべてがスムーズに動作します。これが削除されると、我々のインディケータののサブウィンドウは、チャートウィンドウのリストで新しい番号を受け取ることになります。ライブラリは、作業しているウィンドウの番号を必要とするため(例えば相対座標を特定するため)、いくつかの機能が正しく動作しません。そのため、チャート内のウィンドウの数を追跡する必要があります。番号が変わったらすぐに、値はこれが必要とされるクラスのフィールド内で宣言する必要があります。

プログラムがチャートからロードされるか削除されるとCHARTEVENT_CHART_CHANGEイベントが生成されます。これが、チェックがCWndEvents::ChartEventChartChange()メソッドに位置しなければならない理由です。このようなチェックに専用の新しいメソッドを作成し、CWndEvents::CheckSubwindowNumber()と呼びましょう。その宣言と実装は以下のコードに示されています。最初にプログラムウィンドウやチャート上にプログラムをロードする際に保存された数とのマッチングのチェックがあります。一致しない場合、プログラムは、配置されている現在のウィンドウの番号が定義され、すべてのフォームコントロールで更新されなければなりません。

class CWndEvents : public CWndContainer
{
private:
//--- コントロールイベントの検証
void              CheckSubwindowNumber(void);
//---
};
//+------------------------------------------------------------------+
//| プログラムウィンドウ番号のチェックと更新                               |
//+------------------------------------------------------------------+
void CWndEvents::CheckSubwindowNumber(void)
{
//--- サブウィンドウのプログラムと数がマッチしない場合
if(m_subwin!=0 && m_subwin!=::ChartWindowFind())
{
//--- サブウィンドウ番号の検出
DetermineSubwindow();
//--- すべてのコントロールを格納する
int windows_total=CWndContainer::WindowsTotal();
for(int w=0; w<windows_total; w++)
{
int elements_total=CWndContainer::ElementsTotal(w);
for(int e=0; e<elements_total; e++)
m_wnd[w].m_elements[e].SubwindowNumber(m_subwin);
}
}
}

このメソッドの呼び出しはCWndEvents::ChartEventChartChange()メソッドでやられなければなりません。

//+------------------------------------------------------------------+
//| CHARTEVENT CHART CHANGE イベント                                  |
//+------------------------------------------------------------------+
void CWndEvents::ChartEventChartChange(void)
{
//--- チャートプロパティ変更イベント
if(m_id!=CHARTEVENT_CHART_CHANGE)
return;
//--- プログラムウィンドウ番号のチェックと更新
CheckSubwindowNumber();
//--- ウィンドウの移動
MovingWindow();
//--- チャートを再描画する
m_chart.Redraw();
}

これで、すべてが、設計されたように動作するはずです。上述のモードのテストのためのすべてのインディケータは本稿末尾でダウンロードすることができます。

図2。チャートのサブウインドウ内のインディケータ上のフォームのテスト

図2。チャートのサブウインドウ内のインディケータ上のフォームのテスト

現在の実装が1つのチャート上で正常に動作するためには、では、このライブラリが開発に使用された複数のアプリケーションを使用しないことをお勧めします。上記のスクリーンショットは、記事の省スペース化のために、すべてのモードのコンパクトなデモのために1つのチャート上に複数のインディケータを表示します。チャートスクロールの有効/無効化の時点では、ライブラリが使用されているアプリケーション間の競合が存在することになります。アプリケーション間のような競合を排除について、いくつかのアイデアがあります。テストの結果が良好であり、解決法が実行可能である場合には、このシリーズの今後の記事の1つはそれについて書かれます。


スクリプトでのフォームの使用

既にEAとインディケータ上でのフォームの使用が検討されました。フォームはスクリプトで使用できるのでしょうか?答えは「できる」です。スクリプトでは、フォームの役割は非常に制限されます。スクリプトにはイベントハンドラがありません。これが(1)フォームは移動できない(2)フォーム上のあsクションのためにコントロールを設定する意味はない(3)図形オブジェクトがマウスの動きに反応しない理由です。

しかし、スクリプトの実行時に動作しユーザーに統計データを示す情報パネルをすぐに作成する必要がある場合、このためのベースとしてフォームが使用されるべきです。アプリケーションはみな同じライブラリを使用するので、属しているものMQLプログラムのタイプに関係なく単一の設計を持つことになります。

スクリプトのメインファイルとProgram.mqhファイルを配置するフォルダを作成します。上記でEAとインディケータのために考慮されたように、このファイルのCProgram クラスはCWndEventsクラスから派生します。一般的には、すべてが同じです。例外は、ここでは1つのOnEvent()ハンドラがあり、そのイベントは人工的にスクリプトのメインファイルで永久ループで作成されることです。このメソッドには、メソッドを終了する前の一時停止のためのミリ秒数に当たるパラメータが1つだけあります.

//+------------------------------------------------------------------+
//| アプリケーション作成のクラス                                         |
//+------------------------------------------------------------------+
class CProgram : public CWndEvents
{
protected:
//--- ウィンドウ
CWindow           m_window;
//---
public:
CProgram(void);
~CProgram(void);
//--- イベントハンドラ
virtual void      OnEvent(const int milliseconds);
//--- 情報パネルの作成
bool              CreateInfoPanel(void);
//---
protected:
//--- フォームを作成する
bool              CreateWindow(const string text);
};

いくつかのプロセスを模倣するために、渡されたミリ秒数に等しい間隔でCProgram::OnEvent()メソッドでフォームのヘッダーのテキストを変更します。中略プロセスインディケータを作成してみましょう。以下のコードは、これを実装する方法を示しています。

//+------------------------------------------------------------------+
//| イベント                                                          |
//+------------------------------------------------------------------+
void CProgram::OnEvent(const int milliseconds)
{
static int count =0;  // カウンタ
string     str   =""; // ヘッダの行
//--- プロセスの進行でのヘッダの生成
switch(count)
{
case 0 : str="SCRIPT PANEL";     break;
case 1 : str="SCRIPT PANEL .";   break;
case 2 : str="SCRIPT PANEL ..";  break;
case 3 : str="SCRIPT PANEL ..."; break;
}
//--- ヘッダラインを更新する
m_window.CaptionText(str);
//--- チャートの再描画
m_chart.Redraw();
//--- カウンタの増加
count++;
//--- 3より大きい場合、ゼロにする
if(count>3)
count=0;
//--- 一時停止
::Sleep(milliseconds);
}

フォームを作成し、スクリプトのメインファイルのCProgram::OnEvent()メソッドの定数呼び出しをOnStart()関数で管理します。以下のコードは、このメソッドが250ミリ秒ごとに呼び出されることを示しています。 これは IsStopped() 関数がfalse値を返す限り動作します。この無限ループを停止するには、スクリプトを手動でチャートから削除する必要があります。プログラムがユーザーによって削除されると IsStopped() 関数がtrue値を返し、ループが停止してスクリプトの作業が終了します。

//+------------------------------------------------------------------+
//|                                                    InfoPanel.mq5 |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2015, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.0"
//--- 取引パネルクラスを含む
#include "Program.mqh"
CProgram program;
//+------------------------------------------------------------------+
//| スクリプトプログラム開始関数                                         |
//+------------------------------------------------------------------+
void OnStart(void)
{
//--- 取引パネルを設定する
if(!program.CreateInfoPanel())
{
::Print(__FUNCTION__," > Failed to create graphical interface!");
return;
}
//--- スクリプトは削除されるまで動作する
while(!::IsStopped())
{
//--- 250ミリ秒ごとにイベントを生成する
program.OnEvent(250);
}
}
//+------------------------------------------------------------------+

ファイルをコンパイルしてスクリプトをチャートに読み込みます。

図3。スクリプトでのフォームのテスト

図3。スクリプトでのフォームのテスト

上のスクリーンショットでは、ライブラリがプログラムの種類を特定し、フォームのラベルとしてデフォルトのアイコンを設定することを見ることができます。このスクリプトは本稿末尾でダウンロードすることができます。これは、後に他の実施例のための情報群に属する要素で改善されます。このスクリプトだけでなく、この記事で考慮されたEAやインディケータは、プログラムを開発するためのテンプレートとして使用することができます。


MetaTrader 4でのライブラリの使用

グラフィカルインターフェース作成のインターフェースはそのままでMetaTrader 4 トレーーディングプラットフォームで利用できます。本稿で作成されたライブラリファイルとプログラムをすべてMetaTrader 5ターミナルディレクトリからMetaTrader 4ターミナルディレクトリにコピーします。ライブラリはほとんどそのまま使用できます。

コンパイルエラーを回避するためにいくつかのライブラリファイルに追加されるべき唯一のものは、エラーをチェックするための特別なstrictモードを使用するようにコンパイラに指示するための特定のパラメータです。そのため、ライブラリのヘッダファイルの先頭に次のコード行を追加します。

#property strict

これをWndContainer.mqhElement.mqhファイルおよびプログラムのメインファイルで行います。これらのファイル内のコードをコンパイルするときにエラーを防ぐことができます。

すべてが正しく、すべてのファイルがコンパイルされている場合は、本稿のプログラムをすべてMetaTrader 4ターミナルでテストします。すべてがMetaTrader 5ターミナル上と同じように動作するはずです。

図4。Metatrader 4ターミナルでのライブラリのテスト

図4。Metatrader 4ターミナルでのライブラリのテスト


おわりに

グラフィカルインタフェースを作成するためのライブラリの開発のどこにいるかを評価してみます。この時点で、ライブラリの構造は下の図に示されている通りです。まめに記事を読んできた場合は、この図解は論理的で理解しやすいことと思います。

図5。開発の現段階でのライブラリの構造

図5。開発の現段階でのライブラリの構造

この構造体の要素が何を意味するかを簡単に復習します。

The first part of this series considered in detail the process of preparation of the main library structure for creating graphical interfaces and the creation of the main interface element - form or window. その他のコントロールは、このフォームに追加されます。これらの作成については、このシリーズの次の記事で説明します。このシリーズの継続は、独自のカスタムコントロールの作成と、他のコントロールと競走しないような十分な統合化のためのライブラリへの追加を詳細に議論するので、役に立つことと思います。

以下の現在の開発段階でのライブラリファイルと記事で考慮されたプログラム(EA、インディケータ、スクリプト)の写真やファイルのアーカイブはMetaTrader 4およびMetaTrader 5ターミナルのテスtのためにダウンロードできます。それらのファイルの資料を使用についてご質問がある場合は、以下のリストにある記事のいずれかでライブラリの開発の詳細をご参照になるるか、本稿へのコメント欄でご質問ください。

第一部の記事(チャプター)のリスト: