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>
(cherry picked from commit 604b2feca751c5eb43b20e180c175acc2a87099e)
Reviewed-by: <carl@carlschwan.eu>
Reviewed-by: Kai Köhne <kai.koehne@qt.io>
This commit is contained in:
Carl Schwan 2023-05-26 18:05:27 +02:00 committed by Friedemann Kleint
parent 280a60ebe9
commit 2b250954d3
5 changed files with 81 additions and 83 deletions

View File

@ -230,32 +230,30 @@
internal \c childItems member using the \c insertChildren() function internal \c childItems member using the \c insertChildren() function
described later. described later.
The destructor ensures that each child added to the item is deleted The children are stored in std::unique_ptr to ensures that each child
when the item itself is deleted: added to the item is deleted when the item itself is deleted.
\snippet itemviews/editabletreemodel/treeitem.cpp 1
\target TreeItem::parent \target TreeItem::parent
Since each item stores a pointer to its parent, the \c parent() function Since each item stores a pointer to its parent, the \c parent() function
is trivial: is trivial:
\snippet itemviews/editabletreemodel/treeitem.cpp 9 \snippet itemviews/editabletreemodel/treeitem.cpp 8
\target TreeItem::child \target TreeItem::child
Three functions provide information about the children of an item. Three functions provide information about the children of an item.
\c child() returns a specific child from the internal list of children: \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: 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 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 in its parent's list of children. It accesses the parent's \c childItems
member directly to obtain this information: 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 The root item has no parent item; for this item, we return zero to be
consistent with the other items. consistent with the other items.
@ -263,20 +261,20 @@
The \c columnCount() function simply returns the number of elements in The \c columnCount() function simply returns the number of elements in
the internal \c itemData list of QVariant objects: the internal \c itemData list of QVariant objects:
\snippet itemviews/editabletreemodel/treeitem.cpp 5 \snippet itemviews/editabletreemodel/treeitem.cpp 4
\target TreeItem::data \target TreeItem::data
Data is retrieved using the \c data() function, which accesses the Data is retrieved using the \c data() function, which accesses the
appropriate element in the \c itemData list: appropriate element in the \c itemData list:
\snippet itemviews/editabletreemodel/treeitem.cpp 6 \snippet itemviews/editabletreemodel/treeitem.cpp 5
\target TreeItem::setData \target TreeItem::setData
Data is set using the \c setData() function, which only stores values Data is set using the \c setData() function, which only stores values
in the \c itemData list for valid list indexes, corresponding to column in the \c itemData list for valid list indexes, corresponding to column
values in the model: 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 To make implementation of the model easier, we return true to indicate
that the data was set successfully. that the data was set successfully.
@ -286,20 +284,20 @@
in the model leads to the insertion of new child items in the corresponding in the model leads to the insertion of new child items in the corresponding
item, handled by the \c insertChildren() function: 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 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. and inserted at a valid position in the internal \c childItems list.
Items are removed with the \c removeChildren() function: 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 As discussed above, the functions for inserting and removing columns are
used differently to those for inserting and removing child items because 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 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: 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 \section1 TreeModel Class Definition
@ -335,11 +333,12 @@
use with the model. Other models may be initialized with a ready-made 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. 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 \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 \target TreeModel::getItem
Since the model's interface to the other model/view components is based 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, on model indexes, and since the internal data structure is item-based,

View File

@ -10,76 +10,77 @@
#include "treeitem.h" #include "treeitem.h"
//! [0] //! [0]
TreeItem::TreeItem(const QList<QVariant> &data, TreeItem *parent) TreeItem::TreeItem(const QVariantList &data, TreeItem *parent)
: itemData(data), parentItem(parent) : itemData(data), m_parentItem(parent)
{} {}
//! [0] //! [0]
//! [1] //! [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] //! [1]
//! [2] //! [2]
TreeItem *TreeItem::child(int number) int TreeItem::childCount() const
{ {
if (number < 0 || number >= childItems.size()) return m_childItems.size();
return nullptr;
return childItems.at(number);
} }
//! [2] //! [2]
//! [3] //! [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] //! [3]
//! [4] //! [4]
int TreeItem::childNumber() const
{
if (parentItem)
return parentItem->childItems.indexOf(const_cast<TreeItem*>(this));
return 0;
}
//! [4]
//! [5]
int TreeItem::columnCount() const int TreeItem::columnCount() const
{ {
return itemData.count(); return itemData.count();
} }
//! [5] //! [4]
//! [6] //! [5]
QVariant TreeItem::data(int column) const QVariant TreeItem::data(int column) const
{ {
if (column < 0 || column >= itemData.size()) if (column < 0 || column >= itemData.size())
return QVariant(); return QVariant();
return itemData.at(column); return itemData.at(column);
} }
//! [6] //! [5]
//! [7] //! [6]
bool TreeItem::insertChildren(int position, int count, int columns) bool TreeItem::insertChildren(int position, int count, int columns)
{ {
if (position < 0 || position > childItems.size()) if (position < 0 || position > m_childItems.size())
return false; return false;
for (int row = 0; row < count; ++row) { for (int row = 0; row < count; ++row) {
QList<QVariant> data(columns); QVariantList data(columns);
TreeItem *item = new TreeItem(data, this); m_childItems.insert(m_childItems.cbegin() + position,
childItems.insert(position, item); std::make_unique<TreeItem>(data, this));
} }
return true; return true;
} }
//! [7] //! [6]
//! [8] //! [7]
bool TreeItem::insertColumns(int position, int columns) bool TreeItem::insertColumns(int position, int columns)
{ {
if (position < 0 || position > itemData.size()) if (position < 0 || position > itemData.size())
@ -88,32 +89,32 @@ bool TreeItem::insertColumns(int position, int columns)
for (int column = 0; column < columns; ++column) for (int column = 0; column < columns; ++column)
itemData.insert(position, QVariant()); itemData.insert(position, QVariant());
for (TreeItem *child : std::as_const(childItems)) for (auto &child : std::as_const(m_childItems))
child->insertColumns(position, columns); child->insertColumns(position, columns);
return true; return true;
} }
//! [7]
//! [8]
TreeItem *TreeItem::parent()
{
return m_parentItem;
}
//! [8] //! [8]
//! [9] //! [9]
TreeItem *TreeItem::parent()
{
return parentItem;
}
//! [9]
//! [10]
bool TreeItem::removeChildren(int position, int count) bool TreeItem::removeChildren(int position, int count)
{ {
if (position < 0 || position + count > childItems.size()) if (position < 0 || position + count > m_childItems.size())
return false; return false;
for (int row = 0; row < count; ++row) for (int row = 0; row < count; ++row)
delete childItems.takeAt(position); m_childItems.erase(m_childItems.cbegin() + position);
return true; return true;
} }
//! [10] //! [9]
bool TreeItem::removeColumns(int position, int columns) 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) for (int column = 0; column < columns; ++column)
itemData.remove(position); itemData.remove(position);
for (TreeItem *child : std::as_const(childItems)) for (auto &child : std::as_const(m_childItems))
child->removeColumns(position, columns); child->removeColumns(position, columns);
return true; return true;
} }
//! [11] //! [10]
bool TreeItem::setData(int column, const QVariant &value) bool TreeItem::setData(int column, const QVariant &value)
{ {
if (column < 0 || column >= itemData.size()) if (column < 0 || column >= itemData.size())
@ -138,4 +139,4 @@ bool TreeItem::setData(int column, const QVariant &value)
itemData[column] = value; itemData[column] = value;
return true; return true;
} }
//! [11] //! [10]

View File

@ -11,8 +11,7 @@
class TreeItem class TreeItem
{ {
public: public:
explicit TreeItem(const QList<QVariant> &data, TreeItem *parent = nullptr); explicit TreeItem(const QVariantList &data, TreeItem *parent = nullptr);
~TreeItem();
TreeItem *child(int number); TreeItem *child(int number);
int childCount() const; int childCount() const;
@ -23,13 +22,13 @@ public:
TreeItem *parent(); TreeItem *parent();
bool removeChildren(int position, int count); bool removeChildren(int position, int count);
bool removeColumns(int position, int columns); bool removeColumns(int position, int columns);
int childNumber() const; int row() const;
bool setData(int column, const QVariant &value); bool setData(int column, const QVariant &value);
private: private:
QList<TreeItem *> childItems; std::vector<std::unique_ptr<TreeItem>> m_childItems;
QList<QVariant> itemData; QVariantList itemData;
TreeItem *parentItem; TreeItem *m_parentItem;
}; };
//! [0] //! [0]

View File

@ -6,24 +6,23 @@
#include <QtWidgets> #include <QtWidgets>
using namespace Qt::StringLiterals;
//! [0] //! [0]
TreeModel::TreeModel(const QStringList &headers, const QString &data, QObject *parent) TreeModel::TreeModel(const QStringList &headers, const QString &data, QObject *parent)
: QAbstractItemModel(parent) : QAbstractItemModel(parent)
{ {
QList<QVariant> rootData; QVariantList rootData;
for (const QString &header : headers) for (const QString &header : headers)
rootData << header; rootData << header;
rootItem = new TreeItem(rootData); rootItem = std::make_unique<TreeItem>(rootData);
setupModelData(data.split('\n'), rootItem); setupModelData(data.split('\n'_L1));
} }
//! [0] //! [0]
//! [1] //! [1]
TreeModel::~TreeModel() TreeModel::~TreeModel() = default;
{
delete rootItem;
}
//! [1] //! [1]
//! [2] //! [2]
@ -65,7 +64,7 @@ TreeItem *TreeModel::getItem(const QModelIndex &index) const
if (item) if (item)
return item; return item;
} }
return rootItem; return rootItem.get();
} }
//! [4] //! [4]
@ -130,10 +129,10 @@ QModelIndex TreeModel::parent(const QModelIndex &index) const
TreeItem *childItem = getItem(index); TreeItem *childItem = getItem(index);
TreeItem *parentItem = childItem ? childItem->parent() : nullptr; TreeItem *parentItem = childItem ? childItem->parent() : nullptr;
if (parentItem == rootItem || !parentItem) if (parentItem == rootItem.get() || !parentItem)
return QModelIndex(); return QModelIndex();
return createIndex(parentItem->childNumber(), 0, parentItem); return createIndex(parentItem->row(), 0, parentItem);
} }
//! [7] //! [7]
@ -202,11 +201,11 @@ bool TreeModel::setHeaderData(int section, Qt::Orientation orientation,
return result; return result;
} }
void TreeModel::setupModelData(const QStringList &lines, TreeItem *parent) void TreeModel::setupModelData(const QStringList &lines)
{ {
QList<TreeItem *> parents; QList<TreeItem *> parents;
QList<int> indentations; QList<int> indentations;
parents << parent; parents << rootItem.get();
indentations << 0; indentations << 0;
int number = 0; int number = 0;
@ -214,7 +213,7 @@ void TreeModel::setupModelData(const QStringList &lines, TreeItem *parent)
while (number < lines.count()) { while (number < lines.count()) {
int position = 0; int position = 0;
while (position < lines[number].length()) { while (position < lines[number].length()) {
if (lines[number].at(position) != ' ') if (lines[number].at(position) != ' '_L1)
break; break;
++position; ++position;
} }
@ -224,8 +223,8 @@ void TreeModel::setupModelData(const QStringList &lines, TreeItem *parent)
if (!lineData.isEmpty()) { if (!lineData.isEmpty()) {
// Read the column data from the rest of the line. // Read the column data from the rest of the line.
const QStringList columnStrings = const QStringList columnStrings =
lineData.split(QLatin1Char('\t'), Qt::SkipEmptyParts); lineData.split('\t'_L1, Qt::SkipEmptyParts);
QList<QVariant> columnData; QVariantList columnData;
columnData.reserve(columnStrings.size()); columnData.reserve(columnStrings.size());
for (const QString &columnString : columnStrings) for (const QString &columnString : columnStrings)
columnData << columnString; columnData << columnString;

View File

@ -50,10 +50,10 @@ public:
const QModelIndex &parent = QModelIndex()) override; const QModelIndex &parent = QModelIndex()) override;
private: private:
void setupModelData(const QStringList &lines, TreeItem *parent); void setupModelData(const QStringList &lines);
TreeItem *getItem(const QModelIndex &index) const; TreeItem *getItem(const QModelIndex &index) const;
TreeItem *rootItem; std::unique_ptr<TreeItem> rootItem;
}; };
//! [2] //! [2]