量化交易软件:图形界面 XI渲染控件 (统合构建14.2)
首篇文章 图形界面 I: 函数库结构的准备 (第 1 章) 详细研究了这个函数库。本系列每篇文章的最后, 提供了当前开发阶段的完整版函数库。文件必须放置于存档中所在的相同目录下。
在更新版的函数库中, 所有控件将在 OBJ_BITMAP_LABEL 类型的单独图形对象上绘制。此外, 赫兹量化将继续描述函数库代码的全局优化。此描述已在 以前的文章 中开始。现在赫兹量化来研究函数库中核心类的变化。新版本的函数库已经变得更加面向对象。代码变得更加简明易懂。这有助于用户根据自己的任务独立开发函数库。

编辑切换为居中
绘制控件的方法
已在 CElement 类已声明了一个 画布类的实例。其方法允许创建一个对象来绘制和删除它。若有必要, 可以获得它的指针。
class CElement : public CElementBase { protected: //--- 绘制控件的画布 CRectCanvas m_canvas; //--- public: //--- 返回指向控件画布的指针 CRectCanvas *CanvasPointer(void) { return(::GetPointer(m_canvas)); } };
现在有一个通用的方法来创建一个用于绘制控件外观的对象 (画布)。它位于 CElement 基类中, 可以从函数库的所有控件类访问。CElement::CreateCanvas() 方法用于创建此类型的图形对象。作为参数, 必须传递 (1) 名称, (2) 坐标, (3) 维度和 (4) 颜色格式。省缺格式为 COLOR_FORMAT_ARGB_NORMALIZE, 这可令控件变得透明。如果传递了无效维度, 它们将在方法的开头被修正。一旦在运行 MQL 应用程序的图表上对象了创建并加载, 将会为其设置基本属性, 这在以前的所有控件类中不断重复。
class CElement : public CElementBase { public: //--- 创建画布 bool CreateCanvas(const string name,const int x,const int y, const int x_size,const int y_size,ENUM_COLOR_FORMAT clr_format=COLOR_FORMAT_ARGB_NORMALIZE); }; //+------------------------------------------------------------------+ //| 创建绘制控件的画布 | //+------------------------------------------------------------------+ bool CElement::CreateCanvas(const string name,const int x,const int y, const int x_size,const int y_size,ENUM_COLOR_FORMAT clr_format=COLOR_FORMAT_ARGB_NORMALIZE) { //--- 调整尺寸 int xsize =(x_size<1)? 50 : x_size; int ysize =(y_size<1)? 20 : y_size; //--- 重置最后的错误 ::ResetLastError(); //--- 创建对象 if(!m_canvas.CreateBitmapLabel(m_chart_id,m_subwin,name,x,y,xsize,ysize,clr_format)) { ::Print(__FUNCTION__," > 创建绘制控件的画布失败 ("+m_class_name+"): ",::GetLastError()); return(false); } //--- 重置最后的错误 ::ResetLastError(); //--- 获取指向基类的指针 CChartObject *chart=::GetPointer(m_canvas); //--- 挂载到图表 if(!chart.Attach(m_chart_id,name,(int)m_subwin,(int)1)) { ::Print(__FUNCTION__," > 将绘图画布挂载到图表失败: ",::GetLastError()); return(false); } //--- 属性 m_canvas.Tooltip("\n"); m_canvas.Corner(m_corner); m_canvas.Selectable(false); //--- 除窗体外, 所有控件的优先级高于主控件 Z_Order((dynamic_cast<CWindow*>(&this)!=NULL)? 0 : m_main.Z_Order()+1); //--- 坐标 m_canvas.X(x); m_canvas.Y(y); //--- 大小 m_canvas.XSize(x_size); m_canvas.YSize(y_size); //--- 距极点的偏移 m_canvas.XGap(CalculateXGap(x)); m_canvas.YGap(CalculateYGap(y)); return(true); }
赫兹量化来转进到绘制控件的基本方法。它们都位于 CElement 类中, 并声明为 virtual。
首先来绘制背景。在基本版中, 它只是简单地使用 CElement::DrawBackground() 方法填充颜色。如有必要, 可以启用透明度。为此, 请使用 CElement::Alpha() 方法, Alpha 通道值从 0 到 255 作为参数传递。零值意味着完全透明。在当前版本中, 透明度仅适用于背景填充和边框。文字和图像将保持完全不透明, 并清除所有 alpha 通道值。
class CElement : public CElementBase { protected: //--- alpha 通道值 (控件的透明度) uchar m_alpha; //--- public: //--- alpha 通道值 (控件的透明度) void Alpha(const uchar value) { m_alpha=value; } uchar Alpha(void) const { return(m_alpha); } //--- protected: //--- 绘制背景 virtual void DrawBackground(void); }; //+------------------------------------------------------------------+ //| 绘制背景 | //+------------------------------------------------------------------+ void CElement::DrawBackground(void) { m_canvas.Erase(::ColorToARGB(m_back_color,m_alpha)); }
通常需要为特定的控件画一个边框。CElement::DrawBorder() 方法在画布对象的边缘周围绘制一个边框。Rectangle() 方法也可以用于此目的。它绘制一个未经填充的矩形。
class CElement : public CElementBase { protected: //--- 绘制边框 virtual void DrawBorder(void); }; //+------------------------------------------------------------------+ //| 绘制边框 | //+------------------------------------------------------------------+ void CElement::DrawBorder(void) { //--- 坐标 int x1=0,y1=0; int x2=m_canvas.X_Size()-1; int y2=m_canvas.Y_Size()-1; //--- 绘制一个未经填充的矩形 m_canvas.Rectangle(x1,y1,x2,y2,::ColorToARGB(m_border_color,m_alpha)); }
上一篇文章中已经提到可以将任意数量的图片组分配给任何控件。所以, 绘制控件的方法必须能够输出用户设置的所有图像。CElement::DrawImage() 方法即用于此目的。程序按顺序 遍历所有的组 和 其中的图片, 将它们逐像素输出到画布。在输出图像的循环开始之前, 检测组中当前所选的图片。参见此方法的代码:
class CElement : public CElementBase { protected: //--- 绘制图片 virtual void DrawImage(void); }; //+------------------------------------------------------------------+ //| 绘制图片 | //+------------------------------------------------------------------+ void CElement::DrawImage(void) { //--- 组的数量 uint group_total=ImagesGroupTotal(); //--- 绘制图片 for(uint g=0; g<group_total; g++) { //--- 所选图片的索引 int i=SelectedImage(g); //--- 如果没有图片 if(i==WRONG_VALUE) continue; //--- 坐标 int x =m_images_group[g].m_x_gap; int y =m_images_group[g].m_y_gap; //--- 大小 uint height =m_images_group[g].m_image[i].Height(); uint width =m_images_group[g].m_image[i].Width(); //--- 绘制 for(uint ly=0,p=0; ly<height; ly++) { for(uint lx=0; lx<width; lx++,p++) { //--- 如果没有颜色, 转至下一像素 if(m_images_group[g].m_image[i].Data(p)<1) continue; //--- 获取下层 (单元格背景) 的颜色, 和图标指定像素的颜色 uint background =::ColorToARGB(m_canvas.PixelGet(x+lx,y+ly)); uint pixel_color =m_images_group[g].m_image[i].Data(p); //--- 混合颜色 uint foreground=::ColorToARGB(m_clr.BlendColors(background,pixel_color)); //--- 绘制叠加图标的像素 m_canvas.PixelSet(x+lx,y+ly,foreground); } } } }
许多控件都有一个文本描述。可以使用 CElement::DrawText() 方法显示它。此方法中的若干字段允许根据控件的状态自定义文本的显示。控件有三个状态可用:
锁定;
按下;
聚焦 (鼠标悬停)。