DoEasy. 控件 (第 28 部分): 进度条控件中的柱线样式(基础篇)
「给进度条塞进柱线样式」
DoEasy 库这一轮把进度条控件补上了柱线(bar)渲染样式,原先只能画连续填充,现在能按离散柱段显示进度,视觉上更接近量能条或成交密度。 该改动在 2023-04-18 的库更新里随示例发布,原文示例贴文当时显示阅读量 752、评论 0,说明这类底层控件改造关注者不多但属于刚需。 要验证效果,在 MT5 里加载 DoEasy 示例面板,找 CProgressBar 派生类里 SetBarMode 一类接口;贵金属与外汇 EA 用这种控件做风险敞口条时,注意 MT5 回测中控件仅作可视化、不参与撮合,实盘前务必在模拟账户跑一遍防绘制卡顿。
进度条控件的三种显示样式与文本叠加
当前函数库里的进度条控件只实现了连续线这一种样式,但实际还有两种可用形态:分段块(Blocks)和连续滚动块(Marquee)。分段块是把连续线拆成独立定位的块,视觉边界更清晰;当你在运行前无法确定进度条要迭代多少次时,Marquee 更合适——一个宽度等于进度条一半的块会持续滚动,暗示“进行中”。 我打算在进度条里再叠一层文本用来显示状态信息。文本本身由函数库已有的 CLabel 类对象承担,但它不绑在进度条对象上,而是绑到进度条底层的基底对象。渲染时,文本放在一个和进度条同宽高的全透明标签里,这个标签始终置于前景,盖在进度条所有元素之上。 默认状态下进度条不显示文字,但在基于函数库的 EA 或脚本运行期间,可以随时手动加。只要指定输出内容,以及字体、字号、字体标志、颜色、不透明度等属性就能直接生效。外汇与贵金属自动化界面开发属高风险底层改造,改完建议在 MT5 策略测试器里跑一遍验证对象层级。
◍ 改进库类
在开始为进度条控件开发新样式之前,我们先简化基准图形元素的构造函数。 创建对象后,我在受保护和参数化构造函数中为其属性设置数值。 这两个构造函数,是一个长清单,其中的参数包含创建新对象时不断添加的一百多个不同的函数库图形元素。 因此,我们必须连续两次编写对象属性的设置 — 在 CGCnvElement 类的受保护构造函数和参数化构造函数之中。 将所有这些属性的设置移到单独的初始化方法中是合乎逻辑的。 与此同时,如果某些属性在不同的构造函数中设置不同,那么它们只需简单地传递到新方法的形式参数中,并在其中加以指示。 在 \MQL5\Include\DoEasy\Objects\Graph\ GCnvElement.mqh 中,往对象属性里 添加新属性 : 图形对象的结构是将对象属性正确写入文件,并在恢复期间从文件中读取它们所必需的。 现在往结构里写入属性已添加到对象当中,但尚未添加到结构之中。 即使结构的字段与对象的属性不匹配,也没有什么可担心的。 到目前为止,我还没有完成将对象保存到文件,并从文件中读取其属性的实现。 当然,随后,我会将图形对象的属性保存到一个文件之中,并从其中读取它们。 在那个时刻,我确实需要结构字段与对象属性的完全匹配。 在类的私密部分中, 声明初始化图形元素属性的新方法 : 所创建对象的属性将传递给该方法。 属性在不同的构造函数中具有不同的值,或者直接在构造函数的形式参数中指定,即从外部传递。 我将通过其参数将所有此类属性传递给该方法。 删除每个构造函数中设置对象属性的一长段清单,将其替换为调用初始化方法: 正如我们所见,每个特定构造函数中固有的值都传递给两个构造函数中的初始化方法。 在 新的初始化方法里 ,从类构造函数中删除属性设置: 在此,我们简单地从类构造函数中删除代码。 现在,不同构造函数中不同的属性值通过形式化方法参数传递,并设置到对象属性。 在创建对象结构的方法中,将设置相应图形元素属性的值添加到新的结构字段之中: 在依据结构创建对象的方法中,在当前文章中添加 从相应结构字段取值设置到对象属性的实现 : 现在,图形元素对象将正确保存到文件之中,并从文件中恢复。 沿着进度条运动的眩光可以具有平行六面体的形状。 这是一个倾斜的四边形,其顶点与底面顶点偏移 6 个像素。 对于一个小型对象,此偏移足以令对象看起来相当倾斜。 但随着其高度的增加,刚性设置为六个像素的斜率变得难以区分。 为了校正这种情况,我们需要令斜率成为相对值 — 对象越高,斜率越强。 如此,为了设置顶点偏移量的大小,我们将取对象的高度 — 然后视觉上的坡度将为 45 度。 在 \MQL5\Include\DoEasy\Objects\Graph\WForms\ GlareObj.mqh 中,在绘制平行四边形形式对象眩光形状的方法中,修复顶点坐标参数的初始化。 分配图形对象高度的数值,替代数字 6 : 现在,顶点将相对对象高度值偏移,并且无论对象高度如何,斜率都将始终在 45 度左右。 我可以选取不同的选项来创建“分段块”进度条样式 — 从直接创建每个块,到简单地绘制它们。 而我会按如下方式执行此操作。 我已经有一个完全填充的进度条,我只需要在没有块的地方擦除这个对象的背景。 在这种情况下,我将获得一个进度条的外观,由分段块组成。 这种方法的缺点是需要计算每个块的位置。 而这种方法的优点是实现简单。 在 \MQL5\Include\DoEasy\Objects\Graph\WForms\Helpers\ BarProgressBar.mqh 的私密部分中,
「图形对象结构体里的分组与进度条字段」
在 MT5 自定义指标或 EA 的 GUI 开发中,图形元素往往不是孤立存在的。用结构体统一管理时,除了最基础的 id 和 type,还需要 group 字段来标记该元素从属的逻辑组,方便批量隐藏或更新。 tab_size_mode、tab_page_number、tab_page_row、tab_page_column 这四个 int 字段控制元素在标签页布局里的位置:尺寸自适应模式、页序号、行号和列号。调面板布局时改这几个值比重新创建对象更轻量。 ProgressBar 相关字段全是 int:minimum 和 maximum 划定进度条值域边界,step 是重绘所需增量,style 决定样式,value 是当前值,marquee_speed 仅在跑马灯样式下生效,控制动画速度。 tooltip_initial_delay 和 tooltip_auto_pop_delay 是 ulong 类型,单位毫秒,分别管悬浮提示的初次延迟和持续时长。外汇与贵金属交易界面涉及实时资金,GUI 卡顿可能引发误操作,属高风险场景,参数需在实盘前于策略测试器验证。
class="type">int id; class=class="str">"cmt">// Element ID class="type">int type; class=class="str">"cmt">// Graphical element type class=class="str">"cmt">//---... class=class="str">"cmt">//---... class=class="str">"cmt">//--- class="type">int group; class=class="str">"cmt">// Group the graphical element belongs to class="type">int tab_size_mode; class=class="str">"cmt">// Tab size setting mode class="type">int tab_page_number; class=class="str">"cmt">// Tab index number class="type">int tab_page_row; class=class="str">"cmt">// Tab row index class="type">int tab_page_column; class=class="str">"cmt">// Tab column index class="type">int progress_bar_minimum; class=class="str">"cmt">// The lower bound of the range ProgressBar operates in class="type">int progress_bar_maximum; class=class="str">"cmt">// The upper bound of the range ProgressBar operates in class="type">int progress_bar_step; class=class="str">"cmt">// ProgressBar increment needed to redraw it class="type">int progress_bar_style; class=class="str">"cmt">// ProgressBar style class="type">int progress_bar_value; class=class="str">"cmt">// Current ProgressBar value from Min to Max class="type">int progress_bar_marquee_speed; class=class="str">"cmt">// Progress bar animation speed in case of Marquee style class=class="str">"cmt">//--- class="type">class="kw">ulong tooltip_initial_delay; class=class="str">"cmt">// Tooltip display delay class="type">class="kw">ulong tooltip_auto_pop_delay; class=class="str">"cmt">// Tooltip display duration
图形对象结构里的 tooltip 与文本字段
在 MQL5 自定义图形面板里,每个元素的结构体都预留了一组 tooltip 与字符串属性,直接决定鼠标悬停时终端弹什么、怎么弹。下面这段结构体片段里,tooltip_reshow_delay 是单元素新提示的重复显示延迟(ulong 类型),tooltip_show_always 控制非活动窗口是否也显示提示,tooltip_is_balloon 切换“云朵”样式,tooltip_use_fading 管显隐淡入淡出。 字符串侧用定长 uchar 数组承接:name_obj[64] 存图形对象名,name_res[64] 存资源名,text[256] 与 descript[256] 分别是元素文本和描述;tooltip_title[256]、tooltip_text[256] 专门放悬停标题与正文。开 MT5 在自定义指标里打印 sizeof(SData) 能看到这些字段实际占用的内存布局。 背景色保存走两个方法:SaveColorsBG 把传入的 color 数组拷进 m_array_colors_bg,SaveColorsBGMouseDown 则拷进按下态的背景色数组 m_array_colors_bg_dwn,都调了 CopyArraysColors 并传 DFUN 作默认处理。外汇与贵金属图表上做这类面板,注意高波动时段重绘可能拖慢响应,属高风险操作环境。
class="type">class="kw">ulong tooltip_reshow_delay; class=class="str">"cmt">// One element new tooltip display delay class="type">bool tooltip_show_always; class=class="str">"cmt">// Display a tooltip in inactive window class="type">int tooltip_icon; class=class="str">"cmt">// Icon displayed in a tooltip class="type">bool tooltip_is_balloon; class=class="str">"cmt">// Tooltip in the form of a "cloud" class="type">bool tooltip_use_fading; class=class="str">"cmt">// Fade when showing/hiding a tooltip class=class="str">"cmt">//--- Object real properties class=class="str">"cmt">//--- Object class="type">class="kw">string properties class="type">uchar name_obj[class="num">64]; class=class="str">"cmt">// Graphical element object name class="type">uchar name_res[class="num">64]; class=class="str">"cmt">// Graphical resource name class="type">uchar text[class="num">256]; class=class="str">"cmt">// Graphical element text class="type">uchar descript[class="num">256]; class=class="str">"cmt">// Graphical element description class="type">uchar tooltip_title[class="num">256]; class=class="str">"cmt">// Element tooltip title class="type">uchar tooltip_text[class="num">256]; class=class="str">"cmt">// Element tooltip text }; SData m_struct_obj; class=class="str">"cmt">// Object structure class=class="str">"cmt">//--- Save the colors to the background class="type">color array class="type">void SaveColorsBG(class="type">color &colors[]) { this.CopyArraysColors(this.m_array_colors_bg,colors,DFUN); } class="type">void SaveColorsBGMouseDown(class="type">color &colors[]) { this.CopyArraysColors(this.m_array_colors_bg_dwn,colors,DFUN); }
◍ 图形元素的悬停与初始化封装
在 MT5 自定义图形库里,鼠标悬停背景色和初始背景色是两套独立数组,分别由 SaveColorsBGMouseOver 与 SaveColorsBGInit 写入,二者都调用 CopyArraysColors 完成深拷贝,DFUN 作为默认颜色函数参数传入。 悬停态用 m_array_colors_bg_ovr 承载,初始化态用 m_array_colors_bg_init 承载;若你改了配色逻辑,只需替换这两个成员变量对应的源数组,不必动绘制主流程。 Initialize 方法负责把元素类型、ID、坐标(x,y)、宽高(w,h)、描述文本以及 movable/activity 开关一次性落定,参数多达 11 个,调用时漏传任一 int 都会让元素在图表上不响应点击事件。 CGCnvElement 的带参构造函数进一步接收 main_obj、base_obj、chart_id、wnd_num 等,说明一个元素可以挂载到另一元素之下形成层级;在外汇或贵金属图表上做悬浮面板时,这种层级关系决定了哪层先吃鼠标消息,实操中建议开 MT5 用注释掉 movable 的方式验证点击穿透概率。
class="type">void SaveColorsBGMouseOver(class="type">color &colors[]) { this.CopyArraysColors(this.m_array_colors_bg_ovr,colors,DFUN); } class="type">void SaveColorsBGInit(class="type">color &colors[]) { this.CopyArraysColors(this.m_array_colors_bg_init,colors,DFUN); } class=class="str">"cmt">//--- Initialize class="kw">property values class="type">void Initialize(class="kw">const ENUM_GRAPH_ELEMENT_TYPE element_type, class="kw">const class="type">int element_id,class="kw">const class="type">int element_num, class="kw">const class="type">int x,class="kw">const class="type">int y,class="kw">const class="type">int w,class="kw">const class="type">int h, class="kw">const class="type">class="kw">string descript,class="kw">const class="type">bool movable,class="kw">const class="type">bool activity); class="kw">public: class=class="str">"cmt">//+---------------------------------------------+ class=class="str">"cmt">//| Parametric constructor | class=class="str">"cmt">//+---------------------------------------------+ CGCnvElement::CGCnvElement(class="kw">const ENUM_GRAPH_ELEMENT_TYPE element_type, CGCnvElement *main_obj,CGCnvElement *base_obj, class="kw">const class="type">int element_id, class="kw">const class="type">int element_num, class="kw">const class="type">long chart_id, class="kw">const class="type">int wnd_num, class="kw">const class="type">class="kw">string descript, class="kw">const class="type">int x, class="kw">const class="type">int y, class="kw">const class="type">int w, class="kw">const class="type">int h, class="kw">const class="type">color colour,