股票量化軟件:赫茲量化程序圖形窗體設(shè)計(jì)
我們研究了在 MQL 中構(gòu)建界面標(biāo)記系統(tǒng)的一般概念,并為表達(dá)類的層次結(jié)構(gòu)的界面元素實(shí)現(xiàn)了基本類的初始化,對(duì)其進(jìn)行緩存,樣式化,為其設(shè)置屬性,以及響應(yīng)事件。 動(dòng)態(tài)創(chuàng)建所需元素能夠即時(shí)修改簡單對(duì)話框布局,而已創(chuàng)建元素的單個(gè)存儲(chǔ)的可用性通常能用建議的 MQL 語法來創(chuàng)建它,隨后可以將其按原樣插入到需要 GUI 的 MQL 程序之中。 故此,我們已著手創(chuàng)建窗體的圖形編輯器。 我們會(huì)在本文中密切關(guān)注此任務(wù)。
就 MQL 而言,該程序?qū)⒃诟髯悦Q的頭文件中定義 2 個(gè)基類,即 InspectorDialog 和 DesignerForm。
?#include "InspectorDialog.mqh" ?#include "DesignerForm.mqh" ? ?InspectorDialog inspector; ?DesignerForm designer; ? ?int OnInit() ?{ ? ? ?if(!inspector.CreateLayout(0, "Inspector", 0, 20, 20, 200, 400)) return (INIT_FAILED); ? ? ?if(!inspector.Run()) return (INIT_FAILED); ? ? ?if(!designer.CreateLayout(0, "Designer", 0, 300, 50, 500, 300)) return (INIT_FAILED); ? ? ?if(!designer.Run()) return (INIT_FAILED); ? ? ?return (INIT_SUCCEEDED); ?}
這兩個(gè)窗口都是由 MQL 標(biāo)記技術(shù)形成的 AppDialogResizable(以下稱為 CAppDialog)的子類。 因此,我們看到調(diào)用 CreateLayout,替代了 Create。
每個(gè)窗口都有自己的界面元素緩存。 然而,在 Inspector 當(dāng)中,它從一開始就填充了“控件”,它們以相當(dāng)復(fù)雜的布局(我們嘗試以一般術(shù)語進(jìn)行研究)進(jìn)行描述,而在 Designer 里則是空的。 這很容易解釋:幾乎所有程序的業(yè)務(wù)邏輯都存儲(chǔ)在 Inspector 之中,而 Designer 則是一個(gè)虛擬對(duì)象,Inspector 將根據(jù)用戶的命令逐個(gè)實(shí)現(xiàn)新元素。
PropertySet
上面列出的每個(gè)屬性都由特定類型的值表示。 例如,元素名稱是一個(gè)字符串,而寬度和高度則是整數(shù)。 全部的數(shù)值集合完整定義了必須出現(xiàn)在 Designer 中的對(duì)象。 將集合存儲(chǔ)在一個(gè)位置是合理的,為此引入了特殊類 PropertySet。 但是其中必須包含哪些成員變量呢?
乍一看,使用簡單嵌入式類型的變量似乎是一個(gè)顯而易見的解決方案。 不過,它們?nèi)狈頃?huì)用到的重要功能。 MQL 不支持指向簡單變量的鏈接。 于此同時(shí),鏈接在處理用戶界面的算法當(dāng)中又非常重要。 這通常意味著針對(duì)數(shù)值變化的復(fù)雜反應(yīng)。 例如,在一個(gè)字段中輸入的數(shù)值若是超界,其必然會(huì)阻塞某些依賴它的“控件”。如果這些“控件”能夠通過檢查存儲(chǔ)在單一位置處的數(shù)值,并遵其指導(dǎo)控制自己的狀態(tài),將會(huì)很方便。 最簡單的方式是利用鏈接“指路”到同一變量。 因此,我們將用近似如下所示的模板包裝器類,替代簡單的嵌入式類型,臨時(shí)將其命名為 Value。
?template<typename V> ?class Value ?{ ? ?protected: ? ? ?V value; ? ? ? ? ?public: ? ? ?V operator~(void) const // getter ? ? ?{ ? ? ? ?return value; ? ? ?} ? ? ? ? ? ?void operator=(V v) ? ? // setter ? ? ?{ ? ? ? ?value = v; ? ? ?} ?};
加上單詞“大約”是個(gè)好主意。 實(shí)際上,會(huì)在類中添加更多功能,下面將會(huì)進(jìn)行研究。
對(duì)象包裝器能夠攔截重載運(yùn)算符 '=' 作為賦新值,而在使用簡單類型時(shí)是不可能的。 我們將需要它。
研究該類,可以大致如下描述新界面對(duì)象的屬性集合。
?class PropertySet ?{ ? ?public: ? ? ?Value<string> name; ? ? ?Value<int> type; ? ? ?Value<int> width; ? ? ?Value<int> height; ? ? ?Value<int> style; // VERTICAL_ALIGN / HORIZONTAL_ALIGN / ENUM_ALIGN_MODE ? ? ?Value<string> text; ? ? ?Value<color> clr; ? ? ?Value<int> align; // ENUM_WND_ALIGN_FLAGS + WND_ALIGN_CONTENT ? ? ?Value<ushort> margins[4]; ?};
在 “Inspector” 對(duì)話框里,我們將引入該類的變量,從 Inspector 控件輸入的當(dāng)前設(shè)置會(huì)集中存儲(chǔ)于此。
顯然,在 Inspector 里每個(gè)屬性都會(huì)用合適的控件來定義。 例如,為了選擇要?jiǎng)?chuàng)建的“控件”類型,將用下拉列表 CComboBox,而將 CEdit 輸入框則用于名稱。 屬性代表類型的單個(gè)值,例如列表中的行、數(shù)字或索引。 即使是復(fù)合屬性,例如為 4 個(gè)邊中的每一個(gè)分別定義的偏移量,也應(yīng)獨(dú)立考慮(左,上、等等),因?yàn)樾枰A?4 個(gè)字段用于輸入它們,因此,每個(gè)值都將連接到為其分配的控件。
因此,我們?yōu)?“Inspector” 對(duì)話框制定一條顯而易見的規(guī)則 — 其中的每個(gè)控件都定義與之相關(guān)的屬性,并且始終擁有給定類型的特定值。 這導(dǎo)致我們獲得以下架構(gòu)解決方案。
“控件”的特性
在之前的文章中,我們引入了一個(gè)特殊的接口 Notifiable,該接口允許為特定控件定義事件處理。
?template<typename C> ?class Notifiable: public C ?{ ? ?public: ? ? ?virtual bool onEvent(const int event, void *parent) { return false; }; ?};
在這里,C 是“控件”類之一,譬如 CEdit、CSpinEdit、等等。 布局緩存自動(dòng)為相關(guān)元素和事件類型調(diào)用 onEvent 應(yīng)答程序。 很自然,只有將正確的代碼添加到事件映射中,它才會(huì)發(fā)生。 例如,在上一部分中,依此原理調(diào)整了處理 “Inject” 按鈕單擊的過程(它已被描述為 Notifiable <CButton> 的后代)。
如果用控件來調(diào)整預(yù)定義類型的屬性,那么創(chuàng)建一個(gè)更專業(yè)的接口 PlainTypeNotifiable 會(huì)很令人神往。
?template<typename C, typename V> ?class PlainTypeNotifiable: public Notifiable<C> ?{ ? ?public: ? ? ?virtual V value() = 0; ?};
方法 value 旨在從 C 元素里返回最典型的 V-類型值。例如,對(duì)于類 CEdit,返回字符串類型值看起來很自然(在某些假設(shè)的類 ExtendedEdit 中)。
?class ExtendedEdit: public PlainTypeNotifiable<CEdit, string> ?{ ? ?public: ? ? ?virtual string value() override ? ? ?{ ? ? ? ?return Text(); ? ? ?} ?};
對(duì)于每種“控件”,都有一個(gè)單一特征的數(shù)據(jù)類型,或有限范圍(例如,對(duì)于整數(shù),您可選的精度有 short、int 或 long)。 所有“控件”都有一個(gè)或另一個(gè) “getter” 方法,可以在可重載的 “value” 方法中取值。
故此,我們進(jìn)入了體系結(jié)構(gòu)解決方案的要點(diǎn) — 協(xié)調(diào) Value 和 PlainTypeNotifiable 類。 它用后代類實(shí)現(xiàn),PlainTypeNotifiable,該類將“控件”值從 Inspector 里移到與其鏈接的 Value 屬性中。
?template<typename C, typename V> ?class NotifiableProperty: public PlainTypeNotifiable<C,V> ?{ ? ?protected: ? ? ?Value<V> *property; ? ? ? ? ?public: ? ? ?void bind(Value<V> *prop) ? ? ?{ ? ? ? ?property = prop; ? ? // pointer assignment ? ? ? ?property = value(); ?// overloaded operator assignment for value of type V ? ? ?} ? ? ? ? ? ?virtual bool onEvent(const int event, void *parent) override ? ? ?{ ? ? ? ?if(event == ON_CHANGE || event == ON_END_EDIT) ? ? ? ?{ ? ? ? ? ?property = value(); ? ? ? ? ?return true; ? ? ? ?} ? ? ? ?return false; ? ? ?}; ?};
股票量化軟件:赫茲量化程序圖形窗體設(shè)計(jì)的評(píng)論 (共 條)
