グラフィカルインタフェース I: コントロールのフォーム(チャプター 2)
コンテンツ
はじめに
本稿はグラフィカルインターフェイスに関するシリーズの続きです。シリーズ第一弾のグラフィカルインタフェース I: ライブラリストラクチャの準備(チャプター 1)ではライブラリの目的を詳細に考慮されます。第一部の記事へのリンクの完全なリストは各章の終わりにあります。そこではまた、開発の現段階でのライブラリの完全版をダウンロードすることができます。ファイルはアーカイブと同じディレクトリに配置される必要があります。
前章では、グラフィカルインターフェイス作成のためのライブラリの構造についてお話ししました。そこでは (1)プリミティブオブジェクトの派生クラス、 (2)全てのコントロールの基本クラス、及び (3)共通のイベントハンドラ内でこれらのコントロールを制御ポインタを格納して管理するための原則的なクラスが作成されました。
本稿ではグラフィカルインタフェースの最も重要な主要素であるコントロールフォームを作成します。このフォームには複数のコントロールが任意の場所と組み合わせで添付することができます。フォームは移動可能で、それに接続されている全てのコントロールは一緒に移動されます。
コントロールフォームクラス
前述されたように、CElementクラスの記述はすでにいくつかのvirtualメソッドを含み、これらはそれぞれのコントロールで一意となります。これらをCWindowクラスに複製し、他のコントロールの記述でも同じようにします。コントロールフォーム(ウィンドウ)を構築するためのメソッドの作成後、これらのメソッドのコードが考慮されます。
//+------------------------------------------------------------------+ //| Window.mqh | //| Copyright 2015, MetaQuotes Software Corp. | //| http://www.mql5.com | //+------------------------------------------------------------------+ #include "Element.mqh" //+------------------------------------------------------------------+ //| コントロールフォーム作成クラス | //+------------------------------------------------------------------+ class CWindow : public CElement { public: CWindow(void); ~CWindow(void); //--- チャートイベントハンドラ virtual void OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam); //--- タイマー virtual void OnEventTimer(void); //--- コントロールの移動 virtual void Moving(const int x,const int y); //--- (1)表示 (2)非表示 (3)リセット (4)削除 virtual void Show(void); virtual void Hide(void); virtual void Reset(void); virtual void Delete(void); //--- マウスの左クリックの優先順位の(1)設定と(2)リセット virtual void SetZorders(void); virtual void ResetZorders(void); }; //+------------------------------------------------------------------+ //| コンストラクタ | //+------------------------------------------------------------------+ CWindow::CWindow(void) { } //+------------------------------------------------------------------+ //| デストラクタ | //+------------------------------------------------------------------+ CWindow::~CWindow(void) { } //+------------------------------------------------------------------+
CWindow及び他のいくつかのクラスでは、種々のアクションに対した色々なモードが使用されます。全てのモード及びデータタイプの名前付き定数の識別子のリストは別のEnums.mqhファイルに格納されます。これをObjects.mqhファイルに含み、全てのモードとタイプの列挙が全てのライブラリファイルで使用できるようにします。
//+------------------------------------------------------------------+ //| Objects.mqh | //| Copyright 2015, MetaQuotes Software Corp. | //| http://www.mql5.com | //+------------------------------------------------------------------+ #include "Enums.mqh" #include "Defines.mqh" #include <ChartObjects\ChartObjectsBmpControls.mqh> #include <ChartObjects\ChartObjectsTxtControls.mqh>
作成が試まれているウィンドウはどのような部分で構成されているのでしょうか。
- 背景全てのコントロールはこのエリアに位置します。
- ヘッダーこの部分は、ウィンドウを移動可能にし、以下に示すインターフェイスコントロールを含みます。
- アイコン。視覚的識別のための追加的なの属性。
- キャプション。ウィンドウ名。
- 「ツールヒント」ボタン。このボタンを押すと、存在する場合コントロールのツールチップを表示するモードが有効にされます。
- ウィンドウを最大化/最小化するためのボタン。
- ウィンドウを閉じるボタン。
図1。コントロールフォームの複合パーツ
我々が開発しているグラフィカルインターフェイスライブラリでは、マルチウィンドウモードが実装されます。アプリケーションは、ウィンドウを1つのみ有することができます。しかし、プログラムに非常に多数のオプションがあり1つのフォームに全てを配置するのが無理な場合、複数のフォームが必要です。これは、アプリケーションの一般的な設定、参照ウィンドウやファイルのオープン/保存時のウィンドウや色を選択するパレットなどで使用される場合があります。つまり、追加のウィンドウは様々な用途で必要となる場合があります。これらは通常「ダイアログウィンドウ」と呼ばれます。
CWindowクラスは、どちらのウィンドウがアプリケーションのメインウィンドウで、どちらがダイアログウィンドウであるかのウィンドウタイプを定義する機会を備えていなければなりません。開発の行程で新しいウィンドウタイプを持ったアイデアが発生することがありますが、今のところ (1)メインウィンドウと(2)ダイアログウィンドウの2種類のみを含む列挙型をEnums.mqhファイルで作成します。
//+------------------------------------------------------------------+ //| ウィンドウタイプの列挙 | //+------------------------------------------------------------------+ enum ENUM_WINDOW_TYPE { W_MAIN =0, W_DIALOG =1 };
背景やヘッダーは、プリミティブオブジェクト「長方形ラベル」から作成されます。これにはCRectLabelクラスがあります。キャプションにはCLabelクラスを使い、これによって「テキストラベル」オブジェクトを作成します。上記されたウィンドウアイコンとボタンに必要な「ビットマップラベル」はCBmpLabelクラスで賄います。
Element.mqhファイルが既に含まれている場合Object.mqhファイルで定義されたクラスの全ては使用可能になります。ウィンドウのコントロールのインスタンスとウィンドウ作成に必要なメソッドの全てをCWindowクラスの本体で作成しましょう。
class CWindow : public CElement { private: //--- フォーム作成のオブジェクト CRectLabel m_bg; CRectLabel m_caption_bg; CBmpLabel m_icon; CLabel m_label; CBmpLabel m_button_tooltip; CBmpLabel m_button_unroll; CBmpLabel m_button_rollup; CBmpLabel m_button_close; //--- public: //--- ウィンドウ作成のメソッド bool CreateWindow(const long chart_id,const int window,const string caption_text,const int x,const int y); //--- private: bool CreateBackground(void); bool CreateCaption(void); bool CreateIcon(void); bool CreateLabel(void); bool CreateButtonClose(void); bool CreateButtonRollUp(void); bool CreateButtonUnroll(void); bool CreateButtonTooltip(void); };
これらのメソッドに書き入れる前に、インターフェースコントロールとパートを配列によって格納するCWndContainerクラスでいくつかの機能を準備しなければなりません。また、ライブラリ構造の開発における次のステップを行い、テスト用のプログラムを準備する必要があります。
CWindowクラスを持つWindow.mqhファイルをCWndContainerクラスのあるWndContainer.mqhファイルに含みます。他の全てのコントロールクラスもここに含みます。
//+------------------------------------------------------------------+ //| WndContainer.mqh | //| Copyright 2015, MetaQuotes Software Corp. | //| http://www.mql5.com | //+------------------------------------------------------------------+ #include "Window.mqh" // ... // 他の全てのコントロールクラスもここに含まれる // ...
こうすることによって、MQLアプリケーションのユーザーインターフェースが作成されるCProgramクラスでの使用が可能になります。これらのデータタイプを持つクラスメンバをCWndContainerとCWndEventsクラスで作成及び使用することができます。例えば、Window.mqhファイルが含まれた後にCWindowタイプのポインタの動的配列が作成された場合(作業には必要でありませんが)、問題も生じずエラ―メッセージも表示されません。
//+------------------------------------------------------------------+ //| WndContainer.mqh | //| Copyright 2015, MetaQuotes Software Corp. | //| http://www.mql5.com | //+------------------------------------------------------------------+ #include "Window.mqh" //+------------------------------------------------------------------+ //| インターフェースオブジェクト全てを格納するクラス | //+------------------------------------------------------------------+ class CWndContainer { protected: //--- ウィンドウ配列 CWindow *m_windows[]; };
しかし、Window.mqhファイルが含まれなかった場合、ファイルのコンパイルが試まれると、CWindowタイプのクラスメンバ作成の途中で下のスクリーンショットのようなエラーメッセージが表示されます。
図2。指定された型が不足していることを訴えるメッセージ
ウィンドウの動的配列の他に、全てのコントロールのポインタの動的配列(CElement)と関連したオブジェクト全ての配列(CChartObject)も必要です。この構造体のインスタンスとして、ウィンドウ配列と等しいサイズの動的配列(m_window[])を作成しましょう。その結果、各フォームのコントロールへのポインタ配列の配列が受け取られます(以下のコードを参照)。
m_objects[] 配列はCChartObject型で宣言されていることにご注意下さい。このオブジェクト型がすでに開発中のライブラリの構造内に存在しObjects.mqhファイルに含まれているので、コンパイル時にはエラーが発生しません。
class CWndContainer { protected: //--- コントロール配列の構造体 struct WindowElements { //--- 全てのオブジェクトの共通配列 CChartObject *m_objects[]; //--- 全てのコントロールの共通配列 CElement *m_elements[]; }; //--- 各ウィンドウの配列コントロールの配列 WindowElements m_wnd[]; };
これはWindowElements構造体の動的配列の完全なリストではありません。このリストは、他のコントロールが作成されるにつれて更新されます。
配列のサイズを取得するには、適切な方法が必要とされています。全てのコントロール及び各ウィンドウのコントロールのオブジェクト数は、ウィンドウインデック(window_index)をパラメータとして受け渡しすることによって取得できます。
class CWndContainer { public: //--- インターフェースでのウィンドウの数 int WindowsTotal(void) { return(::ArraySize(m_windows)); } //--- 全てのコントロールのオブジェクト宇う int ObjectsElementsTotal(const int window_index); //--- コントロール数 int ElementsTotal(const int window_index); };
配列サイズの超過を確認する必要があります。しばしば、繰り返されるフレーズは先頭に関数名(__FUNCTION__)を含む定義済みのマクロ置換を追加することで、マクロ置換のように書くことができます。このようなマクロ置き換え(PREVENTING_OUT_OF_RANGE)を書いてDefines.mqhファイルに追加しましょう。
//+------------------------------------------------------------------+ //| Defines.mqh | //| Copyright 2015, MetaQuotes Software Corp. | //| http://www.mql5.com | //+------------------------------------------------------------------+ //--- 配列サイズ超過の防止 #define PREVENTING_OUT_OF_RANGE __FUNCTION__," > Prevention of exceeding the array size."
ここで、配列のサイズ超過のチェックを全ての関数で使用すると便利になります。
//+------------------------------------------------------------------+ //| 指定されたウィンドウインデックスのオブジェクト数を返す | //+------------------------------------------------------------------+ int CWndContainer::ObjectsElementsTotal(const int window_index) { if(window_index>=::ArraySize(m_wnd)) { ::Print(PREVENTING_OUT_OF_RANGE); return(WRONG_VALUE); } //--- return(::ArraySize(m_wnd[window_index].m_objects)); } //+------------------------------------------------------------------+ //| 指定されたウィンドウインデックスのコントロール数を返す | //+------------------------------------------------------------------+ int CWndContainer::ElementsTotal(const int window_index) { if(window_index>=::ArraySize(m_wnd)) { ::Print(PREVENTING_OUT_OF_RANGE); return(WRONG_VALUE); } //--- return(::ArraySize(m_wnd[window_index].m_elements)); }
さて、あなたがこのライブラリのユーザーであることを想像して先ほどテストのために作成したEAのCProgramクラスに移動します。取引パネルを作成する必要があると仮定しましょう。このためにはCProgramクラスでCreateTradePanel()メソッドを作成します。記事のスペースを節約するために、作成はすでに前に行われたものに追加する形で行われ、追加されたもののみが議論されるのでご了承ください。
class CProgram : public CWndEvents { public: //--- 取引パネルを作成する bool CreateTradePanel(void); }; //+------------------------------------------------------------------+ //| 取引パネルを作成する | //+------------------------------------------------------------------+ bool CProgram::CreateTradePanel(void) { //--- コントロールフォームの作成 // ... //--- コントロールの作成 // ... //--- ::ChartRedraw(); return(true); }
このメソッドの本体は現在空ですが、すぐに必要な機能で満たされます。メソッド本体のコメントから、コントロールウィンドウの作成が初めに来ることは明らかです。作成されたウィンドウのプロパティを指定できるメソッドが必要になります。Window.mqhファイルが既にWndContainer.mqhファイルに含まれているので、 CWindowクラスがCProgramクラスで使用できるようになります。したがって、このクラスのインスタンスとコントロールのウィンドウを作成するためのメソッドCreateWindow()を作成します。
class CProgram : public CWndEvents { private: //--- コントロールフォーム CWindow m_window; //--- private: //--- フォームの作成 bool CreateWindow(const string caption_text); };
ここで以下の質問が答えられるべきです。
- コントロールとオブジェクトへのポインタがどのように以前にCWndContainerクラスで作成した配列にアクセスするのか
- 各インタフェースコントロールの識別子がどのように定義されるのか
インターフェイスの作成には、特定の(厳しい)順序が必要なため、インターフェース開発の過程は、順序が正確に従われない場合に開発者がどうするべきかわからない状況になるのを避けるように編成されなければなりません。 これが、ライブラリのユーザの全てのアクションがライブラリのエンジンによって制御される理由です。ポインタがCWndContainerクラスのポインタベースにない限りコントロールフォームが作成できないようにアレンジしてみます。コントロールは、それが添付されるフォームへのポインタがそのクラスに格納されるまでは作成されません。
ウィンドウポインタをCWndContainerクラスのインターフェースベースに追加するAddWindow()メソッドを作成しましょう。ここではまた、(1)コントロール配列でのポインタ格納、(2)AddToObjectsArray()メソッドを必要とするコントロールオブジェクトのポインタのオブジェクトポインタ共通配列への格納、及び、(3)識別子の設定も行われます。その上、(4)クラス内部の全てのコントロールの識別子の定義のため、ウィンドウのプロパティで最後の識別子を格納する事も必要です。これは、前の段落で述べたように、全てのコントロールは連結されるウィンドウへのポインタを持っているので可能です。
CWindowクラスで最後に生成されたインターフェースコントロールの識別子を格納/取得するLastId()メソッドの作成から始めましょう。
class CWindow : public CElement { private: //--- 最後のコントロールの識別子 int m_last_id; //--- public: //--- 最後に作成されたコントロールのIDを格納/取得するメソッド void LastId(const int id) { m_last_id=id; } int LastId(void) const { return(m_last_id); } };
CWndContainerクラスの残りのメソッドを作成しましょう。これはAddWindow()メソッドで使用されます。全てのコントロールは一意のクラス名を持つので、AddToObjectsArray()メソッドは、様々なコントロールのオブジェクトが引き渡しされるテンプレートとなります。このメソッドでは、コントロールオブジェクトの配列を順番に繰り返し処理し、ベース配列にそれぞれのポインタを追加します。これにはあと1つのメソッドが必要です。それをAddToArray()と名付けます。このメソッドには単一の型(CChartObject)のオブジェクトのみが受け渡されるので、これをテンプレートにする必要はありません。
class CWndContainer { protected: //--- コントロールオブジェクトポインタの共通配列への追加 template<typename T> void AddToObjectsArray(const int window_index,T &object); //--- 配列へのオブジェクトポインタの追加 void AddToArray(const int window_index,CChartObject &object); }; //+------------------------------------------------------------------+ //| コントロールオブジェクトポインタの共通配列への追加 | //+------------------------------------------------------------------+ template<typename T> void CWndContainer::AddToObjectsArray(const int window_index,T &object) { int total=object.ObjectsElementTotal(); for(int i=0; i<total; i++) AddToArray(window_index,object.Object(i)); } //+------------------------------------------------------------------+ //| 配列へのオブジェクトポインタの追加 | //+------------------------------------------------------------------+ void CWndContainer::AddToArray(const int window_index,CChartObject &object) { int size=::ArraySize(m_wnd[window_index].m_objects); ::ArrayResize(m_wnd[window_index].m_objects,size+1); m_wnd[window_index].m_objects[size]=::GetPointer(object); }
ここでAddWindow()メソッドが作成できます。コントロールカウンタ(m_counter_element_id)も必要となります。この変数の値は、ベースにコントロールが追加されたたびに増加しなければなりません。
class CWndContainer { private: //--- コントロールカウンタ int m_counter_element_id; //--- protected: //--- ウィンドウポインタをインターフェースコントロールのベースに追加する void AddWindow(CWindow &object); }; //+------------------------------------------------------------------+ //| ウィンドウポインタをインターフェースコントロールのベースに追加する | //+------------------------------------------------------------------+ void CWndContainer::AddWindow(CWindow &object) { int windows_total=::ArraySize(m_windows); //--- ウィンドウが一つもない場合、カウンタをゼロにする if(windows_total<1) m_counter_element_id=0; //--- ポインタをウィンドウ配列に追加する ::ArrayResize(m_wnd,windows_total+1); ::ArrayResize(m_windows,windows_total+1); m_windows[windows_total]=::GetPointer(object); //--- ポインタをコントロールの共通配列に追加する int elements_total=::ArraySize(m_wnd[windows_total].m_elements); ::ArrayResize(m_wnd[windows_total].m_elements,elements_total+1); m_wnd[windows_total].m_elements[elements_total]=::GetPointer(object); //--- コントロールオブジェクトをオブジェクトの共通配列に追加する AddToObjectsArray(windows_total,object); //--- 最後のコントロールの識別子を設定して格納する m_windows[windows_total].Id(m_counter_element_id); m_windows[windows_total].LastId(m_counter_element_id); //--- コントロール識別子のカウンタを増加すsる m_counter_element_id++; }
以後、カスタムクラスの新しいウィンドウがMQLアプリケーション(本稿ではCProgram)で作成される場合は、毎回AddWindow() メソッドを使用してベースに追加する必要があります。
また、先に宣言されたウィンドウ作成メソッドもCWindowクラスで実装される必要があります。そのために、型、ウィンドウの外観、またユーザの指示またはMQLアプリケーションの種類に応じてウィンドウがとれるモードに関連する追加的な変数とメソッドが必要になります。これらは、以下に列挙され、簡単な説明を伴っています。
- ウィンドウの状態(最小化/最大化)を設定/取得するためのメソッド。
- ウィンドウの種類(メイン/ダイアログ)を設定/取得するためのメソッド。
- 作成中のMQLアプリケーションの種類(EA/インディケータ)を考慮して、ウィンドウ最小化のモードを設定するメソッド。そのために、メインチャートウィンドウにないインディケータの場合にウィンドウサイズを管理するメソッドが必要。
- 各ウィンドウオブジェクトの色をユーザによって設定するメソッド。
- ウィンドウラベルを設定するメソッド。
- ユーザーがしなかった場合、デフォルトでウィンドウのラベルを定義するメソッド。
- ウィンドウヘッダ内の捕捉領域の境界の同定。
- ウィンドウの右端からのボタンのインデントのための定数。
- ツールヒント表示モードのボタンの表示。
- 全てのウィンドウオブジェクトのためのマウスの左クリックの優先順位の変数。
上記の全てがCWindowクラスに追加されます。フォームの右端からのボタンのインデントの定数は、ファイルの先頭に追加されるべきです。
//+------------------------------------------------------------------+ //| Window.mqh | //| Copyright 2015, MetaQuotes Software Corp. | //| http://www.mql5.com | //+------------------------------------------------------------------+ #include "Element.mqh" //--- ウィンドウの右端からのボタンのインデント #define CLOSE_BUTTON_OFFSET (20) #define ROLL_BUTTON_OFFSET (36) #define TOOLTIP_BUTTON_OFFSET (53)
言及された変数とメソッドをクラス本体で作成します。
//+------------------------------------------------------------------+ //| コントロールフォーム作成クラス | //+------------------------------------------------------------------+ class CWindow : public CElement { private: //--- 最後のコントロールの識別子 int m_last_id; //--- 最小化されたウィンドウの状態 bool m_is_minimized; //--- ウィンドウタイプ ENUM_WINDOW_TYPE m_window_type; //--- サブウィンドウの高さを定義するモード(インディケータ向き) bool m_height_subwindow_mode; //--- インディケータサブウィンドウでのフォーム最小化モード bool m_rollup_subwindow_mode; //--- インディケータサブウィンドウの高さ int m_subwindow_height; //--- 背景の優先順位 int m_bg_zorder; color m_bg_color; int m_bg_full_height; //--- ヘッダーのプロパティ int m_caption_zorder; string m_caption_text; int m_caption_height; color m_caption_bg_color; color m_caption_bg_color_hover; color m_caption_bg_color_off; color m_caption_color_bg_array[]; //--- ボタンのプロパティ int m_button_zorder; //--- フォームフレームの色(背景、ヘッダー) color m_border_color; //--- フォームアイコン string m_icon_file; //--- ツールヒント表示モードのボタンの存在 bool m_tooltips_button; //--- ウィンドウヘッダ内の捕捉領域の境界の同定。 int m_right_limit; //--- public: //--- ウィンドウタイプ ENUM_WINDOW_TYPE WindowType(void) const { return(m_window_type); } void WindowType(const ENUM_WINDOW_TYPE flag) { m_window_type=flag; } //--- デフォルトアイコン string DefaultIcon(void); //--- (1)ウィンドウのカスタムアイコン、(2)参照にボタンを使用、(3)ヘッダの捕捉領域の制限 void IconFile(const string file_path) { m_icon_file=file_path; } void UseTooltipsButton(void) { m_tooltips_button=true; } void RightLimit(const int value) { m_right_limit=value; } //--- 最小化されたウィンドウの状態 bool IsMinimized(void) const { return(m_is_minimized); } void IsMinimized(const bool flag) { m_is_minimized=flag; } //--- ヘッダーのプロパティ void CaptionText(const string text); string CaptionText(void) const { return(m_caption_text); } void CaptionHeight(const int height) { m_caption_height=height; } int CaptionHeight(void) const { return(m_caption_height); } void CaptionBgColor(const color clr) { m_caption_bg_color=clr; } color CaptionBgColor(void) const { return(m_caption_bg_color); } void CaptionBgColorHover(const color clr) { m_caption_bg_color_hover=clr; } color CaptionBgColorHover(void) const { return(m_caption_bg_color_hover); } void CaptionBgColorOff(const color clr) { m_caption_bg_color_off=clr; } //--- ウィンドウプロパティ void WindowBgColor(const color clr) { m_bg_color=clr; } color WindowBgColor(void) { return(m_bg_color); } void WindowBorderColor(const color clr) { m_border_color=clr; } color WindowBorderColor(void) { return(m_border_color); } //--- インディケータサブウィンドウのモード void RollUpSubwindowMode(const bool flag,const bool height_mode); //--- インディケータサブウィンドウの高さの変更 void ChangeSubwindowHeight(const int height); };
クラスコンストラクタ内の変数のデフォルト値による初期化コンストラクタの本体では、インタフェースコントロールのクラス名を格納し、マウスの左クリックのための優先順位の厳密な順序を設定します。
//+------------------------------------------------------------------+ //| コンストラクタ | //+------------------------------------------------------------------+ CWindow::CWindow(void) : m_last_id(0), m_subwindow_height(0), m_rollup_subwindow_mode(false), m_height_subwindow_mode(false), m_is_minimized(false), m_tooltips_button(false), m_window_type(W_MAIN), m_icon_file(""), m_right_limit(20), m_caption_height(20), m_caption_bg_color(C'88,157,255'), m_caption_bg_color_off(clrSilver), m_caption_bg_color_hover(C'118,177,255'), m_bg_color(C'15,15,15'), m_border_color(clrLightGray) { //--- コントロールクラス名を機保温クラスに格納する CElement::ClassName(CLASS_NAME); //--- 優先順位の厳密な順序を設定する m_bg_zorder =0; m_caption_zorder =1; m_button_zorder =2; }
上記のコードの黄色で強調表示されたメソッドの実装が以下に提示されます。基本クラスの変数やメソッドが必要な場合、 クラス名とダブルコロンが付けるのが一番望ましいです(ClassName::)。この方法を使うと、しばらくコードを触っていない後でも、コードを思い出し研究するのが容易かつ迅速になります。m_subwindow_height変数の名前はウィンドウ作成時に自動的に定義されます。
//+------------------------------------------------------------------+ //| インディケータサブウィンドウ最小化のモード | //+------------------------------------------------------------------+ void CWindow::RollUpSubwindowMode(const bool rollup_mode=false,const bool height_mode=false) { if(CElement::m_program_type!=PROGRAM_INDICATOR) return; //--- m_rollup_subwindow_mode =rollup_mode; m_height_subwindow_mode =height_mode; //--- if(m_height_subwindow_mode) ChangeSubwindowHeight(m_subwindow_height); }
CWindow::RollUpSubwindowMode()メソッドについては、後に、ターミナルチャートの実用的な例を考慮し始めた後にチャプターの一つで戻ります。これによって、示唆されたモードのそれぞれが何を表すのかより明確になることと思います。
CWindow::ChangeSubwindowHeight()メソッドは、プログラムが適切なモードである場合にインディケータサブウィンドウの高さの調整に使われます。
//+------------------------------------------------------------------+ //| インディケータサブウィンドウの高さを変更する | //+------------------------------------------------------------------+ void CWindow::ChangeSubwindowHeight(const int height) { if(CElement::m_subwin<1 || CElement::m_program_type!=PROGRAM_INDICATOR) return; //--- if(height>0) ::IndicatorSetInteger(INDICATOR_HEIGHT,height); }
フォーム作成のメソッド
ウィンドウの全ての複合部分が作成後に表示されるようにするには、それらは厳密な順序で(層ごとに)を設定する必要があります。
- 背景
- ヘッダー
- ヘッダーコントロール
CWindow::CreateWindow()メソッドの冒頭の、ウィンドウの全てのパーツが作成される部分で、このウィンドウのポインタがコントロールベースに格納されている確認がとられるべきです。格納されていない場合、ウィンドウの識別子はWRONG_VALUEの値を有します。その場合には、ウィンドウが作成されず、アクションを実行する必要があるかについてのメッセージが操作ログに印字され、関数がfalseを返します。全てが成功した場合、現在のクラスおよび基本クラスの変数は渡されたパラメータの値で初期化されます。これらの変数には次が含まれます。
- チャート識別子。
- チャートウィンドウ識別子。
- ウィンドウ名(ヘッダーテキスト)。
- ウィンドウを設定する (x, y)座標。
- フォームの高さを複製した変数 (m_bg_full_height)。これはフォームサイズの変更に使用されます。フォームの高さは、フォームを作成するメソッドが適用される前に設定されます(例が後に続きます)。
チャート識別子とチャートのウィンドウ番号はCWndEventsコンストラクタクラスで定義されています。カスタムクラスでコントロールを作成するときは(CWndEvents::CProgram)、これらの値が作成メソッドに受け渡されます。
その後、全てのフォームオブジェクトを作成するためのメソッドが続きます。オブジェクトの作成が一つでも失敗した場合、関数はfalseを返します。全てのオブジェクトをインスタンス化した後、プログラムタイプおよび設定モードに応じて、ウィンドウのサイズを設定する必要があるかどうかのチェックが行わなければなりません(別チャートウィンドウのインディケータである場合)。ウィンドウ作成時に初期化される変数については前述しました。これがまさに初期化が起こる場所です。以下のコードでは、それが黄色で強調表示されています。
そして、ウィンドウがダイアログウィンドウであることが確立された場合には、これを非表示にする必要があります。説明が必要です。プログラムインターフェースに複数のウィンドウが含まれている場合、これらのウィンドウは、プログラムのロード中にチャートに添付されます。よって、ダイアログウィンドウを呼び出すときに、ユーザーはダイアログウィンドウを作成/削除し続ける必要はありません。それらは全てチャート上に存在しますが、非表示モードになります。ダイアログウィンドウは、呼び出されると表示され、閉じられると削除されずに隠されます。
上記に言った全てが以下のコードに反映されています。
//+------------------------------------------------------------------+ //| コントロールフォームを作成する | //+------------------------------------------------------------------+ bool CWindow::CreateWindow(const long chart_id,const int subwin,const string caption_text,const int x,const int y) { if(CElement::Id()==WRONG_VALUE) { ::Print(__FUNCTION__," > Before creating a window, its pointer is to be passed " "to the window array using the CWndContainer::AddWindow(CWindow &object) method."); return(false); } //--- 変数の初期化 m_chart_id =chart_id; m_subwin =subwin; m_caption_text =caption_text; m_x =x; m_y =y; m_bg_full_height =m_y_size; //--- ウィンドウのオブジェクトを全て作成 if(!CreateBackground()) return(false); if(!CreateCaption()) return(false); if(!CreateLabel()) return(false); if(!CreateIcon()) return(false); if(!CreateButtonClose()) return(false); if(!CreateButtonRollUp()) return(false); if(!CreateButtonUnroll()) return(false); if(!CreateButtonTooltip()) return(false); //--- プログラムがインディケータの場合 if(CElement::ProgramType()==PROGRAM_INDICATOR) { //--- サブウィンドウの高さを定義するモードが設定されている場合 if(m_height_subwindow_mode) { m_subwindow_height=m_bg_full_height+3; ChangeSubwindowHeight(m_subwindow_height); } } //--- ダイアログウィンドウの場合非表示にする if(m_window_type==W_DIALOG) Hide(); //--- return(true); }
コントロールオブジェクトを非表示にするにはHide() virtual関数が使用されます。上記のコードではこれが緑で強調表示されています。以前にはCWindowクラスについては宣言のみを考慮しました。このコードでは、ウィンドウの全てのオブジェクトは全ての時間軸で非表示になっており基本クラスのm_is_visible 変数がfalseステータスを受け取ります。
//+------------------------------------------------------------------+ //| ウィンドウを非表示にする | //+------------------------------------------------------------------+ void CWindow::Hide(void) { //--- 全てのオブジェクトを非表示にする for(int i=0; i<ObjectsElementTotal(); i++) Object(i).Timeframes(OBJ_NO_PERIODS); //--- 表示の状態 CElement::IsVisible(false); }
上記が実装されてもCWindowクラスをコンパイルするとエラーメッセージが出ます(下記のスクリーンショットを参照)。これは、ウィンドウの部分を作成する全てのメソッドに現在コンテンツがないために発生します。なのでここでその実装にいくらかの時間を捧げることにします。
図3。メソッドの実装が存在しないことについてのメッセージ
背景から初めてCWindow::CreateBackground()メソッドを使います。オブジェクトの作成に関連する各メソッドの先頭では、このオブジェクトの名前を形成しなければなりません。これは、いくつかの部分で構成されます。
- プログラム名
- コントロールへの所属 (“window”)
- コントロールの一部への所属 (“bg”)
- コントロール識別子
パーツは、アンダースコア記号(_)で区切られます。プログラムの名前は、コンストラクタの初期化リスト内のCElement基本クラスに保存されます。コントロールの識別子はコントロールベースにウインドウが追加される時点で得られます。
その後、ウィンドウが最小化または最大化されている場合に応じてウィンドウの背景のサイズのチェックと調整を行うものとします。少し先読みをします。チャートの時間枠や銘柄が切り替えられると、全てのオブジェクトは、プログラムの初期化解除の関数に移動されます。プログラムの初期化関数では、クラスフィールド(変数)の現在の値に応じての復元が行われます。
背景サイズの調整後、オブジェクトの設定に続いてプロパティの設定が行われます。メソッドの最後には、オブジェクトポインタは、基本クラスの配列に保存されています。下記はCWindow::CreateBackground()関数のコードです。
//+------------------------------------------------------------------+ //| ウィンドウ背景を作成する | //+------------------------------------------------------------------+ bool CWindow::CreateBackground(void) { //--- ウィンドウ名の形成 string name=CElement::ProgramName()+"_window_bg_"+(string)CElement::Id(); //--- ウィンドウサイズは状態(最小化/最大化)による int y_size=0; if(m_is_minimized) { y_size=m_caption_height; CElement::YSize(m_caption_height); } else { y_size=m_bg_full_height; CElement::YSize(m_bg_full_height); } //--- ウィンドウ背景の設定 if(!m_bg.Create(m_chart_id,name,m_subwin,m_x,m_y,m_x_size,y_size)) return(false); //--- 優先順位の設定 m_bg.BackColor(m_bg_color); m_bg.Color(m_border_color); m_bg.BorderType(BORDER_FLAT); m_bg.Corner(m_corner); m_bg.Selectable(false); m_bg.Z_Order(m_bg_zorder); m_bg.Tooltip("\n"); //--- オブジェクトポインタを格納する CElement::AddToArray(m_bg); return(true); }
ウィンドウヘッダー作成メソッドでは、ウィンドウ背景作成メソッドでの実装と似た基本的なアクションに加えて、Objects.mqhファイルにあるCRectLabelクラスのプリミティブオブジェクトの座標の保存が必要です。それらのフィールドの値は、ウィンドウが移動されたり大きさが変化したとき(したがって、その座標が変更されたとき)に更新されます。これらの値が静的である場合は、ヘッダー上でカーソルを追跡することが不可能になります。このルールは、他の全てのコントロール内のオブジェクトに適用されます。
後1つの違いは、ウィンドウ領域にあるまたはウィンドウ領域を離れるマウスカーソルへのヘッダーの反応能力にあります。このためには、InitColorArray()メソッドがCElementクラスに作成されました。
CElement::CreateCaption()メソッドのコードは下記です。
//+------------------------------------------------------------------+ //| ウィンドウヘッダーを作成する | //+------------------------------------------------------------------+ bool CWindow::CreateCaption(void) { string name=CElement::ProgramName()+"_window_caption_"+(string)CElement::Id(); //--- ウィンドウヘッダーを設定する if(!m_caption_bg.Create(m_chart_id,name,m_subwin,m_x,m_y,m_x_size,m_caption_height)) return(false); //--- 優先順位の設定 m_caption_bg.BackColor(m_caption_bg_color); m_caption_bg.Color(m_border_color); m_caption_bg.BorderType(BORDER_FLAT); m_caption_bg.Corner(m_corner); m_caption_bg.Selectable(false); m_caption_bg.Z_Order(m_caption_zorder); m_caption_bg.Tooltip("\n"); //--- 座標を格納する m_caption_bg.X(m_x); m_caption_bg.Y(m_y); //--- サイズを(オブジェクトに)格納する m_caption_bg.XSize(m_caption_bg.X_Size()); m_caption_bg.YSize(m_caption_bg.Y_Size()); //--- 配列グラデーションの初期化 CElement::InitColorArray(m_caption_bg_color,m_caption_bg_color_hover,m_caption_color_bg_array); //--- オブジェクトポインタを格納する CElement::AddToArray(m_caption_bg); return(true); }
インタフェースのメインウィンドウ内のプログラムラベルはデフォルトでは前もって準備されたアイコンで表されます。どちらが反映されるかは、開発中のプログラムの種類(ENUM_PROGRAM_TYPE)に依存します。全てのラベルのアイコンは本稿の最後でダウンロードすることができます。プログラムで使用されるには、クリックできないコントロールのアイコンは<data folder>\MQLX\Images\EasyAndFastGUI\Icons\bmp16ディレクトリに配置されるべきです。フォルダ名のbmp16はフォルダに含まれるアイコンサイズが16x16画素であることを示します。例えば、お持ちのライブラリアイコンのコレクションが24x24サイズの場合、フォルダ名はbmp24 となります。
コントロールを作成するためにデフォルトで使用されている標準的なアイコンは、<data folder>\MQLX\Images\EasyAndFastGUI\Controlsディレクトリに配置される必要があります。
メインプログラムウィンドウラベルの自動認識には、DefaultIcon()メソッドがCWindowクラスで宣言されています。下記がこのメソッドのコードです。
//+------------------------------------------------------------------+ //| デフォルトラベルの認識 | //+------------------------------------------------------------------+ string CWindow::DefaultIcon(void) { string path="Images\\EasyAndFastGUI\\Icons\\bmp16\\advisor.bmp"; //--- switch(CElement::ProgramType()) { case PROGRAM_SCRIPT: { path="Images\\EasyAndFastGUI\\Icons\\bmp16\\script.bmp"; break; } case PROGRAM_EXPERT: { path="Images\\EasyAndFastGUI\\Icons\\bmp16\\advisor.bmp"; break; } case PROGRAM_INDICATOR: { path="Images\\EasyAndFastGUI\\Icons\\bmp16\\indicator.bmp"; break; } } //--- return(path); }
スクリプトではデータの可視化に関するインターフェースコントロールのみが使用することができることには留意されなければなりません。ここでの説明は、スクリプトはイベントを追跡するための関数を使用する機会を提供しないことです。
ウィンドウのアイコンを再定義する必要がある場合は、CWindow::IconFile()メソッドが使用されます。端末内のチャート上でこのライブラリに基づいたインターフェイスをテストする場合は、このメソッドおよび他のメソッドの例を考えます。
メソッドの次(メソッド本体の外)には様々なコントロールのアイコンを持つリソース(#resource)が含まれます (黄色で強調表示)。ラベルは、常に1つの状態(1つのアイコン)を持つので、プリミティブオブジェクトCChartObjectBmpLabel::BmpFileOn()及びCChartObjectBmpLabel::BmpFileOff()のメソッドでは1バージョンへのパスのみが指定されます。
各ウィンドウの全てのコントロールの(ウィンドウヘッダを除く)全てのオブジェクトの座標は、このメソッドのローカル変数で、このウィンドウの座標(左上隅)と相対して計算されます。その結果、ウィンドウ座標を基準にした全てのオブジェクトのインデントはObjects.mqhファイルのプリミティブオブジェクトクラスの対応するフィールドに格納されています。これらは、ウィンドウが再配置される際の座標の再計算が必要としない為に必要です。それは、オブジェクト設定の際に行われるような全てのコントロールの各オブジェクトの座標の更新のためには、2つの代わりに1つの操作のみが必要とされることを意味します。以下のコードでは、ウィンドウのエッジ点からのインデントの不在は緑色で強調表示されています。
//+------------------------------------------------------------------+ //| プログラムラベルを作成する | //+------------------------------------------------------------------+ //--- プログラムタイプを象徴する(デフォルト)アイコン #resource "\\Images\\EasyAndFastGUI\\Icons\\bmp16\\advisor.bmp" #resource "\\Images\\EasyAndFastGUI\\Icons\\bmp16\\indicator.bmp" #resource "\\Images\\EasyAndFastGUI\\Icons\\bmp16\\script.bmp" //--- bool CWindow::CreateIcon(void) { string name=CElement::ProgramName()+"_window_icon_"+(string)CElement::Id(); //--- オブジェクト座標 int x=m_x+5; int y=m_y+2; //--- ウィンドウアイコンの設定 if(!m_icon.Create(m_chart_id,name,m_subwin,x,y)) return(false); //--- ユーザーが指定しなかった場合のデフォルトアイコン if(m_icon_file=="") m_icon_file=DefaultIcon(); //--- 優先順位の設定 m_icon.BmpFileOn("::"+m_icon_file); m_icon.BmpFileOff("::"+m_icon_file); m_icon.Corner(m_corner); m_icon.Selectable(false); m_icon.Z_Order(m_button_zorder); m_icon.Tooltip("\n"); //--- 座標を格納する m_icon.X(x); m_icon.Y(y); //--- エッジポイントからのインデント m_icon.XGap(x-m_x); m_icon.YGap(y-m_y); //--- サイズを(オブジェクトに)格納する m_icon.XSize(m_icon.X_Size()); m_icon.YSize(m_icon.Y_Size()); //--- オブジェクトをグループ配列に追加する CElement::AddToArray(m_icon); return(true); }
ヘッダのテキストラベルを作成するためのCWindow::CreateLabel()メソッドのコードは、プログラムのラベルを作成する方法で説明したものとは異なる任意の特殊性を持っていないため、記事内のスペースを節約するためにここでそれを説明しません。コードは本稿の終わりのWindow.mqhファイルにあります。
ここでは、ウィンドウヘッダーに配置されるボタンについて話します。 これらのボタンは全て、上記のメソッドとは異なるルールを持っています。
- カーソルが上にマウスを移動したときの外観を変更します。これが、ボタンのために2つのアイコンがロードされる理由です。そのうち一つはカーソルがボタン上にあるON状態に使われます。2つ目はカーソルがボタン領域外にあるOFFの状態に使われます。
- ボタンはスクリプトには設定されません。
- ダイアログウィンドウでは、ウィンドウを閉じるためのボタンのみが利用できます。
- ウィンドウを移動するためのヘッダー内のキャプチャエリアはボタンの数に応じて計算されます。
ウィンドウを最小化/最大化する機能としては、2つのボタンが必要になります。これらのボタンは重なって位置し、その可視姓はウィンドウの状態に依存します。ウィンドウが最大化されている場合には、ウィンドウを最小化するためのボタンが表示され、その逆もあてはまります。
他の面では、これらのボタンは、コントロールフォームの部分を作成するための他のメソッドから異なるものではありません。CreateButtonClose()、CreateButtonRollUp()、CreateButtonUnroll()及びCreateButtonTooltip()メソッドの詳細のコードは本稿の最後に添付されたWindow.mqhファイルで見られます。
以下の例で使用される全てのボタンアイコンもまた、本稿の最後にダウンロードすることができます。自分のアイコンの利用も可能です。その場合には、添付されたコードと同名のBMPファイルが格納されている、またはファイル名が本稿の物と違う場合、BMPファイルパスをコード内で変更する必要がありことにご注意ください。
チャートへのフォームの連結
ここでついに、今までやったことをテストし、その結果を見ることができる段階にあります。コントロールフォームがチャート上どのように見えるかを見てみましょう。 このフォームを作成するための機能は、上記で作成しました。
以前に作成されたテスト用のファイルでの作業を続行します。以前はCProgram::CreateTradePanel()インターフェースを作成するための共通メソッドとコントロールフォーム作成メソッド CProgram::CreateWindow()の作成に留まりました。下記はフォーム作成コードです。背景色やフォームのヘッダーの再定義の方法にご注意ください。
//+------------------------------------------------------------------+ //| コントロールフォームを作成する | //+------------------------------------------------------------------+ bool CProgram::CreateWindow(const string caption_text) { //--- ウィンドウ配列にウィンドウポインタを追加する CWndContainer::AddWindow(m_window); //--- プロパティ m_window.XSize(200); m_window.YSize(200); m_window.WindowBgColor(clrWhiteSmoke); m_window.WindowBorderColor(clrLightSteelBlue); m_window.CaptionBgColor(clrLightSteelBlue); m_window.CaptionBgColorHover(C'200,210,225'); //--- フォームの作成 if(!m_window.CreateWindow(m_chart_id,m_subwin,caption_text,1,1)) return(false); //--- return(true); }
CProgram::CreateTradePanel()共通メソッドで、ウィンドウヘッダーテキストを唯一のパラメータとしてCProgram::CreateWindow()メソッドを呼び出します。
//+------------------------------------------------------------------+ //| 取引パネルを作成する | //+------------------------------------------------------------------+ bool CProgram::CreateTradePanel(void) { //--- コントロールフォームの作成 if(!CreateWindow("EXPERT PANEL")) return(false); //--- コントロールの作成 // ... //--- ::ChartRedraw(); return(true); }
ここではCProgram::CreateTradePanel()メソッドの呼び出しをプログラムのメインファイルのOnInit()関数で追加するだけです。プログラムインターフェースの作成中にエラーが発生した場合、操作ログに対応するメッセージを印刷して作業を切り上げます。
//+------------------------------------------------------------------+ //| エキスパート初期化関数 | //+------------------------------------------------------------------+ int OnInit(void) { program.OnInitEvent(); //--- 取引パネルを設定する if(!program.CreateTradePanel()) { ::Print(__FUNCTION__," > Failed to create graphical interface!"); return(INIT_FAILED); } //--- 初期化が完成 return(INIT_SUCCEEDED); }
コードをコンパイルしてプログラムをチャートに読み込みます。全てが正しい場合は、下のスクリーンショットのようにチャート上のフォームが表示されます。
図4。チャートにフォームを取り付ける最初のテスト
MQLアプリケーションを開発する際、ポインタベースにウィンドウポインタを追加することを忘れた場合でも、コードをコンパイルすることができます。ただし、チャートにプログラムを読み込もうとすると、プログラムは即座に削除され、次のメッセージが「エキスパートアドバイザー」タブ(「ツール」パネル)の操作ログに表示されます。
2015.09.23 19:26:10.398 TestLibrary (EURUSD,D1) OnInit > Failed to create graphical interface! 2015.09.23 19:26:10.398 TestLibrary (EURUSD,D1) CWindow::CreateWindow > Before creating a window, its pointer is to be saved in the base: CWndContainer::AddWindow(CWindow &object).
CWindowクラスメソッドをインディケータとスクリプトで使用するもっと詳細な例はシリーズの次のチャプター(記事)で紹介されます。
おわりに
開発中のライブラリの現在の構造は、以下の図に示されます。
図5。メインインタフェースコントロールおよびコントロールフォームのプロジェクトへの追加
CElement基本クラスから派生したインターフェースコントロールに関連したクラスは太い青の矢印の付いた大きな長方形に配置されています。インターフェース要素の全てはCWndContainerクラスのファイルに含まれます。
シリーズの最初の部分の全ての資料はお使いのコンピュータにダウンロードしてテストすることができます。これらのファイルに含まれている資料の使用についてご質問がある場合は、以下のリストにある記事のいずれかでライブラリの開発の詳細をご参照になるるか、本稿へのコメント欄でご質問ください。
第一部の記事(チャプター)のリスト:
- グラフィカルインタフェース I: ライブラリストラクチャの準備(チャプター 1)
- グラフィカルインタフェース I: コントロールフォーム(チャプター 2)
- グラフィカルインタフェース I: グラフィカルインタフェースの動画化(チャプター 3)
- グラフィカルインタフェース I: フォームボタンとインターフェイス要素削除のための関数(チャプター 4)
- グラフィカルインタフェース I: 種々のプログラム及びメタトレーダー4ターミナルでのライブラリのテスト(チャプター 5)
MetaQuotes Ltdによってロシア語から翻訳されました。
元の記事: https://www.mql5.com/ru/articles/2126
- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索