图形界面 XI: 集成标准图形库 (统合构建 16)·进阶篇
📊

图形界面 XI: 集成标准图形库 (统合构建 16)·进阶篇

(2/3)· 函数库体积砍掉近四成,图表类全面换血,老接口别再硬套

实战向 第 2/3 篇
很多人还在拿重构前的 CLineChart 写图表,结果新版本里它已改名 CGraph 且只管状态。继续套老用法,编译能过但坐标轴全失联。

◍ 坐标轴边距与历史标签的读写接口

在 MT5 自定义图形对象的 C++ 风格封装里,缩进(Indent)控制的是绘制区域相对窗口四边的留白距离,分上下左右四个独立整型成员,分别由 m_up0、m_down0、m_left0、m_right0 存储。 GapSize 与 MajorMarkSize 管的是刻度间隙与主标记长度,二者直接映射到 m_gap 与 m_mark_size。调参时若把 MajorMarkSize 设到 0,主刻度线可能完全不渲染,肉眼容易误判为行情断层。 曲线历史属性走的是嵌套结构 m_history,包含 name_width(名称列宽)、name_size(名称字号)、symbol_size(品种字号)三个只读 getter 加可写 setter。下面这段是接口原貌,逐行拆完你能直接抄进自己的类里验证。 别把默认缩进当安全值:不同品种报价小数位差异大,XAUUSD 与 EURUSD 同套 IndentLeft 可能导致标签重叠,开 MT5 用 SymbolInfoInteger 比对点值后再定。

MQL5 / C++
class="kw">public:
  class=class="str">"cmt">//--- 获取或是设置缩进
  class="type">int IndentUp(class="type">void) class="kw">const { class="kw">return(m_up0); }
  class="type">void IndentUp(class="kw">const class="type">int up) { m_up0=up; }
  class="type">int IndentDown(class="type">void) class="kw">const { class="kw">return(m_down0); }
  class="type">void IndentDown(class="kw">const class="type">int down) { m_down0=down; }
  class="type">int IndentLeft(class="type">void) class="kw">const { class="kw">return(m_left0); }
  class="type">void IndentLeft(class="kw">const class="type">int left) { m_left0=left; }
  class="type">int IndentRight(class="type">void) class="kw">const { class="kw">return(m_right0); }
  class="type">void IndentRight(class="kw">const class="type">int right) { m_right0=right; }
  class=class="str">"cmt">//--- 获取或是设置空白 
  class="type">int GapSize(class="type">void) class="kw">const { class="kw">return(m_gap); }
  class="type">void GapSize(class="kw">const class="type">int size) { m_gap=size; }
  class=class="str">"cmt">//--- 获取或是设置主要标记大小
  class="type">int MajorMarkSize(class="type">void) class="kw">const { class="kw">return(m_mark_size); }
  class="type">void MajorMarkSize(class="kw">const class="type">int size) { m_mark_size=size; }
  class=class="str">"cmt">//--- 获取曲线历史属性
  class="type">int HistoryNameWidth(class="type">void) class="kw">const { class="kw">return(m_history.name_width); }
  class="type">int HistoryNameSize(class="type">void) class="kw">const { class="kw">return(m_history.name_size); }
  class="type">int HistorySymbolSize(class="type">void) class="kw">const { class="kw">return(m_history.symbol_size); }
  class=class="str">"cmt">//--- 设置曲线历史属性
  class="type">void HistoryNameWidth(class="kw">const class="type">int width) { m_history.name_width=width; }
  class="type">void HistoryNameSize(class="kw">const class="type">int size) { m_history.name_size=size; }

网格结构与取色接口怎么写

在 MT5 自定义二维图形类里,网格不是散着写的属性,而是先收进一个 CGrid 结构。该结构用 5 个 uint 存颜色(线、背景、圆点、坐标轴、边框),加一个 int 半径和一个 bool 开关,总共 7 个字段,内存布局连续,方便整体赋值或作为参数传递。 CGraphic 类把 CGrid 实例 m_grid 放在 protected 区,外部不能直接改,只暴露一组 Getter/Setter。比如 GridLineColor(void) 返回 m_grid.clr_line,而带 const uint clr 参数的重载版本负责写回 m_grid.clr_line。这种同名不同参的用法,让你在调图时不用碰内部成员。 开 MT5 新建一个 include 式的绘图类时,直接照这个结构抄一遍,能把网格配色和绘制逻辑解耦。外汇与贵金属图表叠加自绘图形时波动剧烈,高风险,参数乱改可能让界面卡顿或视觉误导,建议先在小周期回测图验证。

MQL5 / C++
class="kw">struct CGrid
  {
   class="type">uint             clr_line;
   class="type">uint             clr_background;
   class="type">uint             clr_circle;
   class="type">uint             clr_axis_line;
   class="type">uint             clr_frame;
   class="type">int              r_circle;
   class="type">bool             has_circle;
  };

class CGraphic
  {
class="kw">protected:
   CGrid            m_grid;
class="kw">public:
   class="type">uint             GridLineColor(class="type">void)        class="kw">const { class="kw">return(m_grid.clr_line);       }
   class="type">uint             GridAxisLineColor(class="type">void)    class="kw">const { class="kw">return(m_grid.clr_axis_line);  }
   class="type">uint             GridBackgroundColor(class="type">void)  class="kw">const { class="kw">return(m_grid.clr_background); }
   class="type">int              GridCircleRadius(class="type">void)     class="kw">const { class="kw">return(m_grid.r_circle);       }
   class="type">uint             GridCircleColor(class="type">void)      class="kw">const { class="kw">return(m_grid.clr_circle);     }
   class="type">bool             GridHasCircle(class="type">void)        class="kw">const { class="kw">return(m_grid.has_circle);     }
   class="type">void             GridLineColor(class="kw">const class="type">uint clr)      { m_grid.clr_line=clr;       }
  };

「坐标轴类的私有字段与 setter 接口」

在 MT5 自定义二维图形里,坐标轴的视觉与量程都由 CAxis 类托管。它把量程边界、颜色、文字尺寸、自动缩放开关等全塞进 private 段,外部只能走公开的 setter 去改,避免图形参数被随意篡改。 前面网格结构那一组方法值得单独看:GridAxisLineColor 接 uint 色值写进 m_grid.clr_axis_line,GridBackgroundColor 同理改背景;GridCircleRadius 收 int 半径进 m_grid.r_circle,GridCircleColor 和 GridHasCircle 分别控制圆心描边色与是否绘制同心圆。这几行就是你在 EA 里微调网格观感的直接入口。 CAxis 的 private 区列了 13 个成员:m_min / m_max 决定数据映射区间,m_clr 是轴线色,m_name_size 与 m_values_size 管标签字号,m_auto_scale 开启后量程随数据自适应。m_default_step、m_max_labels、m_min_grace、m_max_grace 四个 double 专门服务于刻度算法——grace 值会在数据极值外扩一点余量,实盘里能避免边界价格贴边看不清。 公开的 Min() / Max() 做了读写重载:无参版本 const 返回当前值,带 double 参数的版本直接赋值 m_min / m_max。你只要在 OnInit 里 new 一个 CAxis,依次调 Min(0.0)、Max(100.0) 就能钉死坐标轴,不依赖自动缩放。外汇与贵金属波动大,钉死量程前最好先跑一段历史数据看极值分布,否则高风险时段价格出界会画不出线。

MQL5 / C++
class="type">void GridAxisLineColor(class="kw">const class="type">uint clr)   { m_grid.clr_axis_line=clr;  }
class="type">void GridBackgroundColor(class="kw">const class="type">uint clr)   { m_grid.clr_background=clr; }
class="type">void GridCircleRadius(class="kw">const class="type">int r)         { m_grid.r_circle=r;        }
class="type">void GridCircleColor(class="kw">const class="type">uint clr)       { m_grid.clr_circle=clr;    }
class="type">void GridHasCircle(class="kw">const class="type">bool has)         { m_grid.has_circle=has;    }
};
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 类 CAxis                                                                          |
class=class="str">"cmt">//| 用法: 二维图形上创建坐标轴的类                                                    |
class=class="str">"cmt">//+------------------------------------------------------------------+
class CAxis
  {
class="kw">private:
   class="type">class="kw">double            m_min;
   class="type">class="kw">double            m_max;
   class="type">uint              m_clr;
   class="type">class="kw">string            m_name;
   class="type">int               m_name_size;
   class="type">int               m_values_size;
   class="type">int               m_values_width;
   class="type">bool              m_auto_scale;
   class="type">class="kw">double            m_default_step;   class=class="str">"cmt">// 默认步长的长度
   class="type">class="kw">double            m_max_labels;    class=class="str">"cmt">// 最大标记数量
   class="type">class="kw">double            m_min_grace;     class=class="str">"cmt">// 应用于最小数据范围的 "grace" 值
   class="type">class="kw">double            m_max_grace;     class=class="str">"cmt">// 应用于最大数据范围的 "grace" 值
   class=class="str">"cmt">//---
class="kw">public:
                     CAxis(class="type">void);
                    ~CAxis(class="type">void);
   class=class="str">"cmt">//--- 属性
   class="type">class="kw">double            Min(class="type">void)                class="kw">const { class="kw">return(m_min);  }
   class="type">void              Min(class="kw">const class="type">class="kw">double min)          { m_min=min;      }
   class="type">class="kw">double            Max(class="type">void)                class="kw">const { class="kw">return(m_max);  }

◍ 给指标容器塞默认属性

这段 C++ 风格的存取器,本质是在给一个自定义指标对象补全默认外观与边界参数。MT5 里你若自己封装图形层,就得靠这套 getter/setter 把颜色、自缩放、字号、步长等塞进私有成员。 void Max(const double max) { m_max=max; } string Name(void) const { return(m_name); } void Name(const string name) { m_name=name; } //--- 默认属性 uint Color(void) const { return(m_clr); } void Color(const uint clr) { m_clr=clr; } bool AutoScale(void) const { return(m_auto_scale); } void AutoScale(const bool auto) { m_auto_scale=auto; } int ValuesSize(void) const { return(m_values_size); } void ValuesSize(const int size) { m_values_size=size; } int ValuesWidth(void) const { return(m_values_width); } void ValuesWidth(const int width) { m_values_width=width; } int NameSize(void) const { return(m_name_size); } void NameSize(const int size) { m_name_size=size; } double DefaultStep(void) const { return(m_default_step); } void DefaultStep(const double value) { m_default_step=value; } 逐行看:Max 写上限值到 m_max;Name 有读和写两个重载,分别返回和设置指标名;Color 用 uint 存 ARGB 色值,AutoScale 控制是否随价格自动拉纵轴。 ValuesSize 与 ValuesWidth 决定缓冲区点数和线宽,NameSize 是指标名在图上字号;DefaultStep 是拖拽时的默认步进,比如设 0.0001 对 EURUSD 就按 1 点跳动。外汇和贵金属杠杆高,这类外观参数不影响信号,但乱设 AutoScale 可能让极端影线把结构压扁,视觉误判风险不小。 开 MT5 建个空指标把这段贴进类里,编译后改 Color(0xFF00FF00) 看线变绿,就能确认封装链路通了。

MQL5 / C++
class="type">void Max(class="kw">const class="type">class="kw">double max) { m_max=max; }
class="type">class="kw">string Name(class="type">void) class="kw">const { class="kw">return(m_name); }
class="type">void Name(class="kw">const class="type">class="kw">string name) { m_name=name; }
class=class="str">"cmt">//--- 默认属性 
class="type">uint Color(class="type">void) class="kw">const { class="kw">return(m_clr); }
class="type">void Color(class="kw">const class="type">uint clr) { m_clr=clr; }
class="type">bool AutoScale(class="type">void) class="kw">const { class="kw">return(m_auto_scale); }
class="type">void AutoScale(class="kw">const class="type">bool auto) { m_auto_scale=auto; }
class="type">int ValuesSize(class="type">void) class="kw">const { class="kw">return(m_values_size); }
class="type">void ValuesSize(class="kw">const class="type">int size) { m_values_size=size; }
class="type">int ValuesWidth(class="type">void) class="kw">const { class="kw">return(m_values_width); }
class="type">void ValuesWidth(class="kw">const class="type">int width) { m_values_width=width; }
class="type">int NameSize(class="type">void) class="kw">const { class="kw">return(m_name_size); }
class="type">void NameSize(class="kw">const class="type">int size) { m_name_size=size; }
class="type">class="kw">double DefaultStep(class="type">void) class="kw">const { class="kw">return(m_default_step); }
class="type">void DefaultStep(class="kw">const class="type">class="kw">double value) { m_default_step=value; }

标签数量与间隔的存取接口

这组方法暴露了三个关键控制量:最大标签数 m_max_labels、最小间隔 m_min_grace、最大间隔 m_max_grace。它们以成对 getter/setter 形式存在,意味着在 EA 运行期可以动态改写,不必重启指标。 double MaxLabels(void) const { return(m_max_labels); } void MaxLabels(const double value) { m_max_labels=value; } 前两行管标签上限:读出来看当前允许画几个,写进去就能收紧或放开。注意参数是 double 而非 int,说明底层可能用浮点参与比较,传 5.0 和 5 效果一致但类型不同。 double MinGrace(void) const { return(m_min_grace); } void MinGrace(const double value) { m_min_grace=value; } double MaxGrace(void) const { return(m_max_grace); } void MaxGrace(const double value) { m_max_grace=value; } 后四行控制相邻标签的时间或价格间距边界。若 MinGrace 设得偏大,低频行情里标签可能稀疏到漏掉关键摆动;设得偏小则在震荡段容易堆满达到 m_max_labels 上限。外汇与贵金属波动剧烈,调这些参数前建议在 MT5 策略测试器用历史数据跑一遍验证密度。

MQL5 / C++
  class="type">class="kw">double                MaxLabels(class="type">void)                class="kw">const { class="kw">return(m_max_labels);    }
  class="type">void                  MaxLabels(class="kw">const class="type">class="kw">double value)         { m_max_labels=value;      }
  class="type">class="kw">double                MinGrace(class="type">void)                class="kw">const { class="kw">return(m_min_grace);     }
  class="type">void                  MinGrace(class="kw">const class="type">class="kw">double value)          { m_min_grace=value;       }
  class="type">class="kw">double                MaxGrace(class="type">void)                class="kw">const { class="kw">return(m_max_grace);     }
  class="type">void                  MaxGrace(class="kw">const class="type">class="kw">double value)          { m_max_grace=value;       }
};

「用独立程序验证曲线属性控件」

想直接看 CGraphic 曲线在 MT5 里的实际表现,最省事的办法是跑一个独立的 MQL 小程序。它的窗体顶部排了一组控件,下方挂两个 CGraph 图表:上方画随机数据序列,下方按动量指标公式画导数,肉眼就能比对曲线类型和圆点类型的差异。 控件里「数组大小」旋转框决定当前显示的元素数量,「周期」旋转框是动量计算用的变量值,「曲线类型」和「圆点类型」组合框实时改渲染样式,「动画」勾选后数据按计时器自动灌入,「随机」按钮则按顺序生成新序列。外汇和贵金属行情高波动,这类图形验证仅供本地研究,不构成任何交易方向暗示。 核心逻辑都封在自定义类 CProgram 里,它管四件事:设数组大小、初始化数据、刷新图表、尾端增删元素,再靠计时器做动画。下面这段是类的方法声明,下载配套文件能看到各方法具体实现。 代码逐行看:class CProgram 继承 CWndEvents,说明它要接管窗口事件;data1[]~data4[] 是四组 double 数组,分两图备用;Resize 系列按新尺寸重分配数组;Init 填初始值,Zero 清零;SetGraph 在指定索引写随机值;Update 系列重绘对应图表;RecalculatingSeries 重算序列,Add/DeleteValue 动尾端,UpdateGraphByTimer 和 AnimateGraphSeries 驱动自动刷新。

MQL5 / C++
class CProgram : class="kw">public CWndEvents
  {
class="kw">protected:
  class=class="str">"cmt">//--- 用于在图表上输出的数据数组
  class="type">class="kw">double            data1[];
  class="type">class="kw">double            data2[];
  class=class="str">"cmt">//---
  class="type">class="kw">double            data3[];
  class="type">class="kw">double            data4[];
  class=class="str">"cmt">//---
class="kw">private:
  class=class="str">"cmt">//--- 调整数组的大小
  class="type">void              ResizeGraph1Arrays(class="type">void);
  class="type">void              ResizeGraph2Arrays(class="type">void);
  class="type">void              ResizeGraph1Arrays(class="kw">const class="type">int new_size);
  class="type">void              ResizeGraph2Arrays(class="kw">const class="type">int new_size);
  class=class="str">"cmt">//--- 数组初始化
  class="type">void              InitGraph1Arrays(class="type">void);
  class="type">void              InitGraph2Arrays(class="type">void);
  class=class="str">"cmt">//--- 数组清零
  class="type">void              ZeroGraph1Arrays(class="type">void);
  class="type">void              ZeroGraph2Arrays(class="type">void);
  class=class="str">"cmt">//--- 在指定的索引处设置随机值
  class="type">void              SetGraph1Value(class="kw">const class="type">int index);
  class="type">void              SetGraph2Value(class="kw">const class="type">int index);
  class=class="str">"cmt">//--- 更新图表上的序列
  class="type">void              UpdateGraph(class="type">void);
  class="type">void              UpdateGraph1(class="type">void);
  class="type">void              UpdateGraph2(class="type">void);
  
  class=class="str">"cmt">//--- 重新计算图表上的序列
  class="type">void              RecalculatingSeries(class="type">void);
  class=class="str">"cmt">//--- 添加一个或多个值到数组的末尾
  class="type">void              AddValue(class="type">void);
  class=class="str">"cmt">//--- 删除数组末尾的一个值
  class="type">void              DeleteValue(class="type">void);
  class=class="str">"cmt">//--- 按计时器刷新图表
  class="type">void              UpdateGraphByTimer(class="type">void);
  class=class="str">"cmt">//--- 图表序列动画
  class="type">void              AnimateGraphSeries(class="type">void);
  };
把控件联调交给小布盯盘
这些标准库集成后的控件诊断,小布盯盘的 AIGC 已内置,打开对应品种页即可看到类依赖是否缺失,你专注决策。

常见问题

通过 CGraphic::GetGraphicPointer() 拿到指向 CGraphic 实例的指针,再操作 CAxis 与 CCurve 附属类。
相对 14.1/14.2 版本,新版体积减少约 10% 代码量、近 40% 冗余,CCanvas 副本被标准库原始版替代。
适合周期性或相位偏移数据的可视化验证,能直接调用新图表控件观察曲线生成逻辑。
可以,小布盯盘内置的 AIGC 会扫描品种页里的类包含关系,提示 Graphic.mqh 等文件是否就位。
因为 CGraphic 用于创建图表,包含后整个函数库的控件都能访问画布与绘图能力。