Gráficos en la biblioteca DoEasy (Parte 90): Eventos de objetos gráficos estándar. Funcionalidad básica
Contenido
- Concepto
- Mejorando las clases de la biblioteca
- Eventos de objetos gráficos estándar
- Simulación
- ¿Qué es lo próximo?
Concepto
Al trabajar con los objetos gráficos que nos ofrece el terminal de cliente, a veces nos encontramos con tareas que implican la definición de algunos valores de las propiedades de estos objetos mediante programación. El programa puede monitorear el valor de cualquier línea del objeto gráfico y ejecutar el algoritmo incorporado en él, cuando, por ejemplo, es cruzado por el precio. Una línea puede desplazarse cuando el usuario mueve el objeto gráfico. Por consiguiente, el programa debe reaccionar a ello. Pero el programa debe saber que el valor de la línea que se está monitoreando ha cambiado, o bien debe sondear constantemente los valores de las propiedades del objeto gráfico, lo cual supone una tarea con gran gasto de recursos, como bien informa la ayuda del usuario.
Resulta mucho más conveniente disponer de una función que nos avise sobre los cambios en cualquier objeto gráfico, y el terminal tiene esta función: se trata del manejador de eventos OnChartEvent(). Vamos a añadir a su arsenal algunos eventos que nos informarán en nuestra biblioteca sobre lo que ha ocurrido exactamente con el objeto gráfico, mientras que el programa que trabaja bajo el control de la biblioteca sabrá exactamente lo que ha pasado con el objeto gráfico y sabrá por sus propiedades qué propiedad ha sido cambiada.
Vamos a dividir el trabajo de creación de la funcionalidad descrita en dos pasos: primero crearemos los eventos generales que pueden suceder a los objetos gráficos, y luego añadiremos la funcionalidad que especifica lo que ha ocurrido exactamente con el objeto y permite averiguarlo rápidamente de forma programática. En principio, ya estamos listos para crear tal funcionalidad; solo necesitamos mejorar un poco las clases de la biblioteca y crear un lugar en el manejador de eventos en el que se procesarán los eventos que ocurran a los objetos gráficos.
Los eventos básicos de los objetos gráficos se definen de la forma siguiente:
- Creación de un nuevo objeto gráfico,
- Cambio en las propiedades de un objeto gráfico,
- Cambio de nombre de un objeto gráfico,
- Eliminación de un objeto gráfico,
- Eliminación de un objeto gráfico junto con la ventana gráfica.
Estos son los eventos que implementaremos hoy. Estos serán enviados al manejador OnChartEvent(). En el próximo artículo, crearemos un manejador para cada uno de estos eventos de manera que podamos saber exactamente qué propiedades del objeto gráfico han sido modificadas.
Debemos tener en cuenta que el cambio en el nombre de un objeto gráfico supone a su vez un cambio en las propiedades del objeto gráfico, es decir, el cambio de la propiedad "Nombre". No obstante, hemos decidido convertirlo en un evento aparte para simplificar el procesamiento, ya que el cambio de nombre desencadena varios eventos seguidos: la eliminación de un objeto gráfico, la creación de uno nuevo y el cambio de sus propiedades. Todos los estados mencionados ya los hemos procesado en la biblioteca, y esta calcula correctamente el evento de nuevo nombramiento. Por lo tanto, lo enviaremos para el posterior procesamiento programático del evento.
Mientras que la eliminación de un objeto gráfico nos permite averiguar exactamente qué objeto se ha eliminado e informar sobre su nombre, al eliminar una ventana de gráfico con objetos gráficos en ella, en cambio, solo podremos conocer el número de objetos gráficos en el gráfico eliminado en este momento. Por el momento, estos dos eventos solo nos comunicarán que los objetos gráficos han sido eliminados. En el futuro, tal vez consideremos la utilidad de recordar las propiedades de los objetos eliminados. Por ahora, no vemos necesidad urgente de recordar las propiedades de los objetos eliminados.
Mejorando las clases de la biblioteca
Para monitorear la selección de un objeto gráfico, deberemos añadir el procesamiento de eventos de clic en el objeto gráfico. Clicando dos veces en un objeto gráfico no seleccionado, se elegirá para su edición, y clicando dos veces en el objeto seleccionado, este se deseleccionará. Para procesar un evento de este tipo en el gráfico actual, basta con añadir al manejador de eventos un nuevo ID de evento; sin embargo, para procesar un evento de este tipo en los demás gráficos, deberemos mejorar el indicador de control de eventos que hemos establecido automáticamente para cada ventana de gráfico recién abierta.
Vamos a abrir el archivo de este indicador \MQL5\IndicadoresDoEasy\EventControl.mq5 y añadir el nuevo ID del evento que será enviado al gráfico del programa de control:
//+------------------------------------------------------------------+ //| EventControl.mq5 | //| 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 indicator_chart_window #property indicator_plots 0 //--- input parameters input long InpChartSRC = 0; input long InpChartDST = 0; //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- indicator shortname IndicatorSetString(INDICATOR_SHORTNAME,"EventSend_From#"+(string)InpChartSRC+"_To#"+(string)InpChartDST); //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ int OnCalculate(const int rates_total, const int prev_calculated, const datetime &time[], const double &open[], const double &high[], const double &low[], const double &close[], const long &tick_volume[], const long &volume[], const int &spread[]) { return rates_total; } //+------------------------------------------------------------------+ //| ChartEvent function | //+------------------------------------------------------------------+ void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { //--- if(id==CHARTEVENT_OBJECT_CHANGE || id==CHARTEVENT_OBJECT_DRAG || id==CHARTEVENT_OBJECT_CLICK) { EventChartCustom(InpChartDST,(ushort)id,InpChartSRC,dparam,sparam); } } //+------------------------------------------------------------------+
En el archivo \MQL5\Include\DoEasy\Data.mqh, escribimos los índices de los nuevos mensajes:
//--- 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_ADDED_EVN_CTRL_INDICATOR, // Indicator for controlling and sending events successfully added to the chart MSG_GRAPH_OBJ_FAILED_ADD_EVN_CTRL_INDICATOR, // Failed to add 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: MSG_GRAPH_OBJ_FAILED_CREATE_EVN_OBJ, // Failed to create the event object for a graphical object MSG_GRAPH_OBJ_FAILED_ADD_EVN_OBJ, // Failed to add the event object to the list MSG_GRAPH_OBJ_EVN_GRAPH_OBJ_CREATE, // New graphical object created MSG_GRAPH_OBJ_EVN_GRAPH_OBJ_CHANGE, // Changed the graphical object property MSG_GRAPH_OBJ_EVN_GRAPH_OBJ_RENAME, // Graphical object renamed MSG_GRAPH_OBJ_EVN_GRAPH_OBJ_DELETE, // Graphical object removed MSG_GRAPH_OBJ_EVN_GRAPH_OBJ_DEL_CHART, // Graphical object removed together with the chart }; //+------------------------------------------------------------------+
y los textos de los mensajes que se corresponden con los índices nuevamente añadidos:
//--- 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"}, {"Индикатор контроля и отправки событий успешно добавлен на график ","The indicator for monitoring and sending events has been successfully added to the chart"}, {"Не удалось добавить индикатор контроля и отправки событий на график ","Failed to add the indicator of monitoring and sending events to the chart "}, {"Закрыто окон графиков: ","Closed chart windows: "}, {"С ними удалено объектов: ","Objects removed with them: "}, {"Не удалось создать объект-событие для графического объекта","Failed to create event object for graphic object"}, {"Не удалось добавить объект-событие в список","Failed to add event object to list"}, {"Создан новый графический объект","New graphic object created"}, {"Изменено свойство графического объекта","Changed graphic object property"}, {"Графический объект переименован","Graphic object renamed"}, {"Графический объект удалён","Graphic object deleted"}, {"Графический объект удалён вместе с графиком","The graphic object has been removed along with the chart"}, }; //+---------------------------------------------------------------------+
Introducimos las correciones en el archivo \MQL5\Include\DoEasy\Defines.mqh.
De la lista de posibles eventos de objetos gráficos, eliminamos el evento de desplazamiento del objeto, ya que este cambio de propiedad está ya presente en la enumeración de eventos de objetos gráficos:
//+------------------------------------------------------------------+ //| List of possible graphical object events | //+------------------------------------------------------------------+ enum ENUM_GRAPH_OBJ_EVENT { GRAPH_OBJ_EVENT_NO_EVENT = CHART_OBJ_EVENTS_NEXT_CODE,// No event GRAPH_OBJ_EVENT_CREATE, // "Creating a new graphical object" event GRAPH_OBJ_EVENT_CHANGE, // "Changing graphical object properties" event GRAPH_OBJ_EVENT_MOVE, // "Moving graphical object" event GRAPH_OBJ_EVENT_RENAME, // "Renaming graphical object" event GRAPH_OBJ_EVENT_DELETE, // "Removing graphical object" event };
Y añadimos una nueva constante a esta lista para el evento de eliminación de un objeto gráfico junto con el gráfico:
//+------------------------------------------------------------------+ //| Data for handling graphical elements | //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| List of possible graphical object events | //+------------------------------------------------------------------+ enum ENUM_GRAPH_OBJ_EVENT { GRAPH_OBJ_EVENT_NO_EVENT = CHART_OBJ_EVENTS_NEXT_CODE,// No event GRAPH_OBJ_EVENT_CREATE, // "Creating a new graphical object" event GRAPH_OBJ_EVENT_CHANGE, // "Changing graphical object properties" event GRAPH_OBJ_EVENT_RENAME, // "Renaming graphical object" event GRAPH_OBJ_EVENT_DELETE, // "Removing graphical object" event GRAPH_OBJ_EVENT_DEL_CHART, // "Removing a graphical object together with the chart window" event }; #define GRAPH_OBJ_EVENTS_NEXT_CODE (GRAPH_OBJ_EVENT_DEL_CHART+1) // The code of the next event after the last graphical object event code //+------------------------------------------------------------------+
El siguiente código de evento después de los códigos de evento de los objetos gráficos se calculará ahora a partir del último valor añadido a la enumeración.
Al crear cualquier objeto gráfico estándar, leeremos en su constructor los datos del objeto físico del gráfico y los escribiremos en las propiedades del objeto de la clase. Algunas de las propiedades de los objetos gráficos las hemos escrito como comunes para todos los objetos gráficos: cada objeto las posee, y están escritas en la clase básica de todos los objetos gráficos de la biblioteca.
El algoritmo es el siguiente: primero leemos los parámetros de un objeto gráfico usando las funciones estándar ObjectGetXXX (Integer, Double y String) y luego, si los datos se han obtenido con éxito, estos parámetros se escribirán primero en la propiedad del objeto de clase del objeto gráfico básico CGBaseObj, y luego en las propiedades de su descendiente. Para los objetos gráficos estándar es la clase CGStdGraphObj.
Aquí es donde nos encontramos con un conflicto. Disponemos de métodos para establecer los parámetros del objeto gráfico. Estos métodos deben asignar el valor de la propiedad que se les transmite tanto al propio objeto gráfico como a la propiedad correspondiente del objeto de clase, si asignamos con éxito un parámetro a un objeto gráfico usando las funciones ObjectSetXXX (Integer, Double y String). Sin embargo, a veces solo necesitamos establecer en una propiedad de la clase un valor ya conocido del parámetro de un objeto gráfico. Y para ello, no necesitamos volver a leer ese valor ni asignarlo a un objeto gráfico, sino simplemente asignar su valor a una variable de la clase. Nuestros métodos Set del objeto gráfico básico de la biblioteca primero establecen el valor en los parámetros del objeto gráfico, y solo entonces los escriben en las variables de la clase. Para ahorrarnos la tarea innecesaria de establecer de nuevo una propiedad ya conocida del objeto gráfico en el objeto, y solo registrarla en la variable de la clase, vamos a añadir a tales métodos la variable bool only_prop, que indicará la necesidad de establecer un valor ya sea solo en los valores de las variables, o bien tanto en los parámetros del objeto gráfico, como en las propiedades del objeto de la clase. Si esta variable de entrada es true, los parámetros se escribirán solo en las variables de clase; de lo contrario, el valor se establecerá primero en el objeto gráfico y luego en las variables de la clase.
En la clase objeto básico de todos los objetos gráficos de la biblioteca \MQL5\Include\DoEasy\Objects\Graph\GBaseObj.mqh, vamos a añadir esta variable a dichos métodos y a cambiar la lógica de los mismos:
//--- Set the "Background object" flag bool SetFlagBack(const bool flag,const bool only_prop) { ::ResetLastError(); if((!only_prop && ::ObjectSetInteger(this.m_chart_id,this.m_name,OBJPROP_BACK,flag)) || only_prop) { this.m_back=flag; return true; } else CMessage::ToLog(DFUN,::GetLastError(),true); return false; }
Aquí, si la variable tiene el valor false y al objeto gráfico se le ha asignado la propiedad, o si la variable tiene el valor true,
añadimos el valor transmitido al método en la variable de clase .
Todos los métodos de esta clase se han mejorado de la forma siguiente:
//--- Set the "Background object" flag bool SetFlagBack(const bool flag,const bool only_prop) { ::ResetLastError(); if((!only_prop && ::ObjectSetInteger(this.m_chart_id,this.m_name,OBJPROP_BACK,flag)) || only_prop) { this.m_back=flag; return true; } else CMessage::ToLog(DFUN,::GetLastError(),true); return false; } //--- Set the "Object selection" flag bool SetFlagSelected(const bool flag,const bool only_prop) { ::ResetLastError(); if((!only_prop && ::ObjectSetInteger(this.m_chart_id,this.m_name,OBJPROP_SELECTED,flag)) || only_prop) { this.m_selected=flag; return true; } else CMessage::ToLog(DFUN,::GetLastError(),true); return false; } //--- Set the "Object selection" flag bool SetFlagSelectable(const bool flag,const bool only_prop) { ::ResetLastError(); if((!only_prop && ::ObjectSetInteger(this.m_chart_id,this.m_name,OBJPROP_SELECTABLE,flag)) || only_prop) { this.m_selectable=flag; return true; } else CMessage::ToLog(DFUN,::GetLastError(),true); return false; } //--- Set the "Disable displaying the name of a graphical object in the terminal object list" flag bool SetFlagHidden(const bool flag,const bool only_prop) { ::ResetLastError(); if((!only_prop && ::ObjectSetInteger(this.m_chart_id,this.m_name,OBJPROP_SELECTABLE,flag)) || only_prop) { this.m_hidden=flag; return true; } else CMessage::ToLog(DFUN,::GetLastError(),true); return false; } //--- Set the priority of a graphical object for receiving the event of clicking on a chart bool SetZorder(const long value,const bool only_prop) { ::ResetLastError(); if((!only_prop && ::ObjectSetInteger(this.m_chart_id,this.m_name,OBJPROP_ZORDER,value)) || only_prop) { this.m_zorder=value; return true; } else CMessage::ToLog(DFUN,::GetLastError(),true); return false; } //--- Set object visibility on all timeframes bool SetVisible(const bool flag,const bool only_prop) { long value=(flag ? OBJ_ALL_PERIODS : OBJ_NO_PERIODS); ::ResetLastError(); if((!only_prop && ::ObjectSetInteger(this.m_chart_id,this.m_name,OBJPROP_TIMEFRAMES,value)) || only_prop) { this.m_visible=flag; return true; } else CMessage::ToLog(DFUN,::GetLastError(),true); return false; } //--- Set visibility flags on timeframes specified as flags bool SetVisibleOnTimeframes(const int flags,const bool only_prop) { ::ResetLastError(); if((!only_prop && ::ObjectSetInteger(this.m_chart_id,this.m_name,OBJPROP_TIMEFRAMES,flags)) || only_prop) { this.m_timeframes_visible=flags; return true; } else CMessage::ToLog(DFUN,::GetLastError(),true); return false; } //--- Add the visibility flag on a specified timeframe bool SetVisibleOnTimeframe(const ENUM_TIMEFRAMES timeframe,const bool only_prop) { int flags=this.m_timeframes_visible; switch(timeframe) { case PERIOD_M1 : flags |= OBJ_PERIOD_M1; break; case PERIOD_M2 : flags |= OBJ_PERIOD_M2; break; case PERIOD_M3 : flags |= OBJ_PERIOD_M3; break; case PERIOD_M4 : flags |= OBJ_PERIOD_M4; break; case PERIOD_M5 : flags |= OBJ_PERIOD_M5; break; case PERIOD_M6 : flags |= OBJ_PERIOD_M6; break; case PERIOD_M10 : flags |= OBJ_PERIOD_M10; break; case PERIOD_M12 : flags |= OBJ_PERIOD_M12; break; case PERIOD_M15 : flags |= OBJ_PERIOD_M15; break; case PERIOD_M20 : flags |= OBJ_PERIOD_M20; break; case PERIOD_M30 : flags |= OBJ_PERIOD_M30; break; case PERIOD_H1 : flags |= OBJ_PERIOD_H1; break; case PERIOD_H2 : flags |= OBJ_PERIOD_H2; break; case PERIOD_H3 : flags |= OBJ_PERIOD_H3; break; case PERIOD_H4 : flags |= OBJ_PERIOD_H4; break; case PERIOD_H6 : flags |= OBJ_PERIOD_H6; break; case PERIOD_H8 : flags |= OBJ_PERIOD_H8; break; case PERIOD_H12 : flags |= OBJ_PERIOD_H12; break; case PERIOD_D1 : flags |= OBJ_PERIOD_D1; break; case PERIOD_W1 : flags |= OBJ_PERIOD_W1; break; case PERIOD_MN1 : flags |= OBJ_PERIOD_MN1; break; default : return true; } ::ResetLastError(); if((!only_prop && ::ObjectSetInteger(this.m_chart_id,this.m_name,OBJPROP_TIMEFRAMES,flags)) || only_prop) { this.m_timeframes_visible=flags; return true; } else CMessage::ToLog(DFUN,::GetLastError(),true); return false; }
Ahora podemos elegir cómo escribir la propiedad: o bien asignamos una nueva propiedad al objeto gráfico y a la variable de clase (para que se corresponda con el nuevo valor establecido), o bien asignamos el valor ya conocido del parámetro del objeto gráfico a la variable de clase (como se hace en el constructor de la clase al crear un nuevo objeto gráfico). Esto nos permitirá evitar referencias innecesarias a funciones bastante lentas de trabajo con objetos gráficos, debido a su naturaleza sincrónica.
En el fichero \MQL5\Include\DoEasy\Objects\Graph\GCnvElement.mqh, hemos corregido la llamada a los métodos mejorados:
//--- Return (1) the element ID, (2) element index in the list, (3) flag of the form shadow presence and (4) the chart background color int ID(void) const { return (int)this.GetProperty(CANV_ELEMENT_PROP_ID); } int Number(void) const { return (int)this.GetProperty(CANV_ELEMENT_PROP_NUM); } bool IsShadow(void) const { return this.m_shadow; } color ChartColorBackground(void) const { return this.m_chart_color_bg; } //--- Set the object above all void BringToTop(void) { CGBaseObj::SetVisible(false,false); CGBaseObj::SetVisible(true,false);} //--- (1) Show and (2) hide the element virtual void Show(void) { CGBaseObj::SetVisible(true,false); } virtual void Hide(void) { CGBaseObj::SetVisible(false,false); } //+------------------------------------------------------------------+ //| The methods of receiving raster data | //+------------------------------------------------------------------+
En el archivo \MQL5\Include\DoEasy\Objects\Graph\Form.mqh, corregimos igualmente los dos métodos:
//+------------------------------------------------------------------+ //| Create the shadow object | //+------------------------------------------------------------------+ void CForm::CreateShadowObj(const color colour,const uchar opacity) { //--- If the shadow flag is disabled or the shadow object already exists, exit if(!this.m_shadow || this.m_shadow_obj!=NULL) return; //--- Calculate the shadow object coordinates according to the offset from the top and left int x=this.CoordX()-OUTER_AREA_SIZE; int y=this.CoordY()-OUTER_AREA_SIZE; //--- Calculate the width and height in accordance with the top, bottom, left and right offsets int w=this.Width()+OUTER_AREA_SIZE*2; int h=this.Height()+OUTER_AREA_SIZE*2; //--- Create a new shadow object and set the pointer to it in the variable this.m_shadow_obj=new CShadowObj(this.ChartID(),this.SubWindow(),this.CreateNameDependentObject("Shadow"),x,y,w,h); if(this.m_shadow_obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_FORM_OBJECT_ERR_FAILED_CREATE_SHADOW_OBJ)); return; } //--- Set the properties for the created shadow object this.m_shadow_obj.SetID(this.ID()); this.m_shadow_obj.SetNumber(-1); this.m_shadow_obj.SetOpacityShadow(opacity); this.m_shadow_obj.SetColorShadow(colour); this.m_shadow_obj.SetMovable(true); this.m_shadow_obj.SetActive(false); this.m_shadow_obj.SetVisible(false,false); //--- Move the form object to the foreground this.BringToTop(); } //+------------------------------------------------------------------+ //| Draw the shadow | //+------------------------------------------------------------------+ void CForm::DrawShadow(const int shift_x,const int shift_y,const color colour,const uchar opacity=127,const uchar blur=4) { //--- If the shadow flag is disabled, exit if(!this.m_shadow) return; //--- If there is no shadow object, create it if(this.m_shadow_obj==NULL) this.CreateShadowObj(colour,opacity); //--- If the shadow object exists, draw the shadow on it, //--- set the shadow object visibility flag and //--- move the form object to the foreground if(this.m_shadow_obj!=NULL) { this.m_shadow_obj.DrawShadow(shift_x,shift_y,blur); this.m_shadow_obj.SetVisible(true,false); this.BringToTop(); } } //+------------------------------------------------------------------+
En la clase de objeto gráfico estándar, hacemos que todos los métodos para establecer las propiedades de los objetos gráficos tengan un valor de retorno de tipo booleano (actualmente los métodos no retornan ningún valor y, por lo tanto, son de tipo void). Esto es necesario para asegurarnos de que los métodos correspondientes de la clase padre que también tienen el tipo de retorno bool han sido reasignados en la clase hija, y para que al escribir el código no haya dos métodos en una pista (con el tipo bool del objeto básico, y con el tipo void de su descendiente), lo cual podría provocar confusiones.
Vamos a analizar el método para establecer el número de subventana:
//--- Chart subwindow index int SubWindow(void) const { return (int)this.GetProperty(GRAPH_OBJ_PROP_WND_NUM,0); } bool SetSubWindow(void) { if(!CGBaseObj::SetSubwindow(CGBaseObj::ChartID(),CGBaseObj::Name())) return false; this.SetProperty(GRAPH_OBJ_PROP_WND_NUM,0,CGBaseObj::SubWindow()); return true; }
Como el número de subventana se busca según el identificador del gráfico y el nombre del objeto, entonces transferiremos al método de la clase básica para establecer el número de subventana el identificador del gráfico ya almacenado en el objeto básico y el nombre del objeto gráfico. Si no se ha conseguido establecer un valor en el objeto básico, retornaremos false; en caso contrario, estableceremos en la propiedad del objeto el valor que se acaba de escribir en el objeto básico, y retornaremos true.
Y en el ejemplo del método para establecer la visibilidad de un objeto en todos los marcos temporales:
//--- Object visibility on timeframes bool Visible(void) const { return (bool)this.GetProperty(GRAPH_OBJ_PROP_TIMEFRAMES,0); } bool SetFlagVisible(const bool flag,const bool only_prop) { if(!CGBaseObj::SetVisible(flag,only_prop)) return false; this.SetProperty(GRAPH_OBJ_PROP_TIMEFRAMES,0,flag); return true; }
Aquí, transmitimos el nuevo parámetro al método que hemos añadido antes para especificar en qué valores escribiremos: solo en la propiedad del objeto, o en los parámetros del objeto gráfico y en las propiedades de la clase. A continuación, establecemos estas propiedades en el objeto básico, y si no ha sido posible, retornaremos false.
De lo contrario, escribiremos el valor en la propiedad del objeto de la clase y retornaremos true.
Todos los demás métodos han sido mejorados de forma idéntica a los anteriores:
//--- Background object bool Back(void) const { return (bool)this.GetProperty(GRAPH_OBJ_PROP_BACK,0); } bool SetFlagBack(const bool flag,const bool only_prop) { if(!CGBaseObj::SetFlagBack(flag,only_prop)) return false; this.SetProperty(GRAPH_OBJ_PROP_BACK,0,flag); return true; } //--- Priority of a graphical object for receiving the event of clicking on a chart long Zorder(void) const { return this.GetProperty(GRAPH_OBJ_PROP_ZORDER,0); } bool SetZorder(const long value,const bool only_prop) { if(!CGBaseObj::SetZorder(value,only_prop)) return false; this.SetProperty(GRAPH_OBJ_PROP_ZORDER,0,value); return true; } //--- Disable displaying the name of a graphical object in the terminal object list bool Hidden(void) const { return (bool)this.GetProperty(GRAPH_OBJ_PROP_HIDDEN,0); } bool SetFlagHidden(const bool flag,const bool only_prop) { if(!CGBaseObj::SetFlagHidden(flag,only_prop)) return false; this.SetProperty(GRAPH_OBJ_PROP_HIDDEN,0,flag); return true; } //--- Object selection bool Selected(void) const { return (bool)this.GetProperty(GRAPH_OBJ_PROP_SELECTED,0); } bool SetFlagSelected(const bool flag,const bool only_prop) { if(!CGBaseObj::SetFlagSelected(flag,only_prop)) return false; this.SetProperty(GRAPH_OBJ_PROP_SELECTED,0,flag); return true; } //--- Object availability bool Selectable(void) const { return (bool)this.GetProperty(GRAPH_OBJ_PROP_SELECTABLE,0); } bool SetFlagSelectable(const bool flag,const bool only_prop) { if(!CGBaseObj::SetFlagSelectable(flag,only_prop)) return false; this.SetProperty(GRAPH_OBJ_PROP_SELECTABLE,0,flag); return true; } //--- Time coordinate datetime Time(const int modifier) const { return (datetime)this.GetProperty(GRAPH_OBJ_PROP_TIME,modifier); } bool SetTime(const datetime time,const int modifier) { if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_TIME,modifier,time)) return false; this.SetProperty(GRAPH_OBJ_PROP_TIME,modifier,time); return true; } //--- Color color Color(void) const { return (color)this.GetProperty(GRAPH_OBJ_PROP_COLOR,0); } bool SetColor(const color colour) { if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_COLOR,colour)) return false; this.SetProperty(GRAPH_OBJ_PROP_COLOR,0,colour); return true; } //--- Style ENUM_LINE_STYLE Style(void) const { return (ENUM_LINE_STYLE)this.GetProperty(GRAPH_OBJ_PROP_STYLE,0); } bool SetStyle(const ENUM_LINE_STYLE style) { if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_STYLE,style)) return false; this.SetProperty(GRAPH_OBJ_PROP_STYLE,0,style); return true; } //--- Line width int Width(void) const { return (int)this.GetProperty(GRAPH_OBJ_PROP_WIDTH,0); } bool SetWidth(const int width) { if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_WIDTH,width)) return false; this.SetProperty(GRAPH_OBJ_PROP_WIDTH,0,width); return true; } //--- Object color filling bool Fill(void) const { return (bool)this.GetProperty(GRAPH_OBJ_PROP_FILL,0); } bool SetFlagFill(const bool flag) { if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_FILL,flag)) return false; this.SetProperty(GRAPH_OBJ_PROP_FILL,0,flag); return true; } //--- Ability to edit text in the Edit object bool ReadOnly(void) const { return (bool)this.GetProperty(GRAPH_OBJ_PROP_READONLY,0); } bool SetFlagReadOnly(const bool flag) { if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_READONLY,flag)) return false; this.SetProperty(GRAPH_OBJ_PROP_READONLY,0,flag); return true; } //--- Number of levels int Levels(void) const { return (int)this.GetProperty(GRAPH_OBJ_PROP_LEVELS,0); } bool SetLevels(const int levels) { if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_LEVELS,levels)) return false; this.SetProperty(GRAPH_OBJ_PROP_LEVELS,0,levels); return true; } //--- Line level color color LevelColor(const int modifier) const { return (color)this.GetProperty(GRAPH_OBJ_PROP_LEVELCOLOR,modifier); } bool SetLevelColor(const color colour,const int modifier) { if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_LEVELCOLOR,modifier,colour)) return false; this.SetProperty(GRAPH_OBJ_PROP_LEVELCOLOR,modifier,colour); return true; } //--- Level line style ENUM_LINE_STYLE LevelStyle(const int modifier)const { return (ENUM_LINE_STYLE)this.GetProperty(GRAPH_OBJ_PROP_LEVELSTYLE,modifier); } bool SetLevelStyle(const ENUM_LINE_STYLE style,const int modifier) { if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_LEVELSTYLE,modifier,style)) return false; this.SetProperty(GRAPH_OBJ_PROP_LEVELSTYLE,modifier,style); return true; } ///--- Level line width int LevelWidth(const int modifier)const { return (int)this.GetProperty(GRAPH_OBJ_PROP_LEVELWIDTH,modifier); } bool SetLevelWidth(const int width,const int modifier) { if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_LEVELWIDTH,modifier,width)) return false; this.SetProperty(GRAPH_OBJ_PROP_LEVELWIDTH,modifier,width); return true; } //--- Horizontal text alignment in the Edit object (OBJ_EDIT) ENUM_ALIGN_MODE Align(void) const { return (ENUM_ALIGN_MODE)this.GetProperty(GRAPH_OBJ_PROP_ALIGN,0); } bool SetAlign(const ENUM_ALIGN_MODE align) { if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_ALIGN,align)) return false; this.SetProperty(GRAPH_OBJ_PROP_ALIGN,0,align); return true; } //--- Font size int FontSize(void) const { return (int)this.GetProperty(GRAPH_OBJ_PROP_FONTSIZE,0); } bool SetFontSize(const int size) { if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_FONTSIZE,size)) return false; this.SetProperty(GRAPH_OBJ_PROP_FONTSIZE,0,size); return true; } //--- Ray goes to the left bool RayLeft(void) const { return (bool)this.GetProperty(GRAPH_OBJ_PROP_RAY_LEFT,0); } bool SetFlagRayLeft(const bool flag) { if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_RAY_LEFT,flag)) return false; this.SetProperty(GRAPH_OBJ_PROP_RAY_LEFT,0,flag); return true; } //--- Ray goes to the right bool RayRight(void) const { return (bool)this.GetProperty(GRAPH_OBJ_PROP_RAY_RIGHT,0); } bool SetFlagRayRight(const bool flag) { if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_RAY_RIGHT,flag)) return false; this.SetProperty(GRAPH_OBJ_PROP_RAY_RIGHT,0,flag); return true; } //--- Vertical line goes through all windows of a chart bool Ray(void) const { return (bool)this.GetProperty(GRAPH_OBJ_PROP_RAY,0); } bool SetFlagRay(const bool flag) { if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_RAY,flag)) return false; this.SetProperty(GRAPH_OBJ_PROP_RAY,0,flag); return true; } //--- Display the full ellipse of the Fibonacci Arc object bool Ellipse(void) const { return (bool)this.GetProperty(GRAPH_OBJ_PROP_ELLIPSE,0); } bool SetFlagEllipse(const bool flag) { if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_ELLIPSE,flag)) return false; this.SetProperty(GRAPH_OBJ_PROP_ELLIPSE,0,flag); return true; } //--- Arrow code for the "Arrow" object uchar ArrowCode(void) const { return (uchar)this.GetProperty(GRAPH_OBJ_PROP_ARROWCODE,0); } bool SetArrowCode(const uchar code) { if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_ARROWCODE,code)) return false; this.SetProperty(GRAPH_OBJ_PROP_ARROWCODE,0,code); return true; } //--- Position of the graphical object anchor point int Anchor(void) const { return (int)this.GetProperty(GRAPH_OBJ_PROP_ANCHOR,0); } bool SetAnchor(const int anchor) { if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_ANCHOR,anchor)) return false; this.SetProperty(GRAPH_OBJ_PROP_ANCHOR,0,anchor); return true; } //--- Distance from the base corner along the X axis in pixels int XDistance(void) const { return (int)this.GetProperty(GRAPH_OBJ_PROP_XDISTANCE,0); } bool SetXDistance(const int distance) { if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_XDISTANCE,distance)) return false; this.SetProperty(GRAPH_OBJ_PROP_XDISTANCE,0,distance); return true; } //--- Distance from the base corner along the Y axis in pixels int YDistance(void) const { return (int)this.GetProperty(GRAPH_OBJ_PROP_YDISTANCE,0); } bool SetYDistance(const int distance) { if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_YDISTANCE,distance)) return false; this.SetProperty(GRAPH_OBJ_PROP_YDISTANCE,0,distance); return true; } //--- Gann object trend ENUM_GANN_DIRECTION Direction(void) const { return (ENUM_GANN_DIRECTION)this.GetProperty(GRAPH_OBJ_PROP_DIRECTION,0); } bool SetDirection(const ENUM_GANN_DIRECTION direction) { if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_DIRECTION,direction)) return false; this.SetProperty(GRAPH_OBJ_PROP_DIRECTION,0,direction); return true; } //--- Elliott wave marking level ENUM_ELLIOT_WAVE_DEGREE Degree(void) const { return (ENUM_ELLIOT_WAVE_DEGREE)this.GetProperty(GRAPH_OBJ_PROP_DEGREE,0); } bool SetDegree(const ENUM_ELLIOT_WAVE_DEGREE degree) { if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_DEGREE,degree)) return false; this.SetProperty(GRAPH_OBJ_PROP_DEGREE,0,degree); return true; } //--- Display lines for Elliott wave marking bool DrawLines(void) const { return (bool)this.GetProperty(GRAPH_OBJ_PROP_DRAWLINES,0); } bool SetFlagDrawLines(const bool flag) { if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_DRAWLINES,flag)) return false; this.SetProperty(GRAPH_OBJ_PROP_DRAWLINES,0,flag); return true; } //--- Button state (pressed/released) bool State(void) const { return (bool)this.GetProperty(GRAPH_OBJ_PROP_STATE,0); } bool SetFlagState(const bool flag) { if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_STATE,flag)) return false; this.SetProperty(GRAPH_OBJ_PROP_STATE,0,flag); return true; } //--- Chart object ID (OBJ_CHART) long ChartObjChartID(void) const { return this.GetProperty(GRAPH_OBJ_PROP_CHART_OBJ_CHART_ID,0); } bool SetChartObjChartID(const long chart_id) { this.SetProperty(GRAPH_OBJ_PROP_CHART_OBJ_CHART_ID,0,chart_id); return true; } //--- Chart object period ENUM_TIMEFRAMES ChartObjPeriod(void) const { return (ENUM_TIMEFRAMES)this.GetProperty(GRAPH_OBJ_PROP_CHART_OBJ_PERIOD,0); } bool SetChartObjPeriod(const ENUM_TIMEFRAMES timeframe) { if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_PERIOD,timeframe)) return false; this.SetProperty(GRAPH_OBJ_PROP_CHART_OBJ_PERIOD,0,timeframe); return true; } //--- Time scale display flag for the Chart object bool ChartObjDateScale(void) const { return (bool)this.GetProperty(GRAPH_OBJ_PROP_CHART_OBJ_DATE_SCALE,0); } bool SetFlagChartObjDateScale(const bool flag) { if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_DATE_SCALE,flag)) return false; this.SetProperty(GRAPH_OBJ_PROP_CHART_OBJ_DATE_SCALE,0,flag); return true; } //--- Price scale display flag for the Chart object bool ChartObjPriceScale(void) const { return (bool)this.GetProperty(GRAPH_OBJ_PROP_CHART_OBJ_PRICE_SCALE,0); } bool SetFlagPriceScale(const bool flag) { if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_PRICE_SCALE,flag)) return false; this.SetProperty(GRAPH_OBJ_PROP_CHART_OBJ_PRICE_SCALE,0,flag); return true; } //--- Chart object scale int ChartObjChartScale(void) const { return (int)this.GetProperty(GRAPH_OBJ_PROP_CHART_OBJ_CHART_SCALE,0); } bool SetChartObjChartScale(const int scale) { if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_CHART_SCALE,scale)) return false; this.SetProperty(GRAPH_OBJ_PROP_CHART_OBJ_CHART_SCALE,0,scale); return true; } //--- Object width along the X axis in pixels int XSize(void) const { return (int)this.GetProperty(GRAPH_OBJ_PROP_XSIZE,0); } bool SetXSize(const int size) { if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_XSIZE,size)) return false; this.SetProperty(GRAPH_OBJ_PROP_XSIZE,0,size); return true; } //--- Object height along the Y axis in pixels int YSize(void) const { return (int)this.GetProperty(GRAPH_OBJ_PROP_YSIZE,0); } bool SetYSize(const int size) { if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_YSIZE,size)) return false; this.SetProperty(GRAPH_OBJ_PROP_YSIZE,0,size); return true; } //--- X coordinate of the upper-left corner of the visibility area int XOffset(void) const { return (int)this.GetProperty(GRAPH_OBJ_PROP_XOFFSET,0); } bool SetXOffset(const int offset) { if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_XOFFSET,offset)) return false; this.SetProperty(GRAPH_OBJ_PROP_XOFFSET,0,offset); return true; } //--- Y coordinate of the upper-left corner of the visibility area int YOffset(void) const { return (int)this.GetProperty(GRAPH_OBJ_PROP_YOFFSET,0); } bool SetYOffset(const int offset) { if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_YOFFSET,offset)) return false; this.SetProperty(GRAPH_OBJ_PROP_YOFFSET,0,offset); return true; } //--- Background color for OBJ_EDIT, OBJ_BUTTON, OBJ_RECTANGLE_LABEL color BGColor(void) const { return (color)this.GetProperty(GRAPH_OBJ_PROP_BGCOLOR,0); } bool SetBGColor(const color colour) { if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_BGCOLOR,colour)) return false; this.SetProperty(GRAPH_OBJ_PROP_BGCOLOR,0,colour); return true; } //--- Chart corner for attaching a graphical object ENUM_BASE_CORNER Corner(void) const { return (ENUM_BASE_CORNER)this.GetProperty(GRAPH_OBJ_PROP_CORNER,0); } bool SetCorner(const ENUM_BASE_CORNER corner) { if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_CORNER,corner)) return false; this.SetProperty(GRAPH_OBJ_PROP_CORNER,0,corner); return true; } //--- Border type for the Rectangle label object ENUM_BORDER_TYPE BorderType(void) const { return (ENUM_BORDER_TYPE)this.GetProperty(GRAPH_OBJ_PROP_BORDER_TYPE,0); } bool SetBorderType(const ENUM_BORDER_TYPE type) { if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_BORDER_TYPE,type)) return false; this.SetProperty(GRAPH_OBJ_PROP_BORDER_TYPE,0,type); return true; } //--- Border color for OBJ_EDIT and OBJ_BUTTON color BorderColor(void) const { return (color)this.GetProperty(GRAPH_OBJ_PROP_BORDER_COLOR,0); } bool SetBorderColor(const color colour) { if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_BORDER_COLOR,colour)) return false; this.SetProperty(GRAPH_OBJ_PROP_BORDER_COLOR,0,colour); return true; } //--- Price coordinate double Price(const int modifier) const { return this.GetProperty(GRAPH_OBJ_PROP_PRICE,modifier); } bool SetPrice(const double price,const int modifier) { if(!::ObjectSetDouble(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_PRICE,modifier,price)) return false; this.SetProperty(GRAPH_OBJ_PROP_PRICE,modifier,price); return true; } //--- Level value double LevelValue(const int modifier)const { return this.GetProperty(GRAPH_OBJ_PROP_LEVELVALUE,modifier); } bool SetLevelValue(const double value,const int modifier) { if(!::ObjectSetDouble(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_LEVELVALUE,modifier,value)) return false; this.SetProperty(GRAPH_OBJ_PROP_LEVELVALUE,modifier,value); return true; } //--- Scale double Scale(void) const { return this.GetProperty(GRAPH_OBJ_PROP_SCALE,0); } bool SetScale(const double scale) { if(!::ObjectSetDouble(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_SCALE,scale)) return false; this.SetProperty(GRAPH_OBJ_PROP_SCALE,0,scale); return true; } //--- Angle double Angle(void) const { return this.GetProperty(GRAPH_OBJ_PROP_ANGLE,0); } bool SetAngle(const double angle) { if(!::ObjectSetDouble(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_ANGLE,angle)) return false; this.SetProperty(GRAPH_OBJ_PROP_ANGLE,0,angle); return true; } //--- Deviation of the standard deviation channel double Deviation(void) const { return this.GetProperty(GRAPH_OBJ_PROP_DEVIATION,0); } bool SetDeviation(const double deviation) { if(!::ObjectSetDouble(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_DEVIATION,deviation)) return false; this.SetProperty(GRAPH_OBJ_PROP_DEVIATION,0,deviation); return true; } //--- Object name string Name(void) const { return this.GetProperty(GRAPH_OBJ_PROP_NAME,0); } bool SetName(const string name) { if(CGBaseObj::Name()==name) return true; if(CGBaseObj::Name()=="") { CGBaseObj::SetName(name); this.SetProperty(GRAPH_OBJ_PROP_NAME,0,name); return true; } else { if(!::ObjectSetString(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_NAME,name)) return false; CGBaseObj::SetName(name); this.SetProperty(GRAPH_OBJ_PROP_NAME,0,name); return true; } } //--- Object description (text contained in the object) string Text(void) const { return this.GetProperty(GRAPH_OBJ_PROP_TEXT,0); } bool SetText(const string text) { if(!::ObjectSetString(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_TEXT,text)) return false; this.SetProperty(GRAPH_OBJ_PROP_TEXT,0,text); return true; } //--- Tooltip text string Tooltip(void) const { return this.GetProperty(GRAPH_OBJ_PROP_TOOLTIP,0); } bool SetTooltip(const string tooltip) { if(!::ObjectSetString(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_TOOLTIP,tooltip)) return false; this.SetProperty(GRAPH_OBJ_PROP_TOOLTIP,0,tooltip); return true; } //--- Level description string LevelText(const int modifier) const { return this.GetProperty(GRAPH_OBJ_PROP_LEVELTEXT,modifier); } bool SetLevelText(const string text,const int modifier) { if(!::ObjectSetString(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_LEVELTEXT,modifier,text)) return false; this.SetProperty(GRAPH_OBJ_PROP_LEVELTEXT,modifier,text); return true; } //--- Font string Font(void) const { return this.GetProperty(GRAPH_OBJ_PROP_FONT,0); } bool SetFont(const string font) { if(!::ObjectSetString(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_FONT,font)) return false; this.SetProperty(GRAPH_OBJ_PROP_FONT,0,font); return true; } //--- BMP file name for the "Bitmap Level" object string BMPFile(const int modifier) const { return this.GetProperty(GRAPH_OBJ_PROP_BMPFILE,modifier); } bool SetBMPFile(const string bmp_file,const int modifier) { if(!::ObjectSetString(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_BMPFILE,bmp_file)) return false; this.SetProperty(GRAPH_OBJ_PROP_BMPFILE,modifier,bmp_file); return true; } //--- Symbol for the Chart object string ChartObjSymbol(void) const { return this.GetProperty(GRAPH_OBJ_PROP_CHART_OBJ_SYMBOL,0); } bool SetChartObjSymbol(const string symbol) { if(!::ObjectSetString(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_SYMBOL,symbol)) return false; this.SetProperty(GRAPH_OBJ_PROP_CHART_OBJ_SYMBOL,0,symbol); return true; }
En el método que recupera y guarda las propiedades de tipo entero, primero escribimos los datos que tenemos en el objeto básico, en las propiedades del objeto básico, y luego, partiendo de ahí, escribimos los valores en las propiedades de la clase de objeto gráfico:
//+------------------------------------------------------------------+ //| Get and save the integer properties | //+------------------------------------------------------------------+ void CGStdGraphObj::GetAndSaveINT(void) { //--- Properties inherent in all graphical objects and present in a graphical object CGBaseObj::SetVisibleOnTimeframes((int)::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_TIMEFRAMES),true); // Write Object visibility on timeframes to the base object CGBaseObj::SetFlagBack(::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_BACK),true); // Write Background object flag to the base object CGBaseObj::SetZorder(::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_ZORDER),true); // Write Priority of a graphical object for receiving the event of clicking on a chart to the base object CGBaseObj::SetFlagHidden(::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_HIDDEN),true); // Write Disable displaying the name of a graphical object in the terminal object list to the base object CGBaseObj::SetFlagSelectable(::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_SELECTABLE),true); // Write Object availability to the base object CGBaseObj::SetFlagSelected(::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_SELECTED),true); // Write Object selection to the base object this.SetProperty(GRAPH_OBJ_PROP_TIMEFRAMES,0,CGBaseObj::VisibleOnTimeframes()); // Object visibility on timeframes this.SetProperty(GRAPH_OBJ_PROP_BACK,0,CGBaseObj::IsBack()); // Background object this.SetProperty(GRAPH_OBJ_PROP_ZORDER,0,CGBaseObj::Zorder()); // Priority of a graphical object for receiving the event of clicking on a chart this.SetProperty(GRAPH_OBJ_PROP_HIDDEN,0,CGBaseObj::IsHidden()); // Disable displaying the name of a graphical object in the terminal object list this.SetProperty(GRAPH_OBJ_PROP_SELECTABLE,0,CGBaseObj::IsSelectable()); // Object availability this.SetProperty(GRAPH_OBJ_PROP_SELECTED,0,CGBaseObj::IsSelected()); // Object selection this.SetProperty(GRAPH_OBJ_PROP_CREATETIME,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_CREATETIME)); // Object creation time 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 if(this.GetProperty(GRAPH_OBJ_PROP_LEVELS,0)!=this.GetPropertyPrev(GRAPH_OBJ_PROP_LEVELS,0)) // Check if the number of levels has changed { this.Prop.SetSizeRange(GRAPH_OBJ_PROP_LEVELCOLOR,this.Levels()); this.Prop.SetSizeRange(GRAPH_OBJ_PROP_LEVELSTYLE,this.Levels()); this.Prop.SetSizeRange(GRAPH_OBJ_PROP_LEVELWIDTH,this.Levels()); this.Prop.SetSizeRange(GRAPH_OBJ_PROP_LEVELVALUE,this.Levels()); this.Prop.SetSizeRange(GRAPH_OBJ_PROP_LEVELTEXT,this.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 } //+------------------------------------------------------------------+
De este modo, rellenaremos todas las propiedades que estén duplicadas en la clase básica y su descendiente. De lo contrario, al obtener las propiedades del objeto, se usarán los métodos del objeto básico, y los datos del objeto gráfico no se escribirán en ellos.
Analizaremos algunas otras mejoras al crear la funcionalidad para el seguimiento de eventos de objetos gráficos estándar.
Eventos de objetos gráficos estándar
La lógica de trabajo con los eventos de objetos gráficos se basará en el siguiente concepto: para cada objeto ya podemos definir los eventos que ocurren a sus propiedades. Vamos a crear una pequeña clase de evento básico de objeto gráfico, y al definir cualquier evento sucedido con un objeto un gráfico, crearemos un nuevo objeto de evento, en el que se escribirá:
- El identificador del evento,
- El identificador del gráfico en el que se encuentra el objeto gráfico donde se ha producido el evento,
- El nombre del objeto en el que se ha producido el evento.
En general, un evento personalizado enviado al gráfico especificado por la función EventChartCustom(), tiene cinco parámetros:
//+------------------------------------------------------------------+ bool EventChartCustom( long chart_id, // event receiving chart ID ushort custom_event_id, // event ID long lparam, // long parameter double dparam, // double parameter string sparam // event string parameter ); //+------------------------------------------------------------------+
De estos, necesitaremos rellenar custom_event_id (aquí especificaremos el ID del evento del objeto gráfico), lparam (aquí especificaremos el ID del gráfico en el que se ubica el objeto gráfico en el que ha ocurrido el evento) y sparam (aquí especificaremos el nombre del objeto gráfico). Disponemos de otro parámetro libre, dparam, en el que indicaremos el valor de la constante de la propiedad modificada cuando este cambia, o el número de objetos gráficos borrados que se han eliminado junto con una ventana cerrada del gráfico.
Para cada evento del objeto, crearemos un objeto de evento y lo colocaremos en la lista de eventos. Después de comprobar las propiedades del objeto en busca de cambios, tendremos una lista completa con todos los eventos ocurridos en el objeto gráfico. Una vez completados los ciclos de comprobación de todas las propiedades de los objetos, recorreremos la lista de eventos creada y enviaremos cada uno de ellos al gráfico del programa de control, donde se llamará al manejador de eventos OnChartEvent() de la clase de colección de elementos gráficos. En este manejador, podemos procesar cada evento enviado desde la lista creada de eventos de objeto gráfico. Por consiguiente, cada uno de estos eventos contendrá el identificador del evento, el identificador del gráfico donde se encuentra el objeto gráfico modificado y su nombre. Esto nos permitirá localizar ese objeto gráfico, mientras que el valor de la constante de la propiedad modificada nos indicará exactamente qué propiedad ha sido cambiada, por lo que podremos obtener el puntero a ese objeto en la colección y leer el nuevo valor de la propiedad modificada. Esto nos permitirá localizar tanto el objeto gráfico en sí como la propiedad cambiada, de manera que podremos procesar el evento en el programa de la forma que se ha implementado en su lógica.
En esta ocasión, solo crearemos eventos y los enviaremos al manejador de eventos de la colección de objetos gráficos. En el próximo artículo, descompondremos estos eventos en sus diferentes componentes.
Vamos a escribir la nueva clase del evento básico de los objetos gráficos de la biblioteca al incio del archivo del objeto gráfico básico \MQL5\Include\DoEasy\Objects\Graph\GBaseObj.mqh:
//+------------------------------------------------------------------+ //| GBaseObj.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 "..\..\Services\DELib.mqh" #include <Graphics\Graphic.mqh> //+------------------------------------------------------------------+ //| Graphical object event class | //+------------------------------------------------------------------+ class CGBaseEvent : public CObject { private: ushort m_id; long m_lparam; double m_dparam; string m_sparam; public: void ID(ushort id) { this.m_id=id; } ushort ID(void) const { return this.m_id; } void LParam(const long value) { this.m_lparam=value; } long Lparam(void) const { return this.m_lparam; } void DParam(const double value) { this.m_dparam=value; } double Dparam(void) const { return this.m_dparam; } void SParam(const string value) { this.m_sparam=value; } string Sparam(void) const { return this.m_sparam; } bool Send(const long chart_id) { ::ResetLastError(); return ::EventChartCustom(chart_id,m_id,m_lparam,m_dparam,m_sparam); } CGBaseEvent (const ushort event_id,const long lparam,const double dparam,const string sparam) : m_id(event_id),m_lparam(lparam),m_dparam(dparam),m_sparam(sparam){} ~CGBaseEvent (void){} }; //+------------------------------------------------------------------+ //| Class of the base object of the library graphical objects | //+------------------------------------------------------------------+ class CGBaseObj : public CObject
En la sección privada de la clase, declaramos las variables de miembro de clase para guardar todas las propiedades anteriores del objeto de evento, y en la sección pública, declaramos los métodos para establecer y retornar los valores de estas variables y el método para enviar un evento de usuario al gráfico especificado.
Todos los valores que se asignan a las variables de la clase se transmiten al constructor de la clase, y se asignan a las variables de inmediato en la lista de inicialización del constructor.
En la sección protegida de la clase del objeto gráfico básico, declaramos una lista para almacenar los eventos del objeto y escribimos los métodos para trabajar con la lista y los eventos del objeto gráfico:
//+------------------------------------------------------------------+ //| Class of the base object of the library graphical objects | //+------------------------------------------------------------------+ class CGBaseObj : public CObject { protected: CArrayObj m_list_events; // Object event list ENUM_OBJECT m_type_graph_obj; // Graphical object type ENUM_GRAPH_ELEMENT_TYPE m_type_element; // Graphical element type ENUM_GRAPH_OBJ_BELONG m_belong; // Program affiliation ENUM_GRAPH_OBJ_GROUP m_group; // Graphical object group string m_name_prefix; // Object name prefix string m_name; // Object name long m_chart_id; // Object chart ID long m_object_id; // Object ID long m_zorder; // Priority of a graphical object for receiving the mouse click event int m_subwindow; // Subwindow index int m_shift_y; // Subwindow Y coordinate shift int m_type; // Object type int m_timeframes_visible; // Visibility of an object on timeframes (a set of flags) int m_digits; // Number of decimal places in a quote bool m_visible; // Object visibility bool m_back; // "Background object" flag bool m_selected; // "Object selection" flag bool m_selectable; // "Object availability" flag bool m_hidden; // "Disable displaying the name of a graphical object in the terminal object list" flag datetime m_create_time; // Object creation time //--- Create (1) the object structure and (2) the object from the structure virtual bool ObjectToStruct(void) { return true; } virtual void StructToObject(void){;} //--- Return the list of object events CArrayObj *GetListEvents(void) { return &this.m_list_events; } //--- Create a new object event CGBaseEvent *CreateNewEvent(const ushort event_id,const long lparam,const double dparam,const string sparam) { CGBaseEvent *event=new CGBaseEvent(event_id,lparam,dparam,sparam); return event; } //--- Create a new object event and add it to the event list bool CreateAndAddNewEvent(const ushort event_id,const long lparam,const double dparam,const string sparam) { return this.AddEvent(new CGBaseEvent(event_id,lparam,dparam,sparam)); } //--- Add an event object to the event list bool AddEvent(CGBaseEvent *event) { return this.m_list_events.Add(event);} //--- Clear the event list void ClearEventsList(void) { this.m_list_events.Clear(); } //--- Return the number of events in the list int EventsTotal(void) { return this.m_list_events.Total(); } public:
Todos los métodos escritos son bastante sencillos, así que esperamos que no planteen ninguna duda. En cualquier caso, podrán escribir cualquier duda en los comentarios al artículo.
En el constructor de la clase, limpiamos la lista de eventos del objeto y le asignamos la bandera de lista clasificada:
//+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CGBaseObj::CGBaseObj() : m_shift_y(0),m_visible(false), m_name_prefix(::MQLInfoString(MQL_PROGRAM_NAME)+"_"),m_belong(GRAPH_OBJ_BELONG_PROGRAM) { this.m_list_events.Clear(); // Clear the event list this.m_list_events.Sort(); // Sorted list flag this.m_type=OBJECT_DE_TYPE_GBASE; // Object type this.m_type_graph_obj=WRONG_VALUE; // Graphical object type this.m_type_element=WRONG_VALUE; // Graphical object type this.m_belong=WRONG_VALUE; // Program/terminal affiliation this.m_name_prefix=""; // Object name prefix this.m_name=""; // Object name this.m_chart_id=0; // Object chart ID this.m_object_id=0; // Object ID this.m_zorder=0; // Priority of a graphical object for receiving the mouse click event this.m_subwindow=0; // Subwindow index this.m_shift_y=0; // Subwindow Y coordinate shift this.m_timeframes_visible=OBJ_ALL_PERIODS; // Visibility of an object on timeframes (a set of flags) this.m_visible=true; // Object visibility this.m_back=false; // "Background object" flag this.m_selected=false; // "Object selection" flag this.m_selectable=false; // "Object availability" flag this.m_hidden=true; // "Disable displaying the name of a graphical object in the terminal object list" flag this.m_create_time=0; // Object creation time } //+------------------------------------------------------------------+
En la clase de objeto gráfico estándar \MQL5\Include\DoEasy\Objects\Graph\Standard\GStdGraphObj.mqh, en el método que comprueba el cambio en las propiedades del objeto, limpiamos la lista de eventos, escribimos la creación y la adición de los objetos de evento y su envío al manejador de eventos de la colección de objetos gráficos de la lista creada :
//+------------------------------------------------------------------+ //| Check object property changes | //+------------------------------------------------------------------+ void CGStdGraphObj::PropertiesCheckChanged(void) { CGBaseObj::ClearEventsList(); 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; for(int j=0;j<Prop.CurrSize(prop);j++) { if(this.GetProperty(prop,j)!=this.GetPropertyPrev(prop,j)) { changed=true; this.CreateAndAddNewEvent(GRAPH_OBJ_EVENT_CHANGE,this.ChartID(),prop,this.Name()); } } } 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; for(int j=0;j<Prop.CurrSize(prop);j++) { if(this.GetProperty(prop,j)!=this.GetPropertyPrev(prop,j)) { changed=true; this.CreateAndAddNewEvent(GRAPH_OBJ_EVENT_CHANGE,this.ChartID(),prop,this.Name()); } } } 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; for(int j=0;j<Prop.CurrSize(prop);j++) { if(this.GetProperty(prop,j)!=this.GetPropertyPrev(prop,j) && prop!=GRAPH_OBJ_PROP_NAME) { changed=true; this.CreateAndAddNewEvent(GRAPH_OBJ_EVENT_CHANGE,this.ChartID(),prop,this.Name()); } } } if(changed) { for(int i=0;i<this.m_list_events.Total();i++) { CGBaseEvent *event=this.m_list_events.At(i); if(event==NULL) continue; ::EventChartCustom(::ChartID(),event.ID(),event.Lparam(),event.Dparam(),event.Sparam()); } PropertiesCopyToPrevData(); } } //+------------------------------------------------------------------+
Aquí, en un ciclo por todas las propiedades del objeto (de tipo entero, real y string), comprobamos los cambios en cada una de las siguientes propiedades del objeto y, si hay un cambio, creamos un objeto de evento y lo añadimos a la lista de eventos del objeto. Entonces, si hay al menos un cambio, obtenemos cada objeto de evento en un ciclo por la lista creada y lo enviamos al gráfico del programa de control. Estos eventos llegan al manejador OnChartEvent() de la clase de colección de elementos gráficos y son procesados allí.
Vamosa abrir el archivo de la clase de colección de elementos gráficos \MQL5\Include\DoEasy\Collections\GraphElementsCollection.mqh, y mejorar ligeramente el método de la clase control de objetos gráficos encargado de comprobar los objetos en el gráfico:
//+------------------------------------------------------------------+ //| CChartObjectsControl: Check objects on a chart | //+------------------------------------------------------------------+ void CChartObjectsControl::Refresh(void) { //--- Graphical objects on the chart this.m_total_objects=::ObjectsTotal(this.ChartID()); this.m_delta_graph_obj=this.m_total_objects-this.m_last_objects; //--- If an object is added to the chart if(this.m_delta_graph_obj>0) { //--- find the last added graphical object, select it and write its name string name=this.LastAddedGraphObjName(); //--- Handle only non-programmatically created objects if(name!="" && ::StringFind(name,m_name_program)==WRONG_VALUE) { //--- Create the object of the graphical object class corresponding to the added graphical object type ENUM_OBJECT type=(ENUM_OBJECT)::ObjectGetInteger(this.ChartID(),name,OBJPROP_TYPE); ENUM_OBJECT_DE_TYPE obj_type=ENUM_OBJECT_DE_TYPE(type+OBJECT_DE_TYPE_GSTD_OBJ+1); CGStdGraphObj *obj=this.CreateNewGraphObj(type,name); if(obj==NULL) { CMessage::ToLog(DFUN,MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ); return; } //--- Set the object affiliation and add the created object to the list of new objects obj.SetBelong(GRAPH_OBJ_BELONG_NO_PROGRAM); if(this.m_list_new_graph_obj.Search(obj)==WRONG_VALUE) { //--- If failed to add the object to the list, inform of that, delete the object and leave if(!this.m_list_new_graph_obj.Add(obj)) { CMessage::ToLog(DFUN_ERR_LINE,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST); delete obj; return; } //--- Send an event to the control program chart ::EventChartCustom(this.m_chart_id_main,GRAPH_OBJ_EVENT_CREATE,this.ChartID(),0,obj.Name()); } } } //--- save the index of the last added graphical object and the difference with the last check this.m_last_objects=this.m_total_objects; this.m_is_graph_obj_event=(bool)this.m_delta_graph_obj; } //+------------------------------------------------------------------+
Aquí, hemos añadido la muestra de mensajes sobre los errores en el diario, y también enviado el evento de creación de objetos al gráfico del programa de control.
En la sección pública de la clase de colección de elementos gráficos, añadimos un método que retorna la lista con los objetos de control del gráfico:
//--- Return the number of new graphical objects, (3) the flag of the occurred change in the list of graphical objects int NewObjects(void) const { return this.m_delta_graph_obj; } bool IsEvent(void) const { return this.m_is_graph_obj_event; } //--- Return a graphical object by chart name and ID CGStdGraphObj *GetStdGraphObject(const string name,const long chart_id); //--- Return the chart management object list CArrayObj *GetListChartsControl(void) { return &this.m_list_charts_control; } //--- Constructor CGraphElementsCollection();
Como en los métodos para crear objetos gráficos estándar que hemos creado en el último artículo, tenemos un bloque de código repetitivo bastante grande, lo moveremos a un método privado aparte:
//--- Event handler void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam); private: //--- Create a new graphical object, return the pointer to the chart management object CChartObjectsControl *CreateNewStdGraphObjectAndGetCtrlObj(const long chart_id, const string name, int subwindow, const ENUM_OBJECT type_object, const datetime time1, const double price1, const datetime time2=0, const double price2=0, const datetime time3=0, const double price3=0, const datetime time4=0, const double price4=0, const datetime time5=0, const double price5=0) { //--- If an object with a chart ID and name is already present in the collection, inform of that and return NULL if(this.IsPresentGraphObjInList(chart_id,name)) { ::Print(DFUN,CMessage::Text(MSG_GRAPH_ELM_COLLECTION_ERR_GR_OBJ_ALREADY_EXISTS)," ChartID ",(string)chart_id,", ",name); return NULL; } //--- If failed to create a new standard graphical object, inform of that and return NULL if(!this.CreateNewStdGraphObject(chart_id,name,type_object,subwindow,time1,0)) { ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_STD_GRAPH_OBJ),StdGraphObjectTypeDescription(type_object)); CMessage::ToLog(::GetLastError(),true); return NULL; } //--- If failed to get a chart management object, inform of that CChartObjectsControl *ctrl=this.GetChartObjectCtrlObj(chart_id); if(ctrl==NULL) ::Print(DFUN,CMessage::Text(MSG_GRAPH_ELM_COLLECTION_ERR_FAILED_GET_CTRL_OBJ),(string)chart_id); //--- Return the pointer to a chart management object or NULL in case of a failed attempt to get it return ctrl; } //--- Add a newly created object to the list bool AddCreatedObjToList(const string source,const long chart_id,const string name,CGStdGraphObj *obj) { bool res=true; if(!this.m_list_all_graph_obj.Add(obj)) { CMessage::ToLog(source,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST); ::ObjectDelete(chart_id,name); delete obj; res=false; } ::ChartRedraw(chart_id); return res; } public: //--- Create the "Vertical line" graphical object
Y a continuación, en todos los métodos encargados de crear objetos gráficos estándar, retornamos el resultado de este método:
public: //--- Create the "Vertical line" graphical object bool CreateLineVertical(const long chart_id,const string name,const int subwindow,const datetime time) { //--- Set the name and type of a created object string nm=this.m_name_program+"_"+name; ENUM_OBJECT type_object=OBJ_VLINE; //--- Create a new graphical object and get the pointer to the chart management object CChartObjectsControl *ctrl=this.CreateNewStdGraphObjectAndGetCtrlObj(chart_id,nm,subwindow,type_object,time,0); if(ctrl==NULL) return false; //--- Create a new class object corresponding to the newly created graphical object CGStdVLineObj *obj=ctrl.CreateNewGraphObj(type_object,nm); if(obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ),StdGraphObjectTypeDescription(type_object)); return false; } //--- Set the necessary minimal parameters for an object obj.SetBelong(GRAPH_OBJ_BELONG_PROGRAM); obj.SetFlagSelectable(true,false); obj.SetFlagSelected(true,false); obj.SetObjectID(this.GetFreeGraphObjID(true)); obj.PropertiesCopyToPrevData(); //--- Return the result of adding the object to the list return this.AddCreatedObjToList(DFUN,chart_id,nm,obj); }
Aquí, escribiremos las banderas necesarias para que el método cambiado hoy (encargado de establecer los valores de las propiedades de un objeto gráfico) funcione correctamente.
Hemos modificado de la misma manera el resto de métodos para crear objetos gráficos estándar, así que no los analizaremos aquí. El lector los encontrará en los archivos adjuntos al artículo.
En el método que crea un nuevo objeto para controlar los objetos gráficos del gráfico especificado y que añade este objeto a la lista, añadimos la muestra de mensajes en el diario cuando el indicador de control de eventos es agregado al gráfico:
//+------------------------------------------------------------------+ //| Create a new graphical object management object | //| for a specified and add it to the list | //+------------------------------------------------------------------+ CChartObjectsControl *CGraphElementsCollection::CreateChartObjectCtrlObj(const long chart_id) { //--- Create a new object for managing chart objects by ID CChartObjectsControl *obj=new CChartObjectsControl(chart_id); //--- If the object is not created, inform of the error and return NULL if(obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_GRAPH_ELM_COLLECTION_ERR_FAILED_CREATE_CTRL_OBJ),(string)chart_id); return NULL; } //--- If failed to add the object to the list, inform of the error, remove the object and return NULL if(!this.m_list_charts_control.Add(obj)) { CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST); delete obj; return NULL; } if(obj.ChartID()!=CBaseObj::GetMainChartID() && obj.CreateEventControlInd(CBaseObj::GetMainChartID())) if(!obj.AddEventControlInd()) { CMessage::ToLog(DFUN,MSG_GRAPH_OBJ_FAILED_ADD_EVN_CTRL_INDICATOR); CMessage::ToLog(DFUN,::GetLastError(),true); } else ::Print(DFUN,CMessage::Text(MSG_GRAPH_OBJ_ADDED_EVN_CTRL_INDICATOR),obj.Symbol()," #",obj.ChartID()); //--- Return the pointer to the object that was created and added to the list return obj; } //+------------------------------------------------------------------+
En el método que actualiza la lista de todos los objetos gráficos, en lugar de la muestra simple de un objeto gráfico eliminado que se encuentra en la lista de colección, ahora enviaremos un evento sobre el objeto gráfico eliminado:
//+------------------------------------------------------------------+ //| Update the list of all graphical objects | //+------------------------------------------------------------------+ void CGraphElementsCollection::Refresh(void) { this.RefreshForExtraObjects(); //--- Declare variables to search for charts long chart_id=0; int i=0; //--- In the loop by all open charts in the terminal (no more than 100) while(i<CHARTS_MAX) { //--- Get the chart ID chart_id=::ChartNext(chart_id); if(chart_id<0) break; //--- Get the pointer to the object for managing graphical objects //--- and update the list of graphical objects by chart ID CChartObjectsControl *obj_ctrl=this.RefreshByChartID(chart_id); //--- If failed to get the pointer, move on to the next chart if(obj_ctrl==NULL) continue; //--- If the number of objects on the chart changes if(obj_ctrl.IsEvent()) { //--- If a graphical object is added to the chart if(obj_ctrl.Delta()>0) { //--- Get the list of added graphical objects and move them to the collection list //--- (if failed to move the object to the collection, move on to the next object) if(!this.AddGraphObjToCollection(DFUN_ERR_LINE,obj_ctrl)) continue; } //--- If the graphical object has been removed else if(obj_ctrl.Delta()<0) { // Find an extra object in the list CGStdGraphObj *obj=this.FindMissingObj(chart_id); if(obj!=NULL) { //--- Send an event to the control program chart ::EventChartCustom(this.m_chart_id_main,GRAPH_OBJ_EVENT_DELETE,obj.ChartID(),0,obj.Name()); //--- Remove the class object of a removed graphical object from the collection list if(!this.DeleteGraphObjFromList(obj)) CMessage::ToLog(DFUN,MSG_GRAPH_OBJ_FAILED_DETACH_OBJ_FROM_LIST); } } } //--- Increase the loop index i++; } } //+------------------------------------------------------------------+
En el evento, en su parámetro de línea, transmitimos el nombre del objeto remoto. Es muy posible que luego guardemos los objetos eliminados en una lista aparte, de forma que cuando recibamos un evento de eliminación, podamos ver qué objeto se ha eliminado y qué propiedades tenía y, si fuera necesario, restaurarlo mediante programación.
En el método que procesa la eliminación de una ventana del gráfico, en lugar de un simple mensaje al diario, ahora enviaremos un evento al gráfico del programa de control:
//+------------------------------------------------------------------+ //| Handle removing the chart window | //+------------------------------------------------------------------+ void CGraphElementsCollection::RefreshForExtraObjects(void) { for(int i=this.m_list_charts_control.Total()-1;i>WRONG_VALUE;i--) { CChartObjectsControl *obj_ctrl=this.m_list_charts_control.At(i); if(obj_ctrl==NULL) continue; if(!this.IsPresentChartWindow(obj_ctrl.ChartID())) { long chart_id=obj_ctrl.ChartID(); string chart_symb=obj_ctrl.Symbol(); int total_ctrl=this.m_list_charts_control.Total(); this.DeleteGraphObjCtrlObjFromList(obj_ctrl); int total_obj=this.m_list_all_graph_obj.Total(); this.DeleteGraphObjectsFromList(chart_id); int del_ctrl=total_ctrl-this.m_list_charts_control.Total(); int del_obj=total_obj-this.m_list_all_graph_obj.Total(); ::EventChartCustom(this.m_chart_id_main,GRAPH_OBJ_EVENT_DEL_CHART,chart_id,del_obj,chart_symb); } } } //+------------------------------------------------------------------+
Aquí, transmitimos en los parámetros del evento el identificador del gráfico cerrado, el número de objetos gráficos eliminados junto con él y el símbolo del gráfico cerrado.
En el manejador de eventos de la clase de colección de elementos gráficos, añadimos el procesamiento de eventos básicos de los objetos gráficos:
//+------------------------------------------------------------------+ //| Event handler | //+------------------------------------------------------------------+ void CGraphElementsCollection::OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { CGStdGraphObj *obj=NULL; ushort idx=ushort(id-CHARTEVENT_CUSTOM); if(id==CHARTEVENT_OBJECT_CHANGE || id==CHARTEVENT_OBJECT_DRAG || id==CHARTEVENT_OBJECT_CLICK || idx==CHARTEVENT_OBJECT_CHANGE || idx==CHARTEVENT_OBJECT_DRAG || idx==CHARTEVENT_OBJECT_CLICK) { //--- Calculate the chart ID //--- If the event ID corresponds to an event from the current chart, the chart ID is received from ChartID //--- If the event ID corresponds to a user event, the chart ID is received from lparam //--- Otherwise, the chart ID is assigned to -1 long param=(id==CHARTEVENT_OBJECT_CLICK ? ::ChartID() : idx==CHARTEVENT_OBJECT_CLICK ? lparam : WRONG_VALUE); long chart_id=(param==WRONG_VALUE ? (lparam==0 ? ::ChartID() : lparam) : param); //--- Get the object, whose properties were changed or which was relocated, //--- from the collection list by its name set in sparam obj=this.GetStdGraphObject(sparam,chart_id); //--- If failed to get the object by its name, it is not on the list, //--- which means its name has been changed if(obj==NULL) { //--- Let's search the list for the object that is not on the chart obj=this.FindMissingObj(chart_id); //--- If failed to find the object here as well, exit if(obj==NULL) return; //--- Get the name of the renamed graphical object on the chart, which is not in the collection list string name_new=this.FindExtraObj(chart_id); //--- Send an event with the old name of an object to the control program chart and //--- set a new name for the collection list object, which does not correspond to any graphical object on the chart ::EventChartCustom(this.m_chart_id_main,GRAPH_OBJ_EVENT_RENAME,obj.ChartID(),0,obj.Name()); obj.SetName(name_new); } //--- Update the properties of the obtained object //--- and check their change obj.PropertiesRefresh(); obj.PropertiesCheckChanged(); } //--- Handle standard graphical object events if(idx>GRAPH_OBJ_EVENT_NO_EVENT && idx<GRAPH_OBJ_EVENTS_NEXT_CODE) { //--- Depending on the event type, display an appropriate message in the journal switch(idx) { case GRAPH_OBJ_EVENT_CREATE : ::Print(DFUN,CMessage::Text(MSG_GRAPH_OBJ_EVN_GRAPH_OBJ_CREATE)); obj=this.GetStdGraphObject(sparam,lparam); if(obj!=NULL) obj.PrintShort(); break; case GRAPH_OBJ_EVENT_CHANGE : ::Print(DFUN,CMessage::Text(MSG_GRAPH_OBJ_EVN_GRAPH_OBJ_CHANGE)); obj=this.GetStdGraphObject(sparam,lparam); if(obj!=NULL) obj.PrintShort(); break; case GRAPH_OBJ_EVENT_RENAME : ::Print(DFUN,CMessage::Text(MSG_GRAPH_OBJ_EVN_GRAPH_OBJ_RENAME)); obj=this.GetStdGraphObject(sparam,lparam); if(obj!=NULL) obj.PrintShort(); break; case GRAPH_OBJ_EVENT_DELETE : ::Print(DFUN,CMessage::Text(MSG_GRAPH_OBJ_EVN_GRAPH_OBJ_DELETE)); break; case GRAPH_OBJ_EVENT_DEL_CHART: ::Print(DFUN,CMessage::Text(MSG_GRAPH_OBJ_EVN_GRAPH_OBJ_DEL_CHART),": ChartID: ",lparam,", ChartSymbol: ",sparam); break; default: break; } } } //+------------------------------------------------------------------+
La lógica del método se describe en los comentarios al código. Aquí, hemos añadido una reacción al evento de clic en el gráfico, y si ha habido un doble clic, el estado de la selección del objeto cambiará, lo que provocará la aparición de un evento sobre el cambio de los parámetros del objeto. Así, podremos procesar los eventos de selección y deselección del objeto.
Al final, el mismo método recibe los eventos ya procesados del objeto gráfico, para los que se han creado los eventos básicos. Estos eventos básicos se procesan en un nuevo bloque de código. Hasta ahora, aquí solo teníamos la muestra de mensajes en el diario. En futuros artículos, crearemos el procesamiento completo de cada uno de estos eventos para que el programa pueda reconocer el objeto modificado y acceder rápidamente tanto a este como a sus propiedades.
Para simplificar el trabajo con objetos gráficos, crearemos los métodos necesarios para trabajar con objetos gráficos en la clase principal de la biblioteca CEngine en \MQL5\Include\DoEasy\Engine.mqh.
Para determinar qué gráficos tenemos abiertos al usar las clases de objetos gráficos estándar, podemos buscar en la lista de objetos de control de gráficos. En esta lista, podemos tomar de los objetos los identificadores de todos los gráficos abiertos, con lo cual será posible referirse a ellos. Para simplificar la recuperación de los identificadores en el programa, crearemos un método que rellenará la matriz long transmitida a este con los valores de los identificadores de los gráficos abiertos para los que se han creado los objetos de control:
//--- Launch the new pause countdown void Pause(const ulong pause_msc,const datetime time_start=0) { this.PauseSetWaitingMSC(pause_msc); this.PauseSetTimeBegin(time_start*1000); while(!this.PauseIsCompleted() && !::IsStopped()){} } //--- Return the graphical object collection CGraphElementsCollection *GetGraphicObjCollection(void) { return &this.m_graph_objects; } //--- Fill in the array with IDs of the charts opened in the terminal void GraphGetArrayChartsID(long &array_charts_id[]) { CArrayObj *list=this.m_graph_objects.GetListChartsControl(); if(list==NULL) return; ::ArrayResize(array_charts_id,list.Total()); ::ArrayInitialize(array_charts_id,WRONG_VALUE); for(int i=0;i<list.Total();i++) { CChartObjectsControl *obj=list.At(i); if(obj==NULL) continue; array_charts_id[i]=obj.ChartID(); } } //--- Create the "Vertical line" graphical object
Aquí, fijamos inmediatamente un tamaño para la matriz transmitida al método igual al tamaño de la lista de objetos de control de gráficos e inicializamos la matriz con valores -1. Esto es necesario si se produce un error al obtener un objeto de control de gráficos en el ciclo, de forma que la celda de la matriz correspondiente al índice del objeto de control de gráficos que no se ha podido obtener contenga el valor -1, que serviría como señal de error de obtención de este objeto.
Por supuesto, podemos aumentar el tamaño de la matriz en el ciclo solo si el objeto se obtiene con éxito, pero llamar a ArrayResize() dentro del ciclo no resulta rápido. Por eso, la matriz se incrementa inmediatamente según el tamaño de la lista, y en caso de error, la celda correspondiente de la matriz contendrá -1.
Luego, en un ciclo sobre la lista recibida, obtenemos el siguiente objeto de control gráfico, y escribimos el identificador del gráfico (contenido en el objeto de control de gráficos) en la celda de la matriz que se corresponde con el índice del ciclo.
Para facilitar el acceso a los métodos de creación de objetos gráficos estándar, vamos a escribir los métodos necesarios para su creación, dos métodos para cada objeto. El primer método llamará al método de clase homónimo de la colección de elementos gráficos para crear el objeto gráfico estándar
. y el segundo llamará al primer método con el ID del gráfico actual:
//--- Create the "Vertical line" graphical object bool CreateLineVertical(const long chart_id,const string name,const int subwindow,const datetime time) { return this.m_graph_objects.CreateLineVertical(chart_id,name,subwindow,time); } bool CreateLineVertical(const string name,const int subwindow,const datetime time) { return this.CreateLineVertical(::ChartID(),name,subwindow,time); } //--- Create the "Horizontal line" graphical object bool CreateLineHorizontal(const long chart_id,const string name,const int subwindow,const double price) { return this.m_graph_objects.CreateLineHorizontal(chart_id,name,subwindow,price); } bool CreateLineHorizontal(const string name,const int subwindow,const double price) { return this.CreateLineHorizontal(::ChartID(),name,subwindow,price); } //--- Create the "Trend line" graphical object bool CreateLineTrend(const long chart_id,const string name,const int subwindow, const datetime time1,const double price1,const datetime time2,const double price2) { return this.m_graph_objects.CreateLineTrend(chart_id,name,subwindow,time1,price1,time2,price2); } bool CreateLineTrend(const string name,const int subwindow, const datetime time1,const double price1,const datetime time2,const double price2) { return this.CreateLineTrend(::ChartID(),name,subwindow,time1,price1,time2,price2); } //--- Create the "Trend line by angle" graphical object bool CreateLineTrendByAngle(const long chart_id,const string name,const int subwindow, const datetime time1,const double price1,const datetime time2,const double price2, const double angle) { return this.m_graph_objects.CreateLineTrendByAngle(chart_id,name,subwindow,time1,price1,time2,price2,angle); } bool CreateLineTrendByAngle(const string name,const int subwindow, const datetime time1,const double price1,const datetime time2,const double price2, const double angle) { return this.CreateLineTrendByAngle(::ChartID(),name,subwindow,time1,price1,time2,price2,angle); } //--- Create the "Cyclic lines" graphical object bool CreateLineCycle(const long chart_id,const string name,const int subwindow, const datetime time1,const double price1,const datetime time2,const double price2) { return this.m_graph_objects.CreateLineCycle(chart_id,name,subwindow,time1,price1,time2,price2); } bool CreateLineCycle(const string name,const int subwindow, const datetime time1,const double price1,const datetime time2,const double price2) { return this.CreateLineCycle(::ChartID(),name,subwindow,time1,price1,time2,price2); } //--- Create the "Arrowed line" graphical object bool CreateLineArrowed(const long chart_id,const string name,const int subwindow, const datetime time1,const double price1,const datetime time2,const double price2) { return this.m_graph_objects.CreateLineArrowed(chart_id,name,subwindow,time1,price1,time2,price2); } bool CreateLineArrowed(const string name,const int subwindow, const datetime time1,const double price1,const datetime time2,const double price2) { return this.CreateLineArrowed(::ChartID(),name,subwindow,time1,price1,time2,price2); } //--- Create the "Equidistant channel" graphical object bool CreateChannel(const long chart_id,const string name,const int subwindow, const datetime time1,const double price1,const datetime time2,const double price2, const datetime time3,const double price3) { return this.m_graph_objects.CreateChannel(chart_id,name,subwindow,time1,price1,time2,price2,time3,price3); } bool CreateChannel(const string name,const int subwindow, const datetime time1,const double price1,const datetime time2,const double price2, const datetime time3,const double price3) { return this.CreateChannel(::ChartID(),name,subwindow,time1,price1,time2,price2,time3,price3); } //--- Create the "Standard deviation channel" graphical object bool CreateChannelStdDeviation(const long chart_id,const string name,const int subwindow, const datetime time1,const double price1,const datetime time2,const double price2, const double deviation=1.5) { return this.m_graph_objects.CreateChannelStdDeviation(chart_id,name,subwindow,time1,price1,time2,price2,deviation); } bool CreateChannelStdDeviation(const string name,const int subwindow, const datetime time1,const double price1,const datetime time2,const double price2, const double deviation=1.5) { return this.CreateChannelStdDeviation(::ChartID(),name,subwindow,time1,price1,time2,price2,deviation); } //--- Create the "Linear regression channel" graphical object bool CreateChannelRegression(const long chart_id,const string name,const int subwindow, const datetime time1,const double price1,const datetime time2,const double price2) { return this.m_graph_objects.CreateChannelRegression(chart_id,name,subwindow,time1,price1,time2,price2); } bool CreateChannelRegression(const string name,const int subwindow, const datetime time1,const double price1,const datetime time2,const double price2) { return this.CreateChannelRegression(::ChartID(),name,subwindow,time1,price1,time2,price2); } //--- Create the "Andrews' Pitchfork" graphical object bool CreatePitchforkAndrews(const long chart_id,const string name,const int subwindow, const datetime time1,const double price1,const datetime time2,const double price2, const datetime time3,const double price3) { return this.m_graph_objects.CreatePitchforkAndrews(chart_id,name,subwindow,time1,price1,time2,price2,time3,price3); } bool CreatePitchforkAndrews(const string name,const int subwindow, const datetime time1,const double price1,const datetime time2,const double price2, const datetime time3,const double price3) { return this.CreatePitchforkAndrews(::ChartID(),name,subwindow,time1,price1,time2,price2,time3,price3); } //--- Create the "Gann line" graphical object bool CreateGannLine(const long chart_id,const string name,const int subwindow, const datetime time1,const double price1,const datetime time2,const double price2,const double angle) { return this.m_graph_objects.CreateGannLine(chart_id,name,subwindow,time1,price1,time2,price2,angle); } bool CreateGannLine(const string name,const int subwindow, const datetime time1,const double price1,const datetime time2,const double price2,const double angle) { return this.CreateGannLine(::ChartID(),name,subwindow,time1,price1,time2,price2,angle); } //--- Create the "Gann fan" graphical object bool CreateGannFan(const long chart_id,const string name,const int subwindow, const datetime time1,const double price1,const datetime time2,const double price2, const ENUM_GANN_DIRECTION direction,const double scale) { return this.m_graph_objects.CreateGannFan(chart_id,name,subwindow,time1,price1,time2,price2,direction,scale); } bool CreateGannFan(const string name,const int subwindow, const datetime time1,const double price1,const datetime time2,const double price2, const ENUM_GANN_DIRECTION direction,const double scale) { return this.CreateGannFan(::ChartID(),name,subwindow,time1,price1,time2,price2,direction,scale); } //--- Create the "Gann grid" graphical object bool CreateGannGrid(const long chart_id,const string name,const int subwindow, const datetime time1,const double price1,const datetime time2, const ENUM_GANN_DIRECTION direction,const double scale) { return this.m_graph_objects.CreateGannGrid(chart_id,name,subwindow,time1,price1,time2,direction,scale); } bool CreateGannGrid(const string name,const int subwindow, const datetime time1,const double price1,const datetime time2, const ENUM_GANN_DIRECTION direction,const double scale) { return this.CreateGannGrid(::ChartID(),name,subwindow,time1,price1,time2,direction,scale); } //--- Create the "Fibo levels" graphical object bool CreateFiboLevels(const long chart_id,const string name,const int subwindow, const datetime time1,const double price1,const datetime time2,const double price2) { return this.m_graph_objects.CreateFiboLevels(chart_id,name,subwindow,time1,price1,time2,price2); } bool CreateFiboLevels(const string name,const int subwindow, const datetime time1,const double price1,const datetime time2,const double price2) { return this.CreateFiboLevels(::ChartID(),name,subwindow,time1,price1,time2,price2); } //--- Create the "Fibo Time Zones" graphical object bool CreateFiboTimeZones(const long chart_id,const string name,const int subwindow, const datetime time1,const double price1,const datetime time2,const double price2) { return this.m_graph_objects.CreateFiboTimeZones(chart_id,name,subwindow,time1,price1,time2,price2); } bool CreateFiboTimeZones(const string name,const int subwindow, const datetime time1,const double price1,const datetime time2,const double price2) { return this.CreateFiboTimeZones(::ChartID(),name,subwindow,time1,price1,time2,price2); } //--- Create the "Fibo fan" graphical object bool CreateFiboFan(const long chart_id,const string name,const int subwindow, const datetime time1,const double price1,const datetime time2,const double price2) { return this.m_graph_objects.CreateFiboFan(chart_id,name,subwindow,time1,price1,time2,price2); } bool CreateFiboFan(const string name,const int subwindow, const datetime time1,const double price1,const datetime time2,const double price2) { return this.CreateFiboFan(::ChartID(),name,subwindow,time1,price1,time2,price2); } //--- Create the "Fibo arc" graphical object bool CreateFiboArc(const long chart_id,const string name,const int subwindow, const datetime time1,const double price1,const datetime time2,const double price2, const double scale,const bool ellipse) { return this.m_graph_objects.CreateFiboArc(chart_id,name,subwindow,time1,price1,time2,price2,scale,ellipse); } bool CreateFiboArc(const string name,const int subwindow, const datetime time1,const double price1,const datetime time2,const double price2, const double scale,const bool ellipse) { return this.CreateFiboArc(::ChartID(),name,subwindow,time1,price1,time2,price2,scale,ellipse); } //--- Create the "Fibo channel" graphical object bool CreateFiboChannel(const long chart_id,const string name,const int subwindow, const datetime time1,const double price1,const datetime time2,const double price2,const datetime time3,const double price3) { return this.m_graph_objects.CreateFiboChannel(chart_id,name,subwindow,time1,price1,time2,price2,time3,price3); } bool CreateFiboChannel(const string name,const int subwindow, const datetime time1,const double price1,const datetime time2,const double price2,const datetime time3,const double price3) { return this.CreateFiboChannel(::ChartID(),name,subwindow,time1,price1,time2,price2,time3,price3); } //--- Create the "Fibo extension" graphical object bool CreateFiboExpansion(const long chart_id,const string name,const int subwindow, const datetime time1,const double price1,const datetime time2,const double price2,const datetime time3,const double price3) { return this.m_graph_objects.CreateFiboExpansion(chart_id,name,subwindow,time1,price1,time2,price2,time3,price3); } bool CreateFiboExpansion(const string name,const int subwindow, const datetime time1,const double price1,const datetime time2,const double price2,const datetime time3,const double price3) { return this.CreateFiboExpansion(::ChartID(),name,subwindow,time1,price1,time2,price2,time3,price3); } //--- Create the "Elliott 5 waves" graphical object bool CreateElliothWave5(const long chart_id,const string name,const int subwindow, const datetime time1,const double price1,const datetime time2,const double price2, const datetime time3,const double price3,const datetime time4,const double price4, const datetime time5,const double price5,const ENUM_ELLIOT_WAVE_DEGREE degree, const bool draw_lines) { return this.m_graph_objects.CreateElliothWave5(chart_id,name,subwindow,time1,price1,time2,price2,time3,price3,time4,price4,time5,price5,degree,draw_lines); } bool CreateElliothWave5(const string name,const int subwindow, const datetime time1,const double price1,const datetime time2,const double price2, const datetime time3,const double price3,const datetime time4,const double price4, const datetime time5,const double price5,const ENUM_ELLIOT_WAVE_DEGREE degree, const bool draw_lines) { return this.CreateElliothWave5(::ChartID(),name,subwindow,time1,price1,time2,price2,time3,price3,time4,price4,time5,price5,degree,draw_lines); } //--- Create the "Elliott 3 waves" graphical object bool CreateElliothWave3(const long chart_id,const string name,const int subwindow, const datetime time1,const double price1,const datetime time2, const double price2,const datetime time3,const double price3, const ENUM_ELLIOT_WAVE_DEGREE degree,const bool draw_lines) { return this.m_graph_objects.CreateElliothWave3(chart_id,name,subwindow,time1,price1,time2,price2,time3,price3,degree,draw_lines); } bool CreateElliothWave3(const string name,const int subwindow, const datetime time1,const double price1,const datetime time2, const double price2,const datetime time3,const double price3, const ENUM_ELLIOT_WAVE_DEGREE degree,const bool draw_lines) { return this.CreateElliothWave3(::ChartID(),name,subwindow,time1,price1,time2,price2,time3,price3,degree,draw_lines); } //--- Create the Rectangle graphical object bool CreateRectangle(const long chart_id,const string name,const int subwindow, const datetime time1,const double price1,const datetime time2,const double price2) { return this.m_graph_objects.CreateRectangle(chart_id,name,subwindow,time1,price1,time2,price2); } bool CreateRectangle(const string name,const int subwindow, const datetime time1,const double price1,const datetime time2,const double price2) { return this.CreateRectangle(::ChartID(),name,subwindow,time1,price1,time2,price2); } //--- Create the Triangle graphical object bool CreateTriangle(const long chart_id,const string name,const int subwindow, const datetime time1,const double price1,const datetime time2,const double price2,const datetime time3,const double price3) { return this.m_graph_objects.CreateTriangle(chart_id,name,subwindow,time1,price1,time2,price2,time3,price3); } bool CreateTriangle(const string name,const int subwindow, const datetime time1,const double price1,const datetime time2,const double price2,const datetime time3,const double price3) { return this.CreateTriangle(::ChartID(),name,subwindow,time1,price1,time2,price2,time3,price3); } //--- Create the Ellipse graphical object bool CreateEllipse(const long chart_id,const string name,const int subwindow, const datetime time1,const double price1,const datetime time2,const double price2,const datetime time3,const double price3) { return this.m_graph_objects.CreateEllipse(chart_id,name,subwindow,time1,price1,time2,price2,time3,price3); } bool CreateEllipse(const string name,const int subwindow, const datetime time1,const double price1,const datetime time2,const double price2,const datetime time3,const double price3) { return this.CreateEllipse(::ChartID(),name,subwindow,time1,price1,time2,price2,time3,price3); } //--- Create the "Thumb up" graphical object bool CreateThumbUp(const long chart_id,const string name,const int subwindow,const datetime time,const double price) { return this.m_graph_objects.CreateThumbUp(chart_id,name,subwindow,time,price); } bool CreateThumbUp(const string name,const int subwindow,const datetime time,const double price) { return this.CreateThumbUp(::ChartID(),name,subwindow,time,price); } //--- Create the "Thumb down" graphical object bool CreateThumbDown(const long chart_id,const string name,const int subwindow,const datetime time,const double price) { return this.m_graph_objects.CreateThumbDown(chart_id,name,subwindow,time,price); } bool CreateThumbDown(const string name,const int subwindow,const datetime time,const double price) { return this.CreateThumbDown(::ChartID(),name,subwindow,time,price); } //--- Create the "Arrow up" graphical object bool CreateArrowUp(const long chart_id,const string name,const int subwindow,const datetime time,const double price) { return this.m_graph_objects.CreateArrowUp(chart_id,name,subwindow,time,price); } bool CreateArrowUp(const string name,const int subwindow,const datetime time,const double price) { return this.CreateArrowUp(::ChartID(),name,subwindow,time,price); } //--- Create the "Arrow down" graphical object bool CreateArrowDown(const long chart_id,const string name,const int subwindow,const datetime time,const double price) { return this.m_graph_objects.CreateArrowDown(chart_id,name,subwindow,time,price); } bool CreateArrowDown(const string name,const int subwindow,const datetime time,const double price) { return this.CreateArrowDown(::ChartID(),name,subwindow,time,price); } //--- Create the Stop graphical object bool CreateSignalStop(const long chart_id,const string name,const int subwindow,const datetime time,const double price) { return this.m_graph_objects.CreateSignalStop(chart_id,name,subwindow,time,price); } bool CreateSignalStop(const string name,const int subwindow,const datetime time,const double price) { return this.CreateSignalStop(::ChartID(),name,subwindow,time,price); } //--- Create the "Check mark" graphical object bool CreateSignalCheck(const long chart_id,const string name,const int subwindow,const datetime time,const double price) { return this.m_graph_objects.CreateSignalCheck(chart_id,name,subwindow,time,price); } bool CreateSignalCheck(const string name,const int subwindow,const datetime time,const double price) { return this.CreateSignalCheck(::ChartID(),name,subwindow,time,price); } //--- Create the "Left price label" graphical object bool CreatePriceLabelLeft(const long chart_id,const string name,const int subwindow,const datetime time,const double price) { return this.m_graph_objects.CreatePriceLabelLeft(chart_id,name,subwindow,time,price); } bool CreatePriceLabelLeft(const string name,const int subwindow,const datetime time,const double price) { return this.CreatePriceLabelLeft(::ChartID(),name,subwindow,time,price); } //--- Create the "Right price label" graphical object bool CreatePriceLabelRight(const long chart_id,const string name,const int subwindow,const datetime time,const double price) { return this.m_graph_objects.CreatePriceLabelRight(chart_id,name,subwindow,time,price); } bool CreatePriceLabelRight(const string name,const int subwindow,const datetime time,const double price) { return this.CreatePriceLabelRight(::ChartID(),name,subwindow,time,price); } //--- Create the Buy graphical object bool CreateSignalBuy(const long chart_id,const string name,const int subwindow,const datetime time,const double price) { return this.m_graph_objects.CreateSignalBuy(chart_id,name,subwindow,time,price); } bool CreateSignalBuy(const string name,const int subwindow,const datetime time,const double price) { return this.CreateSignalBuy(::ChartID(),name,subwindow,time,price); } //--- Create the Sell graphical object bool CreateSignalSell(const long chart_id,const string name,const int subwindow,const datetime time,const double price) { return this.m_graph_objects.CreateSignalSell(chart_id,name,subwindow,time,price); } bool CreateSignalSell(const string name,const int subwindow,const datetime time,const double price) { return this.CreateSignalSell(::ChartID(),name,subwindow,time,price); } //--- Create the Arrow graphical object bool CreateArrow(const long chart_id,const string name,const int subwindow,const datetime time,const double price, const uchar arrow_code,const ENUM_ARROW_ANCHOR anchor) { return this.m_graph_objects.CreateArrow(chart_id,name,subwindow,time,price,arrow_code,anchor); } bool CreateArrow(const string name,const int subwindow,const datetime time,const double price, const uchar arrow_code,const ENUM_ARROW_ANCHOR anchor) { return this.CreateArrow(::ChartID(),name,subwindow,time,price,arrow_code,anchor); } //--- Create the Text graphical object bool CreateText(const long chart_id,const string name,const int subwindow,const datetime time,const double price, const string text,const int size,const ENUM_ANCHOR_POINT anchor_point,const double angle) { return this.m_graph_objects.CreateText(chart_id,name,subwindow,time,price,text,size,anchor_point,angle); } bool CreateText(const string name,const int subwindow,const datetime time,const double price, const string text,const int size,const ENUM_ANCHOR_POINT anchor_point,const double angle) { return this.CreateText(::ChartID(),name,subwindow,time,price,text,size,anchor_point,angle); } //--- Create the "Text label" graphical object bool CreateTextLabel(const long chart_id,const string name,const int subwindow,const int x,const int y, const string text,const int size,const ENUM_BASE_CORNER corner, const ENUM_ANCHOR_POINT anchor_point,const double angle) { return this.m_graph_objects.CreateTextLabel(chart_id,name,subwindow,x,y,text,size,corner,anchor_point,angle); } bool CreateTextLabel(const string name,const int subwindow,const int x,const int y, const string text,const int size,const ENUM_BASE_CORNER corner, const ENUM_ANCHOR_POINT anchor_point,const double angle) { return this.CreateTextLabel(::ChartID(),name,subwindow,x,y,text,size,corner,anchor_point,angle); } //--- Create the Button graphical object bool CreateButton(const long chart_id,const string name,const int subwindow,const int x,const int y,const int w,const int h, const ENUM_BASE_CORNER corner,const int font_size,const bool button_state) { return this.m_graph_objects.CreateButton(chart_id,name,subwindow,x,y,w,h,corner,font_size,button_state); } bool CreateButton(const string name,const int subwindow,const int x,const int y,const int w,const int h, const ENUM_BASE_CORNER corner,const int font_size,const bool button_state) { return this.CreateButton(::ChartID(),name,subwindow,x,y,w,h,corner,font_size,button_state); } //--- Create the Chart graphical object bool CreateChart(const long chart_id,const string name,const int subwindow,const int x,const int y,const int w,const int h, const ENUM_BASE_CORNER corner,const int scale,const string symbol,const ENUM_TIMEFRAMES timeframe) { return this.m_graph_objects.CreateChart(chart_id,name,subwindow,x,y,w,h,corner,scale,symbol,timeframe); } bool CreateChart(const string name,const int subwindow,const int x,const int y,const int w,const int h, const ENUM_BASE_CORNER corner,const int scale,const string symbol,const ENUM_TIMEFRAMES timeframe) { return this.CreateChart(::ChartID(),name,subwindow,x,y,w,h,corner,scale,symbol,timeframe); } //--- Create the Bitmap graphical object bool CreateBitmap(const long chart_id,const string name,const int subwindow,const datetime time,const double price, const string image1,const string image2,const ENUM_ANCHOR_POINT anchor) { return this.m_graph_objects.CreateBitmap(chart_id,name,subwindow,time,price,image1,image2,anchor); } bool CreateBitmap(const string name,const int subwindow,const datetime time,const double price, const string image1,const string image2,const ENUM_ANCHOR_POINT anchor) { return this.CreateBitmap(::ChartID(),name,subwindow,time,price,image1,image2,anchor); } //--- Create the "Bitmap label" graphical object bool CreateBitmapLabel(const long chart_id,const string name,const int subwindow,const int x,const int y,const int w,const int h, const string image1,const string image2,const ENUM_BASE_CORNER corner,const ENUM_ANCHOR_POINT anchor, const bool state) { return this.m_graph_objects.CreateBitmapLabel(chart_id,name,subwindow,x,y,w,h,image1,image2,corner,anchor,state); } bool CreateBitmapLabel(const string name,const int subwindow,const int x,const int y,const int w,const int h, const string image1,const string image2,const ENUM_BASE_CORNER corner,const ENUM_ANCHOR_POINT anchor, const bool state) { return this.CreateBitmapLabel(::ChartID(),name,subwindow,x,y,w,h,image1,image2,corner,anchor,state); } //--- Create the "Input field" graphical object bool CreateEditField(const long chart_id,const string name,const int subwindow,const int x,const int y,const int w,const int h, const int font_size,const ENUM_BASE_CORNER corner,const ENUM_ALIGN_MODE align,const bool readonly) { return this.m_graph_objects.CreateEditField(chart_id,name,subwindow,x,y,w,h,font_size,corner,align,readonly); } bool CreateEditField(const string name,const int subwindow,const int x,const int y,const int w,const int h, const int font_size,const ENUM_BASE_CORNER corner,const ENUM_ALIGN_MODE align,const bool readonly) { return this.CreateEditField(::ChartID(),name,subwindow,x,y,w,h,font_size,corner,align,readonly); } //--- Create the "Economic calendar event" graphical object bool CreateCalendarEvent(const long chart_id,const string name,const int subwindow,const datetime time) { return this.m_graph_objects.CreateCalendarEvent(chart_id,name,subwindow,time); } bool CreateCalendarEvent(const string name,const int subwindow,const datetime time) { return this.CreateCalendarEvent(::ChartID(),name,subwindow,time); } //--- Create the "Rectangular label" graphical object bool CreateRectangleLabel(const long chart_id,const string name,const int subwindow,const int x,const int y,const int w,const int h, const ENUM_BASE_CORNER corner,const ENUM_BORDER_TYPE border) { return this.m_graph_objects.CreateRectangleLabel(chart_id,name,subwindow,x,y,w,h,corner,border); } bool CreateRectangleLabel(const string name,const int subwindow,const int x,const int y,const int w,const int h, const ENUM_BASE_CORNER corner,const ENUM_BORDER_TYPE border) { return this.CreateRectangleLabel(::ChartID(),name,subwindow,x,y,w,h,corner,border); } //--- Constructor/destructor CEngine(); ~CEngine();
Por ahora, estas son todas las mejoras necesarias para crear una funcionalidad básica para monitorear los eventos de los objetos gráficos.
Simulación
Para las pruebas, tomaremos el asesor del artículo anterior y
lo guardaremos en la nueva carpeta \MQL5\Experts\TestDoEasy\Part90\ con el nuevo nombre TestDoEasyPart90.mq5.
¿Cómo lo probamos? En el artículo anterior, creamos una línea vertical clicando en un gráfico con la tecla Ctrl pulsada. Ahora también lo crearemos, pero en todos los gráficos abiertos en el terminal.
En el manejador OnChartEvent(), en el bloque de código para un evento de clic en el gráfico, añadiremos el código para rellenar la matriz con los IDs de todos los gráficos abiertos, y en un ciclo a través de la matriz obtenida, crearemos una línea vertical en cada uno de los gráficos:
if(id==CHARTEVENT_CLICK) { if(!IsCtrlKeyPressed()) return; datetime time=0; double price=0; int sw=0; if(ChartXYToTimePrice(ChartID(),(int)lparam,(int)dparam,sw,time,price)) { long array[]; engine.GraphGetArrayChartsID(array); for(int i=0;i<ArraySize(array);i++) engine.CreateLineVertical(array[i],"LineVertical",0,time); } } engine.GetGraphicObjCollection().OnChartEvent(id,lparam,dparam,sparam); } //+------------------------------------------------------------------+
Compilamos el EA y lo ejecutamos en el gráfico, abriendo primero otro gráfico, y disponiéndolos horizontalmente. Un clic sobre el gráfico con el EA creará líneas verticales, una en cada gráfico. Ahora, modificamos sus propiedades y vemos cómo el diario muestra los mensajes sobre los eventos recibidos:
Como podemos ver, los mensajes sobre los eventos de los objetos son registrados. Al crear objetos usando programación, no se crea el evento de creación de un objeto, porque el programador ya sabe en qué momento crea un objeto gráfico mediante programación. Por eso, creemos que será inútil duplicar este hecho enviando el evento.
Por supuesto, el simple registro de mensajes generalizados sobre un evento ocurrido no es suficiente para procesar los eventos. Pero estos son solo mensajes de eventos básicos, cuyos parámetros contienen toda la información sobre un evento; los definiremos más adelante.
¿Qué es lo próximo?
En el próximo artículo, seguiremos trabajando con los eventos de los objetos gráficos y crearemos el procesamiento de cada evento obtenido.
*Artículos de esta serie:
Gráficos en la biblioteca DoEasy (Parte 86): Colección de objetos gráficos - controlando la modificación de propiedades
Gráficos en la biblioteca DoEasy (Parte 87): Colección de objetos gráficos - control de la modificación de propiedades en todos los gráficos abiertos
Gráficos en la biblioteca DoEasy (Parte 88): Colección de objetos gráficos - matriz dinámica bidimensional para almacenar propiedades de objetos que cambian dinámicamente
Gráficos en la biblioteca DoEasy (Parte 89): Creación programática de objetos gráficos estándar Funcionalidad básica
Traducción del ruso hecha por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/ru/articles/10139
- Aplicaciones de trading gratuitas
- 8 000+ señales para copiar
- Noticias económicas para analizar los mercados financieros
Usted acepta la política del sitio web y las condiciones de uso