Рецепты MQL5 - Элементы управления в подокне индикатора - Кнопки
Введение
В этой статье мы рассмотрим пример разработки пользовательского интерфейса с такими элементами управления, как кнопки. В качестве подсказки пользователю о том, что с элементом можно взаимодействовать, сделаем так, чтобы кнопка при наведении на нее курсора мыши меняла цвет. При наведении курсора мыши на кнопку цвет будет немного затеняться, а при нажатии будет становиться заметно темнее. Добавим еще всплывающие подсказки для каждой кнопки. Таким образом, интерфейс станет интуитивно понятным.
Также в статье будут рассматриваться события: перемещение курсора мыши, состояние левой кнопки мыши, нажатие левой кнопкой мыши на объекте, а также событие, которое возникает при изменении свойств графика. Создадим кнопочную панель, которая занимает все пространство подокна индикатора. Для примера сделаем три ряда, в которых будет по четыре кнопки.
Процесс разработки
Для создания кнопок в 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 // Индикатор в подокне #property indicator_plots 0 // Отсутствие графических серий //+------------------------------------------------------------------+ //| 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) { //--- } //+------------------------------------------------------------------+
То есть, сейчас это пустое окно, в котором отсутствуют графические серии. Необходимость наличия таймера рассмотрим чуть позже.
Теперь добавим константы, переменные и массивы, которые будем использовать при создании функций. Все массивы двухмерные. В первом измерении указывается количество кнопок по высоте, а во втором - количество кнопок по ширине подокна:
//--- #define BUTTON_COLUMNS 4 // Количество кнопок по ширине #define BUTTON_ROWS 3 // Количество кнопок по высоте //+------------------------------------------------------------------+ //| Глобальные параметры | //+------------------------------------------------------------------+ //--- Шрифт string font_name="Calibri"; //--- Свойства подокна индикатора int subwindow_number =WRONG_VALUE; // Номер подокна int subwindow_height =0; // Высота подокна string subwindow_shortname ="TestButtons"; // Короткое имя индикатора string prefix =subwindow_shortname+"_"; // Префикс для имен объектов int chart_width =0; // Ширина графика int chart_height =0; // Высота графика int chart_y_offset =0; // Дистанция от верха графика до подокна //--- Цвета элементов кнопки color background_color =clrSteelBlue; // Цвет кнопки color font_color =clrWhite; // Цвет шрифта color hover_background_color =C'38,118,166'; // Цвет кнопки при наведении курсора color clicked_background_color =C'2,72,136'; // Цвет нажатой кнопки //--- Отображаемый текст в кнопках 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"} }; //--- Названия объектов 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"} }; //--- Ширины кнопок int button_widths[BUTTON_ROWS][BUTTON_COLUMNS]; //--- Высоты кнопок int button_heights[BUTTON_ROWS][BUTTON_COLUMNS]; //--- Координаты X int button_x_distances[BUTTON_ROWS][BUTTON_COLUMNS]; //--- Координаты Y int button_y_distances[BUTTON_ROWS][BUTTON_COLUMNS]; //--- Состояния кнопок bool button_states[BUTTON_ROWS][BUTTON_COLUMNS]= { {true,false,false,false}, {false,false,false,false}, {false,false,false,false} }; //--- Цвета кнопок color button_colors[BUTTON_ROWS][BUTTON_COLUMNS];
Во время загрузки индикатора на график в функции OnInit() нужно инициализировать массивы свойствами объектов, рассчитав координаты и размеры. Также нужно включить слежение за перемещением курсора мыши. И, наконец, нужно добавить кнопки в подокно индикатора. Все эти действия для удобства вынесем в отдельные функции, которые рассмотрим далее по порядку. В итоге код в функции OnInit() примет следующий вид:
//+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- Включим таймер с интервалом 1 секунда EventSetTimer(1); //--- Добавим префикс к именам объектов AddPrefix(); //--- Включим слежение за событиями мыши ChartSetInteger(0,CHART_EVENT_MOUSE_MOVE,true); //--- Установим короткое имя IndicatorSetString(INDICATOR_SHORTNAME,subwindow_shortname); //--- Установим свойства подокна SetSubwindowProperties(); //--- Установим свойства кнопок SetButtonColors(); // Цвета SetButtonCoordinates(); // Координаты SetButtonSizes(); // Размеры //--- Добавим кнопочную панель AddButtonsPanel(); //--- Обновим график ChartRedraw(); //--- Все прошло успешно return(INIT_SUCCEEDED); }
В функции AddPrefix() к имени каждого графического объекта добавляется префикс, который является коротким именем индикатора. Это необходимо для того, чтобы исключить замену/удаление/смещение объектов при совпадении имен объектов, если на графике используется более одной программы.
//+------------------------------------------------------------------+ //| Добавляет ко всем названиям объектов префикс | //+------------------------------------------------------------------+ void AddPrefix() { //--- Установим префикс названиям объектов 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():
//+------------------------------------------------------------------+ //| Устанавливает свойства подокна | //+------------------------------------------------------------------+ void SetSubwindowProperties() { //--- Номер подокна индикатора subwindow_number=ChartWindowFind(0,subwindow_shortname); //--- Ширина и высота подокна chart_width=(int)ChartGetInteger(0,CHART_WIDTH_IN_PIXELS); subwindow_height=(int)ChartGetInteger(0,CHART_HEIGHT_IN_PIXELS,subwindow_number); }
После того, как свойства графика получены, можно произвести расчеты для определения цвета кнопок, значений координат и размеров. Это все делается в трех отдельных функциях, которые представлены ниже:
//+------------------------------------------------------------------+ //| Задает цвет для кнопок | //+------------------------------------------------------------------+ void SetButtonColors() { for(int i=0; i<BUTTON_COLUMNS; i++) { for(int j=0; j<BUTTON_ROWS; j++) { //--- Если кнопка нажата if(button_states[j][i]) button_colors[j][i]=clicked_background_color; //--- Если кнопка отжата else button_colors[j][i]=background_color; } } } //+------------------------------------------------------------------+ //| Задает координаты X и Y для кнопок | //+------------------------------------------------------------------+ 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; } } } //+------------------------------------------------------------------+ //| Задает ширину и высоту для кнопок | //+------------------------------------------------------------------+ 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() добавляет кнопки в подокно индикатора:
//+------------------------------------------------------------------+ //| Добавляет кнопки в подокно индикатора | //+------------------------------------------------------------------+ void AddButtonsPanel() { //--- Создадим кнопки 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() выглядит так:
//+------------------------------------------------------------------+ //| Создает кнопку (графический объект типа "Поле ввода") | //+------------------------------------------------------------------+ void CreateButton(long chart_id, // id графика int sub_window, // номер окна (подокна) string object_name, // имя объекта string text, // отображаемый текст long corner, // угол графика string font, // шрифт int font_size, // размер шрифта color c_font, // цвет шрифта color c_background, // цвет фона color c_border, // цвет рамки int x_size, // ширина int y_size, // высота int x_dist, // координата по шкале X int y_dist, // координата по шкале Y long zorder, // приоритет bool read_only, // флаг "Только для чтения" string tooltip) // всплывающая подсказка { //--- Если объект создался успешно, установим остальные свойства if(ObjectCreate(chart_id,object_name,OBJ_EDIT,subwindow_number,0,0)) { ObjectSetString(chart_id,object_name,OBJPROP_TEXT,text); // имя ObjectSetInteger(chart_id,object_name,OBJPROP_CORNER,corner); // угол привязки ObjectSetString(chart_id,object_name,OBJPROP_FONT,font); // шрифт ObjectSetInteger(chart_id,object_name,OBJPROP_FONTSIZE,font_size); // размер шрифта ObjectSetInteger(chart_id,object_name,OBJPROP_COLOR,c_font); // цвет шрифта ObjectSetInteger(chart_id,object_name,OBJPROP_BGCOLOR,c_background); // цвет фона ObjectSetInteger(chart_id,object_name,OBJPROP_BORDER_COLOR,c_border); // цвет рамки ObjectSetInteger(chart_id,object_name,OBJPROP_XSIZE,x_size); // ширина ObjectSetInteger(chart_id,object_name,OBJPROP_YSIZE,y_size); // высота ObjectSetInteger(chart_id,object_name,OBJPROP_XDISTANCE,x_dist); // координата X ObjectSetInteger(chart_id,object_name,OBJPROP_YDISTANCE,y_dist); // координата Y ObjectSetInteger(chart_id,object_name,OBJPROP_SELECTABLE,false); // объект нельзя выделить ObjectSetInteger(chart_id,object_name,OBJPROP_ZORDER,zorder); // приоритет ObjectSetInteger(chart_id,object_name,OBJPROP_READONLY,read_only); // текст не редактируется ObjectSetInteger(chart_id,object_name,OBJPROP_ALIGN,ALIGN_CENTER); // выровнять по центру ObjectSetString(chart_id,object_name,OBJPROP_TOOLTIP,tooltip); // без всплывающей подсказки, если "\n" } }
Обратите внимание на последний параметр функции CreateButton(): он отвечает за всплывающую подсказку при наведении курсора мыши на графический объект. Для примера, в функции AddButtonsPanel() в качестве этого параметра передаются значения из массива button_texts (текст, отображаемый на кнопках). Но при желании можно создать отдельный массив с более подробными пояснениями.
Теперь, если загрузить индикатор на график, можно увидеть такой результат:
Рис. 1. - Добавление кнопок в подокно индикатора
Пока это просто объекты, которые упорядочены в подокне индикатора и не реагируют на действия пользователя. Теперь займемся их "оживлением".
Сначала сделаем так, чтобы кнопки подгонялись под размер подокна при изменении его размера. Для этой цели напишем еще две функции UpdateButtonCoordinates() и ResizeButtons(). Эти функции будут устанавливать координаты и размеры для кнопок:
//+------------------------------------------------------------------+ //| Обновляет координаты кнопок | //+------------------------------------------------------------------+ void UpdateButtonCoordinates() { //--- Установим координаты 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]); } } } //+------------------------------------------------------------------+ //| Обновляет размеры кнопок | //+------------------------------------------------------------------+ void ResizeButtons() { //--- Установим размеры 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, // идентификатор события const long &lparam, // параметр события типа long const double &dparam, // параметр события типа double const string &sparam) // параметр события типа string { //--- Отслеживание события изменения свойств и размеров графика if(id==CHARTEVENT_CHART_CHANGE) { //--- Установим свойства подокна SetSubwindowProperties(); //--- Установим координаты для кнопок SetButtonCoordinates(); //--- Установим размеры кнопок SetButtonSizes(); //--- Установим новые координаты кнопкам UpdateButtonCoordinates(); //--- Установим новые размеры кнопкам ResizeButtons(); //--- Обновим график ChartRedraw(); return; } }
Если теперь загрузить индикатор на график (если он уже на графике, то просто скомпилировать код снова), то при изменении размеров окна графика или подокна индикатора, кнопки будут автоматически менять свой размер и положение на экране.
Далее сделаем так, чтобы при наведении курсора на кнопку ее цвет изменялся. Но перед написанием кода функций сначала разберемся, как обрабатывается событие с идентификатором CHARTEVENT_MOUSE_MOVE.
В функции OnInit() у нас уже содержится строка, которая говорит программе отслеживать перемещение курсора мыши, а также состояние левой кнопки мыши:
//--- Включим слежение за событиями мыши ChartSetInteger(0,CHART_EVENT_MOUSE_MOVE,true);
Без этой строки (или если в качестве последнего параметра передать значение false) события с идентификатором CHARTEVENT_MOUSE_MOVE не будут отслеживаться в функции OnChartEvent(). Это весьма удобно, так как не в каждой программе может понадобиться отслеживать такие события.
Чтобы понять, как работает отслеживание событий мыши, можно временно в код функции OnChartEvent() добавить вывод комментария на график:
//--- Отслеживание движения мыши и нажатия левой кнопки мыши if(id==CHARTEVENT_MOUSE_MOVE) { Comment("id: ",CHARTEVENT_MOUSE_MOVE,"\n", "lparam (x): ",lparam,"\n", "dparam (y): ",dparam,"\n", "sparam (статус кнопок мыши): ",sparam );
Если сейчас перемещать курсор мыши на графике, в левом верхнем углу можно увидеть текущие значения координат курсора. Нажимая левую кнопку мыши, можно увидеть изменение в строке комментария sparam (статус кнопок мыши): единица (1) означает, что кнопка мыши нажата, а ноль (0) - отжата.
Если нужно узнать, в каком подокне сейчас находится курсор мыши, можно воспользоваться функцией ChartXYToTimePrice(). В нее передаются координаты, а возвращает она (в переданные ей по ссылке переменные) номер окна/подокна, время и цену. Увидеть это можно, протестировав следующий код:
//--- Отслеживание движения мыши и нажатия левой кнопки мыши if(id==CHARTEVENT_MOUSE_MOVE) { int x =(int)lparam; // Координата по оси X int y =(int)dparam; // Координата по оси Y int window =WRONG_VALUE; // Номер окна, в котором находится курсор datetime time =NULL; // Время, соответствующее координате X double price =0.0; // Цена, соответствующая координате Y //--- Получим местоположение курсора if(ChartXYToTimePrice(0,x,y,window,time,price)) { Comment("id: ",CHARTEVENT_MOUSE_MOVE,"\n", "x: ",x,"\n", "y: ",y,"\n", "sparam (статус кнопок мыши): ",sparam,"\n", "окно: ",window,"\n", "время: ",time,"\n", "цена: ",DoubleToString(price,_Digits) ); } //--- return; }
Вычисления в подокне индикатора удобнее производить с относительными координатами. В данном случае это касается координаты по оси Y (ценовая шкала). Чтобы получить относительное значение достаточно от текущего значения координаты вычесть расстояние от верха графика до подокна индикатора. Сделать это можно вот так:
//--- Получим местоположение курсора if(ChartXYToTimePrice(0,x,y,window,time,price)) { //--- Получим расстояние от верха графика до подокна индикатора chart_y_offset=(int)ChartGetInteger(0,CHART_WINDOW_YDISTANCE,subwindow_number); //--- Преобразуем координату Y в относительную y-=chart_y_offset; Comment("id: ",CHARTEVENT_MOUSE_MOVE,"\n", "x: ",x,"\n", "y: ",y,"\n", "sparam (статус кнопок мыши): ",sparam,"\n", "окно: ",window,"\n", "время: ",time,"\n", "цена: ",DoubleToString(price,_Digits) ); }
Теперь значение в переменной y будет отрицательным, если курсор мыши находится выше подокна индикатора, и положительным, как только окажется в зоне подокна.
По умолчанию, в какой бы части графика не находился курсор, есть возможность прокручивать график относительно временной шкалы. Но есть так же возможность отключить прокрутку графика, когда это необходимо. Чаще всего это понадобится, когда курсор находится над панелью или пользовательскими элементами управления. Например, чтобы отключить прокрутку графика, когда курсор находится в подокне индикатора, и снова включить, когда курсор вышел из подокна, нужно написать вот такой код:
//--- Если курсор в зоне подокна, отключим скролл графика if(window==subwindow_number) ChartSetInteger(0,CHART_MOUSE_SCROLL,false); //--- Включим скролл графика, если вышли из зоны подокна индикатора else ChartSetInteger(0,CHART_MOUSE_SCROLL,true);
Далее напишем функцию ChangeButtonColorOnHover(), которая изменяет цвет кнопки, когда курсор находится над ней:
//+------------------------------------------------------------------+ //| Изменение цвета кнопки при наведении курсора мыши | //+------------------------------------------------------------------+ void ChangeButtonColorOnHover(int x,int y) { int x1,y1,x2,y2; //--- Инициализируем массив координат XY у кнопок SetButtonCoordinates(); //--- Определим, расположен ли курсор над одной из кнопок for(int i=0; i<BUTTON_COLUMNS; i++) { for(int j=0; j<BUTTON_ROWS; j++) { //--- Если эта кнопка нажата, перейдём к следующей if(button_states[j][i]) continue; //--- Получим границы кнопки 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(x>x1 && x<x2 && y>y1 && y<y2) ObjectSetInteger(0,button_object_names[j][i],OBJPROP_BGCOLOR,hover_background_color); //--- Иначе установим обычный цвет else ObjectSetInteger(0,button_object_names[j][i],OBJPROP_BGCOLOR,background_color); } } }
В итоге, в ветке идентификатора CHARTEVENT_MOUSE_MOVE получился вот такой код:
//--- Отслеживание движения мыши и нажатия левой кнопки мыши if(id==CHARTEVENT_MOUSE_MOVE) { int x =(int)lparam; // Координата по оси X int y =(int)dparam; // Координата по оси Y int window =WRONG_VALUE; // Номер окна, в котором находится курсор datetime time =NULL; // Время, соответствующее координате X double price =0.0; // Цена, соответствующий координате Y //--- Получим местоположение курсора if(ChartXYToTimePrice(0,x,y,window,time,price)) { //--- Получим расстояние от верха графика до подокна индикатора chart_y_offset=(int)ChartGetInteger(0,CHART_WINDOW_YDISTANCE,subwindow_number); //--- Преобразуем координату Y в относительную y-=chart_y_offset; //--- Если курсор в зоне подокна, отключим скролл графика if(window==subwindow_number) ChartSetInteger(0,CHART_MOUSE_SCROLL,false); //--- Включим скролл графика, если вышли из зоны подокна индикатора else ChartSetInteger(0,CHART_MOUSE_SCROLL,true); //--- Изменим цвет кнопки при наведении курсора ChangeButtonColorOnHover(x,y); } //--- Обновим график ChartRedraw(); return; }
Если сейчас провести курсор над кнопками, можно увидеть, как изменяется/восстанавливается их цвет.
На данный момент цвет нажатой кнопки только у Button 01. Если щелкать мышью по другим кнопкам, они не будут реагировать на нажатия и менять свой цвет. Чтобы это реализовать нужно задействовать событие с идентификатором CHARTEVENT_OBJECT_CLICK.
Напишем две функции: InitializeButtonStates() и ChangeButtonColorOnClick(). В функции InitializeButtonStates() будет производиться проверка, было ли нажатие на кнопке, учитывая префикс в имени. Если нажатие было на кнопке, то в цикле инициализируется массив состояний кнопок (button_states) и функция возвращает true.
//+------------------------------------------------------------------+ //| Инициализирует состояния кнопок при нажатии | //+------------------------------------------------------------------+ bool InitializeButtonStates(string clicked_object) { //--- Получим номер подокна индикатора subwindow_number=ChartWindowFind(0,subwindow_shortname); //--- Если кликнули на кнопке и она находится в подокне индикатора if(ObjectFind(0,clicked_object)==subwindow_number && StringFind(clicked_object,prefix+"button_",0)>=0) { //--- Определим нажатую кнопку for(int i=0; i<BUTTON_COLUMNS; i++) { for(int j=0; j<BUTTON_ROWS; j++) { //--- Определим состояние всех кнопок 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.
//+------------------------------------------------------------------+ //| Изменяет цвет кнопки при нажатии | //+------------------------------------------------------------------+ void ChangeButtonColorOnClick() { for(int i=0; i<BUTTON_COLUMNS; i++) { for(int j=0; j<BUTTON_ROWS; j++) { //--- Если эта кнопка нажата, установим ей цвет отличия if(button_states[j][i]) ObjectSetInteger(0,button_object_names[j][i],OBJPROP_BGCOLOR,clicked_background_color); //--- Для ненажатой кнопки установим обычный цвет else ObjectSetInteger(0,button_object_names[j][i],OBJPROP_BGCOLOR,background_color); } } }
Чтобы все заработало, не забудем добавить в функцию отслеживания событий OnChartEvent() обработку нажатий кнопок:
//--- Отслеживание нажатий на графическом объекте левой кнопкой мыши if(id==CHARTEVENT_OBJECT_CLICK) { //--- Если кликнули по кнопке if(InitializeButtonStates(sparam)) { //--- Установим цвета для кнопок ChangeButtonColorOnClick(); } //--- Обновим график ChartRedraw(); return; }
Теперь при нажатии на кнопку ее цвет будет изменяться.
Осталось учесть еще некоторые нюансы. В функции OnDeinit() при удалении индикатора с графика нужно включить обратно прокрутку графика для области подокна и отключить слежение за событиями мыши. Это может быть важным, если на графике одновременно работает несколько программ, в которых отслеживаются события.
//+------------------------------------------------------------------+ //| Деинициализация | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { if(reason==REASON_REMOVE || // Если индикатор удален с графика или reason==REASON_RECOMPILE) // программа была перекомпилирована { //--- Отключим таймер EventKillTimer(); //--- Удалим объекты DeleteButtons(); //--- Включим обратно прокрутку графика ChartSetInteger(0,CHART_MOUSE_SCROLL,true); //--- Отключим слежение за событиями мыши ChartSetInteger(0,CHART_EVENT_MOUSE_MOVE,false); //--- Обновим график ChartRedraw(); } }
Функции для удаления графических объектов программы:
//+------------------------------------------------------------------+ //| Удаляет все кнопки | //+------------------------------------------------------------------+ void DeleteButtons() { for(int i=0; i<BUTTON_COLUMNS; i++) for(int j=0; j<BUTTON_ROWS; j++) DeleteObjectByName(button_object_names[j][i]); } //+------------------------------------------------------------------+ //| Удаляет объект по имени | //+------------------------------------------------------------------+ void DeleteObjectByName(string object_name) { //--- Если есть такой объект if(ObjectFind(0,object_name)>=0) { //--- Если была ошибка при удалении, сообщим об этом if(!ObjectDelete(0,object_name)) Print("Ошибка ("+IntegerToString(GetLastError())+") при удалении объекта!"); } }
И, наконец, пояснение, зачем же нам понадобилось включать таймер в этой программе. Например, если на графике работает более одной программы и в каждой нужно отслеживать события мыши, то при удалении одной из них с графика, отслеживание отключается в функции OnDeinit() для всех программ. Поэтому можно, как вариант, каждую секунду делать проверку, включено ли отслеживание событий мыши:
//+------------------------------------------------------------------+ //| Timer function | //+------------------------------------------------------------------+ void OnTimer() { //--- Проверим, включено ли отслеживание событий мыши CheckChartEventMouseMove(); }
Код функции CheckChartEventMouseMove():
//+------------------------------------------------------------------+ //| Проверяет, включено ли отслеживание событий мыши | //+------------------------------------------------------------------+ void CheckChartEventMouseMove() { //--- Включим слежение за передвижением курсора, если режим отключен if(!ChartGetInteger(0,CHART_EVENT_MOUSE_MOVE)) ChartSetInteger(0,CHART_EVENT_MOUSE_MOVE,true); }
Иногда может быть вполне достаточно установить эту проверку по событию с идентификатором CHARTEVENT_CHART_CHANGE.
Ниже можно посмотреть видеролик с демонстрацией того, что получилось:
Заключение
На этом закончим. Индикатор TestButtons.mq5 можно скачать в приложении к статье. Из этого примера может получиться довольно интересное главное меню, если идею развить дальше. Например, пользователь, нажимая ту или иную кнопку, может переходить к интересующей его информации. При необходимости количество кнопок можно увеличить.
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования
Разумеется, потому что этой информации нигде нет (оказывается есть, см. выше:))). Сам мучился с этой проблемой, пока не обратился в сервис деск. Там-то мне и подсказали, что изменения свойств объектов на чарте происходит асинхронно. А судя по Вашему примеру это касается свойств и самого чарта.
Еще в разделе Графические объекты :
Группа функций, предназначенных для работы с графическими объектами, относящимися к любому указанному графику.
Функции, задающие свойства графических объектов, а также операции создания ObjectCreate() и перемещения ObjectMove() объектов на графике фактически служат для отправки команд графику. При успешном выполнении этих функций команда попадает в общую очередь событий графика. Визуальное изменение свойств графических объектов производится в процессе обработки очереди событий данного графика.
По этой причине не следует ожидать немедленного визуального обновления графических объектов после вызова данных функций. В общем случае обновление графических объектов на чарте производится терминалом автоматически по событиям изменения - поступление новой котировки, изменения размера окна графика и т.д. Для принудительного обновления графических объектов используйте команду на перерисовку графика ChartRedraw().
Еще в разделе Графические объекты :
С объектами понятно и привычно, а вот от свойств графика я ожидал другого.
Мне мое предложение больше не повторять? Или в сервис-деск оформить?
Как решить вот такую проблему: есть событие CHARTEVENT_MOUSE_MOVE с нажатой левой кнопкой мыши, событие получаю, необходимые действия выполняются - перемещаю объект, но при этом двигается график.
Вот собственно сам вопрос: как сделать так чтоб график не затрагивался?
Доброго времени суток. Вижу что последнее сообщение давно было но все же спрошу.
Как решить вот такую проблему: есть событие CHARTEVENT_MOUSE_MOVE с нажатой левой кнопкой мыши, событие получаю, необходимые действия выполняются - перемещаю объект, но при этом двигается график.
Вот собственно сам вопрос: как сделать так чтоб график не затрагивался?
CHART_MOUSE_SCROLL