Discussion of article "Graphical Interfaces X: The Standard Chart Control (build 4)" - page 2

 
Artyom Trishkin:

Anatoly, tell me what the cause of the error is

2016.10.19 03:09:04.993 TestTable (EURUSD,H1)   invalid pointer access in 'Scrolls.mqh' (698,10)

Everything worked fine before this update. Now when building the CTable table this error pops up.

Please tell me where it is wrong - I have already printed every line - the interface is built, but after somewhere stumbles, and .... error.

File with an example in the archive.

I forgot to make a correction, as it was done earlier in the CCanvasTable class.

In the CTable class, you need to replace the current versions of the CreateScrollV() and CreateScrollH() methods with the ones shown in the listing below:

//+------------------------------------------------------------------+
//|| Creates a vertical scroll bar ||
//+------------------------------------------------------------------+
bool CTable::CreateScrollV(void)
  {
//--- Save the form pointer
   m_scrollv.WindowPointer(m_wnd);
//--- Coordinates
   int x=(m_anchor_right_window_side)? m_x-m_x_size+m_scrollv.ScrollWidth() : CElement::X2()-m_scrollv.ScrollWidth();
   int y=CElement::Y();
//--- Set dimensions
   m_scrollv.Id(CElement::Id());
   m_scrollv.IsDropdown(CElement::IsDropdown());
   m_scrollv.XSize(m_scrollv.ScrollWidth());
   m_scrollv.YSize((m_columns_total>m_visible_columns_total)? m_y_size-m_scrollv.ScrollWidth()+1 : m_y_size);
   m_scrollv.AnchorRightWindowSide(m_anchor_right_window_side);
   m_scrollv.AnchorBottomWindowSide(m_anchor_bottom_window_side);
//--- Creating a scroll bar
   if(!m_scrollv.CreateScroll(m_chart_id,m_subwin,x,y,m_rows_total,m_visible_rows_total))
      return(false);
//--- Hide if not needed now
   if(m_rows_total<=m_visible_rows_total)
      m_scrollv.Hide();
//---
   return(true);
  }
//+------------------------------------------------------------------+
//|| Creates a horizontal scroll bar ||
//+------------------------------------------------------------------+
bool CTable::CreateScrollH(void)
  {
//--- Save the form pointer
   m_scrollh.WindowPointer(m_wnd);
//--- Coordinates
   int x=CElement::X();
   int y=(m_anchor_bottom_window_side)? m_y-m_area.Y_Size()+m_scrollh.ScrollWidth() : CElement::Y2()-m_scrollh.ScrollWidth();
//--- Set dimensions
   m_scrollh.Id(CElement::Id());
   m_scrollh.IsDropdown(CElement::IsDropdown());
   m_scrollh.XSize((m_rows_total>m_visible_rows_total)? m_area.XSize()-m_scrollh.ScrollWidth()+1 : m_area.XSize());
   m_scrollh.YSize(m_scrollh.ScrollWidth());
   m_scrollh.AnchorRightWindowSide(m_anchor_right_window_side);
   m_scrollh.AnchorBottomWindowSide(m_anchor_bottom_window_side);
//--- Creating a scroll bar
   if(!m_scrollh.CreateScroll(m_chart_id,m_subwin,x,y,m_columns_total,m_visible_columns_total))
      return(false);
//--- Hide if not needed now
   if(m_columns_total<=m_visible_columns_total)
      m_scrollh.Hide();
//---
   return(true);
  }


//---

Similar changes need to be made in the CLabelsTable class. The fixes will be in the next update.

 
Anatoli Kazharski:

I forgot to make a correction as it was done earlier in the CCanvasTable class.

In the CTable class, you need to replace the current versions of the CreateScrollV() and CreateScrollH() methods with the ones shown in the listing below:

...
//---

Similar changes need to be made in the CLabelsTable class. The fixes will be in the next update.

О! Thank you!
 
Anatoli Kazharski:

I forgot to make a correction as it was done earlier in the CCanvasTable class.

In the CTable class, you need to replace the current versions of the CreateScrollV() and CreateScrollH() methods with the ones shown in the listing below:

//+------------------------------------------------------------------+
//|| Creates a vertical scroll bar ||
//+------------------------------------------------------------------+
bool CTable::CreateScrollV(void)
  {
//--- Save the form pointer
   m_scrollv.WindowPointer(m_wnd);
//--- Coordinates
   int x=(m_anchor_right_window_side)? m_x-m_x_size+m_scrollv.ScrollWidth() : CElement::X2()-m_scrollv.ScrollWidth();
   int y=CElement::Y();
//--- Set dimensions
   m_scrollv.Id(CElement::Id());
   m_scrollv.IsDropdown(CElement::IsDropdown());
   m_scrollv.XSize(m_scrollv.ScrollWidth());
   m_scrollv.YSize((m_columns_total>m_visible_columns_total)? m_y_size-m_scrollv.ScrollWidth()+1 : m_y_size);
   m_scrollv.AnchorRightWindowSide(m_anchor_right_window_side);
   m_scrollv.AnchorBottomWindowSide(m_anchor_bottom_window_side);
//--- Creating a scroll bar
   if(!m_scrollv.CreateScroll(m_chart_id,m_subwin,x,y,m_rows_total,m_visible_rows_total))
      return(false);
//--- Hide if not needed now
   if(m_rows_total<=m_visible_rows_total)
      m_scrollv.Hide();
//---
   return(true);
  }
//+------------------------------------------------------------------+
//|| Creates a horizontal scroll bar ||
//+------------------------------------------------------------------+
bool CTable::CreateScrollH(void)
  {
//--- Save the form pointer
   m_scrollh.WindowPointer(m_wnd);
//--- Coordinates
   int x=CElement::X();
   int y=(m_anchor_bottom_window_side)? m_y-m_area.Y_Size()+m_scrollh.ScrollWidth() : CElement::Y2()-m_scrollh.ScrollWidth();
//--- Set dimensions
   m_scrollh.Id(CElement::Id());
   m_scrollh.IsDropdown(CElement::IsDropdown());
   m_scrollh.XSize((m_rows_total>m_visible_rows_total)? m_area.XSize()-m_scrollh.ScrollWidth()+1 : m_area.XSize());
   m_scrollh.YSize(m_scrollh.ScrollWidth());
   m_scrollh.AnchorRightWindowSide(m_anchor_right_window_side);
   m_scrollh.AnchorBottomWindowSide(m_anchor_bottom_window_side);
//--- Creating a scroll bar
   if(!m_scrollh.CreateScroll(m_chart_id,m_subwin,x,y,m_columns_total,m_visible_columns_total))
      return(false);
//--- Hide if not needed now
   if(m_columns_total<=m_visible_columns_total)
      m_scrollh.Hide();
//---
   return(true);
  }


//---

Similar changes need to be made in the CLabelsTable class. The fixes will be in the next update.

Anatoly, the changes have been made. Take the example from MQL5\Indicators\Article07\ChartWindow02\ChartWindow02.mq5, correct the table in Program.mqh so that the number of rows coincides with the visible number of rows, or the number of columns coincides with the visible number of columns, or both. For example:

//+------------------------------------------------------------------+
//|| Creates a table|
//+------------------------------------------------------------------+
bool CProgram::CreateTable(void)
  {
#define COLUMNS1_TOTAL (6)
#define ROWS1_TOTAL    (15)
//--- Save the pointer to the form
   m_table.WindowPointer(m_window1);
//--- Coordinates
   int x=m_window1.X()+TABLE1_GAP_X;
   int y=m_window1.Y()+TABLE1_GAP_Y;
//--- Number of visible columns and rows
   int visible_columns_total =6;
   int visible_rows_total    =15;
//--- Set properties before creation
   m_table.XSize(600);
   m_table.RowYSize(20);
   m_table.FixFirstRow(true);
   m_table.FixFirstColumn(true);
   m_table.LightsHover(true);
   m_table.SelectableRow(true);
   m_table.TextAlign(ALIGN_CENTER);
   m_table.HeadersColor(C'255,244,213');
   m_table.HeadersTextColor(clrBlack);
   m_table.CellColorHover(clrGold);
   m_table.TableSize(COLUMNS1_TOTAL,ROWS1_TOTAL);
   m_table.VisibleTableSize(visible_columns_total,visible_rows_total);
//--- Create a control
   if(!m_table.CreateTable(m_chart_id,m_subwin,x,y))
      return(false);
//--- Let's fill in the table:
// The first cell is empty
   m_table.SetValue(0,0,"-");
//--- Headings for columns
   for(int c=1; c<COLUMNS1_TOTAL; c++)
     {
      for(int r=0; r<1; r++)
         m_table.SetValue(c,r,"SYMBOL "+string(c));
     }
//--- Headings for rows, text alignment method - to the right
   for(int c=0; c<1; c++)
     {
      for(int r=1; r<ROWS1_TOTAL; r++)
        {
         m_table.SetValue(c,r,"PARAMETER "+string(r));
         m_table.TextAlign(c,r,ALIGN_RIGHT);
        }
     }
//--- Data and table formatting (background colour and cell colour)
   for(int c=1; c<COLUMNS1_TOTAL; c++)
     {
      for(int r=1; r<ROWS1_TOTAL; r++)
        {
         m_table.SetValue(c,r,string(c)+":"+string(r));
         m_table.TextColor(c,r,(c%2==0)? clrRed : clrRoyalBlue);
         m_table.CellColor(c,r,(r%2==0)? clrWhiteSmoke : clrWhite);
        }
     }
//--- Update the table to show changes
   m_table.UpdateTable();
//--- Add the object to the common array of object groups
   CWndContainer::AddToElementsArray(0,m_table);
   return(true);
  }
//+------------------------------------------------------------------+

Compile, run the example, click on the lowest row of the table (everything is fine), and then click on it again. On the second click an error pops up:

2016.10.24 03:37:16.407 ChartWindow02 (USDCHF,H1)       array out of range in 'Table.mqh' (1091,86)

What to do, and what to do?

 
Artyom Trishkin:

Anatoly, the changes have been made. Take the example from MQL5\Indicators\Article07\ChartWindow02\ChartWindow02.mq5, correct the table in Program.mqh so that the number of rows coincides with the visible number of rows, or the number of columns coincides with the visible number of columns, or both. For example, like this:

...

Compile, run the example, click on the lowest row of the table (everything is fine), and then click on it again. An error appears on the second click:

2016.10.24 03:37:16.407 ChartWindow02 (USDCHF,H1)       array out of range in 'Table.mqh' (1091,86)

What to do, and what to do?

In the CScrollH and CScrollV classes in the ScrollBarControl() methods, you need to add an additional check on the visibility of the element as shown in the listing below:

//+------------------------------------------------------------------+
//| Scroll control|
//+------------------------------------------------------------------+
bool CScrollH::ScrollBarControl(const int x,const int y,const bool mouse_state)
  {
//--- Exit if there is no pointer to the form
   if(::CheckPointer(m_wnd)==POINTER_INVALID)
      return(false);
//--- Exit if the form is blocked by another element
   if(m_wnd.IsLocked() && m_wnd.IdActivatedElement()!=CElement::Id())
      return(false);
//--- Exit if the item is hidden
   if(!CElement::IsVisible())
     return(false);

//--- Checking the focus over the slider
   m_thumb.MouseFocus(x>m_thumb.X() && x<m_thumb.X2() &&
                      y>m_thumb.Y() && y<m_thumb.Y2());
//--- Check and remember the state of the mouse button
   CScroll::CheckMouseButtonState(mouse_state);
//--- Change the colour of the list scroll bar
   CScroll::ChangeObjectsColor();
//--- If control is passed to the scroll bar, define the slider position
   if(CScroll::ScrollState())
     {
      //--- Moving the slider
      OnDragThumb(x);
      //--- Changes the slider position number
      CalculateThumbPos();
      return(true);
     }
   return(false);
  }

//---

The fix will be available in the next library update.

 
Anatoli Kazharski:

In the CScrollH and CScrollV classes in the ScrollBarControl() methods, we need to add an additional check for the visibility of the element as shown in the listing below:

...

The fix will be available in the next library update.

Thanks. I made these changes: in the Scrolls.mqh file in the CScrollH and CScrollV classes I added lines:

//+------------------------------------------------------------------+
//| Slider control|
//+------------------------------------------------------------------+
bool CScrollV::ScrollBarControl(const int x,const int y,const bool mouse_state)
  {
//--- Exit if there is no pointer to the form
   if(::CheckPointer(m_wnd)==POINTER_INVALID)
      return(false);
//--- If the form is not locked and the identifiers match
   if(m_wnd.IsLocked() && m_wnd.IdActivatedElement()!=CElement::Id())
      return(false);
//--- Exit if the item is hidden
   if(!CElement::IsVisible())
     return(false);

//--- Checking the focus over the slider
   m_thumb.MouseFocus(x>m_thumb.X() && x<m_thumb.X2() &&
                      y>m_thumb.Y() && y<m_thumb.Y2());
//--- Check and remember the state of the mouse button
   CScroll::CheckMouseButtonState(mouse_state);
//--- Change the colour of the slider
   CScroll::ChangeObjectsColor();
//--- If control is passed to the scroll bar, define the slider position
   if(CScroll::ScrollState())
     {
      //--- Moving the slider
      OnDragThumb(y);
      //--- Changes the slider position number
      CalculateThumbPos();
      return(true);
     }
//---
   return(false);
  }
//+------------------------------------------------------------------+


//+------------------------------------------------------------------+
//| Scroll control|
//+------------------------------------------------------------------+
bool CScrollH::ScrollBarControl(const int x,const int y,const bool mouse_state)
  {
//--- Exit if there is no pointer to the form
   if(::CheckPointer(m_wnd)==POINTER_INVALID)
      return(false);
   if(m_wnd.IsLocked() && m_wnd.IdActivatedElement()!=CElement::Id())
      return(false);
//--- Exit if the item is hidden
   if(!CElement::IsVisible())
     return(false);

//--- Checking the focus over the slider
   m_thumb.MouseFocus(x>m_thumb.X() && x<m_thumb.X2() &&
                      y>m_thumb.Y() && y<m_thumb.Y2());
//--- Check and remember the state of the mouse button
   CScroll::CheckMouseButtonState(mouse_state);
//--- Change the colour of the list scroll bar
   CScroll::ChangeObjectsColor();
//--- If control is passed to the scroll bar, define the slider position
   if(CScroll::ScrollState())
     {
      //--- Moving the slider
      OnDragThumb(x);
      //--- Changes the slider position number
      CalculateThumbPos();
      return(true);
     }
   return(false);
  }
//+------------------------------------------------------------------+

I compile and run the same test file from \MQL5\Indicators\Article07\ChartWindow02\ChartWindow02 .mq5, in which I changed the table building function in Program.mqh so that the number of all columns and rows coincides with the number of visible columns and rows:

//+------------------------------------------------------------------+
//|| Creates a table|
//+------------------------------------------------------------------+
bool CProgram::CreateTable(void)
  {
#define COLUMNS1_TOTAL (6)
#define ROWS1_TOTAL    (15)
//--- Save the pointer to the form
   m_table.WindowPointer(m_window1);
//--- Coordinates
   int x=m_window1.X()+TABLE1_GAP_X;
   int y=m_window1.Y()+TABLE1_GAP_Y;
//--- Number of visible columns and rows
   int visible_columns_total =6;
   int visible_rows_total    =15;
//--- Set properties before creation
   m_table.XSize(600);
   m_table.RowYSize(20);
   m_table.FixFirstRow(true);
   m_table.FixFirstColumn(true);
   m_table.LightsHover(true);
   m_table.SelectableRow(true);
   m_table.TextAlign(ALIGN_CENTER);
   m_table.HeadersColor(C'255,244,213');
   m_table.HeadersTextColor(clrBlack);
   m_table.CellColorHover(clrGold);
   m_table.TableSize(COLUMNS1_TOTAL,ROWS1_TOTAL);
   m_table.VisibleTableSize(visible_columns_total,visible_rows_total);
//--- Create a control
   if(!m_table.CreateTable(m_chart_id,m_subwin,x,y))
      return(false);
//--- Let's fill in the table:
// The first cell is empty
   m_table.SetValue(0,0,"-");
//--- Headings for columns
   for(int c=1; c<COLUMNS1_TOTAL; c++)
     {
      for(int r=0; r<1; r++)
         m_table.SetValue(c,r,"SYMBOL "+string(c));
     }
//--- Headings for rows, text alignment method is right aligned
   for(int c=0; c<1; c++)
     {
      for(int r=1; r<ROWS1_TOTAL; r++)
        {
         m_table.SetValue(c,r,"PARAMETER "+string(r));
         m_table.TextAlign(c,r,ALIGN_RIGHT);
        }
     }
//--- Data and table formatting (background colour and cell colour)
   for(int c=1; c<COLUMNS1_TOTAL; c++)
     {
      for(int r=1; r<ROWS1_TOTAL; r++)
        {
         m_table.SetValue(c,r,string(c)+":"+string(r));
         m_table.TextColor(c,r,(c%2==0)? clrRed : clrRoyalBlue);
         m_table.CellColor(c,r,(r%2==0)? clrWhiteSmoke : clrWhite);
        }
     }
//--- Update the table to show changes
   m_table.UpdateTable();
//--- Add the object to the common array of object groups
   CWndContainer::AddToElementsArray(0,m_table);
   return(true);
  }
//+------------------------------------------------------------------+

Compile and run ChartWindow02.ex5

Several times we click on the lowest row of the table and get a flight outside the array:

2016.10.25 01:39:22.899 ChartWindow02 (USDCHF,H1)       array out of range in 'Table.mqh' (1096,86)
Does it appear that nothing has changed?
 
Artyom Trishkin:

...

So nothing has changed?

After the changes I made, it doesn't play anymore. I may have made some other changes, but I don't remember. I don't have a history of minor edits.

Just in case, try replacing CScroll::Show() and CScroll::Hide() methods with these versions, if they are different:

//+------------------------------------------------------------------+
//| Shows the menu item|
//+------------------------------------------------------------------+
void CScroll::Show(void)
  {
//--- Exit if the number of list items is not greater than the number of the visible part of the list
   if(m_items_total<=m_visible_items_total)
      return;
//---
   m_area.Timeframes(OBJ_ALL_PERIODS);
   m_bg.Timeframes(OBJ_ALL_PERIODS);
   m_inc.Timeframes(OBJ_ALL_PERIODS);
   m_dec.Timeframes(OBJ_ALL_PERIODS);
   m_thumb.Timeframes(OBJ_ALL_PERIODS);
//--- Update the position of objects
   Moving(m_wnd.X(),m_wnd.Y(),true);
//--- Visibility status
   CElement::IsVisible(true);
  }
//+------------------------------------------------------------------+
//| Hides the menu item|
//+------------------------------------------------------------------+
void CScroll::Hide(void)
  {
   m_area.Timeframes(OBJ_NO_PERIODS);
   m_bg.Timeframes(OBJ_NO_PERIODS);
   m_inc.Timeframes(OBJ_NO_PERIODS);
   m_dec.Timeframes(OBJ_NO_PERIODS);
   m_thumb.Timeframes(OBJ_NO_PERIODS);
//--- Visibility status
   CElement::IsVisible(false);
  }


//---

If it doesn't help, you'll have to wait for the next update.

 
Anatoli Kazharski:

But after the changes I made, it doesn't play anymore. I may have made some other changes, but I don't remember. I don't mark the history of minor edits.

Just in case, try replacing CScroll::Show() and CScroll::Hide() methods with these versions, if they are different:

//+------------------------------------------------------------------+
//| Shows the menu item|
//+------------------------------------------------------------------+
void CScroll::Show(void)
  {
//--- Exit if the number of list items is not greater than the number of the visible part of the list
   if(m_items_total<=m_visible_items_total)
      return;
//---
   m_area.Timeframes(OBJ_ALL_PERIODS);
   m_bg.Timeframes(OBJ_ALL_PERIODS);
   m_inc.Timeframes(OBJ_ALL_PERIODS);
   m_dec.Timeframes(OBJ_ALL_PERIODS);
   m_thumb.Timeframes(OBJ_ALL_PERIODS);
//--- Update the position of objects
   Moving(m_wnd.X(),m_wnd.Y(),true);
//--- Visibility status
   CElement::IsVisible(true);
  }
//+------------------------------------------------------------------+
//| Hides the menu item|
//+------------------------------------------------------------------+
void CScroll::Hide(void)
  {
   m_area.Timeframes(OBJ_NO_PERIODS);
   m_bg.Timeframes(OBJ_NO_PERIODS);
   m_inc.Timeframes(OBJ_NO_PERIODS);
   m_dec.Timeframes(OBJ_NO_PERIODS);
   m_thumb.Timeframes(OBJ_NO_PERIODS);
//--- Visibility status
   CElement::IsVisible(false);
  }


//---

If it doesn't help, you'll have to wait for the next update.

Thanks, it seems to help. There were missing lines about visibility status in Show():

//--- Visibility status
   CElement::IsVisible(true);

: and Hide():

//--- Visibility status
   CElement::IsVisible(false);
 
Artyom Trishkin:

Thanks, it seems to have worked. There were missing lines about visibility status in Show():

//--- Visibility status
   CElement::IsVisible(true);

and Hide():

//--- Visibility status
   CElement::IsVisible(false);

One more correction is needed. Visibility should be set before updating the position of the element. Like this:

...
//--- Visibility status
   CElement::IsVisible(true);
//--- Update the position of objects
   Moving(m_wnd.X(),m_wnd.Y(),true);
...
 

Interactivity of interface elements implemented via timer is a strange solution. - Why should it be implemented through (or with) a timer? - Of course, it will consume a lot of resources.

You don't need a timer at all to control the states of controls.

1. Record the current coordinates of all elements in the array (map). Make adjustments to the location coordinates of all form objects as you move the window.

2. Make a function to find elements by coordinates (localiser). The function will loop through the recorded current element coordinates and find the element that is now under the cursor.

3. Call the localiser from the "CHARTEVENT_MOUSE_MOVE" event (on the cursor movement event).

4. Pass the object name returned from the localiser to the interactivity function, where the ObjectSetInteger() function will be applied to the object, which will force it to change its colour.


As you can see, there is no timer in this scheme, so there is no unnecessary consumption of resources.

 
Реter Konow:

Interactivity of interface elements implemented via timer is a strange solution. - Why should it be implemented through (or with) a timer? - Of course, it will consume a lot of resources.

...

As you can see, there is no timer in this scheme, so there is no unnecessary resource consumption.

You are wrong. I have already answered in enough detail in the comments above why it was done this way.

It doesn't consume a lot of resources now (now in Windows 10 too). Did you read the article (and the comments too) ?

//---

P.S. By the way, CPU resource consumption in different terminals (MT4/MT5) and OS versions (Windows) is very different under equal conditions. In Windows 7 the MetaTrader 4 terminal showed itself significantly better than MetaTrader 5. Unfortunately, I can't give you the figures now, as I have already completely switched to Windows 10.

As for optimisation, I have not exhausted all the options yet. There is still something to optimise and an understanding of how.