English Русский 中文 Español Deutsch Português
グラフィカルインタフェースVII: タブコントロール(チャプター2)

グラフィカルインタフェースVII: タブコントロール(チャプター2)

MetaTrader 5 | 6 9月 2016, 13:35
739 0
Anatoli Kazharski
Anatoli Kazharski

コンテンツ


はじめに

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

第七部の最初の章では、テーブルを作成するためのコントロールであるテキストラベルテーブル(CLabelsTable)、エディットボックステーブル(CTable))およびレンダーテーブル(CCanvasTable)の3つのクラスが紹介されました。本稿(チャプター2)ではタブコントロールが考察されます。このコントロールのためには、シンプルなクラスと拡張された機能を持つクラスの2つが提示されます。

 


タブコントロール

タブは、グラフィカルインターフェースコントロールの事前定義されたセットの表示の制御に使用されます。多機能アプリケーションでは、多くの場合、大量のコントロールをグラフィカルインターフェースのために割り当てられた限られたスペースに収めることが必要となります。タブは、カテゴリによってコントロールをグループ分けして現在必要なグループのみを表示するのを可能にします。これによって、エンドユーザーにとって、インターフェイスがはるかにアクセスしやすくより直感的になります。表面上では、タブは、ラベル(コントロールグループの名前)を持つボタンのグループのように見えます。同時に、1つのみの選択(アクティブ)が可能です。

このコントロールのすべての構成要素を列挙してみましょう。

  1. コントロールのグループに合わせた背景またはエリア
  2. タブ

 図1。«タブ» コントロールのの複合部分

図1。タブコントロールのの複合部分

他のコントロールが配置される領域に相対してタブを配置する上、下、左、右の4つのモードを作成してみましょう。 

 


タブコントロール作成クラスの開発

Tabs.mqhファイルを作成してライブラリのWndContainer.mqhファイルに含みます。

//+------------------------------------------------------------------+
//|                                                 WndContainer.mqh |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#include "Tabs.mqh"

CTabsTabs.mqhファイルに作成されなければなりません。他のコントロールのクラスでもそうですが、このクラスでは、標準的なメソッドの作成に加えて、このコントロールが添付されるフォームへのポインタを格納するためのメソッドの作成が必要です。

//+------------------------------------------------------------------+
//| タブ作成クラス                                          |
//+------------------------------------------------------------------+
class CTabs : public CElement
  {
private:
   //--- 要素が取り付けられるフォームへのポインタ
   CWindow          *m_wnd;
   //---
public:
                     CTabs(void);
                    ~CTabs(void);
   //--- (1) フォームポインタを格納し (2) スクロールバーポインタを返す
   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);
   //--- 色をリセットする
   virtual void      ResetColors(void) {}
  };
//+------------------------------------------------------------------+
//| コンストラクタ                                                   |
//+------------------------------------------------------------------+
CTabs::CTabs(void)
  {
  }
//+------------------------------------------------------------------+
//| デストラクタ                                                       |
//+------------------------------------------------------------------+
CTabs::~CTabs(void)
  {
  }

タブのプロパティは、ユニークなものと一般的なものに分けることができます。両方のリストを作ってみましょう。

ユニークなプロパティ

  • タブに接続されているコントロールへのポインタの配列
  • テキスト

ユニークなプロパティのためにはTElements構造体を作成してこの型の動的配列を宣言します。 

class CTabs : public CElement
  {
private:
   //--- プロパティの構造体とコントロールの配列は各タブに取り付けられる
   struct TElements
     {
      CElement         *elements[];
      string            m_text;
      int               m_width;
     };
   TElements         m_tab[];
  };

共通プロパティ

  • タブの整列モード
  • エリアの背景色
  • Y軸に沿ったタブのサイズ(高さ)
  • 異なる状態にあるタブの色
  • 異なる状態にあるタブのテキストの色
  • タブの境界線の色
  • マウスの左クリックの優先順位

整列モードを設定するにはENUM_TABS_POSITION列挙がEnums.mqhファイルに追加される必要があります。 

//+------------------------------------------------------------------+
//| タブ整列の列挙                              |
//+------------------------------------------------------------------+
enum ENUM_TABS_POSITION
  {
   TABS_TOP    =0,
   TABS_BOTTOM =1,
   TABS_LEFT   =2,
   TABS_RIGHT  =3
  };

フィールドの宣言とプロパティを設定するためのメソッドは以下のコードで提供されています。 

class CTabs : public CElement
  {
private:
   //--- タブの整列
   ENUM_TABS_POSITION m_position_mode;
   //--- 共通エリアの背景色
   int               m_area_color;
   //--- Y軸に沿ったタブのサイズ
   int               m_tab_y_size;
   //--- 異なる状態にあるタブの色
   color             m_tab_color;
   color             m_tab_color_hover;
   color             m_tab_color_selected;
   color             m_tab_color_array[];
   //--- 異なる状態にあるタブのテキストの色
   color             m_tab_text_color;
   color             m_tab_text_color_selected;
   //--- タブの境界線の色
   color             m_tab_border_color;
   //--- マウスの左クリックの優先順位
   int               m_zorder;
   int               m_tab_zorder;
   //---
public:
   //--- (1) タブポジションを設定する(上下左右)(2) Y軸に沿ったタブのサイズを設定する
   void              PositionMode(const ENUM_TABS_POSITION mode)     { m_position_mode=mode;          }
   ENUM_TABS_POSITION PositionMode(void)                       const { return(m_position_mode);       }
   void              TabYSize(const int y_size)                      { m_tab_y_size=y_size;           }
   //--- (1) 共通の背景の色 (2) 異なる状態でのタブの色 (3)タブの境界の色
   void              AreaColor(const color clr)                      { m_area_color=clr;              }
   void              TabBackColor(const color clr)                   { m_tab_color=clr;               }
   void              TabBackColorHover(const color clr)              { m_tab_color_hover=clr;         }
   void              TabBackColorSelected(const color clr)           { m_tab_color_selected=clr;      }
   void              TabBorderColor(const color clr)                 { m_tab_border_color=clr;        }
   //--- 異なる状態にあるタブのテキストの色
   void              TabTextColor(const color clr)                   { m_tab_text_color=clr;          }
   void              TabTextColorSelected(const color clr)           { m_tab_text_color_selected=clr; }
  };

コントロールを作成する前に、表示されるテキストと幅を表示するのに必要な数のタブを追加する必要があります。このためにCTabs::AddTab() メソッドを書きましょう。メソッドの引数のデフォルト値は«»(空の文字列)と 50(幅)です。 

class CTabs : public CElement
  {
public:
   //--- タブを加える
   void              AddTab(const string tab_text="",const int tab_width=50);
  };
//+------------------------------------------------------------------+
//| タブを加える                                                       |
//+------------------------------------------------------------------+
void CTabs::AddTab(const string tab_text,const int tab_width)
  {
//--- タブ配列のサイズを設定する
   int array_size=::ArraySize(m_tabs);
   ::ArrayResize(m_tabs,array_size+1);
   ::ArrayResize(m_tab,array_size+1);
//--- 受け渡されたプロパティを格納する
   m_tab[array_size].m_text  =tab_text;
   m_tab[array_size].m_width =tab_width;
//--- タブの数を格納する
   m_tabs_total=array_size+1;
  }

チャートにMQLアプリケーションが読み込まれる際に事前に特定のタブが選択されなければならない場合は、コントロールを作成する前に CTabs::SelectedTab()メソッドを使用してそのインデックスを指定する必要があります。また、選択されたタブのインデックスが範囲を超えた場合に調整するための CTabs::CheckTabIndex() メソッドが必要になります。 

class CTabs : public CElement
  {
private:
   //--- 選択されたタブのインデックス
   int               m_selected_tab;
   //---
public:
   //--- 選択されたタブを (1) 格納して (2) そのインデックスを返す
   void              SelectedTab(const int index)                    { m_selected_tab=index;          }
   int               SelectedTab(void)                         const { return(m_selected_tab);        }
   //---
private:
   //--- 選択されたタブのインデックスを確認する
   void              CheckTabIndex(void);
  };
//+------------------------------------------------------------------+
//| 選択されたタブのインデックスを確認する                                  |
//+------------------------------------------------------------------+
void CTabs::CheckTabIndex(void)
  {
//--- 配列サイズの超過を確認する
   int array_size=::ArraySize(m_tab);
   if(m_selected_tab<0)
      m_selected_tab=0;
   if(m_selected_tab>=array_size)
      m_selected_tab=array_size-1;
  }

コントロールの作成には3つのプライベートメソッドと1つのパブリックメソッドが必要です。 

class CTabs : public CElement
  {
private:
   //--- 要素作成オブジェクト
   CRectLabel        m_main_area;
   CRectLabel        m_tabs_area;
   CEdit             m_tabs[];
   //---
public:
   //--- タブ作成メソッド
   bool              CreateTabs(const long chart_id,const int subwin,const int x,const int y);
   //---
private:
   bool              CreateMainArea(void);
   bool              CreateTabsArea(void);
   bool              CreateButtons(void);
  };

コントロールが取り付けられる前にタブが追加されていない場合、CTabs::CreateTabs() パブリックメソッドの呼び出しはグラフィカルインターフェイスの作成を停止して次のメッセージをログに出力します。 

//--- グループにタブがない場合は報告する
   if(m_tabs_total<1)
     {
      ::Print(__FUNCTION__," > This method is to be called, "
              "if a group contains at least one tab!Use the CTabs::AddTab() method");
      return(false);
     }

コントロールのコンポーネントオブジェクトの座標の決意と計算も選択されたタブの整列モードによって異なります。これらの計算にはCTabs::SumWidthTabs() メソッドが必要で、それはすべてのタブの幅の合計を返します。左(TABS_LEFT)と右(TABS_RIGHT)タブ整列モードでは、1番目のタブの幅が返されます。上(TABS_TOP)と下(TABS_BOTTOM)モードでは、すべてのタブの幅が加算されます。 

class CTabs : public CElement
  {
private:
   //--- タブの整列
   ENUM_TABS_POSITION m_position_mode;
   //---
private:
   //--- すべてのタブの幅
   int               SumWidthTabs(void);
  };
//+------------------------------------------------------------------+
//| すべてのタブの幅の合計                    |
//+------------------------------------------------------------------+
int CTabs::SumWidthTabs(void)
  {
   int width=0;
//--- タブが右か左に整列されている場合、1番目のタブの幅を返す
   if(m_position_mode==TABS_LEFT || m_position_mode==TABS_RIGHT)
      return(m_tab[0].m_width);
//--- すべてのタブの幅を加算する
   for(int i=0; i<m_tabs_total; i++)
      width=width+m_tab[i].m_width;
//--- 1画素オーバーレイを考慮
   width=width-(m_tabs_total-1);
   return(width);
  }

CTabs::CreateMainArea() メソッドは、コントロールのグループが配置される領域を作成するために設計されています。座標とオブジェクトサイズの計算は下記の通りです(メソッドの短縮版)。

//+------------------------------------------------------------------+
//| 共通エリアの背景色を作成する                                |
//+------------------------------------------------------------------+
bool CTabs::CreateMainArea(void)
  {
//--- オブジェクト名の形成
   string name=CElement::ProgramName()+"_tabs_main_area_"+(string)CElement::Id();
//--- 座標
   int x=0;
   int y=0;
//--- サイズ
   int x_size=0;
   int y_size=0;
//--- タブの位置に相対した座標とサイズの計算
   switch(m_position_mode)
     {
      case TABS_TOP :
         x      =CElement::X();
         y      =CElement::Y()+m_tab_y_size-1;
         x_size =CElement::XSize();
         y_size =CElement::YSize()-m_tab_y_size;
         break;
      case TABS_BOTTOM :
         x      =CElement::X();
         y      =CElement::Y();
         x_size =CElement::XSize();
         y_size =CElement::YSize()-m_tab_y_size;
         break;
      case TABS_RIGHT :
         x      =CElement::X();
         y      =CElement::Y();
         x_size =CElement::XSize()-SumWidthTabs()+1;
         y_size =CElement::YSize();
         break;
      case TABS_LEFT :
         x      =CElement::X()+SumWidthTabs()-1;
         y      =CElement::Y();
         x_size =CElement::XSize()-SumWidthTabs()+1;
         y_size =CElement::YSize();
         break;
     }
//--- オブジェクトの作成
   if(!m_main_area.Create(m_chart_id,name,m_subwin,x,y,x_size,y_size))
      return(false);
//--- プロパティの設定
//--- 端からのマージン
//--- サイズを格納する
//--- 座標を格納する
//--- オブジェクトポインタを格納する
//...
   return(true);
  }

以下は CTabs::CreateTabsArea() メソッドで指定された整列モードに応じたタブの座標と背景のサイズの計算です。 

//+------------------------------------------------------------------+
//| タブの背景を作成する                                        |
//+------------------------------------------------------------------+
bool CTabs::CreateTabsArea(void)
  {
//--- オブジェクト名の形成
   string name=CElement::ProgramName()+"_tabs_area_"+(string)CElement::Id();
//--- 座標
   int x=CElement::X();
   int y=CElement::Y();
//--- サイズ
   int x_size=SumWidthTabs();
   int y_size=0;
//--- タブの位置に相対したサイズの計算
   if(m_position_mode==TABS_TOP || m_position_mode==TABS_BOTTOM)
     {
      y_size=m_tab_y_size;
     }
   else
     {
      y_size=m_tab_y_size*m_tabs_total-(m_tabs_total-1);
     }
//--- タブを下と右で位置決めするために座標を調整する
   if(m_position_mode==TABS_BOTTOM)
     {
      y=CElement::Y2()-m_tab_y_size-1;
     }
   else if(m_position_mode==TABS_RIGHT)
     {
      x=CElement::X2()-x_size;
     }
//--- オブジェクトの作成
   if(!m_tabs_area.Create(m_chart_id,name,m_subwin,x,y,x_size,y_size))
      return(false);
//--- プロパティの設定
//--- 端からのマージン
//--- サイズを格納する
//--- 座標を格納する
//--- オブジェクトポインタを格納する
//...
   return(true);
  }


CTabs::CreateButtons() メソッドはタブの作成に座標の計算のみを必要にします。幅はコントロールが作成される前にアプリケーションのカスタムクラスで設定されています。そうでない場合は、デフォルト値(幅)が使用されます。下記はメソッドの短縮版です。 

//+------------------------------------------------------------------+
//| タブを作成する                                                |
//+------------------------------------------------------------------+
bool CTabs::CreateButtons(void)
  {
//--- 座標
   int x =CElement::X();
   int y =CElement::Y();
//--- タブの位置に相対した座標の計算
   if(m_position_mode==TABS_BOTTOM)
      y=CElement::Y2()-m_tab_y_size-1;
   else if(m_position_mode==TABS_RIGHT)
      x=CElement::X2()-SumWidthTabs();
//--- 選択されたタブのインデックスを確認する
   CheckTabIndex();
//--- タブを作成する
   for(int i=0; i<m_tabs_total; i++)
     {
      //--- オブジェクト名の形成
      string name=CElement::ProgramName()+"_tabs_edit_"+(string)i+"__"+(string)CElement::Id();
      //--- 個々のタブでのタブの位置に相対した座標計算
      if(m_position_mode==TABS_TOP || m_position_mode==TABS_BOTTOM)
         x=(i>0) ?x+m_tab[i-1].m_width-1 : CElement::X();
      else
         y=(i>0) ?y+m_tab_y_size-1 : CElement::Y();
      //--- オブジェクトの作成
      if(!m_tabs[i].Create(m_chart_id,name,m_subwin,x,y,m_tab[i].m_width,m_tab_y_size))
         return(false);
      //--- プロパティの設定
      //--- パネルの端からのマージン
      //--- 座標
      //--- サイズ
      //--- グラデーション配列を初期化する
      //--- オブジェクトポインタを格納する
     }
//---
   return(true);
  }

特定のタブに任意のコントロールを接続するためにCTabs::AddToElementsArray() メソッドを書きましょう。これには (1) コントロールが取り付けられるタブのインデックスと (2) ポインタがタブコントロールの配列に格納されるコントロールへの参照 の2つの引数があります。

class CTabs : public CElement
  {
public:
   //--- コントロールをタブ配列に追加する
   void              AddToElementsArray(const int tab_index,CElement &object);
  };
//+------------------------------------------------------------------+
//| コントロールを指定されたタブの配列に追加する                    |
//+------------------------------------------------------------------+
void CTabs::AddToElementsArray(const int tab_index,CElement &object)
  {
//--- 配列サイズの超過を確認する
   int array_size=::ArraySize(m_tab);
   if(array_size<1 || tab_index<0 || tab_index>=array_size)
      return;
//--- 受け渡されたコントロールへのポインタを指定されたタブの配列に追加する
   int size=::ArraySize(m_tab[tab_index].elements);
   ::ArrayResize(m_tab[tab_index].elements,size+1);
   m_tab[tab_index].elements[size]=::GetPointer(object);
  }

タブを切り替えるときは、1つ前のタブのコントロールを非表示にし、新たに選択されたタブのコントロールを表示する必要があります。このためにCTabs::ShowTabElements() メソッドを作成しましょう。コントロールの可視性のチェックは、メソッドの先頭にあります。コントロールが表示されていない場合、プログラムはメソッドを終了します。次に、アクティブなタブのインデックスを確認し、必要であれば、それを調整します。そして、ループ内ですべてのタブをチェックしてメソッドの主なタスクを実行します。   

class CTabs : public CElement
  {
public:
   //--- 選択されたタブのコントロールのみを表示する
   void              ShowTabElements(void);
  };
//+------------------------------------------------------------------+
//| 選択されたタブのコントロールのみを表示する               |
//+------------------------------------------------------------------+
void CTabs::ShowTabElements(void)
  {
//--- タブが非表示の場合は終了する
   if(!CElement::IsVisible())
      return;
//--- 選択されたタブのインデックスを確認する
   CheckTabIndex();
//---
   for(int i=0; i<m_tabs_total; i++)
     {
      //--- タブに取りつけられているコントロールの数を取得する
      int tab_elements_total=::ArraySize(m_tab[i].elements);
      //--- このタブが選択された場合
      if(i==m_selected_tab)
        {
         //--- タブコントロールを表示する
         for(int j=0; j<tab_elements_total; j++)
            m_tab[i].elements[j].Show();
        }
      //--- アクティブでないタブのコントロールを隠す
      else
        {
         for(int j=0; j<tab_elements_total; j++)
            m_tab[i].elements[j].Hide();
        }
     }
  }

CTabs::OnClickTab() メソッドはタブ押下の処理に使用されます。プログラムは初めに(1) 押下されたオブジェクトの名前に基づいた条件 (2) CTabs::IdFromObjectName()メソッドを使ってオブジェクト名から取得されたコントロールの識別子に基づいた条件の2つの条件を満たす必要があります。条件が満たされた場合、プログラムは(1)ループで押下されたタブを見つけ(2)そのインデックスを格納し(3)対応する色を設定します。CTabs::ShowTabElements() メソッドの一番最後では、アクティブなタブのコントロールのみが表示されるようになります。 

class CTabs : public CElement
  {
private:
   //--- タブの押下の処理
   bool              OnClickTab(const string pressed_object);
   //---  オブジェクト名から識別子を取得する                     |
   int               IdFromObjectName(const string object_name);
  };
//+------------------------------------------------------------------+
//| グループでのタブの押下                                        |
//+------------------------------------------------------------------+
bool CTabs::OnClickTab(const string clicked_object)
  {
//--- 押されたのがテーブルセルでなかった場合は終了する
   if(::StringFind(clicked_object,CElement::ProgramName()+"_tabs_edit_",0)<0)
      return(false);
//---  オブジェクト名から識別子を取得する                     |
   int id=IdFromObjectName(clicked_object);
//--- 識別子が一致しない場合は終了する
   if(id!=CElement::Id())
      return(false);
//---
   for(int i=0; i<m_tabs_total; i++)
     {
      //--- このタブがクリックされた場合
      if(m_tabs[i].Name()==clicked_object)
        {
         //--- 選択されたタブのインデックスを格納する
         SelectedTab(i);
         //--- 色を設定する
         m_tabs[i].Color(m_tab_text_color_selected);
         m_tabs[i].BackColor(m_tab_color_selected);
        }
      else
        {
         //--- アクティブでないタブの色を設定する
         m_tabs[i].Color(m_tab_text_color);
         m_tabs[i].BackColor(m_tab_color);
        }
     }
//--- 選択されたタブのコントロールのみを表示する
   ShowTabElements();
   return(true);
  }

この場合のCTabs::OnEvent() メインイベントハンドラのコードは下記のとおりです。 

//+------------------------------------------------------------------+
//| チャートイベントハンドラ                                              |
//+------------------------------------------------------------------+
void CTabs::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
  {
//--- カーソル移動イベントの処理
   if(id==CHARTEVENT_MOUSE_MOVE)
     {
      //--- 要素が隠れている場合は終了する
      if(!CElement::IsVisible())
         return;
      //--- 座標
      int x=(int)lparam;
      int y=(int)dparam;
      for(int i=0; i<m_tabs_total; i++)
         m_tabs[i].MouseFocus(x>m_tabs[i].X() && x<m_tabs[i].X2() && y>m_tabs[i].Y() && y<m_tabs[i].Y2());
      //---
      return;
     }
//--- オブジェクトのマウス左クリックイベントの処理
   if(id==CHARTEVENT_OBJECT_CLICK)
     {
      //--- タブの押下
      if(OnClickTab(sparam))
         return;
     }
  }

CTabsクラスのメソッドはすべて考察されました。その動作を検証してみましょう。 

 


タブコントロールの検証

検証のためには、シリーズの前の部分で使われたEAを使用し、そのグラフィカルインターフェースからメインメニューとステータスバー以外のすべてを削除しましょう。すべてのタブ整列モード(上/下/左/右)ならびにそれらのサイズ(高さ)を調整しやすくしましょう。この目的のためには、カスタムクラスを持ったProgram.mqh でEAに 外部パラメータを追加します。

//--- エキスパートアドバイザーの外部パラメータ
input ENUM_TABS_POSITION TabsPosition =TABS_TOP; // タブの位置
input                int TabsHeight   =20;       // タブの高さ

次に、アプリケーションのカスタムクラス(CProgram)で、CTabsクラスのインスタンスと、フォームの端からインデントされたタブコントロールを作成するメソッドを宣言します。 

class CProgram : public CWndEvents
  {
private:
   //--- タブ
   CTabs             m_tabs;
   //---
private:
   //--- タブ
#define TABS1_GAP_X           (4)
#define TABS1_GAP_Y           (45)
   bool              CreateTabs(void);
  };

タブは全部で4つあります。表示されたテキストとタブの幅は配列の初期化で調整可能で、その要素値はCTabs::AddTab() メソッドを使ってループに受け渡されます。タブの高さと位置は外部パラメータで設定されます。デフォルトでは2番目のタブ(インデックス1)が選択されます(プログラムがチャートに読み込まれるとき)。CProgram::CreateTabs() メソッドの完全なコードは下に見られます。 

//+------------------------------------------------------------------+
//| タブのある領域を作成する                                     |
//+------------------------------------------------------------------+
bool CProgram::CreateTabs(void)
  {
#define TABS1_TOTAL 4
//--- パネルオブジェクトを受け渡す
   m_tabs.WindowPointer(m_window1);
//--- 座標
   int x=m_window1.X()+TABS1_GAP_X;
   int y=m_window1.Y()+TABS1_GAP_Y;
//--- タブのテキストと幅を持った配列
   string tabs_text[]={"Tab 1","Tab 2","Tab 3","Tab 4"};
   int tabs_width[]={90,90,90,90};
//--- 作成前にプロパティを設定する
   m_tabs.XSize(596);
   m_tabs.YSize(243);
   m_tabs.TabYSize(TabsHeight);
   m_tabs.PositionMode(TabsPosition);
   m_tabs.SelectedTab((m_tabs.SelectedTab()==WRONG_VALUE) ?1 : m_tabs.SelectedTab());
   m_tabs.AreaColor(clrWhite);
   m_tabs.TabBackColor(C'225,225,225');
   m_tabs.TabBackColorHover(C'240,240,240');
   m_tabs.TabBackColorSelected(clrWhite);
   m_tabs.TabBorderColor(clrSilver);
   m_tabs.TabTextColor(clrGray);
   m_tabs.TabTextColorSelected(clrBlack);
//--- 指定されたプロパティを持つタブを追加する
   for(int i=0; i<TABS1_TOTAL; i++)
      m_tabs.AddTab(tabs_text[i],tabs_width[i]);
//--- コントロールを作成する
   if(!m_tabs.CreateTabs(m_chart_id,m_subwin,x,y))
      return(false);
//--- オブジェクトをオブジェクトグループの共通配列に追加する
   CWndContainer::AddToElementsArray(0,m_tabs);
   return(true);
  }

メソッドは、アプリケーションのグラフィカルインターフェース(以下のメソッドの短縮版を参照)を作成するためのメインメソッドで呼び出される必要があります。  

//+------------------------------------------------------------------+
//| エキスパートパネルを作成する                                        |
//+------------------------------------------------------------------+
bool CProgram::CreateExpertPanel(void)
  {
//--- コントロールのフォーム1 の作成
//--- コントロールの作成
//    メインメニュー
//--- コンテキストメニュー
//--- タブ
   if(!CreateTabs())
      return(false);
//--- チャートの再描画
   m_chart.Redraw();
   return(true);
  }

コードをコンパイルしてプログラムをチャートに読み込みます。タブの整列モードは外部パラメータで連続的に変化されます(下のスクリーンショットを参照)。

 図2。タブ整列モード — «上».

図2。タブ整列モード — «上».

 図3。タブ整列モード — «下»

図3。タブ整列モード — «下»

 図4。タブ整列モード — — «左»

図4。タブ整列モード — — «左»

 図5。タブ整列モード — — «右»

図5。タブ整列モード — — «右»


ここで、各タブに接続されているコントロールのグループがどのように機能するかを検証してみましょう。これを行うには、同じEAの別のコピーを作成し、そこから外部パラメータを削除します。ここでは、タブはワークスペースの最上部(TABS_TOP)に置かれます。 

  1. テキストラベルテーブルは最初のタブに取り付けられます。 
  2. エディットボックステーブルは2番目のものに取り付けられます。 
  3. 3番目はレンダーテーブルです。
  4. 4番目には下記を含むコントロールのグループが取り付けられます。
    • 4つのチェックボックス
    • 4つのエディットボックス付きのチェックボックス
    • 4つのチェックボックス付きのコンボボックス
    • 1本の区切り線 

テストアプリケーションのカスタムクラス(CProgram)で (1) コントロールのインスタンス(2) それらの作成メソッド (3) フォームの端からのマージンを宣言します(下のコードを参照)。 

class CProgram : public CWndEvents
  {
private:
   //--- テキストラベルテーブル
   CLabelsTable      m_labels_table;
   //--- エディットボックステーブル
   CTable            m_table;
   //--- レンダーテーブル
   CCanvasTable      m_canvas_table;
   //--- チェックボックス
   CCheckBox         m_checkbox1;
   CCheckBox         m_checkbox2;
   CCheckBox         m_checkbox3;
   CCheckBox         m_checkbox4;
   //--- エディットボックス付きのチェックボックス
   CCheckBoxEdit     m_checkboxedit1;
   CCheckBoxEdit     m_checkboxedit2;
   CCheckBoxEdit     m_checkboxedit3;
   CCheckBoxEdit     m_checkboxedit4;
   //--- チェックボックス付きのコンボボックス
   CCheckComboBox    m_checkcombobox1;
   CCheckComboBox    m_checkcombobox2;
   CCheckComboBox    m_checkcombobox3;
   CCheckComboBox    m_checkcombobox4;
   //--- 区切り線
   CSeparateLine     m_sep_line;
   //---
private:
   //--- テキストラベルテーブル
#define TABLE1_GAP_X          (5)
#define TABLE1_GAP_Y          (65)
   bool              CreateLabelsTable(void);
   //--- エディットボックステーブル
#define TABLE2_GAP_X          (5)
#define TABLE2_GAP_Y          (65)
   bool              CreateTable(void);
   //--- レンダーテーブル
#define TABLE3_GAP_X          (5)
#define TABLE3_GAP_Y          (65)
   bool              CreateCanvasTable(void);
   //--- 区切り線
#define SEP_LINE_GAP_X        (300)
#define SEP_LINE_GAP_Y        (70)
   bool              CreateSepLine(void);
   //--- チェックボックス
#define CHECKBOX1_GAP_X       (18)
#define CHECKBOX1_GAP_Y       (75)
   bool              CreateCheckBox1(const string text);
#define CHECKBOX2_GAP_X       (18)
#define CHECKBOX2_GAP_Y       (175)
   bool              CreateCheckBox2(const string text);
#define CHECKBOX3_GAP_X       (315)
#define CHECKBOX3_GAP_Y       (75)
   bool              CreateCheckBox3(const string text);
#define CHECKBOX4_GAP_X       (315)
#define CHECKBOX4_GAP_Y       (175)
   bool              CreateCheckBox4(const string text);
   //--- エディットボックス付きのチェックボックス
#define CHECKBOXEDIT1_GAP_X   (40)
#define CHECKBOXEDIT1_GAP_Y   (105)
   bool              CreateCheckBoxEdit1(const string text);
#define CHECKBOXEDIT2_GAP_X   (40)
#define CHECKBOXEDIT2_GAP_Y   (135)
   bool              CreateCheckBoxEdit2(const string text);
#define CHECKBOXEDIT3_GAP_X   (337)
#define CHECKBOXEDIT3_GAP_Y   (105)
   bool              CreateCheckBoxEdit3(const string text);
#define CHECKBOXEDIT4_GAP_X   (337)
#define CHECKBOXEDIT4_GAP_Y   (135)
   bool              CreateCheckBoxEdit4(const string text);
   //--- チェックボックス付きのコンボボックス
#define CHECKCOMBOBOX1_GAP_X  (40)
#define CHECKCOMBOBOX1_GAP_Y  (205)
   bool              CreateCheckComboBox1(const string text);
#define CHECKCOMBOBOX2_GAP_X  (40)
#define CHECKCOMBOBOX2_GAP_Y  (235)
   bool              CreateCheckComboBox2(const string text);
#define CHECKCOMBOBOX3_GAP_X  (337)
#define CHECKCOMBOBOX3_GAP_Y  (205)
   bool              CreateCheckComboBox3(const string text);
#define CHECKCOMBOBOX4_GAP_X  (337)
#define CHECKCOMBOBOX4_GAP_Y  (235)
   bool              CreateCheckComboBox4(const string text);
  };

以前の記事では、コントロールを作成してフォームに取り付ける方法が繰り返して示されました。したがって、タブにコントロールを取り付ける方法を示すために、ここではこれらの方法のうち1つだけのコードが表示されます。例としてはこれらのコントロールのうちで最も単純な区切り線が十分でしょう。下記のコードではCTabs::AddToElementsArray() メソッドを呼び出す行が黄色で強調表示されています。1番目の引数はコントロールが取り付けられるべきタブのインデックスです。ここではインデックスは3で、つまりタブは4番目のものです。2番目の引数は、指定したタブに取り付けられなければならないコントロールのオブジェクトです。

//+------------------------------------------------------------------+
//| 区切り線を作成する                                        |
//+------------------------------------------------------------------+
bool CProgram::CreateSepLine(void)
  {
//--- ウィンドウポインタを格納する
   m_sep_line.WindowPointer(m_window1);
//--- 1番目のタブグループの4番目のタブに取り付ける
   m_tabs.AddToElementsArray(3,m_sep_line);
//--- 座標  
   int x=m_window1.X()+SEP_LINE_GAP_X;
   int y=m_window1.Y()+SEP_LINE_GAP_Y;
//--- サイズ
   int x_size=2;
   int y_size=210;
//--- 作成前にプロパティを設定する
   m_sep_line.DarkColor(C'213,223,229');
   m_sep_line.LightColor(clrWhite);
   m_sep_line.TypeSepLine(V_SEP_LINE);
//--- 要素の作成
   if(!m_sep_line.CreateSeparateLine(m_chart_id,m_subwin,0,x,y,x_size,y_size))
      return(false);
//--- 要素ポインタをベースに追加する
   CWndContainer::AddToElementsArray(0,m_sep_line);
   return(true);
  }

アプリケーションのグラフィカルインターフェースが作成された後は、アクティブなタブのコントロールのみを表示するためにCTabs::ShowTabElements() メソッドが呼ばれなければなりません(下記の短縮版を参照)。それが行われていない場合は、すべてのタブのすべてのコントロールが表示されます。 

//+------------------------------------------------------------------+
//| エキスパートパネルを作成する                                        |
//+------------------------------------------------------------------+
bool CProgram::CreateExpertPanel(void)
  {
//--- コントロールのフォーム1 の作成
//--- コントロールの作成
//--- メインメニュー
//--- コンテキストメニュー
//--- ステータスバー
//--- タブ
//...
//--- テキストラベルテーブル
   if(!CreateLabelsTable())
      return(false);
//--- エディットボックステーブル
   if(!CreateTable())
      return(false);
//--- レンダーテーブルを作成する
   if(!CreateCanvasTable())
      return(false);
//--- 区切り線
   if(!CreateSepLine())
      return(false);
//--- チェックボックス
   if(!CreateCheckBox1("Checkbox 1"))
      return(false);
   if(!CreateCheckBox2("Checkbox 2"))
      return(false);
   if(!CreateCheckBox3("Checkbox 3"))
      return(false);
   if(!CreateCheckBox4("Checkbox 4"))
      return(false);
//--- エディットボックス付きのチェックボックス
   if(!CreateCheckBoxEdit1("Checkbox Edit 1:"))
      return(false);
   if(!CreateCheckBoxEdit2("Checkbox Edit 2:"))
      return(false);
   if(!CreateCheckBoxEdit3("Checkbox Edit 3:"))
      return(false);
   if(!CreateCheckBoxEdit4("Checkbox Edit 4:"))
      return(false);
//--- チェックボックス付きのコンボボックス
   if(!CreateCheckComboBox1("CheckCombobox 1:"))
      return(false);
   if(!CreateCheckComboBox2("CheckCombobox 2:"))
      return(false);
   if(!CreateCheckComboBox3("CheckCombobox 3:"))
      return(false);
   if(!CreateCheckComboBox4("CheckCombobox 4:"))
      return(false);
//--- アクティブなタブのコントロールのみを表示する
   m_tabs.ShowTabElements();
//--- チャートの再描画
   m_chart.Redraw();
   return(true);
  }

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

 図6。1番目のタブのコントロール

図6。1番目のタブのコントロール

図7。2番目のタブのコントロール 

図7。2番目のタブのコントロール

 図8 3番目のタブのコントロール

図8 3番目のタブのコントロール

 図9 4番目のタブのコントロール

図9 4番目のタブのコントロール


すべてが設計どおり動作します。グラフィカルインタフェースを作成するためのライブラリに捧げられた本シリーズの第七部はこれでしめることができます。補足として、本稿の添付ファイルに提供されている拡張機能を持つタブを作成するあと1つのクラスのコード(CIconTabs)のダウンロードが可能です。CTabsクラスと違ってCIconTabs型のコントロールでは各タブに調整可能なアイコンがあります。これによって、必要に応じてグラフィカルインターフェースをよりユーザーフレンドリーにすることができます。 

アイコンと表示されたテキストは、特別なメソッドを用いて正確に各タブの端に相対して配置することができます(コードは以下を参照)。 

//+------------------------------------------------------------------+
//| アイコンタブ作成クラス                            |
//+------------------------------------------------------------------+
class CIconTabs : public CElement
  {
private:
   //--- ラベルの余白
   int               m_icon_x_gap;
   int               m_icon_y_gap;
   //--- テキストラベルのマージン
   int               m_label_x_gap;
   int               m_label_y_gap;
   //---
public:
   //--- ラベルの余白
   void              IconXGap(const int x_gap)                       { m_icon_x_gap=x_gap;            }
   void              IconYGap(const int y_gap)                       { m_icon_y_gap=y_gap;            }
   //--- テキストラベルのマージン
   void              LabelXGap(const int x_gap)                      { m_label_x_gap=x_gap;           }
   void              LabelYGap(const int y_gap)                      { m_label_y_gap=y_gap;           }
  };

アイコンタブコントロールの外観の一例は以下のスクリーンショットに表示されています。

 図10 «アイコンタブ» コントロール

図10 アイコンタブコントロール

このエキスパートのコードも本稿の添付ファイルからダウンロードできます。 

 

 


おわりに

グラフィカルインタフェースを作成するためのライブラリーの概略は現在以下の通りに見えます。

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

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

テーブルとタブコントロールはMetaTrader取引プラットフォームでのグラフィカルインターフェースの作成についてのシリーズの第七部で考察されました。テーブル作成には3つのクラス(CLabelsTableCTableCCanvasTable)、タブ作成には2つのクラス(CTabsCIconTabs)が提供されました。 

シリーズの次の部分(第八部)は下記のコントロールを考察します。

  • 静的なドロップダウンカレンダー
  • ツリービュー
  • ファイルナビゲータ

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

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

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

添付されたファイル |
グラフィカルインタフェースVIII:カレンダーコントロール(チャプター1) グラフィカルインタフェースVIII:カレンダーコントロール(チャプター1)
このMetaTraderでのグラフィカルインタフェースの作成に専念した記事シリーズの第八部では、カレンダー、ツリービュー、およびファイルナビゲーターのような複雑な複合コントロールが検討されます。情報が大量のため、それぞれは個別の記事に書かれています。この部分の最初の章では、カレンダーコントロールとその拡張バージョンであるドロップダウンカレンダーに ついて説明します。
自己組織化特徴マップ(Kohonenマップ) - サブジェクトリビジッティング 自己組織化特徴マップ(Kohonenマップ) - サブジェクトリビジッティング
この記事では、Kohonenマップで動作するのテクニックについて説明します。Kohonenマップで困難に直面し、MQL4とMQL5でのプログラミングの基本的なレベルがわかる研究者や経験豊富なプログラマーを対象としています。自己組織化特徴マップ(Kohonenマップ) - サブジェクトリビジッティング
グラフィカルインタフェースVIII: ツリービューコントロール(チャプター2) グラフィカルインタフェースVIII: ツリービューコントロール(チャプター2)
前のグラフィカルインターフェイス第八部では静的およびドロップダウンカレンダー要素に焦点が当てられました。この第2章は、グラフィカルインタフェースを作成するために使用されるすべての完全なライブラリーに含まれているツリービューという均等に複雑な要素に焦点を当てます。本稿で実装されるツリービューは複数の柔軟な設定とモードを含み、ニーズに合わせてコントロール要素を調整することができます。
機械学習モデルの評価と変数の選択 機械学習モデルの評価と変数の選択
この記事では、機械学習モデルで使用する入力変数(予測変数)の選択、前処理および評価の詳細に焦点を当てています。新しいアプローチと予測分析とモデルの可能性と過学習への影響を考慮します。モデルを使用した全体的な結果は、この段階の結果に依存します。予測変数の選択に、新しい、オリジナルなアプローチを提供します。