外汇EA编写教程:在MetaTrader5中创建交互式应用程序以显示RSS订阅

目录

  • 简介
  • 1。RSS文档
  • 2。应用程序的总体架构
    • 2.1。用户界面
    • 2.2。代码实现
    • 2.3。简单XML分析器
    • 2.4。EA码
    • 2.5。控件初始化方法
    • 2.6。RSS文档处理方法
      • 2.2.1.LoadDocument()
      • ItemNodes Total ()
      • 2.2.3.自由文档树()
    • 2.7。从文件树中提取信息的方法
      • 获取频道标题()
      • 2.7.2。GETTITLE()
    • 2.8。文本格式设置方法
      • 2.2.1.格式字符串()
      • 2.2.2.ReleVeTAG()
      • 2.8删除特殊字符()
      • 2.2.4.标记()
    • 2.9。独立控制事件的处理方法
      • OnChangeListView()。
      • 2.9OnObjectEdit()
      • OnClickButton 1/2()。
    • 2.10。CRSSreader类的实现
    • 2.11。EA码
  • 总结

简介

本文“通过MQL4语言读取RSS新闻”介绍了一个基本脚本,它使用终端中的一个简单库来显示RSS订阅,最初用于解析HTML文档。

随着MetaTrader 5和MQL5成为语言,我认为可以创建交互式应用程序来呈现RSS内容。本文描述了如何使用MQL5标准类库以及由MQL5社区成员开发的一些工具来制作此应用程序。

1。RSS文档

在我们详细讨论这个应用程序的细节之前,我认为有必要回顾RSS文档的总体架构。

要理解以下描述,您需要熟悉可扩展标记语言和相关概念。如果您不熟悉XML文档,请参阅XML教程。注意,本文中的节点表示XML文档中的标记。正如上面的MQL4文章中提到的,RSS文件是具有特定标记结构的XML文档。

每个RSS文档都有一个全局容器rss标记。这就是RSS文档的共同点。频道标签是RSS标签的直接继承者。它包含关于网站和来源的描述性信息。因此,RSS文档可以根据其包含的特定标签而有所不同。当时,所有的RSS文档都需要一些标记来将它们标识为RSS文件。

所需标签为:

  • 标题-频道的主题。包括网站名称;
  • 链接-提供频道网站的URL;
  • 说明-网站简介;
  • 项-至少一个内容标记。

上面的标签必须是频道标签的子级。项目节点包含与特定内容相关的数据。

每个项目节点包含以下标记:

  • 标题-内容主题;
  • 链接-指向内容的URL链接;
  • 说明-内容介绍;
  • 日期-网站发布的数据内容。

所有RSS文档都包含标签并具有相同的结果。

完整的RSS文档示例如下。


<rss version="2.0">
  <channel>
    <title>Xul.fr: Tutorials and Applications of the Web 2.0</title>
    <link>https://www.xul.fr/</link>
    <description>Ajax, JavaScript, XUL, RSS, PHP and all technologies of the Web 2.0. Building a CMS, tutorial and application.</description>
    <pubDate>Wed, 07 Feb 2007 14:20:24 GMT</pubDate>
    <item>
    <title>News on interfaces of the Web in 2010</title>
    <link>https://www.xul.fr/en/2010.php</link>
    <description>Steve Jobs explains why iPad does not support Adobe Flash:&lt;em&gt;At Adobe they are lazy. 
    They have the potential to make  interesting things, but they refuse to do so. 
    Apple does not support Flash because it is too buggy.
     Each time a Mac crashes, most often it is because of Flash. Nobody will use Flash. 
     The world is moving  to &lt;a href="https://www.xul.fr/en/html5/" target="_parent"&gt;HTML 5&lt;/a&gt;</description>
     <pubDate>Sat, 11 Dec 10 09:41:06 +0100</pubDate>
    </item>
    <item>
      <title>Textured Border in CSS</title>
      <link>https://www.xul.fr/en/css/textured-border.php</link>
      <description>   The border attribute of the style sheets can vary in color and width, but it was not expected to give it a texture. However, only a CSS rule is required to add this graphic effect...   The principle is to assign a texture to the whole &lt;em&gt;fieldset&lt;/em&gt; and insert into it another &lt;em&gt;fieldset&lt;/em&gt; (for rounded edges) or a &lt;em&gt;div&lt;/em&gt;, whose background is the same as that of the page</description>
      <pubDate>Wed, 29 Jul 09 15:56:54  0200</pubDate>
    </item>
    <item>
      <title>Create an RSS feed from SQL, example with WordPress</title>
      <link>https://www.xul.fr/feed/rss-sql-wordpress.html</link>
      <description>Articles contain at least the following items: And possibly, author's name, or an image. This produces the following table: The returned value is true if the database is found, false otherwise. It remains to retrieve the data from the array</description>
      <pubDate>Wed, 29 Jul 09 15:56:50  0200</pubDate>
    </item>
    <item>
      <title>Firefox 3.5</title>
      <link>https://www.xul.fr/gecko/firefox35.php</link>
      <description>Les balises audio et vid&#xE9;o sont impl&#xE9;ment&#xE9;es. Le format de donn&#xE9;e JSON est reconnu nativement par Firefox. L'avantage est d'&#xE9;viter l'utilisation de la fonction eval() qui n'est pas s&#xFB;r, ou d'employer des librairies additionnelles, qui est nettement plus lent</description>
      <pubDate>Wed, 24 Jun 09 15:18:47  0200</pubDate>
    </item>
    <item>
      <title>Contestation about HTML 5</title>
      <link>https://www.xul.fr/en/html5/contestation.php</link>
      <description>  Nobody seemed to be worried so far, but the definition of HTML 5 that is intended to be the format of billions of Web pages in coming years, is conducted and decided by a single person! &lt;em&gt;Hey, wait! Pay no attention to the multi-billions dollar Internet corporation behind the curtain. It's me Ian Hickson! I am my own man</description>
      <pubDate>Wed, 24 Jun 09 15:18:29  0200</pubDate>
    </item>
    <item>
      <title>Form Objects in HTML 4</title>
      <link>https://www.xul.fr/javascript/form-objects.php</link>
      <description>   It is created by the HTML &lt;em&gt;form&lt;/em&gt; tag:   The name or id attribute can access by script to its content. It is best to use both attributes with the same identifier, for the sake of compatibility.   The &lt;em&gt;action&lt;/em&gt; attribute indicates the page to which send the form data. If this attribute is empty, the page that contains the form that will be charged the data as parameters</description>
      <pubDate>Wed, 24 Jun 09 15:17:49  0200</pubDate>
    </item>
    <item>
      <title>DOM Tutorial</title>
      <link>https://www.xul.fr/en/dom/</link>
      <description>  The Document Object Model describes the structure of an XML or HTML document, a web page and allows access to each individual element.</description>
      <pubDate>Wed, 06 May 2009 18:30:11 GMT</pubDate>
    </item>    
  </channel>
</rss>

2。应用程序的总体架构

这里我给出了RSS阅读器将显示的信息描述和应用程序的用户图形界面。

应用程序首先显示频道标题,该标题包含在标题标记中。此信息将用作网站来源的参考。

这个应用程序还应该显示源代码描述的所有内容的屏幕截图。这涉及到标记文档中的所有项。对于每个标记,将显示内容的标题。最后,我希望RSS阅读器提供内容的描述,即每个标记描述中包含的内容。

2.1。用户界面

用户界面是应用程序用来呈现信息的功能。

用户界面的概念最好用下图表示。

应用程序会话框架

图1。应用程序会话框架

下图显示了构成SilverFox接口的未使用模块。

  • 首先是标题栏。此处显示频道标题。
  • 输入区域。用户在此处输入RSS源的地址。
  • 标题区域。此处显示特定内容的标题。
  • 文本区域。这里介绍内容的描述。
  • 列表区域。此滚动列表显示源中包含的所有内容的标题。
  • 左键重置并清空标题、文本和列表区域中显示的文本;
  • 更新当前源的按钮从当前加载的源检索最新更新。

RSS阅读器的工作原理如下:当程序加载到图表中时,空白程序对话框将显示用户在输入区域中键入他想要的RSS源地址,然后按Enter。这会将所有内容标题(例如,每个项目标签的标题)加载到列表视图区域中。列表以1开头,表示发布的最新内容。

每个列表项都是可点击的,点击每个列表项将突出显示,相应的标题描述内容将显示在文本区域中。同时,内容标题将在标题区域中显示得更清楚。如果由于任何原因加载源时发生错误,错误消息将显示在文本区域中。

重置按钮将用于清除任何文本区域、列表显示区域和标题区域。

更新当前源检查源的任何最近更新。

2.2。代码实现

RSS阅读器将作为EA实现,并将使用MQL5标准类库。

代码将包含在crssreader类中,该类是cappdialog类的子类。cappDialog类在对话框中给出。mQH file, which provides the function of title bar and application dialog implementations such as control maximization, minimization and closure. 这将是用户界面的基础,其他模块将在此基础上添加。添加的部分有:标题区、文本区、列表显示区和按钮。每个都是一个控件。按钮将作为控件实现,如按钮中所述。MQH文件。

ListViewArea中的列表显示控件。mqh文件将用于构造RSS阅读器的可视列表区域。

编辑控件显然用于形成在edit.mqh文件中定义的输入区域。

标题和文本区域的实现是一个挑战。问题是两者都必须支持多行文本。MQL5中的文本对象无法识别换行符。另一个问题是,一行文本对象中只能显示少量字符。这意味着,如果创建一个描述较长的文本对象,该对象将显示截断的文本,只显示一定数量的字符。通过尝试和错误信息,我发现只有63个字符显示,包括空格和标点符号。

为了克服这些困难,我决定在两个地方都使用修改后的列表可视化控件。对于标题区域,修订后的列表控件将不会滚动,并且列表项的数量将固定(2)。无法单击每个列表项或选择每个列表项,并且控件看起来不像列表。这两个列表项以两行文本显示。如果文本太长而不能放在一行中,它将被拆分为两行。标题区域的控制在标题栏中定义。MQH文件。

对于文本区域,采用类似的方法。此时,列表项的数量将是动态的,修改后的列表控件将可以垂直滚动。此控件在文本区域中给定。MQH文件。

这里提到的类库都用于处理用户界面。程序还需要引入一个非常重要的类库。它是用于解析XML文档的类库。

2.3。简单XML分析器

因为RSS文档是一个XML文件,Liquinaut开发的EasyXML-XML解析器类库可以在代码库中找到,并将在应用程序中使用。

这个类库非常广泛,几乎涵盖了RSS阅读器所需的所有功能。我修改了原来的类库,并添加了一些我认为必要的附加功能。

这些是次要的补充。第一个附加方法是loadXmlFromUrlWebReq()。此方法是loadxmlfromurl()方法的替代方法,它依赖wininet库来处理网页请求,loadxmlfromurlwebreq()使用内置的webrequest()函数从Internet下载。

//+------------------------------------------------------------------+
//| 使用MQL5webrequest函数加载给定url的xml                              |
//+------------------------------------------------------------------+
bool CEasyXml::loadXmlFromUrlWebReq(string pUrl)
  {
//---
   string cookie=NULL,headers;
   char post[],result[];
   int res;
//---
   string _url=pUrl;
   string sStream;
   res=WebRequest("GET",_url,cookie,NULL,5000,post,0,result,headers);
//--- 检查报错
   if(res==-1)
     {
      Err=EASYXML_ERR_WEBREQUEST_URL;
      return(Error());
     }
//-- 下载文件成功
   sStream=CharArrayToString(result,0,-1,CP_UTF8);
//---设置缓存文件
   if(blSaveToCache)
     {
      bool bResult=writeStreamToCacheFile(sStream);
      if(!bResult) Error(-1,false);
     }
//---
   return(loadXmlFromString(sStream));
  }

第二个附加方法是getErrorMsg(),它在错误分析器报告错误时检索错误信息。

string            GetErrorMsg(void){   return(ErrMsg);}

最后一个附加方法是修复我在测试easyxml解析器时发现的严重缺陷。

我发现这个类库无法识别XML类型表的声明。样式表的属性声明在代码中出错。这会导致程序陷入死循环,代码会继续搜索不存在的属性值。

Skipprolog()方法可以通过微小的修改进行更正。

//+------------------------------------------------------------------+
//| 跳过 xml 头                                                       |
//+------------------------------------------------------------------+
bool CEasyXml::skipProlog(string &pText,int &pPos)
  {
//--- 跳过 xml 声明
   if(StringCompare(EASYXML_PROLOG_OPEN,StringSubstr(pText,pPos,StringLen(EASYXML_PROLOG_OPEN)))==0)
     {
      int iClose=StringFind(pText,EASYXML_PROLOG_CLOSE,pPos+StringLen(EASYXML_PROLOG_OPEN));

      if(blDebug) Print("### Prolog ###    ",StringSubstr(pText,pPos,(iClose-pPos)+StringLen(EASYXML_PROLOG_CLOSE)));

      if(iClose>0)
        {
         pPos=iClose+StringLen(EASYXML_PROLOG_CLOSE);
           } else {
         Err=EASYXML_INVALID_PROLOG;
         return(false);
        }
     }
//--- 跳过样表声明
   if(StringCompare(EASYXML_STYLESHEET_OPEN,StringSubstr(pText,pPos,StringLen(EASYXML_STYLESHEET_OPEN)))==0)
     {
      int iClose=StringFind(pText,EASYXML_STYLESHEET_CLOSE,pPos+StringLen(EASYXML_STYLESHEET_OPEN));

      if(blDebug) Print("### Prolog ###    ",StringSubstr(pText,pPos,(iClose-pPos)+StringLen(EASYXML_STYLESHEET_CLOSE)));

      if(iClose>0)
        {
         pPos=iClose+StringLen(EASYXML_STYLESHEET_CLOSE);
           } else {
         Err=EASYXML_INVALID_PROLOG;
         return(false);
        }
     }
//--- 跳过备注
   if(!skipWhitespaceAndComments(pText,pPos,"")) return(false);

//--- 跳过 文档类型
   if(StringCompare(EASYXML_DOCTYPE_OPEN,StringSubstr(pText,pPos,StringLen(EASYXML_DOCTYPE_OPEN)))==0)
     {
      int iClose=StringFind(pText,EASYXML_DOCTYPE_CLOSE,pPos+StringLen(EASYXML_DOCTYPE_OPEN));

      if(blDebug) Print("### DOCTYPE ###    ",StringSubstr(pText,pPos,(iClose-pPos)+StringLen(EASYXML_DOCTYPE_CLOSE)));

      if(iClose>0)
        {
         pPos=iClose+StringLen(EASYXML_DOCTYPE_CLOSE);
           } else {
         Err=EASYXML_INVALID_DOCTYPE;
         return(false);
        }
     }

//--- 跳过备注
   if(!skipWhitespaceAndComments(pText,pPos,"")) return(false);

   return(true);
  }

除了上述问题,Liquinaut的代码不需要更改,easyxml。MQH是一个很好的工具。

2.4。EA码

现在应用程序所需的所有库都准备好了,现在是时候集成这些组件来定义crssreader类了。

请注意,RSS阅读器EA代码将从定义CRSSreader类开始。

//+------------------------------------------------------------------+
//|                                                    RssReader.mq5 |
//|                                                          Ufranco |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Ufranco"
#property link      "https://www.mql5.com"
#property version   "1.00"
#include <Controls/Dialog.mqh>
#include <Controls/Edit.mqh>
#include <Controls/Button.mqh>
#include <TitleArea.mqh>
#include <TextArea.mqh>
#include <ListViewArea.mqh>
#include <easyxml.mqh>
//+------------------------------------------------------------------+
//| 定义                                                              |
//+------------------------------------------------------------------+
//--- 缩进和间隔
#define INDENT_LEFT                         (11)      // 左边距(留出边界宽度)
#define INDENT_TOP                          (11)      // 顶边距(留出边界宽度)
#define INDENT_RIGHT                        (11)      // 右边距(留出边界宽度)
#define INDENT_BOTTOM                       (11)      // 底边距(留出边界宽度)
#define CONTROLS_GAP_X                      (5)       // 间距 X轴坐标
#define CONTROLS_GAP_Y                      (5)       // 间距 Y轴坐标

#define EDIT_HEIGHT                         (20)      // Y轴坐标尺寸
#define BUTTON_WIDTH                        (150)     // X轴坐标尺寸
#define BUTTON_HEIGHT                       (20)      // Y轴坐标尺寸
#define TEXTAREA_HEIGHT                     (131)     // Y轴坐标尺寸
#define LIST_HEIGHT                         (93)      // Y轴坐标尺寸

首先包括必要的文件。此定义指令用于设置控制像素的物理参数。

indent-left、indent-right、indent-top和indent-down设置控件和应用程序对话框之间的间距。

  • 控件间隙是控件之间的垂直距离。
  • 编辑高度设置构成输入区域的编辑控件的高度。
  • 按钮宽度和按钮高度定义所有按钮控件的高度。
  • textfarea_height是文本区域的高度。
  • list_height设置列表控件的高度。

定义之后,我们开始定义crssreader类。

//+------------------------------------------------------------------+
//| CRssReader类                                                     | 
//| 用途:RSS应用主类                                                   |
//+------------------------------------------------------------------+
class CRssReader : public CAppDialog
  {
private:
   int               m_shift;                    // 第一个向目标标记的索引
   string            m_rssurl;                   // 最新源的网址拷贝 
   string            m_textareaoutput[];         // 用于输出到文本区域的字符串数组
   string            m_titleareaoutput[];        // 用于输出到标题区域的字符串数组
   CButton           m_button1;                  // 按钮对象
   CButton           m_button2;                  // 按钮对象      
   CEdit             m_edit;                     // 输入区域
   CTitleArea        m_titleview;                // 显示区域对象
   CListViewArea     m_listview;                 // 列表对象
   CTextArea         m_textview;                 // 文本区域对象
   CEasyXml          m_xmldocument;              // xml文档对象
   CEasyXmlNode     *RssNode;                    // 根节点对象
   CEasyXmlNode     *ChannelNode;                // 频道节点对象
   CEasyXmlNode     *ChannelChildNodes[];        // 频道子节点对象数组

如前所述,crssreader继承自cappdialog类。

这个类有几个私有属性,如下所示:

  • m_shift-此整数变量存储通道childnodes数组中第一个项节点的索引。
  • m_rssurl-是一个字符串,其中包含作为输入参数的最新URL副本。
  • m_extarea output[]-是一个字符串数组,每个元素对应一行具有一定字符数的文本。
  • m_titlearea output[]-也是一个字符串数组,与前面的字符串数组类似。
  • m_button1和m_button2是cButton类型的对象。
  • m_ListView是列表控件的对象。
  • m_edit是实例化输入区域的CEdit对象。
  • 标题视图是标题区域的对象;
  • 文本视图-文本区域对象;
  • m_xmldocument是一个XML文档对象。
  • rssnode是根节点对象。
  • channel node是通道节点对象。
  • channelChildNodes数组是一系列指向channel标记的直接后续项的指针。

我们班只有两种常用方法。

public:
                     CRssReader(void);
                    ~CRssReader(void);
   //--- 创建
   virtual bool      Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2);
   //--- 图表事件处理函数
   virtual bool      OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam);

第一个方法create()设置应用程序对话框的大小和初始位置。

它还初始化所有RSS阅读器的控件(请记住,我们的类继承自cappdialog类,因此父类及其子类的通用方法可以由实例crssreader调用)。

//+------------------------------------------------------------------+
//| 创建                                                              |
//+------------------------------------------------------------------+
bool CRssReader::Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2)
  {
   if(!CAppDialog::Create(chart,name,subwin,x1,y1,x2,y2))
      return(false);
//--- 创建独立控件
   if(!CreateEdit())
      return(false);
   if(!CreateButton1())
      return(false);
   if(!CreateButton2())
      return(false);
   if(!CreateTitleView())
      return(false);
   if(!CreateListView())
      return(false);
   if(!CreateTextView())
      return(false);
//--- 成功
   return(true);
  }

接下来是onEvent()方法,它将特定事件分配给控件和处理程序函数进行交互。

//+------------------------------------------------------------------+
//| 事件处理                                                          |  
//+------------------------------------------------------------------+
EVENT_MAP_BEGIN(CRssReader)
ON_EVENT(ON_CHANGE,m_listview,OnChangeListView)
ON_EVENT(ON_END_EDIT,m_edit,OnObjectEdit)
ON_EVENT(ON_CLICK,m_button1,OnClickButton1)
ON_EVENT(ON_CLICK,m_button2,OnClickButton2)
EVENT_MAP_END(CAppDialog)

2.5。控件初始化方法

createedit()、createButton1()、createButton2()、createTitleView()、createListView()和createTextView()保护方法由create()调用以初始化相应的控件。

protected:
   // --- 创建控件
   bool              CreateEdit(void);
   bool              CreateButton1(void);
   bool              CreateButton2(void);
   bool              CreateTitleView(void);
   bool              CreateListView(void);
   bool              CreateTextView(void);

正是在这些函数中设置控件的大小、位置和属性(如字体、字体大小、颜色、边界颜色、边界类型)。

//+------------------------------------------------------------------+
//| 创建展示区域                                                       | 
//+------------------------------------------------------------------+
bool CRssReader::CreateEdit(void)
  {
//--- 坐标
   int x1=INDENT_LEFT;
   int y1=INDENT_TOP;
   int x2=ClientAreaWidth()-INDENT_RIGHT;
   int y2=y1+EDIT_HEIGHT;
//--- 创建
   if(!m_edit.Create(m_chart_id,m_name+"Edit",m_subwin,x1,y1,x2,y2))
      return(false);
   if(!m_edit.Text("Please enter the web address of an Rss feed"))
      return(false);
   if(!m_edit.ReadOnly(false))
      return(false);
   if(!Add(m_edit))
      return(false);
//--- 成功
   return(true);
  }
//+------------------------------------------------------------------+
//| 创建按钮 1                                                        |
//+------------------------------------------------------------------+ 
bool CRssReader::CreateButton1(void)
  {
//--- 坐标
   int x1=INDENT_LEFT;
   int y1=INDENT_TOP+(EDIT_HEIGHT+CONTROLS_GAP_Y);
   int x2=x1+BUTTON_WIDTH;
   int y2=y1+BUTTON_HEIGHT;
//--- 创建
   if(!m_button1.Create(m_chart_id,m_name+"Button1",m_subwin,x1,y1,x2,y2))
      return(false);
   if(!m_button1.Text("Reset"))
      return(false);
   if(!m_button1.Font("Comic Sans MS"))
      return(false);
   if(!m_button1.FontSize(8))
      return(false);
   if(!m_button1.Color(clrWhite))
      return(false);
   if(!m_button1.ColorBackground(clrBlack))
      return(false);
   if(!m_button1.ColorBorder(clrBlack))
      return(false);
   if(!m_button1.Pressed(true))
      return(false);
   if(!Add(m_button1))
      return(false);
//--- 成功
   return(true);
  }
//+------------------------------------------------------------------+
//| 创建按钮 2                                                        |
//+------------------------------------------------------------------+ 
bool CRssReader::CreateButton2(void)
  {
//--- 坐标
   int x1=(ClientAreaWidth()-INDENT_RIGHT)-BUTTON_WIDTH;
   int y1=INDENT_TOP+(EDIT_HEIGHT+CONTROLS_GAP_Y);
   int x2=ClientAreaWidth()-INDENT_RIGHT;
   int y2=y1+BUTTON_HEIGHT;
//--- 创建
   if(!m_button2.Create(m_chart_id,m_name+"Button2",m_subwin,x1,y1,x2,y2))
      return(false);
   if(!m_button2.Text("Update current feed"))
      return(false);
   if(!m_button2.Font("Comic Sans MS"))
      return(false);
   if(!m_button2.FontSize(8))
      return(false);
   if(!m_button2.Color(clrWhite))
      return(false);
   if(!m_button2.ColorBackground(clrBlack))
      return(false);
   if(!m_button2.ColorBorder(clrBlack))
      return(false);
   if(!m_button2.Pressed(true))
      return(false);
   if(!Add(m_button2))
      return(false);
//--- 成功
   return(true);
  }
//+------------------------------------------------------------------+
//| 创建展示区域                                                       | 
//+------------------------------------------------------------------+
bool CRssReader::CreateTitleView(void)
  {
//--- 坐标
   int x1=INDENT_LEFT;
   int y1=INDENT_TOP+(EDIT_HEIGHT+CONTROLS_GAP_Y)+BUTTON_HEIGHT+CONTROLS_GAP_Y;
   int x2=ClientAreaWidth()-INDENT_RIGHT;
   int y2=y1+(EDIT_HEIGHT*2);
   m_titleview.Current();
//--- 创建 
   if(!m_titleview.Create(m_chart_id,m_name+"TitleView",m_subwin,x1,y1,x2,y2))
     {
      Print("error creating title view");
      return(false);
     }
   else
     {
      for(int i=0;i<2;i++)
        {
         m_titleview.AddItem(" ");
        }
     }
   if(!Add(m_titleview))
     {
      Print("error adding title view");
      return(false);
     }
//--- 成功
   return(true);
  }
//+------------------------------------------------------------------+
//| 创建"ListView"组建                                                |
//+------------------------------------------------------------------+
bool CRssReader::CreateListView(void)
  {

//--- 坐标
   int x1=INDENT_LEFT;
   int y1=INDENT_TOP+((EDIT_HEIGHT+CONTROLS_GAP_Y)*2)+20+TEXTAREA_HEIGHT+CONTROLS_GAP_Y+BUTTON_HEIGHT+CONTROLS_GAP_Y;
   int x2=ClientAreaWidth()-INDENT_RIGHT;
   int y2=y1+LIST_HEIGHT;
//--- 创建
   if(!m_listview.Create(m_chart_id,m_name+"ListView",m_subwin,x1,y1,x2,y2))
      return(false);
   if(!Add(m_listview))
      return(false);
//--- 用字符填充
   for(int i=0;i<20;i++)
      if(!m_listview.AddItem(" "))
         return(false);
//--- 成功
   return(true);
  }
//+------------------------------------------------------------------+
//| 创建展示区域                                                       | 
//+------------------------------------------------------------------+
bool CRssReader::CreateTextView(void)
  {
//--- 坐标
   int x1=INDENT_LEFT;
   int y1=INDENT_TOP+((EDIT_HEIGHT+CONTROLS_GAP_Y)*2)+20+BUTTON_HEIGHT+CONTROLS_GAP_Y;
   int x2=ClientAreaWidth()-INDENT_RIGHT;
   int y2=y1+TEXTAREA_HEIGHT;
   m_textview.Current();
//--- 创建 
   if(!m_textview.Create(m_chart_id,m_name+"TextArea",m_subwin,x1,y1,x2,y2))
     {
      Print("error creating text area view");
      return(false);
     }
   else
     {
      for(int i=0;i<1;i++)
        {
         m_textview.AddItem(" ");
        }
      m_textview.VScrolled(true);
      ChartRedraw();
     }
   if(!Add(m_textview))
     {
      Print("error adding text area view");
      return(false);
     }
//----成功      
   return(true);
  }

2.6。RSS文档处理方法

// --- rss文档处理
   bool              LoadDocument(string filename);
   int               ItemNodesTotal(void);
   void              FreeDocumentTree(void);

2.2.1.LoadDocument()

这个函数有许多重要的用途。主要是处理网站请求。加载xmlFromUrlWebReq()用于下载RSS文件。

如果成功,该函数将执行第二步来初始化rssnode、channelnode指针并填充channelchildnodes数组。在这里设置了m_rssurl和m_shift指针。完成所有这些操作后,函数返回true。

如果无法下载RSS文件,将清除标题区域、列表区域和文本区域中的文本,并在标题栏中显示状态信息。然后在文本区域中输出错误消息。函数返回false。

//+------------------------------------------------------------------+
//|   加载文档                                                        |
//+------------------------------------------------------------------+
bool CRssReader::LoadDocument(string filename)
  {
   if(!m_xmldocument.loadXmlFromUrlWebReq(filename))
     {
      m_textview.ItemsClear();
      m_listview.ItemsClear();
      m_titleview.ItemsClear();
      CDialog::Caption("Failed to load Feed");
      if(!m_textview.AddItem(m_xmldocument.GetErrorMsg()))
         Print("error displaying error message");
      return(false);
     }
   else
     {
      m_rssurl=filename;
      RssNode=m_xmldocument.getDocumentRoot();
      ChannelNode=RssNode.FirstChild();
      if(CheckPointer(RssNode)==POINTER_INVALID || CheckPointer(ChannelNode)==POINTER_INVALID)
         return(false);
     }
   ArrayResize(ChannelChildNodes,ChannelNode.Children().Total());
   for(int i=0;i<ChannelNode.Children().Total();i++)
     {
      ChannelChildNodes[i]=ChannelNode.Children().At(i);
     }
   m_shift=ChannelNode.Children().Total()-ItemNodesTotal();
   return(true);
  }

ItemNodes Total ()

此函数用于LoadDocument()方法。它返回从通道标签继承的项目节点数的整数值。

如果没有项目节点,则无效的RSS文档和函数返回0。

//+------------------------------------------------------------------+
//|  统计文档中标签项数量的函数                                          |     
//+------------------------------------------------------------------+
int CRssReader::ItemNodesTotal(void)
  {
   int t=0;
   for(int i=0;i<ChannelNode.Children().Total();i++)
     {
      if(ChannelChildNodes[i].getName()=="item")
        {
         t++;
        }
      else continue;
     }
   return(t);
  }

2.2.3.自由文档树()

此函数重置所有ceasyxmlnode指针。

首先,通过调用carrayobj类的shutdown()方法删除channelChildNodes数组的元素。然后调用一次arrayfree(),然后释放该数组。

然后删除通道节点的指针,清除easyxml解析器的文档树。这些操作导致rssnode和channelnode指针变为空指针,这就是为它们分配空值的原因。

//+------------------------------------------------------------------+
//|  释放文档树并重置指针值                                             |
//+------------------------------------------------------------------+ 
void CRssReader::FreeDocumentTree(void)
  {
   ChannelNode.Children().Shutdown();
   ArrayFree(ChannelChildNodes);
   RssNode.Children().Shutdown();
   m_xmldocument.Clear();
   m_shift=0;
   RssNode=NULL;
   ChannelNode=NULL;
  }

2.7。从文档树中提取信息的方法

这些函数用于从RSS文档中检索文本。

//--- 获取函数
   string            getChannelTitle(void);
   string            getTitle(CEasyXmlNode *Node);
   string            getDescription(CEasyXmlNode *Node);
   string            getDate(CEasyXmlNode *Node);

获取频道标题()

此函数获取RSS文档当前频道的标题。

首先检查通道节点的指针有效性。如果指针有效,它将循环访问所有通道节点的直接后续节点,以查找主题标记。

对于循环,使用m_shift属性限制循环搜索的通道节点数。如果函数没有成功返回空值。

//+------------------------------------------------------------------+
//| 获取频道标题                                                       |
//+------------------------------------------------------------------+
string CRssReader::getChannelTitle(void)
  {
   string ret=NULL;
   if(!CheckPointer(ChannelNode)==POINTER_INVALID)
     {
      for(int i=0;i<m_shift;i++)
        {
         if(ChannelChildNodes[i].getName()=="title")
           {
            ret=ChannelChildNodes[i].getValue();
            break;
           }
         else continue;
        }
     }
//---return value
   return(ret);
  }

2.7.2。GETTITLE()

此函数将输入指针指向项标记,遍历标记的继承对象,查找主题标记并返回其值。

getDescription()和getDate()函数的格式相同,运行方式也相同。成功调用此函数将返回一个字符串或空值。

//+------------------------------------------------------------------+
//| 显示标题                                                          |
//+------------------------------------------------------------------+
string CRssReader::getTitle(CEasyXmlNode *Node)
  {
   int k=Node.Children().Total();
   string n=NULL;
   for(int i=0;i<k;i++)
     {
      CEasyXmlNode*subNode=Node.Children().At(i);
      if(subNode.getName()=="title")
        {
         n=subNode.getValue();
         break;
        }
      else continue;
     }
   return(n);
  }
//+------------------------------------------------------------------+
//| 显示描述                                                          |
//+------------------------------------------------------------------+
string CRssReader::getDescription(CEasyXmlNode *Node)
  {
   int k=Node.Children().Total();
   string n=NULL;
   for(int i=0;i<k;i++)
     {
      CEasyXmlNode*subNode=Node.Children().At(i);
      if(subNode.getName()=="description")
        {
         n=subNode.getValue();
         break;
        }
      else continue;
     }
   return(n);
  }
//+------------------------------------------------------------------+
//| 显示日期                                                          |
//+------------------------------------------------------------------+ 
string CRssReader::getDate(CEasyXmlNode *Node)
  {
   int k=Node.Children().Total();
   string n=NULL;
   for(int i=0;i<k;i++)
     {
      CEasyXmlNode*subNode=Node.Children().At(i);
      if(subNode.getName()=="pubDate")
        {
         n=subNode.getValue();
         break;
        }
      else continue;
     }
   return(n);
  }

2.8。文本格式设置方法

这些函数用于为输出文本对象准备文本,以克服某些文本对象所面临的限制。

 //--- 文本格式 
   bool              FormatString(string v,string &array[],int n);
   string            removeTags(string _string);
   string            removeSpecialCharacters(string s_tring);
   int               tagPosition(string _string,int w);

2.2.1.格式字符串()

这是从RSS文档中提取文档并将其输出到应用程序的主要功能。

它的主要功能是通过使用字符串作为输入参数,将文本分为“n”个字符和一行。n”是每行文本的字符数。在文本中的每个“n”字符之后,代码将查找插入新行的适当位置。然后处理整个字符串,并在原始文本位置插入一行新的源字符。

函数用于创建每个元素不超过“n”个字符的字符串数组。此函数返回一个布尔值和一个字符串作为输出。

//+------------------------------------------------------------------+
//|  文本区域面板输出字符格式化                                          |
//+------------------------------------------------------------------+
bool CRssReader::FormatString(string v,string &array[],int n)
  {
   ushort ch[],space,fullstop,comma,semicolon,newlinefeed;
   string _s,_k;
   space=StringGetCharacter(" ",0);
   fullstop=StringGetCharacter(".",0);
   comma=StringGetCharacter(",",0);
   semicolon=StringGetCharacter(";",0);
   newlinefeed=StringGetCharacter("/n",0);
   _k=removeTags(v);
   _s=removeSpecialCharacters(_k);
   int p=StringLen(_s);
   ArrayResize(ch,p+1);
   int d=StringToShortArray(_s,ch,0,-1);
   for(int i=1;i<d;i++)
     {
      int t=i%n;
      if(!t== 0)continue;
      else 
        {
         if(ch[(i/n)*n]==fullstop || ch[(i/n)*n]==semicolon || ch[(i/n)*n]==comma)
           {
            ArrayFill(ch,((i/n)*n)+1,1,newlinefeed);
           }
         else
           {
            for(int k=i;k>=0;k--)
              {
               if(ch[k]==space)
                 {
                  ArrayFill(ch,k,1,newlinefeed);
                  break;
                 }
               else continue;
              }
           }
        }
     }
   _s=ShortArrayToString(ch,0,-1);
   int s=StringSplit(_s,newlinefeed,array);
   if(!s>0)
     {return(false);}
// 成功 
   return(true);
  }

2.2.2.ReleVeTAG()

当我注意到许多RSS文档在XML节点中包含HTML标记时,此函数是必需的。

一些RSS文档以这种方式发布,许多RSS聚合应用程序在浏览器中运行。

此函数将字符串作为参数,并在文本中查找标记。如果在文本中找到任何标记,则标记字符的起始和结束位置将保存在二维数组a[]中。此数组用于提取标记之间的文本并返回提取的字符串。如果找不到标记,则返回输入字符串。

//+------------------------------------------------------------------+
//| 移除标签                                                          |
//+------------------------------------------------------------------+
string CRssReader::removeTags(string _string)
  {
   string now=NULL;
   if(StringFind(_string,"<",0)>-1)
     {
      int v=0,a[][2];
      ArrayResize(a,2024);
      for(int i=0;i<StringLen(_string);i++)
        {
         int t=tagPosition(_string,i);
         if(t>0)
           {
            v++;
            a[v-1][0]=i;
            a[v-1][1]=t;
           }
         else continue;
        }
      ArrayResize(a,v);
      for(int i=0;i<v-1;i++)
        {
         now+=StringSubstr(_string,(a[i][1]+1),(a[i+1][0]-(a[i][1]+1)));
        }
     }
   else
     {
      now=_string;
     }
   return(now);
  }

这些文件的一些例子如下。

<item>            
    <title>GIGABYTE X99-Gaming G1 WIFI Motherboard Review</title>
    <author>Ian Cutress</author>
    <description><![CDATA[ <p>The gaming motherboard range from a manufacturer is one with a lot of focus in terms of design and function due to the increase in gaming related PC sales. On the Haswell-E side of gaming, GIGABYTE is putting forward the X99-Gaming G1 WIFI at the top of its stack, and this is what we are reviewing today.&nbsp;</p>
<p align="center"><a href='https://dynamic1.anandtech.com/www/delivery/ck.php?n=a1f2f01f&amp;cb=582254849' target='_blank'><img src='https://dynamic1.anandtech.com/www/delivery/avw.php?zoneid=24&amp;cb=582254849&amp;n=a1f2f01f' border='0' alt='' /></a><img src="https://toptenreviews.122.2o7.net/b/ss/tmn-test/1/H.27.3--NS/0" height="1" width="1" border="0" alt="" /></p>]]></description>
    <link>http://www.anandtech.com/show/8788/gigabyte-x99-gaming-g1-wifi-motherboard-review</link>
        <pubDate>Thu, 18 Dec 2014 10:00:00 EDT</pubDate>
        <guid isPermaLink="false">tag:www.anandtech.com,8788:news</guid>
        <category><![CDATA[ Motherboards]]></category>                               
</item>  

2.8删除特殊字符()

此函数用正确的字符替换特定的字符串。

例如,XML中的符号和字符串可以表示为“&amp;amp”。发生这种情况时,此函数将替换为内置的StringReplace()函数。

//+------------------------------------------------------------------+
//| 移除特殊的字符                                                     |
//+------------------------------------------------------------------+ 
string CRssReader::removeSpecialCharacters(string s_tring)
  {
   string n=s_tring;
   StringReplace(n,"&amp;","&");
   StringReplace(n,"&#39;","'");
   StringReplace(n,"&nbsp;"," ");
   StringReplace(n,"&ldquo;","/'");
   StringReplace(n,"&rdquo;","/'");
   StringReplace(n,"&quot;","/"");
   StringReplace(n,"&ndash;","-");
   StringReplace(n,"&rsquo;","'");
   StringReplace(n,"&gt;","");
   return(n);
  }

2.2.4.标记()

此函数是在函数removeTags()中调用的辅助函数。它的输入是一个字符串和一个整数值。

输入整数值表示字符串中字符的位置,函数从中搜索标记的起始字符。例如,“&lt;”。如果找到了开始标记,则函数将开始查找结束标记,而uoh将返回输出的结束标记字符“&gt;”的位置。如果找不到标记,则返回-1。

//+------------------------------------------------------------------+
//| 标签位置                                                          |
//+------------------------------------------------------------------+
int CRssReader::tagPosition(string _string,int w)
  {
   int iClose=-1;
   if(StringCompare("<",StringSubstr(_string,w,StringLen("<")))==0)
     {
      iClose=StringFind(_string,">",w+StringLen("<"));
     }

   return(iClose);
  }

2.9。独立控制事件的处理方法

这些函数处理由特定控件捕获的事件。

//--- 控件事件处理函数
   void              OnChangeListView(void);
   void              OnObjectEdit(void);
   void              OnClickButton1(void);
   void              OnClickButton2(void);
  };

OnChangeListView()。

这是在单击应用程序可视化列表区域中的列表项时调用的事件处理程序。

此功能用于在RSS文档中可视化某些内容摘要。

此函数清除文本和标题区域中的任何文本,从文档树中检索新数据并输出。只有当channel childnodes数组不为空时,才会发生所有这一切。

//+------------------------------------------------------------------+
//| 事件处理                                                          |
//+------------------------------------------------------------------+
void CRssReader::OnChangeListView(void)
  {
   int a=0,k=0,l=0;
   a=m_listview.Current()+m_shift;
   if(ArraySize(ChannelChildNodes)>a)
     {
      if(m_titleview.ItemsClear())
        {
         if(!FormatString(getTitle(ChannelChildNodes[a]),m_titleareaoutput,55))
           {
            return;
           }
         else
         if(ArraySize(m_titleareaoutput)>0)
           {
            for(l=0;l<ArraySize(m_titleareaoutput);l++)
              {
               m_titleview.AddItem(removeSpecialCharacters(m_titleareaoutput[l]));
              }
           }
        }
      if(m_textview.ItemsClear())
        {
         if(!FormatString(getDescription(ChannelChildNodes[a]),m_textareaoutput,35))
            return;
         else
         if(ArraySize(m_textareaoutput)>0)
           {
            for(k=0;k<ArraySize(m_textareaoutput);k++)
              {
               m_textview.AddItem(m_textareaoutput[k]);
              }
            m_textview.AddItem(" ");
            m_textview.AddItem("Date|"+getDate(ChannelChildNodes[a]));
           }
         else return;
        }
     }
  }

2.9OnObjectEdit()

当用户在输入区域输入文本时,将调用处理函数。

函数调用LoadDocument()方法。如果下载成功,文本将从整个程序中删除。接下来,标题更改,新内容输出到列表区域。

//+------------------------------------------------------------------+
//| 事件处理                                                          |
//+------------------------------------------------------------------+
void CRssReader::OnObjectEdit(void)
  {
   string f=m_edit.Text();
   if(StringLen(f)>0)
     {
      if(ArraySize(ChannelChildNodes)<1)
        {
         CDialog::Caption("Loading...");
         if(LoadDocument(f))
           {
            if(!CDialog::Caption(removeSpecialCharacters(getChannelTitle())))
               Print("error changing caption");
            if(m_textview.ItemsClear() && m_listview.ItemsClear() && m_titleview.ItemsClear())
              {
               for(int i=0;i<ItemNodesTotal()-1;i++)
                 {
                  if(!m_listview.AddItem(removeSpecialCharacters(IntegerToString(i+1)+"."+getTitle(ChannelChildNodes[i+m_shift]))))
                    {
                     Print("can not add item to listview area");
                     return;
                    }
                 }
              }
            else
              {
               Print("text area/listview area not cleared");
               return;
              }
           }
         else return;
        }
      else
        {
         FreeDocumentTree();
         CDialog::Caption("Loading new RSS Feed...");
         if(LoadDocument(f))
           {
            if(!CDialog::Caption(removeSpecialCharacters(getChannelTitle())))
               Print("error changing caption");
            if(m_textview.ItemsClear() && m_listview.ItemsClear() && m_titleview.ItemsClear())
              {
               for(int i=0;i<ItemNodesTotal()-1;i++)
                 {
                  if(!m_listview.AddItem(removeSpecialCharacters(IntegerToString(i+1)+"."+getTitle(ChannelChildNodes[i+m_shift]))))
                    {
                     Print("can not add item to listview area");
                     return;
                    }
                 }
              }
            else
              {
               Print("text area/listview area not cleared");
               return;
              }
           }
         else return;
        }
     }
   else return;
  }

OnClickButton 1/2()。

当用户单击重置或源更新按钮时,将调用这些处理程序。

首次加载EA时,单击重置按钮以更新应用程序对话框。

Click the “check for feed update” button to trigger the callback LoadDocument () method. 将下载RSS源数据并更新可视列表区域。

//+------------------------------------------------------------------+
//| 事件处理  更新程序对话框                                            |
//+------------------------------------------------------------------+   
void CRssReader::OnClickButton1(void)
  {
   if(ArraySize(ChannelChildNodes)<1)
     {
      if(!m_edit.Text("Enter the web address of an Rss feed"))
         Print("error changing edit text");
      if(!CDialog::Caption("RSSReader"))
         Print("error changing caption");
      if(m_textview.ItemsClear() && m_listview.ItemsClear() && m_titleview.ItemsClear())
        {
         for(int i=0;i<20;i++)
           {
            if(!m_listview.AddItem(" "))
               Print("error adding to listview");
           }
         m_listview.VScrolled(true);
         for(int i=0;i<1;i++)
           {
            m_textview.AddItem(" ");
           }
         m_textview.VScrolled(true);
         for(int i=0;i<2;i++)
           {
            m_titleview.AddItem(" ");
           }
         return;
        }
     }
   else
     {
      FreeDocumentTree();
      if(!m_edit.Text("Enter the web address of an Rss feed"))
         Print("error changing edit text");
      if(!CDialog::Caption("RSSReader"))
         Print("error changing caption");
      if(m_textview.ItemsClear() && m_listview.ItemsClear() && m_titleview.ItemsClear())
        {
         for(int i=0;i<20;i++)
           {
            if(!m_listview.AddItem(" "))
               Print("error adding to listview");
           }
         m_listview.VScrolled(true);
         for(int i=0;i<1;i++)
           {
            m_textview.AddItem(" ");
           }
         m_textview.VScrolled(true);
         for(int i=0;i<2;i++)
           {
            m_titleview.AddItem(" ");
           }
         return;
        }
     }
  }
//+------------------------------------------------------------------+
//| 事件处理  更新当前源                                               |
//+------------------------------------------------------------------+ 
void CRssReader::OnClickButton2(void)
  {
   string f=m_rssurl;
   if(ArraySize(ChannelChildNodes)<1)
      return;
   else
     {
      FreeDocumentTree();
      CDialog::Caption("Checking for RSS Feed update...");
      if(LoadDocument(f))
        {
         if(!CDialog::Caption(removeSpecialCharacters(getChannelTitle())))
            Print("error changing caption");
         if(m_textview.ItemsClear() && m_listview.ItemsClear() && m_titleview.ItemsClear())
           {
            for(int i=0;i<ItemNodesTotal()-1;i++)
              {
               if(!m_listview.AddItem(removeSpecialCharacters(IntegerToString(i+1)+"."+getTitle(ChannelChildNodes[i+m_shift]))))
                 {
                  Print("can not add item to listview area");
                  return;
                 }
              }
           }
         else
           {
            Print("text area/listview area not cleared");
            return;
           }
        }
      else return;
     }
  }

这就完成了crssreader类的定义。

2.10。CRSSreader类的实现

//+------------------------------------------------------------------+
//| CRssReader类                                                     | 
//| 用途:RSS应用主类                                                   |
//+------------------------------------------------------------------+
class CRssReader : public CAppDialog
  {
private:
   int               m_shift;                   // 第一个项目标记的索引
   string            m_rssurl;                  // 最新源的网址副本 
   string            m_textareaoutput[];        // 用于输出到文本区域的字符串数组
   string            m_titleareaoutput[];       // 用于输出到标题区域的字符串数组
   CButton           m_button1;                 // 按钮对象
   CButton           m_button2;                 // 按钮对象      
   CEdit             m_edit;                    // 输入区域
   CTitleArea        m_titleview;               // 显示区域对象
   CListViewArea     m_listview;                // 列表对象
   CTextArea         m_textview;                // 文本区域对象
   CEasyXml          m_xmldocument;             // xml文档对象
   CEasyXmlNode     *RssNode;                   // 根节点对象
   CEasyXmlNode     *ChannelNode;               // 频道节点对象
   CEasyXmlNode     *ChannelChildNodes[];       // 频道子节点对象数组

public:
                     CRssReader(void);
                    ~CRssReader(void);
   //--- 创建
   virtual bool      Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2);
   //--- 图表事件处理函数
   virtual bool      OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam);

protected:
   // --- 创建控件
   bool              CreateEdit(void);
   bool              CreateButton1(void);
   bool              CreateButton2(void);
   bool              CreateTitleView(void);
   bool              CreateListView(void);
   bool              CreateTextView(void);
   // --- rss文档处理
   bool              LoadDocument(string filename);
   int               ItemNodesTotal(void);
   void              FreeDocumentTree(void);
   //--- 获取函数
   string            getChannelTitle(void);
   string            getTitle(CEasyXmlNode *Node);
   string            getDescription(CEasyXmlNode *Node);
   string            getDate(CEasyXmlNode *Node);
   //--- 文本格式 
   bool              FormatString(string v,string &array[],int n);
   string            removeTags(string _string);
   string            removeSpecialCharacters(string s_tring);
   int               tagPosition(string _string,int w);
   //--- 控件事件处理函数
   void              OnChangeListView(void);
   void              OnObjectEdit(void);
   void              OnClickButton1(void);
   void              OnClickButton2(void);
  };
//+------------------------------------------------------------------+
//| 事件处理                                                          |  
//+------------------------------------------------------------------+
EVENT_MAP_BEGIN(CRssReader)
ON_EVENT(ON_CHANGE,m_listview,OnChangeListView)
ON_EVENT(ON_END_EDIT,m_edit,OnObjectEdit)
ON_EVENT(ON_CLICK,m_button1,OnClickButton1)
ON_EVENT(ON_CLICK,m_button2,OnClickButton2)
EVENT_MAP_END(CAppDialog)
//+------------------------------------------------------------------+
//| 构造函数                                                          |
//+------------------------------------------------------------------+
CRssReader::CRssReader(void)
  {

  }
//+------------------------------------------------------------------+
//| 析构函数                                                          |
//+------------------------------------------------------------------+
CRssReader::~CRssReader(void)
  {
  }
//+------------------------------------------------------------------+
//| 创建                                                              |
//+------------------------------------------------------------------+
bool CRssReader::Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2)
  {
   if(!CAppDialog::Create(chart,name,subwin,x1,y1,x2,y2))
      return(false);
//--- 创建独立控件
   if(!CreateEdit())
      return(false);
   if(!CreateButton1())
      return(false);
   if(!CreateButton2())
      return(false);
   if(!CreateTitleView())
      return(false);
   if(!CreateListView())
      return(false);
   if(!CreateTextView())
      return(false);
//--- 成功
   return(true);
  }
//+------------------------------------------------------------------+
//| 创建展示区域                                                       | 
//+------------------------------------------------------------------+
bool CRssReader::CreateEdit(void)
  {
//--- 坐标
   int x1=INDENT_LEFT;
   int y1=INDENT_TOP;
   int x2=ClientAreaWidth()-INDENT_RIGHT;
   int y2=y1+EDIT_HEIGHT;
//--- 创建
   if(!m_edit.Create(m_chart_id,m_name+"Edit",m_subwin,x1,y1,x2,y2))
      return(false);
   if(!m_edit.Text("Please enter the web address of an Rss feed"))
      return(false);
   if(!m_edit.ReadOnly(false))
      return(false);
   if(!Add(m_edit))
      return(false);
//--- 成功
   return(true);
  }
//+------------------------------------------------------------------+
//| 创建按钮 1                                                        |
//+------------------------------------------------------------------+ 
bool CRssReader::CreateButton1(void)
  {
//--- 坐标
   int x1=INDENT_LEFT;
   int y1=INDENT_TOP+(EDIT_HEIGHT+CONTROLS_GAP_Y);
   int x2=x1+BUTTON_WIDTH;
   int y2=y1+BUTTON_HEIGHT;
//--- 创建
   if(!m_button1.Create(m_chart_id,m_name+"Button1",m_subwin,x1,y1,x2,y2))
      return(false);
   if(!m_button1.Text("Reset"))
      return(false);
   if(!m_button1.Font("Comic Sans MS"))
      return(false);
   if(!m_button1.FontSize(8))
      return(false);
   if(!m_button1.Color(clrWhite))
      return(false);
   if(!m_button1.ColorBackground(clrBlack))
      return(false);
   if(!m_button1.ColorBorder(clrBlack))
      return(false);
   if(!m_button1.Pressed(true))
      return(false);
   if(!Add(m_button1))
      return(false);
//--- 成功
   return(true);
  }
//+------------------------------------------------------------------+
//| 创建按钮 2                                                        |
//+------------------------------------------------------------------+ 
bool CRssReader::CreateButton2(void)
  {
//--- 坐标
   int x1=(ClientAreaWidth()-INDENT_RIGHT)-BUTTON_WIDTH;
   int y1=INDENT_TOP+(EDIT_HEIGHT+CONTROLS_GAP_Y);
   int x2=ClientAreaWidth()-INDENT_RIGHT;
   int y2=y1+BUTTON_HEIGHT;
//--- 创建
   if(!m_button2.Create(m_chart_id,m_name+"Button2",m_subwin,x1,y1,x2,y2))
      return(false);
   if(!m_button2.Text("Update current feed"))
      return(false);
   if(!m_button2.Font("Comic Sans MS"))
      return(false);
   if(!m_button2.FontSize(8))
      return(false);
   if(!m_button2.Color(clrWhite))
      return(false);
   if(!m_button2.ColorBackground(clrBlack))
      return(false);
   if(!m_button2.ColorBorder(clrBlack))
      return(false);
   if(!m_button2.Pressed(true))
      return(false);
   if(!Add(m_button2))
      return(false);
//--- 成功
   return(true);
  }
//+------------------------------------------------------------------+
//| 创建展示区域                                                       | 
//+------------------------------------------------------------------+
bool CRssReader::CreateTitleView(void)
  {
//--- 坐标
   int x1=INDENT_LEFT;
   int y1=INDENT_TOP+(EDIT_HEIGHT+CONTROLS_GAP_Y)+BUTTON_HEIGHT+CONTROLS_GAP_Y;
   int x2=ClientAreaWidth()-INDENT_RIGHT;
   int y2=y1+(EDIT_HEIGHT*2);
   m_titleview.Current();
//--- 创建 
   if(!m_titleview.Create(m_chart_id,m_name+"TitleView",m_subwin,x1,y1,x2,y2))
     {
      Print("error creating title view");
      return(false);
     }
   else
     {
      for(int i=0;i<2;i++)
        {
         m_titleview.AddItem(" ");
        }
     }
   if(!Add(m_titleview))
     {
      Print("error adding title view");
      return(false);
     }
//--- 成功
   return(true);
  }
//+------------------------------------------------------------------+
//| 创建"ListView"组建                                                |
//+------------------------------------------------------------------+
bool CRssReader::CreateListView(void)
  {

//--- 坐标
   int x1=INDENT_LEFT;
   int y1=INDENT_TOP+((EDIT_HEIGHT+CONTROLS_GAP_Y)*2)+20+TEXTAREA_HEIGHT+CONTROLS_GAP_Y+BUTTON_HEIGHT+CONTROLS_GAP_Y;
   int x2=ClientAreaWidth()-INDENT_RIGHT;
   int y2=y1+LIST_HEIGHT;
//--- 创建
   if(!m_listview.Create(m_chart_id,m_name+"ListView",m_subwin,x1,y1,x2,y2))
      return(false);
   if(!Add(m_listview))
      return(false);
//--- 用字符填充
   for(int i=0;i<20;i++)
      if(!m_listview.AddItem(" "))
         return(false);
//--- 成功
   return(true);
  }
//+------------------------------------------------------------------+
//| 创建展示区域                                                       | 
//+------------------------------------------------------------------+
bool CRssReader::CreateTextView(void)
  {
//--- 坐标
   int x1=INDENT_LEFT;
   int y1=INDENT_TOP+((EDIT_HEIGHT+CONTROLS_GAP_Y)*2)+20+BUTTON_HEIGHT+CONTROLS_GAP_Y;
   int x2=ClientAreaWidth()-INDENT_RIGHT;
   int y2=y1+TEXTAREA_HEIGHT;
   m_textview.Current();
//--- 创建 
   if(!m_textview.Create(m_chart_id,m_name+"TextArea",m_subwin,x1,y1,x2,y2))
     {
      Print("error creating text area view");
      return(false);
     }
   else
     {
      for(int i=0;i<1;i++)
        {
         m_textview.AddItem(" ");
        }
      m_textview.VScrolled(true);
      ChartRedraw();
     }
   if(!Add(m_textview))
     {
      Print("error adding text area view");
      return(false);
     }
//----成功      
   return(true);
  }
//+------------------------------------------------------------------+
//|   加载文档                                                        |
//+------------------------------------------------------------------+
bool CRssReader::LoadDocument(string filename)
  {
   if(!m_xmldocument.loadXmlFromUrlWebReq(filename))
     {
      m_textview.ItemsClear();
      m_listview.ItemsClear();
      m_titleview.ItemsClear();
      CDialog::Caption("Failed to load Feed");
      if(!m_textview.AddItem(m_xmldocument.GetErrorMsg()))
         Print("error displaying error message");
      return(false);
     }
   else
     {
      m_rssurl=filename;
      RssNode=m_xmldocument.getDocumentRoot();
      ChannelNode=RssNode.FirstChild();
      if(CheckPointer(RssNode)==POINTER_INVALID || CheckPointer(ChannelNode)==POINTER_INVALID)
         return(false);
     }
   ArrayResize(ChannelChildNodes,ChannelNode.Children().Total());
   for(int i=0;i<ChannelNode.Children().Total();i++)
     {
      ChannelChildNodes[i]=ChannelNode.Children().At(i);
     }
   m_shift=ChannelNode.Children().Total()-ItemNodesTotal();
   return(true);
  }
//+------------------------------------------------------------------+
//|  统计文档中标签项数量的函数                                          |     
//+------------------------------------------------------------------+
int CRssReader::ItemNodesTotal(void)
  {
   int t=0;
   for(int i=0;i<ChannelNode.Children().Total();i++)
     {
      if(ChannelChildNodes[i].getName()=="item")
        {
         t++;
        }
      else continue;
     }
   return(t);
  }
//+------------------------------------------------------------------+
//|  释放文档树并重置指针值                                             |
//+------------------------------------------------------------------+ 
void CRssReader::FreeDocumentTree(void)
  {
   ChannelNode.Children().Shutdown();
   ArrayFree(ChannelChildNodes);
   RssNode.Children().Shutdown();
   m_xmldocument.Clear();
   m_shift=0;
   RssNode=NULL;
   ChannelNode=NULL;
  }
//+------------------------------------------------------------------+
//| 获取频道标题                                                       |
//+------------------------------------------------------------------+
string CRssReader::getChannelTitle(void)
  {
   string ret=NULL;
   if(!CheckPointer(ChannelNode)==POINTER_INVALID)
     {
      for(int i=0;i<m_shift;i++)
        {
         if(ChannelChildNodes[i].getName()=="title")
           {
            ret=ChannelChildNodes[i].getValue();
            break;
           }
         else continue;
        }
     }
//---return value
   return(ret);
  }
//+------------------------------------------------------------------+
//| 显示标题                                                          |
//+------------------------------------------------------------------+
string CRssReader::getTitle(CEasyXmlNode *Node)
  {
   int k=Node.Children().Total();
   string n=NULL;
   for(int i=0;i<k;i++)
     {
      CEasyXmlNode*subNode=Node.Children().At(i);
      if(subNode.getName()=="title")
        {
         n=subNode.getValue();
         break;
        }
      else continue;
     }
   return(n);
  }
//+------------------------------------------------------------------+
//| 显示描述                                                          |
//+------------------------------------------------------------------+
string CRssReader::getDescription(CEasyXmlNode *Node)
  {
   int k=Node.Children().Total();
   string n=NULL;
   for(int i=0;i<k;i++)
     {
      CEasyXmlNode*subNode=Node.Children().At(i);
      if(subNode.getName()=="description")
        {
         n=subNode.getValue();
         break;
        }
      else continue;
     }
   return(n);
  }
//+------------------------------------------------------------------+
//| 显示日期                                                          |
//+------------------------------------------------------------------+ 
string CRssReader::getDate(CEasyXmlNode *Node)
  {
   int k=Node.Children().Total();
   string n=NULL;
   for(int i=0;i<k;i++)
     {
      CEasyXmlNode*subNode=Node.Children().At(i);
      if(subNode.getName()=="pubDate")
        {
         n=subNode.getValue();
         break;
        }
      else continue;
     }
   return(n);
  }
//+------------------------------------------------------------------+
//|  文本区域面板输出字符格式化                                          |
//+------------------------------------------------------------------+
bool CRssReader::FormatString(string v,string &array[],int n)
  {
   ushort ch[],space,fullstop,comma,semicolon,newlinefeed;
   string _s,_k;
   space=StringGetCharacter(" ",0);
   fullstop=StringGetCharacter(".",0);
   comma=StringGetCharacter(",",0);
   semicolon=StringGetCharacter(";",0);
   newlinefeed=StringGetCharacter("/n",0);
   _k=removeTags(v);
   _s=removeSpecialCharacters(_k);
   int p=StringLen(_s);
   ArrayResize(ch,p+1);
   int d=StringToShortArray(_s,ch,0,-1);
   for(int i=1;i<d;i++)
     {
      int t=i%n;
      if(!t== 0)continue;
      else 
        {
         if(ch[(i/n)*n]==fullstop || ch[(i/n)*n]==semicolon || ch[(i/n)*n]==comma)
           {
            ArrayFill(ch,((i/n)*n)+1,1,newlinefeed);
           }
         else
           {
            for(int k=i;k>=0;k--)
              {
               if(ch[k]==space)
                 {
                  ArrayFill(ch,k,1,newlinefeed);
                  break;
                 }
               else continue;
              }
           }
        }
     }
   _s=ShortArrayToString(ch,0,-1);
   int s=StringSplit(_s,newlinefeed,array);
   if(!s>0)
     {return(false);}
// 成功 
   return(true);
  }
//+------------------------------------------------------------------+
//| 移除特殊的字符                                                     |
//+------------------------------------------------------------------+ 
string CRssReader::removeSpecialCharacters(string s_tring)
  {
   string n=s_tring;
   StringReplace(n,"&amp;","&");
   StringReplace(n,"&#39;","'");
   StringReplace(n,"&nbsp;"," ");
   StringReplace(n,"&ldquo;","/'");
   StringReplace(n,"&rdquo;","/'");
   StringReplace(n,"&quot;","/"");
   StringReplace(n,"&ndash;","-");
   StringReplace(n,"&rsquo;","'");
   StringReplace(n,"&gt;","");
   return(n);
  }
//+------------------------------------------------------------------+
//| 移除标签                                                          |
//+------------------------------------------------------------------+
string CRssReader::removeTags(string _string)
  {
   string now=NULL;
   if(StringFind(_string,"<",0)>-1)
     {
      int v=0,a[][2];
      ArrayResize(a,2024);
      for(int i=0;i<StringLen(_string);i++)
        {
         int t=tagPosition(_string,i);
         if(t>0)
           {
            v++;
            a[v-1][0]=i;
            a[v-1][1]=t;
           }
         else continue;
        }
      ArrayResize(a,v);
      for(int i=0;i<v-1;i++)
        {
         now+=StringSubstr(_string,(a[i][1]+1),(a[i+1][0]-(a[i][1]+1)));
        }
     }
   else
     {
      now=_string;
     }
   return(now);
  }
//+------------------------------------------------------------------+
//| 标签位置                                                          |
//+------------------------------------------------------------------+
int CRssReader::tagPosition(string _string,int w)
  {
   int iClose=-1;
   if(StringCompare("<",StringSubstr(_string,w,StringLen("<")))==0)
     {
      iClose=StringFind(_string,">",w+StringLen("<"));
     }

   return(iClose);
  }
//+------------------------------------------------------------------+
//| 事件处理                                                           |
//+------------------------------------------------------------------+
void CRssReader::OnChangeListView(void)
  {
   int a=0,k=0,l=0;
   a=m_listview.Current()+m_shift;
   if(ArraySize(ChannelChildNodes)>a)
     {
      if(m_titleview.ItemsClear())
        {
         if(!FormatString(getTitle(ChannelChildNodes[a]),m_titleareaoutput,55))
           {
            return;
           }
         else
         if(ArraySize(m_titleareaoutput)>0)
           {
            for(l=0;l<ArraySize(m_titleareaoutput);l++)
              {
               m_titleview.AddItem(removeSpecialCharacters(m_titleareaoutput[l]));
              }
           }
        }
      if(m_textview.ItemsClear())
        {
         if(!FormatString(getDescription(ChannelChildNodes[a]),m_textareaoutput,35))
            return;
         else
         if(ArraySize(m_textareaoutput)>0)
           {
            for(k=0;k<ArraySize(m_textareaoutput);k++)
              {
               m_textview.AddItem(m_textareaoutput[k]);
              }
            m_textview.AddItem(" ");
            m_textview.AddItem("Date|"+getDate(ChannelChildNodes[a]));
           }
         else return;
        }
     }
  }
//+------------------------------------------------------------------+
//| 事件处理                                                           |
//+------------------------------------------------------------------+
void CRssReader::OnObjectEdit(void)
  {
   string f=m_edit.Text();
   if(StringLen(f)>0)
     {
      if(ArraySize(ChannelChildNodes)<1)
        {
         CDialog::Caption("Loading...");
         if(LoadDocument(f))
           {
            if(!CDialog::Caption(removeSpecialCharacters(getChannelTitle())))
               Print("error changing caption");
            if(m_textview.ItemsClear() && m_listview.ItemsClear() && m_titleview.ItemsClear())
              {
               for(int i=0;i<ItemNodesTotal()-1;i++)
                 {
                  if(!m_listview.AddItem(removeSpecialCharacters(IntegerToString(i+1)+"."+getTitle(ChannelChildNodes[i+m_shift]))))
                    {
                     Print("can not add item to listview area");
                     return;
                    }
                 }
              }
            else
              {
               Print("text area/listview area not cleared");
               return;
              }
           }
         else return;
        }
      else
        {
         FreeDocumentTree();
         CDialog::Caption("Loading new RSS Feed...");
         if(LoadDocument(f))
           {
            if(!CDialog::Caption(removeSpecialCharacters(getChannelTitle())))
               Print("error changing caption");
            if(m_textview.ItemsClear() && m_listview.ItemsClear() && m_titleview.ItemsClear())
              {
               for(int i=0;i<ItemNodesTotal()-1;i++)
                 {
                  if(!m_listview.AddItem(removeSpecialCharacters(IntegerToString(i+1)+"."+getTitle(ChannelChildNodes[i+m_shift]))))
                    {
                     Print("can not add item to listview area");
                     return;
                    }
                 }
              }
            else
              {
               Print("text area/listview area not cleared");
               return;
              }
           }
         else return;
        }
     }
   else return;
  }
//+------------------------------------------------------------------+
//| 事件处理  更新程序对话框                                            |
//+------------------------------------------------------------------+   
void CRssReader::OnClickButton1(void)
  {
   if(ArraySize(ChannelChildNodes)<1)
     {
      if(!m_edit.Text("Enter the web address of an Rss feed"))
         Print("error changing edit text");
      if(!CDialog::Caption("RSSReader"))
         Print("error changing caption");
      if(m_textview.ItemsClear() && m_listview.ItemsClear() && m_titleview.ItemsClear())
        {
         for(int i=0;i<20;i++)
           {
            if(!m_listview.AddItem(" "))
               Print("error adding to listview");
           }
         m_listview.VScrolled(true);
         for(int i=0;i<1;i++)
           {
            m_textview.AddItem(" ");
           }
         m_textview.VScrolled(true);
         for(int i=0;i<2;i++)
           {
            m_titleview.AddItem(" ");
           }
         return;
        }
     }
   else
     {
      FreeDocumentTree();
      if(!m_edit.Text("Enter the web address of an Rss feed"))
         Print("error changing edit text");
      if(!CDialog::Caption("RSSReader"))
         Print("error changing caption");
      if(m_textview.ItemsClear() && m_listview.ItemsClear() && m_titleview.ItemsClear())
        {
         for(int i=0;i<20;i++)
           {
            if(!m_listview.AddItem(" "))
               Print("error adding to listview");
           }
         m_listview.VScrolled(true);
         for(int i=0;i<1;i++)
           {
            m_textview.AddItem(" ");
           }
         m_textview.VScrolled(true);
         for(int i=0;i<2;i++)
           {
            m_titleview.AddItem(" ");
           }
         return;
        }
     }
  }
//+------------------------------------------------------------------+
//| 事件处理  更新当前源                                               |
//+------------------------------------------------------------------+ 
void CRssReader::OnClickButton2(void)
  {
   string f=m_rssurl;
   if(ArraySize(ChannelChildNodes)<1)
      return;
   else
     {
      FreeDocumentTree();
      CDialog::Caption("Checking for RSS Feed update...");
      if(LoadDocument(f))
        {
         if(!CDialog::Caption(removeSpecialCharacters(getChannelTitle())))
            Print("error changing caption");
         if(m_textview.ItemsClear() && m_listview.ItemsClear() && m_titleview.ItemsClear())
           {
            for(int i=0;i<ItemNodesTotal()-1;i++)
              {
               if(!m_listview.AddItem(removeSpecialCharacters(IntegerToString(i+1)+"."+getTitle(ChannelChildNodes[i+m_shift]))))
                 {
                  Print("can not add item to listview area");
                  return;
                 }
              }
           }
         else
           {
            Print("text area/listview area not cleared");
            return;
           }
        }
      else return;
     }
  }

现在它可以在EA代码中使用。

2.11。EA码

EA没有输入参数,因为程序设计为完全交互式。

首先,我们声明一个全局变量,它是crssreader类的一个实例。在oninit()函数中,我们调用create()方法来初始化应用程序对话框。如果成功,将调用父类的方法run()。

在ondeinit()函数中,调用父类的destroy()方法删除整个应用程序并从关系图中删除EA。

onChartEvent()函数包含对CrssReader类的父方法的调用,该类处理所有事件。

//EA代码从这里开始
//+------------------------------------------------------------------+
//| 全局变量                                                           |
//+------------------------------------------------------------------+
CRssReader ExtDialog;
//+------------------------------------------------------------------+
//| EA初始化函数                                                       |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- 创建程序对话框
   if(!ExtDialog.Create(0,"RSSReader",0,20,20,518,394))
      return(INIT_FAILED);
//--- 运行程序
   ExtDialog.Run();
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| EA反初始化函数                                                     |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
   ExtDialog.Destroy(reason);
  }
//+------------------------------------------------------------------+
//| ChartEvent函数                                                    |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//---
   ExtDialog.ChartEvent(id,lparam,dparam,sparam);
  }

编译完代码后,就可以使用程序了。

将rssreader.mq5 ea加载到图中时,将出现一个空白对话框:

图 2. RssReader EA程序空白对话框截图

图2。RSSReader EA程序空白对话框
的屏幕截图

输入地址,RSS内容将加载到应用程序对话框中,如下图所示:

图 3. RssReader EA 在终端中运行

图3。RSSReader EA在终端运行

我使用大量的RSS源来测试这个程序。我观察到的唯一问题是,有时我会显示我不想要的字符,而且大多数时候RSS文档中包含的字符都可以在HTML文档中找到。

我还发现,当程序运行时,更改图表周期会导致EA重新初始化,这会导致程序空间绘制错误。

我还没能纠正这个问题,所以我建议您不要在RSS阅读器程序运行时更改图表的时间范围。

总结

到目前为止,我们已经完成了使用面向对象编程技术创建完整的MetaTrader5交互式RSS阅读器的程序。

我相信更多的功能将被添加到程序中,用户界面将更具组织性。我希望添加更好的应用程序GUI设计技术来进一步优化此应用程序。

另请注意,下载的asyxml.mqh文件与代码基文件不同,它们包含文本中提到的更改。所有必需的包含文件都在rssreader.zip文件中。

由MetaQuotes Software Corp.从英文翻译为
原文。https://www.mql5.com/en/articles/1589

附加文件下载zip easyxml.mqh(25.66 kb)、easyxmltattribute.mqh(3.03 kb)、easyxmlErrorDescription.mqh(3.48 kb)easyxmlNode。mqh(7.67 kb),列表视图区域。mqh(19.89 kb),文本区域。mqh(13.47 kb),标题栏。mqh(13.56 kb)rssreader.mq5(27.83 kb)rssreader.zip(20.49 kb)

 

 


MyFxtop迈投(www.myfxtop.com)-靠谱的外汇跟单社区,免费跟随高手做交易!

 

免责声明:本文系转载自网络,如有侵犯,请联系我们立即删除,另:本文仅代表作者个人观点,与迈投财经(www.myfxtop.cn)无关。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。

著作权归作者所有。
商业转载请联系作者获得授权,非商业转载请注明出处。

風險提示

MyFxtops邁投所列信息僅供參考,不構成投資建議,也不代表任何形式的推薦或者誘導行為。MyFxtops邁投非外匯經紀商,不接觸妳的任何資金。 MYFXTOPS不保證客戶盈利,不承擔任何責任。從事外彙和差價合約等金融產品的槓桿交易具有高風險,損失有可能超過本金,請量力而行,入市前需充分了解潛在的風險。過去的交易成績並不代表以後的交易成績。依據各地區法律法規,MyFxtops邁投不向中國大陸、美國、加拿大、朝鮮居民提供服務。

邁投公眾號

聯繫我們

客服QQ:981617007
Email: service@myfxtop.com

MyFxtops 邁投