English Русский Español Deutsch 日本語 Português
preview
利用 MQL5 的交互式 GUI 改进您的交易图表(第 III 部分):简易可移动交易 GUI

利用 MQL5 的交互式 GUI 改进您的交易图表(第 III 部分):简易可移动交易 GUI

MetaTrader 5交易 | 10 四月 2024, 11:18
499 0
Kailash Bai Mina
Kailash Bai Mina

概述

您好,欢迎回到我们系列的第 3 部分《利用 MQL5 的交互式 GUI 改进您的交易图表》。

在我们进入新领域之前,我们先快速回顾一下我们在第 I 部分和第 II 部分中涵盖的内容:

1. 在第 I 部分中,我们从理解图表事件概念开始,然后在同一图表上创建了两个简单的可移动仪表板。

2. 至于第 II 部分,我们更进一步。我们利用 .mqh 文件中的类令我们的代码更加高效和通用,准备好与满级的 EA/指标集成。

现在,我们已经准备好了第 III 部分!在该部分中,我们把重点放在通过将 GUI 集成到仪表板中来增强仪表板。因为若无 GUI,仪表板就无法达到其预期目的。

以下的快速概览,就是我们将在本文中解决的问题:

  1. 我们在创造什么?
  2. 创建简易的静态交易仪表板
  3. 讨论令我们其中包含所有元素的静态仪表板能够移动的方式。
  4. 使用讨论的方式来移动我们的静态仪表板
  5. 结束语


我们在创造什么?

我们瞄准的目标是创建一个带有 GUI 的可移动仪表板,为此,我们需要决定我们将要创建的内容。我选择了一个简单的 EA,特别是简易交易 EA,作为我们的基础。

首先,我们需要构建这个静态仪表板,即简易交易 EA。有效地做到这一点至关重要,因为我们正在创建一个成熟的 EA。至于效率,我指的是我们不能仅开一个文件,并在那里编写所有代码。取而代之,我们需要一个经过深思熟虑的计划,允许我们跨若干规划良好的 .mqh 文件,并在每个文件里编写最少的代码。最重要的是,在为我们的可移动仪表板创建所需的静态 GUI 时,我们必须避免重复相同的代码。

以下是我们将为此目的创建的基础静态仪表板:

图例 1. 简易静态仪表盘

图例 1. 简易静态仪表盘


其构成:

元素 说明
Label 1 标题文本(Simple Trading EA V1.0)
标签就是这样。 手数
Edit 1 您在上图中看到的白色编辑框,里面写着 0.01。
Button 1 绿色的 “Buy(买入)”按钮。
Button 2 红色的 “Sell(卖出)” 按钮。
Rectangle Label 1 标题栏,写有 “Simple Trading EA V1.0” 字样的深蓝色栏。
Rectangle Label 2  主仪表板区域,浅蓝色仪表板。

因此,我们的仪表板由这七个组件构成。如果您问我,我会说这是我们创建的一个非常漂亮的仪表板,仅结合了七个元素。

现在,我们开始编写仪表板代码。


创建简易的静态交易仪表板

我们要编写什么样的类?我们想想......

我们需要 2 个标签、2 个按钮、1 个编辑框、和 2 个矩形标签。故此,我们创建 4 个 .mqh 文件,每种元素一个文件。以下是我们项目的文件夹结构:

  • Simple Trading EA/
    • SimpleTradingEA.mq5
    • Button.mqh
    • Label.mqh
    • Edit.mqh
    • RectangleLabel.mqh

这些就是我们将在其中编写代码的文件。现在,我们创建第一个文件 “SimpleTradingEA.mq5”,其为我们的主要 EA 文件。

我已经删除了 OnTick() 函数,因为在这个项目中我们不需要它。以下是文件此刻的样子:

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
   {
    return(INIT_SUCCEEDED);
   }

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
   {
   }
//+------------------------------------------------------------------+

现在我们制定一个计划。我们将按以下顺序构建静态仪表板:

  1. 标题栏
  2. 主仪表板实体
  3. 标题文本
  4. “Lot Size:” 文本
  5. 编辑框
  6. 买入和卖出按钮
  7. 添加任何必要的收尾润色

这看似是一个合理的后续顺序。我们开始吧,

  1. 标题栏

    为了创建标题栏,我们需要使用矩形标签对象。故此,我们创建一个类来处理与矩形标签对象相关的所有事宜。我们将创建一个 .mqh 文件;从简起见,我们将其命名为 “RectangleLabel.mqh”,且再为简约而将该类命名为 “RectangleLabel”。
    下面是我们创建的空类:

    //+------------------------------------------------------------------+
    //| Class Definition: RectangleLabel                                 |
    //+------------------------------------------------------------------+
    class RectangleLabel
       {
    public:
                         RectangleLabel(void);
                        ~RectangleLabel(void);
       };
    
    //+------------------------------------------------------------------+
    //| Constructor: RectangleLabel                                      |
    //+------------------------------------------------------------------+
    RectangleLabel::RectangleLabel(void)
       {
       }
    
    //+------------------------------------------------------------------+
    //| Destructor: RectangleLabel                                       |
    //+------------------------------------------------------------------+
    RectangleLabel::~RectangleLabel(void)
       {
       }
    //+------------------------------------------------------------------+

    我们将需要一些函数,我们看看

    1. Create             -> 创建矩形标签
    2. Destroy            -> 注销仪表板
    3. SetBorderType  -> 设置边框类型
    4. SetBGColor       -> 设置背景颜色

    我们在成员函数清单中声明上述函数。现在我们的类看起来像这样:

    //+------------------------------------------------------------------+
    //| Class Definition: RectangleLabel                                 |
    //+------------------------------------------------------------------+
    class RectangleLabel
       {
    public:
                         RectangleLabel(void); // Constructor
                        ~RectangleLabel(void); // Destructor
        void             Create(string name, int xDis, int yDis, int xSize, int ySize); //Creates a Rectangle Label with the given parameters
        void             Destroy(); // Destroys the Rectangle Label
        void             SetBorderType(ENUM_BORDER_TYPE borderType); // Sets the border type of the Rectangle Label
        void             SetBGColor(color col); // Sets the background color of the Rectangle Label
       };
    //+------------------------------------------------------------------+

    我们编写一个基本的创建函数:

    //+------------------------------------------------------------------+
    //| RectangleLabel Class - Create Method                             |
    //+------------------------------------------------------------------+
    void RectangleLabel::Create(string name, int xDis, int yDis, int xSize, int ySize)
       {
        ObjectCreate(0, name, OBJ_RECTANGLE_LABEL, 0, 0, 0); // Create the Rectangle Label object
        ObjectSetInteger(0, name, OBJPROP_XDISTANCE, xDis); // Set the X-axis distance
        ObjectSetInteger(0, name, OBJPROP_YDISTANCE, yDis); // Set the Y-axis distance
        ObjectSetInteger(0, name, OBJPROP_XSIZE, xSize); // Set the X size
        ObjectSetInteger(0, name, OBJPROP_YSIZE, ySize); // Set the Y size
       }
    //+------------------------------------------------------------------+

    我们在同一行中创建 Destroy、SetBorderType 和 SetBGColor,因为它们只需要一行。此处我们更新的类:

    //+------------------------------------------------------------------+
    //| Class Definition for the Rectangle Label                         |
    //+------------------------------------------------------------------+
    class RectangleLabel
       {
    private:
        string           _name; // Name of the rectangle label
    public:
                         RectangleLabel(void); // Constructor
                        ~RectangleLabel(void); // Destructor
    
        void             Create(string name, int xDis, int yDis, int xSize, int ySize); // Method to create a rectangle label with given dimensions
    
        void             Destroy() {ObjectDelete(0, _name);} // Method to delete the object using the object's name
    
        void             SetBorderType(ENUM_BORDER_TYPE borderType) {ObjectSetInteger(0, _name, OBJPROP_BORDER_TYPE, borderType);} // Method to set the border type for the rectangle label
    
        void             SetBGColor(color col) {ObjectSetInteger(0, _name, OBJPROP_BGCOLOR, col);} // Method to set the background color for the rectangle label
       };
    //+------------------------------------------------------------------+

    还有,我们添加了一个名为 “_name” 的私密变量,因为 ObjectDelete 需要一个名称,故我们在 Create 函数中设置了 “_name”,现在它如下所示:

    //+------------------------------------------------------------------+
    //| Rectangle Label Creation Method                                  |
    //+------------------------------------------------------------------+
    void RectangleLabel::Create(string name, int xDis, int yDis, int xSize, int ySize)
       {
        ObjectCreate(0, name, OBJ_RECTANGLE_LABEL, 0, 0, 0); // Create rectangle label object
        ObjectSetInteger(0, name, OBJPROP_XDISTANCE, xDis); // Set X distance
        ObjectSetInteger(0, name, OBJPROP_YDISTANCE, yDis); // Set Y distance
        ObjectSetInteger(0, name, OBJPROP_XSIZE, xSize); // Set X size
        ObjectSetInteger(0, name, OBJPROP_YSIZE, ySize); // Set Y size
        _name = name; // Assign the name to the member variable
       }
    //+------------------------------------------------------------------+

    我们简单地在最后一行加上 “_name = name;”,即可在 _name 变量里设置创建矩形标签时的名称。

    如果您正在犹豫,代码在何处可令它移动,我们此刻忽略了这个方面,是为了保持简单,直到我们创建一个简易静态仪表板。

    现在,我们在主文件(即 SimpleTradingEA.mq5)中使用这个类来查看结果:


    我们首先使用 “#include” 包含 RectangleLabel.mqh 文件,并创建了一个名为 TitleBar 的类实例,因为我们会用这个 RectangleLabel 类实例创建仪表板的标题栏,我们将还会将其用于主仪表板实体。

    然后,我们用该实例在图表上创建一个矩形标签,坐标为(100,100),尺寸为 200x20。然后我们将其边框设置为平坦 (BORDER_FLAT),因为在我看来这更好;您可以根据自己的喜好进行更改。然后我们调用 ChartRedraw(0) 函数重绘图表;这样,仪表板会立即在图表上创建。否则,它可能会等待下一次价格更新,即跳价来临时。

    这一切都在 OnInit() 中,即,仅一次性执行就可在图表上创建和显示仪表板。

    最后,我们在 OnDeinit() 里,即从图表中删除 EA 时,调用已创建的 Destroy() 函数注销仪表板。

    结果:

    图例 2. 标题栏

    图例 2. 标题栏


  2. 主仪表板实体

    我们再次利用 RectangleLabel 类来创建主体。这很简单;我们只需要创建另一个实例;我们将其命名为 “MainDashboardBody”,并在创建标题栏后在 OnInit() 中添加以下简单代码,最后在 OnDeinit() 中加入 MainDashboardBody.Destroy():

    // Creating a rectangle label called "MainDashboardBody" with specific dimensions
    MainDashboardBody.Create("MainDashboardBody", 100, 119, 200, 100);
    // Setting the border type of the "MainDashboardBody" rectangle label to be flat
    MainDashboardBody.SetBorderType(BORDER_FLAT);
    之后,我们的代码如下所示:
    #include "RectangleLabel.mqh" // Including the RectangleLabel class definition
    RectangleLabel TitleBar; // Declaration of a TitleBar object
    RectangleLabel MainDashboardBody; // Declaration of a MainDashboardBody object
    
    //+------------------------------------------------------------------+
    //| Expert initialization function                                   |
    //+------------------------------------------------------------------+
    int OnInit()
       {
        TitleBar.Create("TitleBar", 100, 100, 200, 20); // Creating the TitleBar with specified dimensions
        TitleBar.SetBorderType(BORDER_FLAT); // Setting the border type of TitleBar to be flat
    
        MainDashboardBody.Create("MainDashboardBody", 100, 119, 200, 100); // Creating the MainDashboardBody with specified dimensions
        MainDashboardBody.SetBorderType(BORDER_FLAT); // Setting the border type of MainDashboardBody to be flat
    
        ChartRedraw(0); // Redrawing the chart to reflect changes
        return(INIT_SUCCEEDED); // Indicating successful initialization
       }
    
    //+------------------------------------------------------------------+
    //| Expert deinitialization function                                 |
    //+------------------------------------------------------------------+
    void OnDeinit(const int reason)
       {
        MainDashboardBody.Destroy(); // Destroying the MainDashboardBody object
        TitleBar.Destroy(); // Destroying the TitleBar object
       }
    //+------------------------------------------------------------------+

    如此这般,我们的结果看起来相当不错:

    图例 3. 添加了主仪表板实体

    图例 3. 添加了主仪表板实体



  3. 标题文本

    为了添加标题文本,我们需要创建一个类似于 RectangleLabel 的类,但专门当做标签,允许我们添加文本。下面是一个名为 Label 的新类代码:

    //+------------------------------------------------------------------+
    //| Label class definition                                           |
    //+------------------------------------------------------------------+
    class Label
       {
    private:
        string           _name; // Name of the label
    public:
                         Label(void); // Constructor
                        ~Label(void); // Destructor
                        
        void             Create(string name, int xDis, int yDis); // Method to create a label    
        void             Destroy() {ObjectDelete(0, _name);} // Method to destroy a label    
        void             SetTextColor(color col) {ObjectSetInteger(0, _name, OBJPROP_COLOR, col);} // Method to set the text color    
        void             SetText(string text) {ObjectSetString(0, _name, OBJPROP_TEXT, text);} // Method to set the text content    
        string           GetText() {return ObjectGetString(0, _name, OBJPROP_TEXT);} // Method to retrieve the text content    
        void             SetFontSize(int fontSize) {ObjectSetInteger(0, _name, OBJPROP_FONTSIZE, fontSize);} // Method to set the font size    
        void             SetFont(string fontName) {ObjectSetString(0, _name, OBJPROP_FONT, fontName);} // Method to set the font name
       };
    
    //+------------------------------------------------------------------+
    //| Constructor                                                      |
    //+------------------------------------------------------------------+
    Label::Label(void)
       {
    
       }
    
    //+------------------------------------------------------------------+
    //| Destructor                                                       |
    //+------------------------------------------------------------------+
    Label::~Label(void)
       {
    
       }
    
    //+------------------------------------------------------------------+
    //| Method to create a label object                                  |
    //+------------------------------------------------------------------+
    void Label::Create(string name, int xDis, int yDis)
       {
        // Code to create label object, set its position, and assign its name
        ObjectCreate(0, name, OBJ_LABEL, 0, 0, 0);
        ObjectSetInteger(0, name, OBJPROP_XDISTANCE, xDis);
        ObjectSetInteger(0, name, OBJPROP_YDISTANCE, yDis);
        _name = name;
       }
    //+------------------------------------------------------------------+
    • 在名为 Label.mqh 的新 .mqh 文件中创建了名为 Label 的类
    • 声明一个名为 _name 的私密变量,以私密形式存储名称。
    • 创建一个名为 Create 的函数,其中包含 3 个必需的参数:name、xDis、yDis。大小与标签对象无关,若要更改文本大小,我们更改字号
    • 创建一个名为 Destroy 的函数来注销标签
    • 创建一个函数 SetTextColor 来设置文本颜色
    • 创建一个 SetText 函数来设置标签对象的文本
    • 创建一个函数 GetText 来获取标签对象的文本,该对象当然是返回字符串
    • 创建一个函数 SetFontSize 当然是为了设置字号
    • 创建一个 setFont 函数设置字体,需要字符串格式的字体名称,当然该字体应该在操作系统中可用/已安装

    对于标签就是这些。现在我们用它在图表上创建一个标签对象,图表上实际上没有 2 个标签对象。
    现在我们的 SimpleTradingEA.mq5 如下所示:
    #include "RectangleLabel.mqh" // Including the RectangleLabel class definition
    RectangleLabel TitleBar; // Declaration of a TitleBar object
    RectangleLabel MainDashboardBody; // Declaration of a MainDashboardBody object
    
    #include "Label.mqh" // Including the Label class definition
    Label TitleText; // Declaration of a Label object
    
    //+------------------------------------------------------------------+
    //| Expert initialization function                                   |
    //+------------------------------------------------------------------+
    int OnInit()
       {
        TitleBar.Create("TitleBar", 100, 100, 200, 20); // Creating the TitleBar with specified dimensions
        TitleBar.SetBorderType(BORDER_FLAT); // Setting the border type of TitleBar to be flat
    
        MainDashboardBody.Create("MainDashboardBody", 100, 119, 200, 100); // Creating the MainDashboardBody with specified dimensions
        MainDashboardBody.SetBorderType(BORDER_FLAT); // Setting the border type of MainDashboardBody to be flat
        
        TitleText.Create("TitleText", 110, 101); // Creating the TitleText at (110,101)
        TitleText.SetText("Simple Trading EA V1.0"); // Setting its text to "Simple Trading EA V1.0"
        TitleText.SetFontSize(10); // Setting its font size to 10
        TitleText.SetTextColor(clrBlack); // Setting its text color to clrBlack
        
        ChartRedraw(0); // Redrawing the chart to reflect changes
        return(INIT_SUCCEEDED); // Indicating successful initialization
       }
    
    //+------------------------------------------------------------------+
    //| Expert deinitialization function                                 |
    //+------------------------------------------------------------------+
    void OnDeinit(const int reason)
       {
        MainDashboardBody.Destroy(); // Destroying the MainDashboardBody object
        TitleBar.Destroy(); // Destroying the TitleBar object
        TitleText.Destroy(); // Destroying the TitleText object
       }
    //+------------------------------------------------------------------+
    • 创建名为 TitleText 的标签实例
    • 调用 TitleText.Create 函数创建标题文本
    • 调用 TitleText.SetText 将标题文本设置为 “Simple Trading EA V1.0”
    • 调用 TitleText.SetFontSize 将字号设置为 10
    • 调用 TitleText.SetTextColor 将颜色设置为黑色
    • 调用 TitleText.Destroy 注销 OnDeinit 中的标题文本对象

    结果:


    图例 4. 添加了标题文本
    图例 4. 添加了标题文本

  4. “Lot Size:” 文本

    对于 “Lot Size:” 文本,您将遵循与标题文本类似的流程。最终代码如下:

    #include "RectangleLabel.mqh" // Including the RectangleLabel class definition
    RectangleLabel TitleBar; // Declaration of a TitleBar object
    RectangleLabel MainDashboardBody; // Declaration of a MainDashboardBody object
    
    #include "Label.mqh" // Including the Label class definition
    Label TitleText; // Declaration of a Label object
    Label LotSizeText; // Declaration of a LotSizeText object
    
    //+------------------------------------------------------------------+
    //| Expert initialization function                                   |
    //+------------------------------------------------------------------+
    int OnInit()
       {
        TitleBar.Create("TitleBar", 100, 100, 200, 20); // Creating the TitleBar with specified dimensions
        TitleBar.SetBorderType(BORDER_FLAT); // Setting the border type of TitleBar to be flat
    
        MainDashboardBody.Create("MainDashboardBody", 100, 119, 200, 100); // Creating the MainDashboardBody with specified dimensions
        MainDashboardBody.SetBorderType(BORDER_FLAT); // Setting the border type of MainDashboardBody to be flat
        
        TitleText.Create("TitleText", 110, 101); // Creating the TitleText at (110,101)
        TitleText.SetText("Simple Trading EA V1.0"); // Setting its text to "Simple Trading EA V1.0"
        TitleText.SetFontSize(10); // Setting its font size to 10
        TitleText.SetTextColor(clrBlack); // Setting its text color to clrBlack
        
        LotSizeText.Create("LotSizeText", 110, 140); // Creating the LotSizeText at (110,140)
        LotSizeText.SetText("Lot Size:"); // Setting its text to "Lot Size:"
        LotSizeText.SetFontSize(12); // Setting its font size to 12
        LotSizeText.SetTextColor(clrBlack); // Setting its text color to clrBlack
        
        ChartRedraw(0); // Redrawing the chart to reflect changes
        return(INIT_SUCCEEDED); // Indicating successful initialization
       }
    
    //+------------------------------------------------------------------+
    //| Expert deinitialization function                                 |
    //+------------------------------------------------------------------+
    void OnDeinit(const int reason)
       {
        MainDashboardBody.Destroy(); // Destroying the MainDashboardBody object
        TitleBar.Destroy(); // Destroying the TitleBar object
        TitleText.Destroy(); // Destroying the TitleText object
        LotSizeText.Destroy(); // Destroying the LotSizeText object
       }
    //+------------------------------------------------------------------+
    • 建名为 LotSizeText 的标签实例
    • 调用 LotSizeText.Create 函数创建手数文本
    • 调用 LotSizeText.SetText 将文本设置为 “Lot Size:”
    • 调用 LotSizeText.SetFontSize 将字号设置为 12
    • 调用 LotSizeText.SetTextColor 将颜色设置为黑色
    • 调用 LotSizeText.Destroy 注销 OnDeinit 中的标签对象

    仅此而已。结果:


    图例 5. 添加了 “Lot Size:” 文本
    图例 5. 添加了 “Lot Size:” 文本





  5. 编辑框

    对于编辑框,您将创建一个与标签类非常相似的类。下面是一个名为 Edit 的新类代码:

    //+------------------------------------------------------------------+
    //| Edit class definition                                            |
    //+------------------------------------------------------------------+
    class Edit
       {
    private:
        string           _name; // Name of the edit control
    public:
                         Edit(void); // Constructor
                        ~Edit(void); // Destructor
                        
        void             Create(string name, int xDis, int yDis, int xSize, int ySize); // Method to create an edit control    
        void             Destroy() {ObjectDelete(0, _name);} // Method to destroy an edit control    
        void             SetBorderColor(color col) {ObjectSetInteger(0, _name, OBJPROP_BORDER_COLOR, col);} // Method to set the border color    
        void             SetBGColor(color col) {ObjectSetInteger(0, _name, OBJPROP_BGCOLOR, col);} // Method to set the background color    
        void             SetTextColor(color col) {ObjectSetInteger(0, _name, OBJPROP_COLOR, col);} // Method to set the text color    
        void             SetText(string text) {ObjectSetString(0, _name, OBJPROP_TEXT, text);} // Method to set the text content    
        string           GetText() {return ObjectGetString(0, _name, OBJPROP_TEXT);} // Method to retrieve the text content
       };
    
    //+------------------------------------------------------------------+
    //| Constructor                                                      |
    //+------------------------------------------------------------------+
    Edit::Edit(void)
       {
    
       }
    
    //+------------------------------------------------------------------+
    //| Destructor                                                       |
    //+------------------------------------------------------------------+
    Edit::~Edit(void)
       {
    
       }
    
    //+------------------------------------------------------------------+
    //| Method to create an edit control object                          |
    //+------------------------------------------------------------------+
    void Edit::Create(string name, int xDis, int yDis, int xSize, int ySize)
       {
        // Code to create edit control object, set its position, size, and assign its name
        ObjectCreate(0, name, OBJ_EDIT, 0, 0, 0);
        ObjectSetInteger(0, name, OBJPROP_XDISTANCE, xDis);
        ObjectSetInteger(0, name, OBJPROP_YDISTANCE, yDis);
        ObjectSetInteger(0, name, OBJPROP_XSIZE, xSize);
        ObjectSetInteger(0, name, OBJPROP_YSIZE, ySize);
        _name = name;
       }
    //+------------------------------------------------------------------+
    • 在名为 Edit.mqh 的新 .mqh 文件中创建了名为 Edit 的类
    • 声明一个名为 _name 的私密变量,以私密形式存储名称。
    • 创建一个名为 Create 的函数,其中包含 5 个必需参数:name、xDis、yDis、xSize、ySize
    • 创建一个名为 Destroy 的函数来注销编辑框对象
    • 创建一个函数 SetBorderColor 来设置边框颜色
    • 创建一个函数 SetBGColor 来设置背景色为 WhiteSmoke
    • 创建一个函数 SetTextColor 来设置编辑框内文本的文本颜色
    • 创建一个函数 SetText 来设置文本
    • 创建一个函数 GetText 来获取文本

    您现在可以在 SimpleTradingEA 中使用编辑框类,如下所示:

    #include "RectangleLabel.mqh" // Including the RectangleLabel class definition
    RectangleLabel TitleBar; // Declaration of a TitleBar object
    RectangleLabel MainDashboardBody; // Declaration of a MainDashboardBody object
    
    #include "Label.mqh" // Including the Label class definition
    Label TitleText; // Declaration of a Label object
    Label LotSizeText; // Declaration of a LotSizeText object
    
    #include "Edit.mqh" // Including the Edit class definition
    Edit LotSize; // Declaration of a LotSize object
    
    //+------------------------------------------------------------------+
    //| Expert initialization function                                   |
    //+------------------------------------------------------------------+
    int OnInit()
       {
        TitleBar.Create("TitleBar", 100, 100, 200, 20); // Creating the TitleBar with specified dimensions
        TitleBar.SetBorderType(BORDER_FLAT); // Setting the border type of TitleBar to be flat
    
        MainDashboardBody.Create("MainDashboardBody", 100, 119, 200, 100); // Creating the MainDashboardBody with specified dimensions
        MainDashboardBody.SetBorderType(BORDER_FLAT); // Setting the border type of MainDashboardBody to be flat
        
        TitleText.Create("TitleText", 110, 101); // Creating the TitleText at (110,101)
        TitleText.SetText("Simple Trading EA V1.0"); // Setting its text to "Simple Trading EA V1.0"
        TitleText.SetFontSize(10); // Setting its font size to 10
        TitleText.SetTextColor(clrBlack); // Setting its text color to clrBlack
        
        LotSizeText.Create("LotSizeText", 110, 140); // Creating the LotSizeText at (110,140)
        LotSizeText.SetText("Lot Size:"); // Setting its text to "Lot Size:"
        LotSizeText.SetFontSize(12); // Setting its font size to 12
        LotSizeText.SetTextColor(clrBlack); // Setting its text color to clrBlack
        
        LotSize.Create("LotSize", 220, 140, 50, 20); // Creating the LotSize with specified dimensions
        LotSize.SetBorderColor(clrBlack); // Setting its Border Color to clrBlack
        LotSize.SetBGColor(clrWhiteSmoke); // Setting its BG Color to clrWhiteSmoke
        LotSize.SetText("0.01"); // Setting its text to 0.01
        LotSize.SetTextColor(clrBlack); // Setting its text color to clrBlack
        
        ChartRedraw(0); // Redrawing the chart to reflect changes
        return(INIT_SUCCEEDED); // Indicating successful initialization
       }
    
    //+------------------------------------------------------------------+
    //| Expert deinitialization function                                 |
    //+------------------------------------------------------------------+
    void OnDeinit(const int reason)
       {
        MainDashboardBody.Destroy(); // Destroying the MainDashboardBody object
        TitleBar.Destroy(); // Destroying the TitleBar object
        TitleText.Destroy(); // Destroying the TitleText object
        LotSizeText.Destroy(); // Destroying the LotSizeText object
        LotSize.Destroy(); // Destroying the LotSize object
       }
    //+------------------------------------------------------------------+
    • 创建名为 LotSize 的编辑框实例
    • 调用 LotSize.Create 函数创建编辑框对象
    • 调用 LotSize.SetBorderColor 将边框颜色设置为黑色
    • 调用 LotSize.SetBGColor 将背景颜色设置为 WhiteSmoke
    • 调用 LotSize.SetText 将文本设置为 0.01,表示手数大小
    • 调用 LotSize.SetTextColor 将编辑框内的文本颜色设置为黑色
    • 调用 LotSize.Destroy 注销 OnDeinit 中的编辑框对象

  6. 买入和卖出按钮

    最后,我们来到按钮。我们为按钮创建一个类,类似针对其它元素的方式:

    //+------------------------------------------------------------------+
    //| Button class definition                                          |
    //+------------------------------------------------------------------+
    class Button
       {
    private:
        string           _name; // Name of the button control
    
    public:
                         Button(void); // Constructor
                        ~Button(void); // Destructor
                        
        void             Create(string name, int xDis, int yDis, int xSize, int ySize); // Method to create a button control    
        void             SetBorderColor(color col) {ObjectSetInteger(0, _name, OBJPROP_BORDER_COLOR, col);} // Method to set the border color    
        void             SetBGColor(color col) {ObjectSetInteger(0, _name, OBJPROP_BGCOLOR, col);} // Method to set the background color    
        void             SetText(string text) {ObjectSetString(0, _name, OBJPROP_TEXT, text);} // Method to set the text content    
        void             Destroy() {ObjectDelete(0, _name);} // Method to destroy a button control
       };
    
    //+------------------------------------------------------------------+
    //| Constructor                                                      |
    //+------------------------------------------------------------------+
    Button::Button(void)
       {
    
       }
    
    //+------------------------------------------------------------------+
    //| Destructor                                                       |
    //+------------------------------------------------------------------+
    Button::~Button(void)
       {
    
       }
    
    //+------------------------------------------------------------------+
    //| Method to create a button control object                         |
    //+------------------------------------------------------------------+
    void Button::Create(string name, int xDis = 0, int yDis = 0, int xSize = 0, int ySize = 0)
       {
        // Code to create button control object, set its position, size, and assign its name
        ObjectCreate(0, name, OBJ_BUTTON, 0, 0, 0);
        ObjectSetInteger(0, name, OBJPROP_XDISTANCE, xDis);
        ObjectSetInteger(0, name, OBJPROP_YDISTANCE, yDis);
        ObjectSetInteger(0, name, OBJPROP_XSIZE, xSize);
        ObjectSetInteger(0, name, OBJPROP_YSIZE, ySize);
        _name = name;
       }
    //+------------------------------------------------------------------+
    

    在一个名为 Button.mqh 的新 .mqh 文件中,我们创建了一个名为 Button 的类。我们声明了一个名为 _name 的私密变量,以私密形式存储名称。我们还创建了以下函数:

      • 名为 Create 的函数,具有 5 个必需参数:name、xDis、yDis、xSize、ySize。
      • 一个名为 Destroy 的函数,用于注销按钮对象。
      • 一个名为 SetBorderColor 的函数,用于设置边框颜色。
      • 一个名为 SetBGColor 的函数,用于将背景颜色设置为 WhiteSmoke。
      • 一个名为 SetText 的函数,用于设置文本。

      现在我们看一下添加按钮后的 SimpleTradingEA.mq5 主文件。您会注意到,它现在包括矩形标签、标签、编辑框、BuyButton 和 SellButton 的按钮实例。

      #include "RectangleLabel.mqh" // Including the RectangleLabel class definition
      RectangleLabel TitleBar; // Declaration of a TitleBar object
      RectangleLabel MainDashboardBody; // Declaration of a MainDashboardBody object
      
      #include "Label.mqh" // Including the Label class definition
      Label TitleText; // Declaration of a Label object
      Label LotSizeText; // Declaration of a LotSizeText object
      
      #include "Edit.mqh" // Including the Edit class definition
      Edit LotSize; // Declaration of a LotSize object
      
      #include "Button.mqh" // Including the Button class definition
      Button BuyButton; // Declaration of a BuyButton object
      Button SellButton; // Declaration of a SellButton object
      
      //+------------------------------------------------------------------+
      //| Expert initialization function                                   |
      //+------------------------------------------------------------------+
      int OnInit()
         {
          TitleBar.Create("TitleBar", 100, 100, 200, 20); // Creating the TitleBar with specified dimensions
          TitleBar.SetBorderType(BORDER_FLAT); // Setting the border type of TitleBar to be flat
      
          MainDashboardBody.Create("MainDashboardBody", 100, 119, 200, 100); // Creating the MainDashboardBody with specified dimensions
          MainDashboardBody.SetBorderType(BORDER_FLAT); // Setting the border type of MainDashboardBody to be flat
          
          TitleText.Create("TitleText", 110, 101); // Creating the TitleText at (110,101)
          TitleText.SetText("Simple Trading EA V1.0"); // Setting its text to "Simple Trading EA V1.0"
          TitleText.SetFontSize(10); // Setting its font size to 10
          TitleText.SetTextColor(clrBlack); // Setting its text color to clrBlack
          
          LotSizeText.Create("LotSizeText", 110, 140); // Creating the LotSizeText at (110,140)
          LotSizeText.SetText("Lot Size:"); // Setting its text to "Lot Size:"
          LotSizeText.SetFontSize(12); // Setting its font size to 12
          LotSizeText.SetTextColor(clrBlack); // Setting its text color to clrBlack
          
          LotSize.Create("LotSize", 220, 140, 50, 20); // Creating the LotSize with specified dimensions
          LotSize.SetBorderColor(clrBlack); // Setting its Border Color to clrBlack
          LotSize.SetBGColor(clrWhiteSmoke); // Setting its BG Color to clrWhiteSmoke
          LotSize.SetText("0.01"); // Setting its text to 0.01
          LotSize.SetTextColor(clrBlack); // Setting its text color to clrBlack
          
          BuyButton.Create("BuyButton", 110, 180, 80, 25); // Creating the BuyButton with specified dimensions
          BuyButton.SetBorderColor(clrBlack); // Setting its Border Color to clrBlack
          BuyButton.SetText("Buy"); // Setting its text to "Buy"
          BuyButton.SetBGColor(clrLime); // Setting its BG Color to clrLime
          
          SellButton.Create("SellButton", 210, 180, 80, 25); // Creating the SellButton with specified dimensions
          SellButton.SetBorderColor(clrBlack); // Setting its Border Color to clrBlack
          SellButton.SetText("Sell"); // Setting its text to "Sell"
          SellButton.SetBGColor(clrRed); // Setting its BG Color to clrRed
          
          ChartRedraw(0); // Redrawing the chart to reflect changes
          return(INIT_SUCCEEDED); // Indicating successful initialization
         }
      
      //+------------------------------------------------------------------+
      //| Expert deinitialization function                                 |
      //+------------------------------------------------------------------+
      void OnDeinit(const int reason)
         {
          MainDashboardBody.Destroy(); // Destroying the MainDashboardBody object
          TitleBar.Destroy(); // Destroying the TitleBar object
          TitleText.Destroy(); // Destroying the TitleText object
          LotSizeText.Destroy(); // Destroying the LotSizeText object
          LotSize.Destroy(); // Destroying the LotSize object
          BuyButton.Destroy(); // Destroying the BuyButton object
          SellButton.Destroy(); // Destroying the SellButton object
         }
      //+------------------------------------------------------------------+
      • 创建名为 BuyButton 的按钮实例
      • 调用 BuyButton.Create 函数创建按钮对象
      • 调用 BuyButton.SetBorderColor 将边框颜色设置为黑色
      • 调用 BuyButton.SetBGColor 将背景颜色设置为 Lime
      • 调用 BuyButton.SetText 设置文本 “Buy”
      • 调用 BuyButton.Destroy 注销 OnDeinit 中的按钮对象

      现在对于 “Sell” 按钮:

      • 创建名为 SellButton 的按钮实例
      • 调用 SellButton.Create 函数创建按钮对象
      • 调用 SellButton.SetBorderColor 将边框颜色设置为黑色
      • 调用 SellButton.SetBGColor 将背景色设置为红色
      • 调用 SellButton.SetText 设置文本 “Sell”
      • 调用 SellButton.Destroy 注销 OnDeinit 中的按钮对象

      结果:


      图例 6. 添加了 Buy(买入)和 Sell(卖出)按钮
      图例 6. 添加了 Buy(买入)和 Sell(卖出)按钮

    • 收尾润色

    • 现在进行收尾润色,我们令它变得丰富多彩。我们将进行以下修改:

      我们来做以下事宜:

      • 将标题栏颜色更改为深蓝色
      • 将主仪表板实体颜色更改为浅蓝色
      • 将标题文本颜色从黑色更改为白色
      • 将手数大小文本颜色从黑色更改为白色
      • 添加买入/卖出功能

      最终的 SimpleTradingEA.mq5 代码包括颜色变化,并包含交易函数库。它还创建一个 OnChartEvent 函数,以便在单击 “Buy” 或 “Sell” 按钮时立即相应下单。

      #include "RectangleLabel.mqh" // Including the RectangleLabel class definition
      RectangleLabel TitleBar; // Declaration of a TitleBar object
      RectangleLabel MainDashboardBody; // Declaration of a MainDashboardBody object
      
      #include "Label.mqh" // Including the Label class definition
      Label TitleText; // Declaration of a Label object
      Label LotSizeText; // Declaration of a LotSizeText object
      
      #include "Edit.mqh" // Including the Edit class definition
      Edit LotSize; // Declaration of a LotSize object
      
      #include "Button.mqh" // Including the Button class definition
      Button BuyButton; // Declaration of a BuyButton object
      Button SellButton; // Declaration of a SellButton object
      
      //+------------------------------------------------------------------+
      //| Expert initialization function                                   |
      //+------------------------------------------------------------------+
      int OnInit()
         {
          TitleBar.Create("TitleBar", 100, 100, 200, 20); // Creating the TitleBar with specified dimensions
          TitleBar.SetBorderType(BORDER_FLAT); // Setting the border type of TitleBar to be flat
          TitleBar.SetBGColor(C'27, 59, 146'); // Setting the color to RGB code: C'27, 59, 146'
      
          MainDashboardBody.Create("MainDashboardBody", 100, 119, 200, 100); // Creating the MainDashboardBody with specified dimensions
          MainDashboardBody.SetBorderType(BORDER_FLAT); // Setting the border type of MainDashboardBody to be flat
          MainDashboardBody.SetBGColor(C'102, 152, 250'); // Setting the color to RGB code: C'102, 152, 250'
          
          TitleText.Create("TitleText", 110, 101); // Creating the TitleBar at (110,101)
          TitleText.SetText("Simple Trading EA V1.0"); // Setting its text to "Simple Trading EA V1.0"
          TitleText.SetFontSize(10); // Setting its font size to 10
          TitleText.SetTextColor(clrWhite); // Setting its text color to clrWhite
          
          LotSizeText.Create("LotSizeText", 110, 140); // Creating the LotSizeText at (110,140)
          LotSizeText.SetText("Lot Size:"); // Setting its text to "Lot Size:"
          LotSizeText.SetFontSize(12); // Setting its font size to 12
          LotSizeText.SetTextColor(clrWhite); // Setting its text color to clrWhite
          
          LotSize.Create("LotSize", 220, 140, 50, 20); // Creating the LotSize with specified dimensions
          LotSize.SetBorderColor(clrBlack); // Setting its Border Color to clrBlack
          LotSize.SetBGColor(clrWhiteSmoke); // Setting its BG Color to clrWhiteSmoke
          LotSize.SetText("0.01"); // Setting its text to 0.01
          LotSize.SetTextColor(clrBlack); // Setting its text color to clrBlack
          
          BuyButton.Create("BuyButton", 110, 180, 80, 25); // Creating the BuyButton with specified dimensions
          BuyButton.SetBorderColor(clrBlack); // Setting its Border Color to clrBlack
          BuyButton.SetText("Buy"); // Setting its text to "Buy"
          BuyButton.SetBGColor(clrLime); // Setting its BG Color to clrLime
          
          SellButton.Create("SellButton", 210, 180, 80, 25); // Creating the SellButton with specified dimensions
          SellButton.SetBorderColor(clrBlack); // Setting its Border Color to clrBlack
          SellButton.SetText("Sell"); // Setting its text to "Sell"
          SellButton.SetBGColor(clrRed); // Setting its BG Color to clrRed
          
          ChartRedraw(0); // Redrawing the chart to reflect changes
          return(INIT_SUCCEEDED); // Indicating successful initialization
         }
      
      //+------------------------------------------------------------------+
      //| Expert deinitialization function                                 |
      //+------------------------------------------------------------------+
      void OnDeinit(const int reason)
         {
          MainDashboardBody.Destroy(); // Destroying the MainDashboardBody object
          TitleBar.Destroy(); // Destroying the TitleBar object
          TitleText.Destroy(); // Destroying the TitleText object
          LotSizeText.Destroy(); // Destroying the LotSizeText object
          LotSize.Destroy(); // Destroying the LotSize object
          BuyButton.Destroy(); // Destroying the BuyButton object
          SellButton.Destroy(); // Destroying the SellButton object
         }
      
      //+------------------------------------------------------------------+
      //| Chart event handling function                                    |
      //+------------------------------------------------------------------+
      void OnChartEvent(const int id, const long& lparam, const double& dparam, const string& sparam)
         {
          // Handles click events for Buy and Sell buttons and opens corresponding positions
          if(id == CHARTEVENT_OBJECT_CLICK) {
              double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
              double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
              if(sparam == "BuyButton") {
                  trade.PositionOpen(_Symbol, ORDER_TYPE_BUY, (double)LotSize.GetText(), ask, 0, 0);
              }
              if(sparam == "SellButton") {
                  trade.PositionOpen(_Symbol, ORDER_TYPE_SELL, (double)LotSize.GetText(), bid, 0, 0);
              }
          }
         }
      //+------------------------------------------------------------------+

      变化:

      1. 颜色修改:

        • 调用 TitleBar.SetBGColor(C'27, 59, 146') 将标题栏的背景色更改为深蓝色。
        • 调用 MainDashboardBody.SetBGColor(C'102, 152, 250') 将主仪表板实体的颜色更新为浅蓝色。
        • 调用 TitleText.SetTextColor(clrWhite) 将标题文本的颜色更改为白色。
        • 调用 LotSizeText.SetTextColor(clrWhite) 将手数文本的颜色调整为白色。
      2. 包含交易函数库:

        • 集成了交易函数库,并用以下代码创建了一个名为 trade 的实例:
          #include <Trade/Trade.mqh>
          CTrade trade;

      3. 创建 OnChartEvent 函数:

        实现了一个 OnChartEvent 函数,该函数在单击 “Buy” 或 “Sell” 按钮时立即执行相应的订单。代码如下:

        //+------------------------------------------------------------------+
        //| Chart event handling function                                    |
        //+------------------------------------------------------------------+
        void OnChartEvent(const int id, const long& lparam, const double& dparam, const string& sparam)
           {
            if(id == CHARTEVENT_OBJECT_CLICK) {
                double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
                double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
                if(sparam == "BuyButton") {
                    trade.PositionOpen(_Symbol, ORDER_TYPE_BUY, (double)LotSize.GetText(), ask, 0, 0);
                }
                if(sparam == "SellButton") {
                    trade.PositionOpen(_Symbol, ORDER_TYPE_SELL, (double)LotSize.GetText(), bid, 0, 0);
                }
            }
           }
        //+------------------------------------------------------------------+
        如果事件 ID 等于 CHARTEVENT_OBJECT_CLICK,则该函数会检测对象点击,通过 sparam 提取被点击对象的名称,检查对象名称是 “BuyButton” 亦或 “SellButton”,然后调用交易函数库进行相应的交易。

      最终结果: 


      图例 7. 完成简易交易 EA(静态)
      图例 7. 完成简易交易 EA(静态)


    本章节就此完结。


    讨论令我们其中包含所有元素的静态仪表板能够移动的方式。

    现在真正的工作开始了。我们如何让所有东西都可移动?我们思考一下。

    此刻,我们可以令任何单个元素可移动。但我们需要的是让所有元素都移动起来。然后,我们让一个元素移动,且所有其它元素都跟随它。我们可以使用 CustomChartEvent 令其它元素从字面上跟随主动元素,但不幸的是,该方法速度很慢,因此效率低下。由此,我发现最有效的方法是移动我们的主动元素(所有其它元素将围绕它移动),并同时移动其它元素。这只是理论,但我们如何实施它呢?

    我们将主动元素称为“中心元素”,并将标题栏设置为 “Central Element”。现在我们将围绕它移动所有其它元素。

    以前,我们调用在类中定义的名为 OnEvent 的函数来移动单个元素。现在我们将修改此函数,如此其可移动单个元素,然后按精确的距离移动所有其它元素。

    下面是我们当前的 OnEvent 函数:

    //+------------------------------------------------------------------+
    //|                                                                  |
    //+------------------------------------------------------------------+
    void RectangleLabel::OnEvent(int id, long lparam, double dparam, string sparam)
      {
       //Verify the event that triggered the OnChartEvent was CHARTEVENT_MOUSE_MOVE because we only want to execute out code when that is the case
       if(id == CHARTEVENT_MOUSE_MOVE)
         {
          //define X, Y, XDistance, YDistance, XSize, YSize
          int X = (int)lparam;
          int Y = (int)dparam;
          int MouseState = (int)sparam;
    
          string name = Name;
          int XDistance = (int)ObjectGetInteger(0, name, OBJPROP_XDISTANCE); //Should be 100 initially as we set it in OnInit()
          int YDistance = (int)ObjectGetInteger(0, name, OBJPROP_YDISTANCE); //Should be 100 initially as we set it in OnInit()
          int XSize = (int)ObjectGetInteger(0, name, OBJPROP_XSIZE); //Should be 200 initially as we set it in OnInit()
          int YSize = (int)ObjectGetInteger(0, name, OBJPROP_YSIZE); //Should be 200 initially as we set it in OnInit()
    
          if(previousMouseState == 0 && MouseState == 1) //Check if this was the MLB first click
            {
             mlbDownX = X; //Set mlbDownX (Variable that stores the initial MLB X location) equal to the current X
             mlbDownY = Y; //Set mlbDownY (Variable that stores the initial MLB Y location) equal to the current Y
             mlbDownXDistance = XDistance; //Set mlbDownXDistance (Variable that stores the initial XDistance i.e. Width of the dashboard) equal to the current XDistance
             mlbDownYDistance = YDistance; //Set mlbDownYDistance (Variable that stores the initial YDistance i.e. Height of the dashboard) equal to the current YDistance
    
             if(X >= XDistance && X <= XDistance + XSize && Y >= YDistance && Y <= YDistance + YSize) //Check if the click was on the dashboard
               {
                movingState = true; //If yes the set movingState to True
               }
    
            }
    
          if(movingState)//if movingState is true, Update the Dashboard position
            {
             ChartSetInteger(0, CHART_MOUSE_SCROLL, false);//Restrict Chart to be moved by Mouse
             ObjectSetInteger(0, name, OBJPROP_XDISTANCE, mlbDownXDistance + X - mlbDownX);//Update XDistance to: mlbDownXDistance + (X - mlbDownX)
             ObjectSetInteger(0, name, OBJPROP_YDISTANCE, mlbDownYDistance + Y - mlbDownY);//Update YDistance to: mlbDownYDistance + (Y - mlbDownY)
             ChartRedraw(0); //Redraw Chart
            }
    
          if(MouseState == 0)//Check if MLB is not pressed
            {
             movingState = false;//set movingState again to false
             ChartSetInteger(0, CHART_MOUSE_SCROLL, true);//allow the cahrt to be moved again
            }
    
          previousMouseState = MouseState;//update the previousMouseState at the end so that we can use it next time and copare it with new value
         }
      }
    //+------------------------------------------------------------------+

    我知道我们还没有将此函数添加到 RectangleLabel 类中;我们将在方法讨论完毕后再做这件事。

    现在,我们移动任何物体时需要什么?其名称,对吧?

    我们要做的十分简单:我们将遍历这些名称,并按中心元素相同的距离移动其它对象。但这里有一个很难发现的重大缺陷。

    每当鼠标移动时,我们都会像这样设置中心元素的 XDis 和 YDis:

    ObjectSetInteger(0, name, OBJPROP_XDISTANCE, mlbDownXDistance + X - mlbDownX);//Update XDistance to: mlbDownXDistance + (X - mlbDownX)
    ObjectSetInteger(0, name, OBJPROP_YDISTANCE, mlbDownYDistance + Y - mlbDownY);//Update YDistance to: mlbDownYDistance + (Y - mlbDownY)

    此处,我们知道按下鼠标 MLB 时中心元素的 XDis 和 YDis。故此,我们也需要知道其它元素的信息。然而,这将令该函数变得非常复杂或效率低下,因此我们需要一种更好的方式。

    经过仔细检查,更好的方式就在我们面前。我们只需要保持“中心元素和其它元素之间的 X 距离和 Y 距离”。是的,就这么简单。

    故此,我们将记下“中心元素和其它元素之间的 X 距离和 Y 距离”,并保持该距离。我们如何记录这些距离呢?好吧,在某个时候,我们将把其它元素添加到中心元素当中,在这一点上,我们将注意到“中心元素和其它元素之间的 X 距离和 Y 距离”。

    重申一下,在某个点,我们将使用其它元素的名称,把它们添加到中心元素当中,在此点,我们将保存“中心元素和其它元素之间的 X 距离和 Y 距离”。然后,我们将保持其它元素和中心元素之间的距离。我们将在更新中心元素的位置后更新该距离。

    这就是我们完成任务的方式。现在,我们将其付诸行动。


    使用讨论的方式来移动我们的静态仪表板

    由此,我们讨论一下中心元素和其它元素之间的名称、X 距离和 Y 距离的存储位置。这是我们需要存储的仅有的两类信息。

    我们将在 RectangleLabel 类中创建一个名为 Add 的函数。调用该函数,我们将存储以下两件事:

    1. addedNames 数组中的名称
    2. 而中心元素与其它元素之间的 X 距离和 Y 距离,分别在 addedXDisDifference 和 addedYDisDifference 之中。

    关于命名约定,“added” 意味着变量与添加到中心元素的另一个元素相关,而 “XDis” 和 “YDis” 则相当简单。“Difference” 表明变量与差值有关,因此它是一个合理的名称。讨论名称的原因是为了减少任何混淆,因为正确的变量名称可以最大限度地减少误解。

    我们声明这些变量:

    string           addedNamed[];
    int              addedXDisDiffrence[], addedYDisDiffrence[];

    请注意,我们将它们声明为私密,因为我们不需要将它们公开。此外,它们都是数组。

    现在,我们创建 Add 函数:

    //+------------------------------------------------------------------+
    //| Method to add an object by name to the rectangle label           |
    //+------------------------------------------------------------------+
    void RectangleLabel::Add(string name)
       {
        ArrayResize(addedNames, ArraySize(addedNames) + 1);
        ArrayResize(addedXDisDiffrence, ArraySize(addedXDisDiffrence) + 1);
        ArrayResize(addedYDisDiffrence, ArraySize(addedYDisDiffrence) + 1);
        
        addedNames[ArraySize(addedNames) - 1] = name;
        addedXDisDiffrence[ArraySize(addedXDisDiffrence) - 1] = ObjectGetInteger(0, _name, OBJPROP_XDISTANCE) - ObjectGetInteger(0, name, OBJPROP_XDISTANCE);
        addedYDisDiffrence[ArraySize(addedYDisDiffrence) - 1] = ObjectGetInteger(0, _name, OBJPROP_YDISTANCE) - ObjectGetInteger(0, name, OBJPROP_YDISTANCE);
       }
    //+------------------------------------------------------------------+

    此函数在 RectangleLabel 类中声明,因为 TitleBar 是我们的中心元素,它本质上是一个 RECTANGLE_LABEL 对象。显然,因为我们要在这个函数中使用它们,故我们在同一类中声明变量。

    此函数的作用是接受名称作为参数,然后将这三个数组的大小增加一个。在最后一个索引处,我们存储相应的数据。对于 Name,我们只需存储名称。对于距离差值(X 和 Y),我们存储中心元素(在本例中为 TitleBar)和其名称作为参数提供的元素之间的差值。这构成了我们的 Add 函数。

    接下来,我们需要修改 OnEvent 函数。我们创建一个循环来遍历 addedNames 数组,并保持 TitleBar 和命名元素之间的距离,将其设置为新的 TitleBar X/Y 距离减去相应数组中给出的差值。

    for(int i = 0; i < ArraySize(addedNames); i++)
       {
        ObjectSetInteger(0, addedNames[i], OBJPROP_XDISTANCE, mlbDownXDistance + X - mlbDownX - addedXDisDiffrence[i]);
        ObjectSetInteger(0, addedNames[i], OBJPROP_YDISTANCE, mlbDownYDistance + Y - mlbDownY - addedYDisDiffrence[i]);
       }

    请注意,带下划线的部分是 TitleBar(中心元素)的新 X/Y 距离,我们减去相应数组中给出的差值(指中心元素与其它元素之间 X 距离和 Y 距离的差值)。

    我们把这个循环放在哪里?我们把它放在中心元素更新之后。

    下面是我们的新 OnEvent 函数:

    //+------------------------------------------------------------------+
    //| Event handling for mouse movements                               |
    //+------------------------------------------------------------------+
    void RectangleLabel::OnEvent(int id, long lparam, double dparam, string sparam)
       {
        // Handle mouse movement events for dragging the rectangle label
        if(id == CHARTEVENT_MOUSE_MOVE)
           {
            int X = (int)lparam;
            int Y = (int)dparam;
            int MouseState = (int)sparam;
    
            string name = _name;
            int XDistance = (int)ObjectGetInteger(0, name, OBJPROP_XDISTANCE);
            int YDistance = (int)ObjectGetInteger(0, name, OBJPROP_YDISTANCE);
            int XSize = (int)ObjectGetInteger(0, name, OBJPROP_XSIZE);
            int YSize = (int)ObjectGetInteger(0, name, OBJPROP_YSIZE);
    
            if(previousMouseState == 0 && MouseState == 1)
               {
                mlbDownX = X;
                mlbDownY = Y;
                mlbDownXDistance = XDistance;
                mlbDownYDistance = YDistance;
    
                if(X >= XDistance && X <= XDistance + XSize && Y >= YDistance && Y <= YDistance + YSize)
                   {
                    movingState = true;
                   }
    
               }
    
            if(movingState)
               {
                ChartSetInteger(0, CHART_MOUSE_SCROLL, false);
                ObjectSetInteger(0, name, OBJPROP_XDISTANCE, mlbDownXDistance + X - mlbDownX);
                ObjectSetInteger(0, name, OBJPROP_YDISTANCE, mlbDownYDistance + Y - mlbDownY);
                for(int i = 0; i < ArraySize(addedNames); i++)
                   {
                    ObjectSetInteger(0, addedNames[i], OBJPROP_XDISTANCE, mlbDownXDistance + X - mlbDownX - addedXDisDiffrence[i]);
                    ObjectSetInteger(0, addedNames[i], OBJPROP_YDISTANCE, mlbDownYDistance + Y - mlbDownY - addedYDisDiffrence[i]);
                   }
                ChartRedraw(0);
               }
    
            if(MouseState == 0)
               {
                movingState = false;
                ChartSetInteger(0, CHART_MOUSE_SCROLL, true);
               }
    
            previousMouseState = MouseState;
           }
       }

    高亮显示的部分是我们的新循环。

    现在我们只需要调用 Add 函数将元素附加到中心元素,如我们所选的 TitleBar。我们调用 TitleBar 实例中的 Add 函数,我们将其命名为 “TitleBar”。

    我们调用 TitleBar 实例中的 Add 函数将所有其它元素添加到 TitleBar:

    // Add the other elements to the Central Element i.e. TitleBar object in this case
    TitleBar.Add("MainDashboardBody");
    TitleBar.Add("TitleText");
    TitleBar.Add("LotSizeText");
    TitleBar.Add("LotSize");
    TitleBar.Add("BuyButton");
    TitleBar.Add("SellButton");


    如此这般,所有这些元素的名称都将添加到 addedNames 数组之中,允许它们移动。此外,还会记录它们与 TitleBar 的距离,如此即可保持该距离。

    现在,我们调用 OnEvent 函数。没有它,这一切都将是徒劳的。

    // Passes events to the TitleBar object
    TitleBar.OnEvent(id, lparam, dparam, sparam);
    我们将其添加到 OnChartEvent() 当中,我们最终完成了。我知道这很漫长,但最终的结果应该是值得的。

    图例 8. 最终结果

    图例 8. 最终结果



    结束语

    至此,我们来到了本文的结尾。在本文经历的旅程中,我们取得了极大的成就,最终完成了第 3 部分《利用 MQL5 的交互式 GUI 改进您的交易图表》。

    我们已经成功地实现了我们在《可移动 GUI》系列中为自己设定的目标,即第 1 部分和第 2 部分,为动态交易图表和用户友好的界面带来了生命。感谢您抽出宝贵时间阅读我的文章。我希望您能发现它们对您的努力既有益处、又有帮助。

    如果您对我的下一篇文章有任何想法或建议,请不必犹豫尽管分享。

    祝编码愉快!祝交易愉快!

    本文由MetaQuotes Ltd译自英文
    原文地址: https://www.mql5.com/en/articles/12923

    附加的文件 |
    RectangleLabel.mqh (5.75 KB)
    Label.mqh (2.35 KB)
    Edit.mqh (2.53 KB)
    Button.mqh (2.31 KB)
    开发回放系统 — 市场模拟(第 23 部分):外汇(IV) 开发回放系统 — 市场模拟(第 23 部分):外汇(IV)
    现在,创建发生在我们将跳价转换为柱线的同一点。以这种方式,如果在转换过程中出现问题,我们就能立即注意到错误。这是因为在快进期间,在图表上放置 1-分钟柱线的代码,也同样在正常表现期间用于定位系统放置柱线。换言之,负责此任务的代码不会在其它任何地方重复。如此这般,我们获得的系统就能更好的维护和改进。
    神经网络变得轻松(第五十二部分):研究乐观情绪和分布校正 神经网络变得轻松(第五十二部分):研究乐观情绪和分布校正
    由于模型是基于经验复现缓冲区进行训练,故当前的扮演者政策会越来越远离存储的样本,这会降低整个模型的训练效率。在本文中,我们将查看一些能在强化学习算法中提升样本使用效率的算法。
    神经网络变得轻松(第五十三部分):奖励分解 神经网络变得轻松(第五十三部分):奖励分解
    我们已经不止一次地讨论过正确选择奖励函数的重要性,我们通过为单独动作添加奖励或惩罚来刺激代理者的预期行为。但是关于由代理者解密我们的信号的问题仍旧悬而未决。在本文中,我们将探讨将单独信号传输至已训练代理者时的奖励分解。
    在 ONNX 模型中使用 float16 和 float8 格式 在 ONNX 模型中使用 float16 和 float8 格式
    用于表示机器学习模型的数据格式对其有效性起着至关重要的作用。近年来,出现了几种新类型的数据,专门为使用深度学习模型而设计。在本文中,我们将重点介绍两种新的数据格式,它们已在现代模型中广泛采用。