Разработка и реализация новых виджетов на основе класса CChartObject
Введение
После написания статьи про полуавтоматический советник с графическим интерфейсом пользователя (GUI) у меня возникла необходимость расширения интерфейса новым функционалом для более сложных индикаторов и экспертов. Ознакомившись с классами Стандартной библиотеки, я сделал новые виджеты.
В этой статье я опишу процесс использования классов Стандартной библиотеки для объектов пользовательского интерфейса, мы рассмотрим применение новых классов CChartObjectProgressBar, CChartObjectSpinner и CChartEditTable, созданных на основе класса CChartObjectEdit. В классе CChartEditTable используется динамический двумерный массив объектов, это работающий пример реализации работы с двумерными динамическими массивами в MQL5.
1. Класс CChartObject и его потомки
Если мы не используем классы Стандартной библиотеки, то для создания и управления объектами на графике мы должны использовать функции для работы с графическими объектами.
Объекты создаются при помощи функции ObjectCreate(), тип создаваемого объекта и его свойства задаются в параметрах функции. Все объекты на графике имеют свойства, которые могут быть типа Integer, Double или String. Установка и получение свойств объектов производится при помощи специальных функций ObjectGetInteger(), ObjectSetInteger(), ObjectGetDouble(), ObjectSetDouble(), ObjectGetString() и ObjectSetString(). Также есть функции подсчета, перемещения и удаления объектов на любом графике.
В MQL5 при использовании объектно-ориентированного подхода работа с различными объектами на графике производится при помощи класса CChartObject и его потомков.
Класс CChartObject является базовым классом для всех графических объектов, которые могут быть размещены на графике. Диаграмма наследовния класса CChartObject приведена ниже:
Рисунок 1. Класс CChartObject и его потомки
Как мы видим, на рисунке есть несколько классов, отмеченных маленьким треугольником в правом нижнем углу.
Возможности основных классов расширяются в классах-потомках путем добавления новых переменных и методов обработки объекта. В классах-потомках методы создания и возврата типа объекта Create() и Type() могут различаться.
Рассмотрим это на примере: класс CChartObjectTrend является родительским классом для классов CChartObjectTrendByAngle, CChartObjectChannel, CChartObjectStdDevChannel, CChartObjectRegression и CChartObjectPitchfork.
Класс CChartObjectTrend является базовым классом для объектов со свойствами OBJPROP_RAY_RIGHT и OBJPROP_RAY_LEFT, выглядит он следующим образом:
class CChartObjectTrend : public CChartObject { public: //--- методы доступа к свойствам объекта bool RayLeft() const; bool RayLeft(bool new_sel); bool RayRight() const; bool RayRight(bool new_sel); //--- методы создания объекта bool Create(long chart_id,string name,int window, datetime time1,double price1,datetime time2,double price2); //--- методы идентификации объекта virtual int Type() const { return(OBJ_TREND); } //--- методы работы с файлами virtual bool Save(int file_handle); virtual bool Load(int file_handle); };
Перед определением методов есть комментарии, позволяющие различать предназначение методов.
Функции RayLeft() и RayRight() являются методами доступа к свойствам объекта. Их реализация заключается в вызове методов ObjectGetInteger() и ObjectSetInteger() объекта CChartObjectTrend.
bool CChartObjectTrend::RayLeft(bool new_ray) { //--- проверка if(m_chart_id==-1) return(false); //--- return(ObjectSetInteger(m_chart_id,m_name,OBJPROP_RAY_LEFT,new_ray)); }
Метод Create() отвечает за создание и присоединение объекта к графику.
Он вызывает метод ObjectCreate(), указывая тип объекта OBJ_TREND в качестве одного из параметров:
bool CChartObjectTrend::Create(long chart_id,string name,int window, datetime time1,double price1,datetime time2,double price2) { bool result=ObjectCreate(chart_id,name,OBJ_TREND,window,time1,price1,time2,price2); if(result) result&=Attach(chart_id,name,window,2); //--- return(result); }
Методы Save() и Load() сохраняют и загружают данные объекта на диск при помощи функций FileWriteInteger() и FileLoadInteger().
bool CChartObjectTrend::Save(int file_handle) { bool result; //--- проверка if(file_handle<=0) return(false); if(m_chart_id==-1) return(false); //--- запись result=CChartObject::Save(file_handle); if(result) { //--- пишем в файл значение свойства "Ray left" if(FileWriteInteger(file_handle,(int) ObjectGetInteger(m_chart_id,m_name, OBJPROP_RAY_LEFT),CHAR_VALUE)!=sizeof(char)) return(false); //--- пишем в файл значение свойства "Ray right" if(FileWriteInteger(file_handle,(int) ObjectGetInteger(m_chart_id,m_name, OBJPROP_RAY_RIGHT),CHAR_VALUE)!=sizeof(char)) return(false); } //--- return(result); } bool CChartObjectTrend::Load(int file_handle) { bool result; //--- проверка if(file_handle<=0) return(false); if(m_chart_id==-1) return(false); //--- чтение result=CChartObject::Load(file_handle); if(result) { //--- читаем значение свойства "Ray left" if(!ObjectSetInteger(m_chart_id,m_name,OBJPROP_RAY_LEFT, FileReadInteger(file_handle,CHAR_VALUE)))return(false); //--- читаем значение свойства "Ray right" if(!ObjectSetInteger(m_chart_id,m_name,OBJPROP_RAY_RIGHT, FileReadInteger(file_handle,CHAR_VALUE))) return(false); } //--- return(result); }
Рассмотрим кратко определения потомков класса CChartObjectTrend.
В класс CChartObjectTrendByAngle добавлено свойство Angle(), а в качестве типа в функции Type() возвращается OBJ_TRENDBYANGLE:
class CChartObjectTrendByAngle : public CChartObjectTrend { public: //--- методы доступа к свойствам объекта double Angle() const; bool Angle(double angle); //--- методы создания объекта bool Create(long chart_id,string name,int window,datetime time1,double price1, datetime time2,double price2); //--- методы идентификации объекта virtual int Type() { return(OBJ_TRENDBYANGLE); } };
Класс CChartObjectChannel при идентификации возвращает тип OBJ_CHANNEL, он работает с каналами, поэтому при создании в метод Create() ему передаются три параметра цена/время:
class CChartObjectChannel : public CChartObjectTrend { public: //--- метод создания объекта bool Create(long chart_id,string name,int window,datetime time1,double price1, datetime time2,double price2,datetime time3,double price3); //--- метод идентификации объекта virtual int Type() const { return(OBJ_CHANNEL); } };
В класс CChartObjectStdDevChannel добавлен дополнительный параметр параметр deviation, указываемый при создании в методе Create() и модификаторы этого параметра Deviations():
class CChartObjectStdDevChannel : public CChartObjectTrend { public: //--- методы доступа к свойствам объекта double Deviations() const; bool Deviations(double deviation); //--- методы создания объекта bool Create(long chart_id,string name,int window, datetime time1,datetime time2,double deviation); //--- методы идентификации объекта virtual int Type() const { return(OBJ_STDDEVCHANNEL); } //--- методы работы с файлами virtual bool Save(int file_handle); virtual bool Load(int file_handle); };
Класс CChartObjectRegression создает трендовую линию регресии, у него переопределены лишь методы Create() и Type() родительского класса CChartObjectTrend:
class CChartObjectRegression : public CChartObjectTrend { public: //--- метод создания объекта bool Create(long chart_id,string name,int window,datetime time1,datetime time2); //--- метод идентификации объекта virtual int Type() const { return(OBJ_REGRESSION); } };
Класс CChartObjectPitchfork отвечает за работу с объектом типа "вилы Эндрюса", здесь изменились лишь методы Create() и Type().
class CChartObjectPitchfork : public CChartObjectTrend { public: //--- метод создания объекта bool Create(long chart_id,string name,int window,datetime time1,double price1, datetime time2,double price2,datetime time3,double price3); //--- метод идентификации объекта virtual int Type() const { return(OBJ_CHANNEL); } };
В результате мы увидели основные правила создания новых классов графических объектов на основе некоторых других классов:
- меняется метод создания объекта Create();
- меняется метод идентификации объекта Type();
- добавляются новые методы доступа к свойствам.
Выполнение всех этих правил необязательно, может быть добавлена лишь новая переменная/переменные класса и методы доступа к ним.
Перед тем, как пойти дальше, давайте рассмотрим использование методов класса CChartObject в графических объектах.
Вместо того, чтобы использовать семейство методов ObjectSet/ObjectGet и свойств объекта, достаточно объявить объект типа CChartObject или его потомка и вызывать методы, которые изменяют соответствующие свойства. Для упрощения приведем пример обыкновенной надписи (Label):
Код:
void OnStart() { //--- string label_name="my_OBJ_LABEL_object"; if(ObjectFind(0,label_name)<0) { Print("Объект ",label_name," не найден. Код ошибки = ",GetLastError()); ObjectCreate(0,label_name,OBJ_LABEL,0,0,0); ObjectSetInteger(0,label_name,OBJPROP_XDISTANCE,200); ObjectSetInteger(0,label_name,OBJPROP_YDISTANCE,300); ObjectSetInteger(0,label_name,OBJPROP_COLOR,White); ObjectSetString(0,label_name,OBJPROP_TEXT,UP); ObjectSetString(0,label_name,OBJPROP_FONT,"Wingdings"); ObjectSetInteger(0,label_name,OBJPROP_FONTSIZE,10); ObjectSetDouble(0,label_name,OBJPROP_ANGLE,-45); ObjectSetInteger(0,label_name,OBJPROP_SELECTABLE,false); ChartRedraw(0); } }
можно реализовать при помощи объектно-ориентированного подхода:
1. Объявляем объект типа CChartObjectLabel:
CChartObjectLabel label;
2. Производим над ним нужные действия:
int OnInit() { //--- label.Create(0, label_name, 0, 0); label.X_Distance(200); label.Y_Distance(300); label.Color(White); label.Description(UP); label.Font("Wingdings"); label.FontSize(10); label.Angle(-45); label.Selectable(false); //--- return(0); }
Как видите, главным отличием является то, что мы теперь не используем строку label_name:
string label_name="my_OBJ_LABEL_object";
и не вызываем функции ObjectSetInteger(), ObjectGetInteger(), ObjectSetDouble() и ObjectGetDouble() с этой строкой в качестве параметра. Вместо этого мы объявляем объект класса CChartObjectLabel и используем его методы. Это не только упрощает сам код, но и значительно ускоряет его написание.
Редактор MetaEditor 5 предоставляет функционал выбора свойств/методов объекта при добавления точки (.) к экземпляру класса. Теперь нет необходимости постоянно листать справку по MQL5 в поисках свойств при установке и получении необходимых свойств объекта.
Аналогично, для получения или установки свойств "луч влево" и "луч вправо" класса CChartObjectTrend, рассмотренного выше, достаточно объявить объект класса CChartObjectTrend и вызвать методы RayRight() или RayLeft():
CChartObjectTrend trendline; trendline.RayRight(true);
2. Класс ProgressBar
Первым виджетом, который сделаем, будет ProgressBar. Полоса ProgressBar показывает состояние выполнения некоторой операции, от 0 до x процентов.
Для общности, давайте не будем ограничивать его максимальным значением 100, пусть это будет любое положительное целое значение. Нам понадобится цветная полоса, которая будет изменяться в размерах в соответствии со значением состояния. Первая мысль, которая приходит в голову - использование двух прямоугольников, но я выбрал другой путь: использование двух объектов типа CChartObjectEdit различного цвета, расположенных один внутри другого.
Это упрощает написание кода и добавление текстовой строки, которая может быть помещена в полосу для показа значения. Индикаторы ProgressBar могут быть горизонтальными либо вертикальными в зависимости от того, что требуется показать.
2.1. Реализация класса ProgressBar
Класс CChartObjectProgress унаследован от класса CChartObjectEdit.
Я добавил в него private-переменные для хранения значений и их ограничений: m_value, m_min, m_max.
Ориентация полосы ProgressBar определяется значением переменной m_direction типа integer. Цвет хранится в переменной m_color. Метод идентификации возвращает тип OBJ_EDIT, поскольку для нашем случае мы его не используем.
Отметим, что переменная m_bar типа CChartObjectEdit объявлена внутри определения класса - это внутренняя полоса, которая изменяет свой размер в зависимости от значения переменной m_value. Дополнительные переменные m_name и m_chart хранят внутренние значения переменной m_bar.
class CChartObjectProgressBar : public CChartObjectEdit { private: int m_value; int m_min; int m_max; int m_direction; color m_color; CChartObjectEdit m_bar; string m_name; long m_chart_id; public: int GetValue(); int GetMin(); int GetMax(); void SetValue(int val); void SetMin(int val); void SetMax(int val); void SetColor(color bgcol,color fgcol); bool Create(long chart_id,string name,int window,int X,int Y, int sizeX,int sizeY,int direction); //--- метод идентификации объекта virtual int Type() const { return(OBJ_EDIT); } };
Метод Create() создает объект ProgressBar и присоединяет его к графику.
Как видно, в случае вертикальной ориентации переменная Y вычитается из переменной sizeY, т.к. обычно объект класса CChartObjectEdit рисуется сверху вниз, а я хочу рисовать внутренний прямоугольник снизу вверх:
bool CChartObjectProgressBar::Create(long chart_id,string name,int window,int X,int Y, int sizeX,int sizeY,int direction=0) { bool result=ObjectCreate(chart_id,name,(ENUM_OBJECT)Type(),window,0,0,0); m_name=name; m_chart_id=chart_id; m_direction=direction; if(direction!=0) { Y=Y-sizeY; } ObjectSetInteger(chart_id,name,OBJPROP_BGCOLOR,White); ObjectSetInteger(chart_id,name,OBJPROP_COLOR,White); ObjectSetInteger(chart_id,name,OBJPROP_SELECTABLE,false); ObjectSetInteger(chart_id,name,OBJPROP_READONLY,true); result&=m_bar.Create(chart_id,name+"m_bar",window,X,Y,sizeX,sizeY); m_bar.Color(White); m_bar.ReadOnly(true); m_bar.Selectable(false); //--- if(result) result&=Attach(chart_id,name,window,1); result&=X_Distance(X); result&=Y_Distance(Y); result&=X_Size(sizeX); result&=Y_Size(sizeY); //--- return(result); }
Метод SetColor() устанавливает цвет обоих прямоугольников и их фона:
void CChartObjectProgressBar::SetColor(color bgCol,color fgCol=White) { m_color=bgCol; m_bar.BackColor(m_color); m_bar.Color(fgCol); }
Метод SetValue() отвечает за установку значения переменной m_val и пересчет размера внутреннего прямоугольника.
Для полос с горизонтальной и вертикальной ориентацией расчет размера производится по-разному:
void CChartObjectProgressBar::SetValue(int val) { if(m_direction==0) // горизонтальный ProgressBar { double sizex=(double)ObjectGetInteger(m_chart_id,m_name,OBJPROP_XSIZE,0); double stepSize=sizex/(m_max-m_min); m_value=val; m_bar.Create(m_bar.ChartId(),m_bar.Name(),m_bar.Window(), m_bar.X_Distance(),m_bar.Y_Distance(),(int)MathFloor(stepSize*m_value),m_bar.Y_Size()); } else { double sizey=(double)ObjectGetInteger(m_chart_id,m_name,OBJPROP_YSIZE,0); double stepSize=sizey/(m_max-m_min); m_value=val; m_bar.Create(m_bar.ChartId(),m_bar.Name(),m_bar.Window(), m_bar.X_Distance(),(int)(this.Y_Distance()+sizey-MathFloor(stepSize*m_value)), m_bar.X_Size(),(int)MathFloor(stepSize*m_value)); } m_bar.Description(IntegerToString(m_value)); }
2.2. Демонстрация использования класса ProgressBar
Поскольку мы реализовали класс CChartObjectProgressBar, пришло время увидеть его в действии.
Для помещения нового Progress Bar на график достаточно объявить объект класса CChartObjectProgressBar, создать его методом Create() и вызвать соответствующие методы установки свойств:
progressBar.Create(0, "progressBar1", 0, 10, 10, 200, 40); progressBar.SetColor(YellowGreen); progressBar.SetMin(0); progressBar.SetMax(100); progressBar.SetValue(0);
Я написал демонстрационный советник, который размещает на графике шесть различных объектов ProgressBar и меняет их свойства при клике мышкой на любой из этих объектов графика:
Полный исходный код этого и других демонстрационных примеров находятся в приложении, ролик приведен ниже:
3. Класс Spinner
Виджет Spinner представляет собой поле ввода с двумя кнопками.
Они используются для увеличения или уменьшения значения поля ввода при нажатии на одну из кнопок.
При разработке данного объекта я хотел ограничиваться лишь целочисленными значениями, поэтому объект Spinner был разработан на базе значений типа double. У него также есть возможность задавать размер шага, т.е. число, на которое будет увеличиваться или уменьшаться текущее значение. Он также должен иметь максимальные и минимальные значения в качестве ограничителей.
3.1. Реализация класса Spinner
В MQL5 есть классы CChartObjectEdit и CChartObjectButton, которые могут быть объединены в один класс CChartObjectSpinner. Этот класс наследован от класса CChartObjectEdit и содержит два закрытых (private) члена - объекты класса CChartObjectButton.
Ограничения для минимального и максимального значения переменной m_value хранятся в членах класса m_min и m_max, а точность (до n-го десятичного знака) хранится в переменной m_precision. Нам потребуются методы для доступа к текущему значению, установки шага изменения значения и установки значения переменной m_value.
class CChartObjectSpinner: public CChartObjectEdit { private: double m_value; double m_stepSize; double m_min; double m_max; int m_precision; string m_name; long m_chart_id; CChartObjectButton m_up,m_down; public: double GetValue(); double GetMin(); double GetMax(); void SetValue(double val); void SetMin(double val); void SetMax(double val); double Inc(); double Dec(); bool Create(long chart_id,string name,int window,int X,int Y, int sizeX,int sizeY,double val,double stepSize,int precision); //--- метод идентификации объекта virtual int Type() const { return(OBJ_EDIT); } };
Метод Create() создает новый объект CChartObjectSpinner и присоединяет его к графику.
Два объекта класса CChartObjectButtons создаются на графике справа от объекта класса CChartObjectEdit, высота каждого из них составляет половину высоты объекта CChartObjectEdit.
Кнопки инкремента и декремента обозначены знаками "+" и "-".
bool CChartObjectSpinner::Create(long chart_id,string name,int window,int X,int Y, int sizeX,int sizeY,double val=0.0,double stepSize=1.0,int precision=8) { bool result=ObjectCreate(chart_id,name,(ENUM_OBJECT)Type(),window,0,0,0); m_name=name; m_chart_id=chart_id; m_value=val; m_stepSize=stepSize; m_precision=precision; ObjectSetInteger(chart_id,name,OBJPROP_BGCOLOR,White); ObjectSetInteger(chart_id,name,OBJPROP_COLOR,Black); ObjectSetInteger(chart_id,name,OBJPROP_SELECTABLE,false); ObjectSetInteger(chart_id,name,OBJPROP_READONLY,true); result&=m_up.Create(chart_id, name+"_up", window, X+sizeX, Y, 15, sizeY/2); result&=m_down.Create(chart_id, name+"_down", window, X+sizeX, Y+sizeY/2, 15, sizeY/2); m_up.Description("+"); m_down.Description("-"); ObjectSetString(chart_id,name,OBJPROP_TEXT,0,(DoubleToString(m_value,precision))); //--- if(result) result&=Attach(chart_id,name,window,1); result&=X_Distance(X); result&=Y_Distance(Y); result&=X_Size(sizeX); result&=Y_Size(sizeY); //--- return(result); }
Метод SetValue() устанавливает значение закрытой переменной m_value только в том случае, если оно находится в диапазоне от m_min до m_max.
void CChartObjectSpinner::SetValue(double val) { if(val>=m_min && val<=m_max) m_value=val; this.Description(DoubleToString(m_value)); }
Метод Inc() увеличивает значение на заданный шаг, но не более чем до значения m_max.
Отметим, что при сравнении значений до указанной точности я использовал функцию NormalizeDouble().
double CChartObjectSpinner::Inc(void) { if(NormalizeDouble(m_max-m_value-m_stepSize,m_precision)>0.0) m_value+=m_stepSize; else m_value=m_max; this.Description(DoubleToString(m_value, m_precision)); m_up.State(false); return m_value; }
Метод Dec() уменьшает значение на заданную величину шага, но не менее чем до значения, указанного в переменной m_min.
double CChartObjectSpinner::Dec(void) { if(NormalizeDouble(m_value-m_stepSize-m_min,m_precision)>0.0) m_value-=m_stepSize; else m_value=m_min; this.Description(DoubleToString(m_value,m_precision)); m_down.State(false); return m_value; }
3.2. Демонстрация использования класса Spinner
Пришло время протестировать объекты класса Spinner.
Для их использования достаточно объявить объект класса CChartObjectSpinne и использовать методы Create(), SetMin() и SetMax().
spinner.Create(0, "spinner1", 0, 10, 10, 200, 40, 0.0, 0.4); spinner.SetMin(0); spinner.SetMax(100);
Я подготовил демонстрационный пример, в нем используется три виджета Spinner, их значения увеличиваются при нажатии кнопок, суммарное значение выводится на график.
Это производится внутри функции OnChartEvent():
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { //--- Проверяем событие - нажатие на кнопку мыши if(id==CHARTEVENT_OBJECT_CLICK) { if (sparam=="spinner1_up") spinner.Inc(); if (sparam=="spinner1_down") spinner.Dec(); if (sparam=="spinner2_up") spinner2.Inc(); if (sparam=="spinner2_down") spinner2.Dec(); if (sparam=="spinner3_up") spinner3.Inc(); if (sparam=="spinner3_down") spinner3.Dec(); label.Description(DoubleToString(NormalizeDouble(spinner.GetValue()+spinner2.GetValue()+spinner3.GetValue(),10),10)); ChartRedraw(); } }
Посмотрите ролик:
4. Класс CChartObjectEditTable
В советниках, использующих множество таймфреймов, для каждого таймфрейма значения индикаторов выводятся отдельно.
Иногда значения индикаторов на различных таймфреймах (например, направление тренда) показываются в форме двумерной таблицы, состоящей из прямоугольников или квадратов различного цвета. Я разработал универсальную двумерную таблицу для таких объектов в виде класса CChartObjectEditTable. Этот класс может хранить произвольное количество строк и столбцов, поскольку они реализованы в виде двумерного динамического массива объектов.
При разработке я решил отдельно реализовать установку цвета каждой ячейки, а также добавил возможность размещения различных текстовых строк в любой ячейке. Все ячейки одинакового размера, но реализована возможность указания их высоты, ширины и расстояния между ними.
Класс CChartObjectEditTable содержит указатель CArrayObj на двумерный массив объектов, переменные класса m_rows и m_columns хранят число строк и столбцов таблицы.
В качестве префикса всех ячеек таблицы (объектов типа CChartObjectEdit) используется строковая переменная m_baseName. Методы GetColor(), SetColor(), GetText() и SetText() предназначены для установки и получения цвета и текста любой указанной ячейки. Метод Delete() удаляет все объекты, созданные при помощи метода Create().
class CChartObjectEditTable { private: CArrayObj *array2D; int m_rows; int m_cols; string m_baseName; public: bool Create(long chart_id,string name,int window,int rows,int cols,int startX,int startY, int sizeX,int sizeY,color Bg,int deltaX,int deltaY); bool Delete(); bool SetColor(int row,int col,color newColor); color GetColor(int row,int col); bool SetText(int row,int col,string newText); string GetText(int row,int col); };
В методе Create() создается двумерная динамическая таблица объектов класса CChartObjectEdit.
Обратите внимание на способ создания двумерных массивов в MQL5: сначала мы объявляем указатель на двумерный массив, затем заполняем массив объектами класса CArrayObj(), тем самым мы создаем массивы в массиве. Все массивы можно представить в виде столбцов таблицы.
Каждый столбец содержит строки с объектами класса CChartObjectEdit, каждый из них является отображаемой ячейкой таблицы.
bool CChartObjectEditTable::Create(long chart_id,string name,int window,int rows=1,int cols=1, int startX=0,int startY=0,int sizeX=15,int sizeY=15, color Bg=White,int deltaX=5,int deltaY=5) { m_rows=rows; m_cols=cols; m_baseName=name; int i=0,j=0; array2D=new CArrayObj(); if (array2D==NULL) return false; for(j=0; j<m_cols; j++) { CArrayObj *new_array=new CArrayObj(); if (array2D==NULL) return false; array2D.Add(new_array); for(i=0; i<m_rows; i++) { CChartObjectEdit *new_edit=new CChartObjectEdit(); new_edit.Create(chart_id, name+IntegerToString(i)+":"+IntegerToString(j), window, startX+j*(sizeX+deltaX), startY+i*(sizeY+deltaY), sizeX, sizeY); new_edit.BackColor(Bg); new_edit.Color(White); new_edit.Selectable(false); new_edit.ReadOnly(true); new_edit.Description(""); new_array.Add(new_edit); } } return true; }
Метод SetColor() устанавливает цвет любой ячейки. Сначала он находит массив столбцов, а затем n-й элемент этого массива.
Затем значение цвета данного элемента изменяется путем вызова метода BackColor():
bool CChartObjectEditTable::SetColor(int row,int col,color newColor) { CArrayObj *sub_array; CChartObjectEdit *element; if((row>=0 && row<m_rows) && (col>=0 && col<m_cols)) { if(array2D!=NULL) { sub_array=array2D.At(col); element=(CChartObjectEdit*)sub_array.At(row); element.BackColor(newColor); return true; } } return false; }
Алгоритм метода GetColor() аналогичный методу нахождения ячейки, реализованного в SetColor(), отличие лишь в том, что он возвращает значение цвета любой заданной ячейки.
color CChartObjectEditTable::GetColor(int row,int col) { CArrayObj *sub_array; CChartObjectEdit *element; if((row>=0 && row<m_rows) && (col>=0 && col<m_cols)) { if(array2D!=NULL) { sub_array=array2D.At(col); element=(CChartObjectEdit*)sub_array.At(row); return element.BackColor(); } } return NULL; }
Метод SetText() находит элемент и устанавливает значение текстовой надписи вызовом метода Description().
bool CChartObjectEditTable::SetText(int row,int col,string newText) { CArrayObj *sub_array; CChartObjectEdit *element; if((row>=0 && row<m_rows) && (col>=0 && col<m_cols)) { if(array2D!=NULL) { sub_array=array2D.At(col); element=(CChartObjectEdit*)sub_array.At(row); element.Description(newText); return true; } } return false; }
Метод Delete() удаляет все объекты, созданные методом Create().
Сначала он очищает все массивы столбцов а затем удаляет из памяти объект array2D.
bool CChartObjectEditTable::Delete(void) { for(int j=0; j<m_cols; j++) { CArrayObj *column_array=array2D.At(j); column_array.Clear(); delete column_array; } delete array2D; return true; }
4.2. Демонстрация использования класса CChartObjectEditTable
Для использования виджета CChartObjectEditTable необходимо объявить объект CChartEditTable и использовать метод Create() с параметрами, определяющими количество строк и столбцов таблицы.
Затем при помощи модификаторов свойств мы можем изменять цвет и текст любой ячейки.
table.Create(0,"t",0,1,10,10,10,15,15,Yellow); table.SetColor(2,2,Red); table.SetText(2,2,"2");
Пожалуйста посмотрите скрипт, который иллюстрирует возможности использования объекта CChartObjectEditTable.
Исходный код скрипта находится в приложении к статье.
Выводы
В данной статье я описал процесс создания новых виджетов на базе класса CChartObject.
Использовать разработанные виджеты очень просто, достаточно добавить всего несколько строк кода.
Для этого необходимо включить файл ChartObjectsExtControls.mqh в код советника или индикатора.
Перевод с английского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/en/articles/196
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования
Отличная демонстрация!!!
И очень актуальна для текущего конкурса! Обязательно участвуйте!