通过学习Qt自带的例子和帮助文档,简化了其中的一个例子,来学习如何自定义TreeView的Model。
以下是自定义一个只读的TreeView的Model,实际中可以直接复制过来使用,避免重复造轮子。
官方的文档一直都是很好的学习资料,尤其是官方的例子和help文档。本文参考了官方的Simple Tree Model Example例子。
这是简单的树状结构,可以看出行的结构,但是缺乏列的信息。
这样我们就清楚treeitem中的成员变量:QList m_itemData;中存储的链表的情况了。
treeitem.h文件:
#ifndef TREEITEM_H
#define TREEITEM_H
#include <QList>
#include <QVariant>
class TreeItem
{
public:
explicit TreeItem(const QList<QVariant> &data, TreeItem *parentItem = nullptr);
~TreeItem();
void appendChild(TreeItem *child);
TreeItem *child(int row);
int childCount() const;
int columnCount() const;
QVariant data(int column) const;
int row() const;
TreeItem *parentItem();
private:
QList<TreeItem*> m_childItems;
QList<QVariant> m_itemData;
TreeItem *m_parentItem;
};
#endif // TREEITEM_H
treeitem.cpp文件:
#include <QStringList>
#include "treeitem.h"
TreeItem::TreeItem(const QList<QVariant> &data, TreeItem *parent)
{
m_parentItem = parent;
m_itemData = data;
}
TreeItem::~TreeItem()
{
qDeleteAll(m_childItems);
}
void TreeItem::appendChild(TreeItem *item)
{
m_childItems.append(item);
}
TreeItem *TreeItem::child(int row)
{
return m_childItems.value(row);
}
int TreeItem::childCount() const
{
return m_childItems.count();
}
int TreeItem::columnCount() const
{
return m_itemData.count();
}
QVariant TreeItem::data(int column) const
{
return m_itemData.value(column);
}
TreeItem *TreeItem::parentItem()
{
return m_parentItem;
}
int TreeItem::row() const
{
if (m_parentItem)
return m_parentItem->m_childItems.indexOf(const_cast<TreeItem*>(this));
return 0;
}
treemodel.h文件:
#ifndef TREEMODEL_H
#define TREEMODEL_H
#include <QAbstractItemModel>
#include <QModelIndex>
#include <QVariant>
class TreeItem;
class TreeModel : public QAbstractItemModel
{
Q_OBJECT
public:
explicit TreeModel(QObject *parent = nullptr);
~TreeModel() override;
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 &index) const override;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
private:
TreeItem *rootItem;
};
#endif // TREEMODEL_H
treemodel.cpp文件:
#include "treeitem.h"
#include "treemodel.h"
TreeModel::TreeModel(QObject *parent)
: QAbstractItemModel(parent)
{
//以下代码都是为了装填数据用,实际中需要另外写接口加载数据
QList<QVariant> rootData;
rootData << "Title" << "summary";
rootItem = new TreeItem(rootData);
QList<QVariant> gettingStartedData;
gettingStartedData << "Getting Started" << "How to familiarize yourself with Qt Designer";
TreeItem * gettingStartedItem = new TreeItem(gettingStartedData, rootItem);
rootItem->appendChild(gettingStartedItem);
QList<QVariant> launchingDesignerData;
launchingDesignerData << "Launching Designer" << "Running the Qt Designer application";
TreeItem * launchingDesignerItem = new TreeItem(launchingDesignerData, gettingStartedItem);
gettingStartedItem->appendChild(launchingDesignerItem);
QList<QVariant> userInterfaceData;
userInterfaceData << "The User Interface" << "How to interact with Qt Designer";
TreeItem * userInterfaceItem = new TreeItem(userInterfaceData, gettingStartedItem);
gettingStartedItem->appendChild(userInterfaceItem);
QList<QVariant> designingComponentData;
designingComponentData << "Designing a Component" << "Creating a GUI for your application";
TreeItem * designingComponentItem = new TreeItem(designingComponentData, rootItem);
rootItem->appendChild(designingComponentItem);
QList<QVariant> creatingDialogData;
creatingDialogData << "Creating a Dialog" << "How to create a dialog";
TreeItem * creatingDialogItem = new TreeItem(creatingDialogData, designingComponentItem);
designingComponentItem->appendChild(creatingDialogItem);
QList<QVariant> composingDialogData;
composingDialogData << "Composing the Dialog" << "Putting widgets into the dialog example";
TreeItem * composingDialogItem = new TreeItem(composingDialogData, designingComponentItem);
designingComponentItem->appendChild(composingDialogItem);
}
TreeModel::~TreeModel()
{
delete rootItem;
}
int TreeModel::columnCount(const QModelIndex &parent) const
{
if (parent.isValid())
return static_cast<TreeItem*>(parent.internalPointer())->columnCount();
else
return rootItem->columnCount();
}
QVariant TreeModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
if (role != Qt::DisplayRole)
return QVariant();
TreeItem *item = static_cast<TreeItem*>(index.internalPointer());
return item->data(index.column());
}
Qt::ItemFlags TreeModel::flags(const QModelIndex &index) const
{
if (!index.isValid())
return 0;
return QAbstractItemModel::flags(index);
}
QVariant TreeModel::headerData(int section, Qt::Orientation orientation,
int role) const
{
if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
return rootItem->data(section);
return QVariant();
}
QModelIndex TreeModel::index(int row, int column, const QModelIndex &parent)
const
{
if (!hasIndex(row, column, parent))
return QModelIndex();
TreeItem *parentItem;
if (!parent.isValid())
parentItem = rootItem;
else
parentItem = static_cast<TreeItem*>(parent.internalPointer());
TreeItem *childItem = parentItem->child(row);
if (childItem)
return createIndex(row, column, childItem);
else
return QModelIndex();
}
QModelIndex TreeModel::parent(const QModelIndex &index) const
{
if (!index.isValid())
return QModelIndex();
TreeItem *childItem = static_cast<TreeItem*>(index.internalPointer());
TreeItem *parentItem = childItem->parentItem();
if (parentItem == rootItem)
return QModelIndex();
return createIndex(parentItem->row(), 0, parentItem);
}
int TreeModel::rowCount(const QModelIndex &parent) const
{
TreeItem *parentItem;
if (parent.column() > 0)
return 0;
if (!parent.isValid())
parentItem = rootItem;
else
parentItem = static_cast<TreeItem*>(parent.internalPointer());
return parentItem->childCount();
}
main.cpp
#include <QApplication>
#include <QFile>
#include <QTreeView>
#include "treemodel.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
TreeModel model;
QTreeView view;
view.setModel(&model);
view.setWindowTitle(QObject::tr("Simple Tree Model"));
view.show();
return app.exec();
}
运行效果:
接下来,我们对treemodel.cpp文件的构造函数进行改造
如果去掉第二列的数据:
去掉方框的代码,就能看到:
发现不显示第二列的数据,就是一般见到的树形控件了。
对treemodel.cpp中的构造函数进行改造
TreeModel::TreeModel(QObject *parent)
: QAbstractItemModel(parent)
{
//以下代码都是为了装填数据用,实际中需要另外写接口加载数据
QList<QVariant> rootData;
rootData << "Title";
rootItem = new TreeItem(rootData);
QList<QVariant> gettingStartedData;
gettingStartedData << "Getting Started";
TreeItem * gettingStartedItem = new TreeItem(gettingStartedData, rootItem);
rootItem->appendChild(gettingStartedItem);
QList<QVariant> launchingDesignerData;
launchingDesignerData << "Launching Designer";
TreeItem * launchingDesignerItem = new TreeItem(launchingDesignerData, gettingStartedItem);
gettingStartedItem->appendChild(launchingDesignerItem);
QList<QVariant> userInterfaceData;
userInterfaceData << "The User Interface";
TreeItem * userInterfaceItem = new TreeItem(userInterfaceData, gettingStartedItem);
gettingStartedItem->appendChild(userInterfaceItem);
QList<QVariant> designingComponentData;
designingComponentData << "Designing a Component";
TreeItem * designingComponentItem = new TreeItem(designingComponentData, rootItem);
rootItem->appendChild(designingComponentItem);
QList<QVariant> creatingDialogData;
creatingDialogData << "Creating a Dialog";
TreeItem * creatingDialogItem = new TreeItem(creatingDialogData, designingComponentItem);
designingComponentItem->appendChild(creatingDialogItem);
QList<QVariant> composingDialogData;
composingDialogData << "Composing the Dialog";
TreeItem * composingDialogItem = new TreeItem(composingDialogData, designingComponentItem);
designingComponentItem->appendChild(composingDialogItem);
}
这样就只有一列的数据。
继续修改构造函数:
TreeModel::TreeModel(QObject *parent)
: QAbstractItemModel(parent)
{
//以下代码都是为了装填数据用,实际中需要另外写接口加载数据
QList<QVariant> rootData;
rootData << "Title";
rootItem = new TreeItem(rootData);
QList<QVariant> AData;
AData << "A";
TreeItem * AItem = new TreeItem(AData, rootItem);
rootItem->appendChild(AItem);
QList<QVariant> a0Data;
a0Data << "a0";
TreeItem * a0Item = new TreeItem(a0Data, AItem);
AItem->appendChild(a0Item);
QList<QVariant> a1Data;
a1Data << "a1";
TreeItem * a1Item = new TreeItem(a1Data, AItem);
AItem->appendChild(a1Item);
QList<QVariant> a2Data;
a2Data << "a2";
TreeItem * a2Item = new TreeItem(a2Data, AItem);
AItem->appendChild(a2Item);
QList<QVariant> BData;
BData << "B";
TreeItem * BItem = new TreeItem(BData, rootItem);
rootItem->appendChild(BItem);
QList<QVariant> b0Data;
b0Data << "b0";
TreeItem * b0Item = new TreeItem(b0Data, BItem);
BItem->appendChild(b0Item);
QList<QVariant> b1Data;
b1Data << "b1";
TreeItem * b1Item = new TreeItem(b1Data, BItem);
BItem->appendChild(b1Item);
QList<QVariant> b2Data;
b2Data << "b2";
TreeItem * b2Item = new TreeItem(b2Data, BItem);
BItem->appendChild(b2Item);
}
这样我们就掌握了只读TreeView的Model的开发。