QT5.14.2自带Examples:Simple DOM Model

概述

本示例展示显示如何调整现有类,以用于 Model/View 框架。

使用:运行程序加载任意一个xml文件。

QT5.14.2自带Examples:Simple DOM Model_第1张图片

Qt为读取XML文件提供了两组互补的类:

  1. 基于QXMLRead的类,为大文件的增量读取提供了一种SAX风格的API
  2. 基于QDomDocument的类,使开发人员能够使用文档对象模型(Document Object Model,DOM)API访问XML文件的内容.
    在本例中,我们创建了一个模型,该模型使用DOM API通过标准QAbstractModel接口向 QTreeView 公开XML文档的结构和内容。
    使用Qt的DOM类读取XML文档是一个简单的过程。通常,文件的内容提供给QDomDocument,使用QDomNode及其子类提供的函数访问节点。
    目的是使用QDomDocument提供的结构,将QDomNode对象包装在item对象中,类似于 Simple Tree Model 示例中使用的TreeItem对象。

DomItem类

类定义


#ifndef DOMITEM_H
#define DOMITEM_H

#include 
#include 

class DomItem
{
     
public:
    DomItem(const QDomNode &node, int row, DomItem *parent = nullptr);
    ~DomItem();
	//parent()、child()和row()函数是提供给DomModel使用的函数,帮助快速发现项的基本信息。
    DomItem *child(int i);
    DomItem *parent();
    int row() const;

	//提供对底层QDomNode对象的访问。    
    QDomNode node() const;

private:
//每个 DomItem 为从底层文档获得的QDomNode提供一个封装,该文档包含对该节点的引用、该节点在父节点的子节点列表中的位置以及指向父项的指针。
    //domNode是QDomDocument内容,也就是真实的数据(包含本节点和子节点列表)
    //对于root来说,domNode就是整个xml文档
    QDomNode domNode;
    DomItem *parentItem;
    int rowNumber;
    //除了在构造函数中提供的信息外,该类还维护有关子项的信息。
    //这用于提供持久项对象的集合,模型可以一致地标识这些对象,并在访问子项时提高模型的性能。
    QHash<int, DomItem *> childItems;
};

#endif // DOMITEM_H

类实现

由于DomItem类只是QDomNode对象的一个简单封装,有一些额外的特性可以帮助改进性能和内存使用。

#include "domitem.h"

#include 
//构造函数只记录需要封装的QDomNode的详细信息
DomItem::DomItem(const QDomNode &node, int row, DomItem *parent)
	: domNode(node),
      parentItem(parent),
      rowNumber(row)
{
     }

DomItem::~DomItem()
{
     
    qDeleteAll(childItems);
}

QDomNode DomItem::node() const
{
     
    return domNode;
}

DomItem *DomItem::parent()
{
     
    return parentItem;
}
//有必要维护一个可以由模型一致标识的项集合。
//我们维护子包装项的散列,以最小化内存使用,该散列最初为空。
//模型使用项的child()函数帮助创建模型索引,
//并为项的QDomNode的子级进行封装,将每个子级的行号与新构造的封装相关联:
DomItem *DomItem::child(int i)
{
     
    DomItem *childItem = childItems.value(i);

    if (childItem)
        return childItem;

    //如果child不存在,则创建它
    //本示例为只读一次,所以所有的child都会通过下面的语句实现。
    //通过domNode里面保存的真实数据,创建childItems
    if (i >= 0 && i < domNode.childNodes().count()) {
     
        QDomNode childNode = domNode.childNodes().item(i);
        childItem = new DomItem(childNode, i, this);
        childItems[i] = childItem;
    }
    return childItem;
}

int DomItem::row() const
{
     
    return rowNumber;
}

DomModel类

类定义

类定义包含只读模型所需的所有基本函数。私有变量 domDocument 用于保存模型公开的文档;rootItem变量包含指向模型中根项的指针。

#ifndef DOMMODEL_H
#define DOMMODEL_H

#include 
#include 
#include 

class DomItem;

class DomModel : public QAbstractItemModel
{
     
    Q_OBJECT

public:
    explicit DomModel(const QDomDocument &document, QObject *parent = nullptr);
    ~DomModel();

    QVariant data(const QModelIndex &index, int role) const override;
    Qt::ItemFlags flags(const QModelIndex &index) const override;
    QVariant headerData(int section, Qt::Orientation orientation,
                        int role = Qt::DisplayRole) const override;
    QModelIndex index(int row, int column,
                      const QModelIndex &parent = QModelIndex()) const override;
    QModelIndex parent(const QModelIndex &child) const override;
    int rowCount(const QModelIndex &parent = QModelIndex()) const override;
    int columnCount(const QModelIndex &parent = QModelIndex()) const override;

private:
    QDomDocument domDocument;
    DomItem *rootItem;
};

#endif // DOMMODEL_H

类实现

#include "dommodel.h"
#include "domitem.h"

#include 
//DomItem 类提供的结构使 DomModel 的实现类似于Simple Tree Model 示例中显示的TreeModel。
//构造函数接受一个现有的文档和父对象.
//对文档进行浅拷贝,用于后面的引用。并创建根项以提供文档的封装。
DomModel::DomModel(const QDomDocument &document, QObject *parent)
    : QAbstractItemModel(parent),
      domDocument(document),
      //由于根项没有同级项,因此我们只为其分配一个行号为零的行以保持一致。
      rootItem(new DomItem(domDocument, 0))
{
     
}
//模型只包含有关根项的信息,析构函数只需要删除这一项:
//树中的所有子项都将被DomItem析构函数删除,因为它们的父项已被删除。
DomModel::~DomModel()
{
     
    delete rootItem;
}

int DomModel::columnCount(const QModelIndex &parent) const
{
     
    Q_UNUSED(parent);
    return 4;
}

QVariant DomModel::data(const QModelIndex &index, int role) const
{
     
    if (!index.isValid())
        return QVariant();

    if (role != Qt::DisplayRole)
        return QVariant();

    const DomItem *item = static_cast<DomItem*>(index.internalPointer());

    const QDomNode node = item->node();

    switch (index.column()) {
     
        case 0://对于第一列,我们返回节点的名称。
            return node.nodeName();
         //对于第二列,我们读取节点可能具有的任何属性
         //并返回一个字符串,该字符串包含属性值赋值列表。
        case 1:
        {
     
            const QDomNamedNodeMap attributeMap = node.attributes();
            QStringList attributes;
            for (int i = 0; i < attributeMap.count(); ++i) {
     
                QDomNode attribute = attributeMap.item(i);
                attributes << attribute.nodeName() + "=\""
                              + attribute.nodeValue() + '"';
            }
            //值用空格作为分隔符
            return attributes.join(' ');
        }
        case 2:
        	//合并为1行。
        	//如果不希望合并,也可以直接 return node.nodeValue()
            return node.nodeValue().split('\n').join(' ');
        default:
            break;
    }
    return QVariant();
}

Qt::ItemFlags DomModel::flags(const QModelIndex &index) const
{
     
    if (!index.isValid())
        return Qt::NoItemFlags;

    return QAbstractItemModel::flags(index);
}

QVariant DomModel::headerData(int section, Qt::Orientation orientation,
                              int role) const
{
     
    if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
     
        switch (section) {
     
            case 0:
                return tr("Name");
            case 1:
                return tr("Attributes");
            case 2:
                return tr("Value");
            default:
                break;
        }
    }
    return QVariant();
}
//为模型中具有给定行、列和父项的项创建模型索引:
QModelIndex DomModel::index(int row, int column, const QModelIndex &parent) const
{
     
    if (!hasIndex(row, column, parent))
        return QModelIndex();
	//首先必须将父索引与包含基础文档节点的项关联。
    DomItem *parentItem;
	//如果父索引无效,则它引用文档中的根节点
    if (!parent.isValid())
        parentItem = rootItem;
    else
   		 //索引的internalPointer得到一个指向相关项(创建索引是,封装的item)的指针
        parentItem = static_cast<DomItem*>(parent.internalPointer());

    DomItem *childItem = parentItem->child(row);
    if (childItem)
  		  //创建索引,封装item
        return createIndex(row, column, childItem);
    return QModelIndex();
}

QModelIndex DomModel::parent(const QModelIndex &child) const
{
     
    if (!child.isValid())
        return QModelIndex();

    DomItem *childItem = static_cast<DomItem*>(child.internalPointer());
    DomItem *parentItem = childItem->parent();

    if (!parentItem || parentItem == rootItem)
        return QModelIndex();

    return createIndex(parentItem->row(), 0, parentItem);
}

int DomModel::rowCount(const QModelIndex &parent) const
{
     
    //tree结构只在第一列
    if (parent.column() > 0)
        return 0;

    DomItem *parentItem;

    if (!parent.isValid())
        parentItem = rootItem;
    else
        parentItem = static_cast<DomItem*>(parent.internalPointer());

    return parentItem->node().childNodes().count();
}

main函数

#include "mainwindow.h"

#include 

int main(int argc, char *argv[])
{
     
    QApplication app(argc, argv);
    MainWindow window;
    window.resize(640, 480);
    window.show();
    return app.exec();
}

你可能感兴趣的:(QT5.14.2自带Examples:Simple DOM Model)