MQL5 Coobook: 지표 하위 창 컨트롤 - 버튼
소개
이 글에서는 버튼 컨트롤이 있는 사용자 인터페이스를 개발하는 예를 고려할 것입니다. 사용자에게 상호 작용에 대한 아이디어를 전달하기 위해 커서가 버튼 위에 있을 때 버튼의 색상이 변경됩니다. 버튼 위에 커서가 있으면 버튼 색상이 약간 어두워지고 버튼을 클릭하면 훨씬 더 어두워집니다. 또한 각 버튼에 툴팁을 추가하여 직관적인 인터페이스를 만듭니다.
이 글에서는 마우스 이동 이벤트, 마우스 왼쪽 버튼의 상태, 개체를 마우스 왼쪽 버튼으로 클릭 및 차트 속성 수정 이벤트와 같은 일부 이벤트도 다룹니다. 지표 하위 창의 전체 공간을 차지하는 버튼 패널을 만들 것입니다. 설명을 위해 버튼은 각 행에 4개의 버튼이 있는 3개의 행으로 정렬됩니다.
개발
MQL5에서는 OBJ_BUTTON(버튼), OBJ_BITMAP(비트맵), OBJ_BITMAP_LABEL(비트맵 레이블) 또는 OBJ_EDIT (편집)과 같은 다양한 그래픽 개체를 사용하여 버튼이 생성될 수 있습니다.
이 글에서는 OBJ_EDIT를 사용하여 버튼을 생성합니다. 이 유형의 개체는 읽기 전용으로 만들 수 있습니다. 또한 사용자가 지정한 텍스트를 표시할 수 있다는 점에서 유용합니다. 또한 테두리를 유지하면서 개체의 모서리를 날카롭게 만들 수 있습니다.
이제 MQL5 마법사를 사용하여 지표를 생성해 보겠습니다. 약간 수정하여 지표의 소스 코드는 다음과 같습니다.
//+------------------------------------------------------------------+ //| TestButtons.mq5 | //| Copyright 2013, MetaQuotes Software Corp. | //| http://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2013, MetaQuotes Software Corp." #property link "http://www.mql5.com" #property version "1.00" //--- #property indicator_separate_window // Indicator is in the subwindow #property indicator_plots 0 // No plotting series //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- indicator buffers mapping //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) { //--- //--- return value of prev_calculated for next call return(rates_total); } //+------------------------------------------------------------------+ //| Timer function | //+------------------------------------------------------------------+ void OnTimer() { //--- } //+------------------------------------------------------------------+ //| ChartEvent function | //+------------------------------------------------------------------+ void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { //--- } //+------------------------------------------------------------------+
우리가 지금 가지고 있는 것은 플로팅 시리즈가 없는 빈 창입니다. 타이머의 필요성은 잠시 후에 논의될 것입니다.
이제 함수를 만드는 데 사용할 상수, 변수 및 배열을 추가해 보겠습니다. 모든 배열은 2차원입니다. 첫 번째 치수는 창 높이의 버튼 수를 나타내고 두 번째 치수는 창 너비의 버튼 수를 나타냅니다.
//--- #define BUTTON_COLUMNS 4 // Number of buttons across the width #define BUTTON_ROWS 3 // Number of buttons across the height //+------------------------------------------------------------------+ //| Global parameters | //+------------------------------------------------------------------+ //--- Font string font_name="Calibri"; //--- Indicator subwindow properties int subwindow_number =WRONG_VALUE; // Subwindow number int subwindow_height =0; // Subwindow height string subwindow_shortname ="TestButtons"; // Short name of the indicator string prefix =subwindow_shortname+"_"; // Prefix for object names int chart_width =0; // Chart width int chart_height =0; // Chart height int chart_y_offset =0; // Distance from the chart top to the subwindow //--- Colors of button elements color background_color =clrSteelBlue; // Button color color font_color =clrWhite; // Font color color hover_background_color =C'38,118,166'; // Button color when the cursor goes over color clicked_background_color =C'2,72,136'; // Clicked button color //--- Text displayed on buttons string button_texts[BUTTON_ROWS][BUTTON_COLUMNS]= { {"Button 01","Button 02","Button 03","Button 04"}, {"Button 05","Button 06","Button 07","Button 08"}, {"Button 09","Button 10","Button 11","Button 12"} }; //--- Object names string button_object_names[BUTTON_ROWS][BUTTON_COLUMNS]= { {"button_01","button_02","button_03","button_04"}, {"button_05","button_06","button_07","button_08"}, {"button_09","button_10","button_11","button_12"} }; //--- Button widths int button_widths[BUTTON_ROWS][BUTTON_COLUMNS]; //--- Button heights int button_heights[BUTTON_ROWS][BUTTON_COLUMNS]; //--- X-coordinates int button_x_distances[BUTTON_ROWS][BUTTON_COLUMNS]; //--- Y-coordinates int button_y_distances[BUTTON_ROWS][BUTTON_COLUMNS]; //--- Button states bool button_states[BUTTON_ROWS][BUTTON_COLUMNS]= { {true,false,false,false}, {false,false,false,false}, {false,false,false,false} }; //--- Button colors color button_colors[BUTTON_ROWS][BUTTON_COLUMNS];
지표를 차트에 로드하는 동안 배열은 좌표와 크기를 계산한 후 OnInit() 함수의 개체 속성으로 초기화되어야 합니다. 커서 추적도 활성화해야 합니다. 마지막으로 지표 하위 창에 버튼을 추가해야 합니다. 편의를 위해 이러한 작업은 별도의 기능에서 수행되며 아래에서 하나씩 살펴보겠습니다. 결과적으로 OnInit() 함수 코드는 다음과 같습니다.
//+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- Set the timer at 1-second intervals EventSetTimer(1); //--- Add prefix to object names AddPrefix(); //--- Enable tracking of mouse events ChartSetInteger(0,CHART_EVENT_MOUSE_MOVE,true); //--- Set the short name IndicatorSetString(INDICATOR_SHORTNAME,subwindow_shortname); //--- Set subwindow properties SetSubwindowProperties(); //--- Set button properties SetButtonColors(); // Colors SetButtonCoordinates(); // Coordinates SetButtonSizes(); // Sizes //--- Add the button panel AddButtonsPanel(); //--- Refresh the chart ChartRedraw(); //--- Everything completed successfully return(INIT_SUCCEEDED); }
AddPrefix() 함수에서 각 그래픽 개체의 이름에 접두사, 즉 지표의 짧은 이름이 추가됩니다. 이는 차트에서 둘 이상의 프로그램이 실행되고 있는 개체 이름이 일치하는 경우 개체의 교체/삭제/이동을 제외하기 위해 필요합니다.
//+------------------------------------------------------------------+ //| Adding prefix to all object names | //+------------------------------------------------------------------+ void AddPrefix() { //--- Add prefix to object names for(int i=0; i<BUTTON_COLUMNS; i++) for(int j=0; j<BUTTON_ROWS; j++) button_object_names[j][i]=prefix+button_object_names[j][i]; }
계산에 필요한 차트 속성은 SetSubwindowProperties() 함수에서 초기화됩니다.
//+------------------------------------------------------------------+ //| Setting subwindow properties | //+------------------------------------------------------------------+ void SetSubwindowProperties() { //--- Indicator subwindow number subwindow_number=ChartWindowFind(0,subwindow_shortname); //--- Subwindow width and height chart_width=(int)ChartGetInteger(0,CHART_WIDTH_IN_PIXELS); subwindow_height=(int)ChartGetInteger(0,CHART_HEIGHT_IN_PIXELS,subwindow_number); }
차트 속성을 얻은 후 버튼 색상, 좌표 값 및 크기를 결정하기 위한 계산을 수행할 수 있습니다. 이러한 모든 작업은 아래에 제공된 세 가지 개별 기능에서 수행됩니다.
//+------------------------------------------------------------------+ //| Setting button color | //+------------------------------------------------------------------+ void SetButtonColors() { for(int i=0; i<BUTTON_COLUMNS; i++) { for(int j=0; j<BUTTON_ROWS; j++) { //--- If the button is clicked if(button_states[j][i]) button_colors[j][i]=clicked_background_color; //--- If the button is unclicked else button_colors[j][i]=background_color; } } } //+------------------------------------------------------------------+ //| Setting X and Y coordinates for buttons | //+------------------------------------------------------------------+ void SetButtonCoordinates() { int button_width=chart_width/BUTTON_COLUMNS; int button_height=subwindow_height/BUTTON_ROWS; //--- for(int i=0; i<BUTTON_COLUMNS; i++) { for(int j=0; j<BUTTON_ROWS; j++) { if(i==0) button_x_distances[j][i]=0; else button_x_distances[j][i]=(button_width*i)-i; //--- if(j==0) button_y_distances[j][i]=0; else button_y_distances[j][i]=(button_height*j)-j; } } } //+------------------------------------------------------------------+ //| Setting button width and height | //+------------------------------------------------------------------+ void SetButtonSizes() { int button_width=chart_width/BUTTON_COLUMNS; int button_height=subwindow_height/BUTTON_ROWS; //--- for(int i=0; i<BUTTON_COLUMNS; i++) { for(int j=0; j<BUTTON_ROWS; j++) { if(i==BUTTON_COLUMNS-1) button_widths[j][i]=chart_width-(button_width*(BUTTON_COLUMNS-1)-i); else button_widths[j][i]=button_width; //--- if(j==BUTTON_ROWS-1) button_heights[j][i]=subwindow_height-(button_height*(BUTTON_ROWS-1)-j)-1; else button_heights[j][i]=button_height; } } }
마지막으로 AddButtonsPanel() 함수는 지표 하위 창에 버튼을 추가합니다.
//+------------------------------------------------------------------+ //| Adding buttons to the indicator subwindow | //+------------------------------------------------------------------+ void AddButtonsPanel() { //--- Create buttons for(int i=0; i<BUTTON_COLUMNS; i++) { for(int j=0; j<BUTTON_ROWS; j++) { CreateButton(0,subwindow_number,button_object_names[j][i],button_texts[j][i], CORNER_LEFT_UPPER,font_name,8,font_color,button_colors[j][i],clrNONE, button_widths[j][i],button_heights[j][i], button_x_distances[j][i],button_y_distances[j][i],2,true,button_texts[j][i]); } } }
보조 함수 CreateButton()의 소스 코드는 다음과 같습니다.
//+------------------------------------------------------------------+ //| Creating a button (graphical object of the Edit type) | //+------------------------------------------------------------------+ void CreateButton(long chart_id, // chart id int sub_window, // (sub)window number string object_name, // object name string text, // displayed text long corner, // chart corner string font, // font int font_size, // font size color c_font, // font color color c_background, // background color color c_border, // border color int x_size, // width int y_size, // height int x_dist, // X-coordinate int y_dist, // Y-coordinate long zorder, // Z-order bool read_only, // Read Only flag string tooltip) // tooltip { //--- If the object has been created successfully, set the remaining properties if(ObjectCreate(chart_id,object_name,OBJ_EDIT,subwindow_number,0,0)) { ObjectSetString(chart_id,object_name,OBJPROP_TEXT,text); // name ObjectSetInteger(chart_id,object_name,OBJPROP_CORNER,corner); // chart corner ObjectSetString(chart_id,object_name,OBJPROP_FONT,font); // font ObjectSetInteger(chart_id,object_name,OBJPROP_FONTSIZE,font_size); // font size ObjectSetInteger(chart_id,object_name,OBJPROP_COLOR,c_font); // font color ObjectSetInteger(chart_id,object_name,OBJPROP_BGCOLOR,c_background); // background color ObjectSetInteger(chart_id,object_name,OBJPROP_BORDER_COLOR,c_border); // border color ObjectSetInteger(chart_id,object_name,OBJPROP_XSIZE,x_size); // width ObjectSetInteger(chart_id,object_name,OBJPROP_YSIZE,y_size); // height ObjectSetInteger(chart_id,object_name,OBJPROP_XDISTANCE,x_dist); // X-coordinate ObjectSetInteger(chart_id,object_name,OBJPROP_YDISTANCE,y_dist); // Y-coordinate ObjectSetInteger(chart_id,object_name,OBJPROP_SELECTABLE,false); // object is not available for selection ObjectSetInteger(chart_id,object_name,OBJPROP_ZORDER,zorder); // Z-order ObjectSetInteger(chart_id,object_name,OBJPROP_READONLY,read_only); // Read Only text ObjectSetInteger(chart_id,object_name,OBJPROP_ALIGN,ALIGN_CENTER); // align center ObjectSetString(chart_id,object_name,OBJPROP_TOOLTIP,tooltip); // no tooltip if "\n" } }
CreateButton() 함수의 마지막 매개변수에 유의하십시오. 이 매개변수는 마우스 커서가 그래픽 개체 위로 이동할 때 도구 설명을 담당합니다. 예를 들어, AddButtonsPanel() 함수에서 이 매개변수는 button_texts 배열(버튼에 표시되는 텍스트)에서 전달된 값으로 표시됩니다. 원하는 경우 더 자세한 설명이 포함된 별도의 배열을 생성할 수 있습니다.
이제 지표를 차트에 연결하면 결과는 다음과 같습니다.
그림 1. 지표 하위 창에 추가된 버튼
현재, 이것들은 지표 서브 윈도우에 배열된 단순한 개체입니다. 사용자와의 상호 작용은 아직 구현되지 않았습니다. 이제 이러한 개체에 "생명을 불어넣습니다".
먼저 후자의 크기를 조정할 때 하위 창의 크기에 따라 단추 크기를 조정할 수 있는 가능성을 구현합니다. 이를 위해 UpdateButtonCoordinates() 및 ResizeButtons()라는 두 가지 함수를 더 작성합니다. 버튼 좌표와 크기를 설정합니다.
//+------------------------------------------------------------------+ //| Updating button coordinates | //+------------------------------------------------------------------+ void UpdateButtonCoordinates() { //--- Set coordinates for(int i=0; i<BUTTON_COLUMNS; i++) { for(int j=0; j<BUTTON_ROWS; j++) { ObjectSetInteger(0,button_object_names[j][i],OBJPROP_XDISTANCE,button_x_distances[j][i]); ObjectSetInteger(0,button_object_names[j][i],OBJPROP_YDISTANCE,button_y_distances[j][i]); } } } //+------------------------------------------------------------------+ //| Updating button sizes | //+------------------------------------------------------------------+ void ResizeButtons() { //--- Set sizes for(int i=0; i<BUTTON_COLUMNS; i++) { for(int j=0; j<BUTTON_ROWS; j++) { ObjectSetInteger(0,button_object_names[j][i],OBJPROP_XSIZE,button_widths[j][i]); ObjectSetInteger(0,button_object_names[j][i],OBJPROP_YSIZE,button_heights[j][i]); } } }
차트 속성 수정 및 차트 크기 조정 이벤트를 처리하려면 CHARTEVENT_CHART_CHANGE 식별자를 사용해야 합니다. 아래에서 OnChartEvent() 함수 본문에 추가해야 하는 코드를 볼 수 있습니다.
//+------------------------------------------------------------------+ //| ChartEvent function | //+------------------------------------------------------------------+ void OnChartEvent(const int id, // event identifier const long &lparam, // parameter of the event of type long const double &dparam, // parameter of the event of type double const string &sparam) // parameter of the event of type string { //--- Tracking the event of modifying the chart properties and resizing the chart if(id==CHARTEVENT_CHART_CHANGE) { //--- Set subwindow properties SetSubwindowProperties(); //--- Set button coordinates SetButtonCoordinates(); //--- Set button sizes SetButtonSizes(); //--- Set new button coordinates UpdateButtonCoordinates(); //--- Set new button sizes ResizeButtons(); //--- Refresh the chart ChartRedraw(); return; } }
지금 차트에 지표를 추가하면(또는 지표가 이미 차트에 있는 경우 코드를 다시 컴파일하면) 차트 창 또는 지표 하위 창의 크기가 조정되는 즉시 버튼의 크기가 자동으로 조정되고 위치가 변경됩니다.
커서가 버튼 위에 있을 때 버튼 색상 변경을 추가로 구현합니다. 그러나 함수 코드를 작성하기 전에 먼저 CHARTEVENT_MOUSE_MOVE 식별자로 이벤트를 전달하는 과정을 살펴보겠습니다.
OnInit() 함수에는 프로그램이 마우스 커서 움직임과 왼쪽 마우스 버튼의 상태를 추적하도록 지시하는 문자열이 이미 있습니다.
//--- Enable tracking of mouse events ChartSetInteger(0,CHART_EVENT_MOUSE_MOVE,true);
이 문자열이 없으면(또는 전달된 마지막 매개변수 값이 false인 경우) CHARTEVENT_MOUSE_MOVE 식별자가 있는 이벤트는 OnChartEvent() 함수에서 추적되지 않습니다. 이것은 모든 프로그램에서 이러한 이벤트를 추적할 필요가 없기 때문에 매우 유용하게 보일 수 있습니다.
마우스 이벤트 추적이 작동하는 방식을 이해하기 위해 차트에 해당 주석을 표시할 수 있는 가능성을 OnChartEvent() 함수 코드에 임시로 추가할 수 있습니다.
//--- Mouse movement and left-click tracking if(id==CHARTEVENT_MOUSE_MOVE) { Comment("id: ",CHARTEVENT_MOUSE_MOVE,"\n", "lparam (x): ",lparam,"\n", "dparam (y): ",dparam,"\n", "sparam (state of the mouse buttons): ",sparam );
이제 차트에서 마우스 커서를 움직이기 시작하면 왼쪽 상단 모서리에서 커서의 현재 좌표를 볼 수 있습니다. 왼쪽 버튼을 클릭하면 변경 사항이 주석 줄 sparam(마우스 버튼의 상태)에 표시됩니다. 여기서 1(1)은 마우스 버튼을 클릭하고 0(0)은 해제됨을 의미합니다.
현재 마우스 커서가 있는 하위 창을 알아야 하는 경우 ChartXYToTimePrice() 함수를 사용할 수 있습니다. 좌표를 가져오고 창/하위 창 번호, 시간 및 가격을 반환합니다(참조에 의해 전달된 변수로). 다음 코드를 테스트하면 실제로 작동하는 것을 볼 수 있습니다.
//--- Mouse movement and left-click tracking if(id==CHARTEVENT_MOUSE_MOVE) { int x =(int)lparam; // X-coordinate int y =(int)dparam; // Y-coordinate int window =WRONG_VALUE; // Number of the window where the cursor is located datetime time =NULL; // Time corresponding to the X-coordinate double price =0.0; // Price corresponding to the Y-coordinate //--- Get the position of the cursor if(ChartXYToTimePrice(0,x,y,window,time,price)) { Comment("id: ",CHARTEVENT_MOUSE_MOVE,"\n", "x: ",x,"\n", "y: ",y,"\n", "sparam (state of the mouse buttons): ",sparam,"\n", "window: ",window,"\n", "time: ",time,"\n", "price: ",DoubleToString(price,_Digits) ); } //--- return; }
상대 좌표를 사용하면 지표 하위 창에서 계산이 더 쉬워집니다. 이 경우 Y 좌표(가격 척도)와 관련이 있습니다. 상대 값을 얻으려면 현재 값에서 차트 상단에서 지표 하위 창까지의 거리만 빼면 됩니다. 이것은 다음과 같이 할 수 있습니다:
//--- Get the position of the cursor if(ChartXYToTimePrice(0,x,y,window,time,price)) { //--- Get the distance from the chart top to the indicator subwindow chart_y_offset=(int)ChartGetInteger(0,CHART_WINDOW_YDISTANCE,subwindow_number); //--- Convert the Y-coordinate to the relative value y-=chart_y_offset; Comment("id: ",CHARTEVENT_MOUSE_MOVE,"\n", "x: ",x,"\n", "y: ",y,"\n", "sparam (state of the mouse buttons): ",sparam,"\n", "window: ",window,"\n", "time: ",time,"\n", "price: ",DoubleToString(price,_Digits) ); }
이제 y 변수의 값은 마우스 커서가 지표 하위 창 위에 있으면 음수이고 커서가 하위 창 영역 위로 이동하면 양수입니다.
기본적으로 차트의 커서 위치에 관계없이 시간 척도를 따라 차트를 스크롤할 수 있습니다. 그러나 필요한 경우 차트 스크롤을 비활성화할 수 있습니다. 커서가 패널이나 사용자 정의 컨트롤 위에 있을 때 주로 필요합니다. 커서가 지표 하위 창에 있을 때 차트 스크롤을 비활성화하고 커서가 하위 창 밖으로 이동할 때 활성화하는 코드는 예를 들어 다음과 같을 수 있습니다.
//--- If the cursor is in the subwindow area, disable chart scrolling if(window==subwindow_number) ChartSetInteger(0,CHART_MOUSE_SCROLL,false); //--- Enable chart scrolling if the cursor moves out of the indicator subwindow area else ChartSetInteger(0,CHART_MOUSE_SCROLL,true);
또한 커서가 해당 버튼 위에 있을 때 버튼 색상을 변경하는 함수를 작성해 보겠습니다. ChangeButtonColorOnHover():
//+------------------------------------------------------------------+ //| Changing the button color when the cursor hovers over the button | //+------------------------------------------------------------------+ void ChangeButtonColorOnHover(int x,int y) { int x1,y1,x2,y2; //--- Initialize the array of XY coordinates for buttons SetButtonCoordinates(); //--- Determine if the cursor is over any of the buttons for(int i=0; i<BUTTON_COLUMNS; i++) { for(int j=0; j<BUTTON_ROWS; j++) { //--- If this button is clicked, go to the next one if(button_states[j][i]) continue; //--- Get the button boundaries x1=button_x_distances[j][i]; y1=button_y_distances[j][i]; x2=button_x_distances[j][i]+button_widths[j][i]; y2=button_y_distances[j][i]+button_heights[j][i]; //--- If the cursor is within the button area, set the new button color if(x>x1 && x<x2 && y>y1 && y<y2) ObjectSetInteger(0,button_object_names[j][i],OBJPROP_BGCOLOR,hover_background_color); //--- Otherwise set the standard color else ObjectSetInteger(0,button_object_names[j][i],OBJPROP_BGCOLOR,background_color); } } }
결과적으로 CHARTEVENT_MOUSE_MOVE 식별자 분기에 다음 소스 코드가 있습니다.
//--- Mouse movement and left-click tracking if(id==CHARTEVENT_MOUSE_MOVE) { int x =(int)lparam; // X-coordinate int y =(int)dparam; // Y-coordinate int window =WRONG_VALUE; // Number of the window where the cursor is located datetime time =NULL; // Time corresponding to the X-coordinate double price =0.0; // Price corresponding to the Y-coordinate //--- Get the position of the cursor if(ChartXYToTimePrice(0,x,y,window,time,price)) { //--- Get the distance from the chart top to the indicator subwindow chart_y_offset=(int)ChartGetInteger(0,CHART_WINDOW_YDISTANCE,subwindow_number); //--- Convert the Y-coordinate to the relative value y-=chart_y_offset; //--- If the cursor is in the subwindow area, disable chart scrolling if(window==subwindow_number) ChartSetInteger(0,CHART_MOUSE_SCROLL,false); //--- Enable chart scrolling if the cursor moves out of the indicator subwindow area else ChartSetInteger(0,CHART_MOUSE_SCROLL,true); //--- Change the button color when the cursor is hovered over ChangeButtonColorOnHover(x,y); } //--- Refresh the chart ChartRedraw(); return; }
이제 버튼 위로 커서를 이동하면 버튼 색상이 변경/정상으로 돌아오는 것을 볼 수 있습니다.
현재 버튼 01에만 클릭한 버튼의 색상이 있습니다. 다른 버튼을 클릭하려고 하면 응답이 없으므로 색상이 변경되지 않습니다. 이 경우 색상 변경을 구현하려면 CHARTEVENT_OBJECT_CLICK 식별자가 있는 이벤트를 사용해야 합니다.
InitializeButtonStates() 및 ChangeButtonColorOnClick()이라는 두 가지 함수를 작성해 보겠습니다. InitializeButtonStates() 함수는 이름의 접두사를 고려하여 주어진 버튼이 클릭되었는지 여부를 확인합니다. 클릭 이벤트가 식별되면 버튼 상태 배열(button_states)이 루프에서 초기화되고 함수가 true를 반환합니다.
//+------------------------------------------------------------------+ //| Initializing button states in case of click | //+------------------------------------------------------------------+ bool InitializeButtonStates(string clicked_object) { //--- Get the indicator subwindow number subwindow_number=ChartWindowFind(0,subwindow_shortname); //--- If a button in the indicator subwindow has been clicked if(ObjectFind(0,clicked_object)==subwindow_number && StringFind(clicked_object,prefix+"button_",0)>=0) { //--- Determine the clicked button for(int i=0; i<BUTTON_COLUMNS; i++) { for(int j=0; j<BUTTON_ROWS; j++) { //--- Determine the state of all buttons if(clicked_object==button_object_names[j][i]) button_states[j][i]=true; else button_states[j][i]=false; } } //--- return(true); } //--- return(false); }
다음으로 ChangeButtonColorOnClick() 함수는 button_states 배열의 값에 따라 버튼 색상을 설정합니다.
//+------------------------------------------------------------------+ //| Changing the button color in case of click | //+------------------------------------------------------------------+ void ChangeButtonColorOnClick() { for(int i=0; i<BUTTON_COLUMNS; i++) { for(int j=0; j<BUTTON_ROWS; j++) { //--- If the button has been clicked, it is set a distinctive color if(button_states[j][i]) ObjectSetInteger(0,button_object_names[j][i],OBJPROP_BGCOLOR,clicked_background_color); //--- Set the standard color to the unclicked button else ObjectSetInteger(0,button_object_names[j][i],OBJPROP_BGCOLOR,background_color); } } }
모두 작동하도록 하려면 이벤트 추적 기능 OnChartEvent()에 버튼 클릭 처리를 추가해야 합니다.
//--- Tracking left mouse button clicks on a graphical object if(id==CHARTEVENT_OBJECT_CLICK) { //--- If the button has been clicked if(InitializeButtonStates(sparam)) { //--- Set button colors ChangeButtonColorOnClick(); } //--- Refresh the chart ChartRedraw(); return; }
이제 클릭하면 버튼의 색상이 변경됩니다.
아직 처리해야 할 몇 가지 사항이 있습니다. OnDeinit() 함수에서 차트에서 지표를 삭제할 때 하위 창 영역에서 차트 스크롤을 활성화하고 마우스 이벤트 추적을 비활성화해야 합니다. 이는 이벤트 추적을 사용하는 여러 프로그램이 차트에서 동시에 실행 중인 경우 중요할 수 있습니다.
//+------------------------------------------------------------------+ //| Deinitialization | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { if(reason==REASON_REMOVE || // If the indicator has been deleted from the chart or reason==REASON_RECOMPILE) // the program has been recompiled { //--- Deactivate the timer EventKillTimer(); //--- Delete the objects DeleteButtons(); //--- Enable chart scrolling ChartSetInteger(0,CHART_MOUSE_SCROLL,true); //--- Disable tracking of mouse events ChartSetInteger(0,CHART_EVENT_MOUSE_MOVE,false); //--- Refresh the chart ChartRedraw(); } }
프로그램의 그래픽 개체를 삭제하는 기능:
//+------------------------------------------------------------------+ //| Deleting all buttons | //+------------------------------------------------------------------+ void DeleteButtons() { for(int i=0; i<BUTTON_COLUMNS; i++) for(int j=0; j<BUTTON_ROWS; j++) DeleteObjectByName(button_object_names[j][i]); } //+------------------------------------------------------------------+ //| Deleting the object by name | //+------------------------------------------------------------------+ void DeleteObjectByName(string object_name) { //--- If such object exists if(ObjectFind(0,object_name)>=0) { //--- If an error occurred when deleting, print the relevant message if(!ObjectDelete(0,object_name)) Print("Error ("+IntegerToString(GetLastError())+") when deleting the object!"); } }
마지막으로 이 프로그램에 타이머가 필요한 이유가 있습니다. 예를 들어, 차트에서 둘 이상의 프로그램이 실행 중이고 각 프로그램이 마우스 이벤트를 추적해야 하는 경우 그 중 하나가 차트에서 삭제되면 OnDeinit() 모든 프로그램에 대한 기능. 따라서 대안으로 매초 확인을 실행하여 마우스 이벤트 추적이 활성화되었는지 확인할 수 있습니다.
//+------------------------------------------------------------------+ //| Timer function | //+------------------------------------------------------------------+ void OnTimer() { //--- Check whether tracking of mouse events is enabled CheckChartEventMouseMove(); }
CheckChartEventMouseMove() 함수 코드는 다음과 같습니다.
경우에 따라 CHARTEVENT_CHART_CHANGE 식별자가 있는 이벤트에 대해 이 검사를 수행하는 것으로 충분할 수 있습니다.
아래에서 우리가 얻은 결과를 보여주는 비디오를 볼 수 있습니다.
결론
자, 이것으로 기본적인 건 마쳤습니다. TestButtons.mq5 지표는 글에 첨부되어 있으며 다운로드할 수 있습니다. 추가 개발을 통해 이 예제는 흥미로운 메인 메뉴로 성장할 수 있습니다. 예를 들어, 사용자는 특정 버튼을 클릭하여 관련 정보로 이동할 수 있습니다. 필요한 경우 버튼 수를 늘릴 수 있습니다.
MetaQuotes 소프트웨어 사를 통해 러시아어가 번역됨.
원본 기고글: https://www.mql5.com/ru/articles/750