English Русский Español Deutsch 日本語 Português
使用 CCanvas 类绘制刻度表盘

使用 CCanvas 类绘制刻度表盘

MetaTrader 5示例 | 4 十一月 2015, 09:07
3 222 1
Serhii Shevchuk
Serhii Shevchuk

内容目录

介绍

这一切的开始, 是当我通过 CCanvas 类首次认清自我。当使用它进行实践时, 我偶然间想到利用它绘制一个指示器表盘。我的第一个表盘计相当粗糙, 但最终它们都补充了新的元素, 变得赏心悦目。结果就是, 我现在拥有了一个小型程序库, 可用一种简单易行的方式为一款指标或 EA 加入刻度表盘。在此文中, 我们将兼顾表盘的结构, 熟悉绘制和设置视觉外观的必要函数, 并评估资源强度。

刻度表盘

图例.1. 刻度表盘


1. 坐标和锚点

表盘在图表上的定位有两种类型: 绝对和相对。

绝对 定位情况下, 坐标代表自锚点边角沿 X 和 Y 数轴的像素距离。

相对 定位情况下, 坐标原点根据指定相对定位类型创建。当选择了垂直类型, 原点位于参考对象之下或之上 (如果分别选择了锚点上边角或下边角)。当选择了垂直类型, 它位于锚点边角方向的左侧或右侧。在此种情况下, 指定的坐标表示距其原点的偏移。正偏移量导致物体远离参考对象。当负偏移量的情况, 则物体侵占参照对象。

参照对象可以表示为另一个表盘对象。至关重要的是, 这两个对象将具有相同的锚点边角。

图例. 2 描绘相对定位的一个示例。

相对定位

图例.2. 表盘的相对定位

让我们回顾一下每一个表盘的设置:

  • 表盘 "gg01": 相对定位被禁止。水平偏移 — 40, 垂直偏移 — 40。
  • 表盘 "gg02": 相对定位 — 水平, 参考对象 — "gg01"。自坐标原点 (点 A) 的水平偏移— 15, 垂直偏移 — 0。
  • 表盘 "gg03": 相对定位 — 垂直, 参照对象 — "gg01"。自坐标原点 (点 B) 的水平偏移— 0, 垂直偏移 — 15。
  • 表盘 "gg04": 相对定位 — 垂直, 参照对象 — "gg02"。自坐标原点 (点 C) 的水平偏移— 50, 垂直偏移 — 15。

如果图表上有若干指标含有表盘, 则相对定位有利于输入设置。如果您决定改变某一个表盘的大小, 其它表盘的坐标将会自动重新计算。

函数 GaugeCreate() 设置定位类型和坐标。


2. 表盘元素

刻度表盘由两类图形对象组成。其一称为 刻度层, 另一个称为 表针层。两类图形对象都有相同的坐标。表针层位于刻度层之上。再输入参数里设置的表盘名作为这两类对象的前缀。例如, 如果表盘名是 "Gauge01", 则刻度层将被称为 "Gauge01_s", 而表针层将名为 "Gauge01_n"。

图例.3 描绘表盘结构。

图例.3. 表盘结构

图例.3. 表盘结构

刻度层 包括:

  • 边框 (1)
  • 刻度标记 (5, 6, 7)
  • 刻度标签 (4)
  • 高亮范围 (2, 12)
  • 图解 (3, 10, 11)

图解是以用途区分:

  • 表盘描述 (3)
  • 测量单位 (11)
  • 当前数值 (10)
  • 刻度标签倍数 (省略)

刻度划分为:

  • 主要 (7)
  • 中等 (5)
  • 次要 (6)

只有主要刻度点有标签。刻度步长设定为一个数字值。中等刻度步长依据指定的主要刻度之间的中等标记数量计算。次要刻度步长依据指定的中等刻度之间的次要标记数量计算。次要和中等刻度可以省略。

表针层 包括:

  • 表针 (8)
  • 表针中心 (9)


2.1. 大小

图例.3 描绘一些表盘元素的大小:

  • d — 表盘大小, 其与表盘外轮廓线的直径相对应
  • b — 边框大小
  • g — 边框与刻度元素之间的空间大小
  • c — 表针中心的大小。

注释。只有表盘直径以像素为单位设置大小 (图例.3 中的 "d")。所有其它元素和字体依照条件单位设置, 且它们的大小以直径的百分比计算。这样便于缩放。改变直径, 则所有其它尺寸将被按比例重新计算。计算系数已列于 宏替换 章节, 且可由用户修改。


2.2. 实体形状

表盘的形状有两种类型: 圆形和扇形。如果 刻度范围 角度小于 180 度, 扇形更加便利。

表盘形状

图例.4. 表盘形状

图例.4 描绘一个圆形表盘 (a) 和两个扇形表盘 (b, c)。函数 GaugeSetCaseParameters() 用于设置期望的实体形状。


2.3. 刻度

这是表盘十分重要的元素。数据可读性依据其显示。刻度不应过于复杂, 但在同一时间, 它必须能足够体现信息。选择刻度极值, 以及主要标记的步长, 需要特别注意。函数 GaugeSetScaleParameters() 允许设置刻度范围, 它的旋转和极值 (最小值和最大值)。最小值可以在左侧 (顺时针) 或右侧 (逆时针)。

刻度范围 是两个刻度极值间由半径向量构成的扇形所包含的夹角。它的示范在图例.5.

刻度范围

图例.5. 刻度范围

刻度旋转 是自表盘中心的向上垂直线, 与刻度范围的平分点的偏离角度。它的示范在图例.6.

刻度旋转角度

图利.6. 刻度旋转角度

结合刻度范围角度和旋转角度可以帮助您以十分灵活方式的设置表盘的外观。图例.4(c) 示范一个 90 度范围和 45 度旋转的表盘。

最大和最小刻度值 是重要参数, 它们应依据显示变化的允许数值范围进行选择。零标记为方便起见可以省略。如果您的变化范围从 400 到 600, 那么刻度零不会绘制任何点。图例.7 描绘最大和最小刻度值的例子。

最大和最小刻度值

图例.7. 最大和最小刻度值

  • a) 数值从 0 到 500, 顺时针
  • b) 数值从 -200 到 400, 顺时针
  • c) 数值从 -400 到 0, 顺时针
  • d) 数值从 500 到 0, 逆时针
  • e) 数值从 200 到 800, 顺时针
  • f) 数值从 0 到 -800, 逆时针


2.4. 刻度线

刻度标记设置为选择的标记大小和排列方式。

对其可以如下:

  • 刻度的内边缘
  • 刻度的外边缘
  • 中心

图例.8 描绘刻度标记排列的例子:

  • a — 中心
  • b — 内边缘
  • c — 外边缘

函数 GaugeSetMarkParameters() 用于设置。

标记的标签定位参照刻度设置并使用 GaugeSetScaleParameters() 函数调整。

图例.8(a) 描绘刻度内的标签定位例子, 图例.8(b) 和 8(c) — 刻度外。

建议使用一个 倍数, 所有显示数值将会除以一个系数, 因此标签将不会在刻度上占据太多空间。倍数值可以从 0.0001 到 10000。图例.4(c) 描绘应用倍数等于 100 的例子, 它允许在标签里用一位数字替代原来的三位数字。图例.1 描绘我们用倍数等于 0.0001 用来显示 ATR 数值的情形, 它可以在标签里不用小数和零值。函数 GaugeSetScaleParameters() 设置倍数。

标记和标签定位

图例.8. 标记和标签定位


2.5. 图解

图解都是为了显示附加信息, 可以是以下四种类型:

  • 表盘描述
  • 测量单位
  • 当前数值
  • 倍数

所有图解都可隐藏。省缺仅显示表盘描述。

图解定位通过角度和半径设置。角度按度数设置, 其值等于自表盘中心的向上垂直线与想象的表盘中心和图解中心连线的夹角。半径设置按条件单位。如果其值从 0 到 10, 则 0 与表针中心的半径相对应, 而 10 则与刻度的外半径相对应。

图例.9 描绘图解定位的例子。

  • 图解 "Profit" (表盘描述) 有以下坐标: 角度 - 0, 半径 - 3。
  • 图解 "0.00" (当前值) 有以下坐标: 角度 - 225, 半径 - 4。
  • 图解 "USD" (测量单位) 有以下坐标: 角度 - 215, 半径 - 8。

函数 GaugeSetLegendParameters() 用于设置图解参数。

图解坐标

图例.9. 图解坐标

注释。 图解并非在刻度上固定, 且它们的角度与刻度旋转角度无关。


2.6. 高亮范围

高亮数据范围代表所有表盘的固有元素。它们有助于观察变化已触及紧急值, 或进入一些特殊的范围。函数 GaugeSetRangeParameters() 可以设置高达四个高亮范围。为此, 您需要设置极值和高亮颜色。图例.1 描绘带有两个高亮范围的盈利指示器: 从 200 到 400 为绿色范围, 它表示固定盈利的倍数, 从 -200 到 -400 为橙色范围, 警示有较大回撤。


2.7. 表针

函数 GaugeSetNeedleParameters() 设置表针中心和填充区域的大小。填充区域的类型影响该指示器的资源强度, 如每次数据更新后表针层将被完全重绘。图例.10 描绘填充区域的例子。

  • 使用抗锯齿算法填充表针 (a)
  • 不使用抗锯齿算法填充表针 (b)
  • 使用抗锯齿等高线但不填充表针 (c)

表针填充方法

图例.10. 表针填充方法

每种方法的优缺点将在修改 CCanvas 类和 资源强度评估的章节里专门描述。


3. 函数

表 1 列出了绘制表盘和设置其外观的函数。

函数
行为
GaugeCalcLocation
计算表盘中心坐标
GaugeCreate
创建表盘
GaugeDelete
删除表盘
GaugeNewValue
更新表针定位并显示数值
GaugeRedraw
重绘表盘
GaugeRelocation
改变表盘在图表上的位置
GaugeSetCaseParameters
设置表盘实体参数
GaugeSetLegendParameters
设置图解参数
GaugeSetMarkLabelFont
设置表盘标签字体
GaugeSetMarkParameters
设置表盘刻度参数
GaugeSetNeedleParameters
设置表针参数
GaugeSetRangeParameters
设置范围参数
GaugeSetScaleParameters
设置刻度参数

表 1. 函数列表

让我们来深入研究每个函数。它也代表我们建议的, 在初始化时调用它们的顺序。


3.1. GaugeCreate

创建表盘。

bool GaugeCreate(
   string name,              // 表盘名称
   GAUGE_STR &g,             // 引用表盘结构
   int x,                    // 自锚点边角的水平缩进
   int y,                    // 自锚点边角的垂直缩进
   int size,                 // 表盘大小
   string ObjRel,            // 设置相对定位的图形对象名
   ENUM_REL_MODE rel_mode,   // 相对定位
   ENUM_BASE_CORNER corner,  // 锚点边角
   bool back,                // 对象作为背景
   uchar scale_transparency, // 刻度透明度
   uchar needle_transparency // 表针透明度 
 );

参数

 name

   [输入]  表盘名。用于合成表盘的各图形对象的的前缀。

 g

   [输出]  表盘结构的引用。

 x

   [输入]  自锚点边角沿 X 数轴的像素距离。在相对定位情况下 — 自坐标原点的距离。

 y

   [输入]  自锚点边角沿 Y 数轴的像素距离。在相对定位情况下 — 自坐标原点的距离。

 size

   [输入]  表盘的 大小。代表实体直径。

 ObjRel

   [输入]  另一个设置相对定位的图形对象名。仅在设置相对定位使用。

 rel_mode

   [输入]  相对定位 的方法。可为 ENUM_REL_MODE 列表中的任何值。

 corner

   [输入]  作为图形对象锚点的图表边角。可为 ENUM_BASE_CORNER 列表中的任何值。

 back

   [输入]  对象作为背景。

 scale_transparency

   [输入]  刻度透明度级别。数值可从 0 到 255。

 needle_transparency

   [输入]  表针透明度级别。数值可从 0 到 255。

返回值

  返回 true 如果刻度层和表针层对象已经被创建。否则返回 false。


3.2. GaugeSetCaseParameters

设置表盘实体参数

void GaugeSetCaseParameters(
   GAUGE_STR &g,                  // 引用表盘结构
   ENUM_CASE_STYLE style,         // 实体样式
   color ccol,                    // 实体颜色
   ENUM_CASE_BORDER_STYLE bstyle, // 边框样式
   color bcol,                    // 边框颜色
   ENUM_SIZE border_gap_size      // 边框与刻度元素之间的空间大小
);

参数

 g

   [输出]  表盘结构的引用。

 style

   [输入]  实体样式。可为 ENUM_CASE_STYLE列表中的任何值。

 ccol

   [输入]  实体颜色。

 bstyle

   [输入]  边框样式。可为 ENUM_CASE_BORDER_STYLE 列表中的任何值。

 bcol

   [输入]  边框颜色。

 gap_size

   [输入]  内边框线与最接近刻度元素之间的区域 (图例.3 的 "g")。可为 ENUM_SIZE 列表中的任何值。


3.3. GaugeSetScaleParameters

设置刻度参数。

void GaugeSetScaleParameters(
   GAUGE_STR &g,           // 引用表盘结构
   int range,              // 刻度范围
   int rotation,           // 旋转角度
   double min,             // 最小值 (左侧)
   double max,             // 最大值 (右侧)
   ENUM_MUL_SCALE mul,     // 刻度标签的倍数
   ENUM_SCALE_STYLE style, // 刻度样式
   color col,              // 刻度颜色
   bool display_arc        // 显示刻度线
);

参数

 g

   [输出]  表盘结构的引用。

 range

   [输入]  刻度范围。设置两个刻度极值间由半径向量构成的扇形所包含的夹角。值从 30 到 320 度 (图例.5)。

 rotation

   [输入]  刻度旋转角度 (图例.6)。

 min

   [输入]  直接编号分配情况下的最小刻度值。

 max

   [输入]  直接编号分配情况下的最大刻度值。

 mul

   [输入]  刻度标签的倍数。可为 ENUM_MUL_SCALE 列表中的任何值。

 style

   [输入]  刻度样式。可为 ENUM_SCALE_STYLE 列表中的任何值。

 col

   [输入]  刻度颜色。

 display_arc=false

   [输入]  显示刻度线。


3.4. GaugeSetMarkParameters

设置刻度参数。

void GaugeSetMarkParameters(  
   GAUGE_STR &g,          // 引用表盘结构
   ENUM_MARK_STYLE style, // 刻度标记的样式
   ENUM_SIZE size,        // 标记大小
   double major_tmi,      // 主要标记间隔
   int middle_tmarks,     // 主要标记之间中等标记的数量
   int minor_tmarks       // 中等标记之间次要标记的数量
);

参数

 g

   [输出]  表盘结构的引用。

 style

   [输入]  刻度样式。可为 ENUM_MARK_STYLE 列表中的任何值。

 size

   [输入]  标记大小。可为 ENUM_SIZE 列表中的任何值。

 major_tmi

   [输入]  主要刻度的步长。主要标记伴随着相关数值的标签。

 middle_tmarks

   [输入]  主要标记之间中等标记的数量可为任何正数。没有大小限制。如果设为 0, 中等标记不显示。

 minor_tmarks

   [输入]  中等标记之间次要标记的数量 (若中等标记不显示则为主要标记)。可为任何正数。没有大小限制。如果设为 0, 次要标记不显示。


3.5. GaugeSetMarkLabelFont

设置刻度标记字体。

void GaugeSetMarkLabelFont(
   GAUGE_STR &g,        // 引用表盘结构
   ENUM_SIZE font_size, // 字号 
   string font,         // 字体
   bool italic,         // 斜体
   bool bold,           // 粗体
   color col            // 颜色
);

参数

 g

   [输出]  表盘结构的引用。

 font_size

   [输入]  刻度标签的字号。可为 ENUM_SIZE 列表中的任何值。

 font

   [输入]  字体。

 italic

   [输入]  斜体。

 bold

   [输入]  粗体。

 col

   [输入]  字体颜色。


3.6. GaugeSetLegendParameters

设置图解参数

void GaugeSetLegendParameters(
   GAUGE_STR &g,         // 引用表盘结构
   ENUM_GAUGE_LEGEND gl, // 图解类型
   bool enable,          // 显示图解
   string str,           // 字符串 (或互补参数)
   int radius,           // 坐标 - 半径
   double angle,         // 坐标 - 角度
   uint font_size,       // 字号
   string font,          // 字体
   bool italic,          // 斜体
   bool bold,            // 粗体
   color col             // 颜色
);

参数

 g

   [输出]  表盘结构的引用

 gl

   [输入]  图解类型。可为 ENUM_GAUGE_LEGEND列表中的任何值。

 enable

   [输入]  显示图解。

 str

   [输入]  这是将要显示的 LEGEND_DESCRIPTION 或 LEGEND_UNITS 类型的图解字符串。如果图解类型为 LEGEND_MUL 则参数被忽略。图解类型为 LEGEND_VALUE 的小数点位置。数值可以从 "0" 到 "8"。任何其它的值被认为是 "0"。例如, 字符串 "2" 意味着两位小数。字符创 "hello" 意味着 0 位小数。

 radius

   [输入]  半径。从表盘中心到图解中心的条件单位距离 (图例. 9)。

 angle

   [输入]  角坐标。其值等于自表盘中心的向上垂直线与想象的表盘中心和图解中心连线的夹角 (图例.9)。

 font_size

   [输入]  图解字号。

 font

   [输入]  字体。

 italic

   [输入]  斜体。

 bold

   [输入]  粗体。

 col

   [输入]  字体颜色。


3.7. GaugeSetRangeParameters

设置高亮范围参数

void GaugeSetRangeParameters(
   GAUGE_STR &g, // 引用表盘结构
   int index,    // 范围索引
   bool enable,  // 显示范围
   double start, // 初始值
   double end,   // 终值
   color col     // 颜色
);

参数

 g

   [输出]  表盘结构的引用。

 index

   [输入]  范围索引.数值可从 0 到 3。

 enable

   [输入]  显示范围。

 start

   [输入]  初值。

 end

   [输入]  终值。

 col

   [输入]  高亮范围颜色。


3.8. GaugeSetNeedleParameters

设置表针参数。

void GaugeSetNeedleParameters(
   GAUGE_STR &g,                     // 引用表盘结构
   ENUM_NCENTER_STYLE ncenter_style, // 表针中心样式
   color ncenter_col,                // 表针中心颜色
   color needle_col,                 // 表针颜色
   ENUM_NEEDLE_FILL needle_fill      // 表针填充区域方法
);

参数

 g

   [输出]  表盘结构的引用。

 ncenter_style

   [输入]  表针中心样式。可为 ENUM_NCENTER_STYLE列表中的任何值。

 ncenter_col

   [输入]  表针中心颜色。

 needle_col

   [输入]  表针颜色。

 needle_fill

   [输入]  表针填充方法。可为 ENUM_NEEDLE_FILL列表中的任何值。


3.9. GaugeRedraw

重绘表盘。应在改变任意参数之后调用该函数以便应用修改。

void GaugeRedraw(
   GAUGE_STR &g       // 引用表盘结构
); 

参数

 g

   [输入]  引用表盘结构。


3.10. GaugeNewValue

更新表针定位和显示数值。

void GaugeNewValue(
   GAUGE_STR &g,     // 引用表盘结构
   double v          // 变化值
);

参数

 g

   [输入]  引用表盘结构。

 v

   [输入]  当前变化值。


3.11. GaugeDelete

删除合成表盘的图形对象.从 OnDeinit() 处理器里调用该函数.

void GaugeDelete(
   GAUGE_STR &g      // 引用表盘结构
);

参数

 g

   [输入]  引用表盘结构。


3.12. GaugeCalcLocation

计算表盘对象的坐标.如果 相对定位 被禁用, 它将永远返回某些坐标。如果引用对象已被改变其位置或大小, 则坐标也许与之前的不同。

bool GaugeCalcLocation( 
   GAUGE_STR& g         // 引用表盘结构
);

参数

 g

   [输入]  引用表盘结构。

返回值

  返回 true 如果坐标与前值不同。否则返回 false。如果函数返回 true, 调用 GaugeRelocation() 函数来应用已计算参数。


3.13. GaugeRelocation

定位图表上指定点的合成表盘的图形对象。如果设置了相对定位, 且 GaugeCalcLocation() 函数已返回 true, 则有必要调用。

void GaugeRelocation(
   GAUGE_STR &g       // 引用表盘结构
);

参数

 g

   [输入]  引用表盘结构。


4. 枚举

表 2 列出可用作函数参数的枚举。

枚举
描述
ENUM_CASE_BORDER_STYLE边框样式
ENUM_CASE_STYLE
实体样式
ENUM_GAUGE_LEGEND
图解样式
ENUM_MARK_STYLE
刻度样式
ENUM_MUL_SCALE
刻度标签倍数
ENUM_NCENTER_STYLE表针中心样式
ENUM_NEEDLE_FILL表针填充区域方法
ENUM_REL_MODE相对定位方法
ENUM_SCALE_STYLE刻度样式
ENUM_SIZE大小

表 2. 枚举列表


4.1. ENUM_CASE_BORDER_STYLE

边框样式值列于表 3.

标识符
描述
CASE_BORDER_NONE
无边框
CASE_BORDER_THIN细边框
CASE_BORDER_THICK
粗边框

表 3. ENUM_CASE_BORDER_STYLE 的值

4.2. ENUM_CASE_STYLE

实体样式。值列于表 4.

标识符
描述
CASE_ROUND
圆形实体
CASE_SECTOR
扇形实体

表 4. ENUM_CASE_STYLE 的值

4.3. ENUM_GAUGE_LEGEND

图解类型。值列于表 5.

标识符
 描述
LEGEND_DESCRIPTION苦读描述
LEGEND_UNITS测量单位
LEGEND_MUL刻度标签倍数
LEGEND_VALUE当前变化值

表 5. ENUM_GAUGE_LEGEND 的值

4.4. ENUM_MARK_STYLE

刻度样式。值列于表 6.

标识符
 描述
MARKS_INNER标记内侧排列
MARKS_MIDDLE标记中心排列
MARKS_OUTER标记外侧排列

表 6. ENUM_MARK_STYLE 的值

4.5. ENUM_MUL_SCALE

刻度标签倍数。值列于表 7.

 标识符含义
显示
MUL_1000010000
 х10k
MUL_10001000
 х1k
MUL_100100
 х100
MUL_1010
 х10
MUL_11
 不显示
MUL_010.1
 /10
MUL_0010.01
 /100
MUL_00010.001
 /1k
MUL_000010.0001
 /10k

表 7. ENUM_MUL_SCALE 的值

4.6. ENUM_NCENTER_STYLE

表针中心样式。值列于表 8.

标识符
描述
NDCS_NONE不显示表针中心
NDCS_SMALL显示较小
NDCS_LARGE显示较大

表 8. ENUM_NCENTER_STYLE 的值

4.7. ENUM_NEEDLE_FILL

表针填充区域方法。值列于表 9.

 标识符 描述
NEEDLE_FILL无需边缘抗锯齿填充表针
NEEDLE_FILL_AA边缘抗锯齿填充表针
NEEDLE_NOFILL_AA不填充表针, 但应用边缘抗锯齿

表 9. ENUM_NEEDLE_FILL 的值

4.8. ENUM_REL_MODE

相对定位方法。值列于表 10.

 标识符 描述
RELATIVE_MODE_NONE相对定位被禁止
RELATIVE_MODE_HOR水平
RELATIVE_MODE_VERT垂直
RELATIVE_MODE_DIAG对角线

表 10. ENUM_REL_MODE 的值

4.9. ENUM_SCALE_STYLE

刻度样式。值列于表 11.

标识符
 描述
SCALE_INNER刻度标签在刻度内侧
SCALE_OUTER刻度标签在刻度外侧

表 11. ENUM_SCALE_STYLE 的值

4.10. ENUM_SIZE

大小。值列于表 12.

标识符
 描述
SIZE_SMALL较小
SIZE_MIDDLE中等
SIZE_LARGE较大

表 12. ENUM_SIZE 的值


5. 宏替换

大小系数:

#define DIAM_TO_NDCSL_RATIO   5   //表针中心直径 (较小) 作为实体直径的百分比
#define DIAM_TO_NDCSB_RATIO   7.5 //表针中心直径 (较大) 作为实体直径的百分比

//---
#define DIAM_TO_BD_SIZE_S     2 //边框宽度 (较小) 作为实体直径的百分比

#define DIAM_TO_BD_SIZE_B     5 //边框宽度 (较大) 作为实体直径的百分比
//---
#define DIAM_TO_BD_GAP_S      2.0 //自表盘实体边框到内部元素(较小) 的空间作为实体直径的百分比
#define DIAM_TO_BD_GAP_M      3.0 //自表盘实体边框到内部元素(中等) 的空间作为实体直径的百分比
#define DIAM_TO_BD_GAP_L      7.0 //自表盘实体边框到内部元素(较大) 的空间作为实体直径的百分比
//---
#define DIAM_TO_MSZ_MJ_S      3.3 //主要刻度 (较小) 的大小作为实体直径的百分比
#define DIAM_TO_MSZ_MD_S      2.3 //中等刻度 (较小) 的大小作为实体直径的百分比
#define DIAM_TO_MSZ_MN_S      1.3 //次要刻度 (较小) 的大小作为实体直径的百分比
//---
#define DIAM_TO_MSZ_MJ_M      6.5 //主要刻度 (中等) 的大小作为实体直径的百分比
#define DIAM_TO_MSZ_MD_M      4.8 //中等刻度 (中等) 的大小作为实体直径的百分比
#define DIAM_TO_MSZ_MN_M      3.0 //次要刻度 (中等) 的大小作为实体直径的百分比
//---
#define DIAM_TO_MSZ_MJ_L      10.0 //主要刻度 (较大) 的大小作为实体直径的百分比
#define DIAM_TO_MSZ_MD_L      7.5  //中等刻度 (较大) 的大小作为实体直径的百分比
#define DIAM_TO_MSZ_MN_L      5.0  //次要刻度 (较大) 的大小作为实体直径的百分比
//---
#define DIAM_TO_MFONT_SZ_S    4   //刻度标签 (较小) 的字号作为实体直径的百分比
#define DIAM_TO_MFONT_SZ_M    6.5 //刻度标签 (中等) 的字号作为实体直径的百分比
#define DIAM_TO_MFONT_SZ_L    10  //刻度标签 (较大) 的字号作为实体直径的百分比

省缺颜色:

#define DEF_COL_SCALE      clrBlack
#define DEF_COL_MARK_FONT  clrBlack
#define DEF_COL_CASE       clrMintCream
#define DEF_COL_BORDER     clrLightSteelBlue
#define DEF_COL_LAB        clrDarkGray
#define DEF_COL_NCENTER    clrLightSteelBlue
#define DEF_COL_NEEDLE     clrDimGray


6. 修改 CCanvas 类


6.1. 使用抗锯齿算法绘制段落

方法 LineAA 允许使用抗锯齿算法绘制段落。但是当我们绘制圆形刻度线时, 冒出一个问题。当我们从极坐标系转换段落的起点和终点坐标到直角坐标系时, 我们将分数四舍五入到整数。因此标记看起来有点歪, 就像图例.11(b) 看到的那样。

这就是为什么我们加入了 LineAA2 方法, 其与 LineAA 的不同之处事实上在于将输入参数 x1, y1, x2, y2 的类型改为双精度。所以我们可以传递分数坐标来消除所述问题, 这可以形象地从图例 11(c) 中看到。

//+------------------------------------------------------------------+
//| 利用抗锯齿画线 (带样式) v.2                                          |
//+------------------------------------------------------------------+
void CCanvas::LineAA2(const double x1,const double y1,const double x2,const double y2,const uint clr,const uint style)
  {
//--- 线超过图像边界
   if((x1<0 && x2<0) || (y1<0 && y2<0))
      return;
   if(x1>=m_width && x2>=m_width)
      return;
   if(y1>=m_height && y2>=m_height)
      return;
//--- 检查
   if(x1==x2 && y1==y2)
     {
      PixelSet(int(x1),int(y1),clr);
      return;
     }
//--- 设置线样式
   if(style!=UINT_MAX)
      LineStyleSet(style);
//--- 初步计算
   double dx=x2-x1;
   double dy=y2-y1;
   double xy=sqrt(dx*dx+dy*dy);
   double xx=x1;
   double yy=y1;
   uint   mask=1<<m_style_idx;
//--- 设置像素
   dx/=xy;
   dy/=xy;
   do
     {
      if((m_style&mask)==mask)
         PixelSetAA(xx,yy,clr);
      xx+=dx;
      yy+=dy;
      mask<<=1;
      if(mask==0x1000000)
         mask=1;
     }
   while(fabs(x2-xx)>=fabs(dx) && fabs(y2-yy)>=fabs(dy));
  } 

图例.11 描绘各种绘制刻度标记方法的例子:

绘制刻度标记的方法

图例. 11. 各种绘制刻度标记的方法 (提升 200%)


6.2. 利用边缘抗锯齿填充区域

方法 Fill 是指不使用抗锯齿算法填充有界区域。如果我们使用 LineAA 方法来填充一个有界区域, 区域填充是不完整的, 如图例.12(a) 所见。

利用边缘抗锯齿填充区域

图例.12. 利用边缘抗锯齿填充区域 (提升 200%)

因此我们加入 Fill2 方法。不同之处在于它不填充背景色, 但任何有别于有界区域的颜色。它允许填充轻淡颜色, 而这是 Fill 方法无法做到的。图例.12(b) 描绘一个例子。

//+------------------------------------------------------------------+
//| 以颜色填充闭合区域 (v.2)                                            |
//+------------------------------------------------------------------+
void CCanvas::Fill2(int x,int y,const uint clr)
  {
//--- 检查
   if(x<0 || x>=m_width || y<0 || y>=m_height)
      return;
//---
   int  index=y*m_width+x;
   uint old_clr=m_pixels[index];
//--- 检查替换是否必要
   if(old_clr==clr)
      return;
//--- 使用哑元栈来模拟深度嵌套的递归调用
   int  stack[];
   uint count=1;
   int  idx;
   int  total=ArraySize(m_pixels);
//--- 为堆栈分配内存
   if(ArrayResize(stack,total)==-1)
      return;
   stack[0]=index;
   m_pixels[index]=clr;
   for(uint i=0;i<count;i++)
     {
      index=stack[i];
      x=index%m_width;
      //--- 左邻点
      idx=index-1;
      if(x>0 && m_pixels[idx]!=clr)
        {
         stack[count]=idx;
         if(m_pixels[idx]==old_clr)
            count++;
         m_pixels[idx]=clr;
        }
      //--- 顶邻点
      idx=index-m_width;
      if(idx>=0 && m_pixels[idx]!=clr)
        {
         stack[count]=idx;
         if(m_pixels[idx]==old_clr)
            count++;
         m_pixels[idx]=clr;
        }
      //--- 右邻点
      idx=index+1;
      if(x<m_width-1 && m_pixels[idx]!=clr)
        {
         stack[count]=idx;
         if(m_pixels[idx]==old_clr)
            count++;
         m_pixels[idx]=clr;
        }
      //--- 底邻点
      idx=index+m_width;
      if(idx<total && m_pixels[idx]!=clr)
        {
         stack[count]=idx;
         if(m_pixels[idx]==old_clr)
            count++;
         m_pixels[idx]=clr;
        }
     }
//--- 释放内存
   ArrayFree(stack);
  }  

然而, 这种方法也有缺点。如果存在一个小锐角, 它的一部分不会填充, 如图例.12(c) 所示。因此, 我们已经找到了这个问题的一条出路。

   1)首先整块画布 (表针层) 以表针颜色填充:

n.canvas.Fill(10, 10, ColorToARGB(n.needle.c, n.transparency));

   2)之后我们使用 LineAA2 方法绘制三个段落合成的表针:

n.canvas.LineAA2(db_xbuf[0], db_ybuf[0], db_xbuf[1], db_ybuf[1], 0);
n.canvas.LineAA2(db_xbuf[1], db_ybuf[1], db_xbuf[2], db_ybuf[2], 0);
n.canvas.LineAA2(db_xbuf[2], db_ybuf[2], db_xbuf[0], db_ybuf[0], 0);

  3)此后我们使用 Fill2 方法以透明色来填充表针周围的区域:

n.canvas.Fill2(10, 10, 0);

这不是最佳方法, 但它可以绘制正确的表针。

表针填充方法

图例.13. 使用不同方法填充表针

图例.13 描绘使用不同方法填充表针。

  • a) 使用 LineAA2 方法绘制三个段落合成表针并使用 Fill2 方法填充。
  • b) 通过 FillTriangle 方法绘制表针。
  • c) 使用 LineAA2 方法绘制合成未填充表针的三个段落。

如我们所见, 图例.13(b) 所示表针是扭曲的, 并且在 90 度角时有略微偏差。此外, 我们可以看到, 表针从中心偏移, 这是由于当我们将它们从极坐标系转换到直角坐标系时坐标值的四舍五入造成的。但与此同时, 该方法在资源强度境况下经常实施 (我们将 稍后 回到这个问题)。在图例.13(c) 中所示表针是上述两种方法的折中。它使用 LineAA2 方法绘制合成表针的三个段落, 但不进行区域填充。


7. 应用例程

让我们通过几个例程检验程序库的应用。


7.1. 当前盈利指示器

我们将从最简单的开始。以下例程演示了将表盘添加到 EA 或指标的基本设置。

//+------------------------------------------------------------------+
//|                                       profit_gauge_indicator.mq5 |
//|                                         Copyright 2015, Decanium |
//+------------------------------------------------------------------+
#property copyright "Copyright 2015, Decanium"
#property version   "1.00"
#property indicator_plots 0
#property indicator_chart_window

#include <Gauge\gauge_graph.mqh>

input string inp_gauge_name="gg01";                  // 指标名
input int inp_x = 40;                                // 水平偏移
input int inp_y = 40;                                // 垂直偏移
input int inp_size=300;                              // 指标大小
input string inp_ObjRel="";                          // 相对定位情况下的基准指标名
input ENUM_REL_MODE inp_rel_mode=RELATIVE_MODE_NONE; // 相对定位模式
input ENUM_BASE_CORNER inp_corner=CORNER_LEFT_UPPER; // 锚点边角
input bool inp_back=false;                           // 指标作为背景
input uchar inp_scale_transparency=0;                // 刻度透明级别, 0..255
input uchar inp_needle_transparency=0;               // 表针透明级别, 0..255

//--- 声明表盘结构
GAUGE_STR g0;
//+------------------------------------------------------------------+
//| 自定义指标初始化函数                                                | 
//+------------------------------------------------------------------+
int OnInit()
  {
//--- 创建表盘
   if(GaugeCreate(inp_gauge_name,g0,inp_x,inp_y,inp_size,inp_ObjRel,inp_rel_mode,
      inp_corner,inp_back,inp_scale_transparency,inp_needle_transparency)==false)
      return(INIT_FAILED);
//--- 绘制表盘
   GaugeRedraw(g0);
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| 自定义指标逆始函数                                                  |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- 删除表盘
   GaugeDelete(g0);
   ChartRedraw();
  }    
//+------------------------------------------------------------------+
//| 自定义指标迭代函数                                                  |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const int begin,
                const double &price[])
  {
//--- 更新读数
   double profit=AccountInfoDouble(ACCOUNT_PROFIT);
   GaugeNewValue(g0,profit);
//---
   return(rates_total);
  }
//+------------------------------------------------------------------+
//| ChartEvent 函数                                                   |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//---
   if(id==CHARTEVENT_CHART_CHANGE)
     {
      if(GaugeCalcLocation(g0)==true)
         GaugeRelocation(g0);
     }
  }
//+------------------------------------------------------------------+

首先我们需要声明表盘结构。之后我们继续对使用 GaugeCreate() 函数创建的表盘进行初始化, 并调用绘图函数 — GaugeRedraw()GaugeNewValue() 可用于更新读数。在此例中, 它由 OnCalculate() 处理器调用。

表盘将如图例.14 所示。

指示器外观, 省缺参数

图例.14. 表盘的省缺外观

现在我们将添加设置刻度范围和旋转角度的能力。它将在输入列表里添加两个参数。

input int inp_scale_range=270;   // 刻度范围, 30..320 度
input int inp_rotation=45;       // 刻度旋转, 0..359 度

现在我们扩展来初始化代码, 利用函数调用设置刻度参数。

//--- 设置刻度和标记的参数
   GaugeSetScaleParameters(g0,inp_scale_range,inp_rotation,-200,400,MUL_1,SCALE_INNER,clrBlack);

为了补充新的输入参数, 我们还将设置:

  • 新的最大和最小值 (分别是 -200 和 400)
  • 刻度标签倍数 (MUL_1)
  • 刻度样式 (SCALE_INNER — 刻度标签在内侧)
  • 标签颜色 (clrBlack)

由于我们已经改变了刻度极值, 比较理想的是调整主要 标记 的步长。最佳值是 100, 因为它消除了冗余文字。在此, 我们将在两个主要标记之间放置一个中等标记, 并在两个中等标记之间放置四个次要标记。所以在标记之间, 我们的最小步长等于 10。

   GaugeSetMarkParameters(g0,MARKS_INNER,SIZE_MIDDLE,100,1,4);

现在, 我们将强调两个数据范围。范围有索引 0, 它始于 200 并止于 400, 以颜色 clrLimeGreen 强调。范围有索引 1, 它始于 -100 并止于 -200, 以颜色 clrCoral 强调。

//--- 刻度的强调范围
   GaugeSetRangeParameters(g0,0,true,200,400,clrLimeGreen);
   GaugeSetRangeParameters(g0,1,true,-100,-200,clrCoral);

现在我们继续设置 图解。我们确定表盘描述, 测量单位和当前值要截至小数点后一位。让我们来挨个复查。

表盘描述:

   GaugeSetLegendParameters(g0,LEGEND_DESCRIPTION,true,"Profit",3,0,14,"Arial",false,false);

显示字符串是 "Profit", 半径是 3, 角度是 0, 字号是 14 条件单位。

测量单位:

   GaugeSetLegendParameters(g0,LEGEND_UNITS,true,"USD",8,215,10,"Arial",true,false);

显示字符串是 "USD", 半径是 8, 角度是 215, 字号是 10 条件单位。

当前值:

   GaugeSetLegendParameters(g0,LEGEND_VALUE,true,"1",4,225,20,"Arial",true,false);

此处字符串 "1" 意为显示格式 (小数点后一位)。坐标: 半径是 4, 角度是 255。字号是 20 条件单位。

所以, 在我们执行一些附加设置之后, 表盘将如图例.15 所示。

当前盈利指示器

图例.15. 添加附加设置之后的表盘外观


7.2. 表盘指示器

现在让我们来审查更为复杂的例子, 名为 仪表板 的指示器。它如图例.1 所示。此指示器显示当前盈利, 点差, 可用保证金百分比以及当前 ATR, 力度指数RSI 的值。

首先我们需要声明表盘结构数组。 

//--- 声明表盘结构数组
GAUGE_STR gg[6];

之后我们要创建并调整表盘。

保证金级别指示器将置于左下角。它有绝对坐标, 且所有其它指示器将依此指示器放置或其临近。

//--- 构建 gg00 表盘, 保证金级别
   if(GaugeCreate("gg00",gg[0],5,-90,240,"",RELATIVE_MODE_NONE,
      CORNER_LEFT_LOWER,inp_back,inp_scale_transparency,inp_needle_transparency)==false)
      return(INIT_FAILED);
//--- 设置实体参数
   GaugeSetCaseParameters(gg[0],CASE_SECTOR,DEF_COL_CASE,CASE_BORDER_THIN,DEF_COL_BORDER,SIZE_MIDDLE);
//--- 设置刻度和标记的参数
   GaugeSetScaleParameters(gg[0],120,35,800,2000,MUL_100,SCALE_INNER,clrBlack);
   GaugeSetMarkParameters(gg[0],MARKS_INNER,SIZE_MIDDLE,200,1,4);
   GaugeSetMarkLabelFont(gg[0],SIZE_MIDDLE,"Arial",false,false,DEF_COL_MARK_FONT);
//--- 刻度的强调范围
   GaugeSetRangeParameters(gg[0],0,true,1400,2000,clrLimeGreen);
   GaugeSetRangeParameters(gg[0],1,true,1000,800,clrCoral);
//--- 设置文字标签
   GaugeSetLegendParameters(gg[0],LEGEND_DESCRIPTION,true,"Margin lvl",4,15,12,"Arial",false,false);
   GaugeSetLegendParameters(gg[0],LEGEND_VALUE,true,"0",3,80,16,"Arial",true,false);
   GaugeSetLegendParameters(gg[0],LEGEND_MUL,true,"",4,55,8,"Arial",true,false);
//--- 设置表针参数
   GaugeSetNeedleParameters(gg[0],NDCS_SMALL,DEF_COL_NCENTER,DEF_COL_NEEDLE,NEEDLE_FILL_AA);

我们将继续安排底行。下一个是当前盈利指示器。

//--- 构建 gg01 表盘, 当前盈利
   if(GaugeCreate("gg01",gg[1],-80,20,320,"gg00",RELATIVE_MODE_HOR,
      CORNER_LEFT_LOWER,inp_back,inp_scale_transparency,inp_needle_transparency)==false)
      return(INIT_FAILED);
//--- 设置实体参数
   GaugeSetCaseParameters(gg[1],CASE_SECTOR,DEF_COL_CASE,CASE_BORDER_THIN,DEF_COL_BORDER,SIZE_MIDDLE);
//--- 设置刻度和标记的参数
   GaugeSetScaleParameters(gg[1],200,0,-400,400,MUL_1,SCALE_INNER,clrBlack);
   GaugeSetMarkParameters(gg[1],MARKS_INNER,SIZE_MIDDLE,100,1,4);
   GaugeSetMarkLabelFont(gg[1],SIZE_MIDDLE,"Arial",false,false,DEF_COL_MARK_FONT);
//--- 刻度的强调范围
   GaugeSetRangeParameters(gg[1],0,true,200,400,clrLimeGreen);
   GaugeSetRangeParameters(gg[1],1,true,-200,-400,clrCoral);
//--- 设置文字标签
   GaugeSetLegendParameters(gg[1],LEGEND_DESCRIPTION,true,"Profit",3,0,16,"Arial",false,false);
   GaugeSetLegendParameters(gg[1],LEGEND_UNITS,true,"USD",3,-90,10,"Arial",true,false);
   GaugeSetLegendParameters(gg[1],LEGEND_VALUE,true,"1",3,90,12,"Arial",true,false);
//--- 设置表针参数
   GaugeSetNeedleParameters(gg[1],NDCS_SMALL,DEF_COL_NCENTER,DEF_COL_NEEDLE,NEEDLE_FILL_AA);

点差指示器靠近底行。

//--- 构建 gg02 表盘, 点差
   if(GaugeCreate("gg02",gg[2],-80,-20,240,"gg01",RELATIVE_MODE_HOR,
      CORNER_LEFT_LOWER,inp_back,inp_scale_transparency,inp_needle_transparency)==false)
      return(INIT_FAILED);
//--- 设置实体参数
   GaugeSetCaseParameters(gg[2],CASE_SECTOR,DEF_COL_CASE,CASE_BORDER_THIN,DEF_COL_BORDER,SIZE_MIDDLE);
//--- 设置刻度和标记的参数
   GaugeSetScaleParameters(gg[2],120,-35,60,0,MUL_1,SCALE_INNER,clrBlack);
   GaugeSetMarkParameters(gg[2],MARKS_INNER,SIZE_MIDDLE,10,1,4);
   GaugeSetMarkLabelFont(gg[2],SIZE_MIDDLE,"Arial",false,false,DEF_COL_MARK_FONT);
//--- 刻度的强调范围
   GaugeSetRangeParameters(gg[2],0,true,35,10,clrLimeGreen);
   GaugeSetRangeParameters(gg[2],1,true,50,60,clrCoral);
//--- 设置文字标签
   GaugeSetLegendParameters(gg[2],LEGEND_DESCRIPTION,true,"Spread",4,-15,14,"Arial",false,false);
   GaugeSetLegendParameters(gg[2],LEGEND_VALUE,true,"0",3,-80,16,"Arial",true,false);
//--- 设置表针参数
   GaugeSetNeedleParameters(gg[2],NDCS_SMALL,DEF_COL_NCENTER,DEF_COL_NEEDLE,NEEDLE_FILL_AA);

指示器 ATR (顶行左侧) 相对于保证金指示器放置。

//--- 构建 gg03 表盘, ATR
   if(GaugeCreate("gg03",gg[3],30,0,180,"gg00",RELATIVE_MODE_VERT,
      CORNER_LEFT_LOWER,inp_back,inp_scale_transparency,inp_needle_transparency)==false)
      return(INIT_FAILED);
//--- 设置实体参数
   GaugeSetCaseParameters(gg[3],CASE_ROUND,DEF_COL_CASE,CASE_BORDER_THIN,DEF_COL_BORDER,SIZE_MIDDLE);
//--- 设置刻度和标记的参数
   GaugeSetScaleParameters(gg[3],270,45,0.001,0.004,MUL_00001,SCALE_INNER,clrBlack);
   GaugeSetMarkParameters(gg[3],MARKS_INNER,SIZE_LARGE,0.001,9,3);
   GaugeSetMarkLabelFont(gg[3],SIZE_LARGE,"Arial",false,false,DEF_COL_MARK_FONT);
//--- 刻度的强调范围
   GaugeSetRangeParameters(gg[3],0,true,0.002,0.001,clrYellow);
//--- 设置文字标签
   GaugeSetLegendParameters(gg[3],LEGEND_DESCRIPTION,true,"ATR",7,-140,26,"Arial",false,false);
//GaugeSetLegendParameters(gg[3],LEGEND_UNITS,true,"USD",8,180,5,"Arial",true,false);
   GaugeSetLegendParameters(gg[3],LEGEND_VALUE,true,"5",2,180,14,"Arial",true,false);
   GaugeSetLegendParameters(gg[3],LEGEND_MUL,true,"",2,0,20,"Arial",true,false);
//--- 设置表针参数
   GaugeSetNeedleParameters(gg[3],NDCS_SMALL,DEF_COL_NCENTER,DEF_COL_NEEDLE,NEEDLE_FILL_AA);

指示器 RSI 相对于点差指示器放置 (之上)。

//--- 构建 gg04 表盘, RSI
   if(GaugeCreate("gg04",gg[4],-30,0,180,"gg02",RELATIVE_MODE_VERT,
      CORNER_LEFT_LOWER,inp_back,inp_scale_transparency,inp_needle_transparency)==false)
      return(INIT_FAILED);
//--- 设置实体参数
   GaugeSetCaseParameters(gg[4],CASE_ROUND,DEF_COL_CASE,CASE_BORDER_THIN,DEF_COL_BORDER,SIZE_MIDDLE);
//--- 设置刻度和标记的参数
   GaugeSetScaleParameters(gg[4],270,45,0,100,MUL_10,SCALE_INNER,clrBlack);
   GaugeSetMarkParameters(gg[4],MARKS_INNER,SIZE_LARGE,10,1,4);
   GaugeSetMarkLabelFont(gg[4],SIZE_LARGE,"Arial",false,false,DEF_COL_MARK_FONT);
//--- 设置文字标签
   GaugeSetLegendParameters(gg[4],LEGEND_DESCRIPTION,true,"RSI",7,-140,26,"Arial",false,false);
   GaugeSetLegendParameters(gg[4],LEGEND_VALUE,true,"2",2,180,16,"Arial",true,false);
   GaugeSetLegendParameters(gg[4],LEGEND_MUL,true,"",2,0,20,"Arial",true,false);
//--- 设置表针参数
   GaugeSetNeedleParameters(gg[4],NDCS_SMALL,DEF_COL_NCENTER,DEF_COL_NEEDLE,NEEDLE_FILL_AA);

指示器 力度指数 置于当前盈利指示器之上。

//--- 构建 gg05 表盘, 力度
   if(GaugeCreate("gg05",gg[5],-10,60,180,"gg03",RELATIVE_MODE_HOR,
      CORNER_LEFT_LOWER,inp_back,inp_scale_transparency,inp_needle_transparency)==false)
      return(INIT_FAILED);
//--- 设置实体参数
   GaugeSetCaseParameters(gg[5],CASE_ROUND,DEF_COL_CASE,CASE_BORDER_THIN,DEF_COL_BORDER,SIZE_MIDDLE);
//--- 设置刻度和标记的参数
   GaugeSetScaleParameters(gg[5],270,45,-4,4,MUL_1,SCALE_INNER,clrBlack);
   GaugeSetMarkParameters(gg[5],MARKS_INNER,SIZE_LARGE,1,1,4);
   GaugeSetMarkLabelFont(gg[5],SIZE_LARGE,"Arial",false,false,DEF_COL_MARK_FONT);
//--- 刻度的强调范围
   GaugeSetRangeParameters(gg[5],0,true,-1,-4,clrMediumSeaGreen);
   GaugeSetRangeParameters(gg[5],1,true,1,4,clrCrimson);
//--- 设置文字标签
   GaugeSetLegendParameters(gg[5],LEGEND_DESCRIPTION,true,"Force",7,-140,20,"Arial",false,false);
   GaugeSetLegendParameters(gg[5],LEGEND_VALUE,true,"5",2,180,14,"Arial",true,false);
   GaugeSetLegendParameters(gg[5],LEGEND_MUL,true,"",3,0,10,"Arial",true,false);
//--- 设置表针参数
   GaugeSetNeedleParameters(gg[5],NDCS_SMALL,DEF_COL_NCENTER,DEF_COL_NEEDLE,NEEDLE_FILL_AA);

表盘以循环方式进行绘制。

//--- 绘制表盘
   for(int i=0; i<6;i++)
     {
      GaugeRedraw(gg[i]);
      GaugeNewValue(gg[i],0);
     }

OnCalculate() 事件发生, 我们重计算当前值并调用每个指示器的 GaugeNewValue() 函数。

//--- 更新读数
//--- 点差
   GaugeNewValue(gg[2],spread[rates_total-1]);
//--- 当前盈利   
   double profit=AccountInfoDouble(ACCOUNT_PROFIT);
   GaugeNewValue(gg[1],profit);
//--- 保证金级别
   double margin_level=AccountInfoDouble(ACCOUNT_MARGIN_LEVEL);
   GaugeNewValue(gg[0],margin_level);
//--- 指示器 ATR
   calculated=BarsCalculated(handle_ATR);
   if(calculated>0)
     {
      double ival[1];
      if(CopyBuffer(handle_ATR,0,0,1,ival)<0)
         Print("ATR CopyBuffer 错误");
      else
         GaugeNewValue(gg[3],ival[0]);
     }
//--- 指示器 RSI
   calculated=BarsCalculated(handle_RSI);
   if(calculated>0)
     {
      double ival[1];
      if(CopyBuffer(handle_RSI,0,0,1,ival)<0)
         Print("RSI CopyBuffer 错误");
      else
         GaugeNewValue(gg[4],ival[0]);
     }
//--- 指示器力度指数
   calculated=BarsCalculated(handle_Force);
   if(calculated>0)
     {
      double ival[1];
      if(CopyBuffer(handle_Force,0,0,1,ival)<0)
         Print("力度指数 CopyBuffer 错误");
      else
         GaugeNewValue(gg[5],ival[0]);
     }

请注意, 在给出的例程当中没有从 OnChartEvent() 事件调用 GaugeRelocation()。尽管此处使用了 相对定位, 如果它们的定位或大小已经改变, 我们不需要重计算表盘的坐标, 因为所有表盘会被一次性初始化。


8. 资源强度评估

每当读数更新, 表针层将会完全重绘。它可能会经常发生, 甚至在某些情况下每秒几次。这就是为什么绘制表针的资源强度问题相当严重。我们将编写一个小的脚本来评估使用各种区域填充方法绘制表针时 CPU 的开销。

//+------------------------------------------------------------------+
//|                                                    test_fill.mq5 |
//|                                         Copyright 2015, Decanium |
//+------------------------------------------------------------------+
#property copyright "Copyright 2015, Decanium"
#property version   "1.00"

#include <Canvas/Canvas2.mqh>

CCanvas canvas;
//+------------------------------------------------------------------+
//| 脚本程序开始函数                                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
   Print("***** 开始测试 *****");
//---
   string ObjName="test";
   ObjectDelete(0,ObjName);
   canvas.CreateBitmapLabel(ObjName,10,10,400,400,COLOR_FORMAT_ARGB_NORMALIZE);
//---
   int x[3]={200,185,215};
   int y[3]={70, 250,250};
   int cycles=1000;
   uint col=ColorToARGB(clrRed,255);
   uint c1,c2;
//--- 测试采用边缘抗锯齿填充区域
   canvas.Erase();
   c1=GetTickCount();
   for(int i=0; i<cycles; i++)
     {
      canvas.Fill(10, 10, col);
      canvas.LineAA2(x[0], y[0], x[1], y[1], 0);
      canvas.LineAA2(x[1], y[1], x[2], y[2], 0);
      canvas.LineAA2(x[2], y[2], x[0], y[0], 0);
      canvas.Fill2(10, 10, 0);
     }
   c2=GetTickCount();
   canvas.Update(true);
   Print("填充 AA: ",c2-c1," 毫秒, ",cycles," 循环, ",
         DoubleToString(double(c2-c1)/double(cycles),2)," 每循环毫秒");
//--- 测试抗锯齿等高线但不填充
   canvas.Erase();
   c1=GetTickCount();
   for(int i=0; i<cycles; i++)
     {
      canvas.LineAA2(x[0], y[0], x[1], y[1], col);
      canvas.LineAA2(x[1], y[1], x[2], y[2], col);
      canvas.LineAA2(x[2], y[2], x[0], y[0], col);
     }
   c2=GetTickCount();
   canvas.Update(true);
   Print("不填充 AA: ",c2-c1," 毫秒, ",cycles," 循环, ",
         DoubleToString(double(c2-c1)/double(cycles),2)," 每循环毫秒");
//--- 测试不使用抗锯齿填充区域
   canvas.Erase();
   c1=GetTickCount();
   for(int i=0; i<cycles; i++)
     {
      canvas.FillTriangle(x[0],y[0],x[1],y[1],x[2],y[2],col);
      canvas.LineAA2(x[0], y[0], (x[1]+x[2])/2, y[1], col);
     }
   c2=GetTickCount();
   canvas.Update(true);
   Print("填充: ",c2-c1," 毫秒, ",cycles," 循环, ",
         DoubleToString(double(c2-c1)/double(cycles),2)," 每循环毫秒");
  }
//+------------------------------------------------------------------+

脚本在一个周期里每种绘制表针的方法启动 1000 次, 并测量处理过程所花费的毫秒时间。

资源强度测试

图例.16. 资源强度测试结果

正您在结果中所见, 采用边缘抗锯齿绘制填充表针, 比不采用抗锯齿填充表针时间上要长数百倍, 且比仅以抗锯齿绘制轮廓线而不填充的方法时间上长数十倍。在这种情况下, 美观真的有其价值。


结论

在这篇文章中, 我们已经审阅了一套绘制表盘函数。程序库创建的主要目标是简化将表盘加入 EA 或指标的工作, 而无须了解绘图和几何形状的细节。虽然, 取决于您来决定我们是否已经达到了这个目标。

特别要注意资源强度。在 OnCalculate() 处理器内进行时间消耗型计算可导致终端崩溃。因此, 我们建议采用折中方法绘制表针 (抗锯齿无填充)。

本文由MetaQuotes Ltd译自俄文
原文地址: https://www.mql5.com/ru/articles/1699

附加的文件 |
dashboard.mq5 (22.69 KB)
canvas2.mqh (171.52 KB)
gauge_graph.mqh (154.69 KB)
最近评论 | 前往讨论 (1)
Little---Prince
Little---Prince | 16 11月 2015 在 08:22
微信免费体验喊单,另有盈利超过千点的QQ群:375124107,加群请备注“77”,谢谢配合
在 GUI 控件中使用布局和容器: CBox 类 在 GUI 控件中使用布局和容器: CBox 类
本文介绍一种基于布局和容器来创建 GUI (图形用户界面) 的替代方法, 使用一个布局管理器 — CBox 类。类 CBox class 是一个辅助控件, 在 GUI 面板里充当一个基本控件的容器。它可令图形面板设计更加简便, 并且在某些场合, 减少编写代码时间。
在 MetaTrader 5 里使用 HedgeTerminal (对冲终端) 面板进行双向交易和仓位对冲, 第二部分 在 MetaTrader 5 里使用 HedgeTerminal (对冲终端) 面板进行双向交易和仓位对冲, 第二部分
本文描述了一种新的方法来进行仓位对冲, 并在 MetaTrader 4 和 MetaTrader 5 的用户之间就此事的争辩划清界线。这是: "在 MetaTrader 5 里使用 HedgeTerminal (对冲终端) 面板进行双向交易和仓位对冲" 第一部分的延续。在第二部分里, 我们讨论自定义 EA 与 HedgeTerminalAPI 的集成, 其作为特别的可视化程序库, 设计用于在一个舒适的软件环境里作为工具进行便利的双向交易仓位管理。
MQL5秘笈之:采用关联数组或字典实现快速数据访问 MQL5秘笈之:采用关联数组或字典实现快速数据访问
本文介绍一种能够通过key来访问元素的特殊算法。任何基本数据类型都可以被当作key。例如它可以是一个字符串或一个整型值。这样的数据容器通常被称为字典或这关联数组。这为解决问题提供了便捷。
模糊逻辑介绍 模糊逻辑介绍
模糊逻辑扩展了我们的数理逻辑和集合论的界限。本文揭示了模糊逻辑的基本原理, 同时描述使用马丹尼型和关野型的两种推理系统。提供的例程将描述如何使用 MQL5 版本的模糊库来实现这两种类型的系统。