Modernize EditableTreeModel
- Use unique_ptr instead of manual memory management - Improve consistenty in variable name with the simpletreemodel childrenNumber -> row, m_ prefix for member variables Change-Id: Iface30c2224c2b1db7c623a9e6fcbb449c556f3e Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
This commit is contained in:
parent
7bad2902f8
commit
604b2feca7
@ -229,32 +229,30 @@
|
||||
internal \c childItems member using the \c insertChildren() function
|
||||
described later.
|
||||
|
||||
The destructor ensures that each child added to the item is deleted
|
||||
when the item itself is deleted:
|
||||
|
||||
\snippet itemviews/editabletreemodel/treeitem.cpp 1
|
||||
The children are stored in std::unique_ptr to ensures that each child
|
||||
added to the item is deleted when the item itself is deleted.
|
||||
|
||||
\target TreeItem::parent
|
||||
Since each item stores a pointer to its parent, the \c parent() function
|
||||
is trivial:
|
||||
|
||||
\snippet itemviews/editabletreemodel/treeitem.cpp 9
|
||||
\snippet itemviews/editabletreemodel/treeitem.cpp 8
|
||||
|
||||
\target TreeItem::child
|
||||
Three functions provide information about the children of an item.
|
||||
\c child() returns a specific child from the internal list of children:
|
||||
|
||||
\snippet itemviews/editabletreemodel/treeitem.cpp 2
|
||||
\snippet itemviews/editabletreemodel/treeitem.cpp 1
|
||||
|
||||
The \c childCount() function returns the total number of children:
|
||||
|
||||
\snippet itemviews/editabletreemodel/treeitem.cpp 3
|
||||
\snippet itemviews/editabletreemodel/treeitem.cpp 2
|
||||
|
||||
The \c childNumber() function is used to determine the index of the child
|
||||
in its parent's list of children. It accesses the parent's \c childItems
|
||||
member directly to obtain this information:
|
||||
|
||||
\snippet itemviews/editabletreemodel/treeitem.cpp 4
|
||||
\snippet itemviews/editabletreemodel/treeitem.cpp 3
|
||||
|
||||
The root item has no parent item; for this item, we return zero to be
|
||||
consistent with the other items.
|
||||
@ -262,20 +260,20 @@
|
||||
The \c columnCount() function simply returns the number of elements in
|
||||
the internal \c itemData list of QVariant objects:
|
||||
|
||||
\snippet itemviews/editabletreemodel/treeitem.cpp 5
|
||||
\snippet itemviews/editabletreemodel/treeitem.cpp 4
|
||||
|
||||
\target TreeItem::data
|
||||
Data is retrieved using the \c data() function, which accesses the
|
||||
appropriate element in the \c itemData list:
|
||||
|
||||
\snippet itemviews/editabletreemodel/treeitem.cpp 6
|
||||
\snippet itemviews/editabletreemodel/treeitem.cpp 5
|
||||
|
||||
\target TreeItem::setData
|
||||
Data is set using the \c setData() function, which only stores values
|
||||
in the \c itemData list for valid list indexes, corresponding to column
|
||||
values in the model:
|
||||
|
||||
\snippet itemviews/editabletreemodel/treeitem.cpp 11
|
||||
\snippet itemviews/editabletreemodel/treeitem.cpp 10
|
||||
|
||||
To make implementation of the model easier, we return true to indicate
|
||||
that the data was set successfully.
|
||||
@ -285,20 +283,20 @@
|
||||
in the model leads to the insertion of new child items in the corresponding
|
||||
item, handled by the \c insertChildren() function:
|
||||
|
||||
\snippet itemviews/editabletreemodel/treeitem.cpp 7
|
||||
\snippet itemviews/editabletreemodel/treeitem.cpp 6
|
||||
|
||||
This ensures that new items are created with the required number of columns
|
||||
and inserted at a valid position in the internal \c childItems list.
|
||||
Items are removed with the \c removeChildren() function:
|
||||
|
||||
\snippet itemviews/editabletreemodel/treeitem.cpp 10
|
||||
\snippet itemviews/editabletreemodel/treeitem.cpp 9
|
||||
|
||||
As discussed above, the functions for inserting and removing columns are
|
||||
used differently to those for inserting and removing child items because
|
||||
they are expected to be called on every item in the tree. We do this by
|
||||
recursively calling this function on each child of the item:
|
||||
|
||||
\snippet itemviews/editabletreemodel/treeitem.cpp 8
|
||||
\snippet itemviews/editabletreemodel/treeitem.cpp 7
|
||||
|
||||
\section1 TreeModel Class Definition
|
||||
|
||||
@ -334,11 +332,12 @@
|
||||
use with the model. Other models may be initialized with a ready-made
|
||||
data structure, or use an API from a library that maintains its own data.
|
||||
|
||||
The destructor only has to delete the root item, which will cause all child
|
||||
items to be recursively deleted.
|
||||
|
||||
\snippet itemviews/editabletreemodel/treemodel.cpp 1
|
||||
|
||||
The destructor only has to delete the root item, which will cause all child
|
||||
items to be recursively deleted. This is done automatically by the default
|
||||
destructor since the root item is stored inside an unique_ptr.
|
||||
|
||||
\target TreeModel::getItem
|
||||
Since the model's interface to the other model/view components is based
|
||||
on model indexes, and since the internal data structure is item-based,
|
||||
|
@ -10,76 +10,77 @@
|
||||
#include "treeitem.h"
|
||||
|
||||
//! [0]
|
||||
TreeItem::TreeItem(const QList<QVariant> &data, TreeItem *parent)
|
||||
: itemData(data), parentItem(parent)
|
||||
TreeItem::TreeItem(const QVariantList &data, TreeItem *parent)
|
||||
: itemData(data), m_parentItem(parent)
|
||||
{}
|
||||
//! [0]
|
||||
|
||||
//! [1]
|
||||
TreeItem::~TreeItem()
|
||||
TreeItem *TreeItem::child(int number)
|
||||
{
|
||||
qDeleteAll(childItems);
|
||||
if (number < 0 || number >= m_childItems.size())
|
||||
return nullptr;
|
||||
return m_childItems.at(number).get();
|
||||
}
|
||||
//! [1]
|
||||
|
||||
//! [2]
|
||||
TreeItem *TreeItem::child(int number)
|
||||
int TreeItem::childCount() const
|
||||
{
|
||||
if (number < 0 || number >= childItems.size())
|
||||
return nullptr;
|
||||
return childItems.at(number);
|
||||
return m_childItems.size();
|
||||
}
|
||||
//! [2]
|
||||
|
||||
//! [3]
|
||||
int TreeItem::childCount() const
|
||||
int TreeItem::row() const
|
||||
{
|
||||
return childItems.count();
|
||||
if (!m_parentItem)
|
||||
return 0;
|
||||
const auto it = std::find_if(m_parentItem->m_childItems.cbegin(), m_parentItem->m_childItems.cend(),
|
||||
[this](const std::unique_ptr<TreeItem> &treeItem) {
|
||||
return treeItem.get() == const_cast<TreeItem *>(this);
|
||||
});
|
||||
|
||||
if (it != m_parentItem->m_childItems.cend())
|
||||
return std::distance(m_parentItem->m_childItems.cbegin(), it);
|
||||
Q_ASSERT(false); // should not happen
|
||||
return -1;
|
||||
}
|
||||
//! [3]
|
||||
|
||||
//! [4]
|
||||
int TreeItem::childNumber() const
|
||||
{
|
||||
if (parentItem)
|
||||
return parentItem->childItems.indexOf(const_cast<TreeItem*>(this));
|
||||
return 0;
|
||||
}
|
||||
//! [4]
|
||||
|
||||
//! [5]
|
||||
int TreeItem::columnCount() const
|
||||
{
|
||||
return itemData.count();
|
||||
}
|
||||
//! [5]
|
||||
//! [4]
|
||||
|
||||
//! [6]
|
||||
//! [5]
|
||||
QVariant TreeItem::data(int column) const
|
||||
{
|
||||
if (column < 0 || column >= itemData.size())
|
||||
return QVariant();
|
||||
return itemData.at(column);
|
||||
}
|
||||
//! [6]
|
||||
//! [5]
|
||||
|
||||
//! [7]
|
||||
//! [6]
|
||||
bool TreeItem::insertChildren(int position, int count, int columns)
|
||||
{
|
||||
if (position < 0 || position > childItems.size())
|
||||
if (position < 0 || position > m_childItems.size())
|
||||
return false;
|
||||
|
||||
for (int row = 0; row < count; ++row) {
|
||||
QList<QVariant> data(columns);
|
||||
TreeItem *item = new TreeItem(data, this);
|
||||
childItems.insert(position, item);
|
||||
QVariantList data(columns);
|
||||
m_childItems.insert(m_childItems.cbegin() + position,
|
||||
std::make_unique<TreeItem>(data, this));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
//! [7]
|
||||
//! [6]
|
||||
|
||||
//! [8]
|
||||
//! [7]
|
||||
bool TreeItem::insertColumns(int position, int columns)
|
||||
{
|
||||
if (position < 0 || position > itemData.size())
|
||||
@ -88,32 +89,32 @@ bool TreeItem::insertColumns(int position, int columns)
|
||||
for (int column = 0; column < columns; ++column)
|
||||
itemData.insert(position, QVariant());
|
||||
|
||||
for (TreeItem *child : std::as_const(childItems))
|
||||
for (auto &child : std::as_const(m_childItems))
|
||||
child->insertColumns(position, columns);
|
||||
|
||||
return true;
|
||||
}
|
||||
//! [7]
|
||||
|
||||
//! [8]
|
||||
TreeItem *TreeItem::parent()
|
||||
{
|
||||
return m_parentItem;
|
||||
}
|
||||
//! [8]
|
||||
|
||||
//! [9]
|
||||
TreeItem *TreeItem::parent()
|
||||
{
|
||||
return parentItem;
|
||||
}
|
||||
//! [9]
|
||||
|
||||
//! [10]
|
||||
bool TreeItem::removeChildren(int position, int count)
|
||||
{
|
||||
if (position < 0 || position + count > childItems.size())
|
||||
if (position < 0 || position + count > m_childItems.size())
|
||||
return false;
|
||||
|
||||
for (int row = 0; row < count; ++row)
|
||||
delete childItems.takeAt(position);
|
||||
m_childItems.erase(m_childItems.cbegin() + position);
|
||||
|
||||
return true;
|
||||
}
|
||||
//! [10]
|
||||
//! [9]
|
||||
|
||||
bool TreeItem::removeColumns(int position, int columns)
|
||||
{
|
||||
@ -123,13 +124,13 @@ bool TreeItem::removeColumns(int position, int columns)
|
||||
for (int column = 0; column < columns; ++column)
|
||||
itemData.remove(position);
|
||||
|
||||
for (TreeItem *child : std::as_const(childItems))
|
||||
for (auto &child : std::as_const(m_childItems))
|
||||
child->removeColumns(position, columns);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//! [11]
|
||||
//! [10]
|
||||
bool TreeItem::setData(int column, const QVariant &value)
|
||||
{
|
||||
if (column < 0 || column >= itemData.size())
|
||||
@ -138,4 +139,4 @@ bool TreeItem::setData(int column, const QVariant &value)
|
||||
itemData[column] = value;
|
||||
return true;
|
||||
}
|
||||
//! [11]
|
||||
//! [10]
|
||||
|
@ -11,8 +11,7 @@
|
||||
class TreeItem
|
||||
{
|
||||
public:
|
||||
explicit TreeItem(const QList<QVariant> &data, TreeItem *parent = nullptr);
|
||||
~TreeItem();
|
||||
explicit TreeItem(const QVariantList &data, TreeItem *parent = nullptr);
|
||||
|
||||
TreeItem *child(int number);
|
||||
int childCount() const;
|
||||
@ -23,13 +22,13 @@ public:
|
||||
TreeItem *parent();
|
||||
bool removeChildren(int position, int count);
|
||||
bool removeColumns(int position, int columns);
|
||||
int childNumber() const;
|
||||
int row() const;
|
||||
bool setData(int column, const QVariant &value);
|
||||
|
||||
private:
|
||||
QList<TreeItem *> childItems;
|
||||
QList<QVariant> itemData;
|
||||
TreeItem *parentItem;
|
||||
std::vector<std::unique_ptr<TreeItem>> m_childItems;
|
||||
QVariantList itemData;
|
||||
TreeItem *m_parentItem;
|
||||
};
|
||||
//! [0]
|
||||
|
||||
|
@ -6,24 +6,23 @@
|
||||
|
||||
#include <QtWidgets>
|
||||
|
||||
using namespace Qt::StringLiterals;
|
||||
|
||||
//! [0]
|
||||
TreeModel::TreeModel(const QStringList &headers, const QString &data, QObject *parent)
|
||||
: QAbstractItemModel(parent)
|
||||
{
|
||||
QList<QVariant> rootData;
|
||||
QVariantList rootData;
|
||||
for (const QString &header : headers)
|
||||
rootData << header;
|
||||
|
||||
rootItem = new TreeItem(rootData);
|
||||
setupModelData(data.split('\n'), rootItem);
|
||||
rootItem = std::make_unique<TreeItem>(rootData);
|
||||
setupModelData(data.split('\n'_L1));
|
||||
}
|
||||
//! [0]
|
||||
|
||||
//! [1]
|
||||
TreeModel::~TreeModel()
|
||||
{
|
||||
delete rootItem;
|
||||
}
|
||||
TreeModel::~TreeModel() = default;
|
||||
//! [1]
|
||||
|
||||
//! [2]
|
||||
@ -65,7 +64,7 @@ TreeItem *TreeModel::getItem(const QModelIndex &index) const
|
||||
if (item)
|
||||
return item;
|
||||
}
|
||||
return rootItem;
|
||||
return rootItem.get();
|
||||
}
|
||||
//! [4]
|
||||
|
||||
@ -130,10 +129,10 @@ QModelIndex TreeModel::parent(const QModelIndex &index) const
|
||||
TreeItem *childItem = getItem(index);
|
||||
TreeItem *parentItem = childItem ? childItem->parent() : nullptr;
|
||||
|
||||
if (parentItem == rootItem || !parentItem)
|
||||
if (parentItem == rootItem.get() || !parentItem)
|
||||
return QModelIndex();
|
||||
|
||||
return createIndex(parentItem->childNumber(), 0, parentItem);
|
||||
return createIndex(parentItem->row(), 0, parentItem);
|
||||
}
|
||||
//! [7]
|
||||
|
||||
@ -202,11 +201,11 @@ bool TreeModel::setHeaderData(int section, Qt::Orientation orientation,
|
||||
return result;
|
||||
}
|
||||
|
||||
void TreeModel::setupModelData(const QStringList &lines, TreeItem *parent)
|
||||
void TreeModel::setupModelData(const QStringList &lines)
|
||||
{
|
||||
QList<TreeItem *> parents;
|
||||
QList<int> indentations;
|
||||
parents << parent;
|
||||
parents << rootItem.get();
|
||||
indentations << 0;
|
||||
|
||||
int number = 0;
|
||||
@ -214,7 +213,7 @@ void TreeModel::setupModelData(const QStringList &lines, TreeItem *parent)
|
||||
while (number < lines.count()) {
|
||||
int position = 0;
|
||||
while (position < lines[number].length()) {
|
||||
if (lines[number].at(position) != ' ')
|
||||
if (lines[number].at(position) != ' '_L1)
|
||||
break;
|
||||
++position;
|
||||
}
|
||||
@ -224,8 +223,8 @@ void TreeModel::setupModelData(const QStringList &lines, TreeItem *parent)
|
||||
if (!lineData.isEmpty()) {
|
||||
// Read the column data from the rest of the line.
|
||||
const QStringList columnStrings =
|
||||
lineData.split(QLatin1Char('\t'), Qt::SkipEmptyParts);
|
||||
QList<QVariant> columnData;
|
||||
lineData.split('\t'_L1, Qt::SkipEmptyParts);
|
||||
QVariantList columnData;
|
||||
columnData.reserve(columnStrings.size());
|
||||
for (const QString &columnString : columnStrings)
|
||||
columnData << columnString;
|
||||
|
@ -50,10 +50,10 @@ public:
|
||||
const QModelIndex &parent = QModelIndex()) override;
|
||||
|
||||
private:
|
||||
void setupModelData(const QStringList &lines, TreeItem *parent);
|
||||
void setupModelData(const QStringList &lines);
|
||||
TreeItem *getItem(const QModelIndex &index) const;
|
||||
|
||||
TreeItem *rootItem;
|
||||
std::unique_ptr<TreeItem> rootItem;
|
||||
};
|
||||
//! [2]
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user