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:
parent
280a60ebe9
commit
2b250954d3
@ -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,
|
||||||
|
@ -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]
|
||||||
|
@ -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]
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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]
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user