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:
Carl Schwan 2023-05-26 18:05:27 +02:00 committed by Volker Hilsheimer
parent 7bad2902f8
commit 604b2feca7
5 changed files with 81 additions and 83 deletions

View File

@ -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,

View File

@ -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]

View File

@ -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]

View File

@ -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;

View File

@ -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]