【MQL4 / MQL5】透過画像なしで十字カーソルを表現する方法

MQL では PNG 画像が使えないので、透過画像であれば簡単にできる十字カーソルのような表現が難しいですよね。

かなりの力技ですが、BMP 画像を使って、透過画像を使ったかのように標準の十字カーソルとまったく同じ表現ができたので共有します。体感的な負荷も特に上がりません。

必要な BMP 画像は十字線の実体上下左右4枚と1ピクセル線用画像縦横1枚ずつの6枚ですが、1ピクセル線用画像を縦横1枚ずつコピーして上下左右の名前を付けて計8枚にしてしまった方が直感的です。十字線の実体は色とか塗ったらカワイくなりそうですね。

構造は以下の通り1ピクセル線用画像4枚の上にレイヤー的に十字線実体画像4枚を重ねて置いているだけです。この構造を守ったままマウスカーソルに追従させます。

使うオブジェクトは OBJ_BITMAP_LABEL です。OBJPROP_ANCHOR を表示位置と逆側に設定して、それぞれをピクセル単位で微調整したら完成です。

BITMAP_LABEL で実装

//+------------------------------------------------------------------+
//|                                               crosshairs_bmp.mq4 |
//|                                       Copyright 2021, DJ Trader. |
//|                                            https://dj-trader.net |
//+------------------------------------------------------------------+
#property copyright "Copyright 2021, DJ Trader."
#property link      "https://dj-trader.net/fx/mql/7079"
#property version   "1.00"
#property strict
#property indicator_chart_window

// ex4・ex5 ファイルに画像埋め込み
#resource "\\Images\\crosshairs\\top_out.bmp"
#resource "\\Images\\crosshairs\\top.bmp"
#resource "\\Images\\crosshairs\\right_out.bmp"
#resource "\\Images\\crosshairs\\right.bmp"
#resource "\\Images\\crosshairs\\bottom_out.bmp"
#resource "\\Images\\crosshairs\\bottom.bmp"
#resource "\\Images\\crosshairs\\left_out.bmp"
#resource "\\Images\\crosshairs\\left.bmp"
// 接頭辞 + オブジェクト名
string bmp_prefix = "bmp_";
string bmp_top_out = bmp_prefix + "top_out";
string bmp_top = bmp_prefix + "top";
string bmp_right_out = bmp_prefix + "right_out";
string bmp_right = bmp_prefix + "right";
string bmp_bottom_out = bmp_prefix + "bottom_out";
string bmp_bottom = bmp_prefix + "bottom";
string bmp_left_out = bmp_prefix + "left_out";
string bmp_left = bmp_prefix + "left";
// チャート ID
long chart_ID;

//+------------------------------------------------------------------+
//| Custom indicator initialization function
//+------------------------------------------------------------------+
int OnInit()
  {
//--- チャート ID
   chart_ID = ChartID();
//--- マウス動作
   ChartSetInteger(chart_ID, CHART_EVENT_MOUSE_MOVE, true);
//--- BITMAP_LABEL オブジェクト生成
   ObjectCreate(chart_ID, bmp_top_out, OBJ_BITMAP_LABEL, 0, 0, 0);
   ObjectCreate(chart_ID, bmp_top, OBJ_BITMAP_LABEL, 0, 0, 0);
   ObjectCreate(chart_ID, bmp_right_out, OBJ_BITMAP_LABEL, 0, 0, 0);
   ObjectCreate(chart_ID, bmp_right, OBJ_BITMAP_LABEL, 0, 0, 0);
   ObjectCreate(chart_ID, bmp_bottom_out, OBJ_BITMAP_LABEL, 0, 0, 0);
   ObjectCreate(chart_ID, bmp_bottom, OBJ_BITMAP_LABEL, 0, 0, 0);
   ObjectCreate(chart_ID, bmp_left_out, OBJ_BITMAP_LABEL, 0, 0, 0);
   ObjectCreate(chart_ID, bmp_left, OBJ_BITMAP_LABEL, 0, 0, 0);
//--- 埋め込み画像読み込み
   ObjectSetString(chart_ID, bmp_top_out, OBJPROP_BMPFILE, 1, "::Images\\crosshairs\\top_out.bmp");
   ObjectSetString(chart_ID, bmp_top, OBJPROP_BMPFILE, 1, "::Images\\crosshairs\\top.bmp");
   ObjectSetString(chart_ID, bmp_right_out, OBJPROP_BMPFILE, 1, "::Images\\crosshairs\\right_out.bmp");
   ObjectSetString(chart_ID, bmp_right, OBJPROP_BMPFILE, 1, "::Images\\crosshairs\\right.bmp");
   ObjectSetString(chart_ID, bmp_bottom_out, OBJPROP_BMPFILE, 1, "::Images\\crosshairs\\bottom_out.bmp");
   ObjectSetString(chart_ID, bmp_bottom, OBJPROP_BMPFILE, 1, "::Images\\crosshairs\\bottom.bmp");
   ObjectSetString(chart_ID, bmp_left_out, OBJPROP_BMPFILE, 1, "::Images\\crosshairs\\left_out.bmp");
   ObjectSetString(chart_ID, bmp_left, OBJPROP_BMPFILE, 1, "::Images\\crosshairs\\left.bmp");
//--- アンカー設定
   ObjectSetInteger(chart_ID, bmp_top_out, OBJPROP_ANCHOR, ANCHOR_LOWER);
   ObjectSetInteger(chart_ID, bmp_top, OBJPROP_ANCHOR, ANCHOR_LOWER);
   ObjectSetInteger(chart_ID, bmp_right_out, OBJPROP_ANCHOR, ANCHOR_LEFT);
   ObjectSetInteger(chart_ID, bmp_right, OBJPROP_ANCHOR, ANCHOR_LEFT);
   ObjectSetInteger(chart_ID, bmp_bottom_out, OBJPROP_ANCHOR, ANCHOR_UPPER);
   ObjectSetInteger(chart_ID, bmp_bottom, OBJPROP_ANCHOR, ANCHOR_UPPER);
   ObjectSetInteger(chart_ID, bmp_left_out, OBJPROP_ANCHOR, ANCHOR_RIGHT);
   ObjectSetInteger(chart_ID, bmp_left, OBJPROP_ANCHOR, ANCHOR_RIGHT);
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
// 終了時に同じ接頭辞を持つ BITMAP_LABEL を全削除
   ObjectsDeleteAll(chart_ID, bmp_prefix, 0, OBJ_BITMAP_LABEL);
  }
//+------------------------------------------------------------------+
//| ChartEvent function
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
   if(id == CHARTEVENT_MOUSE_MOVE)
     {
      //--- マウスの動きに追従(横方向)
      ObjectSetInteger(chart_ID, bmp_top_out, OBJPROP_XDISTANCE, (int)lparam - 1);
      ObjectSetInteger(chart_ID, bmp_top, OBJPROP_XDISTANCE, (int)lparam - 1);
      ObjectSetInteger(chart_ID, bmp_right_out, OBJPROP_XDISTANCE, (int)lparam + 1);
      ObjectSetInteger(chart_ID, bmp_right, OBJPROP_XDISTANCE, (int)lparam + 1);
      ObjectSetInteger(chart_ID, bmp_bottom_out, OBJPROP_XDISTANCE, (int)lparam - 1);
      ObjectSetInteger(chart_ID, bmp_bottom, OBJPROP_XDISTANCE, (int)lparam - 1);
      ObjectSetInteger(chart_ID, bmp_left_out, OBJPROP_XDISTANCE, (int)lparam - 2);
      ObjectSetInteger(chart_ID, bmp_left, OBJPROP_XDISTANCE, (int)lparam - 2);
      //--- マウスの動きに追従(縦方向)
      ObjectSetInteger(chart_ID, bmp_top_out, OBJPROP_YDISTANCE, (int)dparam - 1);
      ObjectSetInteger(chart_ID, bmp_top, OBJPROP_YDISTANCE, (int)dparam - 1);
      ObjectSetInteger(chart_ID, bmp_right_out, OBJPROP_YDISTANCE, (int)dparam);
      ObjectSetInteger(chart_ID, bmp_right, OBJPROP_YDISTANCE, (int)dparam);
      ObjectSetInteger(chart_ID, bmp_bottom_out, OBJPROP_YDISTANCE, (int)dparam + 2);
      ObjectSetInteger(chart_ID, bmp_bottom, OBJPROP_YDISTANCE, (int)dparam + 2);
      ObjectSetInteger(chart_ID, bmp_left_out, OBJPROP_YDISTANCE, (int)dparam);
      ObjectSetInteger(chart_ID, bmp_left, OBJPROP_YDISTANCE, (int)dparam);
      //--- チャートを再描画
      ChartRedraw(chart_ID);
     }
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
//---
//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+

実装に使った BMP ファイルです。展開して MQL4\Images または MQL5\Images へ入れてください。

RECTANGLE_LABEL での実装

RECTANGLE_LABEL でも実装できます。画像を作らなくて済むのでその分楽ですが、部品が増えてサイズ指定、色指定、ボーダー指定が必要になるのでコードが長くなります。でも簡単に色を変えられますね。RECTANGLE_LABEL は ANCHOR が効いているのかいないのかよく分からないので未設定です。MQL4 では感じないですが MQL5 では BITMAP_LABEL の方が軽快です。

//+------------------------------------------------------------------+
//|                                            crosshairs_rlabel.mq4 |
//|                                       Copyright 2021, DJ Trader. |
//|                                            https://dj-trader.net |
//+------------------------------------------------------------------+
#property copyright "Copyright 2021, DJ Trader."
#property link      "https://dj-trader.net/fx/mql/7079"
#property version   "1.00"
#property strict
#property indicator_chart_window

// 接頭辞 + オブジェクト名
string rlabel_prefix = "rlabel_";
string rlabel_top_out = rlabel_prefix + "top_out";
string rlabel_top_dot = rlabel_prefix + "top_dot";
string rlabel_top = rlabel_prefix + "top";
string rlabel_right_out = rlabel_prefix + "right_out";
string rlabel_right_dot = rlabel_prefix + "right_dot";
string rlabel_right = rlabel_prefix + "right";
string rlabel_bottom_out = rlabel_prefix + "bottom_out";
string rlabel_bottom_dot = rlabel_prefix + "bottom_dot";
string rlabel_bottom = rlabel_prefix + "bottom";
string rlabel_left_out = rlabel_prefix + "left_out";
string rlabel_left_dot = rlabel_prefix + "left_dot";
string rlabel_left = rlabel_prefix + "left";
// チャート ID
long chart_ID;

//+------------------------------------------------------------------+
//| Custom indicator initialization function
//+------------------------------------------------------------------+
int OnInit()
  {
//--- チャート ID
   chart_ID = ChartID();
//--- マウス動作
   ChartSetInteger(chart_ID, CHART_EVENT_MOUSE_MOVE, true);
//--- RECTANGLE_LABEL オブジェクト生成
   ObjectCreate(chart_ID, rlabel_top_out, OBJ_RECTANGLE_LABEL, 0, 0, 0);
   ObjectCreate(chart_ID, rlabel_top_dot, OBJ_RECTANGLE_LABEL, 0, 0, 0);
   ObjectCreate(chart_ID, rlabel_top, OBJ_RECTANGLE_LABEL, 0, 0, 0);
   ObjectCreate(chart_ID, rlabel_right_out, OBJ_RECTANGLE_LABEL, 0, 0, 0);
   ObjectCreate(chart_ID, rlabel_right_dot, OBJ_RECTANGLE_LABEL, 0, 0, 0);
   ObjectCreate(chart_ID, rlabel_right, OBJ_RECTANGLE_LABEL, 0, 0, 0);
   ObjectCreate(chart_ID, rlabel_bottom_out, OBJ_RECTANGLE_LABEL, 0, 0, 0);
   ObjectCreate(chart_ID, rlabel_bottom_dot, OBJ_RECTANGLE_LABEL, 0, 0, 0);
   ObjectCreate(chart_ID, rlabel_bottom, OBJ_RECTANGLE_LABEL, 0, 0, 0);
   ObjectCreate(chart_ID, rlabel_left_out, OBJ_RECTANGLE_LABEL, 0, 0, 0);
   ObjectCreate(chart_ID, rlabel_left_dot, OBJ_RECTANGLE_LABEL, 0, 0, 0);
   ObjectCreate(chart_ID, rlabel_left, OBJ_RECTANGLE_LABEL, 0, 0, 0);
//--- サイズ設定
   ObjectSetInteger(chart_ID, rlabel_top_out, OBJPROP_XSIZE, 3);
   ObjectSetInteger(chart_ID, rlabel_top_out, OBJPROP_YSIZE, 7);
   ObjectSetInteger(chart_ID, rlabel_top_dot, OBJPROP_XSIZE, 1);
   ObjectSetInteger(chart_ID, rlabel_top_dot, OBJPROP_YSIZE, 8);
   ObjectSetInteger(chart_ID, rlabel_top, OBJPROP_XSIZE, 1);
   ObjectSetInteger(chart_ID, rlabel_top, OBJPROP_YSIZE, 7);
   ObjectSetInteger(chart_ID, rlabel_right_out, OBJPROP_XSIZE, 7);
   ObjectSetInteger(chart_ID, rlabel_right_out, OBJPROP_YSIZE, 3);
   ObjectSetInteger(chart_ID, rlabel_right_dot, OBJPROP_XSIZE, 8);
   ObjectSetInteger(chart_ID, rlabel_right_dot, OBJPROP_YSIZE, 1);
   ObjectSetInteger(chart_ID, rlabel_right, OBJPROP_XSIZE, 7);
   ObjectSetInteger(chart_ID, rlabel_right, OBJPROP_YSIZE, 1);
   ObjectSetInteger(chart_ID, rlabel_bottom_out, OBJPROP_XSIZE, 3);
   ObjectSetInteger(chart_ID, rlabel_bottom_out, OBJPROP_YSIZE, 7);
   ObjectSetInteger(chart_ID, rlabel_bottom_dot, OBJPROP_XSIZE, 1);
   ObjectSetInteger(chart_ID, rlabel_bottom_dot, OBJPROP_YSIZE, 8);
   ObjectSetInteger(chart_ID, rlabel_bottom, OBJPROP_XSIZE, 1);
   ObjectSetInteger(chart_ID, rlabel_bottom, OBJPROP_YSIZE, 7);
   ObjectSetInteger(chart_ID, rlabel_left_out, OBJPROP_XSIZE, 7);
   ObjectSetInteger(chart_ID, rlabel_left_out, OBJPROP_YSIZE, 3);
   ObjectSetInteger(chart_ID, rlabel_left_dot, OBJPROP_XSIZE, 8);
   ObjectSetInteger(chart_ID, rlabel_left_dot, OBJPROP_YSIZE, 1);
   ObjectSetInteger(chart_ID, rlabel_left, OBJPROP_XSIZE, 7);
   ObjectSetInteger(chart_ID, rlabel_left, OBJPROP_YSIZE, 1);
//--- ボーダー設定
   ObjectSetInteger(chart_ID, rlabel_top_out, OBJPROP_BORDER_TYPE, BORDER_FLAT);
   ObjectSetInteger(chart_ID, rlabel_top_dot, OBJPROP_BORDER_TYPE, BORDER_FLAT);
   ObjectSetInteger(chart_ID, rlabel_top, OBJPROP_BORDER_TYPE, BORDER_FLAT);
   ObjectSetInteger(chart_ID, rlabel_right_out, OBJPROP_BORDER_TYPE, BORDER_FLAT);
   ObjectSetInteger(chart_ID, rlabel_right_dot, OBJPROP_BORDER_TYPE, BORDER_FLAT);
   ObjectSetInteger(chart_ID, rlabel_right, OBJPROP_BORDER_TYPE, BORDER_FLAT);
   ObjectSetInteger(chart_ID, rlabel_bottom_out, OBJPROP_BORDER_TYPE, BORDER_FLAT);
   ObjectSetInteger(chart_ID, rlabel_bottom_dot, OBJPROP_BORDER_TYPE, BORDER_FLAT);
   ObjectSetInteger(chart_ID, rlabel_bottom, OBJPROP_BORDER_TYPE, BORDER_FLAT);
   ObjectSetInteger(chart_ID, rlabel_left_out, OBJPROP_BORDER_TYPE, BORDER_FLAT);
   ObjectSetInteger(chart_ID, rlabel_left_dot, OBJPROP_BORDER_TYPE, BORDER_FLAT);
   ObjectSetInteger(chart_ID, rlabel_left, OBJPROP_BORDER_TYPE, BORDER_FLAT);
//--- 色設定(インプットから色が選べるとか)
   ObjectSetInteger(chart_ID, rlabel_top_out, OBJPROP_COLOR, clrBlack);
   ObjectSetInteger(chart_ID, rlabel_top_dot, OBJPROP_COLOR, clrBlack);
   ObjectSetInteger(chart_ID, rlabel_top, OBJPROP_COLOR, clrRed);
   ObjectSetInteger(chart_ID, rlabel_right_out, OBJPROP_COLOR, clrBlack);
   ObjectSetInteger(chart_ID, rlabel_right_dot, OBJPROP_COLOR, clrBlack);
   ObjectSetInteger(chart_ID, rlabel_right, OBJPROP_COLOR, clrRed);
   ObjectSetInteger(chart_ID, rlabel_bottom_out, OBJPROP_COLOR, clrBlack);
   ObjectSetInteger(chart_ID, rlabel_bottom_dot, OBJPROP_COLOR, clrBlack);
   ObjectSetInteger(chart_ID, rlabel_bottom, OBJPROP_COLOR, clrRed);
   ObjectSetInteger(chart_ID, rlabel_left_out, OBJPROP_COLOR, clrBlack);
   ObjectSetInteger(chart_ID, rlabel_left_dot, OBJPROP_COLOR, clrBlack);
   ObjectSetInteger(chart_ID, rlabel_left, OBJPROP_COLOR, clrRed);
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
// 終了時に同じ接頭辞を持つ RECTANGLE_LABEL を全削除
   ObjectsDeleteAll(chart_ID, rlabel_prefix, 0, OBJ_RECTANGLE_LABEL);
  }
//+------------------------------------------------------------------+
//| ChartEvent function
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
   if(id == CHARTEVENT_MOUSE_MOVE)
     {
      //--- マウスの動きに追従(横方向)
      ObjectSetInteger(chart_ID, rlabel_top_out, OBJPROP_XDISTANCE, (int)lparam - 2);
      ObjectSetInteger(chart_ID, rlabel_top_dot, OBJPROP_XDISTANCE, (int)lparam - 1);
      ObjectSetInteger(chart_ID, rlabel_top, OBJPROP_XDISTANCE, (int)lparam - 1);
      ObjectSetInteger(chart_ID, rlabel_right_out, OBJPROP_XDISTANCE, (int)lparam + 1);
      ObjectSetInteger(chart_ID, rlabel_right_dot, OBJPROP_XDISTANCE, (int)lparam + 1);
      ObjectSetInteger(chart_ID, rlabel_right, OBJPROP_XDISTANCE, (int)lparam + 1);
      ObjectSetInteger(chart_ID, rlabel_bottom_out, OBJPROP_XDISTANCE, (int)lparam - 2);
      ObjectSetInteger(chart_ID, rlabel_bottom_dot, OBJPROP_XDISTANCE, (int)lparam - 1);
      ObjectSetInteger(chart_ID, rlabel_bottom, OBJPROP_XDISTANCE, (int)lparam - 1);
      ObjectSetInteger(chart_ID, rlabel_left_out, OBJPROP_XDISTANCE, (int)lparam - 9);
      ObjectSetInteger(chart_ID, rlabel_left_dot, OBJPROP_XDISTANCE, (int)lparam - 10);
      ObjectSetInteger(chart_ID, rlabel_left, OBJPROP_XDISTANCE, (int)lparam - 9);
      //--- マウスの動きに追従(縦方向)
      ObjectSetInteger(chart_ID, rlabel_top_out, OBJPROP_YDISTANCE, (int)dparam - 8);
      ObjectSetInteger(chart_ID, rlabel_top_dot, OBJPROP_YDISTANCE, (int)dparam - 9);
      ObjectSetInteger(chart_ID, rlabel_top, OBJPROP_YDISTANCE, (int)dparam - 8);
      ObjectSetInteger(chart_ID, rlabel_right_out, OBJPROP_YDISTANCE, (int)dparam - 1);
      ObjectSetInteger(chart_ID, rlabel_right_dot, OBJPROP_YDISTANCE, (int)dparam);
      ObjectSetInteger(chart_ID, rlabel_right, OBJPROP_YDISTANCE, (int)dparam);
      ObjectSetInteger(chart_ID, rlabel_bottom_out, OBJPROP_YDISTANCE, (int)dparam + 2);
      ObjectSetInteger(chart_ID, rlabel_bottom_dot, OBJPROP_YDISTANCE, (int)dparam + 2);
      ObjectSetInteger(chart_ID, rlabel_bottom, OBJPROP_YDISTANCE, (int)dparam + 2);
      ObjectSetInteger(chart_ID, rlabel_left_out, OBJPROP_YDISTANCE, (int)dparam - 1);
      ObjectSetInteger(chart_ID, rlabel_left_dot, OBJPROP_YDISTANCE, (int)dparam);
      ObjectSetInteger(chart_ID, rlabel_left, OBJPROP_YDISTANCE, (int)dparam);
      //--- チャートを再描画
      ChartRedraw(chart_ID);
     }
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
//---
//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+

このブログは読んでくださるみなさまに支えられています。気に入って頂けたら応援クリックをお願いします。
にほんブログ村 FX 無職投資家へ    

コメント

MQL4 / MQL5
スポンサーリンク
DJ Traderをフォローする
しがない DJ が FX 専業トレーダーになるまで



タイトルとURLをコピーしました