
Разработка интерактивного графического пользовательского интерфейса на MQL5 (Часть 1): Создание панели
Введение
Добро пожаловать в первую часть нашего подробного руководства по построению пользовательских панелей графического интерфейса (GUI) на языке MetaQuotes Language 5 (MQL5)! Как трейдеры и разработчики, мы осознаем важность эффективных и удобных интерфейсов для наших средств торговли. В этой серии статей мы погрузися в мир MQL5 и узнаем, как создавать мощные панели графического интерфейса, которые улучшат вашу торговлю.
В первой части мы рассмотрим основы: настройку проекта, разработку макета панели и добавление важнейших элементов управления. В следующей части мы оживим панель, сделав ее интерактивной и адаптивной. Независимо от того, опытный вы программист на MQL5 или только начинаете, в этой статье вы найдете пошаговые инструкции, которые помогут вам создать функциональную и визуально привлекательную панель графического интерфейса. Этого мы добьемся, рассмотрев следующие темы:
- Иллюстрация элементов
- Сборка панели графического интерфейса на языке MQL5
- Заключение
В нашей работе мы будем активно использовать язык MetaQuotes Language 5 (MQL5) в качестве базовой среды разработки и запускать файлы в торговом терминале MetaTrader 5 (MT5). Таким образом, наличие указанных выше версий будет иметь первостепенное значение. Начнем.
Иллюстрация элементов
Создадим панель графического интерфейса, где будут наиболее распространенные служебные инструменты, которые могут понадобиться трейдеру, поэтому постараемся описать и охватить всё в этой серии статей. В связи с этим количество необходимых элементов велико, но мы ограничим их группами для удобства понимания. Для разработки графического интерфейса будем использовать 4 элемента, и именно с их помощью создадим его. В панель войдут создание торговых кнопок, четких прямоугольников, обновления в реальном времени, использование эмодзи, шрифтов разного стиля, меток, подвижных элементов панели и эффектов наведения. Для иллюстрации всего этого — пример ниже.
Сборка панели графического интерфейса на языке MQL5
Для создания панели возьмем за основу советник. Для создания советника (EA) в терминале MetaTrader 5 выберите "Сервис" > "Редактор MetaQuotes Language" или просто нажмите F4. А можно просто щелкнуть мышкой по иконке IDE на панели инструментов. Откроется среда разработки на MQL5, которая позволяет писать торговых роботов, технические индикаторы, скрипты и библиотеки функций.
После открытия редактора MetaEditor на панели инструментов для создания нового документа перейдите на вкладку "Файл" и выберите "Новый файл" или просто нажмите CTRL + N. А можно просто щелкнуть мышкой по иконке "Новый" на панели инструментов. Это приведет к открытию всплывающего Мастера MQL.
В открывшемся Мастере выберите "Советник (шаблон)" и нажмите "Далее".
В диалоге "Общие параметры советника", под разделом "Имя" введите имя файла своего советника. Обратите внимание: чтобы указать или создать папку, если она не существует, нужно использовать перед именем эксперта обратную косую черту. Например, здесь у нас по умолчанию "Experts\". Это значит, что наш советник будет создан в папке "Experts", и мы сможем его там найти. Прочие разделы не должны вызывать затруднений, но вы можете пройти по ссылке внизу Мастера, чтобы узнать, как именно выполнить этот процесс.
Указав название файла советника, нажмите "Далее" > "Далее", а затем "Готово". После всего этого мы готовы воплотить панель графического интерфейса в коде.
Во-первых, нужно будет создать функции для 4 элементов, которые нам понадобятся, а именно: прямоугольной метки, кнопки, поля редактирования и текстовых меток. Это будет очень полезно, поскольку позволит повторно использовать ту же функцию при создании аналогичных элементов, а не повторять весь процесс их создания. Кроме того, это сэкономит нам массу времени и места, ускорив и упростив весь процесс и укоротив фрагменты кода.
Для получения прямоугольной метки создадим функцию, принимающую 10 аргументов или параметров.
//+------------------------------------------------------------------+ //| Function to create rectangle label | //+------------------------------------------------------------------+ bool createRecLabel(string objName, int xD, int yD, int xS, int yS, color clrBg, int widthBorder, color clrBorder = clrNONE, ENUM_BORDER_TYPE borderType = BORDER_FLAT, ENUM_LINE_STYLE borderStyle = STYLE_SOLID) { ... }
Все иллюстрирует сигнатура функции. Это логическая функция с именем "createRecLabel", которая вернет два логических флага, true или false, в случае успеха или неудачи соответственно. Для лучшего понимания ее параметров опишем каждый из них ниже.
- objName: этот параметр представляет собой уникальное имя объекта с прямоугольной меткой. Он служит идентификатором для создаваемого графического элемента.
- xD and yD: эти параметры определяют расстояния X и Y от угла, где будет размещена прямоугольная метка. Считайте их координатами, определяющими верхний левый угол прямоугольника относительно графика.
- xS and yS: эти параметры указывают ширину и высоту прямоугольника. Значение xS определяет ширину прямоугольника по горизонтали, а yS управляет его высотой по вертикали.
- clrBg: параметр clrBg представляет собой цвет фона прямоугольной метки. Выбирайте цвет, контрастирующий с фоном графика или дополняющий другие элементы.
- widthBorder: этот параметр определяет ширину рамки прямоугольника. Если вы хотите установить рамку, задайте положительное значение; а чтобы не отображать ее, используйте нулевое значение.
- clrBorder: необязательный параметр, задающий цвет рамки. Если вам нужна рамка, выберите цвет (например, clrNONE для отсутствия цвета).
- borderType: задайте тип рамки прямоугольника. Возможно использовать плоский, выпуклый и другие стили. Для простой плоской рамки используйте BORDER_FLAT.
- borderStyle: при выборе плоской рамки этот параметр определяет стиль линии (например, сплошная, пунктирная). Для сплошной линии используйте STYLE_SOLID.
В подписи функции вы должны были заметить, что некоторые аргументы уже инициализированы некоторым значением. Значение инициализации представляет собой значение по умолчанию, которое будет присвоено этому параметру, если он будет проигнорирован при вызове функции. Например, по умолчанию цвет рамки у нас — none, то есть, если при вызове функции значение цвета не будет указано, к рамке нашей прямоугольной метки цвет применяться не будет.
Внутри тела функции мы задаем в фигурных скобках ({}) процедуры создания нашего объекта.
// Create a rectangle label object if (!ObjectCreate(0, objName, OBJ_RECTANGLE_LABEL, 0, 0, 0)) { Print(__FUNCTION__, ": failed to create rec label! Error code = ", _LastError); return (false); // Return false if object creation fails }
Начнем с использования оператора if для проверки, не создан ли объект. Используется ObjectCreate, логическая функция, принимающая 6 аргументов. Эта функция создает объект с заданными названием, типом и начальными координатами в указанном подокне графика. Сначала укажем окно графика: 0 означает объект, создаваемый в главном окне. Затем укажем имя объекта. Это уникальное, присваемое только этому объекту название. Тип создаваемого нами объекта — "OBJ_RECTANGLE_LABEL", что означает объект для создания и конструирования пользовательского графического интерфейса. Затем перейдем к выделению подокна, для текущего подокна это 0. Наконец, укажем значения времени и цены как нулевые (0), поскольку мы будем прикреплять их не к графику, а к координатам его окна. Для настройки отображения используются пиксели.
Если создание объекта не удалось, функция "ObjectCreate" в конечном итоге возвращает false, и очевидно, что продолжать смысла нет, мы возвращаемся с ошибкой. В этом случае сообщаем об ошибке, печатая ее в журнале рядом с кодом ошибки и возвращая false. Возможно, этому предшествовала ошибка, поэтому для получения последней ошибки нужно очистить предыдущую. Это достигается вызовом "ResetLastError", встроенной функции языка MQL5, непосредственно перед логикой создания нашего объекта.
ResetLastError(); // Reset any previous error codes
Цель функции — установить нулевое значение для предопределенной переменной "_LastError", сохраняющее код ошибки последней операции, в которой возникла эта ошибка. Вызывая ее, мы обеспечиваем очистку от кодов всех предыдущих ошибок до продолжения следующих операций. Этот шаг важен, поскольку позволяет обрабатывать новые ошибки независимо, без влияния предшествующих состояний ошибки.
Если мы не вернемся к описанному моменту, это означает, что мы создали объект и, таким образом, можем продолжить обновление его свойств. Встроенная функция "ObjectSet..." устанавливает значение соответствующего свойства объекта. Свойство объекта должно быть одного из следующих типов: datetime (дата и время), integer (целые числа), color (цвет), boolean (логическое) или character (символы).
// Set properties for the rectangle label ObjectSetInteger(0, objName, OBJPROP_XDISTANCE, xD); // X distance from the corner ObjectSetInteger(0, objName, OBJPROP_YDISTANCE, yD); // Y distance from the corner ObjectSetInteger(0, objName, OBJPROP_XSIZE, xS); // Width of the rectangle ObjectSetInteger(0, objName, OBJPROP_YSIZE, yS); // Height of the rectangle ObjectSetInteger(0, objName, OBJPROP_CORNER, CORNER_LEFT_UPPER); // Positioning corner ObjectSetInteger(0, objName, OBJPROP_BGCOLOR, clrBg); // Rectangle background color ObjectSetInteger(0, objName, OBJPROP_BORDER_TYPE, borderType); // Border type ObjectSetInteger(0, objName, OBJPROP_STYLE, borderStyle); // Border style (only if borderType is flat) ObjectSetInteger(0, objName, OBJPROP_WIDTH, widthBorder); // Border width (only if borderType is flat) ObjectSetInteger(0, objName, OBJPROP_COLOR, clrBorder); // Border color (only if borderType is flat) ObjectSetInteger(0, objName, OBJPROP_BACK, false); // Not a background object ObjectSetInteger(0, objName, OBJPROP_STATE, false); // Not selectable ObjectSetInteger(0, objName, OBJPROP_SELECTABLE, false); // Not selectable ObjectSetInteger(0, objName, OBJPROP_SELECTED, false); // Not selected
Сосредоточимся на логике первого свойства.
ObjectSetInteger(0, objName, OBJPROP_XDISTANCE, xD); // X distance from the corner
Здесь мы используем встроенную функцию "ObjectSetInteger" и, соответственно, передаем параметры. Параметры описаны ниже.
- Chart id: это идентификатор графика. "0" относится к текущему графику (ID графика). Скорректируем свойства объекта на этом графике.
- Name: это название объекта. "objName" представляет собой уникальное имя, назначенное объекту с прямоугольной меткой.
- Property id: это идентификатор свойства объекта, и его значение может быть одним из значений перечисления "ENUM_OBJECT_PROPERTY_INTEGER". "OBJPROP_XDISTANCE" указывает, что мы изменяем свойство расстояния X.
- Property value: это значение свойства. Значение, присвоенное параметру "xD", определяет, насколько далеко вправо (или влево, если оно отрицательное) от левого края графика сместится по горизонтали левый верхний угол нашей прямоугольной метки.
Аналогично, пользуясь тем же форматом, задаем другие свойства. "OBJPROP_YDISTANCE" конфигурирует свойство расстояния Y прямоугольной метки. Значение "yD" определяет, как далеко левый верхний угол прямоугольной метки сместится по вертикали от верхнего края графика. Иными словами, оно контролирует положение метки в области графика по вертикали. Так задается расстояние Y от угла. "OBJPROP_XSIZE" и "OBJPROP_YSIZE" задают ширину и высоту прямоугольника соответственно.
Для расположения объекта будем правильно использовать "OBJPROP_CORNER", чтобы определить угол окна графика, где мы хотим видеть свой объект.
ObjectSetInteger(0, objName, OBJPROP_CORNER, CORNER_LEFT_UPPER); // Positioning corner
Свойство может быть только 4 типов:
- CORNER_LEFT_UPPER: центр координат находится в левом верхнем углу графика.
- CORNER_LEFT_LOWER: центр координат находится в левом нижнем углу графика.
- CORNER_RIGHT_LOWER: центр координат находится в правом нижнем углу графика.
- CORNER_RIGHT_UPPER: центр координат находится в правом верхнем углу графика.
Вот что получилось в фотопредставлении.
Остальные свойства не вызовут затруднений. Для облегчения понимания мы добавили к ним комментарии. Затем просто перерисуем график, чтобы изменения вступили в силу автоматически, не дожидаясь изменения котировок или событий на графике.
ChartRedraw(0); // Redraw the chart
Наконец, вернем значение true, указывающее, что создание и обновление свойств объекта прошли успешно.
return (true); // Return true if object creation and property settings are successful
Полный код функции, ответственной за создание прямоугольного объекта в окне графика, приводится ниже.
bool createRecLabel(string objName, int xD, int yD, int xS, int yS, color clrBg, int widthBorder, color clrBorder = clrNONE, ENUM_BORDER_TYPE borderType = BORDER_FLAT, ENUM_LINE_STYLE borderStyle = STYLE_SOLID) { ResetLastError(); // Reset any previous error codes // Create a rectangle label object if (!ObjectCreate(0, objName, OBJ_RECTANGLE_LABEL, 0, 0, 0)) { Print(__FUNCTION__, ": failed to create rec label! Error code = ", _LastError); return (false); // Return false if object creation fails } // Set properties for the rectangle label ObjectSetInteger(0, objName, OBJPROP_XDISTANCE, xD); // X distance from the corner ObjectSetInteger(0, objName, OBJPROP_YDISTANCE, yD); // Y distance from the corner ObjectSetInteger(0, objName, OBJPROP_XSIZE, xS); // Width of the rectangle ObjectSetInteger(0, objName, OBJPROP_YSIZE, yS); // Height of the rectangle ObjectSetInteger(0, objName, OBJPROP_CORNER, CORNER_LEFT_UPPER); // Positioning corner ObjectSetInteger(0, objName, OBJPROP_BGCOLOR, clrBg); // Rectangle background color ObjectSetInteger(0, objName, OBJPROP_BORDER_TYPE, borderType); // Border type ObjectSetInteger(0, objName, OBJPROP_STYLE, borderStyle); // Border style (only if borderType is flat) ObjectSetInteger(0, objName, OBJPROP_WIDTH, widthBorder); // Border width (only if borderType is flat) ObjectSetInteger(0, objName, OBJPROP_COLOR, clrBorder); // Border color (only if borderType is flat) ObjectSetInteger(0, objName, OBJPROP_BACK, false); // Not a background object ObjectSetInteger(0, objName, OBJPROP_STATE, false); // Not selectable ObjectSetInteger(0, objName, OBJPROP_SELECTABLE, false); // Not selectable ObjectSetInteger(0, objName, OBJPROP_SELECTED, false); // Not selected ChartRedraw(0); // Redraw the chart return (true); // Return true if object creation and property settings are successful }
Для создания объекта-кнопки применяется тот же функциональный подход. Код для создания пользовательской функции кнопки приводится ниже.
//+------------------------------------------------------------------+ //| Function to create button | //+------------------------------------------------------------------+ bool createButton(string objName, int xD, int yD, int xS, int yS, string txt = "", color clrTxt = clrBlack, int fontSize = 12, color clrBg = clrNONE, color clrBorder = clrNONE, string font = "Arial Rounded MT Bold") { // Reset any previous errors ResetLastError(); // Attempt to create the button object if (!ObjectCreate(0, objName, OBJ_BUTTON, 0, 0, 0)) { // Print an error message if creation fails Print(__FUNCTION__, ": failed to create the button! Error code = ", _LastError); return (false); } // Set properties for the button ObjectSetInteger(0, objName, OBJPROP_XDISTANCE, xD); // X distance from the corner ObjectSetInteger(0, objName, OBJPROP_YDISTANCE, yD); // Y distance from the corner ObjectSetInteger(0, objName, OBJPROP_XSIZE, xS); // Width of the button ObjectSetInteger(0, objName, OBJPROP_YSIZE, yS); // Height of the button ObjectSetInteger(0, objName, OBJPROP_CORNER, CORNER_LEFT_UPPER); // Positioning corner ObjectSetString(0, objName, OBJPROP_TEXT, txt); // Text displayed on the button ObjectSetInteger(0, objName, OBJPROP_COLOR, clrTxt); // Text color ObjectSetInteger(0, objName, OBJPROP_FONTSIZE, fontSize); // Font size ObjectSetString(0, objName, OBJPROP_FONT, font); // Font name ObjectSetInteger(0, objName, OBJPROP_BGCOLOR, clrBg); // Background color ObjectSetInteger(0, objName, OBJPROP_BORDER_COLOR, clrBorder); // Border color ObjectSetInteger(0, objName, OBJPROP_BACK, false); // Transparent background ObjectSetInteger(0, objName, OBJPROP_STATE, false); // Button state (not pressed) ObjectSetInteger(0, objName, OBJPROP_SELECTABLE, false); // Not selectable ObjectSetInteger(0, objName, OBJPROP_SELECTED, false); // Not selected // Redraw the chart to display the button ChartRedraw(0); return (true); // Button creation successful }
Различия в коде состоят в том, что объект-прямоугольник не может содержать текст, а кнопка при необходимости включает в себя текст, описывающий ее функции. Поэтому в качестве входных параметров рассматриваем свойства такста, в нашем случае — значение, цвет, размер шрифта и название шрифта. Тип границы для нашей кнопки статический, поэтому мы избавляемся от остальных ее свойств и оставляем только цвет.
Тип создаваемого объекта — "OBJ_BUTTON", а это значит, что мы создаем графический объект-кнопку. Точки его привязки задают в пикселях. Из всех свойств границы сохраним ее цвет, а остальное заменим свойствами ввода текста.
Для создания кнопки поля редактирования применяем ту же логику.
//+------------------------------------------------------------------+ //| Function to create edit field | //+------------------------------------------------------------------+ bool createEdit(string objName, int xD, int yD, int xS, int yS, string txt = "", color clrTxt = clrBlack, int fontSize = 12, color clrBg = clrNONE, color clrBorder = clrNONE, string font = "Arial Rounded MT Bold") { // Reset any previous errors ResetLastError(); // Attempt to create the edit object if (!ObjectCreate(0, objName, OBJ_EDIT, 0, 0, 0)) { // Print an error message if creation fails Print(__FUNCTION__, ": failed to create the edit! Error code = ", _LastError); return (false); } // Set properties for the edit field ObjectSetInteger(0, objName, OBJPROP_XDISTANCE, xD); // X distance from the corner ObjectSetInteger(0, objName, OBJPROP_YDISTANCE, yD); // Y distance from the corner ObjectSetInteger(0, objName, OBJPROP_XSIZE, xS); // Width of the edit field ObjectSetInteger(0, objName, OBJPROP_YSIZE, yS); // Height of the edit field ObjectSetInteger(0, objName, OBJPROP_CORNER, CORNER_LEFT_UPPER); // Positioning corner ObjectSetString(0, objName, OBJPROP_TEXT, txt); // Default text in the edit field ObjectSetInteger(0, objName, OBJPROP_COLOR, clrTxt); // Text color ObjectSetInteger(0, objName, OBJPROP_FONTSIZE, fontSize); // Font size ObjectSetString(0, objName, OBJPROP_FONT, font); // Font name ObjectSetInteger(0, objName, OBJPROP_BGCOLOR, clrBg); // Background color ObjectSetInteger(0, objName, OBJPROP_BORDER_COLOR, clrBorder); // Border color ObjectSetInteger(0, objName, OBJPROP_ALIGN, ALIGN_LEFT); // Text alignment (left-aligned) ObjectSetInteger(0, objName, OBJPROP_READONLY, false); // Edit field is not read-only ObjectSetInteger(0, objName, OBJPROP_BACK, false); // Transparent background ObjectSetInteger(0, objName, OBJPROP_STATE, false); // Edit field state (not active) ObjectSetInteger(0, objName, OBJPROP_SELECTABLE, false); // Not selectable ObjectSetInteger(0, objName, OBJPROP_SELECTED, false); // Not selected // Redraw the chart to display the edit field ChartRedraw(0); return (true); // Edit field creation successful }
Основное отличие кода от кода функции кнопки — это, в первую очередь, тип объекта. Используем "OBJ_EDIT" для обозначения желания создать редактируемую кнопку. Затем к свойствам поля добавим свойство выравнивания текста по горизонтали — по правому или левому краю либо по центру. В нашем случае выберем выравнивание текста по левому краю.
ObjectSetInteger(0, objName, OBJPROP_ALIGN, ALIGN_LEFT); // Text alignment (left-aligned)
Последнее отличие — свойство, позволяющее редактировать текст в объекте. Для включения редактирования установим значение свойства "OBJPROP_READONLY" равным false.
ObjectSetInteger(0, objName, OBJPROP_READONLY, false); // Edit field is not read-only
Остальные аргументы остаются.
Наконец, нам нужна функция последнего элемента — текстовая метка. Текстовая метка устраняет потребность в фоновом объекте, поэтому ее гораздо проще реализовать, чем остальные функции. Нам только нужен текст, поэтому сосредоточимся на свойствах текста. Его код выглядит следующим образом.
//+------------------------------------------------------------------+ //| Function to create text label | //+------------------------------------------------------------------+ bool createLabel(string objName, int xD, int yD, string txt, color clrTxt = clrBlack, int fontSize = 12, string font = "Arial Rounded MT Bold") { // Reset any previous errors ResetLastError(); // Attempt to create the label object if (!ObjectCreate(0, objName, OBJ_LABEL, 0, 0, 0)) { // Print an error message if creation fails Print(__FUNCTION__, ": failed to create the label! Error code = ", _LastError); return (false); } // Set properties for the label ObjectSetInteger(0, objName, OBJPROP_XDISTANCE, xD); // X distance from the corner ObjectSetInteger(0, objName, OBJPROP_YDISTANCE, yD); // Y distance from the corner ObjectSetInteger(0, objName, OBJPROP_CORNER, CORNER_LEFT_UPPER); // Positioning corner ObjectSetString(0, objName, OBJPROP_TEXT, txt); // Text displayed on the label ObjectSetInteger(0, objName, OBJPROP_COLOR, clrTxt); // Text color ObjectSetInteger(0, objName, OBJPROP_FONTSIZE, fontSize); // Font size ObjectSetString(0, objName, OBJPROP_FONT, font); // Font name ObjectSetInteger(0, objName, OBJPROP_BACK, false); // Transparent background ObjectSetInteger(0, objName, OBJPROP_STATE, false); // Label state (not active) ObjectSetInteger(0, objName, OBJPROP_SELECTABLE, false); // Not selectable ObjectSetInteger(0, objName, OBJPROP_SELECTED, false); // Not selected // Redraw the chart to display the label ChartRedraw(0); return (true); // Label creation successful }
Основные отличия в структуре этого кода от функции кнопки — размер объекта и свойства границы. В сигнатуре функции избавимся от размеров объекта, а также от свойств границ. Определим тип объекта как "OBJ_LABEL", обозначая, что мы рисуем метки в соответствии с заданными в окне диаграммы координатами. Наконец, избавляемся от параметров размера и границ — и всё на этом. Все просто.
Теперь, имея нужные для создания графического интерфейса функции, используем их для создания панели. Нам понадобятся имена обхекта, а чтобы было легче управлять ими, намного проще задать макросы.
#define MAIN_REC "MAIN_REC"
Используем ключевое слово #define для описания макроса с именем "MAIN_REC" и значением "MAIN_REC", чтобы с легкостью сохранить базовое имя нашего основного прямоугольника вместо повторого введения имени при каждом создании уровня, существенно экономя время и снижая вероятность ошибочного указания имени. Так что, по сути, макросы используют для подстановки текста при компиляции.
Наш код будет преимущественно основывать на разделе инициализации эксперта, поскольку мы хотим создать панель при инициализации. Таким образом, обработчик событий OnInit будет содержать большую часть структуры кода.
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit(){ ... return(INIT_SUCCEEDED); }
Функция OnInit — обработчик событий, вызываемый для экземпляра инициализации советника, чтобы выполнить необходимые инициализации.
Затем вызовем функцию для создания прямоугольной метки, введя ее имя и задав ее параметры.
createRecLabel(MAIN_REC,10,30,250,400,clrWhite,1,clrBlack);
Как следует из определения макроса, имя нашего прямоугольника — "MAIN_REC". Расстояние по оси x, шкале даты и времени, от верхнего левого угла окна графика составляет 10 пикселей, а по оси y, шкалы цен, — 30 пикселей. Ширина 250 пикселей, а высота — 400 пикселей соответственно. Выберем цвет фона — белый, ширина линии границы — 1, а цвет границы — черный. Для получения приблизительного диапазона в пикселях можно уменьшить масштаб графика до 0, а количество баров между двумя координатами перекрестья равно количеству пикселей на горизонтальной шкале. Подведем итог с помощью примера.
Прочие параметры опущены, и это означает, что значения по умолчанию будут применяться автоматически, то есть тип границы будет плоским, а стиль линии — непрерывная сплошная. После компиляции мы имеем вот что.
Даже если бы у нас были все параметры со значениями по умолчанию, как показано ниже, результаты были бы такими же.
createRecLabel(MAIN_REC,10,30,250,400,clrWhite,1,clrBlack,BORDER_FLAT,STYLE_SOLID);
Для создания подфрейма мы снова явно объявим макрос для него.
#define MAIN_SUB_REC "MAIN_SUB_REC"
Затем вызовем ту же функцию для создания подфрейма. Нам нужно, чтобы фрейм был внутри рамки базовой панели, поэтому используем немного другой цвет. Для этого мы использовали голубой цвет и ширину поля 5 пикселов.
createRecLabel(MAIN_SUB_REC,15,35,240,390,clrLightBlue,1,clrNONE);
Так как нам нужен отступ в 5 пикселей, сместимся с левой стороны на 5 пикселей и сверху на 5 пикселей. Таким образом, расстояние по оси x составит 10 исходных пикселей плюс запас в 5 пикселей, что будет равно 15 пикселям. То же самое сделаем для шкалы по оси y. Это означает, что размер тоже нужно уменьшить на 5 пикселей, но погодите секунду, нам же нужен и отступ 5 пикселей справа от панели. Поэтому необходимо уменьшение на 5 пикселей с каждой стороны для двух сторон. Таким образом, общее количество пикселей, которые потребуются, равно 5, умноженному на 2, то есть 10 пикселям. Так уменьшение размеров прямоугольника более низкого уровня на 10 пикселей составит для прямоугольника-подфрейма 250 - 10 = 240 пикселей в ширину и 400 - 10 = 390 пикселей в высоту. Вот какие результаты мы получили.
Голубой цвет не очень приятен для глаз, поэтому выберем более темный. Для более эффективного управления цветом представим цвет фона в литералах. Это будет формат "C'000,000,000'", где вместо трех нулей может быть любое число от 0 до 255. Принятый формат — RGB (Red, Green, Blue). Три значения представляют красный, зеленый и синий компоненты соответственно по шкале от 0 до 255. Таким образом, 245,245,245 соответствует почти белому оттенку.
createRecLabel(MAIN_SUB_REC,15,35,240,390,C'245,245,245',1,clrNONE);
Установим цвет границы равным none для создания более привлекательного сочетания цветов. При добавлении нового элемента графического интерфейса всегда рекомендуется скомпилировать программу и проверить результат. Ниже приводятся наши результаты.
Для создания вертикальных и горизонтальных граничных линий зададим еще два макроса.
#define MAIN_LINE_UP "MAIN_LINE_UP" #define MAIN_LINE_DN "MAIN_LINE_DN"
Используется та же структура, но теперь применим приподнятую линию для границы. Цвет границы будет, таким образом, проигнорирован, и возникнет иллюзия границы.
createRecLabel(MAIN_LINE_UP,15,35,240,1,C'245,245,245',1,clrNONE,BORDER_RAISED); createRecLabel(MAIN_LINE_DN,15,35-1,1,390+1,C'245,245,245',1,clrNONE,BORDER_RAISED);
Вот что мы получили.
На этом настройка рамок, полей и границ для нашей панели графического интерфейса завершена. Теперь перейдем к добавлению других утилит панели, например адаптивных кнопок, заголовков, полей редактирования и меток, а также их свойств и эффектов.
Для начала дадим панели название.
#define LABEL_NAME "LABEL_NAME" ... createLabel(LABEL_NAME,25,35,"DashBoard v1.0",clrBlue,14,"Cooper Black");
Сначала определим макрос для имени заголовка и вызовем функцию, ответственную за создание меток, введя ее имя. Затем зададим расстояния по осям x и y равными 25 и 35 пикселям соответственно. Выбираем для ввода текст "DashBoard v1.0", в первую очередь обозначающий тип и версию панели. Одновременно задаем цвет текста — синий, размер шрифта — 14 и тип шрифта — "Cooper Black". Что касается типа шрифта, это значение не чувствительно к регистру. Использование верхнего или нижнего регистра либо их сочетания ни на что не повлияет. Можно ввести "cooper black" или "COOPER BLACK". Значение имеет только правильное название шрифта. Открыв компиляцию, получим следующие результаты.
Вы заметили, что тип шрифта может меняться. Добавим в музыку сахара и посмотрим, какая получится мелодия. Некоторые типы шрифтов содержат иконки и эмодзи. Некоторые из них MQL5 теперь принимает и включает в себя. Примеры шрифтов: Webdings, Wingdings, Wingdings 2 и Wingdings 3. Их гораздо больше, но мы выбрали эти. Это значит, мы можем разукрасить панель с помощью этих шрифтов с иконками и эмодзи. Ниже показано, как это делается.
Добавим три иконки перед названием панели: сердечко, ящик с инструментами и машину скорой помощи. Конечно, вы можете выбрать их по своему вкусу. Для этого снова потребуется определить три макроса для хранения названий иконок, а затем использовать эти имена при их создании. Логика такая.
#define ICON_HEART "ICON_HEART" #define ICON_TOOL "ICON_TOOL" #define ICON_CAR "ICON_CAR" ... createLabel(ICON_HEART,190,35,"Y",clrRed,15,"Webdings");
Та же функция используется для расстояний по осям, цветов и размера шрифта. Однако можно заметить, что название нашего шрифта теперь изменилось на "Webdings", а текстовое значение — прописная буква Y. Это не значит, что мы будем отображать Y в качестве выходных данных. Отобразим иконку, соответствующую конкретно этому символу. Что касается шрифта, иконка под символом — сердечко, как показано ниже.
Текстовое значение чувствительно к регистру, поскольку, как видите, иконка под строчной "y" отличается от иконки прописной "Y". Вот что у нас получается после компиляции.
Теперь можно добавить две другие иконки так же, как и предыдущие.
createLabel(ICON_TOOL,210,35,"@",clrBlue,15,"Webdings"); createLabel(ICON_CAR,230,35,"h",clrBlack,15,"Webdings");
Затем ту же реализацию используем для добавления на график других служебных объектов. Логика показана ниже.
#define LINE1 "LINE1" #define BTN_CLOSE "BTN_CLOSE" #define BTN_MARKET "BTN_MARKET" #define BTN_PROFIT "BTN_PROFIT" #define BTN_LOSS "BTN_LOSS" #define BTN_PENDING "BTN_PENDING" #define LINE2 "LINE2" ... createRecLabel(LINE1,15+10,60,240-10,1,C'245,245,245',1,clrNONE,BORDER_RAISED); createLabel(BTN_CLOSE,25,65,"Close",clrBlack,13,"Impact"); createLabel(BTN_MARKET,70,65,"Market",clrDarkRed,13,"Impact"); createLabel(BTN_PROFIT,125,65,"Profit",clrGreen,13,"Impact"); createLabel(BTN_LOSS,170,65,"Loss",clrRed,13,"Impact"); createLabel(BTN_PENDING,205,65,"Pend'n",clrDarkGray,13,"Impact"); createRecLabel(LINE2,15+10,87,240-10,1,C'245,245,245',1,clrNONE,BORDER_RAISED);
Зададим имена макросов объектов и вызовем соответствующие пользовательские функции для создания этих объектов. Создается экземпляр из двух вертикальных граничных линий, в который вставляются служебные кнопки меток. Метки: Close, Market, Profit, Loss и Pending, адаптированные к "Pend'n", чтобы вместить формулировку в границы фрейма. Они предназначены для закрытия всех рыночных и отложенных ордеров, закрытия только рыночных ордеров, закрытия только прибыльных позиций, закрытия только убыточных позиций и удаления отложенных ордеров соответственно. После компиляции получим следующий результат.
К этому моменту мы успешно создали раздел заголовка нашей панели. Приступим к созданию кнопок-утилит. Сначала сосредоточимся на кнопке объема торговли. У нашей кнопки будут метка и выпадающий список.
#define BTN_LOTS "BTN_LOTS" #define LABEL_LOTS "LABEL_LOTS" #define ICON_DROP_DN1 "ICON_DROP_DN1"
Определение макросов завершено. После этого создадим кнопку, вызвав соответствующую функцию.
createButton(BTN_LOTS,25,95,130,25,"",clrBlack,12,C'210,210,210',C'150,150,150');
Снова задаем расстояния кнопки по осям x и y размером 130 на 25 пикселей в ширину и высоту соответственно. Относительно текста: мы могли бы указать значение, но решили не делать этого, поскольку хотели получить контроль над размещением текста, поэтому оставили поле пустым (нулевым). Литеральные представления цветов в конечном итоге используются для установки фона и окантовки кнопки. Поскольку мы решили не использовать метку кнопки по умолчанию, создадим новую текстовую метку недалеко от кнопки с помощью нижеприведенной функции.
createLabel(LABEL_LOTS,25+10,95+5,"LotSize",clrBlack,9);
Теперь функция вам знакома, и вы знаете передаваемые параметры. Для раскрывающейся иконки используем тип шрифта, передающий эту иконку.
createLabel(ICON_DROP_DN1,130,95+5,CharToString(240),C'070,070,070',20,"Wingdings 3");
Можно заметить, что в текстовом аргументе мы преобразуем символ в строку — еще один способ передачи соответствующего кода символа вместо строки. Это полезно, если вы уже знаете код символа. Та же процедура применяется для создания кнопки редактирования поля.
#define EDIT_LOTS "EDIT_LOTS" #define BTN_P1 "BTN_P1" #define BTN_M1 "BTN_M1" ... createEdit(EDIT_LOTS,165,95,60,25,"0.01",clrBlack,14,clrWhite,C'100,100,100',"Callibri"); createButton(BTN_P1,225,95,20,12,"5",clrBlack,12,clrLightGray,C'100,100,100',"Webdings"); createButton(BTN_M1,225,95+12,20,12,"6",clrBlack,12,clrLightGray,C'100,100,100',"Webdings");
Обычным способом определим имена макросов и вызовем функции. Для создания кнопки редактирования используются размеры 60 и 25 пикселей для ширины и высоты соответственно. Значение текста по умолчанию установлено на 0.01, цвет текста черный, размер шрифта 14, а тип — Calibri. Установленный цвет фона кнопки — белый, а цвет обрамления почти темный. Для показателей увеличения и уменьшения объема торгов используем тип шрифта Webdings и текстовые значения "5" и "6" соответственно. По итогам компиляции у нас вот такой важный этап.
Мы хорошо продвинулись. Для создания других кнопок-утилит стоп-лосса и тейк-профита та же логика применяется с помощью кода ниже.
#define BTN_SL "BTN_SL" #define LABEL_SL "LABEL_SL" #define ICON_DROP_DN2 "ICON_DROP_DN2" #define EDIT_SL "EDIT_SL" #define BTN_P2 "BTN_P2" #define BTN_M2 "BTN_M2" #define BTN_TP "BTN_TP" #define LABEL_TP "LABEL_TP" #define ICON_DROP_DN3 "ICON_DROP_DN3" #define EDIT_TP "EDIT_TP" #define BTN_P3 "BTN_P3" #define BTN_M3 "BTN_M3" ... createButton(BTN_SL,25,95+30,130,25,"",clrBlack,12,C'210,210,210',C'150,150,150'); createLabel(LABEL_SL,35,95+30,"SL Pips",clrBlack,14); createLabel(ICON_DROP_DN2,130,100+30,CharToString(240),C'070,070,070',20,"Wingdings 3"); createEdit(EDIT_SL,165,95+30,60,25,"100.0",clrBlack,13,clrWhite,C'100,100,100',"Callibri"); createButton(BTN_P2,225,95+30,20,12,"5",clrBlack,12,clrLightGray,C'100,100,100',"Webdings"); createButton(BTN_M2,225,107+30,20,12,"6",clrBlack,12,clrLightGray,C'100,100,100',"Webdings"); createButton(BTN_TP,25,95+30+30,130,25,"",clrBlack,12,C'210,210,210',C'150,150,150'); createLabel(LABEL_TP,35,95+30+30,"TP Pips",clrBlack,14); createLabel(ICON_DROP_DN3,130,100+30+30,CharToString(240),C'070,070,070',20,"Wingdings 3"); createEdit(EDIT_TP,165,95+30+30,60,25,"100.0",clrBlack,13,clrWhite,C'100,100,100',"Callibri"); createButton(BTN_P3,225,95+30+30,20,12,"5",clrBlack,12,clrLightGray,C'100,100,100',"Webdings"); createButton(BTN_M3,225,107+30+30,20,12,"6",clrBlack,12,clrLightGray,C'100,100,100',"Webdings");
Определены макросы утилит, вызваны соответствующие функции для их создания. Этот код по логике не отличается от исходного, который мы использовали для создания кнопок объема торговли. При компиляции у вас должен получиться следующий результат.
#define BTN_BUY "BTN_BUY" #define LABEL_BUY "LABEL_BUY" #define LABEL_BUY_PRICE "LABEL_BUY_PRICE" #define BTN_OVERLAY "BTN_OVERLAY" #define BTN_SPREAD "BTN_SPREAD" #define BTN_SELL "BTN_SELL" #define LABEL_SELL "LABEL_SELL" #define LABEL_SELL_PRICE "LABEL_SELL_PRICE" #define BTN_CONTACT "BTN_CONTACT" ... createRecLabel(BTN_SELL,25,335,105,60,clrRed,1,clrNONE); createLabel(LABEL_SELL,35,335,"Sell",clrWhite,15,"ARIAL black"); createLabel(LABEL_SELL_PRICE,35,335+30,DoubleToString(Bid(),_Digits),clrWhite,13,"ARIAL black"); createRecLabel(BTN_BUY,140,335,105,60,clrGreen,1,clrNONE); createLabel(LABEL_BUY,150+35,335,"Buy",clrWhite,15,"ARIAL black"); createLabel(LABEL_BUY_PRICE,150,335+30,DoubleToString(Ask(),_Digits),clrWhite,13,"ARIAL black"); createRecLabel(BTN_OVERLAY,90,335,90,25,C'245,245,245',0,clrNONE); createButton(BTN_SPREAD,95,335,80,20,(string)Spread(),clrBlack,13,clrWhite,clrBlack); createButton(BTN_CONTACT,25,335+62,230-10,25,"https://t.me/Forex_Algo_Trader",clrBlack,10,clrNONE,clrBlack);
Для создания кнопки осуществления покупки, продажи, наложения, кнопки вызова дополнительной информации и кнопки спреда реализован вышеприведенный код. Единственное, что меняется сверх того, — это текст для цен продажи и покупки, а также значение спреда. Для заполнения значений вызываются дополнительные пользовательские функции: Bid, Ask и Spread. Функции bid и ask возвращают значение типа double, поэтому для преобразования double в string, вызывается встроенная функция "DoubleToString". Для спреда преобразуем значение integer непосредственно в string. Ниже приведены пользовательские функции.
double Ask(){return(NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),_Digits));} double Bid(){return(NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),_Digits));} int Spread(){return((int)SymbolInfoInteger(_Symbol,SYMBOL_SPREAD));}
Функции возвращают соответствующие типы данных символьной информации. После компиляции получим следующий результат.
Теперь приступим к созданию некоторых дополнительных кнопок, которыми мы воспользуемся для иллюстрации возможности создать кнопку с острыми краями, а также дубликата того, что мы будем использовать для автоматизации эффекта наведения.
#define BTN_SHARP "BTN_SHARP" #define LABEL_SHARP "LABEL_SHARP" #define BTN_HOVER "BTN_HOVER" #define LABEL_HOVER "LABEL_HOVER" ... createRecLabel(BTN_SHARP,25,190,220,35,C'220,220,220',2,C'100,100,100'); createLabel(LABEL_SHARP,25+20,190+5,"Sharp Edged Button",clrBlack,12,"ARIAL black"); //createRecLabel(BTN_HOVER,25,230,220,35,C'220,220,220',3,C'050,050,255'); createRecLabel(BTN_HOVER,25,230,220,35,C'220,220,220',3,C'100,100,100'); createLabel(LABEL_HOVER,25+50,230+5,"Hover Effect",clrBlack,12,"ARIAL black");
Ниже представлен важный этап.
Осталось немного места, которое можно использовать для других утилит, но мы заполним его некоторыми символами эмодззи и иконок.
#define LABEL_EXTRA1 "LABEL_EXTRA1" #define LABEL_EXTRA2 "LABEL_EXTRA2" #define LABEL_EXTRA3 "LABEL_EXTRA3" #define LABEL_EXTRA4 "LABEL_EXTRA4" ... createLabel(LABEL_EXTRA1,25,290,"_",clrBlack,25,"WEBDINGS"); createLabel(LABEL_EXTRA2,25+40,290,"J",clrBlack,25,"WINGDINGS"); createLabel(LABEL_EXTRA3,25+40+40,290,"{",clrBlack,25,"WINGDINGS 2"); createLabel(LABEL_EXTRA4,25+40+40+40,290,"G",clrBlack,25,"WINGDINGS 3");
После нажатия кнопки объема торговли нам понадобятся несколько экземпляров для создания выпадающего списка, в котором мы можем выбрать различные опции. Нужны 3 опции, а также иконка, с помощью которой можно будет перетащить список из начальной точки создания в другое место. Используется та же логика.
#define BTN_DROP_DN "BTN_DROP_DN" #define LABEL_OPT1 "LABEL_OPT1" #define LABEL_OPT2 "LABEL_OPT2" #define LABEL_OPT3 "LABEL_OPT3" #define ICON_DRAG "ICON_DRAG" ... void createDropDown(){ createRecLabel(BTN_DROP_DN,25,95+25,130,70,clrWhite,2,clrBlack); createLabel(LABEL_OPT1,25+10,95+25,"LotSize",clrBlack,12,"stencil"); createLabel(LABEL_OPT2,25+10,95+25+20,"Risk Percent %",clrBlack,12,"calibri Italic"); createLabel(LABEL_OPT3,25+10,95+25+20+20,"Money Balance",clrBlack,12,"Arial bold"); createLabel(ICON_DRAG,25+10+95,95+25,"d",clrRoyalBlue,15,"webdings"); }
Здесь мы создадим кнопки и метки в пользовательской функции, чтобы вызывать функцию при нажатии на кнопку объема торговли. Однако для отображения позиций утилит мы вызовем функцию, но позднее закомментируем ее.
createDropDown();
Заключительный этап описан ниже.
Наконец, нам нужно уничтожить и удалить созданные объекты после удаления советника с графика. Для этого создана еще одна функция — удаление созданных объектов.
void destroyPanel(){ ObjectDelete(0,MAIN_REC); ObjectDelete(0,MAIN_SUB_REC); ObjectDelete(0,MAIN_LINE_UP); //... other objects ChartRedraw(0); }
Используется пользовательская функция void, которая означает, что она ничего не возвращает. Встроенная функция "ObjectDelete" используется для удаления соответствующих переданных объектов. Она принимает два аргумента: идентификатор графика (0 для текущего графика) и имя удаляемого объекта. Она вызывается во время работы функции деинициализации советника.
//+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason){ //--- destroyPanel(); }
Ниже приведен полный исходный код, отвечающий за создание панели графического интерфейса, как показано на MQL5 для MetaTrader 5 (MT5).
//+------------------------------------------------------------------+ //| DASHBOARD PART 1.mq5 | //| Copyright 2024, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2024, MetaQuotes Ltd." #property link "https://www.mql5.com" #property version "1.00" //--- DEFINE MACRO PROPERTIES #define MAIN_REC "MAIN_REC" #define MAIN_SUB_REC "MAIN_SUB_REC" #define MAIN_LINE_UP "MAIN_LINE_UP" #define MAIN_LINE_DN "MAIN_LINE_DN" #define LABEL_NAME "LABEL_NAME" #define ICON_HEART "ICON_HEART" #define ICON_TOOL "ICON_TOOL" #define ICON_CAR "ICON_CAR" #define LINE1 "LINE1" #define BTN_CLOSE "BTN_CLOSE" #define BTN_MARKET "BTN_MARKET" #define BTN_PROFIT "BTN_PROFIT" #define BTN_LOSS "BTN_LOSS" #define BTN_PENDING "BTN_PENDING" #define LINE2 "LINE2" #define BTN_LOTS "BTN_LOTS" #define LABEL_LOTS "LABEL_LOTS" #define ICON_DROP_DN1 "ICON_DROP_DN1" #define EDIT_LOTS "EDIT_LOTS" #define BTN_P1 "BTN_P1" #define BTN_M1 "BTN_M1" #define BTN_SL "BTN_SL" #define LABEL_SL "LABEL_SL" #define ICON_DROP_DN2 "ICON_DROP_DN2" #define EDIT_SL "EDIT_SL" #define BTN_P2 "BTN_P2" #define BTN_M2 "BTN_M2" #define BTN_TP "BTN_TP" #define LABEL_TP "LABEL_TP" #define ICON_DROP_DN3 "ICON_DROP_DN3" #define EDIT_TP "EDIT_TP" #define BTN_P3 "BTN_P3" #define BTN_M3 "BTN_M3" #define BTN_BUY "BTN_BUY" #define LABEL_BUY "LABEL_BUY" #define LABEL_BUY_PRICE "LABEL_BUY_PRICE" #define BTN_OVERLAY "BTN_OVERLAY" #define BTN_SPREAD "BTN_SPREAD" #define BTN_SELL "BTN_SELL" #define LABEL_SELL "LABEL_SELL" #define LABEL_SELL_PRICE "LABEL_SELL_PRICE" #define BTN_CONTACT "BTN_CONTACT" #define BTN_SHARP "BTN_SHARP" #define LABEL_SHARP "LABEL_SHARP" #define BTN_HOVER "BTN_HOVER" #define LABEL_HOVER "LABEL_HOVER" #define LABEL_EXTRA1 "LABEL_EXTRA1" #define LABEL_EXTRA2 "LABEL_EXTRA2" #define LABEL_EXTRA3 "LABEL_EXTRA3" #define LABEL_EXTRA4 "LABEL_EXTRA4" #define BTN_DROP_DN "BTN_DROP_DN" #define LABEL_OPT1 "LABEL_OPT1" #define LABEL_OPT2 "LABEL_OPT2" #define LABEL_OPT3 "LABEL_OPT3" #define ICON_DRAG "ICON_DRAG" //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit(){ createRecLabel(MAIN_REC,10,30,250,400,clrWhite,1,clrBlack); createRecLabel(MAIN_SUB_REC,15,35,240,390,C'245,245,245',1,clrNONE); createRecLabel(MAIN_LINE_UP,15,35,240,1,C'245,245,245',1,clrNONE,BORDER_RAISED); createRecLabel(MAIN_LINE_DN,15,35-1,1,390+1,C'245,245,245',1,clrNONE,BORDER_RAISED); createLabel(LABEL_NAME,25,35,"DashBoard v1.0",clrBlue,14,"Cooper Black"); createLabel(ICON_HEART,190,35,"Y",clrRed,15,"Webdings"); createLabel(ICON_TOOL,210,35,"@",clrBlue,15,"Webdings"); createLabel(ICON_CAR,230,35,"h",clrBlack,15,"Webdings"); createRecLabel(LINE1,15+10,60,240-10,1,C'245,245,245',1,clrNONE,BORDER_RAISED); createLabel(BTN_CLOSE,25,65,"Close",clrBlack,13,"Impact"); createLabel(BTN_MARKET,70,65,"Market",clrDarkRed,13,"Impact"); createLabel(BTN_PROFIT,125,65,"Profit",clrGreen,13,"Impact"); createLabel(BTN_LOSS,170,65,"Loss",clrRed,13,"Impact"); createLabel(BTN_PENDING,205,65,"Pend'n",clrDarkGray,13,"Impact"); createRecLabel(LINE2,15+10,87,240-10,1,C'245,245,245',1,clrNONE,BORDER_RAISED); createButton(BTN_LOTS,25,95,130,25,"",clrBlack,12,C'210,210,210',C'150,150,150'); createLabel(LABEL_LOTS,25+10,95+5,"LotSize",clrBlack,9); //createLabel(ICON_DROP_DN1,150,75,CharToString(100),clrBlack,12,"Wingdings"); createLabel(ICON_DROP_DN1,130,95+5,CharToString(240),C'070,070,070',20,"Wingdings 3"); createEdit(EDIT_LOTS,165,95,60,25,"0.01",clrBlack,14,clrWhite,C'100,100,100',"Callibri"); createButton(BTN_P1,225,95,20,12,"5",clrBlack,12,clrLightGray,C'100,100,100',"Webdings"); createButton(BTN_M1,225,95+12,20,12,"6",clrBlack,12,clrLightGray,C'100,100,100',"Webdings"); createButton(BTN_SL,25,95+30,130,25,"",clrBlack,12,C'210,210,210',C'150,150,150'); createLabel(LABEL_SL,35,95+30,"SL Pips",clrBlack,14); createLabel(ICON_DROP_DN2,130,100+30,CharToString(240),C'070,070,070',20,"Wingdings 3"); createEdit(EDIT_SL,165,95+30,60,25,"100.0",clrBlack,13,clrWhite,C'100,100,100',"Callibri"); createButton(BTN_P2,225,95+30,20,12,"5",clrBlack,12,clrLightGray,C'100,100,100',"Webdings"); createButton(BTN_M2,225,107+30,20,12,"6",clrBlack,12,clrLightGray,C'100,100,100',"Webdings"); createButton(BTN_TP,25,95+30+30,130,25,"",clrBlack,12,C'210,210,210',C'150,150,150'); createLabel(LABEL_TP,35,95+30+30,"TP Pips",clrBlack,14); createLabel(ICON_DROP_DN3,130,100+30+30,CharToString(240),C'070,070,070',20,"Wingdings 3"); createEdit(EDIT_TP,165,95+30+30,60,25,"100.0",clrBlack,13,clrWhite,C'100,100,100',"Callibri"); createButton(BTN_P3,225,95+30+30,20,12,"5",clrBlack,12,clrLightGray,C'100,100,100',"Webdings"); createButton(BTN_M3,225,107+30+30,20,12,"6",clrBlack,12,clrLightGray,C'100,100,100',"Webdings"); createRecLabel(BTN_SELL,25,335,105,60,clrRed,1,clrNONE); createLabel(LABEL_SELL,35,335,"Sell",clrWhite,15,"ARIAL black"); createLabel(LABEL_SELL_PRICE,35,335+30,DoubleToString(Bid(),_Digits),clrWhite,13,"ARIAL black"); createRecLabel(BTN_BUY,140,335,105,60,clrGreen,1,clrNONE); createLabel(LABEL_BUY,150+35,335,"Buy",clrWhite,15,"ARIAL black"); createLabel(LABEL_BUY_PRICE,150,335+30,DoubleToString(Ask(),_Digits),clrWhite,13,"ARIAL black"); createRecLabel(BTN_OVERLAY,90,335,90,25,C'245,245,245',0,clrNONE); createButton(BTN_SPREAD,95,335,80,20,(string)Spread(),clrBlack,13,clrWhite,clrBlack); createButton(BTN_CONTACT,25,335+62,230-10,25,"https://t.me/Forex_Algo_Trader",clrBlack,10,clrNONE,clrBlack); createRecLabel(BTN_SHARP,25,190,220,35,C'220,220,220',2,C'100,100,100'); createLabel(LABEL_SHARP,25+20,190+5,"Sharp Edged Button",clrBlack,12,"ARIAL black"); //createRecLabel(BTN_HOVER,25,230,220,35,C'220,220,220',3,C'050,050,255'); createRecLabel(BTN_HOVER,25,230,220,35,C'220,220,220',3,C'100,100,100'); createLabel(LABEL_HOVER,25+50,230+5,"Hover Effect",clrBlack,12,"ARIAL black"); createLabel(LABEL_EXTRA1,25,290,"_",clrBlack,25,"WEBDINGS"); createLabel(LABEL_EXTRA2,25+40,290,"J",clrBlack,25,"WINGDINGS"); createLabel(LABEL_EXTRA3,25+40+40,290,"{",clrBlack,25,"WINGDINGS 2"); createLabel(LABEL_EXTRA4,25+40+40+40,290,"G",clrBlack,25,"WINGDINGS 3"); // createDropDown(); return(INIT_SUCCEEDED); } double Ask(){return(NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),_Digits));} double Bid(){return(NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),_Digits));} int Spread(){return((int)SymbolInfoInteger(_Symbol,SYMBOL_SPREAD));} //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason){ //--- destroyPanel(); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick(){ } //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Function to create rectangle label | //+------------------------------------------------------------------+ bool createRecLabel(string objName, int xD, int yD, int xS, int yS, color clrBg, int widthBorder, color clrBorder = clrNONE, ENUM_BORDER_TYPE borderType = BORDER_FLAT, ENUM_LINE_STYLE borderStyle = STYLE_SOLID) { ResetLastError(); // Reset any previous error codes // Create a rectangle label object if (!ObjectCreate(0, objName, OBJ_RECTANGLE_LABEL, 0, 0, 0)) { Print(__FUNCTION__, ": failed to create rec label! Error code = ", _LastError); return (false); // Return false if object creation fails } // Set properties for the rectangle label ObjectSetInteger(0, objName, OBJPROP_XDISTANCE, xD); // X distance from the corner ObjectSetInteger(0, objName, OBJPROP_YDISTANCE, yD); // Y distance from the corner ObjectSetInteger(0, objName, OBJPROP_XSIZE, xS); // Width of the rectangle ObjectSetInteger(0, objName, OBJPROP_YSIZE, yS); // Height of the rectangle ObjectSetInteger(0, objName, OBJPROP_CORNER, CORNER_LEFT_UPPER); // Positioning corner ObjectSetInteger(0, objName, OBJPROP_BGCOLOR, clrBg); // Rectangle background color ObjectSetInteger(0, objName, OBJPROP_BORDER_TYPE, borderType); // Border type ObjectSetInteger(0, objName, OBJPROP_STYLE, borderStyle); // Border style (only if borderType is flat) ObjectSetInteger(0, objName, OBJPROP_WIDTH, widthBorder); // Border width (only if borderType is flat) ObjectSetInteger(0, objName, OBJPROP_COLOR, clrBorder); // Border color (only if borderType is flat) ObjectSetInteger(0, objName, OBJPROP_BACK, false); // Not a background object ObjectSetInteger(0, objName, OBJPROP_STATE, false); // Not selectable ObjectSetInteger(0, objName, OBJPROP_SELECTABLE, false); // Not selectable ObjectSetInteger(0, objName, OBJPROP_SELECTED, false); // Not selected ChartRedraw(0); // Redraw the chart return (true); // Return true if object creation and property settings are successful } //+------------------------------------------------------------------+ //| Function to create button | //+------------------------------------------------------------------+ bool createButton(string objName, int xD, int yD, int xS, int yS, string txt = "", color clrTxt = clrBlack, int fontSize = 12, color clrBg = clrNONE, color clrBorder = clrNONE, string font = "Arial Rounded MT Bold") { // Reset any previous errors ResetLastError(); // Attempt to create the button object if (!ObjectCreate(0, objName, OBJ_BUTTON, 0, 0, 0)) { // Print an error message if creation fails Print(__FUNCTION__, ": failed to create the button! Error code = ", _LastError); return (false); } // Set properties for the button ObjectSetInteger(0, objName, OBJPROP_XDISTANCE, xD); // X distance from the corner ObjectSetInteger(0, objName, OBJPROP_YDISTANCE, yD); // Y distance from the corner ObjectSetInteger(0, objName, OBJPROP_XSIZE, xS); // Width of the button ObjectSetInteger(0, objName, OBJPROP_YSIZE, yS); // Height of the button ObjectSetInteger(0, objName, OBJPROP_CORNER, CORNER_LEFT_UPPER); // Positioning corner ObjectSetString(0, objName, OBJPROP_TEXT, txt); // Text displayed on the button ObjectSetInteger(0, objName, OBJPROP_COLOR, clrTxt); // Text color ObjectSetInteger(0, objName, OBJPROP_FONTSIZE, fontSize); // Font size ObjectSetString(0, objName, OBJPROP_FONT, font); // Font name ObjectSetInteger(0, objName, OBJPROP_BGCOLOR, clrBg); // Background color ObjectSetInteger(0, objName, OBJPROP_BORDER_COLOR, clrBorder); // Border color ObjectSetInteger(0, objName, OBJPROP_BACK, false); // Transparent background ObjectSetInteger(0, objName, OBJPROP_STATE, false); // Button state (not pressed) ObjectSetInteger(0, objName, OBJPROP_SELECTABLE, false); // Not selectable ObjectSetInteger(0, objName, OBJPROP_SELECTED, false); // Not selected // Redraw the chart to display the button ChartRedraw(0); return (true); // Button creation successful } //+------------------------------------------------------------------+ //| Function to create edit field | //+------------------------------------------------------------------+ bool createEdit(string objName, int xD, int yD, int xS, int yS, string txt = "", color clrTxt = clrBlack, int fontSize = 12, color clrBg = clrNONE, color clrBorder = clrNONE, string font = "Arial Rounded MT Bold") { // Reset any previous errors ResetLastError(); // Attempt to create the edit object if (!ObjectCreate(0, objName, OBJ_EDIT, 0, 0, 0)) { // Print an error message if creation fails Print(__FUNCTION__, ": failed to create the edit! Error code = ", _LastError); return (false); } // Set properties for the edit field ObjectSetInteger(0, objName, OBJPROP_XDISTANCE, xD); // X distance from the corner ObjectSetInteger(0, objName, OBJPROP_YDISTANCE, yD); // Y distance from the corner ObjectSetInteger(0, objName, OBJPROP_XSIZE, xS); // Width of the edit field ObjectSetInteger(0, objName, OBJPROP_YSIZE, yS); // Height of the edit field ObjectSetInteger(0, objName, OBJPROP_CORNER, CORNER_LEFT_UPPER); // Positioning corner ObjectSetString(0, objName, OBJPROP_TEXT, txt); // Default text in the edit field ObjectSetInteger(0, objName, OBJPROP_COLOR, clrTxt); // Text color ObjectSetInteger(0, objName, OBJPROP_FONTSIZE, fontSize); // Font size ObjectSetString(0, objName, OBJPROP_FONT, font); // Font name ObjectSetInteger(0, objName, OBJPROP_BGCOLOR, clrBg); // Background color ObjectSetInteger(0, objName, OBJPROP_BORDER_COLOR, clrBorder); // Border color ObjectSetInteger(0, objName, OBJPROP_ALIGN, ALIGN_LEFT); // Text alignment (left-aligned) ObjectSetInteger(0, objName, OBJPROP_READONLY, false); // Edit field is not read-only ObjectSetInteger(0, objName, OBJPROP_BACK, false); // Transparent background ObjectSetInteger(0, objName, OBJPROP_STATE, false); // Edit field state (not active) ObjectSetInteger(0, objName, OBJPROP_SELECTABLE, false); // Not selectable ObjectSetInteger(0, objName, OBJPROP_SELECTED, false); // Not selected // Redraw the chart to display the edit field ChartRedraw(0); return (true); // Edit field creation successful } //+------------------------------------------------------------------+ //| Function to create text label | //+------------------------------------------------------------------+ bool createLabel(string objName, int xD, int yD, string txt, color clrTxt = clrBlack, int fontSize = 12, string font = "Arial Rounded MT Bold") { // Reset any previous errors ResetLastError(); // Attempt to create the label object if (!ObjectCreate(0, objName, OBJ_LABEL, 0, 0, 0)) { // Print an error message if creation fails Print(__FUNCTION__, ": failed to create the label! Error code = ", _LastError); return (false); } // Set properties for the label ObjectSetInteger(0, objName, OBJPROP_XDISTANCE, xD); // X distance from the corner ObjectSetInteger(0, objName, OBJPROP_YDISTANCE, yD); // Y distance from the corner ObjectSetInteger(0, objName, OBJPROP_CORNER, CORNER_LEFT_UPPER); // Positioning corner ObjectSetString(0, objName, OBJPROP_TEXT, txt); // Text displayed on the label ObjectSetInteger(0, objName, OBJPROP_COLOR, clrTxt); // Text color ObjectSetInteger(0, objName, OBJPROP_FONTSIZE, fontSize); // Font size ObjectSetString(0, objName, OBJPROP_FONT, font); // Font name ObjectSetInteger(0, objName, OBJPROP_BACK, false); // Transparent background ObjectSetInteger(0, objName, OBJPROP_STATE, false); // Label state (not active) ObjectSetInteger(0, objName, OBJPROP_SELECTABLE, false); // Not selectable ObjectSetInteger(0, objName, OBJPROP_SELECTED, false); // Not selected // Redraw the chart to display the label ChartRedraw(0); return (true); // Label creation successful } //+------------------------------------------------------------------+ //| Function to create the drop down utilities | //+------------------------------------------------------------------+ void createDropDown(){ createRecLabel(BTN_DROP_DN,25,95+25,130,70,clrWhite,2,clrBlack); createLabel(LABEL_OPT1,25+10,95+25,"LotSize",clrBlack,12,"stencil"); createLabel(LABEL_OPT2,25+10,95+25+20,"Risk Percent %",clrBlack,12,"calibri Italic"); createLabel(LABEL_OPT3,25+10,95+25+20+20,"Money Balance",clrBlack,12,"Arial bold"); createLabel(ICON_DRAG,25+10+95,95+25,"d",clrRoyalBlue,15,"webdings"); } //+------------------------------------------------------------------+ //| Function to destroy the entire GUI Panel | //+------------------------------------------------------------------+ void destroyPanel(){ ObjectDelete(0,MAIN_REC); ObjectDelete(0,MAIN_SUB_REC); ObjectDelete(0,MAIN_LINE_UP); ObjectDelete(0,MAIN_LINE_DN); ObjectDelete(0,BTN_LOTS); ObjectDelete(0,LABEL_NAME); ObjectDelete(0,LABEL_LOTS); ObjectDelete(0,ICON_HEART); ObjectDelete(0,ICON_TOOL); ObjectDelete(0,ICON_CAR); ObjectDelete(0,ICON_DROP_DN1); ObjectDelete(0,LINE1); ObjectDelete(0,BTN_CLOSE); ObjectDelete(0,BTN_MARKET); ObjectDelete(0,BTN_PROFIT); ObjectDelete(0,BTN_LOSS); ObjectDelete(0,BTN_PENDING); ObjectDelete(0,LINE2); ObjectDelete(0,EDIT_LOTS); ObjectDelete(0,BTN_P1); ObjectDelete(0,BTN_M1); ObjectDelete(0,BTN_SL); ObjectDelete(0,LABEL_SL); ObjectDelete(0,ICON_DROP_DN2); ObjectDelete(0,EDIT_SL); ObjectDelete(0,BTN_P2); ObjectDelete(0,BTN_M2); ObjectDelete(0,BTN_TP); ObjectDelete(0,LABEL_TP); ObjectDelete(0,ICON_DROP_DN3); ObjectDelete(0,EDIT_TP); ObjectDelete(0,BTN_P3); ObjectDelete(0,BTN_M3); ObjectDelete(0,BTN_BUY); ObjectDelete(0,LABEL_BUY); ObjectDelete(0,LABEL_BUY_PRICE); ObjectDelete(0,BTN_OVERLAY); ObjectDelete(0,BTN_SPREAD); ObjectDelete(0,BTN_SELL); ObjectDelete(0,LABEL_SELL); ObjectDelete(0,LABEL_SELL_PRICE); ObjectDelete(0,BTN_CONTACT); ObjectDelete(0,BTN_SHARP); ObjectDelete(0,LABEL_SHARP); ObjectDelete(0,BTN_HOVER); ObjectDelete(0,LABEL_HOVER); ObjectDelete(0,LABEL_EXTRA1); ObjectDelete(0,LABEL_EXTRA2); ObjectDelete(0,LABEL_EXTRA3); ObjectDelete(0,LABEL_EXTRA4); ObjectDelete(0,BTN_DROP_DN); ObjectDelete(0,LABEL_OPT1); ObjectDelete(0,LABEL_OPT2); ObjectDelete(0,LABEL_OPT3); ObjectDelete(0,ICON_DRAG); ChartRedraw(0); }
Заключение
В заключение можно сказать, что создание панелей графического интерфейса не так сложно, как может показаться. Процесс включает в себя ряд шагов от определения размеров панели, то есть выбора положения, размера и внешнего вида панели с помощью абсолютных координат или координат относительно графика посредством создания различных графических объектов, таких как кнопки, метки, поля редактирования и прямоугольные метки, определяющие их свойства (цвет, текст, шрифт и т. д.), до реализации в обработчиках событий для создания панелей и взаимодействий с пользователем.
Итак, в первой части показано успешное создание панели. Вторая часть вызовет эту панель к жизни, придав ей адаптивность. К примеру, мы заставим кнопки реагировать на нажатия, обновлять котировки на каждом тике, сделаем так, чтобы выпадающий список можно было перетаскивать, добавим эффекты наведения на кнопки и многое другое. Надеемся, эта статья оказалась для вас полезной при создании панелей графического интерфейса, а представленные здесь сведения можно использовать для создания более сложных и интересных панелей.
Перевод с английского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/en/articles/15205





- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования
Но можно и ещё проще удалять все свои графические объекты, если использовать префикс к их именам. Тогда это можно выполнить одной командой ObjectsDeleteAll(0, pref).
Префикс к объектам трейдов изменить нельзя. Но мысль правильная, все имена этих объектов начинаются одинаково. Так-что можно использовать начало имени объекта как префикс.
Конечно,
Конечно