文章 "制作仪表板以显示指标和EA中的数据"

 

新文章 制作仪表板以显示指标和EA中的数据已发布:

在本文中,我们将创建一个用于指标和EA的仪表板类。这是一个小系列文章中的介绍性文章,其中包含模板以在EA交易中包含和使用标准指标。我将首先创建一个类似于MetaTrader 5数据窗口的面板。

在本文中,我将创建一个可以显示开发人员指定的数据的仪表板。这样的面板将便于在图表上直观地显示数据和进行可视化调试,因为在面板上查看必要的值比在调试器中跟踪它们更方便。我指的是根据某些数据值调试策略的情况。

我将以终端数据窗口原型的形式制作面板,并用相同的数据填充:


图1数据窗口和仪表板

我们的自定义面板将允许我们向其添加任何所需数量的必要数据,对其进行签名,以及显示和更新程序代码的读数。

应该可以用鼠标在图表周围移动面板,将其固定在所需的图表位置,以及折叠/展开它。为了方便在面板上放置数据,可以显示具有指定行数和列数的表格。该表中的数据可以显示在日志中(每个表单元格的X和Y坐标),并通过编程获得,以指示该数据应位于的行和列的索引,或者只需打印坐标并在显示文本和数据时将所需坐标输入到代码中。第一种方法由于其完全自动化而更加方便。面板也将有一个活动的关闭按钮,但我们将其处理委托给控制程序,因为只有程序开发人员才能决定如何对按下关闭按钮做出反应。单击按钮时,将向程序事件处理程序发送一个自定义事件。开发人员可以自行处理。

作者:Artyom Trishkin

 

感谢您的分享。

如果需要继承该类,这种实现方式有一个很大的缺点。

1.没有默认构造函数,参数构造函数按大小设置重要参数。例如,如果需要根据类的子类中的参数来计算面板的大小,那么这个逻辑就必须在类外实现,或者在类中实现,但计算要在一个单独的函数中进行,这在一定程度上破坏了逻辑,因为参数构造函数的调用(没有默认构造函数)会出现在初始化列表中。也许更好的解决办法是将初始化从构造函数移到类函数中。

2.带参数的部分被声明为私有,这就不允许后代实现不同的颜色方案或更改页眉大小等功能。

3.我知道这项工作尚未完成,但有些函数还没有实现,例如 SetButtonClose(开/关)、SetButtonMinimize(开/关)。

有了源代码,最终完成它就不成问题了,但还是......

 
Evgeny #:

感谢您的分享。

如果需要继承该类,这种实现方式有一个很大的缺点。

1.没有默认构造函数,参数构造函数按大小设置重要参数。例如,如果需要根据类的子类中的参数来计算面板的大小,那么这个逻辑就必须在类外实现,或者在类中实现,但计算要在一个单独的函数中进行,这在一定程度上破坏了逻辑,因为参数构造函数的调用(没有默认构造函数)将会出现在初始化列表中。也许更好的解决办法是将初始化从构造函数移到类函数中。

2.带参数的部分被声明为私有,这就不允许后代实现不同的颜色方案或改变页眉的大小等。

3.3. 我知道工作还没有完成,但有些函数还没有实现,例如 SetButtonClose(开/关)、SetButtonMinimize(开/关)。

有了源代码,最终完成它就不成问题了,但还是......

这篇文章是一篇教程。其中,面板涵盖了最基本的需求。

1.子代有自己的构造函数,其初始化列表中必须包含父代的构造函数。您可以在其中指定所需的尺寸。

我还没有考虑过颜色方案)以及标题的大小。

这些方法在公共部分声明。奇怪的是,它们并没有被实现。我记得当时我正在测试它们的启用/禁用....。我不得不凭记忆从头开始重写这个类,因为它的第一个版本在磁盘空间耗尽时被 Windows 销毁了。我一定是忘了还原它们。谢谢,我会修好的。

是的,有了源代码--所有的牌都掌握在读者手中。这正是教程文章的目的所在。

 
Artyom Trishkin #:

这篇文章具有指导性。在这篇文章中,专家小组介绍了最起码的需求。

1.子代有自己的构造函数,其初始化列表必须包含父代的构造函数。在其中指定所需的尺寸。

我甚至还没有考虑过配色方案)以及页眉的尺寸。

这些方法在公共部分声明。奇怪的是,它们并没有被实现。我清楚地记得,我当时正在测试它们的启用/禁用....。我不得不凭记忆从头开始重写这个类,因为它的第一个版本在磁盘空间耗尽时被 Windows 销毁了。我一定是忘了还原它们。谢谢,我会修好的。

是的,有了源代码--所有的牌都掌握在读者手中。这正是教程文章的目的所在。

1.是的,但只要在初始化列表 中调用了祖先构造函数,就必须在某个地方对传入的参数进行计算。特别是如果它不是最原始的。

例如,计算右下角初始显示的 X、Y 位置,根据预期的表格行数计算高度....。这将需要 3 个额外的类函数,而不是在其构造函数中调用祖先构造函数的一段代码中完成的。 虽然它可以工作,但看起来更像一根拐杖。如果能让类的架构更适应进一步的变化,那么这样的代码就不太美观了。(当然,这是一个完美主义者的观点)。

2.小的修饰会使产品更有质量。但总的来说,您的代码很漂亮,您的解决方案也很有趣,读起来很舒服。

3.我很同情您,数据丢失总是令人很不愉快,所以可靠的备份是我们的一切。

谢谢您,我们期待着您的新文章。

 
MetaQuotes:

查看新文章:制作仪表盘,在指标和 EA 中显示数据

作者:Artyom TrishkinArtyom Trishkin

感谢您提供非常好的教程


我将其修改为深色外观,还想进一步更改标题和数据的颜色。

此外,我还创建了一个函数,可以轻松添加文本

void DrawData(int cell, string title, string data, color clr ) {
   dashboard.DrawText(title,  dashboard.CellX(cell,0)+2,   dashboard.CellY(cell,0)+2, clr);
   dashboard.DrawText(data,   dashboard.CellX(cell,1)+2,   dashboard.CellY(cell,1)+2,90, clr);
   ChartRedraw(ChartID());
}

但发现很难改变这些文本的颜色,不知道哪里出了问题,因为颜色看起来很暗淡,下面是修改后的 DrawText 函数

//+------------------------------------------------------------------+
//| 在指定坐标处显示文本信息
//+------------------------------------------------------------------+
void CDashboard::DrawText(const string text,const int x,const int y,const color clr,const int width=WRONG_VALUE,const int height=WRONG_VALUE)
  {
//-- 声明变量以记录文本的宽度和高度
   int w=width;
   int h=height;
//--- 如果传递给方法的文本宽度和高度值为零、
//--- 然后使用透明色完全清除整个工作空间
   if(width==0 && height==0)
      this.m_workspace.Erase(0x00FFFFFF);
//--- 否则
   else
     {
      //--- 如果传递的宽度和高度为默认值(-1),我们将从文本中获取其宽度和高度。
      if(width==WRONG_VALUE && height==WRONG_VALUE)
         this.m_workspace.TextSize(text,w,h);
      //--- 否则、
      else
        {
         //--- 如果传递给方法的宽度为默认值 (-1) --从文本中获取宽度,或者
         //--- 如果传递给方法的宽度值大于零,则使用传递给方法的宽度,或者
         //--- 如果传递给方法的宽度值为零,则使用宽度值 1
         w=(width ==WRONG_VALUE ? this.m_workspace.TextWidth(text)  : width>0  ? width  : 1);
         //--- 如果传递给方法的高度为默认值(-1),则从文本中获取高度,或者
         //--- 如果传递给方法的高度值大于零,则使用传递给方法的高度,或者
         //--- 如果传递给该方法的高度值为零,则使用高度值 1
         h=(height==WRONG_VALUE ? this.m_workspace.TextHeight(text) : height>0 ? height : 1);
        }
      //--- 根据指定的坐标和由此产生的宽度和高度,用透明色填充空间(擦除上一条记录)
      this.m_workspace.FillRectangle(x,y,x+w,y+h,0x00FFFFFF);
     }
//--- 将文本显示到清除了之前文本的空间,并在不重绘屏幕的情况下更新工作空间
   this.m_workspace.TextOut(x,y,text,::ColorToARGB(clr,this.m_alpha));
   this.m_workspace.Update(false);
  }
 

Arpit T # :

//--- 将文本显示到清除了之前文本的空间,并更新工作空间,而无需重绘屏幕
   this .m_workspace. TextOut (x,y,text,:: ColorToARGB (clr, this .m_alpha ));
   this .m_workspace.Update( false );

高亮显示的颜色 是面板本身的透明度,而不是文本的透明度。对于文本,最好将透明度(或者说不透明度)设置为默认值 255 - 文本完全不透明。但您也可以通过输入 0 至 255 之间的常规数值来 "玩弄 "不透明度值,而不是输入this.m_alpha。0 表示完全透明的颜色,255 表示完全不透明的颜色。

 
Artyom Trishkin #:

高亮显示的颜色 是面板本身的透明度,而不是文本的透明度。对于文本,最好将透明度(或者说不透明度)设置为默认值 255 - 文本完全不透明。但您也可以通过输入 0 至 255 之间的常规数值来 "玩弄 "不透明度值,而不是输入this.m_alpha。0 表示完全透明的颜色,255 表示完全不透明的颜色。

谢谢,现在一切正常了


void DrawData(int cell, string title, string data, color clr ) {
   dashboard.DrawText(title,  dashboard.CellX(cell,0)+2,   dashboard.CellY(cell,0)+2, clr);
   dashboard.DrawText(data,   dashboard.CellX(cell,1)+2,   dashboard.CellY(cell,1)+2,90, clr);
   ChartRedraw(ChartID());
}

高亮显示的代码是问题所在,我删除后一切正常。

 
干得好,黑得漂亮
 
VIKRAM SINGH #:
做得很好,颜色很深

谢谢

如果您想使用该主题,这里是 dashboard.mqh 中经过修改的暗色外观构造函数

//+------------------------------------------------------------------+
//| 构造函数|
//+------------------------------------------------------------------+
CDashboard::CDashboard(const uint id,const int x,const int y, const int w,const int h,const int wnd=-1) : 
                        m_id(id),
                        m_chart_id(::ChartID()),
                        m_program_type((ENUM_PROGRAM_TYPE)::MQLInfoInteger(MQL_PROGRAM_TYPE)),
                        m_program_name(::MQLInfoString(MQL_PROGRAM_NAME)),
                        m_wnd(wnd==-1 ? GetSubWindow() : wnd),
                        m_chart_w((int)::ChartGetInteger(m_chart_id,CHART_WIDTH_IN_PIXELS,m_wnd)),
                        m_chart_h((int)::ChartGetInteger(m_chart_id,CHART_HEIGHT_IN_PIXELS,m_wnd)),
                        m_mouse_state(MOUSE_STATE_NOT_PRESSED),
                        m_x(x),
                        m_y(::ChartGetInteger(m_chart_id,CHART_SHOW_ONE_CLICK) ? (y<79 ? 79 : y) : y),
                        m_w(w),
                        m_h(h),
                        m_x_dock(m_x),
                        m_y_dock(m_y),
                        m_header(true),
                        m_butt_close(true),
                        m_butt_minimize(true),
                        m_butt_pin(true),
                        m_header_h(18),
                        
                        //--- 面板页眉实现
                        m_header_alpha(255),
                        m_header_alpha_c(m_header_alpha),
                        m_header_back_color(clrBlack),
                        m_header_back_color_c(m_header_back_color),
                        m_header_fore_color(clrSnow),
                        m_header_fore_color_c(m_header_fore_color),
                        m_header_border_color(clrSnow),
                        m_header_border_color_c(m_header_border_color),
                        m_title("Dashboard"),
                        m_title_font("Calibri"),
                        m_title_font_size(-100),
                        
                        //--- 关闭按钮
                        m_butt_close_back_color(clrBlack),
                        m_butt_close_back_color_c(m_butt_close_back_color),
                        m_butt_close_fore_color(clrSnow),
                        m_butt_close_fore_color_c(m_butt_close_fore_color),
                        
                        //--- 折叠/展开按钮
                        m_butt_min_back_color(clrBlack),
                        m_butt_min_back_color_c(m_butt_min_back_color),
                        m_butt_min_fore_color(clrSnow),
                        m_butt_min_fore_color_c(m_butt_min_fore_color),
                        
                        //--- 针式按钮
                        m_butt_pin_back_color(clrBlack),
                        m_butt_pin_back_color_c(m_butt_min_back_color),
                        m_butt_pin_fore_color(clrSnow),
                        m_butt_pin_fore_color_c(m_butt_min_fore_color),
                        
                        //--- 小组实施
                        m_alpha(255),
                        m_alpha_c(m_alpha),
                        m_fore_alpha(255),
                        m_fore_alpha_c(m_fore_alpha),
                        m_back_color(clrBlack),
                        m_back_color_c(m_back_color),
                        m_fore_color(clrSnow),
                        m_fore_color_c(m_fore_color),
                        m_border_color(clrSnow),
                        m_border_color_c(m_border_color),
                        m_font("Calibri"),
                        m_font_size(-100),
                        
                        m_minimized(false),
                        m_movable(true)
  {
//--- 设置图表发送有关移动和按下鼠标按钮事件信息的权限、
//--- 鼠标滚动事件,以及图形对象的创建/删除
   ::ChartSetInteger(this.m_chart_id,CHART_EVENT_MOUSE_MOVE,true);
   ::ChartSetInteger(this.m_chart_id,CHART_EVENT_MOUSE_WHEEL,true);
   ::ChartSetInteger(this.m_chart_id,CHART_EVENT_OBJECT_CREATE,true);
   ::ChartSetInteger(this.m_chart_id,CHART_EVENT_OBJECT_DELETE,true);
   
//--- 设置全局终端变量的名称,以存储面板坐标、折叠/展开状态和固定状态
   this.m_name_gv_x=this.m_program_name+"_id_"+(string)this.m_id+"_"+(string)this.m_chart_id+"_X";
   this.m_name_gv_y=this.m_program_name+"_id_"+(string)this.m_id+"_"+(string)this.m_chart_id+"_Y";
   this.m_name_gv_m=this.m_program_name+"_id_"+(string)this.m_id+"_"+(string)this.m_chart_id+"_Minimize";
   this.m_name_gv_u=this.m_program_name+"_id_"+(string)this.m_id+"_"+(string)this.m_chart_id+"_Unpin";
   
//--- 如果不存在全局变量,则创建该变量并写入当前值、
//--- 否则 - 将终端全局变量的值读入其中
//--- X 坐标
   if(!::GlobalVariableCheck(this.m_name_gv_x))
      ::GlobalVariableSet(this.m_name_gv_x,this.m_x);
   else
      this.m_x=(int)::GlobalVariableGet(this.m_name_gv_x);
//--- Y 坐标
   if(!::GlobalVariableCheck(this.m_name_gv_y))
      ::GlobalVariableSet(this.m_name_gv_y,this.m_y);
   else
      this.m_y=(int)::GlobalVariableGet(this.m_name_gv_y);
//--- 折叠/展开
   if(!::GlobalVariableCheck(this.m_name_gv_m))
      ::GlobalVariableSet(this.m_name_gv_m,this.m_minimized);
   else
      this.m_minimized=(int)::GlobalVariableGet(this.m_name_gv_m);
//-- 折叠/未折叠
   if(!::GlobalVariableCheck(this.m_name_gv_u))
      ::GlobalVariableSet(this.m_name_gv_u,this.m_movable);
   else
      this.m_movable=(int)::GlobalVariableGet(this.m_name_gv_u);

//--- 设置面板大小超过图表窗口大小的标志
   this.m_higher_wnd=this.HigherWnd();
   this.m_wider_wnd=this.WiderWnd();

//--- 如果创建了面板图形资源、
   if(this.m_canvas.CreateBitmapLabel(this.m_chart_id,this.m_wnd,"P"+(string)this.m_id,this.m_x,this.m_y,this.m_w,this.m_h,COLOR_FORMAT_ARGB_NORMALIZE))
     {
      //--- 设置画布字体并用透明色填充画布
      this.m_canvas.FontSet(this.m_title_font,this.m_title_font_size,FW_BOLD);
      this.m_canvas.Erase(0x00FFFFFF);
     }
//--- 否则--向日志报告对象创建不成功
   else
      ::PrintFormat("%s: Error. CreateBitmapLabel for canvas failed",(string)__FUNCTION__);

//--- 如果创建了图形资源的工作空间、
   if(this.m_workspace.CreateBitmapLabel(this.m_chart_id,this.m_wnd,"W"+(string)this.m_id,this.m_x+1,this.m_y+this.m_header_h,this.m_w-2,this.m_h-this.m_header_h-1,COLOR_FORMAT_ARGB_NORMALIZE))
     {
      //--- 为工作区设置字体并填充透明色
      this.m_workspace.FontSet(this.m_font,this.m_font_size);
      this.m_workspace.Erase(0x00FFFFFF);
     }
//--- 否则--向日志报告对象创建不成功
   else
      ::PrintFormat("%s: Error. CreateBitmapLabel for workspace failed",(string)__FUNCTION__);
  }
 
Artem 你好!我正在学习如何创建一个信息面板,但我一点也不明白!因为我并不是这个..... 的专家。一般来说,CDashboard * dashboard =NULL; 我们创建了一个类的实例......对吗?然后将 NULL 赋值给它......?然后在 oninit 中分配描述符......dashboard = new CDashboard(InpUniqID, InpPanelX, InpPanelY, 500, 700); 为什么是这样的,而不是这样的 CDashboard dashboard?我的脑袋一片混乱!如果您不介意...能简单解释一下吗?谢谢!
 
Igor Bakhrushen NULL; 我们创建了一个类的实例......对吗?然后将 NULL 赋值给它......?然后在 oninit 中分配描述符......dashboard = new CDashboard(InpUniqID, InpPanelX, InpPanelY, 500, 700); 为什么是这样的,而不是这样的 CDashboard dashboard?我的脑袋一片混乱!如果您不介意...能简单解释一下吗?谢谢!

您好。

是这样的

CDashboard  *dashboard = NULL;

一个指向未来新的、动态创建的类对象的指针变量被声明,并立即以NULL 值初始化。


这样声明的只是类的一个实例:

CDashboard   dashboard;

但在这种情况下,你不能用这种方法声明和创建实例--该类没有不带形式参数的构造函数。

因此,在以这种方式声明实例时,必须指定类对象的所有必要参数,这些参数必须传递给类的构造函数:

CDashboard   dashboard(InpUniqID, InpPanelX, InpPanelY, 200, 250);

------------------------

在使用类的示例中,首先会在指示器中创建一个指向未来对象的空指针,然后在 OnInit() 中创建一个面板对象,并将指向已创建对象的指针写入指针变量:

//--- Создаём объект панели
   dashboard=new CDashboard(InpUniqID,InpPanelX,InpPanelY,200,250);
   if(dashboard==NULL)
     {
      Print("Error. Failed to create dashboard object");
      return INIT_FAILED;
     }


然后,在 OnDeinit() 中,使用该指针删除内存中的对象:

//--- Если объект панели существует - удаляем
   if(dashboard!=NULL)
      delete dashboard;

如果我们只是通过new 操作符创建了一个新对象,而没有将指向已创建对象的指针写入变量,那么之后就无法删除该对象,这将导致内存泄漏。

因此,简而言之,在文章的示例中

  1. 声明一个指向未来类对象的变量指针,并将其初始化为NULL 值、
  2. 创建一个新的类对象,并将其指针写入之前创建的 dashboard 变量中、
  3. 访问创建的对象时,我们使用指针变量和一个点(dashboard.AnyMethod()
  4. 工作结束时,通过指针删除动态创建的类对象。

如果立即创建了必要的类(CDashboard dashboard)实例,就不需要指向它的指针了--使用 "point "操作符就可以以同样的方式访问它。工作完成后也不需要删除它--终端子系统会自行删除。但它将是程序中唯一的类实例。

通过动态创建,可以快速创建所需类的新对象,并通过指针对其进行引用。这就是例子中使用动态创建类对象的原因。简化,但未涉及某些要点。