文章 "制作仪表板以显示指标和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.我很同情您,数据丢失总是令人很不愉快,所以可靠的备份是我们的一切。

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

 
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 "操作符就可以以同样的方式访问它。工作完成后也不需要删除它--终端子系统会自行删除。但它将是程序中唯一的类实例。

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

 
Artyom Trishkin #:

你好

那么

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


一个类的简单实例声明如下:

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

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

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

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


然后在 OnDeinit() 中通过该指针删除内存中的对象:

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

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

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

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

动态创建可以让您即时创建所需类的新对象,并通过指针来引用它们。这就是例子中使用动态创建类对象的原因。简化,但未涉及某些要点。

感谢您的全面回答!因此,每个对象都有自己的构造函数,在另一种情况下,对象带有类中要求的参数,您可以在任何阶段创建不同的对象!(Nus 变得更清晰了)。

实际上,创建信息面板的兴趣,是 DPI 显示器的结果,在我的例子中是 168。Comment();很小!

我想在考虑到分辨率的情况下创建一个 TerminalInfoInteger(TERMINAL_SCREEN_DPI) ...为了方便起见,我想按颜色组创建。

也许你有这样的解决方案?您的代码很好,可读性强...而我的代码冗长、难以理解,因此我不得不多次重写!谢谢您的支持!

 
Igor Bakhrushen #:

谢谢您的全面回答!因此,每个对象都有自己的构造函数,在另一种情况下,一个对象需要类的参数,在任何阶段,您都可以创建不同的对象!(Nous 说得更清楚了)。

实际上,创建信息面板的兴趣,是 DPI 显示器的结果,在我的例子中是 168。Comment();非常小!

我想在考虑到分辨率的情况下创建一个 TerminalInfoInteger(TERMINAL_SCREEN_DPI) ...为了方便起见,我想按颜色组创建。

也许你有这样的解决方案?您的代码很好,可读性强...而我的代码冗长、难以理解,因此我不得不多次重写!谢谢您的支持!

解决方案就在帮助中的示例中:

交易、自动交易系统和交易策略测试论坛。

用 MQL 编写的用户界面图库

Artyom Trishkin, 2024.05.31 10:33 AM

这里(TERMINAL_SCREEN_DPI):

缩放因子计算示例

//--- создаём кнопку шириной 1.5 дюйма на экране
int screen_dpi = TerminalInfoInteger(TERMINAL_SCREEN_DPI); // получим DPI монитора пользователя
int base_width = 144;                                      // базовая ширина в экранных точках для стандартных мониторов c DPI=96
int width      = (button_width * screen_dpi) / 96;         // вычислим ширину кнопки для монитора пользователя (с учётом его DPI)
...
 
//--- вычисление коэффициента масштабирования в процентах
int scale_factor=(TerminalInfoInteger(TERMINAL_SCREEN_DPI) * 100) / 96;
//--- использование коэффициента масштабирования
width=(base_width * scale_factor) / 100;

以这种方式使用时,图形资源 在不同分辨率的显示器上将具有相同的尺寸。同时,控制元素(按钮、对话窗口等)的大小将与个性化设置一致。


 
不幸的是,我没有 4K 显示器,所以无法检查,出于同样的原因,我还没有将这样的重新计算输入到程序库中--未经检查,随意这样做并不严肃。
 
Artyom Trishkin #:
不幸的是,我没有 4K 显示器,所以无法检查,出于同样的原因,我还没有将这种重新计算输入库中--在没有检查的情况下随意这样做并不严肃。
我明白了...谢谢!我们会慢慢解决的!
 
Artyom Trishkin #:
不幸的是,我没有 4K 显示器,所以无法检查,出于同样的原因,我还没有将这种重新计算输入到库中--未经检查就随意进行重新计算是不严肃的。

Artem 你好!以下是考虑到 dpi.... 的构造但事实上,您可以采用另一种方法。比方说,您设定了页眉的大小,然后根据页眉的大小计算面板大小、行高和文本高度。您可以指定两个参数...以及 dpi 是多少。还是有什么问题?我不太确定,您对这个解决方案有什么看法?

附加的文件: