Ресурсы

Использование графики и звука в программах на MQL5

Программы на MQL5 позволяют работать со звуковыми и графическими файлами:

 

PlaySound()

Пример вызова функции PlaySound():

//+------------------------------------------------------------------+
//| функция вызывает штатную OrderSend() и воспроизводит звук        |
//+------------------------------------------------------------------+
void OrderSendWithAudio(MqlTradeRequest  &request, MqlTradeResult &result)
  {
  //--- отправим запрос на сервер
   OrderSend(request,result);
   //--- если запрос принят, играем звук Ok.wav 
   if(result.retcode==TRADE_RETCODE_PLACED
      PlaySound("Ok.wav");
   //--- при неудаче выдаем тревожный звук из файла timeout.wav
   else 
      PlaySound("timeout.wav");
  }

В данном примере показано как проигрывать звуки из файлов Ok.wav и timeoit.wav, входящих в стандартную поставку терминала. Эти файлы находятся в папке каталог_терминала\Sounds. Здесь каталог_терминала означает папку, из которой запущен клиентский терминал MetaTrader 5.  Программным путем из mql5-программы каталог терминала можно узнать следующим образом:

//--- Папка, в которой хранятся данные терминала
   string terminal_path=TerminalInfoString(TERMINAL_PATH);

Можно использовать звуковые файлы не только из папки каталог_терминала\Sounds, но и из любой подпапки, находящейся в папке каталог_данных_терминала\MQL5. Расположение каталога данных терминала на компьютере можно выяснить через меню терминала "Файл"-"Открыть каталог данных" или программным путем:

//--- Папка, в которой хранятся данные терминала
   string terminal_data_path=TerminalInfoString(TERMINAL_DATA_PATH);

Например, если звуковой файл Demo.wav лежит в папке каталог_данных_терминала\MQL5\Files, то вызов PlaySound() должен быть записан таким образом:

//--- проиграем звуковой файл Demo.wav из папки каталог_данных_терминала\MQL5\Files\
   PlaySound("\\Files\\Demo.wav");

Обратите внимание на то, что в комментарии путь к файлу написан с использованием символа "\", а в самой функции для разделения папок в пути используется последовательность "\\".

При указании пути всегда используйте только двойную обратную косую черту в качестве разделителя, так как одиночная обратная черта является управляющим символом для компилятора при разборе константных строк и символьных констант в исходном коде программы.

Для остановки воспроизведения файла нужно вызвать функцию PlaySound() с параметром NULL:

//--- вызов PlaySound() с параметром NULL останавливает воспроизведение звука
   PlaySound(NULL);

 

ObjectCreate()

Пример эксперта, который с помощью функции  ObjectCreate() создает объект "Графическая метка" (OBJ_BITMAP_LABEL).

string label_name="currency_label";        // имя объекта OBJ_BITMAP_LABEL 
string euro      ="\\Images\\euro.bmp";    // путь к файлу каталог_данных_терминала\MQL5\Images\euro.bmp
string dollar    ="\\Images\\dollar.bmp";  // путь к файлу каталог_данных_терминала\MQL5\Images\dollar.bmp
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- создадим кнопку OBJ_BITMAP_LABEL, если ее еще нет
   if(ObjectFind(0,label_name)<0)
     {
      //--- попробуем создать объект OBJ_BITMAP_LABEL
      bool created=ObjectCreate(0,label_name,OBJ_BITMAP_LABEL,0,0,0);
      if(created)
        {
         //--- привяжем кнопку к правому верхнему углу графика
         ObjectSetInteger(0,label_name,OBJPROP_CORNER,CORNER_RIGHT_UPPER);
         //--- теперь настроим свойства объекта
         ObjectSetInteger(0,label_name,OBJPROP_XDISTANCE,100);
         ObjectSetInteger(0,label_name,OBJPROP_YDISTANCE,50);
         //--- сбросим код последней ошибки в 0
         ResetLastError();
         //--- загрузим картинку для состояния кнопки "Нажата"
         bool set=ObjectSetString(0,label_name,OBJPROP_BMPFILE,0,euro);
         //--- проверим результат
         if(!set)
           {
            PrintFormat("Не удалось загрузить картинку из файла %s. Код ошибки %d",euro,GetLastError());
           }
         ResetLastError();
         //--- загрузим картинку для состояния кнопки "Отжата"
         set=ObjectSetString(0,label_name,OBJPROP_BMPFILE,1,dollar);
         
         if(!set)
           {
            PrintFormat("Не удалось загрузить картинку из файла %s. Код ошибки %d",dollar,GetLastError());
           }
         //--- отдадим графику команду на обновление, чтобы кнопка появилась сразу же, не дожидаясь тика
         ChartRedraw(0);
        }
      else
        {
         //--- объект создать не удалось, сообщим об этом
         PrintFormat("Не удалось создать объект OBJ_BITMAP_LABEL. Код ошибки %d",GetLastError());
        }
     }
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- удалим объект с графика 
   ObjectDelete(0,label_name);
  }

Создание и настройка графического объекта с именем currency_label происходят в функции OnInit(). Пути к загружаемым графическим файлам заданы в глобальных переменных euro и dollar, в качестве разделителя использована двойная обратная косая черта:

string euro      ="\\Images\\euro.bmp";    // путь к файлу каталог_данных_терминала\MQL5\Images\euro.bmp
string dollar    ="\\Images\\dollar.bmp";  // путь к файлу каталог_данных_терминала\MQL5\Images\dollar.bmp

Сами файлы при этом находятся в папке каталог_данных_терминала\MQL5\Images.

Объект OBJ_BITMAP_LABEL фактически представляет собою кнопку, которая в зависимости от состояния (нажата или отжата) может отображать одну из двух картинок: euro.bmp или dollar.bmp.

Свойства объекта OBJ_BITMAP_LABEL

Размеры кнопки с графическим интерфейсом автоматически устанавливаются под размер отображаемой картинки. Смена изображения производится при нажатии левой кнопкой мышки на объекте OBJ_BITMAP_LABEL (в свойствах должна быть выбрана опция "Отключить выделение"). Объект OBJ_BITMAP создается аналогичным образом и предназначен для создания фона с требуемым рисунком.

Значение свойства OBJPROP_BMPFILE, которое отвечает за вид объектов OBJ_BITMAP и OBJ_BITMAP_LABEL, можно менять динамически. Это позволяет создавать разнообразные интерактивные пользовательские интерфейсы для mql5-программ.

 

Включение ресурсов в исполняемые файлы при компиляции mql5-программ #

Для работы mql5-программы может потребоваться множество разнообразных загружаемых ресурсов в виде файлов изображений и звуков. Для того чтобы исключить необходимость переноса всех этих файлов при передаче исполняемой программы на MQL5, следует использовать директиву компилятора #resource:

 #resource путь_к_файлу_ресурса

Команда #resource указывает компилятору, что ресурс по указанному пути путь_к_файлу_ресурса нужно включить в исполняемый файл EX5. Таким образом, все необходимые картинки и звуки можно поместить непосредственно в EX5-файл и для запуска программы в другом терминале не потребуется передавать все используемые в ней отдельные файлы. Любой EX5-файл может содержать ресурсы, и любая EX5-программа может использовать ресурсы из другой EX5-программы.

Файлы в формате BMP и WAV перед включением в исполняемый EX5 файл автоматически сжимаются. Это означает, что использование ресурсов не только позволяет создавать полноценные программы на MQL5, но и уменьшает общий размер требуемых терминалу файлов при использовании графики и звука по сравнению с обычным способом написания mql5-программ.

Размер файла ресурса не может быть больше 128 Mb.

 

Поиск указанных ресурсов компилятором

Ресурс вставляется командой #resource "<путь к файлу ресурса>"

 #resource "<путь_к_файлу_ресурса>"

Длина константной строки <путь_к_файлу_ресурса> не должна превышать 63 символа.

Компилятор ищет ресурс по указанному пути в следующей последовательности:

  • если в начале пути стоит разделитель обратная косая черта "\" (пишется "\\"), то ресурс ищется относительно каталога каталог_данных_терминала\MQL5\,
  • если обратной косой черты нет, то ресурс ищется относительно расположения исходного файла, в котором этот ресурс прописывается.

В пути ресурса недопустимо  использовать подстроки "..\\" и ":\\".

Примеры включения ресурсов:

//--- правильное указание ресурсов
#resource "\\Images\\euro.bmp" // euro.bmp находится в каталог_данных_терминала\MQL5\Images\
#resource "picture.bmp"        // picture.bmp находится в том же каталоге, где и исходный файл
#resource "Resource\\map.bmp"  // ресурс находится в папке каталог_исходного_файла\Resource\map.bmp
 
//--- неправильное указание ресурсов
#resource ":picture_2.bmp"     // нельзя использовать ":"
#resource "..\\picture_3.bmp"  // нельзя использовать ".."
#resource "\\Files\\Images\\Folder_First\\My_panel\\Labels\\too_long_path.bmp" //больше 63 символов

 

Использование ресурсов

Имя ресурса

После того как ресурс объявлен директивой #resource, его можно использовать в любой части программы. Именем ресурса становится его путь без косой черты в начале строки, задающей путь к ресурсу. Для использования своего ресурса в коде нужно перед именем ресурса добавлять специальный признак "::".

Примеры:

//--- примеры указания ресурсов и их имена в комментарии
#resource "\\Images\\euro.bmp"          // имя ресурса - Images\euro.bmp
#resource "picture.bmp"                 // имя ресурса - picture.bmp
#resource "Resource\\map.bmp"           // имя ресурса - Resource\map.bmp
#resource "\\Files\\Pictures\\good.bmp" // имя ресурса - Files\Pictures\good.bmp
#resource "\\Files\\Demo.wav";          // имя ресурса - Files\Demo.wav"
#resource "\\Sounds\\thrill.wav";       // имя ресурса - Sounds\thrill.wav"
...                                  
 
//--- использование ресурсов
ObjectSetString(0,bitmap_name,OBJPROP_BMPFILE,0,"::Images\\euro.bmp");
...
ObjectSetString(0,my_bitmap,OBJPROP_BMPFILE,0,"::picture.bmp");
...
set=ObjectSetString(0,bitmap_label,OBJPROP_BMPFILE,1,"::Files\\Pictures\\good.bmp");
...
PlaySound("::Files\\Demo.wav");
...
PlaySound("::Sounds\\thrill.wav");

Необходимо отметить, что при установке объектам OBJ_BITMAP и OBJ_BITMAP_LABEL изображения из ресурса, значение свойства OBJPROP_BMPFILE уже нельзя менять вручную. Например, пусть мы используем для создания OBJ_BITMAP_LABEL ресурсы файлов euro.bmp и dollar.bmp.

#resource "\\Images\\euro.bmp";    // euro.bmp находится в каталог_данных_терминала\MQL5\Images\
#resource "\\Images\\dollar.bmp";  // dollar.bmp находится в каталог_данных_терминала\MQL5\Images\

Тогда при просмотре свойств этого объекта мы увидим, что свойства BitMap File (On) и BitMap File (Off) имеют серый цвет и недоступны для изменения вручную:

using_resource

 

Использование ресурсов других mql5-программ

Использование ресурсов имеет и другое преимущество – в любой mql5-программе можно использовать ресурсы из любого файла EX5. Таким образом, ресурсы из одного файла EX5 можно использовать во многих других mql5-программах.

Для того чтобы использовать имя ресурса из стороннего файла, его нужно указать в виде <путь_имя_файла_EX5>::<имя_ресурса>. Например, пусть в скрипте Draw_Triangles_Script.mq5 указан ресурс на картинку в файле triangle.bmp:

 #resource "\\Files\\triangle.bmp"

Тогда его имя для использования в самом скрипте будет выглядеть как "Files\triangle.bmp", а для использования к имени ресурса нужно добавить  специальный признак "::".

//--- использование ресурса в самом скрипте
ObjectSetString(0,my_bitmap_name,OBJPROP_BMPFILE,0,"::Files\\triangle.bmp");

Чтобы использовать этот же ресурс из другой программы, например, эксперта, нужно к имени ресурса дополнительно добавить путь к EX5-файлу относительно папки каталог_данных_терминала\MQL5\ и имя EX5-файла этого скрипта - Draw_Triangles_Script.ex5. Пусть скрипт лежит в стандартной папке каталог_данных_терминала\MQL5\Scripts\, тогда вызов нужно написать таким образом:

//--- использование ресурса скрипта в эксперте
ObjectSetString(0,my_bitmap_name,OBJPROP_BMPFILE,0,"\\Scripts\\Draw_Triangles_Script.ex5::Files\\triangle.bmp");

Если при обращении к ресурсу в другом EX5-файле не указать путь к этому исполняемому файлу, то исполняемый файл ищется в той же папке, где находится и обратившаяся за ресурсом программа. Это означает, что если в советнике запрашивается ресурс из файла Draw_Triangles_Script.ex5 без указания пути, например, так:

//--- запрос ресурса скрипта в эксперте без указания пути
ObjectSetString(0,my_bitmap_name,OBJPROP_BMPFILE,0,"Draw_Triangles_Script.ex5::Files\\triangle.bmp");

то файл будет искаться в папке каталог_данных_терминала\MQL5\Experts\, если сам советник находится в папке каталог_данных_терминала\MQL5\Experts\.

 

Работа с пользовательскими индикаторами, подключенными в качестве ресурсов

Для работы mql5-программ может потребоваться один или несколько пользовательских индикаторов, все они могут быть включены в код исполняемой mql5-программы. Включение индикаторов в качестве ресурсов позволяет упростить распространение программ.

Пример подключения и использования пользовательского индикатора SampleIndicator.ex5, расположенного в папке: каталог_данных_терминала\MQL5\Indicators\:

//+------------------------------------------------------------------+
//|                                                     SampleEA.mq5 |
//|                        Copyright 2013, MetaQuotes Software Corp. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#resource "\\Indicators\\SampleIndicator.ex5"
int handle_ind;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   handle_ind=iCustom(_Symbol,_Period,"::Indicators\\SampleIndicator.ex5");
   if(handle_ind==INVALID_HANDLE)
     {
      Print("Expert: iCustom call: Error code=",GetLastError());
      return(INIT_FAILED);
     }
//---
   return(INIT_SUCCEEDED);
  }

Случай, когда пользовательский индикатор в функции OnInit() создает одну или несколько копий себя, требует отдельного рассмотрения. Напомним, что для использования ресурса из mql5-программы его необходимо указывать в виде: <путь_имя_файла_EX5>::<имя_ресурса>.

Например, если индикатор SampleIndicator.ex5 включается в советник SampleEA.ex5 в качестве ресурса, то путь к самому себе, указанный при вызове iCustom() в функции инициализации пользовательского индикатора, будет выглядеть следующим образом: "\\Experts\\SampleEA.ex5::Indicators\\SampleIndicator.ex5". При явном указании данного пути пользовательский индикатор SampleIndicator.ex5 будет жестко привязан к советнику SampleEA.ex5 и теряет способность самостоятельной работы.

Путь до самого себя можно получить при помощи функции GetRelativeProgramPath(), пример использования которой приведен ниже:

//+------------------------------------------------------------------+
//|                                              SampleIndicator.mq5 |
//|                        Copyright 2013, MetaQuotes Software Corp. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property indicator_separate_window
#property indicator_plots 0
int handle;
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- неправильный способ указания ссылки на себя
//--- string path="\\Experts\\SampleEA.ex5::Indicators\\SampleIndicator.ex5";   
//--- правильный способ получения ссылки на себя
  string path=GetRelativeProgramPath();
//--- indicator buffers mapping
   handle=iCustom(_Symbol,_Period,path,0,0);
   if(handle==INVALID_HANDLE)
     {
      Print("Indicator: iCustom call: Error code=",GetLastError());
      return(INIT_FAILED);
     }
   else Print("Indicator handle=",handle);
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| GetRelativeProgramPath                                           |
//+------------------------------------------------------------------+
string GetRelativeProgramPath()
  {
   int pos2;
//--- получаем абсолютный путь к программе
   string path=MQLInfoString(MQL_PROGRAM_PATH);
//--- находим позицию подстроки "\MQL5\"
   int    pos =StringFind(path,"\\MQL5\\");
//--- подстрока не найдена - ошибка
   if(pos<0)
      return(NULL);
//--- пропускаем каталог "\MQL5"
   pos+=5;
//--- пропускаем лишние '\'
   while(StringGetCharacter(path,pos+1)=='\\')
      pos++;
//--- если это ресурс, возвращаем путь относительно MQL5-каталога
   if(StringFind(path,"::",pos)>=0)
      return(StringSubstr(path,pos));
//--- найдем разделитель для первого подкаталога в MQL5 (например, MQL5\Indicators)
//--- если его нет, то вернем путь относительно MQL5-каталога
   if((pos2=StringFind(path,"\\",pos+1))<0)
      return(StringSubstr(path,pos));
//--- вернем путь относительно подкаталога (например, MQL5\Indicators)
   return(StringSubstr(path,pos2+1));
  }
//+------------------------------------------------------------------+
//| 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);
  }

 

Ресурсные переменные #

Ресурсы можно объявлять с помощью ресурсных переменных и обращаться с ними так, как будто они являются переменной соответствующего типа. Формат объявления:

#resource путь_к_файлу_ресурса as тип_ресурсной_переменной имя_ресурсной_переменной

Примеры объявлений:

#resource "data.bin" as int ExtData[]             // объявление массива числового типа, содержащего данные из файла data.bin
#resource "data.bin" as MqlRates ExtData[]        // объявление массива простых структур, содержащего данные из файла data.bin
//--- строки
#resource "data.txt" as string ExtCode            // объявление строки, содержащей данные файла data.txt (поддерживаются кодировки ANSI, UTF-8 и UTF-16)
//--- графические ресурсы
#resource "image.bmp" as bitmap ExtBitmap[]       // объявление одномерного массива, содержащего в себе растр из файла BMP, размер массива = height * width
#resource "image.bmp" as bitmap ExtBitmap2[][]    // объявление двумерного массива, содержащего в себе растр из файла BMP, рамер массива [height][width]

При таком объявлении к данным ресурса можно адресоваться только через переменную, автоматическая адресация через "::<rsource name>" не работает.

#resource "\\Images\\euro.bmp" as bitmap euro[][]
#resource "\\Images\\dollar.bmp"
//+------------------------------------------------------------------+
//|  Функция создания объекта OBJ_BITMAP_LABEL с помощью ресурса     |
//+------------------------------------------------------------------+
void Image(string name,string rc,int x,int y)
  {
   ObjectCreate(0,name,OBJ_BITMAP_LABEL,0,0,0);
   ObjectSetInteger(0,name,OBJPROP_XDISTANCE,x);
   ObjectSetInteger(0,name,OBJPROP_YDISTANCE,y);
   ObjectSetString(0,name,OBJPROP_BMPFILE,rc);
  }
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- выведем размеры картинки [width, height], которая хранится в ресурсной переменной euro
   Print(ArrayRange(euro,1),", ",ArrayRange(euro,0));
//--- изменим картинку в euro - нарисуем красную горизонтальную полосу посередине
   for(int x=0;x<ArrayRange(euro,1);x++)
      euro[ArrayRange(euro,1)/2][x]=0xFFFF0000;
//--- создадим графический ресурс с помощью ресурсной переменной
   ResourceCreate("euro_icon",euro,ArrayRange(euro,1),ArrayRange(euro,0),0,0,ArrayRange(euro,1),COLOR_FORMAT_ARGB_NORMALIZE);
//--- создадим объект графическая метка Euro, которому выставим картинку из ресурса euro_icon
   Image("Euro","::euro_icon",10,40);
//--- другой способ использования ресурса, рисовать в него мы не можем
   Image("USD","::Images\\dollar.bmp",15+ArrayRange(euro,1),40);
//--- прямой способ адресации к ресурсу euro.bmp недоступен, так как он уже объявлен через ресурсную переменную euro
   Image("E2","::Images\\euro.bmp",20+ArrayRange(euro,1)*2,40); // произойдет ошибка времени выполнения
  }

Результат выполнения скрипта – созданы только два объекта OBJ_BITMAP_LABEL из трех. При этом на изображении первого объекта мы видим красную полоску посредине.

res_variables

Важным преимуществом использования ресурсов является то, что ресурсные файлы перед включением в исполняемый EX5-файл перед компиляцией автоматически сжимаются. Таким образом, использование ресурсных переменных позволяет не только упаковывать необходимые для работы данные прямо в исполняемый EX5-файл, но и сокращает количество и общий размер файлов по сравнению с обычным способом написания mql5-программ.

Использование ресурсных переменных особенно удобно для публикации продуктов в Маркете.

Особенности

  • Специальный тип ресурсной переменной bitmap указывает компилятору, что ресурс является графическим изображением. Такие переменные получают тип uint.
  • Ресурсная переменная-массив типа bitmap может иметь две размерности, в этом случае размер массива будет установлен как [высота_картинки ][ ширина_картинки ]. В случае если указан массив одной размерности, то количество элементов будет равно произведению высота_картинки*ширина_картинки.
  • При загрузке 24 битного изображения для всех пикселей изображения компонента альфа-канала устанавливается в значение 255.
  • При загрузке 32 битного изображения без альфа-канала, также, для всех пикселей изображения компонента альфаканала устанавливается в значение 255.
  • При загрузке 32 битного изображения c альфа-каналом никаких манипуляций с пикселями не происходит.
  • Размер файла ресурса не может быть больше 128 Mb.
  • Для строковых файлов производится автоматическое определение кодировки по наличию BOM (заголовку). Если BOM отсутствует, то кодировка определяется по содержимому файла. Поддерживаются файлы в кодировке ANSI, UTF-8 и UTF-16. При чтении данных из файлов все строки переводятся в Unicode.

Программы на OpenCL

Использование ресурсных строковых переменных может существенно облегчить написание некоторых программ. Например, вы можете написать код OpenCL-программы в отдельном CL-файле, а затем включить этот файл в виде строки в ресурсы вашей MQL5-программы.

#resource "seascape.cl" as string cl_program
...
int context;
if((cl_program=CLProgramCreate(context,cl_program)!=INVALID_HANDLE)
  {
   //--- выполняем дальнейшие действия с OpenCL программой
  }

В данном примере без использования ресурсной переменной cl_program вам пришлось бы описывать весь код в виде одной большой строковой переменной.

Смотри также

ResourceCreate(), ResourceSave(), PlaySound(), ObjectSetInteger(), ChartApplyTemplate(), Файловые операции