DoEasy 函数库中的图形(第八十八部分):图形对象集合 — 存储对象动态变化属性的二维动态数组
内容
概述
在以前的所有文章中,我都曾利用简单数组来存储对象属性数据。 我们已有了三个数组 — 整数型、实数型和字符串型属性各自分属的一维数组。 此类数组的每个单元格所存储的数值,均与 Defines.mqh 中的特定对象属性枚举相对应 。 到目前为止,一切都很顺利。 但我现在已决定,针对图形(和其它某些)对象,单个属性也许会返回该属性的独立单位的值。
我来解释一下。 例如,我们有图形对象时间属性。 依据价格/时间坐标在图表上定位的图形对象会在其所放置的图表上拥有若干个轴点。 假设一个对象,譬如 Arrow(箭头形),只有一个轴点,我们就可依据指定的 OBJPROP_TIME 作为属性 ID 调用 ObjectGetInteger() 函数来获取该轴点。 这种构造返回与对象唯一轴点对应的图表时间:
ObjectGetInteger(0, Name, OBJPROP_TIME);
但若是一个对象有两个轴点会如何,比如趋势线? 我们如何获得两个轴点的时间? ObjectGetInteger() 函数的 prop_modifier 形式参数可以帮助实现这一点。 其默认值为 0,并与第一个轴点相对应。 为了获得第二个轴点的数据,我们需要将其设为 1,而对于第三个轴点 — 2,以此类推:
ObjectGetInteger(0, Name, OBJPROP_TIME, 0); ObjectGetInteger(0, Name, OBJPROP_TIME, 1); ObjectGetInteger(0, Name, OBJPROP_TIME, 2); ObjectGetInteger(0, Name, OBJPROP_TIME, n);
到目前为止,一切都简单明了。 从对象获得的所有数据都被设置在数组里:整数型数据被设置在 long 型数组,实数型数据 — 被设置在 double 型数组,而字符串型数据 — 被设置在 string 型数组。 这意味着我们可以简单地利用二维数组来保存数据,这些数据可依据 prop_modifier 中指示的所需轴点来获得。 一切似乎都合乎逻辑。 轴点 0 存储在零维中,轴点 1 — 位于第一维中,轴点 2 — 存储在第二维中,以此类推:
array[TIME][0] = ObjectGetInteger(0, Name, OBJPROP_TIME, 0); array[TIME][1] = ObjectGetInteger(0, Name, OBJPROP_TIME, 1); array[TIME][2] = ObjectGetInteger(0, Name, OBJPROP_TIME, 2); //... array[TIME][n] = ObjectGetInteger(0, Name, OBJPROP_TIME, n);
然而,有一个陷阱。 首先,众多对象属性中的每一个,所得到的数据量也许不同。 例如,对于斐波那契射线对象,对象在图表上定位我们需要两个轴点。 然而,对象还拥有不同的级别数。 默认情况下有九个。 此外,用户可以随时更改它。 其次,单个对象属性的某些“多属性”可以动态更改。
换言之,我们会一直往其中存储对象轴点,故无法预先知道数组第二维度的大小。 此外,这些属性均可动态更改。 考虑到这一点,我们不能用二维数组来存储不同对象的属性,原因如下:
- 所有对象都是其基准对象的衍生后代,由这些基准对象定义了存储属性的数组。 每个属性都应该具有第二个数组维度的预定义大小,对于每个对象及其每个属性,该大小可能有所不同。 在 MQL 中创建二维数组时,我们应该为每个对象的每个属性指定在抽象类中不曾知晓的第二个维度值;
- 这种类型的每个“多维”属性都可以由用户动态更改,也可以通过编程进行更改。 但是,在 MQL 中,我们无法动态更改多维数组的非零维度。
为了创建一个这种数组,我将利用指向 CObject 及其子类实例指针的动态数组类 — CArrayObj
。
基于新创建的类,创建一个存储对象属性的对象类,来替代通常的数组。 这允许我们随时更改数组第二维度中存储的数据数量,以便图形对象的属性变化时能够更改相应对象类属性。
当前任务如下:创建多维动态数组的类,利用它创建存储对象属性的动态二维数组,并用它替换存储对象属性的三个数组。 重新安排抽象图形对象类,并测试在动态数组中存储对象属性和跟踪属性更改的可能性。
动态多维数组类
实际上,CArrayObj 类是一个数组,存储指向自 CObject基类派生出的对象实例指针。 因此,该数组可以存储自 CObject 派生出的任何对象。 这意味着数组单元里可能包含 long,double 或 string 类型的数据,以及一个 CArrayObj 数组,该数组也许包含数据,亦或另外的数组。 虽然 CArrayObj 数组的一切都很清晰,但其内数据的情况则更加复杂。 数据并非从 CObject 类派生而来,因此我们需要创建存储这种数据的类。 此外,数组中的每个对象(以及数组对象本身)都应该具有指定的类型。 这对于清楚地理解数组单元格中到底存储了什么是必要的 — 这个简单对象包含整数型、实数型或字符串型数据,或另一个对象,其也许又会包含带有数据的对象,或又一个对象数组。 这要求必须创建复制类对象的方法 — 以便确切地知道是否将数据复制到单元格中(如果所复制数组的相应单元格中含有数据),亦或在源数组中创建新的数据数组(如果复制数组的相应单元格包含该数组),并从中复制数据。 我们要立即设置必要的对象类型。 在 \MQL5\Include\DoEasy\Defines.mqh 里,把必要的类型常量添加到函数库对象类型列表当中(为了更好地理解,下面展示了完整的枚举):
//+------------------------------------------------------------------+ //| Enumerations | //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| List of library object types | //+------------------------------------------------------------------+ enum ENUM_OBJECT_DE_TYPE { //--- Graphics OBJECT_DE_TYPE_GBASE = COLLECTION_ID_LIST_END+1, // "Base object of all library graphical objects" object type OBJECT_DE_TYPE_GELEMENT, // "Graphical element" object type OBJECT_DE_TYPE_GFORM, // Form object type OBJECT_DE_TYPE_GSHADOW, // Shadow object type //--- Animation OBJECT_DE_TYPE_GFRAME, // "Single animation frame" object type OBJECT_DE_TYPE_GFRAME_TEXT, // "Single text animation frame" object type OBJECT_DE_TYPE_GFRAME_QUAD, // "Single rectangular animation frame" object type OBJECT_DE_TYPE_GFRAME_GEOMETRY, // "Single geometric animation frame" object type OBJECT_DE_TYPE_GANIMATIONS, // "Animations" object type //--- Managing graphical objects OBJECT_DE_TYPE_GELEMENT_CONTROL, // "Managing graphical objects" object type //--- Standard graphical objects OBJECT_DE_TYPE_GSTD_OBJ, // "Standard graphical object" object type OBJECT_DE_TYPE_GSTD_VLINE = OBJECT_DE_TYPE_GSTD_OBJ+1+OBJ_VLINE, // "Vertical line" object type OBJECT_DE_TYPE_GSTD_HLINE = OBJECT_DE_TYPE_GSTD_OBJ+1+OBJ_HLINE, // "Horizontal line" object type OBJECT_DE_TYPE_GSTD_TREND = OBJECT_DE_TYPE_GSTD_OBJ+1+OBJ_TREND, // "Trend line" object type OBJECT_DE_TYPE_GSTD_TRENDBYANGLE = OBJECT_DE_TYPE_GSTD_OBJ+1+OBJ_TRENDBYANGLE, // "Trend line by angle" object type OBJECT_DE_TYPE_GSTD_CYCLES = OBJECT_DE_TYPE_GSTD_OBJ+1+OBJ_CYCLES, // Cyclic lines" object type OBJECT_DE_TYPE_GSTD_ARROWED_LINE = OBJECT_DE_TYPE_GSTD_OBJ+1+OBJ_ARROWED_LINE, // "Arrowed line" object type OBJECT_DE_TYPE_GSTD_CHANNEL = OBJECT_DE_TYPE_GSTD_OBJ+1+OBJ_CHANNEL, // "Equidistant channel" object type OBJECT_DE_TYPE_GSTD_STDDEVCHANNEL = OBJECT_DE_TYPE_GSTD_OBJ+1+OBJ_STDDEVCHANNEL, // "Standard deviation channel" object type OBJECT_DE_TYPE_GSTD_REGRESSION = OBJECT_DE_TYPE_GSTD_OBJ+1+OBJ_REGRESSION, // "Linear regression channel" object type OBJECT_DE_TYPE_GSTD_PITCHFORK = OBJECT_DE_TYPE_GSTD_OBJ+1+OBJ_PITCHFORK, // "Andrews' pitchfork" object type OBJECT_DE_TYPE_GSTD_GANNLINE = OBJECT_DE_TYPE_GSTD_OBJ+1+OBJ_GANNLINE, // "Gann line" object type OBJECT_DE_TYPE_GSTD_GANNFAN = OBJECT_DE_TYPE_GSTD_OBJ+1+OBJ_GANNFAN, // "Gann fan" object type OBJECT_DE_TYPE_GSTD_GANNGRID = OBJECT_DE_TYPE_GSTD_OBJ+1+OBJ_GANNGRID, // "Gann grid" object type OBJECT_DE_TYPE_GSTD_FIBO = OBJECT_DE_TYPE_GSTD_OBJ+1+OBJ_FIBO, // "Fibo levels" object type OBJECT_DE_TYPE_GSTD_FIBOTIMES = OBJECT_DE_TYPE_GSTD_OBJ+1+OBJ_FIBOTIMES, // "Fibo time zones" object type OBJECT_DE_TYPE_GSTD_FIBOFAN = OBJECT_DE_TYPE_GSTD_OBJ+1+OBJ_FIBOFAN, // "Fibo fan" object type OBJECT_DE_TYPE_GSTD_FIBOARC = OBJECT_DE_TYPE_GSTD_OBJ+1+OBJ_FIBOARC, // "Fibo arcs" object type OBJECT_DE_TYPE_GSTD_FIBOCHANNEL = OBJECT_DE_TYPE_GSTD_OBJ+1+OBJ_FIBOCHANNEL, // "Fibo channel" object type OBJECT_DE_TYPE_GSTD_EXPANSION = OBJECT_DE_TYPE_GSTD_OBJ+1+OBJ_EXPANSION, // "Fibo expansion" object type OBJECT_DE_TYPE_GSTD_ELLIOTWAVE5 = OBJECT_DE_TYPE_GSTD_OBJ+1+OBJ_ELLIOTWAVE5, // "Elliott 5 waves" object type OBJECT_DE_TYPE_GSTD_ELLIOTWAVE3 = OBJECT_DE_TYPE_GSTD_OBJ+1+OBJ_ELLIOTWAVE3, // "Elliott 3 waves" object type OBJECT_DE_TYPE_GSTD_RECTANGLE = OBJECT_DE_TYPE_GSTD_OBJ+1+OBJ_RECTANGLE, // "Rectangle" object type OBJECT_DE_TYPE_GSTD_TRIANGLE = OBJECT_DE_TYPE_GSTD_OBJ+1+OBJ_TRIANGLE, // "Triangle" object type OBJECT_DE_TYPE_GSTD_ELLIPSE = OBJECT_DE_TYPE_GSTD_OBJ+1+OBJ_ELLIPSE, // "Ellipse" object type OBJECT_DE_TYPE_GSTD_ARROW_THUMB_UP = OBJECT_DE_TYPE_GSTD_OBJ+1+OBJ_ARROW_THUMB_UP, // "Thumb up" object type OBJECT_DE_TYPE_GSTD_ARROW_THUMB_DOWN = OBJECT_DE_TYPE_GSTD_OBJ+1+OBJ_ARROW_THUMB_DOWN, // "Thumb down" object type OBJECT_DE_TYPE_GSTD_ARROW_UP = OBJECT_DE_TYPE_GSTD_OBJ+1+OBJ_ARROW_UP, // "Arrow up" object type OBJECT_DE_TYPE_GSTD_ARROW_DOWN = OBJECT_DE_TYPE_GSTD_OBJ+1+OBJ_ARROW_DOWN, // "Arrow down" object type OBJECT_DE_TYPE_GSTD_ARROW_STOP = OBJECT_DE_TYPE_GSTD_OBJ+1+OBJ_ARROW_STOP, // "Stop sign" object type OBJECT_DE_TYPE_GSTD_ARROW_CHECK = OBJECT_DE_TYPE_GSTD_OBJ+1+OBJ_ARROW_CHECK, // "Check mark" object type OBJECT_DE_TYPE_GSTD_ARROW_LEFT_PRICE = OBJECT_DE_TYPE_GSTD_OBJ+1+OBJ_ARROW_LEFT_PRICE, // "Left price label" object type OBJECT_DE_TYPE_GSTD_ARROW_RIGHT_PRICE = OBJECT_DE_TYPE_GSTD_OBJ+1+OBJ_ARROW_RIGHT_PRICE,// "Right price label" object type OBJECT_DE_TYPE_GSTD_ARROW_BUY = OBJECT_DE_TYPE_GSTD_OBJ+1+OBJ_ARROW_BUY, // "Buy sign" object type OBJECT_DE_TYPE_GSTD_ARROW_SELL = OBJECT_DE_TYPE_GSTD_OBJ+1+OBJ_ARROW_SELL, // "Sell sign" object type OBJECT_DE_TYPE_GSTD_ARROW = OBJECT_DE_TYPE_GSTD_OBJ+1+OBJ_ARROW, // "Arrow" object type OBJECT_DE_TYPE_GSTD_TEXT = OBJECT_DE_TYPE_GSTD_OBJ+1+OBJ_TEXT, // "Text" object type OBJECT_DE_TYPE_GSTD_LABEL = OBJECT_DE_TYPE_GSTD_OBJ+1+OBJ_LABEL, // "Text label" object type OBJECT_DE_TYPE_GSTD_BUTTON = OBJECT_DE_TYPE_GSTD_OBJ+1+OBJ_BUTTON, // "Button" object type OBJECT_DE_TYPE_GSTD_CHART = OBJECT_DE_TYPE_GSTD_OBJ+1+OBJ_CHART, // "Chart" object type OBJECT_DE_TYPE_GSTD_BITMAP = OBJECT_DE_TYPE_GSTD_OBJ+1+OBJ_BITMAP, // "Bitmap" object type OBJECT_DE_TYPE_GSTD_BITMAP_LABEL = OBJECT_DE_TYPE_GSTD_OBJ+1+OBJ_BITMAP_LABEL, // "Bitmap label" object type OBJECT_DE_TYPE_GSTD_EDIT = OBJECT_DE_TYPE_GSTD_OBJ+1+OBJ_EDIT, // "Input field" object type OBJECT_DE_TYPE_GSTD_EVENT = OBJECT_DE_TYPE_GSTD_OBJ+1+OBJ_EVENT, // "Event object which corresponds to an event in Economic Calendar" object type OBJECT_DE_TYPE_GSTD_RECTANGLE_LABEL = OBJECT_DE_TYPE_GSTD_OBJ+1+OBJ_RECTANGLE_LABEL, // "Rectangle Label object used to create and design the custom graphical interface" object type //--- Objects OBJECT_DE_TYPE_BASE = OBJECT_DE_TYPE_GSTD_RECTANGLE_LABEL+1, // Base object for all library objects OBJECT_DE_TYPE_BASE_EXT, // Extended base object for all library objects OBJECT_DE_TYPE_ACCOUNT, // "Account" object type OBJECT_DE_TYPE_BOOK_ORDER, // "Book order" object type OBJECT_DE_TYPE_BOOK_BUY, // "Book buy order" object type OBJECT_DE_TYPE_BOOK_BUY_MARKET, // "Book buy order at market price" object type OBJECT_DE_TYPE_BOOK_SELL, // "Book sell order" object type OBJECT_DE_TYPE_BOOK_SELL_MARKET, // "Book sell order at market price" object type OBJECT_DE_TYPE_BOOK_SNAPSHOT, // "Book snapshot" object type OBJECT_DE_TYPE_BOOK_SERIES, // "Book snapshot series" object type OBJECT_DE_TYPE_CHART, // "Chart" object type OBJECT_DE_TYPE_CHART_WND, // "Chart window" object type OBJECT_DE_TYPE_CHART_WND_IND, // "Chart window indicator" object type OBJECT_DE_TYPE_EVENT, // "Event" object type OBJECT_DE_TYPE_EVENT_BALANCE, // "Balance operation event" object type OBJECT_DE_TYPE_EVENT_MODIFY, // "Pending order/position modification event" object type OBJECT_DE_TYPE_EVENT_ORDER_PLASED, // "Placing a pending order event" object type OBJECT_DE_TYPE_EVENT_ORDER_REMOVED, // "Pending order removal event" object type OBJECT_DE_TYPE_EVENT_POSITION_CLOSE, // "Position closure event" object type OBJECT_DE_TYPE_EVENT_POSITION_OPEN, // "Position opening event" object type OBJECT_DE_TYPE_IND_BUFFER, // "Indicator buffer" object type OBJECT_DE_TYPE_IND_BUFFER_ARROW, // "Arrow rendering buffer" object type OBJECT_DE_TYPE_IND_BUFFER_BAR, // "Bar buffer" object type OBJECT_DE_TYPE_IND_BUFFER_CALCULATE, // "Calculated buffer" object type OBJECT_DE_TYPE_IND_BUFFER_CANDLE, // "Candle buffer" object type OBJECT_DE_TYPE_IND_BUFFER_FILLING, // "Filling buffer" object type OBJECT_DE_TYPE_IND_BUFFER_HISTOGRAMM, // "Histogram buffer" object type OBJECT_DE_TYPE_IND_BUFFER_HISTOGRAMM2, // "Histogram 2 buffer" object type OBJECT_DE_TYPE_IND_BUFFER_LINE, // "Line buffer" object type OBJECT_DE_TYPE_IND_BUFFER_SECTION, // "Section buffer" object type OBJECT_DE_TYPE_IND_BUFFER_ZIGZAG, // "Zigzag buffer" object type OBJECT_DE_TYPE_INDICATOR, // "Indicator" object type OBJECT_DE_TYPE_IND_DATA, // "Indicator data" object type OBJECT_DE_TYPE_IND_DATA_LIST, // "Indicator data list" object type OBJECT_DE_TYPE_IND_AC, // "Accelerator Oscillator indicator" object type OBJECT_DE_TYPE_IND_AD, // "Accumulation/Distribution indicator" object type OBJECT_DE_TYPE_IND_ADX, // "Average Directional Index indicator" object type OBJECT_DE_TYPE_IND_ADXW, // "ADX indicator by Welles Wilder" object type OBJECT_DE_TYPE_IND_ALLIGATOR, // "Alligator indicator" object type OBJECT_DE_TYPE_IND_AMA, // "Adaptive Moving Average indicator" object type OBJECT_DE_TYPE_IND_AO, // "Awesome Oscillator indicator" object type OBJECT_DE_TYPE_IND_ATR, // "Average True Range" object type OBJECT_DE_TYPE_IND_BANDS, // "Bollinger Bands® indicator" object type OBJECT_DE_TYPE_IND_BEARS, // "Bears Power indicator" object type OBJECT_DE_TYPE_IND_BULLS, // "Bulls Power indicator" object type OBJECT_DE_TYPE_IND_BWMFI, // "Market Facilitation Index indicator" object type OBJECT_DE_TYPE_IND_CCI, // "Commodity Channel Index indicator" object type OBJECT_DE_TYPE_IND_CHAIKIN, // "Chaikin Oscillator indicator" object type OBJECT_DE_TYPE_IND_CUSTOM, // "Custom indicator" object type OBJECT_DE_TYPE_IND_DEMA, // "Double Exponential Moving Average indicator" object type OBJECT_DE_TYPE_IND_DEMARKER, // "DeMarker indicator" object type OBJECT_DE_TYPE_IND_ENVELOPES, // "Envelopes indicator" object type OBJECT_DE_TYPE_IND_FORCE, // "Force Index indicator" object type OBJECT_DE_TYPE_IND_FRACTALS, // "Fractals indicator" object type OBJECT_DE_TYPE_IND_FRAMA, // "Fractal Adaptive Moving Average indicator" object type OBJECT_DE_TYPE_IND_GATOR, // "Gator Oscillator indicator" object type OBJECT_DE_TYPE_IND_ICHIMOKU, // "Ichimoku Kinko Hyo indicator" object type OBJECT_DE_TYPE_IND_MA, // "Moving Average indicator" object type OBJECT_DE_TYPE_IND_MACD, // "Moving Average Convergence/Divergence indicator" object type OBJECT_DE_TYPE_IND_MFI, // "Money Flow Index indicator" object type OBJECT_DE_TYPE_IND_MOMENTUM, // "Momentum indicator" object type OBJECT_DE_TYPE_IND_OBV, // "On Balance Volume indicator" object type OBJECT_DE_TYPE_IND_OSMA, // "Moving Average of Oscillator indicator" object type OBJECT_DE_TYPE_IND_RSI, // "Relative Strength Index indicator" object type OBJECT_DE_TYPE_IND_RVI, // "Relative Vigor Index indicator" object type OBJECT_DE_TYPE_IND_SAR, // "Parabolic SAR indicator" object type OBJECT_DE_TYPE_IND_STDEV, // "Standard Deviation indicator" object type OBJECT_DE_TYPE_IND_STOCH, // "Stochastic Oscillator indicator" object type OBJECT_DE_TYPE_IND_TEMA, // "Triple Exponential Moving Average indicator" object OBJECT_DE_TYPE_IND_TRIX, // "Triple Exponential Moving Averages Oscillator indicator" object type OBJECT_DE_TYPE_IND_VIDYA, // "Variable Index Dynamic Average indicator" object type OBJECT_DE_TYPE_IND_VOLUMES, // "Volumes indicator" object type OBJECT_DE_TYPE_IND_WPR, // "Williams' Percent Range indicator" object type OBJECT_DE_TYPE_MQL5_SIGNAL, // "mql5 signal" object type OBJECT_DE_TYPE_ORDER_DEAL_POSITION, // "Order/Deal/Position" object type OBJECT_DE_TYPE_HISTORY_BALANCE, // "Historical balance operation" object type OBJECT_DE_TYPE_HISTORY_DEAL, // "Historical deal" object type OBJECT_DE_TYPE_HISTORY_ORDER_MARKET, // "Historical market order" object type OBJECT_DE_TYPE_HISTORY_ORDER_PENDING, // "Historical removed pending order" object type OBJECT_DE_TYPE_MARKET_ORDER, // "Market order" object type OBJECT_DE_TYPE_MARKET_PENDING, // "Pending order" object type OBJECT_DE_TYPE_MARKET_POSITION, // "Market position" object type OBJECT_DE_TYPE_PENDING_REQUEST, // "Pending trading request" object type OBJECT_DE_TYPE_PENDING_REQUEST_POSITION_OPEN, // "Pending request to open a position" object type OBJECT_DE_TYPE_PENDING_REQUEST_POSITION_CLOSE, // "Pending request to close a position" object type OBJECT_DE_TYPE_PENDING_REQUEST_POSITION_SLTP, // "Pending request to modify position stop orders" object type OBJECT_DE_TYPE_PENDING_REQUEST_ORDER_PLACE, // "Pending request to place a pending order" object type OBJECT_DE_TYPE_PENDING_REQUEST_ORDER_REMOVE, // "Pending request to delete a pending order" object type OBJECT_DE_TYPE_PENDING_REQUEST_ORDER_MODIFY, // "Pending request to modify pending order parameters" object type OBJECT_DE_TYPE_SERIES_BAR, // "Bar" object type OBJECT_DE_TYPE_SERIES_PERIOD, // "Period timeseries" object type OBJECT_DE_TYPE_SERIES_SYMBOL, // "Symbol timeseries" object type OBJECT_DE_TYPE_SYMBOL, // "Symbol" object type OBJECT_DE_TYPE_SYMBOL_BONDS, // "Bond symbol" object type OBJECT_DE_TYPE_SYMBOL_CFD, // "CFD (contract for difference) symbol" object type OBJECT_DE_TYPE_SYMBOL_COLLATERAL, // "Non-tradable asset symbol" object type" object type OBJECT_DE_TYPE_SYMBOL_COMMODITY, // "Commodity symbol" object type OBJECT_DE_TYPE_SYMBOL_COMMON, // "Common group symbol" object type OBJECT_DE_TYPE_SYMBOL_CRYPTO, // "Cryptocurrency symbol" object type OBJECT_DE_TYPE_SYMBOL_CUSTOM, // "Custom symbol" object type OBJECT_DE_TYPE_SYMBOL_EXCHANGE, // "Exchange symbol" object type OBJECT_DE_TYPE_SYMBOL_FUTURES, // "Futures symbol" object type OBJECT_DE_TYPE_SYMBOL_FX, // "Forex symbol" object type OBJECT_DE_TYPE_SYMBOL_FX_EXOTIC, // "Exotic Forex symbol" object type OBJECT_DE_TYPE_SYMBOL_FX_MAJOR, // "Major Forex symbol" object type OBJECT_DE_TYPE_SYMBOL_FX_MINOR, // "Minor Forex symbol" object type OBJECT_DE_TYPE_SYMBOL_FX_RUB, // "RUB Forex symbol" object type OBJECT_DE_TYPE_SYMBOL_INDEX, // "Index symbol" object type OBJECT_DE_TYPE_SYMBOL_INDICATIVE, // "Indicative symbol" object type OBJECT_DE_TYPE_SYMBOL_METALL, // "Metal symbol" object type OBJECT_DE_TYPE_SYMBOL_OPTION, // "Option symbol" object type OBJECT_DE_TYPE_SYMBOL_STOCKS, // "Stock symbol" object type OBJECT_DE_TYPE_TICK, // "Tick" object type OBJECT_DE_TYPE_NEW_TICK, // "New tick" object type OBJECT_DE_TYPE_TICKSERIES, // "Tick data series" object type OBJECT_DE_TYPE_TRADE, // "Trading object" object type OBJECT_DE_TYPE_LONG, // "Long type data" object type OBJECT_DE_TYPE_DOUBLE, // "Double type data" object type OBJECT_DE_TYPE_STRING, // "String type data" object type OBJECT_DE_TYPE_OBJECT, // "Object type data" object type }; //+------------------------------------------------------------------+ //| Search and sorting data | //+------------------------------------------------------------------+
在 \MQL5\Include\DoEasy\Data.mqh 里,加入新的消息索引:
MSG_LIB_SYS_FAILED_ADD_BUFFER, // Failed to add buffer object to the list MSG_LIB_SYS_FAILED_CREATE_BUFFER_OBJ, // Failed to create \"Indicator buffer\" object MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST, // Failed to add object to the list MSG_LIB_SYS_FAILED_CREATE_LONG_DATA_OBJ, // Failed to create long data object MSG_LIB_SYS_FAILED_CREATE_DOUBLE_DATA_OBJ, // Failed to create double data object MSG_LIB_SYS_FAILED_CREATE_STRING_DATA_OBJ, // Failed to create string data object MSG_LIB_SYS_FAILED_DECREASE_LONG_ARRAY, // Failed to decrease long array size MSG_LIB_SYS_FAILED_DECREASE_DOUBLE_ARRAY, // Failed to decrease double array size MSG_LIB_SYS_FAILED_DECREASE_STRING_ARRAY, // Failed to decrease string array size MSG_LIB_SYS_FAILED_GET_LONG_DATA_OBJ, // Failed to get long data object MSG_LIB_SYS_FAILED_GET_DOUBLE_DATA_OBJ, // Failed to get double data object MSG_LIB_SYS_FAILED_GET_STRING_DATA_OBJ, // Failed to get string data object MSG_LIB_SYS_REQUEST_OUTSIDE_LONG_ARRAY, // Request outside long array MSG_LIB_SYS_REQUEST_OUTSIDE_DOUBLE_ARRAY, // Request outside double array MSG_LIB_SYS_REQUEST_OUTSIDE_STRING_ARRAY, // Request outside string array MSG_LIB_TEXT_YES, // Yes MSG_LIB_TEXT_NO, // No MSG_LIB_TEXT_AND, // and
...
MSG_GRAPH_OBJ_TEXT_ANCHOR_RIGHT_UPPER, // Anchor point at the upper right corner MSG_GRAPH_OBJ_TEXT_ANCHOR_UPPER, // Anchor point at the upper center MSG_GRAPH_OBJ_TEXT_ANCHOR_CENTER, // Anchor point at the very center of the object MSG_GRAPH_OBJ_TEXT_TIME_PRICE, // Price/time coordinates MSG_GRAPH_OBJ_TEXT_PIVOT, // Pivot point MSG_GRAPH_OBJ_TEXT_LEVELSVALUE_ALL, // Level values MSG_GRAPH_OBJ_TEXT_LEVEL, // Level MSG_GRAPH_OBJ_TEXT_BMP_FILE_STATE_ON, // On state MSG_GRAPH_OBJ_TEXT_BMP_FILE_STATE_OFF, // Off state //--- CGraphElementsCollection MSG_GRAPH_OBJ_FAILED_GET_ADDED_OBJ_LIST, // Failed to get the list of newly added objects MSG_GRAPH_OBJ_FAILED_DETACH_OBJ_FROM_LIST, // Failed to remove a graphical object from the list MSG_GRAPH_OBJ_CREATE_EVN_CTRL_INDICATOR, // Indicator for controlling and sending events created MSG_GRAPH_OBJ_FAILED_CREATE_EVN_CTRL_INDICATOR, // Failed to create the indicator for controlling and sending events MSG_GRAPH_OBJ_CLOSED_CHARTS, // Chart windows closed: MSG_GRAPH_OBJ_OBJECTS_ON_CLOSED_CHARTS, // Objects removed together with charts: }; //+------------------------------------------------------------------+
及与新添加的索引相对应的消息文本:
{"Не удалось добавить объект-буфер в список","Failed to add buffer object to list"}, {"Не удалось создать объект \"Индикаторный буфер\"","Failed to create object \"Indicator buffer\""}, {"Не удалось добавить объект в список","Failed to add object to the list"}, {"Не удалось создать объект long-данных","Failed to create long-data object"}, {"Не удалось создать объект double-данных","Failed to create double-data object"}, {"Не удалось создать объект string-данных","Failed to create string-data object"}, {"Не удалось уменьшить размер long-массива","Failed to reduce the size of the long-array"}, {"Не удалось уменьшить размер double-массива","Failed to reduce the size of the double-array"}, {"Не удалось уменьшить размер string-массива","Failed to reduce the size of the string-array"}, {"Не удалось получить объект long-данных","Failed to get long-data object"}, {"Не удалось получить объект double-данных","Failed to get double-data object"}, {"Не удалось получить объект string-данных","Failed to get string-data object"}, {"Запрос за пределами long-массива","Data requested outside the long-array"}, {"Запрос за пределами double-массива","Data requested outside the double-array"}, {"Запрос за пределами string-массива","Data requested outside the string-array"}, {"Да","Yes"}, {"Нет","No"}, {"и","and"},
...
{"Точка привязки в правом верхнем углу","Anchor point at the upper right corner"}, {"Точка привязки сверху по центру","Anchor point above in the center"}, {"Точка привязки строго по центру объекта","Anchor point strictly in the center of the object"}, {"Координаты цена/время","Price/time coordinates"}, {"Опорная точка ","Pivot point "}, {"Значения уровней","Level values"}, {"Уровень ","Level "}, {"Состояние \"On\"","State \"On\""}, {"Состояние \"Off\"","State \"Off\""}, //--- CGraphElementsCollection {"Не удалось получить список вновь добавленных объектов","Failed to get the list of newly added objects"}, {"Не удалось изъять графический объект из списка","Failed to detach graphic object from the list"}, {"Создан индикатор контроля и отправки событий","An indicator for monitoring and sending events has been created"}, {"Не удалось создать индикатор контроля и отправки событий","Failed to create indicator for monitoring and sending events"}, {"Закрыто окон графиков: ","Closed chart windows: "}, {"С ними удалено объектов: ","Objects removed with them: "}, }; //+---------------------------------------------------------------------+
在 \MQL5\Include\DoEasy\Services\ 服务函数文件夹里创建一个含 CXDimArray 类的新文件 XDimArray.mqh。
由于我们打算引入一个存储整数型、实数型和字符串型属性的普通数组的替代品,同时令这些数组在任何维度上都支持多维和动态,因此我们将创建三个完全相同的多维动态数组类。 每个都会存储自己所属的类型 (long — 针对整数数据,double — 针对实数型数据,和 string — 针对文本数据)。
该类的层次结构如下所示:
- CObject --> 抽象数据类型类 --> 数据类型类,
- CArrayObj class --> 存储数据类型类列表的一维类,
- CArrayObj class --> 存储对象列表一维类的多维数组类
每个数据类型都有一个单独的类来存储整数型、实数型和字符串型数据。 在任何情况下,该类都是从基础数据类型类派生而来的;在该类中,我们在其内设置了在派生类中存储的数据类型。
单个数组维度的类派生自 CArrayObj 类。 实际上,它是一个存储指向数据对象或其它列表的指针的列表。
多维数组类是 CArrayObj 列表,存储指向单个数组维度的类对象实例的指针。 换言之,列表是第一个数组维度,而单个维度的类是第一个维度的动态可扩展对象。 如果它只包含一个大小为 1 个维度的对象,则访问它时,对应于数组的 [0][0] 项。 如果它有两个大小为 1 个维度的相同对象,则访问第一个对象时,对应于数组的 [0][0] 项,而访问第二个对象时,对应于数组的 [0][1] 项。
当然,如果一维对象的大小超过 1,则访问它们时,对应于多个项
array[0][0], array[0][1], array[0][2], ..., array[0][N], array[1][0], array[1][1], array[1][2], ..., array[1][N].
当然,执行访问是调用相应的方法。 我们能够动态地改变数组的第一维度和第二维度。
因为我们只需要一个二维动态数组,所以在此我不会考虑添加第三或更多的维度。 基于这些类,可以开发任意维度数量的动态数组。 可以更改(增加/减少)维度,或在任何维度的任何数组单元格中加入新维度。
因为我将制作三个相同的类,每个类都要存储它自己的数据类型,故我们来详研只有一种类型的类。
在已创建的 CXDimArray 类的 XDimArray.mqh 文件里,包含 CArrayObj 类和函数库消息类的文件, 并编写自 CObject 基准对象派生的抽象数据单元类:
//+------------------------------------------------------------------+ //| XDimArray.mqh | //| Copyright 2021, MetaQuotes Ltd. | //| https://mql5.com/en/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2021, MetaQuotes Ltd." #property link "https://mql5.com/en/users/artmedia70" #property version "1.00" #property strict // Necessary for mql4 //+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include <Arrays\ArrayObj.mqh> #include "Message.mqh" //+------------------------------------------------------------------+ //| Abstract data unit class | //+------------------------------------------------------------------+ class CDataUnit : public CObject { private: int m_type; protected: CDataUnit(int type) { this.m_type=type; } public: virtual int Type(void) const { return this.m_type; } CDataUnit(){ this.m_type=OBJECT_DE_TYPE_OBJECT; } }; //+------------------------------------------------------------------+
在此我们有一个私密变量,存储在衍生对象中保存的数据类型,衍生对象受保护的参数化构造函数存储所传递的数据类型,公开的虚拟方法返回在 m_type 变量中设置的存储数据类型。
在默认构造函数中,OBJECT_DE_TYPE_OBJECT 数据类型默认设置为 m_TYPE 变量
接下来,在该类里添加整数型数据单元:
//+------------------------------------------------------------------+ //| Integer data unit class | //+------------------------------------------------------------------+ class CDataUnitLong : public CDataUnit { public: long Value; CDataUnitLong() : CDataUnit(OBJECT_DE_TYPE_LONG){} }; //+------------------------------------------------------------------+
该类有一个存储整数型数值的公开字段变量,以及类构造函数。 OBJECT_DE_TYPE_LONG 类型,指示对象存储的是整数型数据类型,并在类构造函数的初始化清单中传递给父类的构造函数。
现在,创建单一的 long 型数组维度的对象类:
//+------------------------------------------------------------------+ //| Class of a single long array dimension | //+------------------------------------------------------------------+ class CDimLong : public CArrayObj { }
我将在类主体中编写所有方法。 每个方法在清单中都有详细的注释。
在类的私密部分,添加从数组接收 long 型数据对象的方法:
//+------------------------------------------------------------------+ //| Class of a single long array dimension | //+------------------------------------------------------------------+ class CDimLong : public CArrayObj { private: //--- Get long data object from the array CDataUnitLong *GetData(const string source,const int index) const { //--- Get the pointer to the object by the index passed to the method. If the passed index is less than zero, it is set to zero CDataUnitLong *data=this.At(index<0 ? 0 : index); //--- If failed to receive the object if(data==NULL) { //--- if the passed index exceeds the number of objects in the list, display a message informing of exceeding the list size if(index>this.Total()-1) ::Print(source,CMessage::Text(MSG_LIB_SYS_REQUEST_OUTSIDE_LONG_ARRAY)," (",index,"/",this.Total(),")"); //--- otherwise, display the message informing of failing to get the pointer to the object else CMessage::ToLog(source,MSG_LIB_SYS_FAILED_GET_LONG_DATA_OBJ); } //--- Return the pointer to the object or NULL in case of an error return data; }
此方法实现了数组超界错误的控制。 换言之,如果传递的索引于数组单元格里不存在,则会在日志消息里通知该情况。 如果传递给方法的索引小于零(这种情况不该发生),则将该值调整为零。 如果我们用有效索引访问数组但无法获取对象,需在日志中显示相应的错误消息。 结果则为,如果出现错误,我们要么返回指向对象的指针,要么返回 NULL 。 记录这些错误有助于在使用该类时正确指定所需数据的索引。 我们将调用来自其它类的方法来接收数组数据。 该方法会在访问数组对象时通报错误。
接下来,实现将包含对象的指定数量单元格附加到数组末尾的方法:
//--- Add the specified number of cells with objects to the end of the array bool AddQuantity(const string source,const int total,CObject *object) { //--- Declare the variable for storing the result of adding objects to the list bool res=true; //--- in the list by the number of added objects passed to the method for(int i=0;i<total;i++) { //--- if failed to add the object to the list if(!this.Add(object)) { //--- display the appropriate message, add 'false' to the variable value //--- and move on to the loop next iteration CMessage::ToLog(source,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST); res &=false; continue; } } //--- Return the total result of adding the specified number of objects to the list return res; }
该方法接收欲附加到数组末尾的对象数量,及指向欲附加实例的对象指针。 接下来,在循环中按指定的数量,将指向对象的指针添加到列表中,并返回在列表中添加对象的结果。
该方法存在逻辑错误,若附加到数组中的对象数量超过两个,则无法处理这种情况。 我将在下一篇文章中修复它。 如果您已经发现了,那么我祝贺您。 这意味着我的工作并非枉然。
在类的公开部分,添加初始化数组的方法:
public: //--- Initialize the array void Initialize(const int total,const long value=0) { //--- Clear the array and increase its size by the value specified in the parameters by setting the default value this.Clear(); this.Increase(total,value); }
调用父类的 Clear() 方法清除数组,及调用 increase() 方法依据指定值增加其大小:
//--- Increase the number of data cells by the specified value, return the number of added elements int Increase(const int total,const long value=0) { //--- Save the current array size int size_prev=this.Total(); //--- Create a new long data object CDataUnitLong *data=new CDataUnitLong(); //--- If failed to create an object, inform of that and return zero if(data==NULL) { ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_LONG_DATA_OBJ)); return 0; } //--- Set the specified value to a newly created object data.Value=value; //--- Add the specified number of object instances to the list //--- and return the difference between the obtained and previous array size this.AddQuantity(DFUN,total,data); return this.Total()-size_prev; }
该方法依据指定数量,把 long 型数据添加到数组之中,并返回实际添加的数量。
我们来编写依据指定值降低数据单元大小的方法:
//--- Decrease the number of data cells by the specified value, return the number of removed elements. The very first element always remains int Decrease(const int total) { //--- If not a single cell remains after removing array cells, return zero if(total>this.Total()-1) return 0; //--- Save the current array size int total_prev=this.Total(); //--- Calculate the initial index the array cells should be removed from int from=this.Total()-total; //--- The final index is always an array end int to=this.Total()-1; //--- If failed to remove the specified number of array cells, inform of that in the journal if(!this.DeleteRange(from,to)) CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_DECREASE_LONG_ARRAY); //--- return the difference between the previous array size and the one obtained as a result of deleting cells return total_prev-this.Total(); }
由于数组应该至少有一个元素,因此首先,确保删除所需数量的数据之后,数组中至少会有一个或多个元素。 接下来,调用来自父类的 DeleteRange() 方法从列表末尾删除指定数量的元素。 结果就是,返回已删除元素的数量。
编写设置新数组大小的方法:
//--- Set a new array size bool SetSize(const int size,const long initial_value=0) { //--- If the zero size is passed, return 'false' if(size==0) return false; //--- Calculate the number of cells to be added or removed for receiving the required array size int total=fabs(size-this.Total()); //--- If the passed array size exceeds the current one, //--- return the result of adding the calculated number of arrays to the array if(size>this.Total()) return(this.Increase(total,initial_value)==total); //--- otherwise, if the passed array size is less than the current one, //--- return the result of removing the calculated number of arrays from the array else if(size<this.Total()) return(Decrease(total)==total); //--- If the passed size is equal to the current one, simply return 'true' return true; }
该方法调用上述方法在数组中删除/添加元素,使之与指定的大小匹配。
添加在指定数组单元格里设置数据的方法:
//--- Set the value to the specified array cell bool Set(const int index,const long value) { //--- Get the pointer to the data object by a specified index CDataUnitLong *data=this.GetData(DFUN,index); //--- If failed to get the object, return 'false' if(data==NULL) return false; //--- Return the value, passed to the method, to the obtained object and return 'true' data.Value=value; return true; }
在此,我们依据指定索引获取数据对象,并在该数据对象里设置为传递给该方法的值。 成功接收对象后,返回 true。 当出现失败(GetData() 私密方法会在日志中通知),则返回 false。
该方法返回数组中的数据数量:
//--- Return the amount of data (the array size is defined using the Total() method of the CArrayObj parent class) int Size(void) const { return this.Total(); }
该方法调用 CArray 类的 Total() 方法返回列表中的元素数量,而 CArray 类是 CArrayObj 类的父类。
我们来编写一个方法,依据指定索引返回值:
//--- Returns the value at the specified index long Get(const int index) const { //--- Get the pointer to the data object by index CDataUnitLong *data=this.GetData(DFUN,index); //--- If the object is received successfully, //--- return the value set in the object, //--- otherwise, return zero return(data!=NULL ? data.Value : 0); }
依据索引从列表中获取指向数据对象的指针,并返回指定对象中所设置的值。 若出现错误,则返回零。
在依据传递给该方法的引用为变量接收数据时,重载的 Get() 方法返回布尔值:
bool Get(const int index, long &value) const { //--- Set the initial value to 'value' passed to the method via a link value=0; //--- Get the pointer to the data object by index CDataUnitLong *data=this.GetData(DFUN,index); //--- If failed to get the object, return 'false' if(data==NULL) return false; //--- Set the value stored in the object to 'value' and return 'true' value = data.Value; return true; }
在默认构造函数中,简单地调用 initialize() 方法来初始化数组,同时将数组大小指定为 1,默认值指定为 0。
在参数型构造函数中,需指定必要的数组大小和默认值。
在类的析构函数中,删除数组元素,并清除数组,从而彻底释放数组内存。
//--- Constructors CDimLong(void) { this.Initialize(1); } CDimLong(const int total,const long value=0) { this.Initialize(total,value); } //--- Destructor ~CDimLong(void) { this.Clear(); this.Shutdown(); } }; //+------------------------------------------------------------------+
接下来,在同一文件中创建动态多维 long 型数组的类:
//+------------------------------------------------------------------+ //| Dynamic multidimensional long array class | //+------------------------------------------------------------------+ class CXDimArrayLong : public CArrayObj { }
该类是存储上述类对象的列表,而这些对象又是存储包含数据的对象的列表。 因此,类是第一个维度,而其中存储的列表是维度数据的列表。
为了更清楚地阐述:
CXDimArrayLong 类列表的索引 0 指向位于列表中的第一个 CDimLong 类对象;CDimLong 类的索引 0 指向位于 CDimLong 类列表中的第一个 CDATA UnitLong 类对象。
这等同于数组的 [0][0] 项;
CXDimArrayLong 类列表的索引 1 指向位于列表第二位的 CDimLong 类对象;CDimLong 类的索引 0 指向位于 CDimLong 类列表中的第一个 CDATA UnitLong 类对象。
这等同于数组的 [1][0] 项;
------
CXDimArrayLong 类列表的索引 0 指向位于列表中的第一个 CDimLong 类对象;CDimLong 类的索引 1 指向 CDimLong 类列表中的第二个 CDataUnitLong 类对象。
这等同于数组的 [0][1] 项;
CXDimArrayLong 类列表的索引 1 指向位于列表第二位的 CDimLong 类对象;CDimLong 类的索引 1 指向 CDimLong 类列表中的第二个 CDataUnitLong 类对象。
这等同于数组的 [1][1] 项;
诸如此类。
在类的私密部分,添加从第一个维度返回数据数组的方法:
//+------------------------------------------------------------------+ //| Dynamic multidimensional long array class | //+------------------------------------------------------------------+ class CXDimArrayLong : public CArrayObj { private: //--- Return the data array from the first dimensionality CDimLong *GetDim(const string source,const int index) const { //--- Get the first dimension array object by index CDimLong *dim=this.At(index<0 ? 0 : index); //--- If failed to get the pointer to the object, if(dim==NULL) { //--- if the index is outside the array, inform of the request outside the array if(index>this.Total()-1) ::Print(source,CMessage::Text(MSG_LIB_SYS_REQUEST_OUTSIDE_LONG_ARRAY)," (",index,"/",this.Total(),")"); //--- otherwise, inform of the error when receiving the pointer to the array else CMessage::ToLog(source,MSG_LIB_SYS_FAILED_GET_LONG_DATA_OBJ); } //--- Return either the pointer to the object or NULL in case of an error return dim; }
该方法依据指定索引接收指向 CDimLong 类对象的指针。 如果索引小于零,则采用零作为索引。 如果索引指向数组外界,则在日志中通报数组请求超界,或者,如果接收对象失败,则在日志中通报对象接收错误。 结果则为,如果出现错误,我们要么返回指向对象的指针,要么返回 NULL 。
在类的私密部分,编写向第一个维度添加新维度的方法:
//--- Add a new dimension to the first dimensionality bool AddNewDim(const string source,const int size,const long initial_value=0) { //--- Create a new array object CDimLong *dim=new CDimLong(size,initial_value); //--- If failed to create an object, inform of that and return 'false' if(dim==NULL) { CMessage::ToLog(source,MSG_LIB_SYS_FAILED_CREATE_LONG_DATA_OBJ); return false; } //--- If failed to add the object to the list, remove the object, inform of the error in the journal and return 'false' if(!this.Add(dim)) { delete dim; CMessage::ToLog(source,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST); return false; } //--- Object successfully created and added to the list return true; }
该方法创建新的 CDimLong 类对象,并将其追加到列表的末尾,同时第一个维度的大小递增。
在类的公开部分,编写操控数组的方法。 所有方法都在代码注释中有所详述。 我认为,在这里重复毫无意义。 我们来如下研究它们:
public: //--- Increase the number of data cells by the specified 'total' value in the first dimensionality, //--- return the number of added elements to the dimensionality. Added cells' size is 'size' int IncreaseRangeFirst(const int total,const int size,const long initial_value=0) { //--- Save the current array size int total_prev=this.Total(); //--- In the loop by the specified number, add new objects to the array for(int i=0;i<total;i++) this.AddNewDim(DFUN,size,initial_value); //--- Return the difference between the obtained and previous array size return(this.Total()-total_prev); } //--- Increase the number of data cells by the specified 'total' value in the specified 'range' dimensionality, //--- return the number of added elements to the changed dimensionality int IncreaseRange(const int range,const int total,const long initial_value=0) { //--- Get the pointer to the array by 'range' index CDimLong *dim=this.GetDim(DFUN,range); //--- Return the result of increasing the array size by 'total' or zero in case of an error return(dim!=NULL ? dim.Increase(total,initial_value) : 0); } //--- Decrease the number of cells with data in the first dimensionality by the specified value, //--- return the number of removed elements. The very first element always remains int DecreaseRangeFirst(const int total) { //--- Make sure at least one element remains in the array after the decrease, //--- if not, return 'false' if(total>this.Total()-1) return 0; //--- Save the current array size int total_prev=this.Total(); //--- Calculate the initial index to remove the array elements from int from=this.Total()-total; //--- The final index is always the last array element int to=this.Total()-1; //--- If failed to remove the specified number of elements, inform of that in the journal if(!this.DeleteRange(from,to)) CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_DECREASE_LONG_ARRAY); //--- Return the number of removed array elements return total_prev-this.Total(); } //--- Decrease the number of data cells by the specified value in the specified dimensionality, //--- return the number of removed elements. The very first element always remains int DecreaseRange(const int range,const int total) { //--- Get the pointer to the array by 'range' index CDimLong *dim=this.GetDim(DFUN,range); //--- Return the result of decreasing the array size by 'total' or zero in case of an error return(dim!=NULL ? dim.Decrease(total) : 0); } //--- Set the new array size in the specified dimensionality bool SetSizeRange(const int range,const int size,const long initial_value=0) { //--- Get the pointer to the array by 'range' index CDimLong *dim=this.GetDim(DFUN,range); //--- Return the result of setting the array size to 'size' or 'false' in case of an error return(dim!=NULL ? dim.SetSize(size,initial_value) : false); } //--- Set the value to the specified array cell of the specified dimension bool Set(const int index,const int range,const long value) { //--- Get the pointer to the array by 'index' CDimLong *dim=this.GetDim(DFUN,index); //--- Return the result of setting the value to the array cell by 'range' index or 'false' in case of an error return(dim!=NULL ? dim.Set(range,value) : false); } //--- Return the value at the specified index of the specified dimension long Get(const int index,const int range) const { //--- Get the pointer to the array by 'index' CDimLong *dim=this.GetDim(DFUN,index); //--- Return the result of receiving the value from the array cell by 'range' index or 0 in case of an error return(dim!=NULL ? dim.Get(range) : 0); } bool Get(const int index,const int range,long &value) const { //--- Get the pointer to the array by 'index' CDimLong *dim=this.GetDim(DFUN,index); //--- Return the result of receiving the value from the array cell by 'range' index to the 'value' variable //--- or 'false' in case of an error ('value' is set to zero) return(dim!=NULL ? dim.Get(range,value) : false); } //--- Return the amount of data (size of the specified dimension array) int Size(const int range) const { //--- Get the pointer to the array by 'range' index CDimLong *dim=this.GetDim(DFUN,range); //--- Return the size of the obtained array by index or zero in case of an error return(dim!=NULL ? dim.Size() : 0); } //--- Return the total amount of data (the total size of all dimensions) int Size(void) const { //--- Set the initial size int size=0; //--- In the loop by all arrays in the list, for(int i=0;i<this.Total();i++) { //--- get the next array. CDimLong *dim=this.GetDim(DFUN,i); //--- If failed to get the array, move on to the next one if(dim==NULL) continue; //--- Add the array size to the size value size+=dim.Size(); } //--- Return the obtained value return size; } //--- Constructor CXDimArrayLong() { //--- Clear the list and add a single array to it this.Clear(); this.Add(new CDimLong(1)); } CXDimArrayLong(int first_dim_size,const int dim_size,const long initial_value=0) { //--- Clear the list this.Clear(); int total=(first_dim_size<1 ? 1 : first_dim_size); //--- In the loop by the necessary number of arrays calculated in 'total' from first_dim_size, //--- add new arrays with the specified number of elements in dim_size to the list for(int i=0;i<total;i++) this.Add(new CDimLong(dim_size,initial_value)); } //--- Destructor ~CXDimArrayLong() { //--- Remove array elements and //--- clear the array while completely freeing the array memory this.Clear(); this.Shutdown(); } }; //+------------------------------------------------------------------+
如您所见,我们主要运用所获 CDimLong 类对象的方法,在上面编写 CDimLong 类时我曾研究过它们。 如果您有任何疑问,请随时在下面的评论中提问。
现在我们需要为 double 和 string 类型的数据编写完全相同的类。 这些类与上边谈论的类完全相同:
//+------------------------------------------------------------------+ //| Real data unit class | //+------------------------------------------------------------------+ class CDataUnitDouble : public CDataUnit { public: double Value; //--- Constructor CDataUnitDouble() : CDataUnit(OBJECT_DE_TYPE_DOUBLE){} }; //+------------------------------------------------------------------+ //| Class of a single double array dimension | //+------------------------------------------------------------------+ class CDimDouble : public CArrayObj { private: //--- Get long data object from the array CDataUnitDouble *GetData(const string source,const int index) const { CDataUnitDouble *data=this.At(index<0 ? 0 : index); if(data==NULL) { if(index>this.Total()-1) ::Print(source,CMessage::Text(MSG_LIB_SYS_REQUEST_OUTSIDE_DOUBLE_ARRAY)," (",index,"/",this.Total(),")"); else CMessage::ToLog(source,MSG_LIB_SYS_FAILED_GET_DOUBLE_DATA_OBJ); } return data; } //--- Add the specified number of cells with objects to the end of the array bool AddQuantity(const string source,const int total,CObject *object) { bool res=true; for(int i=0;i<total;i++) { if(!this.Add(object)) { CMessage::ToLog(source,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST); res &=false; continue; } } return res; } public: //--- Initialize the array void Initialize(const int total,const double value=0) { this.Clear(); this.Increase(total,value); } //--- Increase the number of data cells by the specified value, return the number of added elements int Increase(const int total,const double value=0) { int size_prev=this.Total(); CDataUnitDouble *data=new CDataUnitDouble(); if(data==NULL) { ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_DOUBLE_DATA_OBJ)); return 0; } data.Value=value; this.AddQuantity(DFUN,total,data); return this.Total()-size_prev; } //--- Decrease the number of data cells by the specified value, return the number of removed elements. The very first element always remains int Decrease(const int total) { if(total>this.Total()-1) return 0; int total_prev=this.Total(); int from=this.Total()-total; int to=this.Total()-1; if(!this.DeleteRange(from,to)) CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_DECREASE_DOUBLE_ARRAY); return total_prev-this.Total(); } //--- Set a new array size bool SetSize(const int size,const double initial_value=0) { if(size==0) return false; int total=fabs(size-this.Total()); if(size>this.Total()) return(this.Increase(total,initial_value)==total); else if(size<this.Total()) return(Decrease(total)==total); return true; } //--- Set the value to the specified array cell bool Set(const int index,const double value) { CDataUnitDouble *data=this.GetData(DFUN,index); if(data==NULL) return false; data.Value=value; return true; } //--- Return the amount of data (array size) int Size(void) const { return this.Total(); } //--- Returns the value at the specified index double Get(const int index) const { CDataUnitDouble *data=this.GetData(DFUN,index); return(data!=NULL ? data.Value : 0); } bool Get(const int index, double &value) const { value=0; CDataUnitDouble *data=this.GetData(DFUN,index); if(data==NULL) return false; value = data.Value; return true; } //--- Constructors CDimDouble(void) { this.Initialize(1); } CDimDouble(const int total,const double value=0){ this.Initialize(total,value); } //--- Destructor ~CDimDouble(void) { this.Clear(); this.Shutdown(); } }; //+------------------------------------------------------------------+ //| Dynamic multidimensional double array class | //+------------------------------------------------------------------+ class CXDimArrayDouble : public CArrayObj { private: //--- Return the data array from the first dimensionality CDimDouble *GetDim(const string source,const int index) const { CDimDouble *dim=this.At(index<0 ? 0 : index); if(dim==NULL) { if(index>this.Total()-1) ::Print(source,CMessage::Text(MSG_LIB_SYS_REQUEST_OUTSIDE_DOUBLE_ARRAY)," (",index,"/",this.Total(),")"); else CMessage::ToLog(source,MSG_LIB_SYS_FAILED_GET_DOUBLE_DATA_OBJ); } return dim; } //--- Add a new dimension to the first dimensionality bool AddNewDim(const string source,const int size,const double initial_value=0) { CDimDouble *dim=new CDimDouble(size,initial_value); if(dim==NULL) { CMessage::ToLog(source,MSG_LIB_SYS_FAILED_CREATE_DOUBLE_DATA_OBJ); return false; } if(!this.Add(dim)) { delete dim; CMessage::ToLog(source,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST); return false; } return true; } public: //--- Increase the number of data cells by the specified 'total' value in the first dimensionality, //--- return the number of added elements to the dimensionality. Added cells' size is 'size' int IncreaseRangeFirst(const int total,const int size,const long initial_value=0) { int total_prev=this.Total(); for(int i=0;i<total;i++) this.AddNewDim(DFUN,size,initial_value); return(this.Total()-total_prev); } //--- Increase the number of data cells by the specified 'total' value in the specified 'range' dimensionality, //--- return the number of added elements to the changed dimensionality int IncreaseRange(const int range,const int total,const double initial_value=0) { CDimDouble *dim=this.GetDim(DFUN,range); return(dim!=NULL ? dim.Increase(total,initial_value) : 0); } //--- Decrease the number of cells with data in the first dimensionality by the specified value, //--- return the number of removed elements. The very first element always remains int DecreaseRangeFirst(const int total) { if(total>this.Total()-1) return 0; int total_prev=this.Total(); int from=this.Total()-total; int to=this.Total()-1; if(!this.DeleteRange(from,to)) CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_DECREASE_DOUBLE_ARRAY); return total_prev-this.Total(); } //--- Decrease the number of data cells by the specified value in the specified dimensionality, //--- return the number of removed elements. The very first element always remains int DecreaseRange(const int range,const int total) { CDimDouble *dim=this.GetDim(DFUN,range); return(dim!=NULL ? dim.Decrease(total) : 0); } //--- Set the new array size in the specified dimensionality bool SetSizeRange(const int range,const int size,const double initial_value=0) { CDimDouble *dim=this.GetDim(DFUN,range); return(dim!=NULL ? dim.SetSize(size,initial_value) : false); } //--- Set the value to the specified array cell of the specified dimension bool Set(const int index,const int range,const double value) { CDimDouble *dim=this.GetDim(DFUN,index); return(dim!=NULL ? dim.Set(range,value) : false); } //--- Return the value at the specified index of the specified dimension double Get(const int index,const int range) const { CDimDouble *dim=this.GetDim(DFUN,index); return(dim!=NULL ? dim.Get(range) : 0); } bool Get(const int index,const int range,double &value) const { CDimDouble *dim=this.GetDim(DFUN,index); return(dim!=NULL ? dim.Get(range,value) : false); } //--- Return the amount of data (size of the specified dimension array) int Size(const int range) const { CDimDouble *dim=this.GetDim(DFUN,range); return(dim!=NULL ? dim.Size() : 0); } //--- Return the total amount of data (the total size of all dimensions) int Size(void) const { int size=0; for(int i=0;i<this.Total();i++) { CDimDouble *dim=this.GetDim(DFUN,i); if(dim==NULL) continue; size+=dim.Size(); } return size; } //--- Constructor CXDimArrayDouble() { this.Clear(); this.Add(new CDimDouble(1)); } CXDimArrayDouble(int first_dim_size,const int dim_size,const double initial_value=0) { this.Clear(); int total=(first_dim_size<1 ? 1 : first_dim_size); for(int i=0;i<total;i++) this.Add(new CDimDouble(dim_size,initial_value)); } //--- Destructor ~CXDimArrayDouble() { this.Clear(); this.Shutdown(); } }; //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| String data unit class | //+------------------------------------------------------------------+ class CDataUnitString : public CDataUnit { public: string Value; CDataUnitString() : CDataUnit(OBJECT_DE_TYPE_STRING){} }; //+------------------------------------------------------------------+ //| Class of a single string array dimension | //+------------------------------------------------------------------+ class CDimString : public CArrayObj { private: //--- Get long data object from the array CDataUnitString *GetData(const string source,const int index) { CDataUnitString *data=this.At(index<0 ? 0 : index); if(data==NULL) { if(index>this.Total()-1) ::Print(source,CMessage::Text(MSG_LIB_SYS_REQUEST_OUTSIDE_STRING_ARRAY)," (",index,"/",this.Total(),")"); else CMessage::ToLog(source,MSG_LIB_SYS_FAILED_GET_STRING_DATA_OBJ); } return data; } //--- Add the specified number of cells with objects to the end of the array bool AddQuantity(const string source,const int total,CObject *object) { bool res=true; for(int i=0;i<total;i++) { if(!this.Add(object)) { CMessage::ToLog(source,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST); res &=false; continue; } } return res; } public: //--- Initialize the array void Initialize(const int total,const string value="") { this.Clear(); this.Increase(total,value); } //--- Increase the number of data cells by the specified value, return the number of added elements int Increase(const int total,const string value="") { int size_prev=this.Total(); CDataUnitString *data=new CDataUnitString(); if(data==NULL) { ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_STRING_DATA_OBJ)); return 0; } data.Value=value; this.AddQuantity(DFUN,total,data); return this.Total()-size_prev; } //--- Decrease the number of data cells by the specified value, return the number of removed elements. The very first element always remains int Decrease(const int total) { if(total>this.Total()-1) return 0; int total_prev=this.Total(); int from=this.Total()-total; int to=this.Total()-1; if(!this.DeleteRange(from,to)) CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_DECREASE_STRING_ARRAY); return total_prev-this.Total(); } //--- Set a new array size bool SetSize(const int size,const string initial_value="") { if(size==0) return false; int total=fabs(size-this.Total()); if(size>this.Total()) return(this.Increase(total,initial_value)==total); else if(size<this.Total()) return(Decrease(total)==total); return true; } //--- Set the value to the specified array cell bool Set(const int index,const string value) { CDataUnitString *data=this.GetData(DFUN,index); if(data==NULL) return false; data.Value=value; return true; } //--- Return the amount of data (array size) int Size(void) const { return this.Total(); } //--- Returns the value at the specified index string Get(const int index) { CDataUnitString *data=this.GetData(DFUN,index); return(data!=NULL ? data.Value : ""); } bool Get(const int index, string &value) { value=""; CDataUnitString *data=this.GetData(DFUN,index); if(data==NULL) return false; value = data.Value; return true; } //--- Constructors CDimString(void) { this.Initialize(1); } CDimString(const int total,const string value="") { this.Initialize(total,value); } //--- Destructor ~CDimString(void) { this.Clear(); this.Shutdown(); } }; //+------------------------------------------------------------------+ //| Dynamic multidimensional string array class | //+------------------------------------------------------------------+ class CXDimArrayString : public CArrayObj { private: //--- Return the data array from the first dimensionality CDimString *GetDim(const string source,const int index) const { CDimString *dim=this.At(index<0 ? 0 : index); if(dim==NULL) { if(index>this.Total()-1) ::Print(source,CMessage::Text(MSG_LIB_SYS_REQUEST_OUTSIDE_STRING_ARRAY)," (",index,"/",this.Total(),")"); else CMessage::ToLog(source,MSG_LIB_SYS_FAILED_GET_STRING_DATA_OBJ); } return dim; } //--- Add a new dimension to the first dimensionality bool AddNewDim(const string source,const int size,const string initial_value="") { CDimString *dim=new CDimString(size,initial_value); if(dim==NULL) { CMessage::ToLog(source,MSG_LIB_SYS_FAILED_CREATE_STRING_DATA_OBJ); return false; } if(!this.Add(dim)) { delete dim; CMessage::ToLog(source,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST); return false; } return true; } public: //--- Increase the number of data cells by the specified 'total' value in the first dimensionality, //--- return the number of added elements to the dimensionality. Added cells' size is 'size' int IncreaseRangeFirst(const int total,const int size,const string initial_value="") { int total_prev=this.Total(); for(int i=0;i<total;i++) this.AddNewDim(DFUN,size,initial_value); return(this.Total()-total_prev); } //--- Increase the number of data cells by the specified 'total' value in the specified 'range' dimensionality, //--- return the number of added elements to the changed dimensionality int IncreaseRange(const int range,const int total,const string initial_value="") { CDimString *dim=this.GetDim(DFUN,range); return(dim!=NULL ? dim.Increase(total,initial_value) : 0); } //--- Decrease the number of cells with data in the first dimensionality by the specified value, //--- return the number of removed elements. The very first element always remains int DecreaseRangeFirst(const int total) { if(total>this.Total()-1) return 0; int total_prev=this.Total(); int from=this.Total()-total; int to=this.Total()-1; if(!this.DeleteRange(from,to)) CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_DECREASE_STRING_ARRAY); return total_prev-this.Total(); } //--- Decrease the number of data cells by the specified value in the specified dimensionality, //--- return the number of removed elements. The very first element always remains int DecreaseRange(const int range,const int total) { CDimString *dim=this.GetDim(DFUN,range); return(dim!=NULL ? dim.Decrease(total) : 0); } //--- Set the new array size in the specified dimensionality bool SetSizeRange(const int range,const int size,const string initial_value="") { CDimString *dim=this.GetDim(DFUN,range); return(dim!=NULL ? dim.SetSize(size,initial_value) : false); } //--- Set the value to the specified array cell of the specified dimension bool Set(const int index,const int range,const string value) { CDimString *dim=this.GetDim(DFUN,index); return(dim!=NULL ? dim.Set(range,value) : false); } //--- Return the value at the specified index of the specified dimension string Get(const int index,const int range) const { CDimString *dim=this.GetDim(DFUN,index); return(dim!=NULL ? dim.Get(range) : ""); } bool Get(const int index,const int range, string &value) const { CDimString *dim=this.GetDim(DFUN,index); return(dim!=NULL ? dim.Get(range,value) : false); } //--- Return the amount of data (size of the specified dimension array) int Size(const int range) const { CDimString *dim=this.GetDim(DFUN,range); return(dim!=NULL ? dim.Size() : 0); } //--- Return the total amount of data (the total size of all dimensions) int Size(void) const { int size=0; for(int i=0;i<this.Total();i++) { CDimString *dim=this.GetDim(DFUN,i); if(dim==NULL) continue; size+=dim.Size(); } return size; } //--- Constructor CXDimArrayString() { this.Clear(); this.Add(new CDimString(1)); } CXDimArrayString(int first_dim_size,const int dim_size,const string initial_value="") { this.Clear(); int total=(first_dim_size<1 ? 1 : first_dim_size); for(int i=0;i<total;i++) this.Add(new CDimString(dim_size,initial_value)); } //--- Destructor ~CXDimArrayString() { this.Clear(); this.Shutdown(); } }; //+------------------------------------------------------------------+
现在,我们已经准备好将抽象图形对象类提升到一个新的层次,把存储整数型、实数型和字符串型属性的简单数组替换为上面所研究类提供的动态数组。
在动手修改图形对象类之前,我们需要令新创建的类在函数库中像其它类一样可见。 为此,将类文件包含到函数库服务函数文件 \MQL5\include\DoEasy\Services\DELib.mqh 当中:
//+------------------------------------------------------------------+ //| DELib.mqh | //| Copyright 2020, MetaQuotes Software Corp. | //| https://mql5.com/en/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2020, MetaQuotes Software Corp." #property link "https://mql5.com/en/users/artmedia70" #property strict // Necessary for mql4 //+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include "..\Defines.mqh" #include "Message.mqh" #include "TimerCounter.mqh" #include "Pause.mqh" #include "Colors.mqh" #include "XDimArray.mqh" //+------------------------------------------------------------------+ //| Service functions | //+------------------------------------------------------------------+
现在这些类于所有函数库类当中可见。
存储对象属性的二维动态数组
几乎所有函数库对象都具有存储整数型、实数型和字符串型对象属性的数组。 数组都是一维的,且严格设置大小,其与必要属性的数量相对应。 在我们面临需要管理动态变化的对象属性数量之前,这种结构非常方便。 我将从抽象标准图形对象类开始调整函数库对象。 所有后续对象的创建都将基于动态多维数组类的新功能。 最有可能的是,我随后会令先前创建的对象能够处理动态数组。
抽象标准图形对象文件的私密部分 \MQL5\Include\DoEasy\Objects\Graph\standard\gstdgraphhobj.mqh,含有存储属性的数组(当前和以前的),及接收数组中指定属性的实际索引的方法:
//+------------------------------------------------------------------+ //| GStdGraphObj.mqh | //| Copyright 2021, MetaQuotes Ltd. | //| https://mql5.com/en/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2021, MetaQuotes Ltd." #property link "https://mql5.com/en/users/artmedia70" #property version "1.00" #property strict // Necessary for mql4 //+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include "..\GBaseObj.mqh" //+------------------------------------------------------------------+ //| The class of the abstract standard graphical object | //+------------------------------------------------------------------+ class CGStdGraphObj : public CGBaseObj { private: long m_long_prop[GRAPH_OBJ_PROP_INTEGER_TOTAL]; // Integer properties double m_double_prop[GRAPH_OBJ_PROP_DOUBLE_TOTAL]; // Real properties string m_string_prop[GRAPH_OBJ_PROP_STRING_TOTAL]; // String properties long m_long_prop_prev[GRAPH_OBJ_PROP_INTEGER_TOTAL]; // Integer properties before change double m_double_prop_prev[GRAPH_OBJ_PROP_DOUBLE_TOTAL]; // Real properties before change string m_string_prop_prev[GRAPH_OBJ_PROP_STRING_TOTAL]; // String properties before change //--- Return the index of the array the (1) double and (2) string properties are actually located at int IndexProp(ENUM_GRAPH_OBJ_PROP_DOUBLE property) const { return(int)property-GRAPH_OBJ_PROP_INTEGER_TOTAL; } int IndexProp(ENUM_GRAPH_OBJ_PROP_STRING property) const { return(int)property-GRAPH_OBJ_PROP_INTEGER_TOTAL-GRAPH_OBJ_PROP_DOUBLE_TOTAL; } public:
现在,所有这些数组和方法都应该转移到一个新类之中,该类会是一个动态二维数组,可存储对象的属性,该对象的第二个维度应能动态变化,从而能跟踪单个对象属性的变化,例如对象枢轴点价格和时间或颜色、样式、宽度、和对象级别的说明,等等数量可以动态更改的属性。
直至该类完全调试完毕,我将直接将其放在抽象标准图形对象类的私密部分。 在整个功能调试完毕并准备就绪之后,我将把动态二维对象属性数组的类转移到一个单独的文件当中,以便在其它对象中访问它,而非在对象内部重新编写它。
从私密部分删除上面指定的所有数组和方法,并在其位置定义对象属性的新类:
//+------------------------------------------------------------------+ //| GStdGraphObj.mqh | //| Copyright 2021, MetaQuotes Ltd. | //| https://mql5.com/en/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2021, MetaQuotes Ltd." #property link "https://mql5.com/en/users/artmedia70" #property version "1.00" #property strict // Necessary for mql4 //+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include "..\GBaseObj.mqh" //+------------------------------------------------------------------+ //| The class of the abstract standard graphical object | //+------------------------------------------------------------------+ class CGStdGraphObj : public CGBaseObj { private: //--- Object property class class CDataPropObj { }
在新类的私密部分,声明存储属性对象的列表,它是由 CXDimArrayLong 动态多维数组类引入的,存储整数型、实数型和字符串型对象属性数量的变量,以及返回数组索引的两个方法,实际存储对象的 double 型和 string 型属性:
//+------------------------------------------------------------------+ //| The class of the abstract standard graphical object | //+------------------------------------------------------------------+ class CGStdGraphObj : public CGBaseObj { private: //--- Object property class class CDataPropObj { private: CArrayObj m_list; // list of property objects int m_total_int; // Number of integer parameters int m_total_dbl; // Number of real parameters int m_total_str; // Number of string parameters //--- Return the index of the array the (1) double and (2) string properties are actually located at int IndexProp(ENUM_GRAPH_OBJ_PROP_DOUBLE property) const { return(int)property-this.m_total_int; } int IndexProp(ENUM_GRAPH_OBJ_PROP_STRING property) const { return(int)property-this.m_total_int-this.m_total_dbl; }
在类的公开部分中,声明返回属性对象列表的方法,指向整数型、实数型和字符串型属性对象的指针,以及设置和接收指定对象属性的方法:
//--- Object property class class CDataPropObj { private: CArrayObj m_list; // list of property objects int m_total_int; // Number of integer parameters int m_total_dbl; // Number of real parameters int m_total_str; // Number of string parameters //--- Return the index of the array the (1) double and (2) string properties are actually located at int IndexProp(ENUM_GRAPH_OBJ_PROP_DOUBLE property) const { return(int)property-this.m_total_int; } int IndexProp(ENUM_GRAPH_OBJ_PROP_STRING property) const { return(int)property-this.m_total_int-this.m_total_dbl; } public: //--- Return the pointer to (1) the list of property objects, as well as to the object of (2) integer, (3) real and (4) string properties CArrayObj *GetList(void) { return &this.m_list; } CXDimArrayLong *Long() const { return this.m_list.At(0); } CXDimArrayDouble *Double() const { return this.m_list.At(1); } CXDimArrayString *String() const { return this.m_list.At(2); } //--- Set object's (1) integer, (2) real and (3) string properties void Set(ENUM_GRAPH_OBJ_PROP_INTEGER property,int index,long value) { this.Long().Set(property,index,value); } void Set(ENUM_GRAPH_OBJ_PROP_DOUBLE property,int index,double value) { this.Double().Set(this.IndexProp(property),index,value); } void Set(ENUM_GRAPH_OBJ_PROP_STRING property,int index,string value) { this.String().Set(this.IndexProp(property),index,value); } //--- Return object’s (1) integer, (2) real and (3) string property from the properties array long Get(ENUM_GRAPH_OBJ_PROP_INTEGER property,int index) const { return this.Long().Get(property,index); } double Get(ENUM_GRAPH_OBJ_PROP_DOUBLE property,int index) const { return this.Double().Get(this.IndexProp(property),index); } string Get(ENUM_GRAPH_OBJ_PROP_STRING property,int index) const { return this.String().Get(this.IndexProp(property),index); }
CArrayObj 列表将存储指向在上面创建的动态数组类对象实例的三个指针 — 整数型、实数型和字符串型指针,这些指针将在类构造函数中创建,并放置到列表当中。 Set 和 Get 方法与以前的方法类似,只是它们现在又多了一个参数。 除了对象属性索引外,我们还要指示数组第二维中的属性索引。 对于大多数属性,索引始终等于零。 但对于若干个相同类型的对象属性,我将指定属性修饰符索引,用于接收价格/时间属性的后续第一个、第二个、第三个、等等。
此外,在类的公开部分,编写返回指定的一维数据数组大小的方法:
//--- Return the size of the specified first dimension data array int Size(const int range) const { if(range<this.m_total_int) return this.Long().Size(range); else if(range<this.m_total_int+this.m_total_dbl) return this.Double().Size(this.IndexProp((ENUM_GRAPH_OBJ_PROP_DOUBLE)range)); else if(range<this.m_total_int+this.m_total_dbl+this.m_total_str) return this.String().Size(this.IndexProp((ENUM_GRAPH_OBJ_PROP_STRING)range)); return 0; }
该方法传递所需属性在数组第一维度(范围)中的索引。 接下来,检查以下内容:
如果属性索引值小于整数型属性的总数,这是请求整数型属性 — 使用 Long 对象的 Size() 方法返回整数型属性数组的大小;
如果属性所以值小于整数型属性总数 + 实数型属性数量,这是请求实数型属性 — 调用 Double 对象的 Size() 方法返回实数型数组的大小;
如果属性索引值小于整数型属性总数 + 实数型属性数量 + 字符串型属性数量,则这是请求字符串属性 — 返回 String 对象的字符串属性数组的大小。
在创建动态数组的对象类时,我们曾研究过上面的 Size()方法。
在类的公开部分,编写在指定维度中设置数组大小的方法:
//--- Set the array size in the specified dimensionality bool SetSizeRange(const int range,const int size) { if(range<this.m_total_int) return this.Long().SetSizeRange(range,size); else if(range<this.m_total_int+this.m_total_dbl) return this.Double().SetSizeRange(this.IndexProp((ENUM_GRAPH_OBJ_PROP_DOUBLE)range),size); else if(range<this.m_total_int+this.m_total_dbl+this.m_total_str) return this.String().SetSizeRange(this.IndexProp((ENUM_GRAPH_OBJ_PROP_STRING)range),size); return false; }
方法逻辑与上述接收尺寸的方法雷同。
该类的构造函数接收整数型、实数型和字符串型对象属性的数量。 这些值会被分配给相应的类变量。 接下来,将整数型、实数型和字符串型动态多维数组的新对象添加到列表之中。 数组的第一个维度与参数中传递的相应属性的数量相等,而第二个维度的大小对于每个属性数组都设置为 1。
//--- Constructor CDataPropObj(const int prop_total_integer,const int prop_total_double,const int prop_total_string) { this.m_total_int=prop_total_integer; this.m_total_dbl=prop_total_double; this.m_total_str=prop_total_string; this.m_list.Add(new CXDimArrayLong(this.m_total_int, 1)); this.m_list.Add(new CXDimArrayDouble(this.m_total_dbl,1)); this.m_list.Add(new CXDimArrayString(this.m_total_str,1)); }
在类析构函数中,应从列表中删除元素,并释放数组内存:
//--- Destructor
~CDataPropObj()
{
m_list.Clear();
m_list.Shutdown();
}
最终结果,对象属性类如下所示:
//--- Object property class class CDataPropObj { private: CArrayObj m_list; // list of property objects int m_total_int; // Number of integer parameters int m_total_dbl; // Number of real parameters int m_total_str; // Number of string parameters //--- Return the index of the array the (1) double and (2) string properties are actually located at int IndexProp(ENUM_GRAPH_OBJ_PROP_DOUBLE property) const { return(int)property-this.m_total_int; } int IndexProp(ENUM_GRAPH_OBJ_PROP_STRING property) const { return(int)property-this.m_total_int-this.m_total_dbl; } public: //--- Return the pointer to (1) the list of property objects, as well as to the object of (2) integer, (3) real and (4) string properties CArrayObj *GetList(void) { return &this.m_list; } CXDimArrayLong *Long() const { return this.m_list.At(0); } CXDimArrayDouble *Double() const { return this.m_list.At(1); } CXDimArrayString *String() const { return this.m_list.At(2); } //--- Set object's (1) integer, (2) real and (3) string properties void Set(ENUM_GRAPH_OBJ_PROP_INTEGER property,int index,long value) { this.Long().Set(property,index,value); } void Set(ENUM_GRAPH_OBJ_PROP_DOUBLE property,int index,double value) { this.Double().Set(this.IndexProp(property),index,value); } void Set(ENUM_GRAPH_OBJ_PROP_STRING property,int index,string value) { this.String().Set(this.IndexProp(property),index,value); } //--- Return object’s (1) integer, (2) real and (3) string property from the properties array long Get(ENUM_GRAPH_OBJ_PROP_INTEGER property,int index) const { return this.Long().Get(property,index); } double Get(ENUM_GRAPH_OBJ_PROP_DOUBLE property,int index) const { return this.Double().Get(this.IndexProp(property),index); } string Get(ENUM_GRAPH_OBJ_PROP_STRING property,int index) const { return this.String().Get(this.IndexProp(property),index); } //--- Return the size of the specified first dimension data array int Size(const int range) const { if(range<this.m_total_int) return this.Long().Size(range); else if(range<this.m_total_int+this.m_total_dbl) return this.Double().Size(this.IndexProp((ENUM_GRAPH_OBJ_PROP_DOUBLE)range)); else if(range<this.m_total_int+this.m_total_dbl+this.m_total_str) return this.String().Size(this.IndexProp((ENUM_GRAPH_OBJ_PROP_STRING)range)); return 0; } //--- Set the array size in the specified dimensionality bool SetSizeRange(const int range,const int size) { if(range<this.m_total_int) return this.Long().SetSizeRange(range,size); else if(range<this.m_total_int+this.m_total_dbl) return this.Double().SetSizeRange(this.IndexProp((ENUM_GRAPH_OBJ_PROP_DOUBLE)range),size); else if(range<this.m_total_int+this.m_total_dbl+this.m_total_str) return this.String().SetSizeRange(this.IndexProp((ENUM_GRAPH_OBJ_PROP_STRING)range),size); return false; } //--- Constructor CDataPropObj(const int prop_total_integer,const int prop_total_double,const int prop_total_string) { this.m_total_int=prop_total_integer; this.m_total_dbl=prop_total_double; this.m_total_str=prop_total_string; this.m_list.Add(new CXDimArrayLong(this.m_total_int, 1)); this.m_list.Add(new CXDimArrayDouble(this.m_total_dbl,1)); this.m_list.Add(new CXDimArrayString(this.m_total_str,1)); } //--- Destructor ~CDataPropObj() { m_list.Clear(); m_list.Shutdown(); } };
然而,为了跟踪对象属性中的变化,我们需要记住所有属性的过去状态,并将其与当前状态进行比较。 为了达此目的,我之前维持两组对象属性。 现在,我会简单地创建另一个类来包含新编类的两个对象。 第一个对象存储当前属性,而第二个对象则存储之前的属性。
另外在类的私密部分,声明当前和之前属性的新数据类:
class CProperty
{
}
所有的类字段和方法都是公开的。 我们来看看类清单。 它相对较小,存储之前曾研究的类对象:
//--- Data class of the current and previous properties class CProperty { public: CDataPropObj *Curr; // Pointer to the current properties object CDataPropObj *Prev; // Pointer to the previous properties object //--- Set the array size ('size') in the specified dimension ('range') bool SetSizeRange(const int range,const int size) { return(this.Curr.SetSizeRange(range,size) && this.Prev.SetSizeRange(range,size) ? true : false); } //--- Return the size of the specified array of the (1) current and (2) previous first dimension data int CurrSize(const int range) const { return Curr.Size(range); } int PrevSize(const int range) const { return Prev.Size(range); } //--- Copy the current data to the previous one void CurrentToPrevious(void) { //--- Copy all integer properties for(int i=0;i<this.Curr.Long().Total();i++) for(int r=0;r<this.Curr.Long().Size(i);r++) this.Prev.Long().Set(i,r,this.Curr.Long().Get(i,r)); //--- Copy all real properties for(int i=0;i<this.Curr.Double().Total();i++) for(int r=0;r<this.Curr.Double().Size(i);r++) this.Prev.Double().Set(i,r,this.Curr.Double().Get(i,r)); //--- Copy all string properties for(int i=0;i<this.Curr.String().Total();i++) for(int r=0;r<this.Curr.String().Size(i);r++) this.Prev.String().Set(i,r,this.Curr.String().Get(i,r)); } //--- Constructor CProperty(const int prop_int_total,const int prop_double_total,const int prop_string_total) { this.Curr=new CDataPropObj(prop_int_total,prop_double_total,prop_string_total); this.Prev=new CDataPropObj(prop_int_total,prop_double_total,prop_string_total); } };
除了处理以前创建的类方法外,我还添加了将当前属性复制到之前属性的方法。 复制是逐元素执行的,因为我们需要同一对象的两个独立副本。 如果我们调用 CArrayObj 类的 AssignArray 方法,我们只是复制了指针,而非属性值。 这导致的是创建对象的精确副本,而修改一个对象中的属性,会将导致另一个对象的属性修改,而我不需要这样做。
故此,简单地将数组替换为我刚才实现的动态数组类对象(尽管尚未完全实现)意味着把 CProperty 类对象作为指向图形对象属性的指针,以及涉及函数库类的多次修改;从现在起,除了属性索引,我们还需要指定价格和时间轴点的索引,或者指定我们想要获取或设置其属性的级别索引。
改进库类
在抽象图形对象类的私密部分(我们当前正在用的),声明指向图形对象属性对象的指针,存储对象轴点数量的变量,并在新编写的属性类之后立即添加为单个对象属性设置多个值的方法:
CProperty *Prop; // Pointer to the properties object int m_pivots; // Number of object reference points //--- Read and set (1) the time and (2) the price of the specified object pivot point void SetTimePivot(const int index); void SetPricePivot(const int index); //--- Read and set (1) color, (2) style, (3) width, (4) value, (5) text of the specified object level void SetLevelColor(const int index); void SetLevelStyle(const int index); void SetLevelWidth(const int index); void SetLevelValue(const int index); void SetLevelText(const int index); //--- Read and set the BMP file name for the "Bitmap Level" object. Index: 0 - ON, 1 - OFF void SetBMPFile(const int index);
在类的公开部分,为所有 Get 和 Set 方法,补充一个表示属性数组第二维度索引的变量。 调用 Get 和 Set 方法从 CProperty 类的 Curr 和 Prev 字段接受并返回属性,这些字段是存储当前和之前属性的数组:
public: //--- Set object's (1) integer, (2) real and (3) string properties void SetProperty(ENUM_GRAPH_OBJ_PROP_INTEGER property,int index,long value) { this.Prop.Curr.Set(property,index,value); } void SetProperty(ENUM_GRAPH_OBJ_PROP_DOUBLE property,int index,double value) { this.Prop.Curr.Set(property,index,value); } void SetProperty(ENUM_GRAPH_OBJ_PROP_STRING property,int index,string value) { this.Prop.Curr.Set(property,index,value); } //--- Return object’s (1) integer, (2) real and (3) string property from the properties array long GetProperty(ENUM_GRAPH_OBJ_PROP_INTEGER property,int index) const { return this.Prop.Curr.Get(property,index); } double GetProperty(ENUM_GRAPH_OBJ_PROP_DOUBLE property,int index) const { return this.Prop.Curr.Get(property,index); } string GetProperty(ENUM_GRAPH_OBJ_PROP_STRING property,int index) const { return this.Prop.Curr.Get(property,index); } //--- Set object's previous (1) integer, (2) real and (3) string properties void SetPropertyPrev(ENUM_GRAPH_OBJ_PROP_INTEGER property,int index,long value) { this.Prop.Prev.Set(property,index,value); } void SetPropertyPrev(ENUM_GRAPH_OBJ_PROP_DOUBLE property,int index,double value){ this.Prop.Prev.Set(property,index,value); } void SetPropertyPrev(ENUM_GRAPH_OBJ_PROP_STRING property,int index,string value){ this.Prop.Prev.Set(property,index,value); } //--- Return object’s (1) integer, (2) real and (3) string property from the previous properties array long GetPropertyPrev(ENUM_GRAPH_OBJ_PROP_INTEGER property,int index) const { return this.Prop.Prev.Get(property,index); } double GetPropertyPrev(ENUM_GRAPH_OBJ_PROP_DOUBLE property,int index) const { return this.Prop.Prev.Get(property,index); } string GetPropertyPrev(ENUM_GRAPH_OBJ_PROP_STRING property,int index) const { return this.Prop.Prev.Get(property,index); }
应该在整个类代码调用 GetProperty()、SetProperty()、GetPropertyPrev() 和 SetPropertyPrev() 方法的地方进行修改,因为现在已在它们中指定了第二个维度的索引。 我不会在此讲述所有的多重修改,因为这会占用大量的文章篇幅。 它们都包含在文后所附的类代码当中。 取而代之,我们来研究一些新的和旧的方法。
在类的公开部分,声明返回轴点和对象级别描述的方法:
//--- Return the description of the (1) (ENUM_OBJECT) graphical object type virtual string TypeDescription(void) const { return CMessage::Text(MSG_GRAPH_STD_OBJ_ANY); } //--- Return the description of the coordinate of the specified graphical object (1) price and (2) time virtual string PriceDescription(const int index) const { return ::DoubleToString(this.GetProperty(GRAPH_OBJ_PROP_PRICE,index),this.m_digits); } virtual string TimeDescription(const int index) const { return ::TimeToString(this.GetProperty(GRAPH_OBJ_PROP_TIME,index),TIME_DATE|TIME_MINUTES); } //--- Return the description of the specified level (1) color, (2) style, (3) width, (4) value virtual string LevelColorDescription(const int index) const { return ::ColorToString((color)this.GetProperty(GRAPH_OBJ_PROP_LEVELCOLOR,index),true); } virtual string LevelStyleDescription(const int index) const { return LineStyleDescription((ENUM_LINE_STYLE)this.GetProperty(GRAPH_OBJ_PROP_LEVELSTYLE,index)); } virtual string LevelWidthDescription(const int index) const { return (string)this.GetProperty(GRAPH_OBJ_PROP_LEVELWIDTH,index); } virtual string LevelValueDescription(const int index) const { return ::DoubleToString(this.GetProperty(GRAPH_OBJ_PROP_LEVELVALUE,index),5); } //--- Return the descriptions of all (1) times, (2) prices, (3) pivot point times and prices, //--- (4) level values, (6) all properties of all levels, (5) BMP files of the graphical object string TimesDescription(void) const; string PricesDescription(void) const; string TimePricesDescription(void) const; string LevelColorsDescription(void) const; string LevelStylesDescription(void) const; string LevelWidthsDescription(void) const; string LevelValuesDescription(void) const; string LevelTextsDescription(void) const; string LevelsAllDescription(void) const; string BMPFilesDescription(void) const;
在受保护的参数型类构造函数中,传递另一个参数 — 构造该对象所需的轴点数量(这意味着我们需要进一步改进子对象类,以便它们能将轴点数量传递给该构造函数):
//--- Default constructor CGStdGraphObj(){ this.m_type=OBJECT_DE_TYPE_GSTD_OBJ; m_group=WRONG_VALUE; } protected: //--- Protected parametric constructor CGStdGraphObj(const ENUM_OBJECT_DE_TYPE obj_type, const ENUM_GRAPH_OBJ_BELONG belong, const ENUM_GRAPH_OBJ_GROUP group, const long chart_id, const int pivots, const string name);
我们来看看类构造函数的实现:
//+------------------------------------------------------------------+ //| Protected parametric constructor | //+------------------------------------------------------------------+ CGStdGraphObj::CGStdGraphObj(const ENUM_OBJECT_DE_TYPE obj_type, const ENUM_GRAPH_OBJ_BELONG belong, const ENUM_GRAPH_OBJ_GROUP group, const long chart_id,const int pivots, const string name) { //--- Create the property object with the default values this.Prop=new CProperty(GRAPH_OBJ_PROP_INTEGER_TOTAL,GRAPH_OBJ_PROP_DOUBLE_TOTAL,GRAPH_OBJ_PROP_STRING_TOTAL); //--- Set the number of pivot points and object levels this.m_pivots=pivots; int levels=(int)::ObjectGetInteger(chart_id,name,OBJPROP_LEVELS); //--- Set the property array dimensionalities according to the number of pivot points and levels this.Prop.SetSizeRange(GRAPH_OBJ_PROP_TIME,this.m_pivots); this.Prop.SetSizeRange(GRAPH_OBJ_PROP_PRICE,this.m_pivots); this.Prop.SetSizeRange(GRAPH_OBJ_PROP_LEVELCOLOR,levels); this.Prop.SetSizeRange(GRAPH_OBJ_PROP_LEVELSTYLE,levels); this.Prop.SetSizeRange(GRAPH_OBJ_PROP_LEVELWIDTH,levels); this.Prop.SetSizeRange(GRAPH_OBJ_PROP_LEVELVALUE,levels); this.Prop.SetSizeRange(GRAPH_OBJ_PROP_LEVELTEXT,levels); this.Prop.SetSizeRange(GRAPH_OBJ_PROP_BMPFILE,2); //--- Set the object (1) type, type of graphical (2) object, (3) element, (4) subwindow affiliation and (5) index, as well as (6) chart symbol Digits this.m_type=obj_type; this.SetName(name); CGBaseObj::SetChartID(chart_id); CGBaseObj::SetTypeGraphObject(CGBaseObj::GraphObjectType(obj_type)); CGBaseObj::SetTypeElement(GRAPH_ELEMENT_TYPE_STANDARD); CGBaseObj::SetBelong(belong); CGBaseObj::SetGroup(group); CGBaseObj::SetSubwindow(chart_id,name); CGBaseObj::SetDigits((int)::SymbolInfoInteger(::ChartSymbol(chart_id),SYMBOL_DIGITS)); //--- Save the integer properties inherent in all graphical objects but not present in the current one this.SetProperty(GRAPH_OBJ_PROP_CHART_ID,0,CGBaseObj::ChartID()); // Chart ID this.SetProperty(GRAPH_OBJ_PROP_WND_NUM,0,CGBaseObj::SubWindow()); // Chart subwindow index this.SetProperty(GRAPH_OBJ_PROP_TYPE,0,CGBaseObj::TypeGraphObject()); // Graphical object type (ENUM_OBJECT) this.SetProperty(GRAPH_OBJ_PROP_ELEMENT_TYPE,0,CGBaseObj::TypeGraphElement()); // Graphical element type (ENUM_GRAPH_ELEMENT_TYPE) this.SetProperty(GRAPH_OBJ_PROP_BELONG,0,CGBaseObj::Belong()); // Graphical object affiliation this.SetProperty(GRAPH_OBJ_PROP_GROUP,0,CGBaseObj::Group()); // Graphical object group this.SetProperty(GRAPH_OBJ_PROP_ID,0,0); // Object ID this.SetProperty(GRAPH_OBJ_PROP_NUM,0,0); // Object index in the list //--- Save the properties inherent in all graphical objects and present in a graphical object this.PropertiesRefresh(); //--- Save basic properties in the parent object this.m_create_time=(datetime)this.GetProperty(GRAPH_OBJ_PROP_CREATETIME,0); this.m_back=(bool)this.GetProperty(GRAPH_OBJ_PROP_BACK,0); this.m_selected=(bool)this.GetProperty(GRAPH_OBJ_PROP_SELECTED,0); this.m_selectable=(bool)this.GetProperty(GRAPH_OBJ_PROP_SELECTABLE,0); this.m_hidden=(bool)this.GetProperty(GRAPH_OBJ_PROP_HIDDEN,0); //--- Save the current properties to the previous ones this.PropertiesCopyToPrevData(); } //+-------------------------------------------------------------------+
在此,我首先依据指定的整数型、实数型和字符串型属性的数量创建图形对象属性类的对象。 接着,针对拥有多个参数(轴点价格和时间、级别数量和每个级别的属性)的属性,设置第二维度中数组的大小。 设置和获取只有一个参数的属性时,需把第二维度的参数索引指定为零。
此刻,在按所有属性比较 CGStdGraphObj 对象的方法中,应按第二个维度大小循环,逐一比较属性:
//+------------------------------------------------------------------+ //| Compare CGStdGraphObj objects by all properties | //+------------------------------------------------------------------+ bool CGStdGraphObj::IsEqual(CGStdGraphObj *compared_obj) const { int begin=0, end=GRAPH_OBJ_PROP_INTEGER_TOTAL; for(int i=begin; i<end; i++) { ENUM_GRAPH_OBJ_PROP_INTEGER prop=(ENUM_GRAPH_OBJ_PROP_INTEGER)i; for(int j=0;j<Prop.CurrSize(prop);j++) if(this.GetProperty(prop,j)!=compared_obj.GetProperty(prop,j)) return false; } begin=end; end+=GRAPH_OBJ_PROP_DOUBLE_TOTAL; for(int i=begin; i<end; i++) { ENUM_GRAPH_OBJ_PROP_DOUBLE prop=(ENUM_GRAPH_OBJ_PROP_DOUBLE)i; for(int j=0;j<Prop.CurrSize(prop);j++) if(this.GetProperty(prop,j)!=compared_obj.GetProperty(prop,j)) return false; } begin=end; end+=GRAPH_OBJ_PROP_STRING_TOTAL; for(int i=begin; i<end; i++) { ENUM_GRAPH_OBJ_PROP_STRING prop=(ENUM_GRAPH_OBJ_PROP_STRING)i; for(int j=0;j<Prop.CurrSize(prop);j++) if(this.GetProperty(prop,j)!=compared_obj.GetProperty(prop,j)) return false; } return true; } //+------------------------------------------------------------------+
现在,在获取和保存图形对象属性的方法中,所有拥有多个值的属性均需按属性数量循环并逐一填充:
//+------------------------------------------------------------------+ //| Get and save the integer properties | //+------------------------------------------------------------------+ void CGStdGraphObj::GetAndSaveINT(void) { //--- Properties inherent in all graphical objects and present in a graphical object this.SetProperty(GRAPH_OBJ_PROP_CREATETIME,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_CREATETIME)); this.SetProperty(GRAPH_OBJ_PROP_CREATETIME,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_CREATETIME)); // Object creation time this.SetProperty(GRAPH_OBJ_PROP_TIMEFRAMES,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_TIMEFRAMES)); // Object visibility on timeframes this.SetProperty(GRAPH_OBJ_PROP_BACK,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_BACK)); // Background object this.SetProperty(GRAPH_OBJ_PROP_ZORDER,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_ZORDER)); // Priority of a graphical object for receiving the event of clicking on a chart this.SetProperty(GRAPH_OBJ_PROP_HIDDEN,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_HIDDEN)); // Disable displaying the name of a graphical object in the terminal object list this.SetProperty(GRAPH_OBJ_PROP_SELECTED,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_SELECTED)); // Object selection this.SetProperty(GRAPH_OBJ_PROP_SELECTABLE,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_SELECTABLE)); // Object availability for(int i=0;i<this.m_pivots;i++) // Point time coordinates this.SetTimePivot(i); this.SetProperty(GRAPH_OBJ_PROP_COLOR,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_COLOR)); // Color this.SetProperty(GRAPH_OBJ_PROP_STYLE,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_STYLE)); // Style this.SetProperty(GRAPH_OBJ_PROP_WIDTH,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_WIDTH)); // Line width //--- Properties belonging to different graphical objects this.SetProperty(GRAPH_OBJ_PROP_FILL,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_FILL)); // Fill an object with color this.SetProperty(GRAPH_OBJ_PROP_READONLY,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_READONLY)); // Ability to edit text in the Edit object this.SetProperty(GRAPH_OBJ_PROP_LEVELS,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_LEVELS)); // Number of levels for(int i=0;i<this.Levels();i++) // Level data { this.SetLevelColor(i); this.SetLevelStyle(i); this.SetLevelWidth(i); } this.SetProperty(GRAPH_OBJ_PROP_ALIGN,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_ALIGN)); // Horizontal text alignment in the Edit object (OBJ_EDIT) this.SetProperty(GRAPH_OBJ_PROP_FONTSIZE,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_FONTSIZE)); // Font size this.SetProperty(GRAPH_OBJ_PROP_RAY_LEFT,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_RAY_LEFT)); // Ray goes to the left this.SetProperty(GRAPH_OBJ_PROP_RAY_RIGHT,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_RAY_RIGHT)); // Ray goes to the right this.SetProperty(GRAPH_OBJ_PROP_RAY,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_RAY)); // Vertical line goes through all windows of a chart this.SetProperty(GRAPH_OBJ_PROP_ELLIPSE,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_ELLIPSE)); // Display the full ellipse of the Fibonacci Arc object this.SetProperty(GRAPH_OBJ_PROP_ARROWCODE,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_ARROWCODE)); // Arrow code for the "Arrow" object this.SetProperty(GRAPH_OBJ_PROP_ANCHOR,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_ANCHOR)); // Position of the binding point of the graphical object this.SetProperty(GRAPH_OBJ_PROP_XDISTANCE,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_XDISTANCE)); // Distance from the base corner along the X axis in pixels this.SetProperty(GRAPH_OBJ_PROP_YDISTANCE,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_YDISTANCE)); // Distance from the base corner along the Y axis in pixels this.SetProperty(GRAPH_OBJ_PROP_DIRECTION,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_DIRECTION)); // Gann object trend this.SetProperty(GRAPH_OBJ_PROP_DEGREE,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_DEGREE)); // Elliott wave marking level this.SetProperty(GRAPH_OBJ_PROP_DRAWLINES,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_DRAWLINES)); // Display lines for Elliott wave marking this.SetProperty(GRAPH_OBJ_PROP_STATE,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_STATE)); // Button state (pressed/released) this.SetProperty(GRAPH_OBJ_PROP_CHART_OBJ_CHART_ID,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_CHART_ID));// Chart object ID (OBJ_CHART). this.SetProperty(GRAPH_OBJ_PROP_CHART_OBJ_PERIOD,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_PERIOD)); // Chart object period this.SetProperty(GRAPH_OBJ_PROP_CHART_OBJ_DATE_SCALE,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_DATE_SCALE)); // Time scale display flag for the Chart object this.SetProperty(GRAPH_OBJ_PROP_CHART_OBJ_PRICE_SCALE,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_PRICE_SCALE));// Price scale display flag for the Chart object this.SetProperty(GRAPH_OBJ_PROP_CHART_OBJ_CHART_SCALE,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_CHART_SCALE));// Chart object scale this.SetProperty(GRAPH_OBJ_PROP_XSIZE,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_XSIZE)); // Object width along the X axis in pixels. this.SetProperty(GRAPH_OBJ_PROP_YSIZE,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_YSIZE)); // Object height along the Y axis in pixels. this.SetProperty(GRAPH_OBJ_PROP_XOFFSET,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_XOFFSET)); // X coordinate of the upper-left corner of the visibility area. this.SetProperty(GRAPH_OBJ_PROP_YOFFSET,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_YOFFSET)); // Y coordinate of the upper-left corner of the visibility area. this.SetProperty(GRAPH_OBJ_PROP_BGCOLOR,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_BGCOLOR)); // Background color for OBJ_EDIT, OBJ_BUTTON, OBJ_RECTANGLE_LABEL this.SetProperty(GRAPH_OBJ_PROP_CORNER,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_CORNER)); // Chart corner for binding a graphical object this.SetProperty(GRAPH_OBJ_PROP_BORDER_TYPE,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_BORDER_TYPE));// Border type for "Rectangle border" this.SetProperty(GRAPH_OBJ_PROP_BORDER_COLOR,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_BORDER_COLOR));// Border color for OBJ_EDIT and OBJ_BUTTON } //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Get and save the real properties | //+------------------------------------------------------------------+ void CGStdGraphObj::GetAndSaveDBL(void) { for(int i=0;i<this.m_pivots;i++) // Point prices coordinates SetPricePivot(i); for(int i=0;i<this.Levels();i++) // Level values this.SetLevelValue(i); this.SetProperty(GRAPH_OBJ_PROP_SCALE,0,::ObjectGetDouble(this.ChartID(),this.Name(),OBJPROP_SCALE)); // Scale (property of Gann objects and Fibonacci Arcs objects) this.SetProperty(GRAPH_OBJ_PROP_ANGLE,0,::ObjectGetDouble(this.ChartID(),this.Name(),OBJPROP_ANGLE)); // Angle this.SetProperty(GRAPH_OBJ_PROP_DEVIATION,0,::ObjectGetDouble(this.ChartID(),this.Name(),OBJPROP_DEVIATION)); // Deviation of the standard deviation channel } //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Get and save the string properties | //+------------------------------------------------------------------+ void CGStdGraphObj::GetAndSaveSTR(void) { this.SetProperty(GRAPH_OBJ_PROP_TEXT,0,::ObjectGetString(this.ChartID(),this.Name(),OBJPROP_TEXT)); // Object description (the text contained in the object) this.SetProperty(GRAPH_OBJ_PROP_TOOLTIP,0,::ObjectGetString(this.ChartID(),this.Name(),OBJPROP_TOOLTIP)); // Tooltip text for(int i=0;i<this.Levels();i++) // Level descriptions this.SetLevelText(i); this.SetProperty(GRAPH_OBJ_PROP_FONT,0,::ObjectGetString(this.ChartID(),this.Name(),OBJPROP_FONT)); // Font this.SetProperty(GRAPH_OBJ_PROP_BMPFILE,0,::ObjectGetString(this.ChartID(),this.Name(),OBJPROP_BMPFILE)); // BMP file name for the "Bitmap Level" object this.SetProperty(GRAPH_OBJ_PROP_CHART_OBJ_SYMBOL,0,::ObjectGetString(this.ChartID(),this.Name(),OBJPROP_SYMBOL));// Chart object symbol } //+------------------------------------------------------------------+
在复制当前数据至之前数据的方法中,调用当前属性对象专门创建的方法,从而把当前属性复制给之前属性:
//+------------------------------------------------------------------+ //| Copy the current data to the previous one | //+------------------------------------------------------------------+ void CGStdGraphObj::PropertiesCopyToPrevData(void) { this.Prop.CurrentToPrevious(); } //+------------------------------------------------------------------+
在检查对象属性变化的方法中,首先检查属性是否拥有多个值。 如果是,则按属性实例数量循环检查其变化。 否则,只需比较两个对象的属性:
//+------------------------------------------------------------------+ //| Check object property changes | //+------------------------------------------------------------------+ void CGStdGraphObj::PropertiesCheckChanged(void) { bool changed=false; int begin=0, end=GRAPH_OBJ_PROP_INTEGER_TOTAL; for(int i=begin; i<end; i++) { ENUM_GRAPH_OBJ_PROP_INTEGER prop=(ENUM_GRAPH_OBJ_PROP_INTEGER)i; if(!this.SupportProperty(prop)) continue; if(prop==GRAPH_OBJ_PROP_TIME || (prop>=GRAPH_OBJ_PROP_LEVELCOLOR && prop<=GRAPH_OBJ_PROP_LEVELWIDTH)) { int total=(prop==GRAPH_OBJ_PROP_TIME ? this.m_pivots : this.Levels()); for(int j=0;j<Prop.CurrSize(prop);j++) { if(this.GetProperty(prop,j)!=this.GetPropertyPrev(prop,j)) { changed=true; ::Print(DFUN,this.Name(),", property index: ",j," ",TextByLanguage(" Изменённое свойство: "," Modified property: "),this.GetPropertyDescription(prop)); } } } else if(this.GetProperty(prop,0)!=this.GetPropertyPrev(prop,0)) { changed=true; ::Print(DFUN,this.Name(),": ",TextByLanguage(" Изменённое свойство: "," Modified property: "),this.GetPropertyDescription(prop)); } } begin=end; end+=GRAPH_OBJ_PROP_DOUBLE_TOTAL; for(int i=begin; i<end; i++) { ENUM_GRAPH_OBJ_PROP_DOUBLE prop=(ENUM_GRAPH_OBJ_PROP_DOUBLE)i; if(!this.SupportProperty(prop)) continue; if(prop==GRAPH_OBJ_PROP_PRICE || GRAPH_OBJ_PROP_LEVELVALUE) { int total=(prop==GRAPH_OBJ_PROP_PRICE ? this.m_pivots : this.Levels()); for(int j=0;j<Prop.CurrSize(prop);j++) { if(this.GetProperty(prop,j)!=this.GetPropertyPrev(prop,j)) { changed=true; ::Print(DFUN,this.Name(),", property index: ",j," ",TextByLanguage(" Изменённое свойство: "," Modified property: "),this.GetPropertyDescription(prop)); } } } else if(this.GetProperty(prop,0)!=this.GetPropertyPrev(prop,0)) { changed=true; ::Print(DFUN,this.Name(),": ",TextByLanguage(" Изменённое свойство: "," Modified property: "),GetPropertyDescription(prop)); } } begin=end; end+=GRAPH_OBJ_PROP_STRING_TOTAL; for(int i=begin; i<end; i++) { ENUM_GRAPH_OBJ_PROP_STRING prop=(ENUM_GRAPH_OBJ_PROP_STRING)i; if(!this.SupportProperty(prop)) continue; if(prop==GRAPH_OBJ_PROP_LEVELTEXT || prop==GRAPH_OBJ_PROP_BMPFILE) { int total=(prop==GRAPH_OBJ_PROP_LEVELTEXT ? this.Levels() : 2); for(int j=0;j<Prop.CurrSize(prop);j++) { if(this.GetProperty(prop,j)!=this.GetPropertyPrev(prop,j)) { changed=true; ::Print(DFUN,this.Name(),", property index: ",j," ",TextByLanguage(" Изменённое свойство: "," Modified property: "),this.GetPropertyDescription(prop)); } } } else if(this.GetProperty(prop,0)!=this.GetPropertyPrev(prop,0)) { changed=true; ::Print(DFUN,this.Name(),": ",TextByLanguage(" Изменённое свойство: "," Modified property: "),GetPropertyDescription(prop)); } } if(changed) PropertiesCopyToPrevData(); } //+------------------------------------------------------------------+
读取和设置对象属性以及返回属性描述的方法:
//+------------------------------------------------------------------+ //| Read and set the time of the specified pivot point | //+------------------------------------------------------------------+ void CGStdGraphObj::SetTimePivot(const int index) { this.SetProperty(GRAPH_OBJ_PROP_TIME,index,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_TIME,index)); } //+------------------------------------------------------------------+ //| Read and set the price of the specified pivot point | //+------------------------------------------------------------------+ void CGStdGraphObj::SetPricePivot(const int index) { this.SetProperty(GRAPH_OBJ_PROP_PRICE,index,::ObjectGetDouble(this.ChartID(),this.Name(),OBJPROP_PRICE,index)); } //+------------------------------------------------------------------+ //| Read and set the color of the specified level | //+------------------------------------------------------------------+ void CGStdGraphObj::SetLevelColor(const int index) { this.SetProperty(GRAPH_OBJ_PROP_LEVELCOLOR,index,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_LEVELCOLOR,index)); } //+------------------------------------------------------------------+ //| Read and set the style of the specified level | //+------------------------------------------------------------------+ void CGStdGraphObj::SetLevelStyle(const int index) { this.SetProperty(GRAPH_OBJ_PROP_LEVELSTYLE,index,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_LEVELSTYLE,index)); } //+------------------------------------------------------------------+ //| Read and set the width of the specified level | //+------------------------------------------------------------------+ void CGStdGraphObj::SetLevelWidth(const int index) { this.SetProperty(GRAPH_OBJ_PROP_LEVELWIDTH,index,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_LEVELWIDTH,index)); } //+------------------------------------------------------------------+ //| Read and set the value of the specified level | //+------------------------------------------------------------------+ void CGStdGraphObj::SetLevelValue(const int index) { this.SetProperty(GRAPH_OBJ_PROP_LEVELVALUE,index,::ObjectGetDouble(this.ChartID(),this.Name(),OBJPROP_LEVELVALUE,index)); } //+------------------------------------------------------------------+ //| Read and set the text of the specified level | //+------------------------------------------------------------------+ void CGStdGraphObj::SetLevelText(const int index) { this.SetProperty(GRAPH_OBJ_PROP_LEVELTEXT,index,::ObjectGetString(this.ChartID(),this.Name(),OBJPROP_LEVELTEXT,index)); } //+------------------------------------------------------------------+ //| Read and set the BMP file name | //| for the Bitmap Label object. | //| Index: 0 - ON, 1 - OFF | //+------------------------------------------------------------------+ void CGStdGraphObj::SetBMPFile(const int index) { this.SetProperty(GRAPH_OBJ_PROP_BMPFILE,index,::ObjectGetString(this.ChartID(),this.Name(),OBJPROP_BMPFILE,index)); } //+------------------------------------------------------------------+ //| Return the descriptions of all pivot point times | //+------------------------------------------------------------------+ string CGStdGraphObj::TimesDescription(void) const { string txt=""; for(int i=0;i<this.m_pivots;i++) txt+=" - "+CMessage::Text(MSG_GRAPH_OBJ_TEXT_PIVOT)+(string)i+": "+this.TimeDescription(i)+(i<this.m_pivots-1 ? "\n" : ""); return txt; } //+------------------------------------------------------------------+ //| Return the prices of all pivot points | //+------------------------------------------------------------------+ string CGStdGraphObj::PricesDescription(void) const { string txt=""; for(int i=0;i<this.m_pivots;i++) txt+=" - "+CMessage::Text(MSG_GRAPH_OBJ_TEXT_PIVOT)+(string)i+": "+this.PriceDescription(i)+(i<this.m_pivots-1 ? "\n" : ""); return txt; } //+------------------------------------------------------------------+ //| Return the descriptions of all pivot points | //+------------------------------------------------------------------+ string CGStdGraphObj::TimePricesDescription(void) const { string txt=""; for(int i=0;i<this.m_pivots;i++) txt+=(" - "+CMessage::Text(MSG_GRAPH_OBJ_TEXT_PIVOT)+(string)i+": "+this.TimeDescription(i)+" / "+this.PriceDescription(i)+(i<this.m_pivots-1 ? "\n" : "")); return txt; } //+------------------------------------------------------------------+ //| Return the descriptions of all level colors | //+------------------------------------------------------------------+ string CGStdGraphObj::LevelColorsDescription(void) const { string txt=""; for(int i=0;i<this.Levels();i++) txt+=(" - "+CMessage::Text(MSG_GRAPH_OBJ_TEXT_LEVEL)+(string)i+": "+this.LevelColorDescription(i)+(i<this.Levels()-1 ? "\n" : "")); return txt; } //+------------------------------------------------------------------+ //| Return the descriptions of all level styles | //+------------------------------------------------------------------+ string CGStdGraphObj::LevelStylesDescription(void) const { string txt=""; for(int i=0;i<this.Levels();i++) txt+=(" - "+CMessage::Text(MSG_GRAPH_OBJ_TEXT_LEVEL)+(string)i+": "+LineStyleDescription(this.LevelStyle(i))+(i<this.Levels()-1 ? "\n" : "")); return txt; } //+------------------------------------------------------------------+ //| Return the descriptions of all level width | //+------------------------------------------------------------------+ string CGStdGraphObj::LevelWidthsDescription(void) const { string txt=""; for(int i=0;i<this.Levels();i++) txt+=(" - "+CMessage::Text(MSG_GRAPH_OBJ_TEXT_LEVEL)+(string)i+": "+this.LevelWidthDescription(i)+(i<this.Levels()-1 ? "\n" : "")); return txt; } //+------------------------------------------------------------------+ //| Return the descriptions of all level values | //+------------------------------------------------------------------+ string CGStdGraphObj::LevelValuesDescription(void) const { string txt=""; for(int i=0;i<this.Levels();i++) txt+=(" - "+CMessage::Text(MSG_GRAPH_OBJ_TEXT_LEVEL)+(string)i+": "+this.LevelValueDescription(i)+(i<this.Levels()-1 ? "\n" : "")); return txt; } //+------------------------------------------------------------------+ //| Return the descriptions of all level texts | //+------------------------------------------------------------------+ string CGStdGraphObj::LevelTextsDescription(void) const { string txt=""; for(int i=0;i<this.Levels();i++) txt+=(" - "+CMessage::Text(MSG_GRAPH_OBJ_TEXT_LEVEL)+(string)i+": "+this.LevelText(i)+(i<this.Levels()-1 ? "\n" : "")); return txt; } //+------------------------------------------------------------------+ //| Return the descriptions of the graphical object BMP files | //+------------------------------------------------------------------+ string CGStdGraphObj::BMPFilesDescription(void) const { string txt= ( " - "+CMessage::Text(MSG_GRAPH_OBJ_TEXT_BMP_FILE_STATE_ON) +" (0): \""+BMPFile(0)+"\"\n"+ " - "+CMessage::Text(MSG_GRAPH_OBJ_TEXT_BMP_FILE_STATE_OFF)+" (1): \""+BMPFile(1)+"\"" ); return txt; } //+------------------------------------------------------------------+ //| Return the descriptions of all values of all levels | //+------------------------------------------------------------------+ string CGStdGraphObj::LevelsAllDescription(void) const { string txt=""; for(int i=0;i<this.Levels();i++) { txt+= ( " - "+CMessage::Text(MSG_GRAPH_OBJ_TEXT_LEVEL)+" "+(string)i+"\n"+ " "+CMessage::Text(MSG_GRAPH_OBJ_PROP_LEVELCOLOR)+": "+LevelColorDescription(i)+"\n"+ " "+CMessage::Text(MSG_GRAPH_OBJ_PROP_LEVELSTYLE)+": "+LevelStyleDescription(i)+"\n"+ " "+CMessage::Text(MSG_GRAPH_OBJ_PROP_LEVELWIDTH)+": "+LevelWidthDescription(i)+"\n"+ " "+CMessage::Text(MSG_GRAPH_OBJ_PROP_LEVELVALUE)+": "+LevelValueDescription(i)+"\n"+ " "+CMessage::Text(MSG_GRAPH_OBJ_PROP_LEVELTEXT) +": "+LevelText(i) ); } return txt; } //+------------------------------------------------------------------+
我相信,这些方法几乎不需要任何评论。 除了所有研究过的方法和修改之外,该类还进行了众多修改,从而能指定第二维度索引。 我不会在此讲述它们。 您可在文后所附的文件中找到它们。
由于类构造函数在创建子类时有一个新的传递轴点数量的变量,因此我们需要逐一修改该类对象的构造函数。 我只考虑两个图形对象类文件。
打开买入信号图形对象类文件,它位于 \MQL5\Include\DoEasy\Objects\Graph\Standard\GStdArrowBuyObj.mqh,并加入将对象轴点数量的参数传递给父类构造函数:
//+------------------------------------------------------------------+ //| Buy graphical object | //+------------------------------------------------------------------+ class CGStdArrowBuyObj : public CGStdGraphObj { private: public: //--- Constructor CGStdArrowBuyObj(const long chart_id,const string name) : CGStdGraphObj(OBJECT_DE_TYPE_GSTD_ARROW_BUY,GRAPH_OBJ_BELONG_NO_PROGRAM,GRAPH_OBJ_GROUP_ARROWS,chart_id,1,name) { //--- Specify the object property CGStdGraphObj::SetProperty(GRAPH_OBJ_PROP_ANCHOR,0,ANCHOR_TOP); } //--- Supported object properties (1) real, (2) integer virtual bool SupportProperty(ENUM_GRAPH_OBJ_PROP_DOUBLE property); virtual bool SupportProperty(ENUM_GRAPH_OBJ_PROP_INTEGER property); virtual bool SupportProperty(ENUM_GRAPH_OBJ_PROP_STRING property); //--- Return the graphical object anchor point position ENUM_ARROW_ANCHOR Anchor(void) const { return (ENUM_ARROW_ANCHOR)this.GetProperty(GRAPH_OBJ_PROP_ANCHOR,0); } //--- Display a short description of the object in the journal virtual void PrintShort(const bool dash=false,const bool symbol=false); //--- Return the object short name virtual string Header(const bool symbol=false); //--- Return the description of the (ENUM_OBJECT) graphical object type virtual string TypeDescription(void) const { return StdGraphObjectTypeDescription(OBJ_ARROW_BUY); } //--- Return the description of the graphical object anchor point position virtual string AnchorDescription(void) const { return AnchorForArrowObjDescription(this.Anchor()); } }; //+------------------------------------------------------------------+
此对象只有一个轴点。 现在我们来研究拥有若干轴点的对象。
打开 "Trend line" 对象类文件,其位于 \MQL5\Include\DoEasy\Objects\Graph\Standard\GStdTrendObj.mqh,并加入将轴点数量(等于两个)传递给父类构造函数:
//+------------------------------------------------------------------+ //| "Trend line" graphical object | //+------------------------------------------------------------------+ class CGStdTrendObj : public CGStdGraphObj { private: public: //--- Constructor CGStdTrendObj(const long chart_id,const string name) : CGStdGraphObj(OBJECT_DE_TYPE_GSTD_TREND,GRAPH_OBJ_BELONG_NO_PROGRAM,GRAPH_OBJ_GROUP_LINES,chart_id,2,name) { //--- Get and save the object properties CGStdGraphObj::SetProperty(GRAPH_OBJ_PROP_RAY_LEFT,0,::ObjectGetInteger(chart_id,name,OBJPROP_RAY_LEFT)); CGStdGraphObj::SetProperty(GRAPH_OBJ_PROP_RAY_RIGHT,0,::ObjectGetInteger(chart_id,name,OBJPROP_RAY_RIGHT)); } //--- Supported object properties (1) real, (2) integer virtual bool SupportProperty(ENUM_GRAPH_OBJ_PROP_DOUBLE property); virtual bool SupportProperty(ENUM_GRAPH_OBJ_PROP_INTEGER property); virtual bool SupportProperty(ENUM_GRAPH_OBJ_PROP_STRING property); //--- Display a short description of the object in the journal virtual void PrintShort(const bool dash=false,const bool symbol=false); //--- Return the object short name virtual string Header(const bool symbol=false); //--- Return the description of the (ENUM_OBJECT) graphical object type virtual string TypeDescription(void) const { return StdGraphObjectTypeDescription(OBJ_TREND); } }; //+------------------------------------------------------------------+
针对 \MQL5\Include\DoEasy\Objects\Graph\Standard\ 中的每个类均进行了此类修改。 每个对象都拥有构造自身的确切轴点数量。 您可以查看文后附件中的所有修改。
现在,我们来改进 \MQL5\Include\DoEasy\Services\Select.mqh 中的 CSelect 类。 在此,我们简单地在访问 GetProperty() 方法的每个方法中加入第二维度索引:
//+------------------------------------------------------------------+ //| Methods of working with standard graphical object data | //+------------------------------------------------------------------+ //--- Return the list of objects with one of (1) integer, (2) real and (3) string properties meeting a specified criterion static CArrayObj *ByGraphicStdObjectProperty(CArrayObj *list_source,ENUM_GRAPH_OBJ_PROP_INTEGER property,int index,long value,ENUM_COMPARER_TYPE mode); static CArrayObj *ByGraphicStdObjectProperty(CArrayObj *list_source,ENUM_GRAPH_OBJ_PROP_DOUBLE property,int index,double value,ENUM_COMPARER_TYPE mode); static CArrayObj *ByGraphicStdObjectProperty(CArrayObj *list_source,ENUM_GRAPH_OBJ_PROP_STRING property,int index,string value,ENUM_COMPARER_TYPE mode); //--- Return the graphical object index in the list with the maximum value of the (1) integer, (2) real and (3) string properties static int FindGraphicStdObjectMax(CArrayObj *list_source,ENUM_GRAPH_OBJ_PROP_INTEGER property,int index); static int FindGraphicStdObjectMax(CArrayObj *list_source,ENUM_GRAPH_OBJ_PROP_DOUBLE property,int index); static int FindGraphicStdObjectMax(CArrayObj *list_source,ENUM_GRAPH_OBJ_PROP_STRING property,int index); //--- Return the graphical object index in the list with the minimum value of the (1) integer, (2) real and (3) string properties static int FindGraphicStdObjectMin(CArrayObj *list_source,ENUM_GRAPH_OBJ_PROP_INTEGER property,int index); static int FindGraphicStdObjectMin(CArrayObj *list_source,ENUM_GRAPH_OBJ_PROP_DOUBLE property,int index); static int FindGraphicStdObjectMin(CArrayObj *list_source,ENUM_GRAPH_OBJ_PROP_STRING property,int index); //--- }; //+------------------------------------------------------------------+
我们来研究这若干个方法的实现:
//+------------------------------------------------------------------+ //| Return the list of objects with one integer | //| property meeting the specified criterion | //+------------------------------------------------------------------+ CArrayObj *CSelect::ByGraphicStdObjectProperty(CArrayObj *list_source,ENUM_GRAPH_OBJ_PROP_INTEGER property,int index,long value,ENUM_COMPARER_TYPE mode) { if(list_source==NULL) return NULL; CArrayObj *list=new CArrayObj(); if(list==NULL) return NULL; list.FreeMode(false); ListStorage.Add(list); int total=list_source.Total(); for(int i=0; i<total; i++) { CGStdGraphObj *obj=list_source.At(i); if(!obj.SupportProperty(property)) continue; long obj_prop=obj.GetProperty(property,index); if(CompareValues(obj_prop,value,mode)) list.Add(obj); } return list; } //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Return the object index in the list | //| with the maximum integer property value | //+------------------------------------------------------------------+ int CSelect::FindGraphicStdObjectMax(CArrayObj *list_source,ENUM_GRAPH_OBJ_PROP_INTEGER property,int index) { if(list_source==NULL) return WRONG_VALUE; int idx=0; CGStdGraphObj *max_obj=NULL; int total=list_source.Total(); if(total==0) return WRONG_VALUE; for(int i=1; i<total; i++) { CGStdGraphObj *obj=list_source.At(i); long obj1_prop=obj.GetProperty(property,index); max_obj=list_source.At(idx); long obj2_prop=max_obj.GetProperty(property,index); if(CompareValues(obj1_prop,obj2_prop,MORE)) idx=i; } return idx; } //+------------------------------------------------------------------+
所有修改都用颜色突出显示。 其余方法的修改与上述方法雷同。 您可以在文后随附的文件中精研它们。
现在,我们来修改图形对象集合类 \MQL5\Include\DoEasy\Collections\GraphElementsCollection.mqh。
在 GetList() 公开方法里,添加数组第二维度索引的指示:
public: //--- Return itself CGraphElementsCollection *GetObject(void) { return &this; } //--- Return the full collection list of standard graphical objects "as is" CArrayObj *GetListGraphObj(void) { return &this.m_list_all_graph_obj; } //--- Return the full collection list of graphical elements on canvas "as is" CArrayObj *GetListCanvElm(void) { return &this.m_list_all_canv_elm_obj;} //--- Return the list of graphical elements by a selected (1) integer, (2) real and (3) string properties meeting the compared criterion CArrayObj *GetList(ENUM_CANV_ELEMENT_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode=EQUAL) { return CSelect::ByGraphCanvElementProperty(this.GetListCanvElm(),property,value,mode); } CArrayObj *GetList(ENUM_CANV_ELEMENT_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode=EQUAL){ return CSelect::ByGraphCanvElementProperty(this.GetListCanvElm(),property,value,mode); } CArrayObj *GetList(ENUM_CANV_ELEMENT_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode=EQUAL){ return CSelect::ByGraphCanvElementProperty(this.GetListCanvElm(),property,value,mode); } //--- Return the list of graphical objects by a selected (1) integer, (2) real and (3) string properties meeting the compared criterion CArrayObj *GetList(ENUM_GRAPH_OBJ_PROP_INTEGER property,int index,long value,ENUM_COMPARER_TYPE mode=EQUAL) { return CSelect::ByGraphicStdObjectProperty(this.GetListGraphObj(),property,index,value,mode); } CArrayObj *GetList(ENUM_GRAPH_OBJ_PROP_DOUBLE property,int index,double value,ENUM_COMPARER_TYPE mode=EQUAL) { return CSelect::ByGraphicStdObjectProperty(this.GetListGraphObj(),property,index,value,mode); } CArrayObj *GetList(ENUM_GRAPH_OBJ_PROP_STRING property,int index,string value,ENUM_COMPARER_TYPE mode=EQUAL) { return CSelect::ByGraphicStdObjectProperty(this.GetListGraphObj(),property,index,value,mode); }
所有方法均能够访问改进后的 CSelect 类方法,添加数组第二维度索引的指示:
//+------------------------------------------------------------------+ //| Return the first free graphical object ID | //+------------------------------------------------------------------+ long CGraphElementsCollection::GetFreeGraphObjID(void) { int index=CSelect::FindGraphicStdObjectMax(this.GetListGraphObj(),GRAPH_OBJ_PROP_ID,0); CGStdGraphObj *obj=this.m_list_all_graph_obj.At(index); return(obj!=NULL ? obj.ObjectID()+1 : 1); } //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //|Find an object present in the collection but not on a chart | //+------------------------------------------------------------------+ CGStdGraphObj *CGraphElementsCollection::FindMissingObj(const long chart_id) { CArrayObj *list=CSelect::ByGraphicStdObjectProperty(this.GetListGraphObj(),GRAPH_OBJ_PROP_CHART_ID,0,chart_id,EQUAL); if(list==NULL) return NULL; for(int i=0;i<list.Total();i++) { CGStdGraphObj *obj=list.At(i); if(obj==NULL) continue; if(!this.IsPresentGraphObjOnChart(obj.ChartID(),obj.Name())) return obj; } return NULL; } //+------------------------------------------------------------------+ //+------------------------------------------------------------------------------+ //| Return the flag indicating the presence of the graphical object class object | //| in the graphical object collection list | //+------------------------------------------------------------------------------+ bool CGraphElementsCollection::IsPresentGraphObjInList(const long chart_id,const string name) { CArrayObj *list=CSelect::ByGraphicStdObjectProperty(this.GetListGraphObj(),GRAPH_OBJ_PROP_CHART_ID,0,chart_id,EQUAL); list=CSelect::ByGraphicStdObjectProperty(list,GRAPH_OBJ_PROP_NAME,0,name,EQUAL); return(list==NULL || list.Total()==0 ? false : true); } //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Remove a graphical object by a chart ID | //| from the graphical object collection list | //+------------------------------------------------------------------+ void CGraphElementsCollection::DeleteGraphObjectsFromList(const long chart_id) { CArrayObj *list=CSelect::ByGraphicStdObjectProperty(GetListGraphObj(),GRAPH_OBJ_PROP_CHART_ID,0,chart_id,EQUAL); if(list==NULL) return; for(int i=list.Total();i>WRONG_VALUE;i--) { CGStdGraphObj *obj=list.At(i); if(obj==NULL) continue; this.DeleteGraphObjFromList(obj); } } //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Return a graphical object by chart name and ID | //+------------------------------------------------------------------+ CGStdGraphObj *CGraphElementsCollection::GetStdGraphObject(const string name,const long chart_id) { CArrayObj *list=this.GetList(GRAPH_OBJ_PROP_CHART_ID,0,chart_id); list=CSelect::ByGraphicStdObjectProperty(list,GRAPH_OBJ_PROP_NAME,0,name,EQUAL); return(list!=NULL && list.Total()>0 ? list.At(0) : NULL); } //+------------------------------------------------------------------+
在将图形对象添加到集合的方法中,显示完整的对象描述,替代对象的简述,如此我们就能够在测试期间查看对象属性的完整列表:
//+------------------------------------------------------------------+ //| Add a graphical object to the collection | //+------------------------------------------------------------------+ bool CGraphElementsCollection::AddGraphObjToCollection(const string source,CChartObjectsControl *obj_control) { //--- Get the list of the last added graphical objects from the class for managing graphical objects CArrayObj *list=obj_control.GetListNewAddedObj(); //--- If failed to obtain the list, inform of that and return 'false' if(list==NULL) { CMessage::ToLog(DFUN_ERR_LINE,MSG_GRAPH_OBJ_FAILED_GET_ADDED_OBJ_LIST); return false; } //--- If the list is empty, return 'false' if(list.Total()==0) return false; //--- Declare the variable for storing the result bool res=true; //--- In the loop by the list of newly added standard graphical objects, for(int i=0;i<list.Total();i++) { //--- retrieve the next object from the list and CGStdGraphObj *obj=list.Detach(i); //--- if failed to get the object, inform of that, add 'false' to the resulting variable and move on to the next one if(obj==NULL) { CMessage::ToLog(source,MSG_GRAPH_OBJ_FAILED_DETACH_OBJ_FROM_LIST); res &=false; continue; } //--- if failed to add the object to the collection list, inform of that, //--- remove the object, add 'false' to the resulting variable and move on to the next one if(!this.m_list_all_graph_obj.Add(obj)) { CMessage::ToLog(source,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST); delete obj; res &=false; continue; } //--- The object has been successfully retrieved from the list of newly added graphical objects and introduced into the collection - //--- find the next free object ID, write it to the property and display the short object description in the journal else { obj.SetObjectID(this.GetFreeGraphObjID()); obj.Print(); } } //--- Return the result of adding the object to the collection return res; } //+------------------------------------------------------------------+
这些都是我打算在这里实现的改进。 在文后所附的文件中可以找到我在文章中未经研讨的一些细碎修改。
利用两个锚点测试对象事件
为了执行测试,我们延用前一篇文章中的 EA,并将其保存到 \MQL5\Experts\TestDoEasy\Part88\ 中,命名为 TestDoEasyPart88.mq5。
无需针对 EA 进行任何修改,因为它们已在函数库文件当中制作完毕。
编译 EA,并在图表上启动它:
添加拥有两个轴点的对象,并修改两个轴点中的任意之一时,EA 会在日志中显示相应的事件记录。 如果我们将包含两个以上轴点的对象添加到图表中,没必要整体修改,也无需修改轴点之一时更改另一个轴点的数据。 发生这种情况是因为上面提到的逻辑错误。 我将在下一篇文章中修复它。
下一步是什么?
在下一篇文章中,我将继续研究标准图形对象事件和动态数组类。
在评论中留下您的问题、意见和建议。
*该系列的前几篇文章:
DoEasy 函数库中的图形(第八十二部分):函数库对象重构和图形对象集合
DoEasy 函数库中的图形(第八十三部分):抽象标准图形对象类
DoEasy 函数库中的图形(第八十四部分):抽象标准图形对象的衍生后代类
DoEasy 函数库中的图形(第八十五部分):图形对象集合 - 添加新创建的对象
DoEasy 函数库中的图形(第八十六部分):图形对象集合 - 管理属性修改
DoEasy 函数库中的图形(第八十七部分):图形对象集合 - 在所有打开的图表上管理对象属性修改
本文由MetaQuotes Ltd译自俄文
原文地址: https://www.mql5.com/ru/articles/10091