文章 "MQL5 细则手册:指标子窗口控件 - 按钮" - 页 7

 
TheXpert:
你确定你的文章完美无缺?

我很高兴您读了这篇文章。

我很乐意在相应的主题中回答所有问题。

===

为了避免不必要的误解,我并不反对阿纳托利!他的文章值得称赞!但有必要回答这些问题...

 
DC2008:

请问,我是否打扰了您写另一篇教程或食谱?

如果没有,那就让我们继续讨论你那篇关于指标子窗口控制的文章吧。 所以,你提供了一个如何在指标中制作便捷菜单的大众解决方案(或想法)。很好,这篇文章的目的很有价值!但是,"初学者 "程序员该如何使用这些工具呢?在哪里放置自定义函数?请举例说明。同时解释一下,要使用 5 个按钮,需要在代码中修改哪些内容?就当这是一个初学者的问题吧。

不,你不是。我现在什么都还没写。我每年至少要休息一天。仅仅休息是没有意思的,尤其是长时间休息。)

这不是一个群众决定,我也没有写。我们不要把没有发生的事情归咎于别人。在讨论开始时就已经有人说过,这不是一个普遍的解决方案,而是一个特例。在我看来,这对初学者 来说是一个很好的实践 范例。而不是免费得到一个现成的解决方案,然后张开嘴笑着跑去迎接太阳。你明白了吗?我希望在学习编程之初就有这样一个简单明了的例子。尤其是当这是你人生中的第一门编程语言,而在此之前,你的全部生活活动都是在一个完全不同的领域,与编程毫无关联的时候。

在这种情况下,为了制作 5 个按钮,我们需要改变数组的大小,并在声明对象(按钮、按钮中显示的文本和按钮状态)名称的数组时排除不必要的元素。

有一个按钮状态数组,因此可以使用相同的原理来检查哪个按钮被按下,并执行其他(用户要求的)操作,而不仅仅是改变按钮的颜色。例如,可以是交易功能(不仅仅是):删除所有挂单、关闭所有仓位等。各种想法层出不穷。如果没有任何想法,那么您就选错了活动类型。)

为了实现这一点,您需要创建另一个数组,用自定义枚举(也需要创建)的标识符对其进行初始化,例如名称为ENUM_SRIPT。然后将调用这些标识符,例如:SCRIPT_01=0、SCRIPT_02=2,等等。此外,在循环中,当检查面板上的按钮是否按下时,需要确定哪个标识符与按下的按钮绑定,以及按钮的当前状态,然后将相应的函数传递给程序执行。

我不会特意展示示例代码。就让它成为初学者的家庭作业吧。)

Документация по MQL5: Стандартные константы, перечисления и структуры / Торговые константы / Свойства ордеров
Документация по MQL5: Стандартные константы, перечисления и структуры / Торговые константы / Свойства ордеров
  • www.mql5.com
Стандартные константы, перечисления и структуры / Торговые константы / Свойства ордеров - Документация по MQL5
 

我按你说的做了修改:

//--- 按钮中显示的文本
string button_texts[BUTTON_ROWS][BUTTON_COLUMNS]=
  {
     {"Button 01","Button 02","Button 03","Button 04"},
     {"Button 05"}
  };
//--- 对象名称
string button_object_names[BUTTON_ROWS][BUTTON_COLUMNS]=
  {
     {"button_01","button_02","button_03","button_04"},
     {"button_05"}
  };
....
bool button_states[BUTTON_ROWS][BUTTON_COLUMNS]=
  {
     {true,false,false,false},
     {false}
  };

这就是我在屏幕上看到的效果:

我该如何修复?(我是初学者)

 
DC2008:

我按你说的做了修改:

这就是我在屏幕上看到的效果:

我该如何修复?(我是初学者)

像这样

#define  BUTTON_COLUMNS 5 // 按宽度计算的按钮数量
#define  BUTTON_ROWS    1 // 按高度划分的按钮数量
...
//--- 按钮中显示的文本
string button_texts[BUTTON_ROWS][BUTTON_COLUMNS]=
  {
     {"Button 01","Button 02","Button 03","Button 04","Button 05"}
  };
//--- 对象名称
string button_object_names[BUTTON_ROWS][BUTTON_COLUMNS]=
  {
     {"button_01","button_02","button_03","button_04","button_05"}
  };
...
//--- 按钮状态
bool button_states[BUTTON_ROWS][BUTTON_COLUMNS]=
  {
     {true,false,false,false,false}
  };
 

哦,太好了!可以用了。

但我不明白如何将我的功能连接到按钮上。给我举个例子。

 
DC2008:

哦,太好了!可以用了。

但我不明白如何将我的功能连接到按钮上。给我举个例子。

好吧,让我们继续你开始的 "初学者 "游戏。)

你在哪一点上卡住了?演示一下如何理解当前的问题。创建一个有五个标识符的枚举和一个数组,数组中的元素需要分配给这些标识符。

 
bool fun_states[BUTTON_ROWS][BUTTON_COLUMNS]=
  {
     {true,false,false,false,false}
  };
enum ENUM_SCRIPT
  {
   SCRIPT_01 =0,
   SCRIPT_02 =1,
   SCRIPT_03 =2,
   SCRIPT_04 =3,
   SCRIPT_05 =4,
  };
void F1()
  {Print("F1");}
bool F2()
  {Print("F2");return(false);}
int F3()
  {Print("F3");return(0);}
double F4()
  {Print("F4");return(0.1);}
color F5()
  {Print("F5");return(clrAliceBlue);}

那么,我们下一步该怎么做?

 
DC2008:

那么接下来我们该怎么做呢?

这就是你需要的数组:

//--- 脚本
ENUM_SCRIPT buttons_scripts[NUMBER_BUTTONS_HEIGHT][NUMBER_BUTTONS_WIDTH]=
  {
     {SCRIPT_01,SCRIPT_02,SCRIPT_03,SCRIPT_04,SCRIPT_05}
  };

然后你需要写一个这样的函数:

//+------------------------------------------------------------------+
//|| 执行脚本
//+------------------------------------------------------------------+
void ScriptOn()
  {
   for(int i=0; i<NUMBER_BUTTONS_WIDTH; i++)
     {
      for(int j=0; j<NUMBER_BUTTONS_HEIGHT; j++)
        {
         //--- 如果按下该按钮,我们将运行相应的脚本
         if(buttons_state[j][i])
           {
            if(buttons_scripts[j][i]==SCRIPT_01)
              {
               F1();
               return;
              }
            //---
            if(buttons_scripts[j][i]==SCRIPT_02)
              {
               F2();
               return;
              }
            //---
            if(buttons_scripts[j][i]==SCRIPT_03)
              {
               F3();
               return;
              }
            //---
            if(buttons_scripts[j][i]==SCRIPT_04)
              {
               F4();
               return;
              }
            //---
            if(buttons_scripts[j][i]==SCRIPT_05)
              {
               F5();
               return;
              }
           }
        }
     }
  }

...然后把这个函数放在代码的这一部分:

//--- 跟踪鼠标左键在图形对象上的点击情况
   if(id==CHARTEVENT_OBJECT_CLICK)
     {
      //--- 如果点击按钮
      if(InitializeButtonStates(sparam))
        {
         //--- 设置按钮的颜色
         ChangeButtonColorOnClick();
         //--- 运行脚本
         ScriptOn();
        }
      //-- 更新图表
      ChartRedraw();
      return;
     }

然后你就可以考虑如何优化代码了。:)

 
tol64:

我就是这么做的。

启动时启用跟踪功能的程序在卸载时将其关闭。而留在图表上并需要跟踪的程序会检查是否启用了跟踪,如果未启用,则会启用。

为了 避免语句含糊不清,最好对我上一页文章中的 专家顾问和指标代码示例进行变通。

没有必要经常检查是否有人禁用了鼠标事件跟踪。更确切地说,如果您想保护自己免受任何情况的影响,您可以检查它,但我认为这太过分了。

也许我们应该建议开发人员在 CHART_EVENT_MOUSE_MOVE 发生变化时生成CHARTEVENT_CHART_CHANGE?这样就可以在运行智能交易系统时优雅地恢复必要的设置。

到目前为止,我有这种变体:

#property copyright "Copyright 2013, komposter"
#property link      "http://www.komposter.me/"
#property version   "1.00"
#property indicator_chart_window

input   bool    EnableMouseDetect = false; // true - 使用鼠标跟踪,false - 不使用鼠标跟踪

bool PrevState = false;

//+------------------------------------------------------------------+
//| 自定义指示器初始化函数
//+------------------------------------------------------------------+
int OnInit()
{
        if ( EnableMouseDetect )
        {
                //--- 获取当前状态
                PrevState = (bool)ChartGetInteger(0,CHART_EVENT_MOUSE_MOVE);
                //--- 启用鼠标事件跟踪
                if ( PrevState == false ) ChartSetInteger(0,CHART_EVENT_MOUSE_MOVE,true);
        }

        return(0);
}

//+------------------------------------------------------------------+
//| 专家的去初始化功能
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
        if ( EnableMouseDetect )
        {
                //--- 禁用鼠标事件跟踪
                if ( PrevState == false )
                {
                        ChartSetInteger(0,CHART_EVENT_MOUSE_MOVE,false);
                        ChartRedraw(); // 如果没有这一行,只有在出现刻度线时才会禁用跟踪功能。是这样设计的吗?
                }
        }
}

//+------------------------------------------------------------------+
//| OnTick|
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                     const int prev_calculated,
                     const datetime& time[],
                     const double& open[],
                     const double& high[],
                     const double& low[],
                     const double& close[],
                     const long& tick_volume[],
                     const long& volume[],
                     const int& spread[])
       {
        
        return(rates_total);
       }
//+------------------------------------------------------------------+
//| 图表事件函数|
//+------------------------------------------------------------------+
void OnChartEvent(const int    id,
                  const long   &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//--- 跟踪鼠标移动和按下鼠标左键的情况
   if(id==CHARTEVENT_MOUSE_MOVE)
     {
      static int count=1;
      Print("CHARTEVENT_MOUSE_MOVE; EXPERT; ",count);
      count++;
     }
  }
//+------------------------------------------------------------------+

我制作了一个指标,但带有一个参数:运行时,EnableMouseDetect = true - 控制跟踪,false - 仅打印事件数(如果启用了跟踪)。


------------------
现在我又想了一下,不得不同意--这个选项行不通。如果你先运行一个跟踪鼠标的程序(它将启用跟踪功能),然后运行第二个程序(它将查看已启用的功能),然后删除第一个程序,它将禁用跟踪功能,第二个程序将什么都没有。也就是说,我们需要想出某种信号灯来发出需要跟踪的信号。

而根据我们在这里进行的研究(关于处理器的负载),这样的拐杖是没有必要的。

因此,我建议把我的建议提交给开发人员,然后这个话题就可以结束了。

 
ChartRedraw(); // 如果没有这一行,只有在出现刻度线时才会禁用跟踪功能。是这样设计的吗?
MT5 具有图表属性异步更新功能。也就是说,我们设置了某个属性并不意味着终端会立即读取该属性。使用ChartRedraw() 函数 可让终端重新读取所有属性。您也可以使用 ChartGet...ObjectGet,在这种情况下,属性也会被重新读取。
Документация по MQL5: Операции с графиками / ChartRedraw
Документация по MQL5: Операции с графиками / ChartRedraw
  • www.mql5.com
Операции с графиками / ChartRedraw - Документация по MQL5