我不会长时间批评这篇文章,因为作者显然没有为读者着想。他是在自说自话,那些对标准库--其类、方法和变量--再熟悉不过的人也会明白 他的意思。作者在自己熟悉的代码中 "驰骋",修改一些....。
对这个图书馆了如指掌的人几乎不需要任何补充。
而对这个资料库再熟悉不过的人也不太可能需要任何补充。
如果仔细阅读文章的结论,就能理解作者的(有争议的)结论:
//------------------------------------------------------------------------------------------------------------------------
1. 作者声称已经检查了"在 MQL 语言本身中描述 MQL 程序图形界面布局这一概念的可行性"(c)。
- 由于材料表述的特殊性,我无法对这一结论表示支持或反对。这一点并不明显。
//------------------------------------------------------------------------------------------------------------------------
2."使用动态生成元素并将其集中存储在缓存中,可以简化组件层次结构的创建和管理。大多数与界面设计有关的任务,如统一风格更改、事件处理、布局的即时编辑以及将其保存为适合以后使用的格式等,都可以在缓存的基础上实现"。(c)
- 目前还不清楚得出这一结论的理论基础是什么,因为没有对该技术进行描述,读者既不能同意也不能否认。 读者应该先了解该技术,然后再同意可实现性的证明。
//------------------------------------------------------------------------------------------------------------------------
3."如果把这些功能放在一起,就会发现简单的可视化表单编辑器几乎无所不能。它可以只支持许多 "控件 "所共有的最重要的属性,但仍允许形成界面空白"(c)
- 同样,如果不对技术上的细微差别进行概括性描述,不完全了解从图形库中生成可视化编辑器系统的读者既不能同意作者的观点,也不能与作者争论。 这一点并不明显。
//------------------------------------------------------------------------------------------------------------------------
4."然而,我们看到,即使是评估新概念的初始阶段也需要付出大量努力。因此,在实践中实现一个成熟的编辑器是一个相当大的挑战。这是另一个故事"。(c)
- 在我看来,这是最客观的结论。我同意这一结论。 你不能 "急于 "去做它(我自己已经尝试了好几年)。
//------------------------------------------------------------------------------------------------------------------------
我的结论:在文章的结论 部分(即最后一个结论),作者最接近现实。但是,文章的读者应该随着主题的发展得出相同的结论,为此,有必要逐步揭示技术。这两篇文章的主要缺点是没有披露技术。它们的内容是讨论作者非显而易见的解决方案的实施,没有任何前言。希望作者能考虑到这一点。
- www.mql5.com
如果仔细阅读文章的结论,就能理解作者的(有争议的)结论:
//------------------------------------------------------------------------------------------------------------------------
1. 作者声称已经检查了"在 MQL 语言本身中描述 MQL 程序图形界面布局这一概念的可行性"(c)。
- 由于材料表述的特殊性,我无法对这一结论表示支持或反对。这一点并不明显。
//------------------------------------------------------------------------------------------------------------------------
2."使用动态元素生成并集中存储在缓存中,可以更容易地创建和管理组件的层次结构。大多数与界面设计有关的任务都可以在缓存的基础上实现,如统一样式更改、事件处理、即时编辑布局以及将其保存为适合以后使用的格式等"。(c)
- 我们不知道得出这一结论的理论依据是什么,因为没有对该技术进行描述,读者既不能 同意也不能否定。 读者应该先了解该技术,然后才能同意可实现性的证明。
//------------------------------------------------------------------------------------------------------------------------
3."如果把这些功能放在一起,就会发现简单的可视化表单编辑器几乎无所不能。它可以只支持许多 "控件 "所共有的最重要的属性,但仍然可以让你形成界面空白"(c)
- 同样,如果不对技术上的细微差别进行概括性描述,不熟悉从图形库中生成可视化编辑器系统的读者既不能同意作者的观点,也不能与作者争论。 这一点并不明显。
//------------------------------------------------------------------------------------------------------------------------
4."然而,我们看到,即使是评估新概念的初始阶段也需要付出大量努力。因此,在实践中实施一个成熟的编辑器是一个相当大的挑战。这是另一个故事"。(c)
- 在我看来,这是最客观的结论。我同意这个结论。 你不能 "明目张胆 "地去做它(我自己也尝试了好几年)。
//------------------------------------------------------------------------------------------------------------------------
我的结论:在文章的结论 部分(即最后一个结论),作者最接近现实。但是,这篇文章的读者应该随着主题的发展得出相同的结论,为此,有必要逐步揭示技术。这两篇文章的主要缺点是没有披露技术。它们的内容是讨论作者非显而易见的解决方案的实施,没有任何前言。希望作者能考虑到这一点。
我将弃权,因为这令人望而却步。
我对文章内容进行了深入分析,发现其中有我想要的技术 "颗粒",但它们随意地散落在材料中。也就是说,作者并没有把理论和实现分开,而是把所有东西都混在一起,在这个过程中又用大量的代码示例把它们分开。我决定将其概念的基本内容收集在一起,以便对其有一个完整的了解。
解释该技术的文章片段副本(保留顺序,省略代码示例):
//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
1. 标准元素库的总体结构(考虑到支持 "橡皮图章 "和第三方容器的改编版本)见类图。(我的补充:已给出图表)。
2.元素生成和缓存
到目前为止,元素都是作为窗口对象中的自动实例构建的。它们本质上是 "空白",然后通过创建(Create)等方法进行初始化。GUI 元素布局系统可以自行创建这些元素,而不是从窗口中获取它们。为此,只要有某种存储空间就足够了。我们称之为 LayoutCache。基本上,它是一个基类指针数组(对所有元素通用),可以使用保存方法将元素放置在其中。该接口还实现(如果可能的话,在这个抽象层)或声明(供以后重写)了一些方法,用于按编号、名称、引用或 "父 "关系(从嵌套元素到容器的反馈)搜索元素。
让我们将缓存作为静态成员添加到 LayoutBase 类中。每个窗口都必须为自己创建一个缓存实例,并在 CreateLayout(创建布局)等方法的开头使用 setCache 将其设置为工作缓存。由于 MQL 程序是单线程的,因此我们可以保证窗口(如果需要多个窗口)不会并行形成并争夺缓存指针。当堆栈结束时,我们将在 LayoutBase 析构函数中自动清零指针--这意味着我们已将最后一个外部容器留在布局描述中,无需再保存任何其他内容。将引用清零并不意味着我们要清除缓存。它只是确保潜在的下一个布局不会错误地添加来自另一个窗口的 "控制器"。为了填充缓存,让我们为 LayoutBase 添加一种新的 init 方法--这次的参数中没有指向 "第三方 "GUI 元素的指针或引用。通过模板,我们可以编写新的 T,并在布局过程中生成对象(默认情况下,一次只能生成一个对象,但也可以选择生成多个对象)。
我们为标准库 元素编写了一个具体的缓存实现--StdLayoutCache(此处使用缩写,完整代码见附录)。
请注意,get 方法可以通过序列号(如果输入参数为正数)或标识符(带减号)搜索 "控件"。这里的标识符指的是标准组件库为事件分派分配的唯一编号。在事件中,它通过 lparam 参数传递。
3.造型器
由于缓存是一个集中处理元素的对象,因此除了样式化之外,使用它来执行许多其他任务也很方便。特别是,单一样式的规则(颜色、字体、缩进)可以统一应用于元素。只需在一个地方配置这种样式,而不必为每个 "控件 "单独编写相同的属性。此外,缓存还可以承担处理缓存元素信息的任务。我们有可能动态地构建、缓存和交互所有元素。这样,我们就完全不需要在窗口中声明任何 "显式 "元素了。稍后我们将看到动态创建的元素与自动创建的元素相比有何优势。
为了支持集中式样式,StdLayoutCache 类有一个停滞方法:每个 "控件 "的 apply 方法将被调用两次:在初始化阶段(STYLE_PHASE_BEFORE_INIT)和在容器中注册阶段(STYLE_PHASE_AFTER_INIT)。因此,LayoutBase::init 方法添加了对第一阶段的调用:析构函数添加了类似的字符串,但第二阶段使用的是 STYLE_PHASE_AFTER_INIT。
之所以需要两个阶段,是因为样式设计的目的不同。有些元素有时需要具有比在样式化器中设置的一般属性优先级更高的个别属性。在初始化阶段,"控件 "仍然是空的,没有布局中的设置。而在注册阶段,所有属性都已设置完毕,您还可以根据这些属性更改样式。最明显的例子如下。我们希望所有输入字段都显示灰色的 "只读 "标志。但 "只读 "属性是在初始化后的布局过程中分配给 "控件 "的,因此第一阶段不合适,需要第二阶段。另一方面,通常并不是所有的输入字段都会有这种标记,在所有其他情况下,有必要在布局语言执行选择性自定义之前设置默认颜色。
顺便提一下,类似的技术也可用于将 MQL 程序界面集中本地化为不同的国家语言。
4.事件处理
在逻辑上分配给缓存的第二个功能是事件处理。为此,在 LayoutCache 类中添加了一个存根方法(C 是类模板的参数):同样,它也可以在派生类中实现,但不一定。事件代码由特定库定义。
为了使该方法生效,我们需要类似于标准库中的事件捕获宏定义,并将其写入 map 中,例如:新宏将把事件重定向到缓存对象。
5示例 2.与控件对话
演示项目中的 CControlsDialog 类包含标准库 "控件 "的基本类型。类比第一个示例,我们删除所有创建控件的方法,代之以一个 CreateLayout 方法。顺便提一下,在旧项目中,这些方法共有 17 个,它们是通过复杂的条件运算符相互调用的。在缓存类中,声明了 onEvent 事件处理程序,我们将通过事件映射连接它。在这里,处理程序会将信息反映到父窗口,在父窗口中,信息会显示在信息栏中,与之前版本的示例相同。
造型器类可为所有元素设置相同的字段、在所有按钮上使用非标准字体,并以灰色显示带有 "只读 "属性的 CEdit(我们有一个,但如果添加,它将自动归入常规设置)。
在 CBox 类中添加了缩放嵌套元素以适应容器大小的算法。该算法在 AdjustFlexControls 方法中执行,只有在容器对齐标志中指定了特殊值 WND_ALIGN_CONTENT 时才会生效。它不是标准 ENUM_WND_ALIGN_FLAGS 枚举的一部分。容器会分析 "控件",以确定哪些是固定大小的,哪些不是。具有固定尺寸的 "控件 "是指没有指定与容器侧面对齐(特定尺寸)的控件。对于所有此类 "控件",容器会计算它们的尺寸总和,然后从容器总尺寸中减去,再将剩余部分按比例分给所有剩余的 "控件"。例如,如果容器中有两个 "控件",而这两个控件都没有锚点,那么它们将共享整个容器区域的一半。
这是一种非常方便的模式,但不应在嵌套容器集上滥用--由于尺寸计算采用一次通过算法,内部元素与容器区域的对齐会产生不确定性,而容器区域又会根据内容进行调整(因此,布局类有一个特殊事件 ON_LAYOUT_REFRESH,窗口可以向自己发送该事件以重复尺寸计算)。
通过 ON_EVENT_LAYOUT_CTRL_DLG 宏,可以为 NotifiableButton 类的任何按钮(在本例中只有一个)发出鼠标点击通知。ON_EVENT_LAYOUT_INDEX 宏向缓存中指定索引的按钮发送相同的事件。但这个宏无法编写,因为 ON_EVENT_LAYOUT_ARRAY 宏的最后一行会将鼠标点击发送到缓存中的任何元素(如果其 lparam 标识符匹配)。
原则上,你可以将所有项目移到缓存中,然后用新方法处理它们的事件,但旧方法也行得通,而且可以将它们结合起来。
示例 3.DynamicForm 动态布局
在最后一个示例中,我们将看到一个所有元素都将在缓存中动态创建的表单。这将给我们带来几个重要的新功能。
与上一个示例一样,缓存将支持元素样式。唯一的样式设置是相同的突出框,这将使我们可以看到相互嵌套的容器,并根据需要用鼠标选择它们。
在 CreateLayout 方法中,描述了以下简单的界面结构。像往常一样,主容器占据窗口的整个客户端区域。顶部是一个带有两个按钮的图块:注入(Inject)和导出(Export)。下面的所有空间都被分为左右两列的容器占据。
首先要在缓存中搜索插入新元素的容器,名称为 "column1"。创建 injectionPanel 对象时,该容器将作为第一个参数。如果要传递的元素已经在缓存中,则布局算法会以特殊方式将其考虑在内--它不会被再次添加到缓存中,而是像往常一样被放置在容器堆栈中。通过这种方式,可以将元素添加到 "旧 "容器中。
根据用户的选择,使用 getPtr 辅助方法中的 new 运算符创建所需类型的对象。为了使添加的 "控件 "正常工作,需要为它们随机生成唯一的标识符。特殊的 AutoPtr 类可在离开代码块时删除指针。
如果添加的元素过多,它们就会超出容器边界。出现这种情况的原因是,我们现有的容器类还不知道如何正确应对溢出。例如,在这种情况下,我们可以显示滚动条并隐藏超出边界的元素。
但这并不重要。本例的重点在于,我们可以通过自定义表单生成动态内容,并提供必要的填充物和容器大小。
除了添加元素,这个对话框还知道如何删除元素。单击鼠标即可选择表单上的任何元素。元素的类别和名称会显示在日志中,元素本身会用红色框突出显示。如果点击一个已选定的元素,对话框会提示您确认删除,如果您同意,就会删除该元素。所有这些都在我们的缓存类中实现。
我们可以删除缓存中任何可用的用户界面元素,也就是说,不仅仅是那些通过注入按钮添加的元素。例如,我们可以通过这种方式删除整个左半部分或右边的 "单选框"。但如果我们尝试删除顶部包含两个按钮的容器,最有趣的事情就会发生。结果,"导出 "按钮将失去与对话框的绑定,而保留在图表上。
这是因为它是唯一一个被刻意描述为自动变量而非动态变量的元素(表单类中有一个 CButton m_button3 的实例)。
当标准库试图移除接口元素时,它将此委托给 CArrayObj 数组类,而 CArrayObj 数组类又会检查指针类型,并只移除带有 POINTER_DYNAMIC 的对象。因此,很明显,要构建一个自适应界面,让元素可以相互替换或完全删除,最好使用动态放置,而缓存为此提供了一个现成的解决方案。
最后,让我们来看看对话框的第二个按钮--导出。从它的名字就可以猜到,它的作用是以 MQL 布局语法将对话的当前状态保存为文本文件。当然,出于演示目的,表单只允许您在有限的范围内自定义其外观,但将外观卸载到现成的 MQL 代码中,然后将其轻松复制到程序中并获得相同的界面,这种可能性是非常有价值的潜在技术。当然,传输的只是界面,事件处理代码或造型器的一般设置必须独立插入。
导出功能由 LayoutExporter 类提供,我们将不再详细介绍,源代码附后。
- www.mql5.com
我试图在上述文字中找到一个系统、一个概念、一种技术--但我没有找到。它甚至不是一个 "草图",更像是一个意识流。不过,如果你经常切换到代码示例,你就不会注意到这一点。真遗憾。
事实上,对于了解 OOP 的人来说,一切都很清楚。
要怪就怪那些不懂的人。
对于了解 OOP 的人来说,这里其实很清楚。
不懂的人难辞其咎。
新文章 MQL 作为 MQL 程序图形界面的标记工具。 第二部分已发布:
本篇论文继续验证新概念,即利用 MQL 结构描述 MQL 程序的窗口界面。 基于 MQL 标记自动创建 GUI 提供了缓存和动态生成元素和控制风格,以及事件处理的新方案。 随附的是标准控件库的增强版本。
我们可以删除缓存中所有的任何界面元素,即,不仅是那些由 “Inject” 按钮添加的元素。 依此方式,您可以删除整个左半部分或右侧的“单选框”。例如,如果我们尝试删除上面含有两个按钮的容器,则会发生很有趣的事情。 这将导致 “Export” 按钮不再与对话框绑定,且将保留在图表当中。
可编辑表单:添加和删除元素
发生这种情况是因为只有元素才会被有意描述为自动,而不是动态变量(在表单类中,有一个 CButton 的实例 m_button3)。
当标准库尝试删除界面元素时,它将其委派给数组类 CArrayObj,后者依次检查指针类型,并仅删除类型为 POINTER_DYNAMIC 的对象。 因此,很明显,为了构建一个自适应界面,即元素可相互替换或完全删除,只能寄希望于动态放置,而缓存提供了一种现成的解决方案。
作者:Stanislav Korotky