图形界面 XI: 集成标准图形库 (统合构建 16)·进阶篇
(2/3)· 函数库体积砍掉近四成,图表类全面换血,老接口别再硬套
◍ 坐标轴边距与历史标签的读写接口
在 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 比对点值后再定。
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 式的绘图类时,直接照这个结构抄一遍,能把网格配色和绘制逻辑解耦。外汇与贵金属图表叠加自绘图形时波动剧烈,高风险,参数乱改可能让界面卡顿或视觉误导,建议先在小周期回测图验证。
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) 就能钉死坐标轴,不依赖自动缩放。外汇与贵金属波动大,钉死量程前最好先跑一段历史数据看极值分布,否则高风险时段价格出界会画不出线。
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) 看线变绿,就能确认封装链路通了。
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 策略测试器用历史数据跑一遍验证密度。
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 驱动自动刷新。
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); };