通过上一篇博文《Qt中自定义只读TreeView的Model》,我们介绍了用C++如何写一个只读TreeView的Model,接下来我们需要其具有编辑功能。
类比自定义ListView的Model,我们发现,让其具有可编辑功能,只需要加三个必要的接口:
Qt::ItemFlags flags(const QModelIndex &index) const override;
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
//在自定义模型中实现TreeView标题的修改
bool setHeaderData(int section, Qt::Orientation orientation,
const QVariant &value, int role = Qt::EditRole) override;
另外我们需要有插入多行和插入多列、删除多行、插入多行和删除多行接口:
bool insertColumns(int position, int columns, const QModelIndex &parent = QModelIndex()) override;
bool removeColumns(int position, int columns, const QModelIndex &parent = QModelIndex()) override;
bool insertRows(int position, int rows, const QModelIndex &parent = QModelIndex()) override;
bool removeRows(int position, int rows, const QModelIndex &parent = QModelIndex()) override;
这样,我们就可以在只读Model上添加这些接口,将其改造成一个可读、可编辑的Model。
treeitem.h
#ifndef TREEITEM_H
#define TREEITEM_H
#include <QList>
#include <QVariant>
#include <QVector>
class TreeItem
{
public:
explicit TreeItem(const QVector<QVariant> &data, TreeItem *parent = nullptr);
~TreeItem();
TreeItem *child(int number);
int childCount() const;
int columnCount() const;
QVariant data(int column) const;
int childNumber() const; //返回子项的行数,在只读模型中是int row() const;其实功能是一样的。
TreeItem *parent();
//可以编辑模式 begin
bool insertChildren(int position, int count, int columns);
bool insertColumns(int position, int columns);
bool removeChildren(int position, int count);
bool removeColumns(int position, int columns);
bool setData(int column, const QVariant &value);
//可以编辑模式 end
private:
QList<TreeItem*> childItems;
QVector<QVariant> itemData;
TreeItem *parentItem;
};
#endif // TREEITEM_H
treeitem.cpp
#include <QStringList>
#include "treeitem.h"
TreeItem::TreeItem(const QVector<QVariant> &data, TreeItem *parent)
{
parentItem = parent;
itemData = data;
}
TreeItem::~TreeItem()
{
qDeleteAll(childItems);
}
TreeItem *TreeItem::child(int number)
{
return childItems.value(number);
}
int TreeItem::childCount() const
{
return childItems.count();
}
int TreeItem::childNumber() const
{
if (parentItem)
return parentItem->childItems.indexOf(const_cast<TreeItem*>(this));
return 0;
}
int TreeItem::columnCount() const
{
return itemData.count();
}
QVariant TreeItem::data(int column) const
{
return itemData.value(column);
}
bool TreeItem::insertChildren(int position, int count, int columns)
{
if (position < 0 || position > childItems.size())
return false;
for (int row = 0; row < count; ++row) {
QVector<QVariant> data(columns);
TreeItem *item = new TreeItem(data, this);
childItems.insert(position, item);
}
return true;
}
bool TreeItem::insertColumns(int position, int columns)
{
if (position < 0 || position > itemData.size())
return false;
for (int column = 0; column < columns; ++column)
itemData.insert(position, QVariant());
foreach (TreeItem *child, childItems)
child->insertColumns(position, columns);
return true;
}
TreeItem *TreeItem::parent()
{
return parentItem;
}
bool TreeItem::removeChildren(int position, int count)
{
if (position < 0 || position + count > childItems.size())
return false;
for (int row = 0; row < count; ++row)
delete childItems.takeAt(position);
return true;
}
bool TreeItem::removeColumns(int position, int columns)
{
if (position < 0 || position + columns > itemData.size())
return false;
for (int column = 0; column < columns; ++column)
itemData.remove(position);
foreach (TreeItem *child, childItems)
child->removeColumns(position, columns);
return true;
}
bool TreeItem::setData(int column, const QVariant &value)
{
if (column < 0 || column >= itemData.size())
return false;
itemData[column] = value;
return true;
}
treemodel.h
#ifndef TREEMODEL_H
#define TREEMODEL_H
#include <QAbstractItemModel>
#include <QModelIndex>
#include <QVariant>
class TreeItem;
class TreeModel : public QAbstractItemModel
{
Q_OBJECT
public:
TreeModel(const QStringList &headers, QObject *parent = nullptr);
~TreeModel() override;
QVariant data(const QModelIndex &index, int role) 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;
//可以编辑模型begin
Qt::ItemFlags flags(const QModelIndex &index) const override;
bool setData(const QModelIndex &index, const QVariant &value,
int role = Qt::EditRole) override;
bool setHeaderData(int section, Qt::Orientation orientation,
const QVariant &value, int role = Qt::EditRole) override;
bool insertColumns(int position, int columns,
const QModelIndex &parent = QModelIndex()) override;
bool removeColumns(int position, int columns,
const QModelIndex &parent = QModelIndex()) override;
bool insertRows(int position, int rows,
const QModelIndex &parent = QModelIndex()) override;
bool removeRows(int position, int rows,
const QModelIndex &parent = QModelIndex()) override;
//可以编辑模型end
private:
TreeItem *getItem(const QModelIndex &index) const;
TreeItem *rootItem;
};
#endif // TREEMODEL_H
treemodel.cpp
#include <QtWidgets>
#include "treeitem.h"
#include "treemodel.h"
TreeModel::TreeModel(const QStringList &headers, QObject *parent)
: QAbstractItemModel(parent)
{
// QVector<QVariant> rootData;
// foreach (QString header, headers)
// rootData << header;
// rootItem = new TreeItem(rootData);
QVector<QVariant> rootData;
rootData << "Title" << "summary";
rootItem = new TreeItem(rootData);
rootItem->insertChildren(rootItem->childCount(), 1, rootItem->columnCount());
QVector<QVariant> gettingStartedData;
gettingStartedData << "Getting Started" << "How to familiarize yourself with Qt Designer";
rootItem->child(rootItem->childCount()-1)->setData(0, gettingStartedData[0]);
rootItem->child(rootItem->childCount()-1)->setData(1, gettingStartedData[1]);
TreeItem *gettingStartedItem = rootItem->child(rootItem->childCount()-1);
gettingStartedItem->insertChildren(gettingStartedItem->childCount(), 1, rootItem->columnCount());
QVector<QVariant> launchingDesignerData;
launchingDesignerData << "Launching Designer" << "Running the Qt Designer application";
gettingStartedItem->child(gettingStartedItem->childCount()-1)->setData(0, launchingDesignerData[0]);
gettingStartedItem->child(gettingStartedItem->childCount()-1)->setData(1, launchingDesignerData[1]);
gettingStartedItem->insertChildren(gettingStartedItem->childCount(), 1, rootItem->columnCount());
QVector<QVariant> userInterfaceData;
userInterfaceData << "The User Interface" << "How to interact with Qt Designer";
gettingStartedItem->child(gettingStartedItem->childCount()-1)->setData(0, userInterfaceData[0]);
gettingStartedItem->child(gettingStartedItem->childCount()-1)->setData(1, userInterfaceData[1]);
QVector<QVariant> designingComponentData;
designingComponentData << "Designing a Component" << "Creating a GUI for your application";
rootItem->insertChildren(rootItem->childCount(), 1, rootItem->columnCount());
rootItem->child(rootItem->childCount()-1)->setData(0, designingComponentData[0]);
rootItem->child(rootItem->childCount()-1)->setData(1, designingComponentData[1]);
TreeItem *designingComponentItem = rootItem->child(rootItem->childCount()-1);
QVector<QVariant> creatingDialogData;
creatingDialogData << "Creating a Dialog" << "How to create a dialog";
QVector<QVariant> composingDialogData;
composingDialogData << "Composing the Dialog" << "Putting widgets into the dialog example";
designingComponentItem->insertChildren(designingComponentItem->childCount(), 2, rootItem->columnCount());
designingComponentItem->child(0)->setData(0, creatingDialogData[0]);
designingComponentItem->child(0)->setData(1, creatingDialogData[1]);
designingComponentItem->child(1)->setData(0, composingDialogData[0]);
designingComponentItem->child(1)->setData(1, composingDialogData[1]);
}
TreeModel::~TreeModel()
{
delete rootItem;
}
int TreeModel::columnCount(const QModelIndex & /* parent */) const
{
return rootItem->columnCount();
}
QVariant TreeModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
if (role != Qt::DisplayRole && role != Qt::EditRole)
return QVariant();
TreeItem *item = getItem(index);
return item->data(index.column());
}
Qt::ItemFlags TreeModel::flags(const QModelIndex &index) const
{
if (!index.isValid())
return 0;
return Qt::ItemIsEditable | QAbstractItemModel::flags(index);
}
TreeItem *TreeModel::getItem(const QModelIndex &index) const
{
if (index.isValid()) {
TreeItem *item = static_cast<TreeItem*>(index.internalPointer());
if (item)
return item;
}
return rootItem;
}
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 (parent.isValid() && parent.column() != 0)
return QModelIndex();
TreeItem *parentItem = getItem(parent);
TreeItem *childItem = parentItem->child(row);
if (childItem)
return createIndex(row, column, childItem);
else
return QModelIndex();
}
bool TreeModel::insertColumns(int position, int columns, const QModelIndex &parent)
{
bool success;
beginInsertColumns(parent, position, position + columns - 1);
success = rootItem->insertColumns(position, columns);
endInsertColumns();
return success;
}
bool TreeModel::insertRows(int position, int rows, const QModelIndex &parent)
{
TreeItem *parentItem = getItem(parent);
bool success;
beginInsertRows(parent, position, position + rows - 1);
success = parentItem->insertChildren(position, rows, rootItem->columnCount());
endInsertRows();
return success;
}
QModelIndex TreeModel::parent(const QModelIndex &index) const
{
if (!index.isValid())
return QModelIndex();
TreeItem *childItem = getItem(index);
TreeItem *parentItem = childItem->parent();
if (parentItem == rootItem)
return QModelIndex();
return createIndex(parentItem->childNumber(), 0, parentItem);
}
bool TreeModel::removeColumns(int position, int columns, const QModelIndex &parent)
{
bool success;
beginRemoveColumns(parent, position, position + columns - 1);
success = rootItem->removeColumns(position, columns);
endRemoveColumns();
if (rootItem->columnCount() == 0)
removeRows(0, rowCount());
return success;
}
bool TreeModel::removeRows(int position, int rows, const QModelIndex &parent)
{
TreeItem *parentItem = getItem(parent);
bool success = true;
beginRemoveRows(parent, position, position + rows - 1);
success = parentItem->removeChildren(position, rows);
endRemoveRows();
return success;
}
int TreeModel::rowCount(const QModelIndex &parent) const
{
TreeItem *parentItem = getItem(parent);
return parentItem->childCount();
}
bool TreeModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
if (role != Qt::EditRole)
return false;
TreeItem *item = getItem(index);
bool result = item->setData(index.column(), value);
if (result)
emit dataChanged(index, index);
return result;
}
bool TreeModel::setHeaderData(int section, Qt::Orientation orientation,
const QVariant &value, int role)
{
if (role != Qt::EditRole || orientation != Qt::Horizontal)
return false;
bool result = rootItem->setData(section, value);
if (result)
emit headerDataChanged(orientation, section, section);
return result;
}
main.cpp
#include <QApplication>
#include <QStringList>
#include <QTreeView>
#include "treemodel.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QStringList headers;
headers << "Title" << "Description";
TreeModel *model = new TreeModel(headers);
QTreeView view;
view.setModel(model);
for (int column = 0; column < model->columnCount(); ++column)
view.resizeColumnToContents(column);
view.show();
return a.exec();
}
运行效果: