preview
Desenvolvendo um sistema de Replay (Parte 67): Refinando o Indicador de controle

Desenvolvendo um sistema de Replay (Parte 67): Refinando o Indicador de controle

MetaTrader 5Exemplos | 24 setembro 2024, 09:26
40 0
Daniel Jose
Daniel Jose

Introdução

No artigo anterior Desenvolvendo um sistema de Replay (Parte 66): Dando play no serviço (VII), fizemos a implementação de um método a fim de saber quando uma nova barra viria a surgir no gráfico. Apesar de aquele método funcionar muito bem para modelos com uma boa liquides. Ele não é de forma alguma, adequado para ser utilizado em modelos com baixa liquidez, ou que entram constantemente em leilão. Este tipo de problema será solucionado muito em breve.

Mas aqui, gostaria de mostrar uma outra coisa. Esta é igualmente interessante, apesar de não fazer assim grande diferença para o usuário que deseja apenas fazer uso da aplicação. Para este, o que começarei explorando no início deste artigo, não faz a menor diferença. No entanto, para você que está estudando e deseja de fato se tornar um bom programador, o que mostrarei a seguir faz uma diferença enorme. Tanto na forma de você programar, quanto na forma de você olhar o seu código e o código de outros programadores também. Pois sim, podemos aprender muito olhando como outros programadores criam soluções para diversos problemas. Mesmo problemas correlacionados, podem ter uma implementação completamente diferente, de um programador para outro. Ou mesmo quando um programador, faz uma revisão em seus métodos de trabalho, em uma dada linguagem.

De qualquer forma, o que mostrarei pode lhe ajudar em algum momento futuro.

Em um artigo passado, fiz algumas mudanças no código, a fim de evitar que a aplicação viesse a produzir erros, quando a mesma estava sendo removida. Seja por conta do encerramento por parte do usuário. Seja por que o gráfico principal havia sido fechado. De qualquer forma a correção foi feita. Porém, aquele mesmo código pode sofrer uma melhoria bastante acentuada. Isto de certa forma faz com que todo o sistema se torne mais simples, principalmente na parte de manutenção do código fonte.

Então vamos ver do que se trata a coisa da qual estou falando. Para isto vamos iniciar o primeiro tópico deste artigo.


Melhorando o dinamismo do indicador de controle

O código em questão que trabalharemos é o do indicador de controle. Apesar de ele já está trabalhando de forma bastante aceitável, podemos melhorar as coisas em termos de qualidade na codificação do mesmo. Primeiramente vamos ver o código original do arquivo de cabeçalho C_Controls.mqh. Este pode ser apreciado na íntegra logo abaixo.

001. //+------------------------------------------------------------------+
002. #property copyright "Daniel Jose"
003. //+------------------------------------------------------------------+
004. #include "..\Auxiliar\C_DrawImage.mqh"
005. #include "..\Defines.mqh"
006. //+------------------------------------------------------------------+
007. #define def_PathBMP           "Images\\Market Replay\\Control\\"
008. #define def_ButtonPlay        def_PathBMP + "Play.bmp"
009. #define def_ButtonPause       def_PathBMP + "Pause.bmp"
010. #define def_ButtonLeft        def_PathBMP + "Left.bmp"
011. #define def_ButtonLeftBlock   def_PathBMP + "Left_Block.bmp"
012. #define def_ButtonRight       def_PathBMP + "Right.bmp"
013. #define def_ButtonRightBlock  def_PathBMP + "Right_Block.bmp"
014. #define def_ButtonPin         def_PathBMP + "Pin.bmp"
015. #resource "\\" + def_ButtonPlay
016. #resource "\\" + def_ButtonPause
017. #resource "\\" + def_ButtonLeft
018. #resource "\\" + def_ButtonLeftBlock
019. #resource "\\" + def_ButtonRight
020. #resource "\\" + def_ButtonRightBlock
021. #resource "\\" + def_ButtonPin
022. //+------------------------------------------------------------------+
023. #define def_ObjectCtrlName(A) "MarketReplayCTRL_" + (typename(A) == "enum eObjectControl" ? EnumToString((C_Controls::eObjectControl)(A)) : (string)(A))
024. #define def_PosXObjects         120
025. //+------------------------------------------------------------------+
026. #define def_SizeButtons         32
027. #define def_ColorFilter         0xFF00FF
028. //+------------------------------------------------------------------+
029. #include "..\Auxiliar\C_Mouse.mqh"
030. //+------------------------------------------------------------------+
031. class C_Controls : private C_Terminal
032. {
033.    protected:
034.    private   :
035. //+------------------------------------------------------------------+
036.       enum eMatrixControl {eCtrlPosition, eCtrlStatus};
037.       enum eObjectControl {ePause, ePlay, eLeft, eRight, ePin, eNull, eTriState = (def_MaxPosSlider + 1)};
038. //+------------------------------------------------------------------+
039.       struct st_00
040.       {
041.          string   szBarSlider,
042.                   szBarSliderBlock;
043.          ushort   Minimal;
044.       }m_Slider;
045.       struct st_01
046.       {
047.          C_DrawImage *Btn;
048.          bool         state;
049.          short        x, y, w, h;
050.       }m_Section[eObjectControl::eNull];
051.       C_Mouse   *m_MousePtr;
052. //+------------------------------------------------------------------+
053. inline void CreteBarSlider(short x, short size)
054.          {
055.             ObjectCreate(GetInfoTerminal().ID, m_Slider.szBarSlider = def_ObjectCtrlName("B1"), OBJ_RECTANGLE_LABEL, 0, 0, 0);
056.             ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_XDISTANCE, def_PosXObjects + x);
057.             ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_YDISTANCE, m_Section[ePin].y + 11);
058.             ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_XSIZE, size);
059.             ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_YSIZE, 9);
060.             ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_BGCOLOR, clrLightSkyBlue);
061.             ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_BORDER_COLOR, clrBlack);
062.             ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_WIDTH, 3);
063.             ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_BORDER_TYPE, BORDER_FLAT);
064.             ObjectCreate(GetInfoTerminal().ID, m_Slider.szBarSliderBlock = def_ObjectCtrlName("B2"), OBJ_RECTANGLE_LABEL, 0, 0, 0);
065.             ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSliderBlock, OBJPROP_XDISTANCE, def_PosXObjects + x);
066.             ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSliderBlock, OBJPROP_YDISTANCE, m_Section[ePin].y + 6);
067.             ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSliderBlock, OBJPROP_YSIZE, 19);
068.             ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSliderBlock, OBJPROP_BGCOLOR, clrRosyBrown);
069.             ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSliderBlock, OBJPROP_BORDER_TYPE, BORDER_RAISED);
070.          }
071. //+------------------------------------------------------------------+
072.       void SetPlay(bool state)
073.          {
074.             if (m_Section[ePlay].Btn == NULL)
075.                m_Section[ePlay].Btn = new C_DrawImage(GetInfoTerminal().ID, 0, def_ObjectCtrlName(ePlay), def_ColorFilter, "::" + def_ButtonPause, "::" + def_ButtonPlay);
076.             m_Section[ePlay].Btn.Paint(m_Section[ePlay].x, m_Section[ePlay].y, m_Section[ePlay].w, m_Section[ePlay].h, 20, (m_Section[ePlay].state = state) ? 1 : 0);
077.             if (!state) CreateCtrlSlider();
078.          }
079. //+------------------------------------------------------------------+
080.       void CreateCtrlSlider(void)
081.          {
082.             if (m_Section[ePin].Btn != NULL) return;
083.             CreteBarSlider(77, 436);
084.             m_Section[eLeft].Btn = new C_DrawImage(GetInfoTerminal().ID, 0, def_ObjectCtrlName(eLeft), def_ColorFilter, "::" + def_ButtonLeft, "::" + def_ButtonLeftBlock);
085.             m_Section[eRight].Btn = new C_DrawImage(GetInfoTerminal().ID, 0, def_ObjectCtrlName(eRight), def_ColorFilter, "::" + def_ButtonRight, "::" + def_ButtonRightBlock);
086.             m_Section[ePin].Btn = new C_DrawImage(GetInfoTerminal().ID, 0, def_ObjectCtrlName(ePin), def_ColorFilter, "::" + def_ButtonPin);
087.             PositionPinSlider(m_Slider.Minimal);
088.          }
089. //+------------------------------------------------------------------+
090. inline void RemoveCtrlSlider(void)
091.          {         
092.             ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_OBJECT_DELETE, false);
093.             for (eObjectControl c0 = ePlay + 1; c0 < eNull; c0++)
094.             {
095.                delete m_Section[c0].Btn;
096.                m_Section[c0].Btn = NULL;
097.             }
098.             ObjectsDeleteAll(GetInfoTerminal().ID, def_ObjectCtrlName("B"));
099.             ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_OBJECT_DELETE, true);
100.          }
101. //+------------------------------------------------------------------+
102. inline void PositionPinSlider(ushort p)
103.          {
104.             int iL, iR;
105.             
106.             m_Section[ePin].x = (short)(p < m_Slider.Minimal ? m_Slider.Minimal : (p > def_MaxPosSlider ? def_MaxPosSlider : p));
107.             iL = (m_Section[ePin].x != m_Slider.Minimal ? 0 : 1);
108.             iR = (m_Section[ePin].x < def_MaxPosSlider ? 0 : 1);
109.             m_Section[ePin].x += def_PosXObjects;
110.              m_Section[ePin].x += 95 - (def_SizeButtons / 2);
111.              for (eObjectControl c0 = ePlay + 1; c0 < eNull; c0++)
112.                m_Section[c0].Btn.Paint(m_Section[c0].x, m_Section[c0].y, m_Section[c0].w, m_Section[c0].h, 20, (c0 == eLeft ? iL : (c0 == eRight ? iR : 0)));
113. 
114.             ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSliderBlock, OBJPROP_XSIZE, m_Slider.Minimal + 2);
115.          }
116. //+------------------------------------------------------------------+
117. inline eObjectControl CheckPositionMouseClick(short &x, short &y)
118.          {
119.             C_Mouse::st_Mouse InfoMouse;
120.             
121.             InfoMouse = (*m_MousePtr).GetInfoMouse();
122.             x = (short) InfoMouse.Position.X_Graphics;
123.             y = (short) InfoMouse.Position.Y_Graphics;
124.             for (eObjectControl c0 = ePlay; c0 < eNull; c0++)
125.             {   
126.                if ((m_Section[c0].Btn != NULL) && (m_Section[c0].x <= x) && (m_Section[c0].y <= y) && ((m_Section[c0].x + m_Section[c0].w) >= x) && ((m_Section[c0].y + m_Section[c0].h) >= y))
127.                   return c0;
128.             }
129.             
130.             return eNull;
131.          }
132. //+------------------------------------------------------------------+
133.    public   :
134. //+------------------------------------------------------------------+
135.       C_Controls(const long Arg0, const string szShortName, C_Mouse *MousePtr)
136.          :C_Terminal(Arg0),
137.           m_MousePtr(MousePtr)
138.          {
139.             if ((!IndicatorCheckPass(szShortName)) || (CheckPointer(m_MousePtr) == POINTER_INVALID)) SetUserError(C_Terminal::ERR_Unknown);
140.             if (_LastError != ERR_SUCCESS) return;
141.             ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_OBJECT_DELETE, false);
142.             ObjectsDeleteAll(GetInfoTerminal().ID, def_ObjectCtrlName(""));
143.             ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_OBJECT_DELETE, true);
144.             for (eObjectControl c0 = ePlay; c0 < eNull; c0++)
145.             {
146.                m_Section[c0].h = m_Section[c0].w = def_SizeButtons;
147.                m_Section[c0].y = 25;
148.                m_Section[c0].Btn = NULL;
149.             }
150.             m_Section[ePlay].x = def_PosXObjects;
151.             m_Section[eLeft].x = m_Section[ePlay].x + 47;
152.             m_Section[eRight].x = m_Section[ePlay].x + 511;
153.             m_Slider.Minimal = eTriState;
154.          }
155. //+------------------------------------------------------------------+
156.       ~C_Controls()
157.          {
158.             for (eObjectControl c0 = ePlay; c0 < eNull; c0++) delete m_Section[c0].Btn;
159.             ObjectsDeleteAll(GetInfoTerminal().ID, def_ObjectCtrlName(""));
160.             delete m_MousePtr;
161.          }
162. //+------------------------------------------------------------------+
163.       void SetBuffer(const int rates_total, double &Buff[])
164.          {
165.             uCast_Double info;
166.             
167.             info._16b[eCtrlPosition] = m_Slider.Minimal;
168.             info._16b[eCtrlStatus] = (ushort)(m_Slider.Minimal > def_MaxPosSlider ? m_Slider.Minimal : (m_Section[ePlay].state ? ePlay : ePause));
169.             if (rates_total > 0)
170.                Buff[rates_total - 1] = info.dValue;
171.          }
172. //+------------------------------------------------------------------+
173.       void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
174.          {
175.             short x, y;
176.             static ushort iPinPosX = 0;
177.             static short six = -1, sps;
178.             uCast_Double info;
179.             
180.             switch (id)
181.             {
182.                case (CHARTEVENT_CUSTOM + evCtrlReplayInit):
183.                   info.dValue = dparam;
184.                   if ((info._8b[7] != 'D') || (info._8b[6] != 'M')) break;
185.                   iPinPosX = m_Slider.Minimal = (info._16b[eCtrlPosition] > def_MaxPosSlider ? def_MaxPosSlider : (info._16b[eCtrlPosition] < iPinPosX ? iPinPosX : info._16b[eCtrlPosition]));
186.                   SetPlay((eObjectControl)(info._16b[eCtrlStatus]) == ePlay);
187.                   break;
188.                case CHARTEVENT_OBJECT_DELETE:
189.                   if (StringSubstr(sparam, 0, StringLen(def_ObjectCtrlName(""))) == def_ObjectCtrlName(""))
190.                   {
191.                      if (sparam == def_ObjectCtrlName(ePlay))
192.                      {
193.                         delete m_Section[ePlay].Btn;
194.                         m_Section[ePlay].Btn = NULL;
195.                         SetPlay(m_Section[ePlay].state);
196.                      }else
197.                      {
198.                         RemoveCtrlSlider();
199.                         CreateCtrlSlider();
200.                      }
201.                   }
202.                   break;
203.                case CHARTEVENT_MOUSE_MOVE:
204.                   if ((*m_MousePtr).CheckClick(C_Mouse::eClickLeft))   switch (CheckPositionMouseClick(x, y))
205.                   {
206.                      case ePlay:
207.                         SetPlay(!m_Section[ePlay].state);
208.                         if (m_Section[ePlay].state)
209.                         {
210.                            RemoveCtrlSlider();
211.                            m_Slider.Minimal = iPinPosX;
212.                         }else CreateCtrlSlider();
213.                         break;
214.                      case eLeft:
215.                         PositionPinSlider(iPinPosX = (iPinPosX > m_Slider.Minimal ? iPinPosX - 1 : m_Slider.Minimal));
216.                         break;
217.                      case eRight:
218.                         PositionPinSlider(iPinPosX = (iPinPosX < def_MaxPosSlider ? iPinPosX + 1 : def_MaxPosSlider));
219.                         break;
220.                      case ePin:
221.                         if (six == -1)
222.                         {
223.                            six = x;
224.                            sps = (short)iPinPosX;
225.                            ChartSetInteger(GetInfoTerminal().ID, CHART_MOUSE_SCROLL, false);
226.                         }
227.                         iPinPosX = sps + x - six;
228.                         PositionPinSlider(iPinPosX = (iPinPosX < m_Slider.Minimal ? m_Slider.Minimal : (iPinPosX > def_MaxPosSlider ? def_MaxPosSlider : iPinPosX)));
229.                         break;
230.                   }else if (six > 0)
231.                   {
232.                      six = -1;
233.                      ChartSetInteger(GetInfoTerminal().ID, CHART_MOUSE_SCROLL, true);                     
234.                   }
235.                   break;
236.             }
237.             ChartRedraw(GetInfoTerminal().ID);
238.          }
239. //+------------------------------------------------------------------+
240. };
241. //+------------------------------------------------------------------+
242. #undef def_PosXObjects
243. #undef def_ButtonPlay
244. #undef def_ButtonPause
245. #undef def_ButtonLeft
246. #undef def_ButtonRight
247. #undef def_ButtonPin
248. #undef def_PathBMP
249. //+------------------------------------------------------------------+

Código fonte do arquivo C_Controls.mqh

Basicamente o que nos importa aqui, o quanto este código está sendo capaz de crescer, ou ter novas funcionalidades. Sem que precisemos de fato codificar algo a mais. Mas apesar de este código acima, não precisar de fato obter novas funcionalidades. O mesmo não se pode dizer o código que ele faz uso extensivamente. E este é o código responsável por criar e manter os objetos no gráfico. Estou falando da classe C_DrawImage, que se encontra declarada na linha 47.

Mesmo que você, imagine que esta classe C_Controls, esteja criando os objetos que estão sendo colocados no gráfico. Isto de fato não acontece, pelo menos em sua grande maioria. Já que os dois objetos que ela de fato cria. São os que suportam o controle deslizante. Ou seja, tudo que esta classe C_Controls, cria e coloca no gráfico, se encontra presente no procedimento que é visto na linha 53. Mas sendo assim, vamos então dar uma boa olhada no código do arquivo de cabeçalho C_DrawImage.mqh. Este pode ser visto na íntegra logo abaixo.

001. //+------------------------------------------------------------------+
002. #property copyright "Daniel Jose"
003. //+------------------------------------------------------------------+
004. #include "C_Terminal.mqh"
005. //+------------------------------------------------------------------+
006. #define def_MaxImages 2
007. //+------------------------------------------------------------------+
008. class C_DrawImage : public C_Terminal
009. {
010. //+------------------------------------------------------------------+
011.    private   :
012.       struct st_00
013.       {
014.          int   widthMap,
015.                heightMap;
016.          uint  Map[];
017.       }m_InfoImage[def_MaxImages];
018.       uint     m_Pixels[];
019.       string   m_szObjName,
020.                m_szRecName;
021. //+------------------------------------------------------------------+
022.       bool LoadBitmap(int index, string szFileName)
023.          {
024.             struct BitmapHeader
025.             {
026.                ushort   type;
027.                uint     size,
028.                         reserv,
029.                         offbits,
030.                         imgSSize,
031.                         imgWidth,
032.                         imgHeight;
033.                ushort   imgPlanes,
034.                         imgBitCount;
035.                uint     imgCompression,
036.                         imgSizeImage,
037.                         imgXPelsPerMeter,
038.                         imgYPelsPerMeter,
039.                         imgClrUsed,
040.                         imgClrImportant;
041.             } Header;
042.             int fp, w;
043.             bool noAlpha, noFlip;
044.             uint imgSize;
045. 
046.             if ((fp = FileOpen(szFileName, FILE_READ | FILE_BIN)) == INVALID_HANDLE) return false;
047.             if (FileReadStruct(fp, Header) != sizeof(Header))
048.             {
049.                FileClose(fp);
050.                return false;
051.             };
052.             m_InfoImage[index].widthMap  = (int)Header.imgWidth;
053.             m_InfoImage[index].heightMap = (int)Header.imgHeight;
054.             if (noFlip = (m_InfoImage[index].heightMap < 0)) m_InfoImage[index].heightMap = -m_InfoImage[index].heightMap;
055.             if (Header.imgBitCount == 32)
056.             {               
057.                uint tmp[];
058.                
059.                noAlpha = true;
060.                imgSize = FileReadArray(fp, m_InfoImage[index].Map);
061.                if (!noFlip) for (int c0 = 0; c0 < m_InfoImage[index].heightMap / 2; c0++)
062.                {
063.                   ArrayCopy(tmp, m_InfoImage[index].Map, 0, m_InfoImage[index].widthMap * c0, m_InfoImage[index].widthMap);
064.                   ArrayCopy(m_InfoImage[index].Map, m_InfoImage[index].Map, m_InfoImage[index].widthMap * c0, m_InfoImage[index].widthMap * (m_InfoImage[index].heightMap - c0 - 1), m_InfoImage[index].widthMap);
065.                   ArrayCopy(m_InfoImage[index].Map, tmp, m_InfoImage[index].widthMap * (m_InfoImage[index].heightMap - c0 - 1), 0, m_InfoImage[index].widthMap);
066.                }
067.                for (uint c0 = 0; (c0 < imgSize && noAlpha); c0++) if (uchar(m_InfoImage[index].Map[c0] >> 24) != 0) noAlpha = false;
068.                if (noAlpha) for(uint c0 = 0; c0 < imgSize; c0++) m_InfoImage[index].Map[c0] |= 0xFF000000;
069.             } else
070.             {
071.                uchar tmp[];
072. 
073.                w = ((m_InfoImage[index].widthMap * 3) + 3) & ~3;
074.                if (ArrayResize(m_InfoImage[index].Map, m_InfoImage[index].widthMap * m_InfoImage[index].heightMap) != -1) for(int c0 = 0; c0 < m_InfoImage[index].heightMap; c0++)
075.                {
076.                   if (FileReadArray(fp, tmp, 0, w) != w)
077.                   {
078.                      FileClose(fp);
079.                      return false;
080.                   };
081.                   for (int j = 0, k = 0, p = m_InfoImage[index].widthMap * (m_InfoImage[index].heightMap - c0 - 1); j < m_InfoImage[index].widthMap; j++, k+=3, p++)
082.                      m_InfoImage[index].Map[p] = 0xFF000000 | (tmp[k+2] << 16) | (tmp[k + 1] << 8) | tmp[k];
083.                }
084.             }
085.             FileClose(fp);
086.             
087.             return true;
088.          }
089. //+------------------------------------------------------------------+
090.       void ReSizeImage(const int w, const int h, const uchar v, const int what)
091.          {
092. #define _Transparency(A) (((A > 100 ? 100 : (100 - A)) * 2.55) / 255.0)
093.             double fx = (w * 1.0) / m_InfoImage[what].widthMap;
094.             double fy = (h * 1.0) / m_InfoImage[what].heightMap;
095.             uint pyi, pyf, pxi, pxf, tmp;
096.             uint uc;
097. 
098.             ArrayResize(m_Pixels, w * h);
099.             for (int cy = 0, y = 0; cy < m_InfoImage[what].heightMap; cy++, y += m_InfoImage[what].widthMap)
100.             {
101.                pyf = (uint)(fy * cy) * w;
102.                tmp = pyi = (uint)(fy * (cy - 1)) * w;
103.                for (int x = 0; x < m_InfoImage[what].widthMap; x++)
104.                {
105.                   pxf = (uint)(fx * x);
106.                   pxi = (uint)(fx * (x - 1));
107.                   uc = (uchar(double((uc = m_InfoImage[what].Map[x + y]) >> 24) * _Transparency(v)) << 24) | uc & 0x00FFFFFF;
108.                   m_Pixels[pxf + pyf] = uc;
109.                   for (pxi++; pxi < pxf; pxi++) m_Pixels[pxi + pyf] = uc;
110.                }
111.                for (pyi += w; pyi < pyf; pyi += w) 
112.                   for (int x = 0; x < w; x++)
113.                      m_Pixels[x + pyi] = m_Pixels[x + tmp];
114.             }
115. #undef _Transparency
116.          }
117. //+------------------------------------------------------------------+
118.       void Initilize(int sub, string szObjName, const color cFilter, const string szFile1, const string szFile2 = NULL)
119.          {
120.             string sz0;
121.             
122.             m_szObjName = m_szRecName = NULL;
123.             CreateObjectGraphics(m_szObjName = szObjName, OBJ_BITMAP_LABEL);
124.             m_szRecName = "::" + m_szObjName;            
125.             for (int c0 = 0; (c0 < def_MaxImages) && (_LastError == ERR_SUCCESS); c0++)
126.             {
127.                switch (c0)
128.                {
129.                   case 1:
130.                      if ((sz0 = szFile2) != NULL) break;
131.                   case 0:
132.                      sz0 = szFile1;
133.                      break;
134.                }
135.                if (StringFind(sz0, "::") >= 0)
136.                   ResourceReadImage(sz0, m_InfoImage[c0].Map, m_InfoImage[c0].widthMap, m_InfoImage[c0].heightMap);
137.                else if (!LoadBitmap(c0, sz0))
138.                {
139.                   SetUserError(C_Terminal::ERR_FileAcess);
140.                   return;
141.                }
142.                ArrayResize(m_Pixels, m_InfoImage[c0].heightMap * m_InfoImage[c0].widthMap);
143.                ArrayInitialize(m_Pixels, 0);
144.                for (int c1 = (m_InfoImage[c0].heightMap * m_InfoImage[c0].widthMap) - 1; c1 >= 0; c1--)
145.                   if ((m_InfoImage[c0].Map[c1] & 0x00FFFFFF) != cFilter) m_Pixels[c1] = m_InfoImage[c0].Map[c1];
146.                ArraySwap(m_InfoImage[c0].Map, m_Pixels);
147.             }
148.             ArrayResize(m_Pixels, 1);
149.          }
150. //+------------------------------------------------------------------+
151.    public   :
152. //+------------------------------------------------------------------+
153.       C_DrawImage(string szShortName, long id, int sub, string szObjName, string szFile)
154.          :C_Terminal(id)
155.          {
156.             if (!IndicatorCheckPass(szShortName)) return;
157.             Initilize(sub, szObjName, clrNONE, szFile);
158.          }
159. //+------------------------------------------------------------------+
160.       C_DrawImage(long id, int sub, string szObjName, const color cFilter, const string szFile1, const string szFile2 = NULL)
161.          :C_Terminal(id)
162.          {
163.             Initilize(sub, szObjName, cFilter, szFile1, szFile2);
164.          }
165. //+------------------------------------------------------------------+
166.       ~C_DrawImage()
167.          {
168.             for (int c0 = 0; c0 < def_MaxImages; c0++)
169.                ArrayFree(m_InfoImage[c0].Map);
170.             ArrayFree(m_Pixels);
171.             ObjectDelete(GetInfoTerminal().ID, m_szObjName);
172.             ResourceFree(m_szRecName);
173.          }
174. //+------------------------------------------------------------------+
175.       void Paint(const int x, const int y, const int w, const int h, const uchar cView, const int what)
176.          {
177.             
178.             if ((m_szRecName == NULL) || (what < 0) || (what >= def_MaxImages)) return;
179.             ReSizeImage(w, h, cView, what);
180.             ObjectSetInteger(GetInfoTerminal().ID, m_szObjName, OBJPROP_XDISTANCE, x);
181.             ObjectSetInteger(GetInfoTerminal().ID, m_szObjName, OBJPROP_YDISTANCE, y);
182.             if (ResourceCreate(m_szRecName, m_Pixels, w, h, 0, 0, 0, COLOR_FORMAT_ARGB_NORMALIZE))
183.             {
184.                ObjectSetString(GetInfoTerminal().ID, m_szObjName, OBJPROP_BMPFILE, what, m_szRecName);
185.                ChartRedraw(GetInfoTerminal().ID);
186.             }
187.          }
188. //+------------------------------------------------------------------+
189. };
190. //+------------------------------------------------------------------+
191. #undef def_MaxImages
192. //+------------------------------------------------------------------+

Código fonte do arquivo C_DrawImage.mqh

Agora é que a coisa realmente fica interessante. Apesar de aqui na classe C_DrawImage, eu ter colocado uma forma de podermos trabalhar com um BitMap em arquivo. Isto dificilmente irá de fato acontecer. E o motivo é que estou dando preferência em colocar os bitmaps como recursos internos da própria aplicação. Então muito deste código não será de fato compilado pelo MQL5. Mas o que de fato quero chamar atenção para este código na classe C_DrawImage, não é para isto e sim para um outro fato.

Vamos pensar um pouco: O código do indicador de controle, de fato não acessará a classe C_DrawImage. Na verdade, o indicador de controle, se quer sabe da existência da classe C_DrawImage. Porém, repare em uma coisa aqui. Na linha oito, declarando a classe e ela está herdando de forma pública a classe C_Terminal. Ok. Mas no código da classe C_Controls, que é justamente a que o indicador de controle tem acesso, na linha 31 estamos declarando a classe e ela está herdando de forma privativa a mesma classe C_Terminal. Opa. Agora a coisa complicou e estamos com um problema. Mas para piorar ainda mais as coisas, observe na linha 156 da classe C_DrawImage. Note que aqui também estamos testando para poder adicionar um novo indicador. Mas não usamos isto de nenhuma forma. Então a classe C_Terminal somente serve para nos dar acesso ao id do gráfico a fim de poder modificar as propriedades dos objetos.

Mas pense. Por quê precisamos dar acesso, a classe C_Terminal, para uma classe que não precisa de fato de tal coisa. E mesmo que fosse preciso que a classe C_DrawImage viesse a ter algum acesso, a classe C_Terminal, seria muito mais prudente, passar um ponteiro para a classe C_DrawImage de forma que ela pudesse acessar a classe C_Terminal por meio deste ponteiro.

Isto tornaria o código mais consistente, ao mesmo tempo mais seguro de ser expandido com o tempo. Esta expansão poderia se dar via herança ou por outro meio. Mas de qualquer forma seria muito mais seguro e eficiente do que está sendo feito agora.

Assim chegamos ao ponto que pretendo mostrar. Vamos modificar esta classe C_DrawImage, de forma que ela se torne dependente, mas que ao mesmo tempo nos permita ampliar a mesma caso seja necessário. Sendo assim a primeira coisa a ser feita é modificar este código original presente no arquivo C_DrawImage.mqh. Mas antes de fazer isto, vamos ver um arquivo de cabeçalho do qual C_DrawImage.mqh, passará a depender. Este arquivo é o Macro.mqh, que pode ser visto logo abaixo:

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #define macroRemoveSec(A) (A - (A % 60))
05. #define macroGetDate(A)   (A - (A % 86400))
06. #define macroGetSec(A)    (A - (A - (A % 60)))
07. //+------------------------------------------------------------------+
08. #define macroColorRGBA(A, B) ((uint)((B << 24) | (A & 0x00FF00) | ((A & 0xFF0000) >> 16) | ((A & 0x0000FF) << 16)))
09. #define macroTransparency(A) (((A > 100 ? 100 : (100 - A)) * 2.55) / 255.0)
10. //+------------------------------------------------------------------+

Código fonte do arquivo Macro.mqh

Muito bem, este arquivo Macro.mqh será incluído no arquivo C_Terminal.mqh. Se tiver alguma dúvida com relação a isto, veja os artigos anteriores para compreender como isto está sendo feito. Então agora podemos ver o novo arquivo C_DrawImage.mqh, já modificado e com novas possibilidades de uso. Então vamos ver abaixo o novo código do mesmo.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #include "C_Terminal.mqh"
05. //+------------------------------------------------------------------+
06. class C_DrawImage
07. {
08. //+------------------------------------------------------------------+
09.    private   :
10.       struct st_00
11.       {
12.          int   widthMap,
13.                heightMap;
14.          uint  Map[];
15.       }m_InfoImage[2];
16.       uint     m_Pixels[];
17.       string   m_szObjName,
18.                m_szRecName;
19.       long     m_ID;
20. //+------------------------------------------------------------------+
21.       void ReSizeImage(const int w, const int h, const uchar v, const int what)
22.          {
23.             double fx = (w * 1.0) / m_InfoImage[what].widthMap;
24.             double fy = (h * 1.0) / m_InfoImage[what].heightMap;
25.             uint pyi, pyf, pxi, pxf, tmp;
26.             uint uc;
27. 
28.             ArrayResize(m_Pixels, w * h);
29.             if ((m_InfoImage[what].widthMap == w) && (m_InfoImage[what].heightMap == h) && (v == 100)) ArrayCopy(m_Pixels, m_InfoImage[what].Map);
30.             else for (int cy = 0, y = 0; cy < m_InfoImage[what].heightMap; cy++, y += m_InfoImage[what].widthMap)
31.             {
32.                pyf = (uint)(fy * cy) * w;
33.                tmp = pyi = (uint)(fy * (cy - 1)) * w;
34.                for (int x = 0; x < m_InfoImage[what].widthMap; x++)
35.                {
36.                   pxf = (uint)(fx * x);
37.                   pxi = (uint)(fx * (x - 1));
38.                   uc = (uchar(double((uc = m_InfoImage[what].Map[x + y]) >> 24) * macroTransparency(v)) << 24) | uc & 0x00FFFFFF;
39.                   m_Pixels[pxf + pyf] = uc;
40.                   for (pxi++; pxi < pxf; pxi++) m_Pixels[pxi + pyf] = uc;
41.                }
42.                for (pyi += w; pyi < pyf; pyi += w) 
43.                   for (int x = 0; x < w; x++)
44.                      m_Pixels[x + pyi] = m_Pixels[x + tmp];
45.             }
46.          }
47. //+------------------------------------------------------------------+
48.       void Initilize(const int index, const color cFilter, const string szFile, const bool r180)
49.          {            
50.             if (StringFind(szFile, "::") < 0) return;
51.             ResourceReadImage(szFile, m_InfoImage[index].Map, m_InfoImage[index].widthMap, m_InfoImage[index].heightMap);
52.             for (int pm = ArrayResize(m_Pixels, m_InfoImage[index].widthMap * m_InfoImage[index].heightMap), pi = 0; pi < pm;)
53.                for (int c0 = 0, pf = pi + (r180 ? m_InfoImage[index].widthMap - 1 : 0); c0 < m_InfoImage[index].widthMap; pi++, (r180 ? pf-- : pf++), c0++)
54.                   m_Pixels[pf] = ((m_InfoImage[index].Map[pi] & 0x00FFFFFF) != cFilter ? m_InfoImage[index].Map[pi] : 0);
55.             ArraySwap(m_InfoImage[index].Map, m_Pixels);
56.             ArrayFree(m_Pixels);
57.          }
58. //+------------------------------------------------------------------+
59.    public   :
60. //+------------------------------------------------------------------+
61.       C_DrawImage(C_Terminal *ptr, string szObjName, const color cFilter, const string szFile1, const string szFile2, const bool r180 = false)
62.          {
63.             (*ptr).CreateObjectGraphics(m_szObjName = szObjName, OBJ_BITMAP_LABEL);
64.             ObjectSetString(m_ID = (*ptr).GetInfoTerminal().ID, m_szObjName, OBJPROP_BMPFILE, m_szRecName = "::" + m_szObjName);
65.             Initilize(0, cFilter, szFile1, r180);
66.             if (szFile2 != NULL) Initilize(1, cFilter, szFile2, r180);
67.          }
68. //+------------------------------------------------------------------+
69.       ~C_DrawImage()
70.          {
71.             ArrayFree(m_Pixels);
72.             ResourceFree(m_szRecName);
73.             ObjectDelete(m_ID, m_szObjName);
74.          }
75. //+------------------------------------------------------------------+
76.       void Paint(const int x, const int y, const int w, const int h, const uchar cView, const int what, const string tipics = "\n")
77.          {
78.             ReSizeImage(w, h, cView, what);
79.             ObjectSetInteger(m_ID, m_szObjName, OBJPROP_XDISTANCE, x);
80.             ObjectSetInteger(m_ID, m_szObjName, OBJPROP_YDISTANCE, y);
81.             ObjectSetString(m_ID, m_szObjName, OBJPROP_TOOLTIP, tipics);
82.             ResourceCreate(m_szRecName, m_Pixels, w, h, 0, 0, 0, COLOR_FORMAT_ARGB_NORMALIZE);
83.             ObjectSetString(m_ID, m_szObjName, OBJPROP_BMPFILE, what, m_szRecName);
84.             ChartRedraw(m_ID);
85.          }
86. //+------------------------------------------------------------------+
87. };
88. //+------------------------------------------------------------------+

Código fonte do arquivo C_DrawImage.mqh

Talvez você ache que não existem grandes diferenças aqui. Mas sim, elas existem e torna a classe algo muito mais sustentável e agradável de ser utilizada a longo prazo. Basicamente mantivemos tudo, em tese, que estava sendo feito antes. Isto por que o foco realmente da classe é fazer uso dos recursos que estarão embutidos nos executáveis e não mais em arquivos externos.

Este tipo de abordagem, facilita bastante o que precisamos de fato fazer. Observe que agora temos bem menos código, rotinas e procedimentos para nos preocupar. Mas o principal é justamente no constructor da classe. Então preste bastante atenção a linha 61, onde declaramos o constructor. Note que agora a classe C_Terminal será repassada como sendo um ponteiro. Desta forma na linha 63 podemos fazer uso de um procedimento da classe C_Terminal. Igualmente na linha 64, inicializamos uma variável privativa da classe, para nos dar acesso ao id do gráfico. Isto tem por finalidade nos permitir trabalhar nas propriedades dos objetos, dos quais a classe terá acesso. Tais objetos, basicamente são OBJ_BITMAP_LABEL. Então não haverá muito a ser feito neste momento.

Mas observe as linhas 65 e 66. Este tipo de construção, permitiu fazer com que o procedimento de inicialização se torna-se bem mais maduro. Sendo totalmente implementado entre as linhas 48 a 57. Observe que é algo bastante simples, pelo menos ao meu ver, de ser compreendido e que faz uso bem massivo das funções da biblioteca do MQL5. Com exceção do laço presente na linha 52, todo o restante são funções do MQL5. Estude a documentação para compreender o que está acontecendo aqui.

Porém, o laço da linha 52, faz algo um pouco engraçado, merecendo desta forma ser explicado. Note que é um duplo laço. Mas vamos entender o que está acontecendo aqui. Caso contrário você não entenderá o que será feito depois, quando formos usar esta classe C_DrawImage. Vamos começar com a primeira parte do laço. Nela declaramos duas variáveis locais. No entanto, apenas a variável Y é que será incrementada neste laço, então não se preocupe com a segunda variável, pois ela somente apenas precisou ser declarada e inicializada aqui.

Já no segundo laço declaramos outras duas variáveis. Porém preste bastante atenção ao que está acontecendo agora nesta linha 52. A primeira variável, PM, é declarada e inicializada com o tamanho do array alocado. Esta variável NÃO DEVERÁ SER MODIFICADA, já que ela dirá quando o laço terminará. Observe que para isto, apenas precisaremos comparar esta variável PM, com a variável PI. Porém note que esta variável PI, também é declarada nesta mesma linha 52, sendo inicializada com o valor zero. Agora fique atento ao fato de que não estamos fazendo absolutamente nada com as variáveis aqui. Então este laço na linha 52, serve apenas para controlar integralmente o próximo laço, que se encontra declarado na linha 53.

Agora na linha 53 temos um laço bem mais complexo. No entanto, é só prestar atenção, que você conseguirá compreender, o que estará acontecendo aqui. A primeira coisa que fazemos é declarar uma nova variável, C0. Iniciamos esta variável em zero, sendo que ela será a responsável por fazer com que este laço execute até alcançar a largura da imagem. Este é o primeiro ponto e também a parte fácil. Agora vem a parte complicada. Observe que também declaramos uma segunda variável, PF. Muito bem, esta variável PF, terá o seu valor inicial dependente da variável PI e do que estaremos fazendo com a imagem. De qualquer maneira a variável PI, sempre e sempre será incrementada. Porém a variável PF, não necessariamente será incrementada, em alguns casos ela poderá ser decrementada. Para entender o porquê disto, repare que na declaração do procedimento, temos um valor booleano, R180. Quando este valor for verdadeiro, queremos fazer com que a imagem obtida seja um reflexo da imagem original. Para fazer isto, quando R180 for verdadeiro, PF será inicializada com o valor da variável PI somado a largura da imagem. E neste caso PF será decrementada a cada interação do laço. Caso R180 seja falso, teremos, teoricamente, a simples cópia da imagem original para o array na memória.

E por que eu disse, que teremos teoricamente a cópia? O motivo é o que acontece na linha 54. Observe que nesta linha, faremos a filtragem da imagem a procura de uma determinada cor. Quando esta cor for encontrada, aquele ponto será transparente na imagem. Por isto que a cópia, assim como o reflexo, são teoricamente, idênticos a imagem original. Porém todo este trabalho nos arrays, assim como alocar e liberar memória, se deve ao fato de que o MQL5, não faz uso de ponteiros da mesma forma que o C / C++. Então precisamos implementar as coisas, de uma forma um pouco diferente. E sempre que possível, aconselho você a usar as funções da biblioteca padrão do MQL5. Isto por que as funções da biblioteca, estarão muito mais otimizadas do que qualquer código que você queira desenvolver, para fazer a mesma coisa. Além disto, qualquer melhoria no funcionamento da biblioteca do MQL5, seu programa irá automaticamente se beneficiar com ela. Diferente do que seria se você viesse a implementar o procedimento.

Mas de qualquer maneira, temos um outro procedimento que faz uso massivo da biblioteca do MQL5. Estou falando do procedimento PAINT, que se inicia na linha 76. Praticamente todas as chamadas, são direcionadas a biblioteca do MQL5, exceto uma, que se encontra na linha 78. Esta executará o procedimento que você pode visualizar entre as linhas 21 e 46. Agora preste atenção a um detalhe aqui. Na linha 28, alocamos a memória para colocar a imagem que será apresentada no objeto. No entanto, na linha 29, verificamos se a imagem será modificada de alguma forma. Se isto não vier a ocorrer, significa que a imagem que foi carregada, poderá ser lançada diretamente no objeto. Então para acelerar ao máximo as coisas, usamos uma função da biblioteca a fim de fazer uma cópia da imagem, diretamente no array. Novamente se o MQL5, fizesse uso dos ponteiros da mesma forma que o C / C++, as coisas seriam implementadas de forma diferente. Mas se a imagem tiver que ser de alguma forma modificada iremos a partir da linha 30 entrar em um laço a fim de modificar a imagem, de maneira que ela fique como o esperado. Pelo menos em termos de dimensão e nível de transparência.

Desta maneira concluímos esta parte, referente as mudanças no código da classe C_DrawImage. Mas como este código foi modificado, precisaremos ajustar o código da classe C_Controls, sendo está a classe que no atual momento, faz uso imediato desta classe C_DrawImage. Então vamos ver o novo código do arquivo C_Controls.mqh.

001. //+------------------------------------------------------------------+
002. #property copyright "Daniel Jose"
003. //+------------------------------------------------------------------+
004. #include "..\Auxiliar\C_DrawImage.mqh"
005. #include "..\Defines.mqh"
006. //+------------------------------------------------------------------+
007. #define def_PathBMP           "Images\\Market Replay\\Control\\"
008. #define def_ButtonPlay        def_PathBMP + "Play.bmp"
009. #define def_ButtonPause       def_PathBMP + "Pause.bmp"
010. #define def_ButtonCtrl        def_PathBMP + "Left.bmp"
011. #define def_ButtonCtrlBlock   def_PathBMP + "Left_Block.bmp"
012. #define def_ButtonPin         def_PathBMP + "Pin.bmp"
013. #resource "\\" + def_ButtonPlay
014. #resource "\\" + def_ButtonPause
015. #resource "\\" + def_ButtonCtrl
016. #resource "\\" + def_ButtonCtrlBlock
017. #resource "\\" + def_ButtonPin
018. //+------------------------------------------------------------------+
019. #define def_ObjectCtrlName(A) "MarketReplayCTRL_" + (typename(A) == "enum eObjectControl" ? EnumToString((C_Controls::eObjectControl)(A)) : (string)(A))
020. #define def_PosXObjects         120
021. //+------------------------------------------------------------------+
022. #define def_SizeButtons         32
023. #define def_ColorFilter         0xFF00FF
024. //+------------------------------------------------------------------+
025. #include "..\Auxiliar\C_Mouse.mqh"
026. //+------------------------------------------------------------------+
027. class C_Controls : private C_Terminal
028. {
029.    protected:
030.    private   :
031. //+------------------------------------------------------------------+
032.       enum eMatrixControl {eCtrlPosition, eCtrlStatus};
033.       enum eObjectControl {ePause, ePlay, eLeft, eRight, ePin, eNull, eTriState = (def_MaxPosSlider + 1)};
034. //+------------------------------------------------------------------+
035.       struct st_00
036.       {
037.          string   szBarSlider,
038.                   szBarSliderBlock;
039.          ushort   Minimal;
040.       }m_Slider;
041.       struct st_01
042.       {
043.          C_DrawImage *Btn;
044.          bool         state;
045.          short        x, y, w, h;
046.       }m_Section[eObjectControl::eNull];
047.       C_Mouse   *m_MousePtr;
048. //+------------------------------------------------------------------+
049. inline void CreteBarSlider(short x, short size)
050.          {
051.             ObjectCreate(GetInfoTerminal().ID, m_Slider.szBarSlider = def_ObjectCtrlName("B1"), OBJ_RECTANGLE_LABEL, 0, 0, 0);
052.             ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_XDISTANCE, def_PosXObjects + x);
053.             ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_YDISTANCE, m_Section[ePin].y + 11);
054.             ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_XSIZE, size);
055.             ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_YSIZE, 9);
056.             ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_BGCOLOR, clrLightSkyBlue);
057.             ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_BORDER_COLOR, clrBlack);
058.             ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_WIDTH, 3);
059.             ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_BORDER_TYPE, BORDER_FLAT);
060.             ObjectCreate(GetInfoTerminal().ID, m_Slider.szBarSliderBlock = def_ObjectCtrlName("B2"), OBJ_RECTANGLE_LABEL, 0, 0, 0);
061.             ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSliderBlock, OBJPROP_XDISTANCE, def_PosXObjects + x);
062.             ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSliderBlock, OBJPROP_YDISTANCE, m_Section[ePin].y + 6);
063.             ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSliderBlock, OBJPROP_YSIZE, 19);
064.             ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSliderBlock, OBJPROP_BGCOLOR, clrRosyBrown);
065.             ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSliderBlock, OBJPROP_BORDER_TYPE, BORDER_RAISED);
066.          }
067. //+------------------------------------------------------------------+
068.       void SetPlay(bool state)
069.          {
070.             if (m_Section[ePlay].Btn == NULL)
071.                m_Section[ePlay].Btn = new C_DrawImage(GetPointer(this), def_ObjectCtrlName(ePlay), def_ColorFilter, "::" + def_ButtonPause, "::" + def_ButtonPlay);
072.             m_Section[ePlay].Btn.Paint(m_Section[ePlay].x, m_Section[ePlay].y, m_Section[ePlay].w, m_Section[ePlay].h, 20, (m_Section[ePlay].state = state) ? 1 : 0, state ? "Press to Pause" : "Press to Start");
073.             if (!state) CreateCtrlSlider();
074.          }
075. //+------------------------------------------------------------------+
076.       void CreateCtrlSlider(void)
077.          {
078.             if (m_Section[ePin].Btn != NULL) return;
079.             CreteBarSlider(77, 436);
080.             m_Section[eLeft].Btn = new C_DrawImage(GetPointer(this), def_ObjectCtrlName(eLeft), def_ColorFilter, "::" + def_ButtonCtrl, "::" + def_ButtonCtrlBlock);
081.             m_Section[eRight].Btn = new C_DrawImage(GetPointer(this), def_ObjectCtrlName(eRight), def_ColorFilter, "::" + def_ButtonCtrl, "::" + def_ButtonCtrlBlock, true);
082.             m_Section[ePin].Btn = new C_DrawImage(GetPointer(this), def_ObjectCtrlName(ePin), def_ColorFilter, "::" + def_ButtonPin, NULL);
083.             PositionPinSlider(m_Slider.Minimal);
084.          }
085. //+------------------------------------------------------------------+
086. inline void RemoveCtrlSlider(void)
087.          {         
088.             ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_OBJECT_DELETE, false);
089.             for (eObjectControl c0 = ePlay + 1; c0 < eNull; c0++)
090.             {
091.                delete m_Section[c0].Btn;
092.                m_Section[c0].Btn = NULL;
093.             }
094.             ObjectsDeleteAll(GetInfoTerminal().ID, def_ObjectCtrlName("B"));
095.             ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_OBJECT_DELETE, true);
096.          }
097. //+------------------------------------------------------------------+
098. inline void PositionPinSlider(ushort p)
099.          {
100.             int iL, iR;
101.             string szMsg;
102.             
103.             m_Section[ePin].x = (short)(p < m_Slider.Minimal ? m_Slider.Minimal : (p > def_MaxPosSlider ? def_MaxPosSlider : p));
104.             iL = (m_Section[ePin].x != m_Slider.Minimal ? 0 : 1);
105.             iR = (m_Section[ePin].x < def_MaxPosSlider ? 0 : 1);
106.             m_Section[ePin].x += def_PosXObjects;
107.              m_Section[ePin].x += 95 - (def_SizeButtons / 2);
108.              for (eObjectControl c0 = ePlay + 1; c0 < eNull; c0++)   if (m_Section[c0].Btn != NULL)
109.              {
110.                 switch (c0)
111.                 {
112.                    case eLeft  : szMsg = "Previous Position";            break;
113.                    case eRight : szMsg = "Next Position";                break;
114.                    case ePin   : szMsg = "Go To: " + IntegerToString(p); break;
115.                    default     : szMsg = "\n";
116.                 }
117.                m_Section[c0].Btn.Paint(m_Section[c0].x, m_Section[c0].y, m_Section[c0].w, m_Section[c0].h, 20, (c0 == eLeft ? iL : (c0 == eRight ? iR : 0)), szMsg);
118.             }
119. 
120.             ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSliderBlock, OBJPROP_XSIZE, m_Slider.Minimal + 2);
121.          }
122. //+------------------------------------------------------------------+
123. inline eObjectControl CheckPositionMouseClick(short &x, short &y)
124.          {
125.             C_Mouse::st_Mouse InfoMouse;
126.             
127.             InfoMouse = (*m_MousePtr).GetInfoMouse();
128.             x = (short) InfoMouse.Position.X_Graphics;
129.             y = (short) InfoMouse.Position.Y_Graphics;
130.             for (eObjectControl c0 = ePlay; c0 < eNull; c0++)
131.             {   
132.                if ((m_Section[c0].Btn != NULL) && (m_Section[c0].x <= x) && (m_Section[c0].y <= y) && ((m_Section[c0].x + m_Section[c0].w) >= x) && ((m_Section[c0].y + m_Section[c0].h) >= y))
133.                   return c0;
134.             }
135.             
136.             return eNull;
137.          }
138. //+------------------------------------------------------------------+
139.    public   :
140. //+------------------------------------------------------------------+
141.       C_Controls(const long Arg0, const string szShortName, C_Mouse *MousePtr)
142.          :C_Terminal(Arg0),
143.           m_MousePtr(MousePtr)
144.          {
145.             if ((!IndicatorCheckPass(szShortName)) || (CheckPointer(m_MousePtr) == POINTER_INVALID)) SetUserError(C_Terminal::ERR_Unknown);
146.             if (_LastError != ERR_SUCCESS) return;
147.             ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_OBJECT_DELETE, false);
148.             ObjectsDeleteAll(GetInfoTerminal().ID, def_ObjectCtrlName(""));
149.             ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_OBJECT_DELETE, true);
150.             for (eObjectControl c0 = ePlay; c0 < eNull; c0++)
151.             {
152.                m_Section[c0].h = m_Section[c0].w = def_SizeButtons;
153.                m_Section[c0].y = 25;
154.                m_Section[c0].Btn = NULL;
155.             }
156.             m_Section[ePlay].x = def_PosXObjects;
157.             m_Section[eLeft].x = m_Section[ePlay].x + 47;
158.             m_Section[eRight].x = m_Section[ePlay].x + 511;
159.             m_Slider.Minimal = eTriState;
160.          }
161. //+------------------------------------------------------------------+
162.       ~C_Controls()
163.          {
164.             for (eObjectControl c0 = ePlay; c0 < eNull; c0++) delete m_Section[c0].Btn;
165.             ObjectsDeleteAll(GetInfoTerminal().ID, def_ObjectCtrlName(""));
166.             delete m_MousePtr;
167.          }
168. //+------------------------------------------------------------------+
169.       void SetBuffer(const int rates_total, double &Buff[])
170.          {
171.             uCast_Double info;
172.             
173.             info._16b[eCtrlPosition] = m_Slider.Minimal;
174.             info._16b[eCtrlStatus] = (ushort)(m_Slider.Minimal > def_MaxPosSlider ? m_Slider.Minimal : (m_Section[ePlay].state ? ePlay : ePause));
175.             if (rates_total > 0)
176.                Buff[rates_total - 1] = info.dValue;
177.          }
178. //+------------------------------------------------------------------+
179.       void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
180.          {
181.             short x, y;
182.             static ushort iPinPosX = 0;
183.             static short six = -1, sps;
184.             uCast_Double info;
185.             
186.             switch (id)
187.             {
188.                case (CHARTEVENT_CUSTOM + evCtrlReplayInit):
189.                   info.dValue = dparam;
190.                   if ((info._8b[7] != 'D') || (info._8b[6] != 'M')) break;
191.                   iPinPosX = m_Slider.Minimal = (info._16b[eCtrlPosition] > def_MaxPosSlider ? def_MaxPosSlider : (info._16b[eCtrlPosition] < iPinPosX ? iPinPosX : info._16b[eCtrlPosition]));
192.                   SetPlay((eObjectControl)(info._16b[eCtrlStatus]) == ePlay);
193.                   break;
194.                case CHARTEVENT_OBJECT_DELETE:
195.                   if (StringSubstr(sparam, 0, StringLen(def_ObjectCtrlName(""))) == def_ObjectCtrlName(""))
196.                   {
197.                      if (sparam == def_ObjectCtrlName(ePlay))
198.                      {
199.                         delete m_Section[ePlay].Btn;
200.                         m_Section[ePlay].Btn = NULL;
201.                         SetPlay(m_Section[ePlay].state);
202.                      }else
203.                      {
204.                         RemoveCtrlSlider();
205.                         CreateCtrlSlider();
206.                      }
207.                   }
208.                   break;
209.                case CHARTEVENT_MOUSE_MOVE:
210.                   if ((*m_MousePtr).CheckClick(C_Mouse::eClickLeft))   switch (CheckPositionMouseClick(x, y))
211.                   {
212.                      case ePlay:
213.                         SetPlay(!m_Section[ePlay].state);
214.                         if (m_Section[ePlay].state)
215.                         {
216.                            RemoveCtrlSlider();
217.                            m_Slider.Minimal = iPinPosX;
218.                         }else CreateCtrlSlider();
219.                         break;
220.                      case eLeft:
221.                         PositionPinSlider(iPinPosX = (iPinPosX > m_Slider.Minimal ? iPinPosX - 1 : m_Slider.Minimal));
222.                         break;
223.                      case eRight:
224.                         PositionPinSlider(iPinPosX = (iPinPosX < def_MaxPosSlider ? iPinPosX + 1 : def_MaxPosSlider));
225.                         break;
226.                      case ePin:
227.                         if (six == -1)
228.                         {
229.                            six = x;
230.                            sps = (short)iPinPosX;
231.                            ChartSetInteger(GetInfoTerminal().ID, CHART_MOUSE_SCROLL, false);
232.                         }
233.                         iPinPosX = sps + x - six;
234.                         PositionPinSlider(iPinPosX = (iPinPosX < m_Slider.Minimal ? m_Slider.Minimal : (iPinPosX > def_MaxPosSlider ? def_MaxPosSlider : iPinPosX)));
235.                         break;
236.                   }else if (six > 0)
237.                   {
238.                      six = -1;
239.                      ChartSetInteger(GetInfoTerminal().ID, CHART_MOUSE_SCROLL, true);                     
240.                   }
241.                   break;
242.             }
243.             ChartRedraw(GetInfoTerminal().ID);
244.          }
245. //+------------------------------------------------------------------+
246. };
247. //+------------------------------------------------------------------+
248. #undef def_PosXObjects
249. #undef def_ButtonPlay
250. #undef def_ButtonPause
251. #undef def_ButtonCtrl
252. #undef def_ButtonCtrlBlock
253. #undef def_ButtonPin
254. #undef def_PathBMP
255. //+------------------------------------------------------------------+

Código fonte do arquivo C_Controls.mqh

A primeira coisa que logo chama a atenção, neste novo código, são as definições. Pergunta: Onde está a definição dos botões para a direita e para a esquerda? Notaram que aqui entre as linhas sete e dezessete não existe mais os botões direita e esquerda. No lugar deles apareceu uma definição um pouco diferente. Mas estranhamente estamos nos referindo a imagem LEFT.BMP e LEFT_BLOCK.BMP, mas não mais as do lado direito. Por que? O motivo disto, foi dado na explicação do arquivo C_DrawImage.mqh. Já que tanto o botão para a direita, quanto o botão para a esquerda são basicamente idênticos, ou melhor, um é espelho do outro. Não precisamos declarar ambos botões. Nos bastará ter acesso a um deles, que será possível fazer uso do outro. Isto fazendo as coisas via a classe C_DrawImage.

Mas além disto, vamos ver o que mais mudou. Na linha 68, temos o local onde ocorre a criação e identificação dos botões de play e pause. Pois bem, observe agora a linha 72 nesta mesma rotina. No final da declaração que acontece nesta mesma linha, damos ao objeto a possibilidade de nos informar algo a mais. Este tipo de coisa, pode ajudar bastante, se bem que neste caso não fará grandes diferenças.

Mas tanto neste procedimento, que cria e define o botão de play ou pause, tanto no procedimento abaixo que cria os botões e controle deslizante, temos uma coisa que de fato precisa ser explicada. Para entender do que se trata, vamos voltar ao código do constructor da classe C_DrawImage.

Observe que no código fonte, do constructor da classe C_DrawImage, que se encontra na linha 61. Existe a menção, de que o primeiro parâmetro deverá ser uma referência a classe C_Terminal. Preste bastante atenção a isto, pois isto é muito, mas muito importante. O contructor da classe C_DrawImage, espera receber no primeiro parâmetro uma referência a um ponteiro que se refira a classe C_Terminal. Porém e agora voltando ao código do arquivo de cabeçalho C_Controls.mqh, nas linhas 71, 80, 81 e 82 não estamos passando este tipo de informação. Não de forma como você, caro leitor esperava ver. Então vamos voltar para a declaração da classe C_Controls, que é feita na linha 27. Então preste muita atenção ao que explicarei aqui, pois se você não entender isto, não conseguirá entender mais nada a partir de agora.

A classe C_Controls está herdando de forma privativa a classe C_Terminal. O fato de que isto esteja acontecendo, faz com que qualquer código DENTRO da classe C_Controls possa se referir a classe C_Terminal, como se a classe C_Terminal fosse parte da classe C_Controls. Atenção a isto. A referência a classe C_Terminal, somente acontecerá DENTRO, e ênfase nesta palavra DENTRO, da classe C_Controls. Isto por conta que a herança está sendo feita de forma privativa. Se a herança estivesse sendo feita de forma publica, qualquer código que viesse a referenciar a classe C_Controls, também conseguiria acesso as funções e procedimentos públicos que estivessem na classe C_Terminal. Muito bem. Legal. Mas o que isto tem a ver com o constructor da classe C_DrawImage? Ok. Se você entendeu esta questão da herança, podemos então ver o que isto tem a ver com o fato do contructor da classe C_DrawImage, está esperando um ponteiro para poder referenciar a classe C_Terminal e estarmos passando um ponteiro para referenciar a classe C_Controls. Não entendeu? Quando usamos a função de biblioteca GetPointer e no seu argumento informamos o operador this, estamos na verdade nos referindo a classe C_Controls e não a uma outra coisa qualquer.

Para entender isto, agora, será preciso que você compreenda, caso ainda não saiba, o que significa o operador this. Este operador , o tal this, sempre estará se referindo a classe em que estamos, seja ela qual for. Então quando usamos a função GetPointer, com o argumento this, estamos pedindo para que a aplicação consiga receber o ponteiro que referência a classe atual. E neste caso é a classe C_Controls. Porém C_Controls está herdando C_Terminal, de forma privativa. Mas como o código está referenciando a classe C_Controls, como se fosse parte dela, isto abre as portas para que o contructor de C_DrawImage, consiga acesso a classe C_Terminal.

Você pode pensar que isto está quebrando o sistema de herança. Mas não é bem assim. Em C++ existe um operador que consegue fazer algo quase igual, porém muito mais perigoso e complicado do que é feito aqui nesta construção que estou mostrando. Mas não é hora de baixar a guarda. Esta construção tem seus perigos. Se você criar a classe C_Terminal de qualquer maneira, permitindo que qualquer procedimento ou função modifique os valores privativos da classe, ou mesmo se deixar as variáveis fora da clausula privativa. Esta construção que acabei de mostrar permitirá que a classe C_DrawImage, mesmo sem ter acesso direto a classe C_Terminal e mesmo com a classe C_Terminal estando sendo herdada de forma privativa pela classe C_Controls, mude os valores dentro da classe C_Terminal. Então se você codificar de qualquer maneira, esta construção será potencialmente danosa ao seu código. Porém, programando as coisas de forma controlada e principalmente, tomando o devido cuidado a fim de manter um bom encapsulamento dos dados, você não terá problemas em usar esta construção.

De qualquer forma, eu queria mesmo chamar a sua atenção para esta construção, pois apesar de ela ser bastante exótica, ela nos permite estender ainda mais as coisas. Nos permitindo fazer algumas coisas bastante interessantes. Desde que você tome os devidos cuidados.

De qualquer forma, sem que a herança de fato existisse aqui, não seria possível usar esta construção. E mesmo que você tente acessar algum componente ou mesma alguma função ou procedimento da classe C_Controls, dentro da classe C_DrawImage, isto não será possível. Já que apesar de estamos nos referindo, via operador this, a classe C_Controls, apenas a parte referente a classe C_Terminal, realmente será e poderá ser acessada na classe C_DrawImage.

Então vamos aos últimos detalhes, que merecem algum destaque antes de terminarmos este artigo. Para isto vamos ao procedimento na linha 98, que é responsável por posicionar o pino do controle deslizante. Além é claro de controlar minimamente os botões que estão a esquerda e a direita do controle deslizante e a barra de bloqueio. No entanto, o que nos interessa aqui, está presente entre as linhas 110 e 117. Legal, e o que temos aqui? Observe lá no código da classe C_DrawImage, que o procedimento Paint pode receber em seu último argumento uma string. Esta será usada para gerar uma informação típica do objeto, isto quando o mouse estiver posicionado sobre o objeto. Até este momento, tudo que tínhamos era a nossa própria intuição sobre o que estava acontecendo. Mas a partir de agora teremos algo um pouco mais interessante. Poderemos colocar de forma bastante fácil e simplificada, uma mensagem qualquer, a fim de nos informar algo que possa ser de algum interesse. As duas primeiras mensagens, presentes nas linhas 112 e 113, são coisas de baixa relevância na maior parte do tempo. Porém observe a mensagem da linha 114. Opá, agora sim, temos algo interessante. Então quando você ajustar o pino do controle deslizante e logo em seguida posicionar o mouse sobre ele, poderá ver para qual posição relativa no qual estará sendo apontando. Pense bem como, isto é interessante e pode, nos ajudar em diversos momentos.

Suponhamos que você tem em seu poder um conjunto de dados, sabe que tem algo neste conjunto que lhe interessa analisar. Mas como o replay/simulador não fará um salto para uma posição específica dentro do conjunto de dados. E isto é de certa forma adequado, pois torna a coisa menos previsível. Você pode ficar experimentando via tentativa e erro, qual é a posição imediatamente anterior ao que você quer analisar. Então olhando o valor do pino antes de habilitar o play, você pode ver se já passou do ponto, ou se ainda não. Se já passou, tudo que você precisará fazer é fechar a aplicação, para logo em seguida abrir ela novamente, deixando o pino algumas posições antes. Algo que antes era muito difícil de ser executado, por você não ter uma noção relativa à posição que o sistema estaria de fato indicando.


Conclusão

Com isto chegamos ao final de mais um artigo. Espero que o conhecimento contido dentro deste artigo, possa de alguma forma lhe ser útil em outras coisas, além do que estou mostrando aqui. Sei que muitos podem achar estranho o que mostrei neste artigo. Outros podem achar desnecessário, já que o indicador de controle já estava funcionando perfeitamente bem. No entanto, o intuito deste e de todos os meus artigos, não é propor uma solução definitiva de algo. Mas sim mostrar como você, aspirante a programador MQL5 tem muito a aprender. E que precisa sempre estar disposto a aprender e conhecer novas coisas e ideias diferentes.

Apesar de não colocar mais no anexo os códigos fontes. Os mesmos estão sendo mostrados de forma completa nos artigos. Mesmo as partes que são referenciadas como sendo fragmentos, são de alguma forma parte integrante do código. Sendo que tudo que você precisa fazer, é capturar o fragmento mostrado no artigo e substituir o código pelo fragmento. Assim irá sempre ter o código no mesmo ponto em que está sendo mostrado nos artigos.

O motivo de eu não está colocando o código fonte no anexo, é justamente por conta de coisas como a que foi vista aqui neste artigo. Uma pessoa com mais experiência conseguirá lidar com possíveis falhas e erros que possam acontecer. Seja durante as modificações, seja no uso normal. No entanto, usuários com menos experiência, acabaram por cometer erros grosseiros ao tentar usar este tipo de código.

Mas de qualquer forma, no anexo, você terá o acesso aos executáveis necessários para usar o sistema. E o resultado será algo parecido com o vídeo mostrado abaixo. Um detalhe: Os dados que estão sendo utilizados podem ser baixados em artigos passados, onde eu disponibilizei em forma de 3 arquivos ZIP os ticks e barras usadas nas demonstrações que você pode ver nos vídeos.

Vídeo de demonstração

Arquivos anexados |
Versko_DEMO.zip (247.12 KB)
Arbitragem Estatística com previsões Arbitragem Estatística com previsões
Vamos explorar a arbitragem estatística, pesquisar com Python símbolos correlacionados e cointegrados, criar um indicador para o coeficiente de Pearson e desenvolver um EA para negociar arbitragem estatística com previsões feitas com Python e modelos ONNX.
Um algoritmo de seleção de características usando aprendizado baseado em energia em MQL5 puro Um algoritmo de seleção de características usando aprendizado baseado em energia em MQL5 puro
Neste artigo, apresentamos a implementação de um algoritmo de seleção de características descrito em um artigo acadêmico intitulado "FREL: Um algoritmo estável de seleção de características", chamado de Ponderação de Características como Aprendizado Baseado em Energia Regularizada.
Técnicas do MQL5 Wizard que você deve conhecer (Parte 18): Pesquisa de Arquitetura Neural com Vetores Próprios Técnicas do MQL5 Wizard que você deve conhecer (Parte 18): Pesquisa de Arquitetura Neural com Vetores Próprios
Pesquisa de Arquitetura Neural, uma abordagem automatizada para determinar as configurações ideais de uma rede neural, pode ser um diferencial ao enfrentar muitas opções e grandes conjuntos de dados de teste. Examinamos como, quando emparelhado com Vetores Próprios, esse processo pode se tornar ainda mais eficiente.
Introdução ao MQL5 (Parte 7): Guia para Iniciantes na Criação de Expert Advisors e Utilização de Código Gerado por IA no MQL5 Introdução ao MQL5 (Parte 7): Guia para Iniciantes na Criação de Expert Advisors e Utilização de Código Gerado por IA no MQL5
Descubra o guia definitivo para iniciantes na criação de Expert Advisors (EAs) com MQL5 em nosso artigo abrangente. Aprenda passo a passo como construir EAs utilizando pseudocódigo e aproveite o poder do código gerado por IA. Seja você novo no trading algorítmico ou esteja buscando aprimorar suas habilidades, este guia oferece um caminho claro para criar EAs eficazes.