zoukankan      html  css  js  c++  java
  • (转)Qt Model/View 学习笔记 (四)——创建新的Models

    创建新的Models

    介绍

    model/view组件之间功能的分离,允许创建model利用现成的views。这也可以使用标准的功能 图形用户接口组件像QListView,QTableView和QTreeView来显示来自各种数据源的数据为。

    QAbstractListModel类提供了非常灵活的接口,允许数据源以层次结构的形式来管理信息,也允许以某种

    方式对数据进行插入、删除、修改和存储。它也提供了对拖拽操作的支持。

    QAbstractListModel与QAbstractTableModel为简单的非层次结构的数据提供了接口,对于比较简单的list和table models来说,这是不错的一个开始点。

    设计一个Model

    当我们为存在的数据结构新建一个model时,首先要考虑的问题是应该选用哪种model来为这些数据提供接口。

    假如数据结构可以用数据项的列表或表来表示,那么可以考虑子类化QAbstractListModel或QAbstractTableModel

    ,既然这些类已经合理地对许多功能提供缺省实现。

    然而,假如底层的数据结构只能表示成具有层次结构的树型结构,那么必须得子类化QAbstractItemModel。

    无论底层的数据结构采取何种形式,在特定的model中实现标准的QAbstractItemModel API总是一个不错的主意,这使得可以使用更自然的方式对底层的数据结构进行访问。这也使得用数据构建model 更为容易,其他

    的model/view组件也可以使用标准的API与之进行交互。

    一个只读model示例

    这个示例实现了一个简单的,非层次结构的,只读的数据model,它基于QStringistModel类。它有一个QStringList作为它内部的数据源,只实现了一些必要的接口。为了简单化,它子类化了QAbstractListModel,这个基类提供了合理的缺省行为,对外提供了比QAbstractItemModel更为简单的接口。当我们实现一个model时,不要忘了QAbstractItemModel本身不存储任何数据,它仅仅提供了给views访问

    数据的接口。

    class StringListModel : public QAbstractListModel

     {

         Q_OBJECT

     public:

         StringListModel(const QStringList &strings, QObject *parent = 0)

             : QAbstractListModel(parent), stringList(strings) {}

         int rowCount(const QModelIndex &parent = QModelIndex()) const;

         QVariant data(const QModelIndex &index, int role) const;

         QVariant headerData(int section, Qt::Orientation orientation,

                             int role = Qt::DisplayRole) const;

     private:

         QStringList stringList;

     };

    除了构造函数,我们仅需要实现两个函数:rowCount()返回model中的行数,data()返回与特定model index对应的数据项。具有良好行为的model也会实现headerData(),它返回tree和table views需要的,在标题中显示的数据。

    因为这是一个非层次结构的model,我们不必考虑父子关系。假如model具有层次结构,我们也应该实现index()与parent()函数。

    Model的尺寸

    我们认为model中的行数与string list中的string数目一致:

    int StringListModel::rowCount(const QModelIndex &parent) const

     {

         return stringList.count();

     }

    在缺省情况下,从QAbstractListModel派生的model只具有一列,因此不需要实现columnCount()。

    Model 标题与数据

     QVariant StringListModel::data(const QModelIndex &index, int role) const

     {

         if (!index.isValid())

             return QVariant();

         if (index.row() >= stringList.size())

             return QVariant();

         if (role == Qt::DisplayRole)

             return stringList.at(index.row());

         else

             return QVariant();

     }

    QVariant StringListModel::headerData(int section, Qt::Orientation orientation,

                                          int role) const

     {

         if (role != Qt::DisplayRole)

             return QVariant();

         if (orientation == Qt::Horizontal)

             return QString("Column %1").arg(section);

         else

             return QString("Row %1").arg(section);

     }

    一个数据项可能有多个角色,根据角色的不同输出不同的数据。上例中,model中的数据项只有一个角色 ,

    DisplayRole,然而我们也可以重用提供给DisplayRole的数据,作为别的角色使用,如我们可以作为ToolTipRole来用。

    可编辑的model

    上面我们演示了一个只读的model,它只用于向用户显示,对于许多程序来说,可编辑的list model可能更有用。我们只需要给只读的model提供另外两个函数flags()与setData()的实现。下列函数声明被添加到类定义中:

         Qt::ItemFlags flags(const QModelIndex &index) const;

         bool setData(const QModelIndex &index, const QVariant &value,

                      int role = Qt::EditRole);

    让model可编辑

    delegate会在创建编辑器之前检查数据项是否是可编辑的。model必须得让delegate知道它的数据项是可

    编辑的。这可以通过为每一个数据项返回一个正确的标记得到,在本例中,我们假设所有的数据项都是

    可编辑可选择的:

    Qt::ItemFlags StringListModel::flags(const QModelIndex &index) const

     {

         if (!index.isValid())

             return Qt::ItemIsEnabled;

         return QAbstractItemModel::flags(index) | Qt::ItemIsEditable;

     }

    我们不必知道delegate执行怎样实际的编辑处理过程,我们只需提供给delegate一个方法,delegate会使用它对model中的数据进行设置。这个特殊的函数就是setData():

    bool StringListModel::setData(const QModelIndex &index,

                                   const QVariant &value, int role)

     {

         if (index.isValid() && role == Qt::EditRole) {

             stringList.replace(index.row(), value.toString());

             emit dataChanged(index, index);

             return true;

         }

         return false;

     }

    当数据被设置后,model必须得让views知道一些数据发生了变化,这可通过发射一个dataChanged() 信号实现。

    因为只有一个数据项发生了变化,因此在信号中说明的变化范围只限于一个model index。

    插入,删除行

    在model中改变行数与列数是可能的。当然在本列中,只考虑行的情况,我们只需要重新实现插入、删除

    的函数就可以了,下面应在类定义中声明:

         bool insertRows(int position, int rows, const QModelIndex &index = QModelIndex());

         bool removeRows(int position, int rows, const QModelIndex &index = QModelIndex());

    既然model中的每行对应于列表中的一个string,因此,insertRows()函数在string list  中指定位置插入一个空string,

    父index通常用于决定model中行列的位置,本例中只有一个单独的顶级项,困此只需要在list中插入空string。

    bool StringListModel::insertRows(int position, int rows, const QModelIndex &parent)

     {

         beginInsertRows(QModelIndex(), position, position+rows-1);

         for (int row = 0; row < rows; ++row) {

             stringList.insert(position, "");

         }

         endInsertRows();

         return true;

     }

    beginInsertRows()通知其他组件行数将会改变。endInsertRows()对操作进行确认与通知。

    返回true表示成功。

    删除操作与插入操作类似:

    bool StringListModel::removeRows(int position, int rows, const QModelIndex &parent)

     {

         beginRemoveRows(QModelIndex(), position, position+rows-1);

         for (int row = 0; row < rows; ++row) {

             stringList.removeAt(position);

         }

         endRemoveRows();

         return true;

     }

  • 相关阅读:
    区块链服务平台设计
    Fabric 架构与设计
    ELSE 技术周刊(2017.12.25期)
    ELSE 技术周刊(2017.12.25期)
    ELSE 技术周刊(2017.12.25期)
    UDT的Sender和Receiver
    UDT的Sender和Receiver
    UDT的Sender和Receiver
    UDT的Sender和Receiver
    JavaScript 后台获取数据
  • 原文地址:https://www.cnblogs.com/takeaction/p/3662262.html
Copyright © 2011-2022 走看看