Biblioteca para el desarrollo rápido y sencillo de programas para MetaTrader (Parte XX): Creación y guardado de los recursos del programa
Contenido
- Concepto
- Clase de generador de archivos
- Clase de colección de recursos de programa
- Simulación del acceso a los archivos creados automáticamente
- ¿Qué es lo próximo?
Concepto
Con frecuencia, al crear un programa, necesitamos usar sonidos e imágenes. En el lenguaje MQL existen diversas posibilidades de uso de estos
datos, y todas ellas se relacionan con la necesidad de
cargar los archivos desde el sandbox de archivos del terminal. Si, como
resultado final, planeamos compilar el archivo, bastará con
añadir el archivo como recurso y librarse de la necesidad de
transmitir archivos adicionales para el funcionamiento del programa. Este método resulta bastante adecuado para la ubicación de
programas en el
Mercado mql5.com, dado que necesitamos ubicar solo el archivo ejecutable, pero no conviene
en absoluto para ubicar el código fuente en
CodeBase mql5.com, puesto que los *.wav de audio y los archivos gráficos en el formato *.bmp no
se pueden colocar allí. Y sin ellos, el código fuente no está completo.
¿Qué podemos hacer en semejante situación? La conclusión es obvia: guardar todos los archivos necesarios en código fuente en archivos
*.mqh de inclusión en forma de matrices binarias. Y después, al inicio del propio programa, crear a partir de los conjuntos de datos ya
disponibles todos los archivos imprescindibles para el funcionamiento del programa en las carpetas necesarias. De esta forma, al iniciar
solo el código del programa, este mismo creará todos los archivos necesarios para el trabajo, colocándolos en sus carpetas
correspondientes, y posibilitando su correcto funcionamiento. Para el usuario del programa, este proceso transcurrirá de forma
prácticamente imperceptible: apenas notará que el tiempo de inicio es ligeramente mayor. Este se invierte en la creación y el guardado de
los archivos que aún no existen.
Para trabajar con los datos de los archivos, hemos creado dos clases:
- una clase generadora de archivos a partir de los datos anteriormente preparados,
- un clase para trabajar con la lista de archivos creados: la colección de objetos descriptores de archivos.
La clase para trabajar con los archivos ya creados tendrá entre sus componentes una lista con todos los archivos creados y los métodos de acceso a estos archivos según los datos guardados en la lista. En esencia, en la lista se guardarán las descripciones simples de los archivos creados (nombre y descripción del archivo), y ya según estos datos podremos obtener la ruta hasta el archivo físico para trabajar con el mismo.
Clase de generador de archivos
Comenzaremos con el archivo Datas.mqh. Vamos a añadir al mismo los mensajes necesarios, que se mostrarán al trabajar con las clases creadas.
Añadimos a la enumeración de los índices de los mensajes de texto las constantes que indican la ubicación de los nuevos mensajes en la matriz de los datos de texto de la biblioteca:
//+------------------------------------------------------------------+ //| List of the library's text message indices | //+------------------------------------------------------------------+ enum ENUM_MESSAGES_LIB { MSG_LIB_PARAMS_LIST_BEG=ERR_USER_ERROR_FIRST, // Beginning of the parameter list MSG_LIB_PARAMS_LIST_END, // End of the parameter list MSG_LIB_PROP_NOT_SUPPORTED, // Property not supported MSG_LIB_PROP_NOT_SUPPORTED_MQL4, // Property not supported in MQL4 MSG_LIB_PROP_NOT_SUPPORTED_POSITION, // Property not supported for position MSG_LIB_PROP_NOT_SUPPORTED_PENDING, // Property not supported for pending order MSG_LIB_PROP_NOT_SUPPORTED_MARKET, // Property not supported for market order MSG_LIB_PROP_NOT_SUPPORTED_MARKET_HIST, // Property not supported for historical market order MSG_LIB_PROP_NOT_SET, // Value not set MSG_LIB_PROP_EMPTY, // Not set MSG_LIB_SYS_ERROR, // Error MSG_LIB_SYS_NOT_SYMBOL_ON_SERVER, // Error. No such symbol on server MSG_LIB_SYS_FAILED_PUT_SYMBOL, // Failed to place to market watch. Error: MSG_LIB_SYS_NOT_GET_PRICE, // Failed to get current prices. Error: MSG_LIB_SYS_NOT_GET_MARGIN_RATES, // Failed to get margin ratios. Error: MSG_LIB_SYS_NOT_GET_DATAS, // Failed to get data MSG_LIB_SYS_FAILED_CREATE_STORAGE_FOLDER, // Failed to create folder for storing files. Error: MSG_LIB_SYS_FAILED_ADD_ACC_OBJ_TO_LIST, // Error. Failed to add current account object to collection list MSG_LIB_SYS_FAILED_CREATE_CURR_ACC_OBJ, // Error. Failed to create account object with current account data MSG_LIB_SYS_FAILED_OPEN_FILE_FOR_WRITE, // Could not open file for writing MSG_LIB_SYS_INPUT_ERROR_NO_SYMBOL, // Input error: no symbol MSG_LIB_SYS_FAILED_CREATE_SYM_OBJ, // Failed to create symbol object MSG_LIB_SYS_FAILED_ADD_SYM_OBJ, // Failed to add symbol MSG_LIB_SYS_NOT_GET_CURR_PRICES, // Failed to get current prices by event symbol MSG_LIB_SYS_EVENT_ALREADY_IN_LIST, // This event is already in the list MSG_LIB_SYS_FILE_RES_ALREADY_IN_LIST, // This file already created and added to list: MSG_LIB_SYS_FAILED_CREATE_RES_LINK, // Error. Failed to create object pointing to resource file MSG_LIB_SYS_ERROR_ALREADY_CREATED_COUNTER, // Error. Counter with ID already created MSG_LIB_SYS_FAILED_CREATE_COUNTER, // Failed to create timer counter MSG_LIB_SYS_FAILED_CREATE_TEMP_LIST, // Error creating temporary list MSG_LIB_SYS_ERROR_NOT_MARKET_LIST, // Error. This is not a market collection list MSG_LIB_SYS_ERROR_NOT_HISTORY_LIST, // Error. This is not a history collection list MSG_LIB_SYS_FAILED_ADD_ORDER_TO_LIST, // Could not add order to the list MSG_LIB_SYS_FAILED_ADD_DEAL_TO_LIST, // Could not add deal to the list MSG_LIB_SYS_FAILED_ADD_CTRL_ORDER_TO_LIST, // Failed to add control order MSG_LIB_SYS_FAILED_ADD_CTRL_POSITION_TO_LIST, // Failed to add control position MSG_LIB_SYS_FAILED_ADD_MODIFIED_ORD_TO_LIST, // Could not add modified order to the list of modified orders MSG_LIB_SYS_NO_TICKS_YET, // No ticks yet MSG_LIB_SYS_FAILED_CREATE_OBJ_STRUCT, // Could not create object structure MSG_LIB_SYS_FAILED_WRITE_UARRAY_TO_FILE, // Could not write uchar array to file MSG_LIB_SYS_FAILED_LOAD_UARRAY_FROM_FILE, // Could not load uchar array from file MSG_LIB_SYS_FAILED_CREATE_OBJ_STRUCT_FROM_UARRAY, // Could not create object structure from uchar array MSG_LIB_SYS_FAILED_SAVE_OBJ_STRUCT_TO_UARRAY, // Failed to save object structure to uchar array, error MSG_LIB_SYS_ERROR_INDEX, // Error. "index" value should be within 0 - 3 MSG_LIB_SYS_ERROR_FAILED_CONV_TO_LOWERCASE, // Failed to convert string to lowercase, error MSG_LIB_SYS_ERROR_EMPTY_STRING, // Error. Predefined symbols string empty, to be used MSG_LIB_SYS_FAILED_PREPARING_SYMBOLS_ARRAY, // Failed to prepare array of used symbols. Error MSG_LIB_SYS_INVALID_ORDER_TYPE, // Invalid order type:
Y añadimos a la matriz de mensajes de texto los índices de texto en ruso e inglés correspondientes a las constantes:
//+------------------------------------------------------------------+ string messages_library[][TOTAL_LANG]= { {"Начало списка параметров","The beginning of the event parameter list"}, {"Конец списка параметров","End of the parameter list"}, {"Свойство не поддерживается","Property is not support"}, {"Свойство не поддерживается в MQL4","Property is not supported in MQL4"}, {"Свойство не поддерживается у позиции","Property not supported for position"}, {"Свойство не поддерживается у отложенного ордера","The property is not supported for a pending order"}, {"Свойство не поддерживается у маркет-ордера","The property is not supported for a market-order"}, {"Свойство не поддерживается у исторического маркет-ордера","The property is not supported for a history market-order"}, {"Значение не задано","Value not set"}, {"Отсутствует","Not set"}, {"Ошибка ","Error "}, {"Ошибка. Такого символа нет на сервере","Error. There is no such symbol on the server"}, {"Не удалось поместить в обзор рынка. Ошибка: ","Failed to put in the market watch. Error: "}, {"Не удалось получить текущие цены. Ошибка: ","Could not get current prices. Error: "}, {"Не удалось получить коэффициенты взимания маржи. Ошибка: ","Failed to get margin rates. Error: "}, {"Не удалось получить данные ","Failed to get data of "}, {"Не удалось создать папку хранения файлов. Ошибка: ","Could not create file storage folder. Error: "}, {"Ошибка. Не удалось добавить текущий объект-аккаунт в список-коллекцию","Error. Failed to add current account object to collection list"}, {"Ошибка. Не удалось создать объект-аккаунт с данными текущего счёта","Error. Failed to create an account object with current account data"}, {"Не удалось открыть для записи файл ","Could not open file for writing: "}, {"Ошибка входных данных: нет символа ","Input error: no "}, {"Не удалось создать объект-символ ","Failed to create symbol object "}, {"Не удалось добавить символ ","Failed to add "}, {"Не удалось получить текущие цены по символу события ","Failed to get current prices by event symbol "}, {"Такое событие уже есть в списке","This event is already in the list"}, {"Такой файл уже создан и добавлен в список: ","This file has already been created and added to the list: "}, {"Ошибка. Не удалось создать объект-указатель на файл ресурса","Error. Failed to create resource file link object"}, {"Ошибка. Уже создан счётчик с идентификатором ","Error. Already created a counter with id "}, {"Не удалось создать счётчик таймера ","Failed to create timer counter "}, {"Ошибка создания временного списка","Error creating temporary list"}, {"Ошибка. Список не является списком рыночной коллекции","Error. The list is not a list of the market collection"}, {"Ошибка. Список не является списком исторической коллекции","Error. The list is not a list of the history collection"}, {"Не удалось добавить ордер в список","Could not add order to the list"}, {"Не удалось добавить сделку в список","Could not add deal to the list"}, {"Не удалось добавить контрольный ордер ","Failed to add a control order "}, {"Не удалось добавить контрольую позицию ","Failed to add a control position "}, {"Не удалось добавить модифицированный ордер в список изменённых ордеров","Could not add modified order to the list of modified orders"}, {"Ещё не было тиков","No ticks yet"}, {"Не удалось создать структуру объекта","Could not create object structure"}, {"Не удалось записать uchar-массив в файл","Could not write uchar array to file"}, {"Не удалось загрузить uchar-массив из файла","Could not load uchar array from file"}, {"Не удалось создать структуру объекта из uchar-массива","Could not create object structure from uchar array"}, {"Не удалось сохранить структуру объекта в uchar-массив, ошибка ","Failed to save object structure to uchar array, error "}, {"Ошибка. Значение \"index\" должно быть в пределах 0 - 3","Error. The \"index\" value must be between 0 - 3"}, {"Не удалось преобразовать строку в нижний регистр, ошибка ","Failed to convert string to lowercase, error "}, {"Ошибка. Строка предопределённых символов пустая, будет использоваться ","Error. String of predefined symbols is empty, the symbol will be used: "}, {"Не удалось подготовить массив используемых символов. Ошибка ","Failed to create an array of used characters. Error "}, {"Не правильный тип ордера: ","Invalid order type: "},
Conviene prestar atención a que la secuencia de ubicación de los textos en la matriz deberá
corresponderse exactamente con la secuencia de declaración de las constantes de los índices en la enumeración.
Para trabajar con la clase generadora de archivos, deberemos crear una base de datos desde la que la clase tomará los datos sobre los archivos de audio e imagen. Dichos datos deberán ser matrices unsigned char. Para crearlas, deberemos disponer de archivos de audio (*.wav) y bitmap (*.bmp), que guardaremos en el código fuente de la biblioteca. A modp de ejemplo, ya hemos preparado varios datos de prueba. Los datos de sonido y bitmap se guardarán en archivos de inclusión por separado.
Vamos a crear en el directorio raíz de la biblioteca \MQL5\Include\DoEasy\ el archivo de inclusión DataSND.mqh, añadiendo de inmediato los nombres de las matrices de los archivos de audio que ubicaremos en las matrices (por cierto, podemos terminar de escribir los nombres de las matrices de datos más tarde, estos son solo necesarios para la búsqueda rápida del lugar donde se declara la matriz con los datos de este o aquel archivo en el listado, dado que las matrices pueden ser grandes, pero no mayores a 16 megabytes):
//+------------------------------------------------------------------+ //| DataSND.mqh | //| Copyright 2019, MetaQuotes Software Corp. | //| https://mql5.com/en/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2019, MetaQuotes Software Corp." #property link "https://mql5.com/en/users/artmedia70" //+------------------------------------------------------------------+ //| Audio | //+------------------------------------------------------------------+ /* sound_array_coin_01 // Falling coin 1 sound_array_coin_02 // Falling coins sound_array_coin_03 // Coins sound_array_coin_04 // Falling coin 2 //--- sound_array_click_01 // Button click 1 sound_array_click_02 // Button click 2 sound_array_click_03 // Button click 3 //--- sound_array_cash_machine_01 // Cash register 1 */ //+------------------------------------------------------------------+
Para insertar un archivo en el código fuente de un programa, podemos utilizar el punto del menú "Editar --> Insertar --> Archivo como matriz binaria":
Después de elegir dicho punto, se abrirá la ventana de selección del archivo, en la que deberemos encontrar el archivo anteriormente preparado para cargar sus datos en la matriz. La matriz se creará automáticamente, usando como base el nombre del archivo elegido (el ejemplo no está completo, dado que hay muchos datos binarios):
//+------------------------------------------------------------------+ //| DataSND.mqh | //| Copyright 2019, MetaQuotes Software Corp. | //| https://mql5.com/en/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2019, MetaQuotes Software Corp." #property link "https://mql5.com/en/users/artmedia70" //+------------------------------------------------------------------+ //| Audio | //+------------------------------------------------------------------+ /* sound_array_coin_01 // Falling coin 1 sound_array_coin_02 // Falling coins sound_array_coin_03 // Coins sound_array_coin_04 // Falling coin 2 //--- sound_array_click_01 // Button click 1 sound_array_click_02 // Button click 2 sound_array_click_03 // Button click 3 //--- sound_array_cash_machine_01 // Cash register 1 */ //+------------------------------------------------------------------+ //| Falling coin 01 | //+------------------------------------------------------------------+ unsigned char sound_array_coin_01[]= { 0x52,0x49,0x46,0x46,0x1E,0x50,0x09,0x00,0x57,0x41,0x56,0x45,0x66,0x6D,0x74,0x20, 0x12,0x00,0x00,0x00,0x03,0x00,0x02,0x00,0x44,0xAC,0x00,0x00,0x20,0x62,0x05,0x00, 0x08,0x00,0x20,0x00,0x00,0x00,0x64,0x61,0x74,0x61,0x28,0x35,0x09,0x00,0x11,0xBA, 0x20,0xBB,0x11,0xBA,0x20,0xBB,0x74,0x81,0x62,0xBB,0x74,0x81,0x62,0xBB,0xE9,0x37, 0x4D,0xBB,0xE9,0x37,0x4D,0xBB,0x7C,0x41,0x13,0xBB,0x7C,0x41,0x13,0xBB,0x8F,0xE4, 0x84,0xBA,0x8F,0xE4,0x84,0xBA,0x51,0x6D,0x05,0x3A,0x51,0x6D,0x05,0x3A,0xE1,0xC7, 0x85,0x3A,0xE1,0xC7,0x85,0x3A,0xF5,0xA9,0xA1,0x39,0xF5,0xA9,0xA1,0x39,0xD6,0xF7, 0x25,0xBA,0xD6,0xF7,0x25,0xBA,0x88,0x38,0x5B,0xBA,0x88,0x38,0x5B,0xBA,0xC8,0x31, 0x05,0x39,0xC8,0x31,0x05,0x39,0x62,0xA0,0xF8,0x39,0x62,0xA0,0xF8,0x39,0x62,0xE5, 0x39,0xBA,0x62,0xE5,0x39,0xBA,0x8A,0xAA,0x8B,0xBA,0x8A,0xAA,0x8B,0xBA,0xDC,0xF9, 0x0B,0xBA,0xDC,0xF9,0x0B,0xBA,0xEA,0x27,0x97,0xBA,0xEA,0x27,0x97,0xBA,0xA1,0x5E, 0xDA,0xBA,0xA1,0x5E,0xDA,0xBA,0x96,0x5B,0x56,0xBA,0x96,0x5B,0x56,0xBA,0x13,0xB9, 0xA6,0xB8,0x13,0xB9,0xA6,0xB8,0x3B,0xFD,0x39,0xB7,0x3B,0xFD,0x39,0xB7,0x05,0x39, 0x37,0xB9,0x05,0x39,0x37,0xB9,0x7D,0xED,0x18,0xBA,0x7D,0xED,0x18,0xBA,0x73,0xDD, 0x8A,0xBA,0x73,0xDD,0x8A,0xBA,0xD1,0x83,0xD4,0xBA,0xD1,0x83,0xD4,0xBA,0xFF,0x94, 0x0C,0xBB,0xFF,0x94,0x0C,0xBB,0xAF,0x13,0xF6,0xBA,0xAF,0x13,0xF6,0xBA,0x5A,0x27, 0x4A,0xBA,0x5A,0x27,0x4A,0xBA,0x68,0xA7,0xA1,0x39,0x68,0xA7,0xA1,0x39,0xBE,0x9B, 0x32,0x3A,0xBE,0x9B,0x32,0x3A,0xFD,0x21,0x0C,0x3A,0xFD,0x21,0x0C,0x3A,0x36,0xFF, 0x21,0x3A,0x36,0xFF,0x21,0x3A,0xB5,0xA2,0x36,0x3A,0xB5,0xA2,0x36,0x3A,0xBB,0x73, 0xE2,0xB9,0xBB,0x73,0xE2,0xB9,0x16,0xDA,0x16,0xBB,0x16,0xDA,0x16,0xBB,0x41,0x70, 0x23,0xBB,0x41,0x70,0x23,0xBB,0xA9,0xC6,0x34,0xBA,0xA9,0xC6,0x34,0xBA,0x78,0x88, 0x35,0x37,0x78,0x88,0x35,0x37,0xFB,0x4C,0xA9,0xBA,0xFB,0x4C,0xA9,0xBA,0xDA,0xAA,
Dado que los datos de la matriz repiten completamente los datos del archivo, las matrices resultantes son bastante grandes. Es por eso que
hemos añadido de antemano los nombres de las matrices creadas para saltar rápidamente al comienzo de cada una en la lista usando
Ctrl+F.
Ahora solo queda añadir al listado de este archivo el número requerido de matrices de datos de audio. Ya hemos creado varios sonidos de prueba. Dado que el archivo resultante es grande, no tiene sentido mostrar su listado aquí. El lector podrá encontrarlo en los archivos de la biblioteca adjuntos a continuación.
Creamos exactamente de la misma forma un archivo con los datos bitmap llamado DataIMG.mqh. En él también hemos introducido dos matrices con la imagen de una bombilla LED de dos colores: una matriz contiene los datos con la imagen del LED verde, y la otra, los datos con la imagen del LED rojo:
//+------------------------------------------------------------------+ //| DataIMG.mqh | //| Copyright 2019, MetaQuotes Software Corp. | //| https://mql5.com/en/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2019, MetaQuotes Software Corp." #property link "https://mql5.com/en/users/artmedia70" //+------------------------------------------------------------------+ //| Images | //+------------------------------------------------------------------+ /* img_array_spot_green // Green LED 16x16, 32 bit img_array_spot_red // Red LED 16x16, 32 bit */ //+------------------------------------------------------------------+ //| Green LED 32 bit, alpha | //+------------------------------------------------------------------+ unsigned char img_array_spot_green[]= { 0x42,0x4D,0x38,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x36,0x00,0x00,0x00,0x28,0x00, 0x00,0x00,0x10,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x01,0x00,0x20,0x00,0x00,0x00, 0x00,0x00,0x02,0x04,0x00,0x00,0xC3,0x0E,0x00,0x00,0xC3,0x0E,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF, 0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF, 0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF, 0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF, 0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF, 0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,
...
0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF, 0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF, 0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF, 0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF, 0xFF,0x00,0xFF,0xFF,0xFF,0x00,0x00,0x00 }; //+------------------------------------------------------------------+ //| Red LED 32 bit, alpha | //+------------------------------------------------------------------+ unsigned char img_array_spot_red[]= { 0x42,0x4D,0x38,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x36,0x00,0x00,0x00,0x28,0x00, 0x00,0x00,0x10,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x01,0x00,0x20,0x00,0x00,0x00, 0x00,0x00,0x02,0x04,0x00,0x00,0xC3,0x0E,0x00,0x00,0xC3,0x0E,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF, 0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF, 0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF, 0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF, 0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,
Y como ya sucedió en el ejemplo con los datos de audio, no vamos a mostrar el listado completo del archivo obtenido.
Para que los datos binarios de los archivos estén disponibles en la biblioteca, incluiremos los archivos con los datos en el archivo Defines.mqh:
//+------------------------------------------------------------------+ //| Defines.mqh | //| Copyright 2018, MetaQuotes Software Corp. | //| https://mql5.com/es/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2018, MetaQuotes Software Corp." #property link "https://mql5.com/es/users/artmedia70" //+------------------------------------------------------------------+ //| Archivos de inclusión | //+------------------------------------------------------------------+ #include "DataSND.mqh" #include "DataIMG.mqh" #include "Datas.mqh" #ifdef __MQL4__ #include "ToMQL4.mqh" #endif //+------------------------------------------------------------------+
En el bloque de macrosustituciones del archivo Defines.mqh, añadimos una macrosustitución en la que se indiquen las carpetas de ubicación de los datos de recurso de la biblioteca:
//+------------------------------------------------------------------+ //| Macro substitutions | //+------------------------------------------------------------------+ //--- Describe the function with the error line number #define DFUN_ERR_LINE (__FUNCTION__+(TerminalInfoString(TERMINAL_LANGUAGE)=="Russian" ? ", Page " : ", Line ")+(string)__LINE__+": ") #define DFUN (__FUNCTION__+": ") // "Function description" #define COUNTRY_LANG ("Russian") // Country language #define END_TIME (D'31.12.3000 23:59:59') // End date for account history data requests #define TIMER_FREQUENCY (16) // Minimal frequency of the library timer in milliseconds //--- Parameters of the orders and deals collection timer #define COLLECTION_ORD_PAUSE (250) // Orders and deals collection timer pause in milliseconds #define COLLECTION_ORD_COUNTER_STEP (16) // Increment of the orders and deals collection timer counter #define COLLECTION_ORD_COUNTER_ID (1) // Orders and deals collection timer counter ID //--- Parameters of the account collection timer #define COLLECTION_ACC_PAUSE (1000) // Account collection timer pause in milliseconds #define COLLECTION_ACC_COUNTER_STEP (16) // Account timer counter increment #define COLLECTION_ACC_COUNTER_ID (2) // Account timer counter ID //--- Symbol collection timer 1 parameters #define COLLECTION_SYM_PAUSE1 (100) // Pause of the symbol collection timer 1 in milliseconds (for scanning market watch symbols) #define COLLECTION_SYM_COUNTER_STEP1 (16) // Increment of the symbol timer 1 counter #define COLLECTION_SYM_COUNTER_ID1 (3) // Symbol timer 1 counter ID //--- Symbol collection timer 2 parameters #define COLLECTION_SYM_PAUSE2 (300) // Pause of the symbol collection timer 2 in milliseconds (for events of the market watch symbol list) #define COLLECTION_SYM_COUNTER_STEP2 (16) // Increment of the symbol timer 2 counter #define COLLECTION_SYM_COUNTER_ID2 (4) // Symbol timer 2 counter ID //--- Collection list IDs #define COLLECTION_HISTORY_ID (0x7779) // Historical collection list ID #define COLLECTION_MARKET_ID (0x777A) // Market collection list ID #define COLLECTION_EVENTS_ID (0x777B) // Event collection list ID #define COLLECTION_ACCOUNT_ID (0x777C) // Account collection list ID #define COLLECTION_SYMBOLS_ID (0x777D) // Symbol collection list ID //--- Data parameters for file operations #define DIRECTORY ("DoEasy\\") // Library directory for storing object folders #define RESOURCE_DIR ("DoEasy\\Resource\\") // Library directory for storing resource folders //--- Symbol parameters #define CLR_DEFAULT (0xFF000000) // Default color #define SYMBOLS_COMMON_TOTAL (1000) // Total number of working symbols //+------------------------------------------------------------------+
Dentro de la subcarpeta de la biblioteca Resource\, se crearán de forma automática las carpetas Sounds y Images, en las que se crearán y guardarán los archivos de audio e imagen, respectivamente.
Dado que al crear los archivos a partir de matrices preparadas debemos especificar la extensión del archivo creado, para que el método de
generación de archivos sepa qué archivo se ha creado exactamente y en qué carpeta debe ubicarse, necesitaremos la enumeración de los tipos
de archivo cuyos datos se escriben en matrices binarias
.
Al final del listado de Defines.mqh, añadimos la enumeración necesaria:
//+------------------------------------------------------------------+ //| Data for working with program resource data | //+------------------------------------------------------------------+ enum ENUM_FILE_TYPE { FILE_TYPE_WAV, // wav file FILE_TYPE_BMP, // bmp file }; //+------------------------------------------------------------------+
Puesto que todos los archivos de recurso se ubicarán en las dos carpetas Sounds y Images, que a su vez colocaremos en el directorio MQL5\Files\
del terminal, necesitaremos corregir el método PlaySound() de la clase
CMessage. Abrimos el archivo \MQL5\Include\DoEasy\ Services\Message.mqh y
corregimos
la ruta del archivo hasta el método PlaySound():
//+------------------------------------------------------------------+ //| Play an audio file | //+------------------------------------------------------------------+ bool CMessage::PlaySound(const string file_name) { bool res=::PlaySound("\\Files\\"+file_name); CMessage::m_global_error=(res ? ERR_SUCCESS : ::GetLastError()); return res; } //+------------------------------------------------------------------+
Para reproducir el archivo, especificamos la subcarpeta \Files\, ya que debemos almacenar todos los datos relativos a
MQL5\ y esta carpeta, mientras que la ruta restante hasta el archivo se establece y transmite al método usando el parámetro
file_name al crear el objeto de descripción del archivo.
Actualmente, esto es suficiente para crear las clases necesarias.
Ahora, vamos a crear en la carpeta \MQL5\Include\DoEasy\Services\ la nueva clase CFileGen en el archivo FileGen.mqh:
//+------------------------------------------------------------------+ //| FileGen.mqh | //| Copyright 2019, MetaQuotes Software Corp. | //| https://mql5.com/en/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2019, MetaQuotes Software Corp." #property link "https://mql5.com/en/users/artmedia70" #property version "1.00" //+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include "..\\Services\DELib.mqh" //+------------------------------------------------------------------+ //| File generator class | //+------------------------------------------------------------------+ class CFileGen { private: static string m_folder_name; // Name of a folder library resource files are stored in static string m_subfolder; // Name of a subfolder storing audio or bitmap files static string m_name; // File name static int m_handle; // File handle //--- Set a (1) file, (2) subfolder name static void SetName(const ENUM_FILE_TYPE file_type,const string file_name); static void SetSubFolder(const ENUM_FILE_TYPE file_type); //--- Return file extension by its type static string Extension(const ENUM_FILE_TYPE file_type); public: //--- Return the (1) set name, (2) the flag of a file presence in the resource directory static string Name(void) { return CFileGen::m_name; } static bool IsExist(const ENUM_FILE_TYPE file_type,const string file_name); //--- Create a file out of the data array static bool Create(const ENUM_FILE_TYPE file_type,const string file_name,const uchar &file_data_array[]); }; //+------------------------------------------------------------------+
En el archivo se ha incluido directamente la biblioteca de funciones de
servicio DELib.mqh, dado que en esta ya se incluyen Defines.mqh y Message.mqh, imprescindibles para el funcionamiento de la clase:
//+------------------------------------------------------------------+ //| DELib.mqh | //| Copyright 2018, MetaQuotes Software Corp. | //| https://mql5.com/es/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2018, MetaQuotes Software Corp." #property link "https://mql5.com/es/users/artmedia70" #property strict // Es necesario para mql4 //+------------------------------------------------------------------+ //| Archivos de inclusión | //+------------------------------------------------------------------+ #include "..\Defines.mqh" #include "Message.mqh" #include "TimerCounter.mqh" //+------------------------------------------------------------------+
Todas las variables de miembro de la clase y sus métodos tienen encabezados en el código, por lo que carece de sentido describir sus valores, todo
está claro aquí. Echemos un vistazo a la implementación de los métodos.
Puesto que las variables de miembro de clase son estáticas,
estas requieren una inicialización explícita:
//+------------------------------------------------------------------+ //| Initialization of static variables | //+------------------------------------------------------------------+ string CFileGen::m_folder_name=RESOURCE_DIR; string CFileGen::m_subfolder="\\"; string CFileGen::m_name=NULL; int CFileGen::m_handle=INVALID_HANDLE; //+------------------------------------------------------------------+
Método para crear un archivo a partir de una matriz de datos:
//+------------------------------------------------------------------+ //| Create a file out of a data array | //+------------------------------------------------------------------+ bool CFileGen::Create(const ENUM_FILE_TYPE file_type,const string file_name,const uchar &file_data_array[]) { //--- Set a file name consisting of the file path, its name and extension CFileGen::SetName(file_type,file_name); //--- If such a file already exists, return 'false' if(::FileIsExist(CFileGen::m_name)) return false; //--- Open the file with the generated name for writing CFileGen::m_handle=::FileOpen(CFileGen::m_name,FILE_WRITE|FILE_BIN); //--- If failed to create the file, receive an error code, display the file opening error message and return 'false' if(CFileGen::m_handle==INVALID_HANDLE) { int err=::GetLastError(); ::Print(CMessage::Text(MSG_LIB_SYS_FAILED_OPEN_FILE_FOR_WRITE),"\"",CFileGen::m_name,"\". ",CMessage::Text(MSG_LIB_SYS_ERROR),"\"",CMessage::Text(err),"\" ",CMessage::Retcode(err)); return false; } //--- Write the contents of the file_data_array[] array, close the file and return 'true' ::FileWriteArray(CFileGen::m_handle,file_data_array); ::FileClose(CFileGen::m_handle); return true; } //+------------------------------------------------------------------+
El método usa la función estándar FileWriteArray(), que
permite escribir en un archivo binario las matrices de cualquier dato excepto de tipo string.
Transmitimos al método el tipo de archivo escrito (audio o
imagen),
el nombre dle futuro archivo y la
matriz con el conjunto de datos binarios del archivo creado.
Todas las acciones realizadas por el método se escriben en su
listado, y resultan sencillas y comprensibles, por eso no vamos a detallarlas.
Método que retorna la bandera de presencia de un archivo en el directorio de recursos:
//+------------------------------------------------------------------+ //| The flag of a file presence in the resource directory | //+------------------------------------------------------------------+ bool CFileGen::IsExist(const ENUM_FILE_TYPE file_type,const string file_name) { CFileGen::SetName(file_type,file_name); return ::FileIsExist(CFileGen::m_name); } //+------------------------------------------------------------------+
Transmitimos al método el tipo de archivo escrito y el nombre del archivo cuya presencia debemos comprobar. A continuación, establecemos el nombre del archivo, que consta de la ruta hasta el archivo, su nombre y su extensión. Después, con la ayuda de la función FileIsExist(), se retorna el resultado de la comprobación de la existencia de un archivo con ese nombre.
Método que establece el nombre del archivo, que consta de la ruta hasta el archivo, su nombre y su extensión:
//+------------------------------------------------------------------+ //| Set a file name | //+------------------------------------------------------------------+ void CFileGen::SetName(const ENUM_FILE_TYPE file_type,const string file_name) { CFileGen::SetSubFolder(file_type); CFileGen::m_name=CFileGen::m_folder_name+CFileGen::m_subfolder+file_name+CFileGen::Extension(file_type); } //+------------------------------------------------------------------+
Transmitimos al método el tipo de archivo escrito y el
nombre del archivo a partir los cuales el método monta el nombre completo del archivo, que incluye: la
carpeta de guardado de los archivos de la biblioteca, la subcarpeta de
extensión del archivo creada por el método SetSubFolder(),
el nombre transmitido (file_name) y la
extensión del archivo, creada por el método Extension()
según el tipo de archivo (audio o imagen). El resultado obtenido se
registra en la variable de miembro de clase
m_name.
Método para establecer el nombre de la subcarpeta dependiendo del tipo de archivo (audio o imagen):
//+------------------------------------------------------------------+ //| Set a subfolder name | //+------------------------------------------------------------------+ void CFileGen::SetSubFolder(const ENUM_FILE_TYPE file_type) { CFileGen::m_subfolder=(file_type==FILE_TYPE_BMP ? "Images\\" : file_type==FILE_TYPE_WAV ? "Sounds\\" : ""); } //+------------------------------------------------------------------+
Transmitimos al método el tipo de archivo, y dependiendo de
este tipo, se registra en la variable de miembro de clase
m_subfolder el nombre de la subcarpeta Images\ o Sounds\.
Método que retorna la extensión de un archivo según su tipo:
//+------------------------------------------------------------------+ //| Return file extension by its type | //+------------------------------------------------------------------+ string CFileGen::Extension(const ENUM_FILE_TYPE file_type) { string ext=::StringSubstr(::EnumToString(file_type),10); if(!::StringToLower(ext)) ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_CONV_TO_LOWERCASE),CMessage::Retcode(::GetLastError())); return "."+ext; } //+------------------------------------------------------------------+
Transmitimos al método el tipo de archivo. A continuación, de
la representación de texto (
EnumToString()) de la constante de la enumeración ENUM_FILE_TYPE
transmitida al método
extraemos la expansión con la ayuda de la función StringSubstr()
(para el archivo de audio "FILE_TYPE_WAV", se extrae la sublínea "WAV", para el archivo bitmap, "BMP"). Después, convertimos todos los símbolos
de la sublínea extraída con la extensión del archivo
en extensiones de línea con la ayuda de StringToLower(),
añadiendo a la línea (antes de ella) el símbolo "punto" (.) y retornando
el resultado obtenido desde el método.
Con esto, podemos dar por finalizada la creación de la clase generadora.
Ahora podemos almacenar datos binarios de cualquier archivo de audio y bitmap en el código fuente y crear automáticamente archivos completos a
partir de estos datos durante el inicio del programa. Los archivos deben colocarse en las carpetas correspondientes, a las que accederá la
biblioteca para obtener los archivos y usarlos para el propósito previsto.
Para obtenerlos de manera adecuada, necesitaremos describir de alguna manera los archivos existentes y disponer de acceso rápido y
conveniente a ellos. Para lograrlo, crearemos la clase de colección de recursos del programa. Esta no será una colección que vamos a crear en
la biblioteca (listas de punteros a los objetos de colección), sino una colección de objetos de descripción de los archivos.
Para cada archivo físico creado, generaremos un objeto que describa este archivo, especificando su nombre, ruta y descripción.
Usaremos estos objetos descriptores para acceder a los archivos físicos. La descripción de un archivo en estos objetos puede ser cualquier
descripción de texto que agreguemos a un objeto descriptor para cada archivo concreto.
Por ejemplo, para el sonido de un clic del ratón se puede usar "Clic del ratón 01", "Clic 01" o cualquier cosa que deseemos. Para obtener el
descriptor del archivo necesario, simplemente introducimos la descripción en los parámetros de búsqueda. El método de búsqueda
retornará el índice del objeto descriptor del archivo que podemos utilizar para obtener las propiedades del archivo físico.
Clase de colección de recursos de programa
Vamos a crear en la carpeta de la biblioteca \MQL5\Include\DoEasy\Collections\ la nueva clase CResourceCollection en el archivo ResourceCollection.mqh. Directamente en el nuevo listado de clases, escribimos otra clase más: la clase de objeto descriptor cuyas ejemplares serán añadidos para cada nuevo archivo y agregados a la colección de descriptores.
//+------------------------------------------------------------------+ //| ResourceCollection.mqh | //| Copyright 2019, MetaQuotes Software Corp. | //| https://mql5.com/en/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2019, MetaQuotes Software Corp." #property link "https://mql5.com/en/users/artmedia70" #property version "1.00" //+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include <Arrays\ArrayObj.mqh> #include "..\\Services\\FileGen.mqh" //+------------------------------------------------------------------+ //| Descriptor object class for the library resource file | //+------------------------------------------------------------------+ class CResObj : public CObject { private: string m_file_name; // Path + file name + extension string m_description; // File text description public: //--- Set (1) file name, (2) file description void FileName(const string name) { this.m_file_name=name; } void Description(const string descr) { this.m_description=descr; } //--- Return (1) file name, (2) file description string FileName(void) const { return this.m_file_name; } string Description(void) const { return this.m_description; } //--- Compare CResObj objects by properties (to search for equal resource objects) bool IsEqual(const CResObj* compared_obj) const { return this.Compare(compared_obj,0)==0; } //--- Compare CResObj objects by all properties (for sorting) virtual int Compare(const CObject *node,const int mode=0) const; //--- Constructor CResObj(void){;} }; //+------------------------------------------------------------------+ //| Compare CResObj objects | //+------------------------------------------------------------------+ int CResObj::Compare(const CObject *node,const int mode=0) const { const CResObj *obj_compared=node; if(mode==0) return(this.m_file_name>obj_compared.m_file_name ? 1 : this.m_file_name<obj_compared.m_file_name ? -1 : 0); else return(this.m_description>obj_compared.m_description ? 1 : this.m_description<obj_compared.m_description ? -1 : 0); } //+------------------------------------------------------------------+
Incluimos directamente en el listado los archivos necesarios: la clase de matriz dinámica de los punteros a los ejemplares de la clase CObject y sus herederos de la biblioteca estándar y la clase generadora de archivos de la biblioteca CFileGen.
Para que el objeto descriptor se pueda guardar en la lista CArrayObj, deberemos convertir este objeto en herederode
la clase básica de la biblioteca estándar CObject.
Todas las variables y métodos de los miembros de la clase han sido descritos en
los comentarios del código. No tiene sentido detenerse en un análisis detallado de su propósito.
Solo debemos matizar que el método virtual Compare()
compara los campos de los dos objetos descriptores por defecto según su nombre. Si los campos comparados de los dos objetos son iguales, el
método retornará cero. Si tenemos que comparar dos objetos según la descripción del archivo, deberemos establecer el modo
mode igual a 1.
Entre tanto, el método
IsEqual(), que retorna la bandera de igualdad de los objetos, compara los objetos solo según el nombre del archivo (ya que no es
posible que existan en una misma carpeta dos archivos con nombres idénticos). El método retorna el resultado del funcionamiento del método
Compare() con el modo mode por defecto (0), que se corresponde con la comparación según el nombre del archivo.
Vamos a implementar la clase de colección de los objetos descriptores de los archivos de recursos del programa:
//+------------------------------------------------------------------+ //| Collection class of resource files descriptor objects | //+------------------------------------------------------------------+ class CResourceCollection { private: //--- List of pointers to descriptor objects CArrayObj m_list_dscr_obj; //--- Create a file descriptor object and add it to the list bool CreateFileDescrObj(const string file_name,const string descript); //--- Add a new object to the list of descriptor objects bool AddToList(CResObj* element); public: //--- Create a file and add its description to the list bool CreateFile(const ENUM_FILE_TYPE file_type,const string file_name,const string descript,const uchar &file_data_array[]); //--- Return the (1) list of pointers to descriptor objects, (2) index of the file descriptor object by description CArrayObj *GetList(void) { return &this.m_list_dscr_obj; } int GetIndexResObjByDescription(const string file_description); //--- Constructor CResourceCollection(void); }; //+------------------------------------------------------------------+
El propósito de cada método también se describe en esta lista, por lo tanto, vamos a pasar a los métodos de clase.
Implementamos el constructor de la clase fuera del cuerpo de la clase:
//+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CResourceCollection::CResourceCollection() { this.m_list_dscr_obj.Clear(); this.m_list_dscr_obj.Sort(); } //+------------------------------------------------------------------+
Aquí: limpiamos la lista de objetos descriptores y asignamos a la lista la bandera de clasificación según el nombre del archivo (por defecto, 0).
Método para crear un archivo y añadir un objeto descriptor a la lista de colección:
//+------------------------------------------------------------------+ //| Create a file and add its descriptor object to the list | //+------------------------------------------------------------------+ bool CResourceCollection::CreateFile(const ENUM_FILE_TYPE file_type,const string file_name,const string descript,const uchar &file_data_array[]) { if(!CFileGen::Create(file_type,file_name,file_data_array)) { if(!::FileIsExist(CFileGen::Name())) return false; } return this.CreateFileDescrObj(file_type,CFileGen::Name(),descript); } //+------------------------------------------------------------------+
Transmitimos al método el tipo del archivo creado (audio o imagen), el nombre del archivo, la descripción del archivo y el enlace a la matriz binaria de datos del archivo a partir del cual se creará el archivo.
Si no logramos crear el archivo con la ayuda del método Create() de la clase
CFileGen, y el archivo realmente está ausente (podría
existir ya, por lo que no se crearía nuevamente),
retornamos false.
Si no había archivo, y ahora ha sido creado,
retornamos desde el método el resultado del funcionamiento del método de creación del
objeto descriptor y su adición a la lista de colección de los objetos descriptores de los archivos.
Método para crear un objeto descriptor y añadirlo a la lista de colección:
//+------------------------------------------------------------------+ //| Create a file descriptor object and add it to the list | //+------------------------------------------------------------------+ bool CResourceCollection::CreateFileDescrObj(const string file_name,const string descript) { CResObj *res_dscr=new CResObj(); if(res_dscr==NULL) { Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_RES_LINK)); return false; } res_dscr.FileName(file_name); res_dscr.Description(descript); if(!this.AddToList(res_dscr)) { delete res_dscr; return false; } return true; } //+------------------------------------------------------------------+
Transmitimos al método el nombre del archivo y su descripción.
Creamos un nuevo objeto descriptor del archivo, y si
no hemos logrado crearlo, mostramos en el diario una entrada sobre ello y
retornamos
false.
A continuación, establecemos
para el objeto descriptor nuevamente creado el nombre del archivo y la
descripción del archivo y lo añadimos a la lista de colección.
Si no hemos logrado añadir el objeto, este deberá ser eliminado para evitar fugas de memoria:
eliminamos el objeto y retornamos
false.
De lo contrario, retornamos
true: el objeto
ha sido correctamente creado y añadido a la colección.
Método para añadir un objeto descriptor a la lista de colección:
//+------------------------------------------------------------------+ //| Add a new object to the list of file descriptor objects | //+------------------------------------------------------------------+ bool CResourceCollection::AddToList(CResObj *element) { this.m_list_dscr_obj.Sort(); if(this.m_list_dscr_obj.Search(element)>WRONG_VALUE) return false; return this.m_list_dscr_obj.Add(element); } //+------------------------------------------------------------------+
Transmitimos el objeto descriptor al método, establecemos
para la lista de colección la bandera de clasificación según el nombre del archivo, y si
ya hay un objeto con ese nombre en la
lista, retornamos false.
De lo contrario, retornamos
el resultado del funcionamiento del
método de adición de un objeto a la lista.
Método que retorna el índice de un objeto descriptor a la lista de colección según la descripción del archivo:
//+----------------------------------------------------------------------+ //| Return the index of the file descriptor object by a file description | //+----------------------------------------------------------------------+ int CResourceCollection::GetIndexResObjByDescription(const string file_description) { CResObj *obj=new CResObj(); if(obj==NULL) return WRONG_VALUE; obj.Description(file_description); this.m_list_dscr_obj.Sort(1); int index=this.m_list_dscr_obj.Search(obj); delete obj; return index; } //+------------------------------------------------------------------+
Transmitimos al método la descripción del archivo según la
cual debemos encontrar el objeto descriptor.
Creamos un ejemplar temporal del objeto descriptor y establecemos
en su campo de descripción la descripción transmitida al método.
Establecemos
para la lista de colección la bandera de clasificación según la descripción del archivo (
1) y obtenemos
el índice del objeto descriptor cuyo campo de descripción contiene el texto buscado.
Eliminamos
necesariamente el objeto temporal y retornamos el índice obtenido (-1
en el caso de que no exista un objeto con esta descricpión en la lista de colección.
La clase de colección de los objetos descriptores de los archivos está lista.
Ahora, tenemos que añadir varios métodos al objeto básico de la biblioteca CEngine.
Abrimos el archivo \MQL5\Include\DoEasy\Engine.mqh e
incluimos
en este el archivo de colección de los objetos descriptores:
//+------------------------------------------------------------------+ //| Engine.mqh | //| Copyright 2018, MetaQuotes Software Corp. | //| https://mql5.com/es/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2018, MetaQuotes Software Corp." #property link "https://mql5.com/es/users/artmedia70" #property version "1.00" //+------------------------------------------------------------------+ //| Archivos de inclusión | //+------------------------------------------------------------------+ #include "Services\TimerCounter.mqh" #include "Collections\HistoryCollection.mqh" #include "Collections\MarketCollection.mqh" #include "Collections\EventsCollection.mqh" #include "Collections\AccountsCollection.mqh" #include "Collections\SymbolsCollection.mqh" #include "Collections\ResourceCollection.mqh" //+------------------------------------------------------------------+
Creamos el nuevo objeto de colección de los recursos del programa (colección de descriptores de archivo):
//+------------------------------------------------------------------+ //| Library basis class | //+------------------------------------------------------------------+ class CEngine : public CObject { private: CHistoryCollection m_history; // Collection of historical orders and deals CMarketCollection m_market; // Collection of market orders and deals CEventsCollection m_events; // Event collection CAccountsCollection m_accounts; // Account collection CSymbolsCollection m_symbols; // Symbol collection CResourceCollection m_resource; // Resource list CArrayObj m_list_counters; // List of timer counters int m_global_error; // Global error code bool m_first_start; // First launch flag bool m_is_hedge; // Hedge account flag bool m_is_tester; // Flag of working in the tester bool m_is_market_trade_event; // Account trading event flag bool m_is_history_trade_event; // Account history trading event flag bool m_is_account_event; // Account change event flag bool m_is_symbol_event; // Symbol change event flag ENUM_TRADE_EVENT m_last_trade_event; // Last account trading event int m_last_account_event; // Last event in the account properties int m_last_symbol_event; // Last event in the symbol properties //--- Return the counter index by id
y añadimos tres nuevos métodos para trabajar con la colección de recursos del programa:
//--- Timer void OnTimer(void); //--- Set the list of used symbols bool SetUsedSymbols(const string &array_symbols[]) { return this.m_symbols.SetUsedSymbols(array_symbols);} //--- Create a resource file bool CreateFile(const ENUM_FILE_TYPE file_type,const string file_name,const string descript,const uchar &file_data_array[]) { return this.m_resource.CreateFile(file_type,file_name,descript,file_data_array); } //--- Return the list of links to resources CArrayObj *GetListResource(void) { return this.m_resource.GetList(); } int GetIndexResObjByDescription(const string file_name) { return this.m_resource.GetIndexResObjByDescription(file_name); } //--- Return event (1) milliseconds, (2) reason and (3) source from its 'long' value ushort EventMSC(const long lparam) const { return this.LongToUshortFromByte(lparam,0); } ushort EventReason(const long lparam) const { return this.LongToUshortFromByte(lparam,1); } ushort EventSource(const long lparam) const { return this.LongToUshortFromByte(lparam,2); } //--- Constructor/destructor CEngine(); ~CEngine(); }; //+------------------------------------------------------------------+
Los métodos tienen los mismos nombres que los métodos en la clase de colección de objetos descriptores y llaman a los métodos homónimos de esta
clase.
Para poner a prueba las clases creadas, crearemos todos los archivos según los datos disponibles en los archivos de inclusión DataSND.mqh y
DataIMG.mqh en matrices binarias. En el diario "Expertos", mostraremos los resultados de la creación de los archivos a partir de las
matrices binarias, mostrando también el contenido de la lista de colección resultante de objetos descriptores de archivos. Además,
reproduciremos uno de los sonidos y visualizaremos una imagen que consta de los dos archivos de imagen creados que muestran los LED rojo y
verde en la esquina inferior derecha de la pantalla.
Simulación del acceso a los archivos creados automáticamente
Vamos a tomar el asesor TestDoEasyPart19.mq5 del artículo anterior y a
guardarlo en la nueva carpeta \MQL5\Experts\TestDoEasy\
Part20\ con el nuevo nombre TestDoEasyPart20.mq5.
Dado que los archivos deben crearse durante el primer inicio del programa, vamos a organizar en el manejador OnInit() el trabajo con las clases para crear los recursos del programa. Precisamente en él simularemos el resultado obtenido.
Añadimos al final del manejador OnInit() este bloque de código:
//--- Set CTrade trading class parameters #ifdef __MQL5__ trade.SetDeviationInPoints(slippage); trade.SetExpertMagicNumber(magic_number); trade.SetTypeFillingBySymbol(Symbol()); trade.SetMarginMode(); trade.LogLevel(LOG_LEVEL_NO); #endif //--- Create and check the resource files Print("\n",TextByLanguage("--- Проверка успешности создания файлов ---","--- Verifying files were created ---")); string dscr=TextByLanguage("Проверка существования файла: ","Checking existence of file: "); engine.CreateFile(FILE_TYPE_WAV,"sound_array_coin_01",TextByLanguage("Звук упавшей монетки 1","Sound of falling coin 1"),sound_array_coin_01); if(CFileGen::IsExist(FILE_TYPE_WAV,"sound_array_coin_01")) Print(dscr+CFileGen::Name(),": OK"); engine.CreateFile(FILE_TYPE_WAV,"sound_array_coin_02",TextByLanguage("Звук упавших монеток","Sound fallen coins"),sound_array_coin_02); if(CFileGen::IsExist(FILE_TYPE_WAV,"sound_array_coin_02")) Print(dscr+CFileGen::Name(),": OK"); engine.CreateFile(FILE_TYPE_WAV,"sound_array_coin_03",TextByLanguage("Звук монеток","Sound of coins"),sound_array_coin_03); if(CFileGen::IsExist(FILE_TYPE_WAV,"sound_array_coin_03")) Print(dscr+CFileGen::Name(),": OK"); engine.CreateFile(FILE_TYPE_WAV,"sound_array_coin_04",TextByLanguage("Звук упавшей монетки 2","Sound of falling coin 2"),sound_array_coin_04); if(CFileGen::IsExist(FILE_TYPE_WAV,"sound_array_coin_04")) Print(dscr+CFileGen::Name(),": OK"); engine.CreateFile(FILE_TYPE_WAV,"sound_array_click_01",TextByLanguage("Звук щелчка по кнопке 1","Click on button sound 1"),sound_array_click_01); if(CFileGen::IsExist(FILE_TYPE_WAV,"sound_array_click_01")) Print(dscr+CFileGen::Name(),": OK"); engine.CreateFile(FILE_TYPE_WAV,"sound_array_click_02",TextByLanguage("Звук щелчка по кнопке 2","Click on button sound 1"),sound_array_click_02); if(CFileGen::IsExist(FILE_TYPE_WAV,"sound_array_click_02")) Print(dscr+CFileGen::Name(),": OK"); engine.CreateFile(FILE_TYPE_WAV,"sound_array_click_03",TextByLanguage("Звук щелчка по кнопке 3","Click on button sound 1"),sound_array_click_03); if(CFileGen::IsExist(FILE_TYPE_WAV,"sound_array_click_03")) Print(dscr+CFileGen::Name(),": OK"); engine.CreateFile(FILE_TYPE_WAV,"sound_array_cash_machine_01",TextByLanguage("Звук кассового аппарата","Sound of cash machine"),sound_array_cash_machine_01); if(CFileGen::IsExist(FILE_TYPE_WAV,"sound_array_cash_machine_01")) Print(dscr+CFileGen::Name(),": OK"); engine.CreateFile(FILE_TYPE_BMP,"img_array_spot_green",TextByLanguage("Изображение \"Зелёный светодиод\"","Image \"Green Spot lamp\""),img_array_spot_green); if(CFileGen::IsExist(FILE_TYPE_BMP,"img_array_spot_green")) Print(dscr+CFileGen::Name(),": OK"); engine.CreateFile(FILE_TYPE_BMP,"img_array_spot_red",TextByLanguage("Изображение \"Красный светодиод\"","Image \"Red Spot lamp\""),img_array_spot_red); if(CFileGen::IsExist(FILE_TYPE_BMP,"img_array_spot_red")) Print(dscr+CFileGen::Name(),": OK"); //--- Check the file description list Print("\n",TextByLanguage("--- Проверка списка описания файлов ---","--- Checking file description list ---")); CArrayObj* list_res=engine.GetListResource(); if(list_res!=NULL) { //--- Let's see the entire list of file descriptions for(int i=0;i<list_res.Total();i++) { CResObj *res=list_res.At(i); if(res==NULL) continue; //--- Display the paths to the files and the file description in the journal string type=(StringFind(res.FileName(),"\\Sounds\\")>0 ? TextByLanguage("Звук ","Sound ") : TextByLanguage("Изображение ","Image ")); Print(type,string(i+1),": ",TextByLanguage("Имя файла :","File name: "),res.FileName()," (",res.Description(),")"); //--- If the current description corresponds to the falling coin sound 1, play the appropriate sound if(res.Description()==TextByLanguage("Звук упавшей монетки 1","Sound of falling coin 1")) { CMessage::PlaySound(res.FileName()); } } //--- Create the image of the red-green LED //--- Get the indices of red and green LEDs image descriptions int index_r=engine.GetIndexResObjByDescription(TextByLanguage("Изображение \"Красный светодиод\"","Image \"Red Spot lamp\"")); int index_g=engine.GetIndexResObjByDescription(TextByLanguage("Изображение \"Зелёный светодиод\"","Image \"Green Spot lamp\"")); if(index_g>WRONG_VALUE && index_r>WRONG_VALUE) { //--- Get two objects with files description from the list CResObj *res_g=list_res.At(index_g); CResObj *res_r=list_res.At(index_r); if(res_g==NULL || res_r==NULL) { Print(TextByLanguage("Не удалось получить данные с описанием файла изображения","Failed to get image file description data")); return(INIT_SUCCEEDED); } //--- Create a button based on image files long chart_ID=ChartID(); string name=prefix+"RedGreenSpot"; if(ObjectCreate(chart_ID,name,OBJ_BITMAP_LABEL,0,0,0)) { ObjectSetString(chart_ID,name,OBJPROP_BMPFILE,0,"\\Files\\"+res_g.FileName()); // Изображение для нажатой кнопки ObjectSetString(chart_ID,name,OBJPROP_BMPFILE,1,"\\Files\\"+res_r.FileName()); // Released button image ObjectSetInteger(chart_ID,name,OBJPROP_CORNER,CORNER_RIGHT_LOWER); ObjectSetInteger(chart_ID,name,OBJPROP_ANCHOR,ANCHOR_RIGHT_LOWER); ObjectSetInteger(chart_ID,name,OBJPROP_STATE,true); ObjectSetInteger(chart_ID,name,OBJPROP_XSIZE,16); ObjectSetInteger(chart_ID,name,OBJPROP_YSIZE,16); ObjectSetInteger(chart_ID,name,OBJPROP_XOFFSET,0); ObjectSetInteger(chart_ID,name,OBJPROP_YOFFSET,0); ObjectSetInteger(chart_ID,name,OBJPROP_BACK,false); ObjectSetInteger(chart_ID,name,OBJPROP_TIMEFRAMES,OBJ_ALL_PERIODS); ChartRedraw(chart_ID); } } } //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+
Aquí hemos organizado la creación de archivos a partir de los datos que se encuentran en las matrices binarias en los códigos fuente de la
biblioteca. Después de crear cada archivo que sigue, comprobamos su existencia e imprimimos el resultado en el diario. Después de crear
todos los archivos y sus descriptores, comprobamos la lista completa con todos los descriptores de los archivos añadidos a la colección en
la etapa de creación de archivos.
En el listado se describen todas las acciones, que el lector podrá estudiar por sí mismo.
Después de compilar el asesor, este mostrará en el diario las entradas con los resultados de la creación de los archivos, reproducirá el sonido de una moneda al caer y mostrará en la esquina inferior derecha de la pantalla la figura de un LED que consta de dos imágenes. El usuario podrá alternar las imágenes clicando con el ratón sobre la imagen del LED (en esencia, se trata de un botón que tiene dos estados: on/off)
Como podemos ver, todo funciona como debe: en el diario se muestran los mensajes sobre la creación exitosa de los archivos, la figura del LED
alterna el color al clicar sobre la imagen, y si abrimos el catálogo de datos del terminal (en el terminal, punto del menú archivo --> Abrir
catálogo de datos) y entramos en la carpeta MQL5\Files\DoEasy\Resource\, encontraremos dos subcarpetas, Images y Sounds, en las que se
encuentran todos los archivos recién creados.
¿Qué es lo próximo?
En el siguiente artículo, comenzaremos un nuevo apartado de la biblioteca: las clases comerciales y todo lo relacionado con ellas.
Más abajo se adjuntan todos los archivos de la versión actual de la biblioteca y los archivos del asesor de prueba. Puede descargarlo todo
y ponerlo a prueba por sí mismo.
Si tiene preguntas, observaciones o sugerencias, podrá concretarlas en los comentarios al artículo.
Artículos de esta serie:
Parte 1: Concepto y organización de datos
Parte
2: Colecciones de las órdenes y transacciones históricas
Parte 3: Colección de
órdenes y posiciones de mercado, organización de la búsqueda
Parte 4: Eventos
comerciales. Concepto
Parte 5: Clases y colección de eventos comerciales. Envío
de eventos al programa.
Parte 6. Eventos en la cuenta con compensación
Parte 7. Eventos de activación de órdenes StopLimit, preparación de la funcionalidad para el
registro de los eventos de modificación de órdenes y posiciones
Parte 8. Eventos de
modificación de órdenes y posiciones
Parte 9. Compatibilidad con MQL4 -
Preparando los datos
Parte 10. Compatibilidad con MQL4 - Eventos de apertura de
posición y activación de órdenes pendientes
Parte 11. Compatibilidad con MQL4 -
Eventos de cierre de posiciones
Parte 12. Implementando la clase de objeto
"cuenta" y la colección de objetos de cuenta
Parte 13. Eventos del objeto "cuenta"
Parte 14. El objeto "Símbolo"
Parte
15. Colección de objetos de símbolo
Parte 16. Eventos de la colección de símbolos
Parte 17. Interactividad de los objetos de la biblioteca
Parte
18. Interactividad del objeto de cuenta y cualquier otro objeto de la biblioteca
Parte
19. Clase de mensajes de la biblioteca
Traducción del ruso hecha por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/ru/articles/7195
- Aplicaciones de trading gratuitas
- 8 000+ señales para copiar
- Noticias económicas para analizar los mercados financieros
Usted acepta la política del sitio web y las condiciones de uso