
グラフィカルインタフェース I: フォームボタンとインターフェイス要素削除のための関数(チャプター 4)
コンテンツ
はじめに
本稿はグラフィカルインターフェイスに関するシリーズの続きです。シリーズ第一弾のグラフィカルインタフェース I: ライブラリストラクチャの準備(チャプター 1)ではライブラリの目的を詳細に考慮します。第一部の記事へのリンクの完全なリストは各章の終わりにあります。そこではまた、開発の現段階でのライブラリの完全版をダウンロードすることができます。ファイルはアーカイブと同じディレクトリに配置される必要があります。
前回の記事ではCWindowクラスがチャート上でのフォームの移動を可能にする機能の追加によって拡張されました。コントロールフォームは、マウスカーソルの動きに反応します。本稿ではCWindowクラスの開発を続け、コントロールのクリックによってのフォーム管理を可能にするメソッドでそれをより豊かにします。フォームのボタンによってのプログラムの終了を有効にするだけでなく、フォームの最小化と最大化機能も実装します。
フォームボタンの機能
開発中のライブラリ内のフォームは、EAやインディケータのために義務付けられた2つの主要なボタンと、まったく表示されないかもしれない追加のボタンを1つ含んでいます。
1番右のボタンにはウィンドウを閉じる機能が含まれている必要があります。これがどのように正確に実装されるかはここで指定される必要があります。押されたボタンがメインウィンドウまたは別のダイアログウィンドウから呼び出されたダイアログウィンドウ内にある場合は、このダイアログウィンドウが閉じられます。押されたボタンがメインウィンドウ内にある場合、このプログラムがチャートから削除されます。
2番目のボタンは2つの部分から構成されています。もっと正確に言うと、これは2つのボタンです。それらの各々は、現在のモードに応じて表示されます。ウィンドウが最大化されている場合は、ウィンドウを最小化するためのボタンが表示されます。最小化されたウィンドウにはウィンドウを最大化するためのボタンがあります。
3番目のボタンは、カスタムツールヒントモードを有効にするために設計されています。「カスタムツールヒント」の意味について明確化が必要かと思います。MQL言語は、マウスカーソルがフォーカスしている各グラフィックオブジェクトのテキストツールヒントの反映を可能にします。
CWindowクラスのコードで、オブジェクトプロパティが指定された際、CChartObject::Tooltip()メソッドが各フォームオブジェクトの作成に使われたことにお気づきかとおもいます。オブジェクトにツールヒントが必要とされる場合には、カーソルがオブジェクト上にホバーされているときに表示されるテキストがTooltip()メソッドに渡される必要があります。ツールヒントが必要でない場合は"\n"テキストが渡されます。
このプロパティには、いくつかの制限があります。(1)テキストに128以上のシンボルが含まれている場合、完全には表示することができません (2)線の太さ、色、フォントといったテキストのプロパティは管理できません。したがって、これらは、カスタムツールヒントではありません。これらはMQL標準ツールヒントとして分類されます。このようなツールヒントは短いコメントに適しています。しかし、テキストが非常に長く、強調表示を伴う複数行での表示が必要な場合には適切でありません。
Excelはご存じだと思います。例として、このプログラムの全てのオプションでのツールヒントのありかたを見ることができます(下のスクリーンショットを参照)。開発中のライブラリではこの種のツールチップを表示できるクラスが作成されます。これらのツールヒントは、我々自身がその作成の機能を実装するので、カスタムと呼ばれます。この問題には、コントロールフォームクラスが完全に実装されてフォームに添付できるコントロールが少なくとも1つあるようになった後に初めて戻ってきます。
図1。Excel 2010でのツールヒント
ウィンドウを閉じるボタンから始めましょう。CloseWindow()メソッドをCWindowクラスで作成します。これは、グラフィックオブジェクトへのクリックによって発生した CHARTEVENT_OBJECT_CLICK識別子を持つイベントの処理の際にCWindow::OnEvent()チャートイベントハンドラ内から呼び出されます。このIDを持つイベントは任意のグラフィカルオブジェクトをクリックしたときに生成されます。その際、チャートイベントの文字列パラメータ(sparam)がクリックされたオブジェクトの名前を含みます。そのため、オブジェクト名のチェックは、正しいオブジェクトに操作が行われる確認のために必要です。
CWindow::CloseWindow()メソッドは、クリックされたオブジェクト名のパラメータを1つ受け取ります。 コードの冒頭に、クリックが我々のデザインによるウィンドウを閉じるためのボタンであるオブジェクトに対してであったかどうかのチェックがあります。その後、メソッドのコードはメインウィンドウとダイアログウィンドウのための2つのブランチに分かれます。マルチウィンドウモードがまだ完成していないので、ダイアログウィンドウのためのブランチは空のままにされます。これには後で実装のために戻ってきます。メインウィンドウでの作業の準備は完了です。これが、このコンディションの本体でプログラムのタイプ(EAやインディケータ)のチェックが必要とされる理由です。異なるタイプのプログラムの削除は、MQL言語の異なる機能を必要とするからです。
MQLは、ユーザーのさまざまなアクションを確認するためのダイアログウィンドウを呼び出すための独自の機能を有しています。これがMessageBox()関数です。チャートからEAを削除する確認のために一時的にこれを使用します。このウィンドウはモーダル型に属するので、インディケータの削除に使用することはできません。その理由は、インディケータはインタフェースストリームで実行され、それらを停止することができないことです。マルチウィンドウモードと「ボタン」コントロールが実装された時、我々のライブラリのメソッドで作成されたダイアログウィンドウが使用できるようになります。
プログラムがチャートから削除される前に、ユーザーの判断によってプログラムが削除されるというメッセージが端末の操作ログに印刷されます。
下記のコードにあるようにCWindowへの追加を紹介しましょう。
ウィンドウを閉じるためのCWindow::CloseWindow()の宣言と実装が下記です。
class CWindow: public CElement { public: //--- ウィンドウを閉じる bool CloseWindow(const string pressed_object); }; //+------------------------------------------------------------------+ //| ダイアログウィンドウかプログラムを閉じる | //+------------------------------------------------------------------+ bool CWindow::CloseWindow(const string pressed_object) { //--- クリックがウィンドウを閉じるボタンに対してでなかった場合 if(pressed_object!=m_button_close.Name()) return(false); //--- これがメインウィンドウの場合 if(m_window_type==W_MAIN) { //--- プログラムが「Expert Advisor」の場合 if(CElement::ProgramType()==PROGRAM_EXPERT) { string text="Do you want the program to be deleted from the chart?"; //--- ダイアログウィンドウを開く int mb_res=::MessageBox(text,NULL,MB_YESNO|MB_ICONQUESTION); //--- "Yes" ボタンが押された場合、プログラムをチャートから削除する if(mb_res==IDYES) { ::Print(__FUNCTION__," > The program was deleted from the chart due to your decision!"); //--- Expert Advisor をチャートから削除 ::ExpertRemove(); return(true); } } //--- プログラムがインディケータの場合 else if(CElement::ProgramType()==PROGRAM_INDICATOR) { //--- インディケータをチャートから削除 if(::ChartIndicatorDelete(m_chart_id,m_subwin,CElement::ProgramName())) { ::Print(__FUNCTION__," > The program was deleted from the chart due to your decision!"); return(true); } } } //--- ダイアログウィンドウの場合 else if(m_window_type==W_DIALOG) { } //--- return(false); }
チャートイベントハンドラでのCWindow::CloseWindow()メソッドの呼び出しは下記です。
//+------------------------------------------------------------------+ //| チャートイベントハンドラ | //+------------------------------------------------------------------+ void CWindow::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam) { //--- オブジェクトクリックのイベント処理 if(id==CHARTEVENT_OBJECT_CLICK) { //--- ウィンドウを閉じる CloseWindow(sparam); return; } }
以前にテストのに使用したライブラリファイルやEAをコンパイルします。チャートにEAを読み込みます。これでフォーム上のウィンドウを閉じるためのボタンを押すと、スクリーンショットに示されたようにウィンドウが表示されます。
図2。フォーム上のボタンを押してプログラムを閉じるテスト
ここで、フォームの最小化/最大化を可能にする2番目のボタンのメソッドを作成します。
- CWindow::RollUp()メソッドはフォームの最小化を扱います。
- CWindow::Unroll()メソッドはフォームの最大化を扱います。
これらのメソッドは非常に簡潔で、その内容は類似しています。各メソッドの開始時に、アイコンが変更され、背景のサイズ(高さ)が設定されて保存され、フォーカスがゼロにされ、ユーザーの選択に対応するステータスが設定されます。プログラムがチャートのメインウィンドウ以外に位置するインディケータである場合、フォームの作成時にはインディケータサブウィンドウの固定縦幅モードが設定され、インディケータサブウィンドウのサイズを変更することを可能にするモードが設定されて、次いで CWindow::ChangeSubwindowHeight()メソッドが呼ばれます。これは以前に作成されCWindowクラスにあるべきです。
CWindow::RollUp()及びCWindow::Unroll()メソッドのコードが下に詳しく表示されています。
//+------------------------------------------------------------------+ //| ウィンドウを最小化する | //+------------------------------------------------------------------+ void CWindow::RollUp(void) { //--- ボタンを変更する m_button_rollup.Timeframes(OBJ_NO_PERIODS); m_button_unroll.Timeframes(OBJ_ALL_PERIODS); //--- サイズを設定して格納する m_bg.Y_Size(m_caption_height); CElement::YSize(m_caption_height); //--- ボタンを無効にする m_button_unroll.MouseFocus(false); m_button_unroll.State(false); //--- フォームの「最小化」状態 m_is_minimized=true; //--- 固定された高さを持ったインディケータがありサブウインドウが最小化モードにがある場合、 // インディケータサブウィンドウのサイズを設定する if(m_height_subwindow_mode) if(m_rollup_subwindow_mode) ChangeSubwindowHeight(m_caption_height+3); } //+------------------------------------------------------------------+ //| ウィンドウを最大化する | //+------------------------------------------------------------------+ void CWindow::Unroll(void) { //--- ボタンを変更する m_button_unroll.Timeframes(OBJ_NO_PERIODS); m_button_rollup.Timeframes(OBJ_ALL_PERIODS); //--- サイズを設定して格納する m_bg.Y_Size(m_bg_full_height); CElement::YSize(m_bg_full_height); //--- ボタンを無効にする m_button_rollup.MouseFocus(false); m_button_rollup.State(false); //--- フォームの「最大化」状態 m_is_minimized=false; //--- 固定された高さを持ったインディケータがありサブウインドウが最小化モードにがある場合、 // インディケータサブウィンドウのサイズを設定する if(m_height_subwindow_mode) if(m_rollup_subwindow_mode) ChangeSubwindowHeight(m_subwindow_height); }
ここで、チャートイベントの文字列パラメータを受け入れるあと一つのメソッドを作成する必要があります。文字列パラメータを使ったクリックされたオブジェクトの名前のチェックはCWindow::CloseWindow()メソッドでの実装と似た形で行われます。押されたボタンに応じて、上記のコードで対応するメソッドが呼び出されます。このメソッドをCWindow::ChangeWindowState()と名付けましょう。この宣言と実装を追加して、以下のコードに示されるようにCWindowクラスのチャートイベントハンドラを呼び出します。
class CWindow: public CElement { public: //--- ウィンドウ状態の変更 bool ChangeWindowState(const string pressed_object); }; //+------------------------------------------------------------------+ //| チャートイベントハンドラ | //+------------------------------------------------------------------+ void CWindow::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam) { //--- オブジェクトクリックのイベント処理 if(id==CHARTEVENT_OBJECT_CLICK) { //--- ウィンドウを最大化/最小化する ChangeWindowState(sparam); return; } } //+------------------------------------------------------------------+ //| ウィンドウの最大化/最小化イベントをチェックする | //+------------------------------------------------------------------+ bool CWindow::ChangeWindowState(const string pressed_object) { //--- 「ウィンドウを最小化」ボタンが押された場合 if(pressed_object==m_button_rollup.Name()) { RollUp(); return(true); } //--- 「ウィンドウを最大化」ボタンが押された場合 if(pressed_object==m_button_unroll.Name()) { Unroll(); return(true); } //--- return(false); }
変更されたライブラリファイルとテストのために意図されたプログラムファイルをコンパイルします。ここで期待される結果は、コントロールフォームを最小化/最大化する機能です。下のスクリーンショットは、最小化モードでのフォームを示しています。
図3。フォーム機能のテスト
動作成功です。コントロールフォームはチャート上で移動でき、全てのオブジェクトがマウスカーソルに反応し、ボタンはデザインの機能に応じて動作します。
インターフェース機能の削除
これまでの記事の一連の推奨アクションに従った場合、EAがチャートから削除された場合に、グラフィカルインターフェースの全てのオブジェクトが削除されることを見ることができます。チャートからグラフィックオブジェクトを削除するメソッドはまだ説明されていません。EAが削除されたときにオブジェクトはなぜ削除されるのでしょうか。これは、標準ライブラリ、正確にはCChartObjectクラスのデストラクタで提供されていて、その派生クラスは我々のライブラリで使用されています。プログラムがチャートから削除された場合、これを含むクラスのデストラクタが呼び出されます。オブジェクトがこのチャートに接続されている場合、それが削除されます。
//+------------------------------------------------------------------+ //| デストラクタ | //+------------------------------------------------------------------+ CChartObject::~CChartObject(void) { if(m_chart_id!=-1) ::ObjectDelete(m_chart_id,m_name); }
EAがチャート上にあるときにチャートの銘柄や時間枠が変更された場合、デストラクタが呼び出されず、グラフィカルオブジェクトは削除されません。グラフィカルインターフェイスがメインプログラムファイルのOnInit()初期化関数で作成され、銘柄またはEAの時間枠の変更によって初期化解除と初期化が実施され、グラフィカルインターフェイスが既存のものの上に作成されます。その結果、このような変更の最初のインスタンスはオブジェクトの2つのコピーを生成します。チャートの銘柄や時間枠を変更し続ける場合は、インタフェースオブジェクトの多数のコピーができます。
図4。チャートの銘柄と時間枠の切り替え時のフォームのテスト
プログラムの初期化解除の際に全てのグラフィックオブジェクトが削除されていることを確認する必要があり、我々が開発しているライブラリでもこれを考慮しなければなりません。また、これらのオブジェクトへのポインタを含む全ての配列は空にされ各次元のサイズはゼロにされるべきです。その後、初期化の段階で、オブジェクトの設定はクローンなしで正しいはずです。ここでこのメカニズムを実装します。
Delete() virtualメソッドはCElementクラスで宣言されます。このメソッドはCElementクラスから派生したクラスの全てで独自に実装されます。Delete()メソッドは以前にCWindowクラスで宣言されました。今はこのメソッドをを実装するだけです(以下のコードを参照)。このメソッドでは、いくつかの変数はゼロにされ、全てのコントロールオブジェクトは削除され、基本クラス内のオブジェクトポインタ配列も空にされています。
//+------------------------------------------------------------------+ //| 削除 | //+------------------------------------------------------------------+ void CWindow::Delete(void) { //--- 変数のゼロ化 m_right_limit=0; //--- オブジェクトの削除 m_bg.Delete(); m_caption_bg.Delete(); m_icon.Delete(); m_label.Delete(); m_button_close.Delete(); m_button_rollup.Delete(); m_button_unroll.Delete(); m_button_tooltip.Delete(); //--- オブジェクト配列を空にする CElement::FreeObjectsArray(); //--- コントロールフォーカスをゼロにする CElement::MouseFocus(false); }
全てのインターフェースコントロールのDelete()メソッドへのアクセスはCWndEventsクラスで取得できます。よってグラフィカルインターフェースを削除するCWndEvents::Destroy()メソッドを作成します。このメソッドでは全てのコントロールのDelete()メソッドを呼び出して、全てのフォームのコントロールを反復処理します。Delete()メソッドの呼び出し前に、ポインタの有効性をチェックします。オブジェクトが削除された後、コントロール配列は空にする必要があります。フォームループを出た後、配列も空にしなければなりません。
下のコードはCWndEvents::Destroy()メソッドの宣言と実装を示します。
class CWndEvents : public CWndContainer { protected: //--- インターフェースの削除 void Destroy(void); }; //+------------------------------------------------------------------+ //| 全てのオブジェクトの削除 | //+------------------------------------------------------------------+ void CWndEvents::Destroy(void) { int window_total=CWndContainer::WindowsTotal(); for(int w=0; w<window_total; w++) { int elements_total=CWndContainer::ElementsTotal(w); for(int e=0; e<elements_total; e++) { //--- ポインタが不正の場合、次に移る if(::CheckPointer(m_wnd[w].m_elements[e])==POINTER_INVALID) continue; //--- コントロールオブジェクトを削除する m_wnd[w].m_elements[e].Delete(); } //--- コントロール配列を空にする ::ArrayFree(m_wnd[w].m_objects); ::ArrayFree(m_wnd[w].m_elements); } //--- フォーム配列を空にする ::ArrayFree(m_wnd); ::ArrayFree(m_windows); }
ここでメインプログラムのOnDeinit()関数と関係するCProgram::OnDeinitEvent()メソッドでDestroy()メソッドを呼び出します。下記のようにそこにそれを追加します。
//+------------------------------------------------------------------+ //| 初期化解除 | //+------------------------------------------------------------------+ void CProgram::OnDeinitEvent(const int reason) { //--- インターフェースの削除 CWndEvents::Destroy(); }
変更されたライブラリファイルとマインプログラムファイルをコンパイルします。EAをチャートに読み込み、チャートの銘柄と時間枠を数回変更します。全てが正しく動作するはずです。オブジェクトのクローンは、もはや表われません。問題解決です。
おわりに
次のチャプターでは、フォームが、インディケータやスクリプトなどの他のプログラムタイプでどのように機能するかを参照するためのテストを実行します。我々の最初の目標は、クロスプラットフォームなグラフィカルインタフェース作成のためのライブラリの作成だったので、テストはまたMetaTrader 4端末でも行われます。
パートIの資料はダウンロードされ、それがどのように動作するかをテストすることができます。それらのファイルの資料を使用についてご質問がある場合は、以下のリストにある記事のいずれかでライブラリの開発の詳細をご参照になるるか、本稿へのコメント欄でご質問ください。
第一部の記事(チャプター)のリスト:
MetaQuotes Ltdによってロシア語から翻訳されました。
元の記事: https://www.mql5.com/ru/articles/2128




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