外汇EA编写教程:在 MQL5 中使用资源

现代程序中界面的重要性

很久以前,计算机程序的主要目的是进行繁重的数学计算和处理大量的数量。但是,随着计算机能力的增强,优先事项已经改变了。现在,在具有相同功能的两个程序之间,用户会选择更易使用的一个。

现在,仅仅依据需要的算法编写程序是不够的,您还必须提供一个用户友好的图形界面。甚至连技术分析也源自交易者希望拥有当前市场状态的图形表示:已经开发了若干趋势线、支撑位和阻力位、各种通道和技术指标来显示正在发生的事情的客观图形。

新的 MQL5 语言拥有更强大的工具来创建除了 MetaTrader 5 客户端以外,不需要任何其他东西的全功能应用程序。在本文中,我们将显示如何使用资源来创建一个含有用户友好界面的可执行 EX5 文件,并且此文件不需要安装或开始等任何例行操作。

MQL5 的可能性

当然,首先,处理图形的可能性是非常重要的。可以在相关文章中找到很多例子,下面是其中的一部分:

从用户的观点来看,是图形元素的使用让一个程序更有趣且更易控制。除了针对技术分析的传统工具以外,MetaTrader 5 客户端还提供了各种各样的图形对象,这些对象可用作构建您自己的图形界面的砖石。

使用图形文件创建界面

要创建一个特别的界面,通常使用来自图形文件的图像。这允许实现含有各种控制元素的独特的可辨认设计。MQL5 语言提供两种使用图形的图形对象

  • OBJ_BITMAP – 位图对象允许从一个 BMP 文件下载图像并在图表中显示该图像;
  • OBJ_BITMAP_LABEL图形标签实际上是一个按钮,其图像视其状态而改变(按下/松开)。

这两种对象允许您创建各种各样的控制,并将它们与事件处理程序的“鼠标单击”进行比较 (CHARTEVENT_OBJECT_CLICK)。要为 OBJ_BITMAP 或 OBJ_BITMAP_LABEL 设置需要的图像,在OBJPROP_BMPFILE属性中指定需要的BMP文件。这可以在图形对象的 “Parameters”(参数)选项卡上手动进行。

第二种方式,也是MQL5程序员的主要方式,是使用ObjectSetString()函数为 OBJPROP_BMPFILE 属性指定一个文件名。例如:

   //--- 加载 "按下" 按钮状态的图像
   bool set=ObjectSetString(0,object_name,OBJPROP_BMPFILE,0,bmp_file_name);

使用 OBJ_BITMAP 或 OBJ_BITMAP_LABEL 的标准算法:

  1. 使用 ObjectCreate() 函数创建一个对象。
  2. 如果需要,使用 ObjectSetInteger() 函数,将对象固定到需要的图表角落。系统将相对该角落设置以像素表示的固定点的 X 坐标和 Y 坐标。
  3. ObjectSetInteger() 中,设置 X 坐标和 Y 坐标的值(OBJPROP_XDISTANCE 和 OBJPROP_YDISTANCE)。
  4. ObjectSetString() 设置图形对象的 OBJPROP_BMPFILE 属性的值(BITMAP 对象有一个,OBJ_BITMAP_LABEL 对象有两个)。
  5. 对于 OBJ_BITMAP_LABEL 对象,使用 ObjectSetInteger(),您可以设置按钮的初始状态 – 按下或松开(OBJPROP_STATE 的值为 true 或 false)。

在创建和配置对象之后,在 MQL5 程序运行期间,您不仅可以动态地更改图像对象的位置和状况,还可以更改 OBJ_BITMAP_LABEL 属性的值以显示图像。因此,界面能够非常灵活且可配置。

播放声音

程序另一个必需的方便性是能够在出现某种情形时要求用户采取某种行动。为了实施这种反向互动,视事件而定,通常使用不同声音的播放。这样,仅在必要时才吸引交易者注意,从而让交易者不必持续观察价格走势图。为了播放音频文件,在 MQL5 中使用 PlaySound() 函数。

PlaySound() 的使用非常方便,仅需要指定声音文件的路径:

//--- 声音文件路径  
string wav_file_name="Ok.wav";
...
//--- 从文件里播放声音 terminal_directory/Sounds/Ok.wav
bool played=PlaySound(wav_file_name);
if(!played)
  //--- 播放声音失败, 提示
  {
   PrintFormat("从文件播放声音失败 %s. 错误代码=%d", wav_file_name, GetLastError());
  }

在哪里定位声音和图像文件

ObjectSetString()PlaySound() 函数需要指定文件的路径。出于安全原因,在 MQL5 程序中使用的所有文件都位于文件沙箱内。这意味着文件只能存储在某些目录中,不允许使用来自其他目录的文件。首先,您需要了解哪些目录可用于文件操作,可用于函数,以及它们是如何命名的。

有三种不同的目录:

  • 客户端目录 – 客户端安装目录。MetaTrader 5 从这个目录启动,要查看该目录,请在客户端的菜单中选择 “File”(文件)-“Open terminal data“(打开客户端数据)。
  • 客户端数据目录 – 存储某名 Windows 用户的数据的文件夹。操作系统的内置保护机制区别对待用户访问,因此,每名用户的数据可以与其他用户的数据分开存储。因此,所有显示在导航窗口中的指标、EA 交易程序和脚本都保存在这个目录中,在 MQL5 子文件夹中。
  • (安装在计算机上的所有 MetaTrader 5 客户端的)所有客户端的共享文件夹 – 用于执行使用 FILE_COMMON 标志的文件操作的文件夹。

为了确定这些目录的位置,您可以使用脚本 WhereMyFolders.mq5:

//+------------------------------------------------------------------
//|                                               WhereMyFolders.mq5 |
//|                        Copyright 2011, MetaQuotes Software Corp. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------
#property copyright "Copyright 2011, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"
//+------------------------------------------------------------------
//| 脚本程序开始函数                                    |
//+------------------------------------------------------------------
void OnStart()
  {
//--- 客户端的开始文件夹 - terminal_directory
   string terminal_path=TerminalInfoString(TERMINAL_PATH);
//--- 保存客户端数据的文件夹 - terminal_data_directory
   string terminal_data_path=TerminalInfoString(TERMINAL_DATA_PATH);
//--- 所有客户端的共享文件夹 - common_terminal_folder
   string common_data_path=TerminalInfoString(TERMINAL_COMMONDATA_PATH);   
   //--- 显示所有路径 
   Print("TERMINAL_PATH(terminal_directory) = ",TerminalInfoString(TERMINAL_PATH));
   Print("TERMINAL_DATA_PATH(terminal_data_directory) = ",TerminalInfoString(TERMINAL_DATA_PATH));
   Print("TERMINAL_COMMONDATA_PATH(comon_terminal_folder) = ",TerminalInfoString(TERMINAL_COMMONDATA_PATH));   
  }

重要须知:在某些情形中,terminal_directoryterminal_data_directory 的位置可能是相同的,但是最好不要依赖这一点,因此不要混淆这两个概念。

客户端运行时系统按以下顺序搜索图像和声音文件:

  • 如果反斜杠分隔符 “/”(写为 “//”)放置在路径的开头,则它相对于目录 terminal_data_directory/MQL5/ 搜索资源;
  • 如果在路径的开头没有反斜杠,相对于 EX5 文件的位置搜索文件,从该位置调用 ObjectSetString(… , OBJPROP_BMPFILE, …)PlaySound() 函数;

对于 PlaySound() 函数,还添加了一项:如果使用以上两种方法都没有找到文件,则相对于 terminal_directory/Sounds/ 搜索此声音文件。

声音文件示例:

  • 将在 terminal_data_directory/MQL5/ 中搜索文件 one.wav
    PlaySound("//one.wav");

  • 将在 terminal_data_directory/MQL5/Files/ 中搜索文件 two.wav
    PlaySound("//Files//two.wav");

  • 将在 terminal_data_directory/MQL5/MySounds/ 中搜索文件 three.wav
    PlaySound("//MySounds//three.wav");

  • 将在从其中运行可执行 EX5 文件的文件夹中搜索文件 four.wav。如果在此文件夹中找不到该文件,则尝试在文件夹 terminal_directory/Sounds/ 中查找文件。
    PlaySound("four.wav");

图像文件示例:

  • 将在 terminal_data_directory/MQL5/ 中搜索文件 bird.bmp
    //--- 为 OBJ_BITMAP_LABEL 对象设置图像
    bool res=ObjectSetString(0,object_name,OBJPROP_BMPFILE,0,"bird.bmp");// 指定修饰符 0
    

  • 将在 terminal_data_directory/MQL5/Files/ 中搜索文件 swan.bmp
    //--- 为 OBJ_BITMAP 对象设置图像
    bool set=ObjectSetString(0,object_name,OBJPROP_BMPFILE,"//Files//swan.bmp");// 无修饰符
    

  • 将在 terminal_data_directory/MQL5/MyPictures/ 中搜索文件 dog.bmp
    //--- 为 OBJ_BITMAP 对象设置图像
    bool done=ObjectSetString(0,object_name,OBJPROP_BMPFILE,"//MyPictures//dog.bmp");// 无修饰符
    

  • 将在从其中运行可执行 EX5 文件的文件夹中搜索文件 cat.bmp
    //--- 为 OBJ_BITMAP 对象设置图像
    bool result=ObjectSetString(0,object_name,OBJPROP_BMPFILE,"cat.bmp");// 无修饰符
    

注意,在写路径时,使用双反斜杠 “//” 作为分隔符。

重要须知:在指定路径时,始终使用双反斜杠作为分隔符,因为在解析程序源代码中的恒定行数和字符常量时,单反斜杠是编译器的一个控制字符。

新的可能 – 资源

要在您的 MQL5 程序中使用图像和声音,确保使用的所有媒体文件位于适当的文件夹中。这意味着在将一个编译好的 EX5 文件从一个客户端移到另一个客户端时有一个明确的缺点。但是在编写代码阶段有可能解决此问题。在此类情形中,使用资源

为了使用一个程序中的资源,应使用编译器指令 #resource 对其进行声明

 #resource path_to_resource_file

现在,可以使用该资源来代替文件路径。#resource命令告诉编译器在指定路径 path_to_resource_file 的资源应包含在可执行 EX5 文件中。因此,所有必需的图像和声音都可以直接保存在 EX5 文件中。现在,要在另一客户端中启动一个 MQL5 程序,您无需传输在该程序中使用的所有文件。

任何 EX5 文件都可以包含资源,并且任何 EX5 程序能够使用来自另一 EX5 程序的资源。换言之,一个 EA 交易程序可以使用一个指标或 EX5 库中的资源。这是一种更加方便的资源使用方式。

使用资源允许您从一个文件就获得需要的一切 – 执行文件本身及其使用的所有资源在编译源代码时都封装在一个 EX5 文件中。

编译器搜索资源

资源是通过指令 #resource “<path to the resource file>” 来指定的

 #resource "<path_to_resource_file>"

字符串常量 ><path_to_resource_file> 的长度不应超过 63 个字符。编译器按以下顺序在指定路径搜索资源:1

  • 如果在路径开头有反斜杠 “/”,则相对于文件夹 terminal_data_directory/MQL5/ 搜索资源,
  • 如果没有反斜杠,则相对于在其中编写该资源的资源文件的位置搜索资源。

重要须知: 在资源路径中,不能使用子字符串 “..//” 和 “://”。

来自帮助主题资源的一些资源示例:

//--- 正确指定一个资源
#resource "//Images//euro.bmp" // euro.bmp 位于 terminal_data_directory/MQL5/Images/
#resource "picture.bmp"        // picture.bmp 位于源文件相同目录
#resource "Resource//map.bmp"  // 资源位于文件夹 source_file_directory/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//cat.bmp"           // 资源名 - Images/cat.bmp
#resource "dog.bmp"                     // 资源名 - dog.bmp
#resource "Resource//map.bmp"           // 资源名 - Resource/map.bmp
#resource "//Files//Pictures//bird.bmp" // 资源名 - Files/Pictures/bird.bmp
#resource "//Files//good.wav"           // 资源名 - Files/good.wav"
#resource "//Sounds//thrill.wav"        // 资源名 - Sounds/thrill.wav"

资源名称不区分大小写 – 对于编译器而言,名称 dog.bmp 和 DOG.bmp 将是相同的。

使用自己的资源和第三方资源

要使用一个资源,您应指定其名称。资源名称是其路径,不包含代码行开头处的反斜杠。当您使用您自己的资源时,在资源名称前面添加特殊属性 “::”。

//--- 使用资源
ObjectSetString(0,bitmap_name,OBJPROP_BMPFILE,0,"::Images//cat.bmp");
...
ObjectSetString(0,my_bitmap,OBJPROP_BMPFILE,0,"::dog.bmp");
...
set=ObjectSetString(0,bitmap_label,OBJPROP_BMPFILE,1,"::Files//Pictures//bird.bmp");
...
PlaySound("::Files//good.wav");
...
PlaySound("::Sounds//thrill.wav");

您不仅可以使用您自己的资源(来自您自己的 EX5 文件),还可以使用来自任何 EX5 库和模块的资源。因此,您可以创建一个资源存储库并在很多其他 mql5 程序中使用它们。

要使用来自另一EX5 文件的资源,应该以 <EX5_file_name_path>::<resource_name> 的形式指定资源名称。假定 Draw_Triangles_Script.mq5 脚本包含文件 triangle.bmp 中的一个图像资源:

 #resource "//Files//triangle.bmp"

则其名称,要在脚本本身中使用的话,将看起来象 “Files/triangle.bmp”,并且要使用该资源,应将 “::” 添加到资源名称 – “::Files/triangle.bmp”要从另一程序,例如从一个 EA 交易程序使用相同的资源,我们需要将相对于 terminal_data_directory/MQL5/ 的 EX5 的路径和脚本的 EX5 文件的名称 – Draw_Triangles_Script.ex5 添加到资源名称。假定脚本位于标准文件夹 terminal_data_directory/MQL5/Scripts/ 中,则应按以下方式编写调用:

//--- 在EA交易中使用脚本资源
ObjectSetString(0,my_bitmap_name,OBJPROP_BMPFILE,0,"//Scripts//Draw_Triangles_Script.ex5::Files//triangle_1.bmp");

如果在从另一 EX5 调用资源时未指定可执行文件的路径,则在调用资源的程序所在的文件夹中搜索可执行文件。这意味着:如果一个 EA 交易程序位于 terminal_data_directory/MQL5/Experts/ 中,并且来自文件 Draw_Triangles_Script.ex5 的资源没有指定路径,则将在 terminal_data_directory/MQL5/Experts/ 中搜索该文件。

//--- 在EA交易中请求脚本资源无需指定路径
ObjectSetString(0,my_bitmap_name,OBJPROP_BMPFILE,0,"Draw_Triangles_Script.ex5::Files//triangle_1.bmp");

EX5 文件中资源的压缩 – 它是如何工作的

BMP 和 WAV 格式的文件在包含到可选择 EX5 文件之前会被自动压缩。这意味着使用资源不仅允许您创建全功能 MQL5 程序,与传统的 MQL5 程序编写方式相比,还在使用图像和声音时减少客户端需要的文件的整体大小。

资源文件的大小不能超过 16 Mb。

重要须知:使用资源的另一优点是在封装到一个可执行 EX5 时自动压缩 WAV 和 BMP 文件。这不仅减少数量,还减少程序使用的文件的大小。

例如,考虑一个小程序 Animals_EA.mq5。下面给出使用资源的一小段代码:

//+------------------------------------------------------------------
//|                                                   Animals_EA.mq5 |
//|                        Copyright 2011, MetaQuotes Software Corp. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------
#property copyright "Copyright 2011, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"
//--- 声明图像资源
#resource "//Images//cat.bmp"
#resource "//Images//dog.bmp"
#resource "//Images//cow.bmp"
#resource "//Images//bird.bmp"
//--- 声明声音资源
#resource "//Files//MySounds//cat.wav"
#resource "//Files//MySounds//dog.wav"
#resource "//Files//MySounds//cow.wav"
#resource "//Files//MySounds//bird.wav"
//--- 对象名
string cat_dog="cat_dog";
string cow_bird="cow_bird";
string canvas="canvas";
string text="text";
//+------------------------------------------------------------------
//| EA初始化函数                                   |
//+------------------------------------------------------------------
int OnInit()
  {
//--- 创建底图
   CreateCanvas(canvas,50,50,500,500);
//--- 创建按钮
   CreateObjectBITMAP_LABEL(cat_dog,110,120,"::Images//cat.bmp","::Images//dog.bmp");
   CreateObjectBITMAP_LABEL(cow_bird,110,330,"::Images//cow.bmp","::Images//bird.bmp");
   CreateText(text,"Click on any graphical object",200,90,clrTan);
//--- 发出立即刷新指令以便查看对象
   ChartRedraw();
//---
   return(0);
  }
//+------------------------------------------------------------------
//|  使用指定图像创建OBJ_BITMAP_LABEL           |
//+------------------------------------------------------------------
bool CreateObjectBITMAP_LABEL(string obj_name,int X,int Y,string res_name1,string res_name2)
  {
//--- 如果图表中没有对象
   if(ObjectFind(0,obj_name)==-1)
     {
      //--- 创建它
      bool res=ObjectCreate(0,obj_name,OBJ_BITMAP_LABEL,0,0,0);
      //--- 检查结果
      if(!res)
        {
         PrintFormat("%s: 创建 OBJ_BITMAP_LABEL 失败,名字 %s. 错误代码=%d",
                     __FUNCTION__,
                     GetLastError());
         return false;
        }
     }

//--- 设置坐标
   ObjectSetInteger(0,obj_name,OBJPROP_XDISTANCE,X);
   ObjectSetInteger(0,obj_name,OBJPROP_YDISTANCE,Y);
//--- 禁止背景显示
   ObjectSetInteger(0,obj_name,OBJPROP_BACK,false);
//--- 重置错误码
   ResetLastError();
//--- 设置按下条件图像
   bool res=ObjectSetString(0,obj_name,OBJPROP_BMPFILE,0,res_name1);
//--- 检查操作结果
   if(!res)
     {
      PrintFormat("%s: 从资源加载图像失败 %s. 错误代码=%d",
                  __FUNCTION__,
                  res_name1,
                  GetLastError());
      return false;
     }
//--- 设置释放状态图像
   res=ObjectSetString(0,obj_name,OBJPROP_BMPFILE,1,res_name2);
//--- 检查操作结果
   if(!res)
     {
      PrintFormat("%s: 从资源加载图像失败 %s. 错误代码=%d",
                  __FUNCTION__,
                  res_name2,
                  GetLastError());
      return false;
     }
//--- 设置按下按钮
   ObjectSetInteger(0,obj_name,OBJPROP_STATE,true);
   return true;
  }
//+------------------------------------------------------------------

程序的任务是绘制一个蓝色背景(底层)、两个在鼠标单击时改变它们的外观的图形按钮。当您单击底层时,它的颜色从蓝色变为米白色,再单击一次时,又从米白色变为蓝色。每次改变时播放一个声音,在OnChartEvent()函数中处理鼠标单击事件。在Adviser Animals_EA.mq5 启动之后,立即有一图表显示在图中。

例如,看一看OBJ_BITMAP_LABEL对象的属性cat_dog。现在不能通过对话框更改 Bitmap File (On) 和 Bitmap File (Off) 的属性,这些字段现在不可用,是灰色的。

重要须知:在图形对象中,只能通过程序方式更改从资源加载的图像。通过对象的 Properties(属性)窗口手动更改这些属性变得不可行。

EA交易 Animals_EA.mq5 使用的图像的总大小为 430 kb。

但是生成的包含所有这些图片的可执行文件Animals_EA.ex5 的大小为 339 kb。因此,代替 9 个文件(一个MQ5 文件、四个BMP 文件和四个WAV 文件),我们现在有一个包含程序的所有必需资源的 EX5 文件。

在资源中只能使用 24 位或 32 位 BMP 图片。32 位 BMP 可以包含alpha 通道 – 在这种情况下,它们将被应用到具有透明度的图表。

本文附带了EA交易 Animals_EA.mq5、图像和声音的文件:

  • 应将图像从 архива images.zip 解压缩到 terminal_data_directory/MQL5/Images/
  • 应将声音从 MySounds.zip 解压缩到 terminal_data_directory/MQL5/Files/MySounds/

如果您想在您的客户端中测试此程序,只需要下载附带的编译好的EA交易 Animals_EA.ex5,该文件包含所有必需的资源。在这个例子中,您不需要下载和安装图像与声音文件。

节省客户端的内存

每个资源仅载入客户端内存一次。在一般使用下,每次访问文件都会将文件再一次载入到内存。例如,假定我们有 50 个 OBJ_BITMAP 对象,每个对象包含大小为 100 kb 的相同图像。因此,在一般使用情况下,这 50 个对象会需要 50*100kb=5Mb 的内存。

如果我们为上传的图像声明一个资源,则此图片将仅加载到内存一次,无论要使用它的对象有多少。

重要须知: 资源仅加载到内存一次,在多次使用时会节省内存。

总结

使用资源可加速 MQL5 程序的使用和分发。创建适合的现代化交易工具需要使用基于多媒体的图像和声音文件。MQL5 中的资源概念非常简单,易于理解,因此请尝试使用。

32 位 BMP 图像可以包含alpha 通道 – 在这种情况下,它们将被应用到具有透明度的图表。

资源提供以下优点:

  • 紧凑 – 所有文件都封装在一个可执行 EX5 文件中,因此程序易于传输和启动;
  • 节省内存 – 客户端内存始终只包含每个资源的一个实例,无论在程序中资源的使用频率如何;
  • 便于存储 – 含有所有资源的一个 EX5 文件小于原来的图像和声音文件的总和。

本文译自 MetaQuotes Software Corp. 撰写的俄文原文
原文地址: https://www.mql5.com/ru/articles/261

附加的文件 |

animals_ea.mq5
(15.95 KB)
images.zip
(214.29 KB)
mysounds.zip
(65.89 KB)

 

 


MyFxtop迈投(www.myfxtop.com)-靠谱的外汇跟单社区,免费跟随高手做交易!

 

免责声明:本文系转载自网络,如有侵犯,请联系我们立即删除,另:本文仅代表作者个人观点,与迈投财经无关。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。

著作权归作者所有。
商业转载请联系作者获得授权,非商业转载请注明出处。

風險提示

MyFxtops邁投所列信息僅供參考,不構成投資建議,也不代表任何形式的推薦或者誘導行為。MyFxtops邁投非外匯經紀商,不接觸妳的任何資金。 MYFXTOPS不保證客戶盈利,不承擔任何責任。從事外彙和差價合約等金融產品的槓桿交易具有高風險,損失有可能超過本金,請量力而行,入市前需充分了解潛在的風險。過去的交易成績並不代表以後的交易成績。依據各地區法律法規,MyFxtops邁投不向中國大陸、美國、加拿大、朝鮮居民提供服務。

邁投公眾號

聯繫我們

客服QQ:981617007
Email: service@myfxtop.com

MyFxtops 邁投