文章 "图形界面 X: 标准图表控件 (集成编译 4)" - 页 2

 
Artyom Trishkin:

阿纳托利,请告诉我错误的原因是什么?

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

更新前一切正常。现在,在创建 CTable 表时却出现了这个错误。

请告诉我哪里出错了--我已经打印了每一行--界面是建好了,但在某个地方出了问题,....。错误。

档案中的文件包含一个示例。

我忘了更正,因为之前在CCanvasTable 类中已经更正过了。

CTable 类中,您需要将当前版本的CreateScrollV() 和CreateScrollH() 方法替换为下表所示的方法:

//+------------------------------------------------------------------+
//|| 创建垂直滚动条
//+------------------------------------------------------------------+
bool CTable::CreateScrollV(void)
  {
//--- 保存表单指针
   m_scrollv.WindowPointer(m_wnd);
//--- 坐标
   int x=(m_anchor_right_window_side)? m_x-m_x_size+m_scrollv.ScrollWidth() : CElement::X2()-m_scrollv.ScrollWidth();
   int y=CElement::Y();
//--- 设置尺寸
   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);
//--- 创建滚动条
   if(!m_scrollv.CreateScroll(m_chart_id,m_subwin,x,y,m_rows_total,m_visible_rows_total))
      return(false);
//--- 如果现在不需要,请隐藏
   if(m_rows_total<=m_visible_rows_total)
      m_scrollv.Hide();
//---
   return(true);
  }
//+------------------------------------------------------------------+
//|| 创建水平滚动条
//+------------------------------------------------------------------+
bool CTable::CreateScrollH(void)
  {
//--- 保存表单指针
   m_scrollh.WindowPointer(m_wnd);
//--- 坐标
   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();
//--- 设置尺寸
   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);
//--- 创建滚动条
   if(!m_scrollh.CreateScroll(m_chart_id,m_subwin,x,y,m_columns_total,m_visible_columns_total))
      return(false);
//--- 如果现在不需要,请隐藏
   if(m_columns_total<=m_visible_columns_total)
      m_scrollh.Hide();
//---
   return(true);
  }


//---

CLabelsTable 类也需要做类似的更改。这些修正将在下一次更新中进行。

 
Anatoli Kazharski:

之前在CCanvasTable 类中做过更正,我忘了更正。

CTable 类中,您需要将当前版本的CreateScrollV() 和CreateScrollH() 方法替换为下表所示的方法:

...
//---

CLabelsTable 类也需要进行类似的更改。这些修正将在下一次更新中进行。

О!谢谢!
 
Anatoli Kazharski:

之前在CCanvasTable 类中做过更正,我忘了更正。

CTable 类中,您需要将当前版本的CreateScrollV() 和CreateScrollH() 方法替换为下表所示的方法:

//+------------------------------------------------------------------+
//|| 创建垂直滚动条
//+------------------------------------------------------------------+
bool CTable::CreateScrollV(void)
  {
//--- 保存表单指针
   m_scrollv.WindowPointer(m_wnd);
//--- 坐标
   int x=(m_anchor_right_window_side)? m_x-m_x_size+m_scrollv.ScrollWidth() : CElement::X2()-m_scrollv.ScrollWidth();
   int y=CElement::Y();
//--- 设置尺寸
   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);
//--- 创建滚动条
   if(!m_scrollv.CreateScroll(m_chart_id,m_subwin,x,y,m_rows_total,m_visible_rows_total))
      return(false);
//--- 如果现在不需要,请隐藏
   if(m_rows_total<=m_visible_rows_total)
      m_scrollv.Hide();
//---
   return(true);
  }
//+------------------------------------------------------------------+
//|| 创建水平滚动条
//+------------------------------------------------------------------+
bool CTable::CreateScrollH(void)
  {
//--- 保存表单指针
   m_scrollh.WindowPointer(m_wnd);
//--- 坐标
   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();
//--- 设置尺寸
   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);
//--- 创建滚动条
   if(!m_scrollh.CreateScroll(m_chart_id,m_subwin,x,y,m_columns_total,m_visible_columns_total))
      return(false);
//--- 如果现在不需要,请隐藏
   if(m_columns_total<=m_visible_columns_total)
      m_scrollh.Hide();
//---
   return(true);
  }


//---

CLabelsTable 类也需要做类似的更改。下一次更新中将进行修复。

Anatoly,修改已经完成。以 MQL5\Indicators\Article07\ChartWindow02\ChartWindow02.mq5 中的表格为例,修改 Program.mqh 中的表格,使行数与可见行数一致,或列数与可见列数一致,或两者都一致。例如

//+------------------------------------------------------------------+
//|| 创建表格|
//+------------------------------------------------------------------+
bool CProgram::CreateTable(void)
  {
#define COLUMNS1_TOTAL (6)
#define ROWS1_TOTAL    (15)
//--- 保存指向表单的指针
   m_table.WindowPointer(m_window1);
//--- 坐标
   int x=m_window1.X()+TABLE1_GAP_X;
   int y=m_window1.Y()+TABLE1_GAP_Y;
//--- 可见列数和行数
   int visible_columns_total =6;
   int visible_rows_total    =15;
//--- 创建前设置属性
   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);
//--- 创建一个控件
   if(!m_table.CreateTable(m_chart_id,m_subwin,x,y))
      return(false);
//--- 让我们填写表格:
// 第一个单元格为空
   m_table.SetValue(0,0,"-");
//--- 栏目标题
   for(int c=1; c<COLUMNS1_TOTAL; c++)
     {
      for(int r=0; r<1; r++)
         m_table.SetValue(c,r,"SYMBOL "+string(c));
     }
//--- 行标题,文本对齐方式 - 向右
   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);
        }
     }
//--- 数据和表格格式化(背景颜色和单元格颜色)
   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);
        }
     }
//-- 更新表格以显示变化
   m_table.UpdateTable();
//--- 将对象添加到对象组的通用数组中
   CWndContainer::AddToElementsArray(0,m_table);
   return(true);
  }
//+------------------------------------------------------------------+

编译并运行示例,点击表格的最低行(一切正常),然后再次点击。第二次点击时,弹出了一个错误:

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

怎么办?

 
Artyom Trishkin:

阿纳托利,修改已经完成。以 MQL5\Indicators\Article07\ChartWindow02\ChartWindow02.mq5 中的示例为例,修改 Program.mqh 中的表格,使行数与可见行数一致,或列数与可见列数一致,或两者都一致。例如,像这样

...

编译并运行示例,点击表格的最低行(一切正常),然后再次点击。第二次点击时出现错误:

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

怎么办?

CScrollH CScrollV 类的ScrollBarControl() 方法中,需要对元素的可见性添加额外检查,如下表所示

//+------------------------------------------------------------------+
//| 滚动控制|
//+------------------------------------------------------------------+
bool CScrollH::ScrollBarControl(const int x,const int y,const bool mouse_state)
  {
//--- 如果没有表单指针,则退出
   if(::CheckPointer(m_wnd)==POINTER_INVALID)
      return(false);
//--- 如果表单被其他元素挡住,则退出
   if(m_wnd.IsLocked() && m_wnd.IdActivatedElement()!=CElement::Id())
      return(false);
//--- 如果项目被隐藏,则退出
   if(!CElement::IsVisible())
     return(false);

//--- 检查滑块上的焦点
   m_thumb.MouseFocus(x>m_thumb.X() && x<m_thumb.X2() &&
                      y>m_thumb.Y() && y<m_thumb.Y2());
//-- 检查并记住鼠标按钮的状态
   CScroll::CheckMouseButtonState(mouse_state);
//--- 更改列表滚动条的颜色
   CScroll::ChangeObjectsColor();
//--- 如果控制传递给了滚动条,则定义滑块位置
   if(CScroll::ScrollState())
     {
      //--- 移动滑块
      OnDragThumb(x);
      //--- 更改滑块位置编号
      CalculateThumbPos();
      return(true);
     }
   return(false);
  }

//---

该修复将在下一次库更新中提供。

 
Anatoli Kazharski:

CScrollH CScrollV 类的ScrollBarControl() 方法中,我们需要对元素的可见性添加额外的检查,如下表所示

...

该修复将在下一次库更新中提供。

谢谢。我做了这些修改:在 Scrolls.mqh 文件的CScrollH 和 CScrollV类中 添加了几行:

//+------------------------------------------------------------------+
//| 滑块控制|
//+------------------------------------------------------------------+
bool CScrollV::ScrollBarControl(const int x,const int y,const bool mouse_state)
  {
//--- 如果没有表单指针,则退出
   if(::CheckPointer(m_wnd)==POINTER_INVALID)
      return(false);
//--- 如果表单未锁定且标识符匹配
   if(m_wnd.IsLocked() && m_wnd.IdActivatedElement()!=CElement::Id())
      return(false);
//--- 如果项目被隐藏,则退出
   if(!CElement::IsVisible())
     return(false);

//--- 检查滑块上的焦点
   m_thumb.MouseFocus(x>m_thumb.X() && x<m_thumb.X2() &&
                      y>m_thumb.Y() && y<m_thumb.Y2());
//-- 检查并记住鼠标按钮的状态
   CScroll::CheckMouseButtonState(mouse_state);
//--- 更改滑块的颜色
   CScroll::ChangeObjectsColor();
//--- 如果控制传递给了滚动条,则定义滑块位置
   if(CScroll::ScrollState())
     {
      //--- 移动滑块
      OnDragThumb(y);
      //--- 更改滑块位置编号
      CalculateThumbPos();
      return(true);
     }
//---
   return(false);
  }
//+------------------------------------------------------------------+


//+------------------------------------------------------------------+
//| 滚动控制|
//+------------------------------------------------------------------+
bool CScrollH::ScrollBarControl(const int x,const int y,const bool mouse_state)
  {
//--- 如果没有表单指针,则退出
   if(::CheckPointer(m_wnd)==POINTER_INVALID)
      return(false);
   if(m_wnd.IsLocked() && m_wnd.IdActivatedElement()!=CElement::Id())
      return(false);
//--- 如果项目被隐藏,则退出
   if(!CElement::IsVisible())
     return(false);

//--- 检查滑块上的焦点
   m_thumb.MouseFocus(x>m_thumb.X() && x<m_thumb.X2() &&
                      y>m_thumb.Y() && y<m_thumb.Y2());
//-- 检查并记住鼠标按钮的状态
   CScroll::CheckMouseButtonState(mouse_state);
//--- 更改列表滚动条的颜色
   CScroll::ChangeObjectsColor();
//--- 如果控制传递给了滚动条,则定义滑块位置
   if(CScroll::ScrollState())
     {
      //--- 移动滑块
      OnDragThumb(x);
      //--- 更改滑块位置编号
      CalculateThumbPos();
      return(true);
     }
   return(false);
  }
//+------------------------------------------------------------------+

我编译并运行了来自 \MQL5\Indicators\Article07\ChartWindow02\ChartWindow02.mq5 的相同测试文件,其中我更改了 Program.mqh 中的建表函数,使所有列和行的数量与可见列和行的数量一致:

//+------------------------------------------------------------------+
//|| 创建表格|
//+------------------------------------------------------------------+
bool CProgram::CreateTable(void)
  {
#define COLUMNS1_TOTAL (6)
#define ROWS1_TOTAL    (15)
//--- 保存指向表单的指针
   m_table.WindowPointer(m_window1);
//--- 坐标
   int x=m_window1.X()+TABLE1_GAP_X;
   int y=m_window1.Y()+TABLE1_GAP_Y;
//--- 可见列数和行数
   int visible_columns_total =6;
   int visible_rows_total    =15;
//--- 创建前设置属性
   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);
//--- 创建一个控件
   if(!m_table.CreateTable(m_chart_id,m_subwin,x,y))
      return(false);
//--- 让我们填写表格:
// 第一个单元格为空
   m_table.SetValue(0,0,"-");
//--- 栏目标题
   for(int c=1; c<COLUMNS1_TOTAL; c++)
     {
      for(int r=0; r<1; r++)
         m_table.SetValue(c,r,"SYMBOL "+string(c));
     }
//--- 行标题,文本对齐方式 - 向右
   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);
        }
     }
//--- 数据和表格格式化(背景颜色和单元格颜色)
   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);
        }
     }
//-- 更新表格以显示变化
   m_table.UpdateTable();
//--- 将对象添加到对象组的通用数组中
   CWndContainer::AddToElementsArray(0,m_table);
   return(true);
  }
//+------------------------------------------------------------------+

编译并运行 ChartWindow02.ex5

我们点击了几次表格的最低行,得到的结果是在数组外飞行:

2016.10.25 01:39:22.899 ChartWindow02 (USDCHF,H1)       array out of range in 'Table.mqh' (1096,86)
看起来没有任何变化吗?
 
Artyom Trishkin:

...

所以什么都没变?

在我做了更改之后,它就不再播放了。我可能还做了其他改动,但我不记得了。我没有小修改的历史记录。

为了以防万一,如果CScroll::Show() 和CScroll::Hide() 方法不同,请尝试用这些版本替换它们:

//+------------------------------------------------------------------+
//| 显示菜单项|
//+------------------------------------------------------------------+
void CScroll::Show(void)
  {
//--- 如果列表项的数量不大于列表可见部分的数量,则退出
   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);
//-- 更新对象的位置
   Moving(m_wnd.X(),m_wnd.Y(),true);
//--- 可见性状态
   CElement::IsVisible(true);
  }
//+------------------------------------------------------------------+
//| 隐藏菜单项|
//+------------------------------------------------------------------+
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);
//--- 可见性状态
   CElement::IsVisible(false);
  }


//---

如果没有帮助,你只能等待下一次更新了。

 
Anatoli Kazharski:

但在我做了更改之后,它就不再播放了。我可能还做了其他改动,但我不记得了。我没有标记小修改的历史记录。

为了以防万一,如果CScroll::Show() 和CScroll::Hide() 方法不同,请尝试用这些版本替换它们:

//+------------------------------------------------------------------+
//| 显示菜单项|
//+------------------------------------------------------------------+
void CScroll::Show(void)
  {
//--- 如果列表项的数量不大于列表可见部分的数量,则退出
   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);
//-- 更新对象的位置
   Moving(m_wnd.X(),m_wnd.Y(),true);
//--- 可见性状态
   CElement::IsVisible(true);
  }
//+------------------------------------------------------------------+
//| 隐藏菜单项|
//+------------------------------------------------------------------+
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);
//--- 可见性状态
   CElement::IsVisible(false);
  }


//---

如果还没用,那就等下次更新吧。

谢谢,似乎有帮助。显示()和隐藏()方法中缺少了关于可见性状态的行:

//--- 可见性状态
   CElement::IsVisible(true);

和 Hide():

//--- 可见性状态
   CElement::IsVisible(false);
 
Artyom Trishkin:

谢谢,看来成功了。在 Show():

//--- 可见性状态
   CElement::IsVisible(true);

和 Hide():

//--- 可见性状态
   CElement::IsVisible(false);

还需要更正。应该在更新元素位置之前设置可见性。就像这样

...
//--- 可见性状态
   CElement::IsVisible(true);
//-- 更新对象的位置
   Moving(m_wnd.X(),m_wnd.Y(),true);
...
 

通过计时器实现界面元素的交互性是一种奇怪的解决方案。- 为什么要通过(或使用)计时器来实现呢?- 当然,这会消耗大量资源。

控制控件的状态根本不需要计时器。

1.记录数组(地图)中所有元素的当前坐标。在移动窗口时调整所有窗体对象的位置坐标。

2.2. 制作一个通过坐标查找元素的函数(定位器)。该函数将循环查看记录的当前元素坐标,并找到光标下的元素。

3.从 "CHARTEVENT_MOUSE_MOVE "事件(光标移动事件)中调用定位器。

4.4. 将从 localiser 返回的对象名称传递给交互函数,在交互函数中,ObjectSetInteger() 函数 将应用于该对象,从而强制其改变颜色。


如您所见,该方案中没有计时器,因此不会消耗不必要的资源。

 
Реter Konow:

通过计时器实现界面元素的交互性是一种奇怪的解决方案。- 为什么要通过(或使用)计时器来实现呢?- 当然,这会消耗大量资源。

...

正如您所看到的,这个方案中没有定时器,因此不存在不必要的资源消耗。

你错了。我已经在上面的评论中详细回答了为什么要这样做。

它现在不会消耗大量资源(现在在Windows 10 中也是如此)。你读了这篇文章(以及评论)吗?

//---

顺便 说一下,在同等条件下,不同终端(MT4/MT5)和不同操作系统版本(Windows)的 CPU 资源消耗是完全不同的。在Windows 7 中,MetaTrader 4 终端的性能明显优于MetaTrader 5。遗憾的是,我现在无法提供相关数据,因为我已经完全切换到Windows 10

至于优化,我还没有用尽所有选项。还有一些需要优化的地方,也需要了解如何优化。