English Русский 中文 Español Deutsch Português
グラフィカルインタフェース IV:情報インターフェース要素(チャプター1)

グラフィカルインタフェース IV:情報インターフェース要素(チャプター1)

MetaTrader 5 | 6 4月 2016, 12:08
548 0
Anatoli Kazharski
Anatoli Kazharski

コンテンツ


はじめに

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

開発の現段階では、グラフィカルインタフェース作成のライブラリは、フォームとそれに取り付けることができるいくつかのコントロールを含んでいます。今後の記事の1つがマルチウィンドウモードについてになることは、以前に言及されました。そのための準備が整ったので、それは次の章で対処します。この章では、ステータスバーとツールチップ情報インタフェース要素を作成するためのクラスを作成します。


ステータスバー要素

ステータスバーは、グラフィカルインタフェースの情報要素の一つです。この要素は、重要なデータ、詳細、値などの迅速なエントリのために使用されています。ステータスバーはMetaTraderターミナルでも使われています。これは、いくつかのセクション(項目)で構成されています。1番目は、マウスカーソルが位置しているターミナルの部分またはプログラムコマンド名についての情報を提供します。カーソルが価格チャートの領域上に移動された場合に日付と価格を反映する項目もあります。マウスの左クリックで表示できるコンテキストメニューを含む項目もあります。MetaEditor コードエディタもステータスバーを用意しています。その項目はプログラムコマンド、カーソル位置(行/列)とテキスト入力モード(INS/ OVR)を反映しています。ターミナルとコードエディタのステータスバーの詳細についてはヘルプ(F1)で見つけることができます。

本稿では、その項目にコンテキストメニューを取り付けずに単純なステータスバーを作成します。他のインターフェイス要素と同様に、ステータスバーは、いくつかのプリミティブオブジェクトで構成されています。

  • 背景
  • 項目
  • 区切り線

図1。ステータスバーの複合部分

図1。ステータスバーの複合部分


StatusBar.mqhファイルを作成してWndContainer.mqhファイルに含んでライブラリ全体での利用を可能にします。下記のコードは、すべてのコントロールクラスで使われる仮想メソッドを持つCStatusBarクラスを示します。 

//+------------------------------------------------------------------+
//|                                                    StatusBar.mqh |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#include "Element.mqh"
#include "Window.mqh"
//+------------------------------------------------------------------+
//| ステータスバー作成クラス                                         |
//+------------------------------------------------------------------+
class CStatusBar : public CElement
  {
private:
   //--- 要素が取り付けられるフォームへのポインタ
   CWindow          *m_wnd;
   //---
public:
                     CStatusBar(void);
                    ~CStatusBar(void);
   //--- フォームポインタを格納する
   void              WindowPointer(CWindow &object)                   { m_wnd=::GetPointer(object);   }
   //---
public:
   //--- チャートイベントハンドラ
   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);
  };
//+------------------------------------------------------------------+
//| コンストラクタ                                                   |
//+------------------------------------------------------------------+
CStatusBar::CStatusBar(void)
  {
  }
//+------------------------------------------------------------------+
//| デストラクタ                                                       |
//+------------------------------------------------------------------+
CStatusBar::~CStatusBar(void)
  {
  }
//+------------------------------------------------------------------+

他のコントロールクラスとの一貫性を保つため、このバージョンではイベント処理に使われるOnEvent() とOnEventTimer() メソッドは使われません。 

ここで、ステータスバーのプロパティを考察します。配列が使用されるオブジェクトとして、それらには一般的でユニークなプロパティがあります。ユニークなものはアイテムの幅のみです。一般的なプロパティのリストはもっと長く、以下に提示されています。

共通プロパティ:

  • 背景と背景フレームの色
  • テキストの色
  • 区切り線の色
  • マウスの左クリックのプロパティ

コンストラクタのデフォルト値によってプロパティフィールドを初期化します。ユーザは、要素の作成時に、クラスのパブリックメソッドを使用してそれらを再定義することができます。 

class CStatusBar : public CElement
  {
private:
   //--- プロパティ:
   //    ユニークなプロパティの配列
   int               m_width[];
   //---(1)背景と(2)背景フレームの色
   color             m_area_color;
   color             m_area_border_color;
   //--- テキストの色
   color             m_label_color;
   //--- マウスの左クリックのプロパティ
   int               m_zorder;
   //--- 区切り線の色
   color             m_sepline_dark_color;
   color             m_sepline_light_color;
   //---
public:
   //---(1)背景と(2)背景フレームと(3)テキストの色
   void              AreaColor(const color clr)                       { m_area_color=clr;             }
   void              AreaBorderColor(const color clr)                 { m_area_border_color=clr;      }
   void              LabelColor(const color clr)                      { m_label_color=clr;            }
   //--- 区切り線の色
   void              SeparateLineDarkColor(const color clr)           { m_sepline_dark_color=clr;     }
   void              SeparateLineLightColor(const color clr)          { m_sepline_light_color=clr;    }
  };
//+------------------------------------------------------------------+
//| コンストラクタ                                                   |
//+------------------------------------------------------------------+
CStatusBar::CStatusBar(void) : m_area_color(C'240,240,240'),
                               m_area_border_color(clrSilver),
                               m_label_color(clrBlack),
                               m_sepline_dark_color(C'160,160,160'),
                               m_sepline_light_color(clrWhite)
  {
//--- 要素クラスの名前を基本クラスに格納する  
   CElement::ClassName(CLASS_NAME);
//--- 左マウスクリックの優先順位を設定する
   m_zorder=2;
  }

ステータスバーを作成するメソッドを考えてみましょう。背景を作成するにはCRectLabel型のプリミティブオブジェクトを使います。作成にはCEdit型のプリミティブオブジェクトの動的配列の宣言が必要です。以前にCSeparateLineクラスが区切り線作成のために書かれました。このクラスは、独立したインターフェース要素として使用できます。この場合、それはステータスバーの複合部分の一部として使用され、そのような要素の配列が必要になります。

項目は、ステータスバーを作成する前にCStatusBar::AddItem()メソッドを使って追加されなければなりません。さもないとグラフィカルインターフェースの作成は失敗します。項目の数はCStatusBar::ItemsTotal()メソッドで取得できます。 

//+------------------------------------------------------------------+
//|                                                    StatusBar.mqh |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#include "Element.mqh"
#include "Window.mqh"
#include "SeparateLine.mqh"
//+------------------------------------------------------------------+
//| ステータスバー作成クラス                                         |
//+------------------------------------------------------------------+
class CStatusBar : public CElement
  {
private:
   //--- ボタン作成のためのオブジェクト
   CRectLabel        m_area;
   CEdit             m_items[];
   CSeparateLine     m_sep_line[];
   //---
public:
   //---  ステータスバー作成メソッド                                         |
   bool              CreateStatusBar(const long chart_id,const int subwin,const int x,const int y);
   //---
private:
   bool              CreateArea(void);
   bool              CreateItems(void);
   bool              CreateSeparateLine(const int line_number,const int x,const int y);
   //---
public:
   //--- 項目数
   int               ItemsTotal(void)                           const { return(::ArraySize(m_items)); }

   //--- ステータスバーを作成する前に、指定されたプロパティを持つ項目を追加する
   void              AddItem(const int width);
  };

ステータスバーの項目作成メソッドとしてはCStatusBar::CreateItems() のみが考察されます。他のメソッドは、他のコントロールのクラスで考慮されたものと根本的に異なりません。

項目は、フレームを曖昧にしない洋に考慮して背景領域内に設定されます。これが1画素のマージンが一番初めに座標に追加された理由です。その後項目数のチェックが行われます。項目が設定されていない場合、メッセージが操作ログに出力され、グラフィカルインターフェイスの作成が終了します。 

最初の項目の幅が定義されていない場合は、それが自動的に計算されるようにします。ステータスバーの幅は、それが取り付けられているフォームの未満である(フォーム領域内に位置するように2画素が引かれる)ことになっています。最初の項目の幅を取得するには、他の項目が反復して追加され、この値がフォームの幅の値から控除されます。 

その後、ステータスバーの項目を作成するループと区切り線を作成するループが続きます。区切り線の座標は、各項目の座標に対して計算されます。最初の項目には区切り線は作成されません。よって、区切り線の数は、常に項目の数よりも1つ少なくなります。  

//+------------------------------------------------------------------+
//| ステータスバー項目のリストを作成する                               |
//+------------------------------------------------------------------+
bool CStatusBar::CreateItems(void)
  {
   int l_w=0;
   int l_x=m_x+1;
   int l_y=m_y+1;
//--- 項目数を取得する
   int items_total=ItemsTotal();
//--- グループに項目がない場合、報告して終了する
   if(items_total<1)
     {
      ::Print(__FUNCTION__," > This method is to be called, "
              "if a group contains at least one item!Use the CStatusBar::AddItem() method");
      return(false);
     }
//--- 一番目の項目の幅が設定されていない場合
   if(m_width[0]<1)
     {
      //--- ...他の項目に共通する幅に相対して計算する
      for(int i=1; i<items_total; i++)
         l_w+=m_width[i];
      //---
      m_width[0]=m_wnd.XSize()-l_w-(items_total+2);
     }
//--- 指定された数の項目を作成する
   for(int i=0; i<items_total; i++)
     {
      //--- オブジェクト名の形成
      string name=CElement::ProgramName()+"_statusbar_edit_"+string(i)+"__"+(string)CElement::Id();
      //--- X座標
      l_x=(i>0)?l_x+m_width[i-1] : l_x;
      //--- オブジェクトの作成
      if(!m_items[i].Create(m_chart_id,name,m_subwin,l_x,l_y,m_width[i],m_y_size-2))
         return(false);
      //--- プロパティの設定
      m_items[i].Description("");
      m_items[i].TextAlign(ALIGN_LEFT);
      m_items[i].Font(FONT);
      m_items[i].FontSize(FONT_SIZE);
      m_items[i].Color(m_label_color);
      m_items[i].BorderColor(m_area_color);
      m_items[i].BackColor(m_area_color);
      m_items[i].Corner(m_corner);
      m_items[i].Anchor(m_anchor);
      m_items[i].Selectable(false);
      m_items[i].Z_Order(m_zorder);
      m_items[i].ReadOnly(true);
      m_items[i].Tooltip("\n");
      //--- パネルの端からのマージン
      m_items[i].XGap(l_x-m_wnd.X());
      m_items[i].YGap(l_y-m_wnd.Y());
      //--- 座標
      m_items[i].X(l_x);
      m_items[i].Y(l_y);
      //--- サイズ
      m_items[i].XSize(m_width[i]);
      m_items[i].YSize(m_y_size-2);
      //--- オブジェクトポインタを格納する
      CElement::AddToArray(m_items[i]);
     }
//--- 区切り線の作成
   for(int i=1; i<items_total; i++)
     {
      //--- X座標
      l_x=m_items[i].X();
      //--- 線の作成
      CreateSeparateLine(i,l_x,l_y+2);
     }
//---
   return(true);
  }

また、すべての項目のテキストを変更するためのパブリックメソッドも必要になります。そのようなメソッドを作成してCStatusBar::ValueToItem()と呼びましょう。項目インデックスとそれに反映されなければならない行の数がパラメータとして受け取られます。 

class CStatusBar : public CElement
  {
public:
   //--- 指定されたインデックスで値を設定する
   void              ValueToItem(const int index,const string value);
  };
//+------------------------------------------------------------------+
//| 指定されたインデックスで値を設定する                         |
//+------------------------------------------------------------------+
void CStatusBar::ValueToItem(const int index,const string value)
  {
//--- 配列サイズの超過を確認する
   int array_size=::ArraySize(m_items);
   if(array_size<1 || index<0 || index>=array_size)
      return;
//--- 渡されたテキストの設定
   m_items[index].Description(value);
  }

 


ステータスバーのテスト

バー要素をテストするための準備がすべて整いました。前シリーズ(第三部)の最初のEAをテストに使用します。メインメニューをCIconButton型の5つのボタン以外の他のコントロールをすべて削除します。StatusBar.mqhファイルは既にライブラリに含まれていてそのインスタンスとステータスバー作成メソッドはCProgramカスタムクラスで作成できます。

2つの項目を持つステータスバーを作成します。初めの項目の幅は定義されず自動的に計算されます。ステータスバーの作成後には、例として最初の項目に «For Help, press F1» のテキストを設定します

//+------------------------------------------------------------------+
//| アプリケーション作成のクラス                                |
//+------------------------------------------------------------------+
class CProgram : public CWndEvents
  {
private:
   //--- ステータスバー
   CStatusBar        m_status_bar;
   //---
private:
   //--- ステータスバー
#define STATUSBAR1_GAP_X         (1)
#define STATUSBAR1_GAP_Y         (175)
   bool              CreateStatusBar(void);
  };
//+------------------------------------------------------------------+
//| 取引パネルを作成する                                        |
//+------------------------------------------------------------------+
bool CProgram::CreateTradePanel(void)
  {
//--- コントロールのフォーム1 の作成
//--- コントロールの作成
//    メインメニュー
//--- コンテキストメニュー
//--- ステータスバーの作成
   if(!CreateStatusBar())
      return(false);
//--- チャートの再描画
   m_chart.Redraw();
   return(true);
  }
//+------------------------------------------------------------------+
//| ステータスバーを作成する                                         |
//+------------------------------------------------------------------+
bool CProgram::CreateStatusBar(void)
  {
#define STATUS_LABELS_TOTAL 2
//--- パネルオブジェクトを受け渡す
   m_status_bar.WindowPointer(m_window1);
//--- 座標
   int x=m_window1.X()+STATUSBAR1_GAP_X;
   int y=m_window1.Y()+STATUSBAR1_GAP_Y;
//--- 幅
   int width[]={0,110};
//--- 作成前にプロパティを設定する
   m_status_bar.YSize(24);
//--- 部分の数を指定してプロパティを設定する
   for(int i=0; i<STATUS_LABELS_TOTAL; i++)
      m_status_bar.AddItem(width[i]);
//--- コントロールを作成する
   if(!m_status_bar.CreateStatusBar(m_chart_id,m_subwin,x,y))
      return(false);
//--- ステータスバーの最初の項目のテストを設定する
   m_status_bar.ValueToItem(0,"For Help, press F1");
//--- オブジェクトをオブジェクトグループの共通配列に追加する
   CWndContainer::AddToElementsArray(0,m_status_bar);
   return(true);
  }

ステータスバーの二番目の項目はローカル時刻を示します。これには、下記のコードに見られるように、アプリケーションタイマーにコードを追加します。つまり、項目は500ミリ秒ごとに更新されます。

//+------------------------------------------------------------------+
//| タイマー                                                            |
//+------------------------------------------------------------------+
void CProgram::OnTimerEvent(void)
  {
   CWndEvents::OnTimerEvent();

//--- ステータスバーの2番目の項目は500ミリ秒ごとに更新される
   static int count=0;
   if(count<500)
     {
      count+=TIMER_STEP_MSC;
      return;
     }
//---
   count=0;
   m_status_bar.ValueToItem(1,TimeToString(TimeLocal(),TIME_DATE|TIME_SECONDS));
   m_chart.Redraw();
  }

すべてが正しく行われた場合は、以下のスクリーンショットに示されたような結果が得られます。

図2。ステータスバー要素のテスト

図2。ステータスバー要素のテスト

 

ステータスバーの要素を作成するためのクラスを開発が完了しました。完全なコードは本稿添付のソースコードファイルで確認可能です。  

 


ツールヒント要素

ここで、ツールヒントを作成するためのクラスを開発します。この要素が別のクラスを必要とする理由はシリーズの第一部の第四章でフォームボタンの機能が考察されたときに詳細に説明されました。要するに、ツールヒントには、シンボルの数を制限しないことといくつかの単語を強調表示するオプションがあることが必要です。これを実装するためには要素を描画するためのクラスを使用します。インターフェイス要素の描画方法の例は、先に示されています。シリーズ第二部の第二章では区切り線を作成するCSeparateLineクラスが書かれました。ツールヒントのクラスの開発には同じ原理が使われます。

図3。Wordでのツールヒントの例

図3。Wordでのツールヒントの例

 

Tooltip.mqhファイルを作成して、すべてのインターフェース要素を含むWndContainer.mqhファイルに含みます。そして、この新しいファイル内で、すでによく知られているすべてのインターフェイス要素に標準なメソッドを持つクラスを作成します。このクラスでは、フォームポインタに加えて、ツールヒントが取り付けられる要素へのポインタ と子のポインタを格納するメソッドが存在します。 

//+------------------------------------------------------------------+
//|                                                      Tooltip.mqh |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#include "Element.mqh"
#include "Window.mqh"
//+------------------------------------------------------------------+
//| ツールヒントを作成するためのクラス                                   |
//+------------------------------------------------------------------+
class CTooltip : public CElement
  {
private:
   //--- 要素が取り付けられるフォームへのポインタ
   CWindow          *m_wnd;
   //--- ツールヒントが取り付けられた要素へのポインタ
   CElement         *m_element;
   //---
public:
   //--- (1) フォームポインタを格納し (2) 要素ポインタを格納する
   void              WindowPointer(CWindow &object)   { m_wnd=::GetPointer(object);     }
   void              ElementPointer(CElement &object) { m_element=::GetPointer(object); }
   //---
public:
   //--- チャートイベントハンドラ
   virtual void      OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam);
   //--- 要素の移動
   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);
  };
//+------------------------------------------------------------------+
//| コンストラクタ                                                   |
//+------------------------------------------------------------------+
CTooltip::CTooltip(void)
  {
  }
//+------------------------------------------------------------------+
//| デストラクタ                                                       |
//+------------------------------------------------------------------+
CTooltip::~CTooltip(void)
  {
  }

ユーザーがツールヒントを作成する前に設定できるプロパティ:

  • ヘッダー
  • ラインの配列

その他のプロパティのはデフォルト値を持ちます。それらは:

  • ツールヒントの背景のグラデーションの色 
  • 背景フレームの色
  • ヘッダの色
  • テキストの色
  • アルファチャンネル(ツールヒントの透明性の管理のため)

ライン配列はCTooltip::AddString()メソッドを使用してツールチップが作成される前に、行ごとに追加することができます。 

class CTooltip : public CElement
  {
private:
   //--- プロパティ:
   //    ヘッダ
   string            m_header;
   //--- ツールヒントの行の配列
   string            m_tooltip_lines[];
   //--- アルファチャネルの値(ツールヒントの透明性)
   uchar             m_alpha;
   //---(1)テキスト(2)背景と(3)背景フレームの色
   color             m_text_color;
   color             m_header_color;
   color             m_border_color;
   //--- 背景のグラデーションの配列
   color             m_array_color[];
   //---
public:
   //--- ツールヒントヘッダ
   void              Header(const string text)        { m_header=text;                  }
   //--- ツールヒントの行を追加する
   void              AddString(const string text);
  };
//+------------------------------------------------------------------+
//| コンストラクタ                                                   |
//+------------------------------------------------------------------+
CTooltip::CTooltip(void) : m_header(""),
                           m_alpha(0),
                           m_text_color(clrDimGray),
                           m_header_color(C'50,50,50'),
                           m_border_color(C'118,118,118'),
                           m_gradient_top_color(clrWhite),
                           m_gradient_bottom_color(C'208,208,235')
  {
//--- 要素クラスの名前を基本クラスに格納する  
   CElement::ClassName(CLASS_NAME);
  }
//+------------------------------------------------------------------+
//| 行を加える                                                     |
//+------------------------------------------------------------------+
void CTooltip::AddString(const string text)
  {
//--- 配列サイズを1要素で増やす
   int array_size=::ArraySize(m_tooltip_lines);
   ::ArrayResize(m_tooltip_lines,array_size+1);
//--- 渡されたパラメータの値を格納する
   m_tooltip_lines[array_size]=text;
  }

区切り線のクラスと同様に、ツールヒントの作成にはCRectCanvasクラスと2津のメソッド(パブリックとプライベート)を使います。 

class CTooltip : public CElement
  {
private:
   //--- ツールヒント作成オブジェクト
   CRectCanvas       m_canvas;
   //---
public:
   //--- ツールヒント作成メソッド
   bool              CreateTooltip (const long chart_id,const int subwin);
   //---
private:
   //--- ツールヒントのキャンバスを作成する
   bool              CreateCanvas(void);
  };

要素が取り付けられるフォームへのポインタのチェックに加えCTooltip::CreateTooltip() パブリックメソッドはツールヒントが意図された要素へのポインタも含みます。要素ポインタが存在する場合、ツールヒントの座標は要素の座標に相対して計算されます。我々の場合、これは要素の下限より1つ低い画素です。

//+------------------------------------------------------------------+
//| ツールヒントオブジェクトを作成する                                       |
//+------------------------------------------------------------------+
bool CTooltip::CreateTooltip(const long chart_id,const int subwin)
  {
//--- フォームポインタがなければ終了する
   if(::CheckPointer(m_wnd)==POINTER_INVALID)
     {
      ::Print(__FUNCTION__," > Before creating the tooltip, the class must be passed "
              "the form pointer: CTooltip::WindowPointer(CWindow &object).");
      return(false);
     }
//--- 要素ポインタがない場合は終了する
   if(::CheckPointer(m_element)==POINTER_INVALID)
     {
      ::Print(__FUNCTION__," > Before creating the tooltip, the class must be passed "
              "the element pointer: CTooltip::ElementPointer(CElement &object).");
      return(false);
     }
//--- ポインタの初期化
   m_id       =m_wnd.LastId()+1;
   m_chart_id =chart_id;
   m_subwin   =subwin;
   m_x        =m_element.X();
   m_y        =m_element.Y2()+1;
//--- 端からのマージン
   CElement::XGap(m_x-m_wnd.X());
   CElement::YGap(m_y-m_wnd.Y());
//--- ツールヒントを作成する
   if(!CreateTooltip())
      return(false);
//---
   return(true);
  }

CSeparateLineクラスの似たメソッドと違い、描画キャンバスを作成するためのCTooltip::CreateCanvas()プライベートメソッドでは、オブジェクトの作成とプロパティの設定に続いて、以下のアクションが実行されます。

  • ツールチップの背景グラデーション配列のサイズの設定。配列サイズはオブジェクトの縦幅の画素数(Y座標のサイズ)に等しいです。
  • グラデーション配列の初期化 
  • キャンバスをクリアする。透明度を100%に設定します。アルファチャンネルの値はゼロです。

オブジェクトは、メソッドの最後で要素オブジェクトの共通配列に追加されます。本メソッドのコードは下記です。 

//+------------------------------------------------------------------+
//| 描画のキャンバスを作成する                                   |
//+------------------------------------------------------------------+
bool CTooltip::CreateCanvas(void)
  {
//--- オブジェクト名の形成
   string name=CElement::ProgramName()+"_help_tooltip_"+(string)CElement::Id();
//--- キャンバスを作成する
   if(!m_canvas.CreateBitmapLabel(m_chart_id,m_subwin,name,m_x,m_y,m_x_size,m_y_size,COLOR_FORMAT_ARGB_NORMALIZE))
      return(false);
//--- チャートに取り付ける
   if(!m_canvas.Attach(m_chart_id,name,m_subwin,1))
      return(false);
//--- 優先順位の設定
   m_canvas.Background(false);
//--- 端からのマージン
   m_canvas.XGap(m_x-m_wnd.X());
   m_canvas.YGap(m_y-m_wnd.Y());
//--- ツールヒント背景のグラデーションの配列のサイズの設定
   CElement::GradientColorsTotal(m_y_size);
   ::ArrayResize(m_array_color,m_y_size);
//--- グラデーション配列を初期化する
   CElement::InitColorArray(m_gradient_top_color,m_gradient_bottom_color,m_array_color);
//--- キャンバスをクリアする
   m_canvas.Erase(::ColorToARGB(clrNONE,0));
   m_canvas.Update();
   m_alpha=0;
//--- オブジェクトポインタを格納する
   CElement::AddToArray(m_canvas);
   return(true);
  }

縦のグラデーションフレームを描画するメソッドを考えてみましょう。グラデーションのためには、キャンバスを作成する瞬間に既に必要な値で初期化された配列があります。これが、単にキャンバスの高さに等しい反復回数を持つループ内で配列から指定された色で線を描画することだけが必要な理由です。フレームを描画するために、メソッドの先頭で、キャンバスの各側の座標を持つ配列が宣言されて初期化されます。フレームが描画された後、1画素分で角を丸め、次にキャンバス内側の4隅にさらに4画像を追加します。 

両メソッドにはアルファチャネルを示すパラメータが1つのみあります。これは、これらのメソッドを呼び出すときに、グラデーションとフレームの透明度を指定することができることを意味します。 

//+------------------------------------------------------------------+
//| 縦のグラデーション                                                |
//+------------------------------------------------------------------+
void CTooltip::VerticalGradient(const uchar alpha)
  {
//--- X座標
   int x1=0;
   int x2=m_x_size;
//--- グラデーションを描画する
   for(int y=0; y<m_y_size; y++)
      m_canvas.Line(x1,y,x2,y,::ColorToARGB(m_array_color[y],alpha));
  }
//+------------------------------------------------------------------+
//| フレーム                                                            |
//+------------------------------------------------------------------+
void CTooltip::Border(const uchar alpha)
  {
//--- フレームの色
   color clr=m_border_color;
//--- 境界
   int x_size =m_canvas.X_Size()-1;
   int y_size =m_canvas.Y_Size()-1;
//--- 座標:上・右・下・左
   int x1[4]; x1[0]=0;      x1[1]=x_size; x1[2]=0;      x1[3]=0;
   int y1[4]; y1[0]=0;      y1[1]=0;      y1[2]=y_size; y1[3]=0;
   int x2[4]; x2[0]=x_size; x2[1]=x_size; x2[2]=x_size; x2[3]=0;
   int y2[4]; y2[0]=0;      y2[1]=y_size; y2[2]=y_size; y2[3]=y_size;
//--- 指定された座標でフレームを描画する
   for(int i=0; i<4; i++)
      m_canvas.Line(x1[i],y1[i],x2[i],y2[i],::ColorToARGB(clr,alpha));
//--- 隅を1画素で丸める
   clr=clrBlack;
   m_canvas.PixelSet(0,0,::ColorToARGB(clr,0));
   m_canvas.PixelSet(0,m_y_size-1,::ColorToARGB(clr,0));
   m_canvas.PixelSet(m_x_size-1,0,::ColorToARGB(clr,0));
   m_canvas.PixelSet(m_x_size-1,m_y_size-1,::ColorToARGB(clr,0));
//--- 指定された座標で画像を追加する
   clr=C'180,180,180';
   m_canvas.PixelSet(1,1,::ColorToARGB(clr,alpha));
   m_canvas.PixelSet(1,m_y_size-2,::ColorToARGB(clr,alpha));
   m_canvas.PixelSet(m_x_size-2,1,::ColorToARGB(clr,alpha));
   m_canvas.PixelSet(m_x_size-2,m_y_size-2,::ColorToARGB(clr,alpha));
  }

マウスカーソルが要素の上にホバーされるとツールヒントがすぐに表示され、カーソルが要素領域を離れるとツールヒントが徐々に退色されるようにしましょう。 

ツールヒントを表示するためのCTooltip::ShowTooltip()メソッドを作成しましょう。メソッドの開始時にはm_alphaフィールド(アルファチャンネル)の値のチェックがあります。アルファチャネルの値が255以上の場合、プログラムが実行されツールヒントが100%表示されているので、続ける理由はありません。そうでなければ、次のステップでツールヒントヘッダーの座標とのマージンが設定され、グラデーションとフレームが描画されます。ユーザーがヘッダーのテキストを指定していない場合は、ヘッダーが描画されません。ヘッダのテキストが存在する場合、フォントのパラメータが設定されてヘッダが描画されます。 

この後、ツールチップのメインテキストはヘッダの存在を考慮して設定されます。これで、メインテキストのパラメータが設定されます。ヘッダのボールドフォント(FW_BLACK)と異なり、メインフォントは目立ちません(FW_THIN)。次に、ツールヒントのテキストは、ユーザが初期化された配列から反復してキャンバスに入力されます。各行のY座標は、それぞれのループで指定された値で調整されます。一番最後に、キャンバスは表示のために更新され、完全に見えるツールチップの表示が設定されます。本メソッドのコードは下記です。 

//+------------------------------------------------------------------+
//| ツールヒントを表示する                                                |
//+------------------------------------------------------------------+
void CTooltip::ShowTooltip(void)
  {
//--- ツールヒントが100%見える場合は表示する
   if(m_alpha>=255)
      return;
//--- ヘッダーの座標とマージン
   int  x        =5;
   int  y        =5;
   int  y_offset =15;
//--- グラデーションを描画する
   VerticalGradient(255);
//--- フレームを描画する
   Border(255);
//--- (指定された場合)ヘッダを描画する
   if(m_header!="")
     {
      //--- フォントパラメータを設定する
      m_canvas.FontSet(FONT,-80,FW_BLACK);
      //--- ヘッダテキストを描画する
      m_canvas.TextOut(x,y,m_header,::ColorToARGB(m_header_color),TA_LEFT|TA_TOP);
     }
//--- ツールヒントのメインテキストの座標(ヘッダの存在が考慮される)
   x=(m_header!="")?15 : 5;
   y=(m_header!="")?25 : 5;
//--- フォントパラメータを設定する
   m_canvas.FontSet(FONT,-80,FW_THIN);
//--- ツールヒントのメインテキストを描画する
   int lines_total=::ArraySize(m_tooltip_lines);
   for(int i=0; i<lines_total; i++)
     {
      m_canvas.TextOut(x,y,m_tooltip_lines[i],::ColorToARGB(m_text_color),TA_LEFT|TA_TOP);
      y=y+y_offset;
     }
//--- キャンバスを更新する
   m_canvas.Update();
//--- ツールヒントが完全に表示されていることを示す
   m_alpha=255;
  }

ツールチップの緩やかなフェージングのためのメソッドを書いてCTooltip::FadeOutTooltip()と名付けましょう。このメソッドの開始時に、アルファチャンネルの値の逆方向のチェックが行われます。これは、完全な透明性が達成されている場合には実行を続ける必要がないことを意味します。そうでない場合は、キャンバスは完全な透明性が達成されるまで、フェージングのための指定されたステップで反復して再描画されます。このメソッドの完全版は、以下のコードに示されています。

//+------------------------------------------------------------------+
// ツールヒントの緩やかなフェージング
//+------------------------------------------------------------------+
void CTooltip::FadeOutTooltip(void)
  {
//--- ツールヒントが100%非表示の場合は終了する
   if(m_alpha<1)
      return;
//--- ヘッダのマージン
   int y_offset=15;
//--- 透明度ステップ
   uchar fadeout_step=7;
//--- ツールヒントの緩やかなフェージング
   for(uchar a=m_alpha; a>=0; a-=fadeout_step)
     {
      //--- 次のステップで負になる場合はループを停止する
      if(a-fadeout_step<0)
        {
         a=0;
         m_canvas.Erase(::ColorToARGB(clrNONE,0));
         m_canvas.Update();
         m_alpha=0;
         break;
        }
      //--- ヘッダの座標
      int x =5;
      int y =5;
      //--- グラデーションとフレームを描画する
      VerticalGradient(a);
      Border(a);
      //--- (指定された場合)ヘッダを描画する
      if(m_header!="")
        {
         //--- フォントパラメータを設定する
         m_canvas.FontSet(FONT,-80,FW_BLACK);
         //--- ヘッダテキストを描画する
         m_canvas.TextOut(x,y,m_header,::ColorToARGB(m_header_color,a),TA_LEFT|TA_TOP);
        }
      //--- ツールヒントのメインテキストの座標(ヘッダの存在が考慮される)
      x=(m_header!="")?15 : 5;
      y=(m_header!="")?25 : 5;
      //--- フォントパラメータを設定する
      m_canvas.FontSet(FONT,-80,FW_THIN);
      //--- ツールヒントのメインテキストを描画する
      int lines_total=::ArraySize(m_tooltip_lines);
      for(int i=0; i<lines_total; i++)
        {
         m_canvas.TextOut(x,y,m_tooltip_lines[i],::ColorToARGB(m_text_color,a),TA_LEFT|TA_TOP);
         y=y+y_offset;
        }
      //--- キャンバスを更新する
      m_canvas.Update();
     }
  }

ツールチップを作成して描画するために必要なすべてのメソッドがそろいました。これらは要素のイベントハンドラで呼ばれます。フォームでツールヒントを表示するボタンが押されたかをみるにはCWindowクラスで>CWindow::TooltipBmpState()メソッドを作成します。

class CWindow : public CElement
  {
public:
   //--- ツールヒント表示モードをチェックする
   bool              TooltipBmpState(void)                             const { return(m_button_tooltip.State());   }
  };

要素がフォーカスされている場合、ツールヒント表示モードがオンならばツールヒントが表示されます。マウスカーソルが要素領域の外にあるかフォームがブロックされている場合、ツールヒントは表示されません

//+------------------------------------------------------------------+
//| チャートイベントハンドラ                                              |
//+------------------------------------------------------------------+
void CTooltip::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
  {
//--- カーソル移動イベントの処理
   if(id==CHARTEVENT_MOUSE_MOVE)
     {
      //--- 要素が隠れている場合は終了する
      if(!CElement::IsVisible())
         return;
      //--- フォームのボタンが無効にされている場合は終了する
      if(!m_wnd.TooltipBmpState())
         return;
      //--- フォームがブロックされている場合
      if(m_wnd.IsLocked())
        {
         //--- ツールヒントを非表示にする
         FadeOutTooltip();
         return;
        }
      //--- 要素がフォーカスされている場合
      if(m_element.MouseFocus())
         //--- ツールヒントを表示する 
         ShowTooltip();
      //--- フォーカスのない場合
      else
      //--- ツールヒントを非表示にする
         FadeOutTooltip();
      //---
      return;
     }
  }

 


ツールヒントのテスト

ここで動作をテストできます。テストEAのフォーム上には現在5つのボタンがあります。すべてのボタンでツールチップを作成してみましょう。5つのCTooltip型のクラスのインスタンスと5つのメソッドを宣言します。ここでは、例として、1つのみの実装を提示します。

class CProgram : public CWndEvents
  {
private:
   CTooltip          m_tooltip1;
   CTooltip          m_tooltip2;
   CTooltip          m_tooltip3;
   CTooltip          m_tooltip4;
   CTooltip          m_tooltip5;
   //---
private:
   bool              CreateTooltip1(void);
   bool              CreateTooltip2(void);
   bool              CreateTooltip3(void);
   bool              CreateTooltip4(void);
   bool              CreateTooltip5(void);
  };
//+------------------------------------------------------------------+
//| ツールヒント5を作成する                                                |
//+------------------------------------------------------------------+
bool CProgram::CreateTooltip5(void)
  {
#define TOOLTIP5_LINES_TOTAL 3
//--- ウィンドウポインタを格納する
   m_tooltip5.WindowPointer(m_window1);
//--- 要素ポインタを格納する
   m_tooltip5.ElementPointer(m_icon_button5);
//--- ツールヒントテキストの配列
   string text[]=
     {
      "Control \"Icon button\" (5).",
      "This is the second line of the tooltip.",
      "This is the third line of the tooltip."
     };
//--- 作成前にプロパティを設定する
   m_tooltip5.Header("Icon Button 5");
   m_tooltip5.XSize(250);
   m_tooltip5.YSize(80);
//--- 線でテキスト行を追加する
   for(int i=0; i<TOOLTIP5_LINES_TOTAL; i++)
      m_tooltip5.AddString(text[i]);
//--- コントロールを作成する
   if(!m_tooltip5.CreateTooltip(m_chart_id,m_subwin))
      return(false);
//--- オブジェクトをオブジェクトグループの共通配列に追加する
   CWndContainer::AddToElementsArray(0,m_tooltip5);
   return(true);
  }

ツールヒントの作成は、それらがバースの要素配列の最後にあるように、グラフィカルインタフェースを作成するメソッドの最後に行われなければなりません。こうすると、ツールヒントは常にチャート上の他のオブジェクトの上になります。 

//+------------------------------------------------------------------+
//| 取引パネルを作成する                                        |
//+------------------------------------------------------------------+
bool CProgram::CreateTradePanel(void)
  {
//--- コントロールのフォーム1 の作成
//--- コントロールの作成
//    メインメニュー
//--- コンテキストメニュー
//--- ステータスバーの作成
//--- アイコンボタン

//--- ツールヒント
   if(!CreateTooltip1())
      return(false);
   if(!CreateTooltip2())
      return(false);
   if(!CreateTooltip3())
      return(false);
   if(!CreateTooltip4())
      return(false);
   if(!CreateTooltip5())
      return(false);
//--- チャートの再描画
   m_chart.Redraw();
   return(true);
  }

フォームでツールヒントボタンを表示するには、作成時にCWindow::UseTooltipsButton()メソッドを使います。

//+------------------------------------------------------------------+
//| コントロールのフォーム1を作成する                                      |
//+------------------------------------------------------------------+
bool CProgram::CreateWindow1(const string caption_text)
  {
//--- ウィンドウ配列にウィンドウポインタを追加する
   CWndContainer::AddWindow(m_window1);
//--- 座標
   int x=1;
   int y=20;
//--- プロパティ
   m_window1.Movable(true);
   m_window1.XSize(251);
   m_window1.YSize(200);
   m_window1.UseTooltipsButton();
   m_window1.CaptionBgColor(clrCornflowerBlue);
   m_window1.CaptionBgColorHover(C'150,190,240');
//--- フォームを作成する
   if(!m_window1.CreateWindow(m_chart_id,m_subwin,caption_text,x,y))
      return(false);
//---
   return(true);
  }

結果は以下のスクリーンショットのようなはずです。

図4。ツールヒントのテスト

図4。ツールヒントのテスト

 

うまくいきました。さて、この要素は、ポインタベースにプライベート配列を必要とします。マルチウィンドウモードを開発しているとき、このような配列が必要になるかもしれない例を説明します。  

 


ツールヒントのプライベート配列

CWndContainerクラスに移動して新しいメンバを加えます。ツールヒントのプライベート配列はWindowElements構造体に追加されます。(1) ツールヒントの数を取得するCWndContainer::TooltipsTotal()メソッドと(2)ツールヒントポインタをプライベート配列に追加するCWndContainer::AddTooltipElements()メソッドも作成します。

class CWndContainer
  {
protected:
   //--- 要素配列の構造体
   struct WindowElements
     {
      //--- ツールヒント
      CTooltip         *m_tooltips[];
     };
   //--- それぞれのウィンドウの要素配列の配列
   WindowElements    m_wnd[];
   //---
public:
   //--- ツールヒントの数
   int               TooltipsTotal(const int window_index);
   //---
private:
   //--- べースにツールヒントへのポインタを格納する
   bool              AddTooltipElements(const int window_index,CElement &object);
  };
//+------------------------------------------------------------------+
//| ウィンドウインデックスで指定されたツールヒントの数を返す     |
//+------------------------------------------------------------------+
int CWndContainer::TooltipsTotal(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_tooltips));
  }
//+------------------------------------------------------------------+
//| プライベート配列にツールヒントのポインタを格納する                  |
//+------------------------------------------------------------------+
bool CWndContainer::AddTooltipElements(const int window_index,CElement &object)
  {
//--- これがツールヒントでない場合には終了する
   if(object.ClassName()!="CTooltip")
      return(false);
//--- ツールヒントポインタを取得する
   CTooltip *t=::GetPointer(object);
//--- ポインタをプライベート配列に追加する
   AddToRefArray(t,m_wnd[window_index].m_tooltips);
   return(true);
  }

CWndContainer::AddTooltipElements()メソッドはCWndContainer::AddToElementsArray() パブリックメソッドで呼び出され、これは要素をベースに追加するときにカスタムクラスで使われます。その短縮版は以下のコードに示されています。

//+------------------------------------------------------------------+
//| 要素配列へのポインタを追加する                            |
//+------------------------------------------------------------------+
void CWndContainer::AddToElementsArray(const int window_index,CElement &object)
  {
//--- ベースにコントロールフォームがない場合
//--- 存在しないフォームへのリクエストの場合
//--- 共通要素配列に追加する
//--- 要素オブジェクトを共通オブジェクト配列に追加する
//--- すべてのフォームの最後の要素のIDを格納する
//--- 要素識別子のカウンタを増加する

//--- ベースにコンテキストメニューオブジェクトへのポインタを格納する
//--- ベースにメインメニューオブジェクトのポインタを格納する
//--- ベース内のスプリットボタンオブジェクトへのポインタを格納する 

//--- べースにツールヒントオブジェクトへのポインタを格納する
   if(AddTooltipElements(window_index,object))
      return;
  }

ツールヒントを作成するためのクラスの開発は完了です。ファイルの完全なバージョンは本稿添付のファイルでダウンロードできます。 

 


おわりに

本稿では、ステータスバーとツールヒント情報インタフェース要素の開発を考察しました。次の章では、マルチウィンドウグラフィカルインターフェイスを作成する可能性を実装し、マウスの左クリックの優先順位管理システムについてお話しします。

シリーズの最初の部分の全ての資料はダウンロードしてテストすることができます。これらのファイルに含まれている資料の使用についてご質問がある場合は、以下のリストにある記事のいずれかでライブラリの開発の詳細をご参照になるるか、本稿へのコメント欄でご質問ください。

第四部の記事(チャプター)のリストは下記の通りです。


MetaQuotes Ltdによってロシア語から翻訳されました。
元の記事: https://www.mql5.com/ru/articles/2307

添付されたファイル |
遺伝的アルゴリズムー数学 遺伝的アルゴリズムー数学
遺伝的アルゴリズムは最適化の問題を解決するために使用されます。このような問題の例として、ニューロネットワークの学習、つまりエラーを最小限にするための、このような重み値の選択を用いることができます。遺伝的アルゴリズムのベースにはランダム探索法があります。
マーケット理論 マーケット理論
現在のところ、どの商品市場や相場にも適応可能で、ミクロでもマクロでも使うことができるような完璧な相場理論というものは存在していません。この記事では、利益分析に基づいた新しい相場理論のエッセンスを紹介し、現在の価格変化とメカニズムの原則を明らかにします。実際の価格上でコントロール可能なバーチャルプライスの連鎖を形成することにより、最適な値を見つけることができます。相場の形成と変化のメカニズムも紹介します。
MQL5でのZIPアーカイブの扱い MQL5でのZIPアーカイブの扱い
MQL5は常に進化しています。この度新しい機能が追加されました。この革新により、DLLなしでZIPアーカイブを標準MQL5ツールで実行できるようになりました。この記事ではCZipクラスの使い方と、ZIPアーカイブの読み込み・生成・修正を例として扱います。
MetaTrader 5にポジション計算のヘッジシステムが追加されました MetaTrader 5にポジション計算のヘッジシステムが追加されました
リテールFXトレーダーの可能性を拡大する為に、プラットフォームに2つ目の計算システムであるヘッジングが追加されました。これからは、シンボルごとに、反対方向のものを含む、多数のポジションを持つことができます。これによって、いわゆる『ロック』を使った取引戦略を実装することができ、つまり、もし価格がトレーダーに反する方向へ向かった場合、反対方向にポジションを開くことができます。