From 2b250954d3844a2b7f42bf9459a9342f2b98878a Mon Sep 17 00:00:00 2001 From: Carl Schwan Date: Fri, 26 May 2023 18:05:27 +0200 Subject: [PATCH] Modernize EditableTreeModel MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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 (cherry picked from commit 604b2feca751c5eb43b20e180c175acc2a87099e) Reviewed-by: Reviewed-by: Kai Köhne --- .../widgets/doc/src/editabletreemodel.qdoc | 33 ++++--- .../itemviews/editabletreemodel/treeitem.cpp | 87 ++++++++++--------- .../itemviews/editabletreemodel/treeitem.h | 11 ++- .../itemviews/editabletreemodel/treemodel.cpp | 29 +++---- .../itemviews/editabletreemodel/treemodel.h | 4 +- 5 files changed, 81 insertions(+), 83 deletions(-) diff --git a/examples/widgets/doc/src/editabletreemodel.qdoc b/examples/widgets/doc/src/editabletreemodel.qdoc index efadfc68428..58cd5446947 100644 --- a/examples/widgets/doc/src/editabletreemodel.qdoc +++ b/examples/widgets/doc/src/editabletreemodel.qdoc @@ -230,32 +230,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. @@ -263,20 +261,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. @@ -286,20 +284,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 @@ -335,11 +333,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, diff --git a/examples/widgets/itemviews/editabletreemodel/treeitem.cpp b/examples/widgets/itemviews/editabletreemodel/treeitem.cpp index 942ba277a35..dbdaaeb5d21 100644 --- a/examples/widgets/itemviews/editabletreemodel/treeitem.cpp +++ b/examples/widgets/itemviews/editabletreemodel/treeitem.cpp @@ -10,76 +10,77 @@ #include "treeitem.h" //! [0] -TreeItem::TreeItem(const QList &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) { + return treeItem.get() == const_cast(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(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 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(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] diff --git a/examples/widgets/itemviews/editabletreemodel/treeitem.h b/examples/widgets/itemviews/editabletreemodel/treeitem.h index cbc818c0811..d30047dd2a4 100644 --- a/examples/widgets/itemviews/editabletreemodel/treeitem.h +++ b/examples/widgets/itemviews/editabletreemodel/treeitem.h @@ -11,8 +11,7 @@ class TreeItem { public: - explicit TreeItem(const QList &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 childItems; - QList itemData; - TreeItem *parentItem; + std::vector> m_childItems; + QVariantList itemData; + TreeItem *m_parentItem; }; //! [0] diff --git a/examples/widgets/itemviews/editabletreemodel/treemodel.cpp b/examples/widgets/itemviews/editabletreemodel/treemodel.cpp index 290ec186a8c..e02a82ec468 100644 --- a/examples/widgets/itemviews/editabletreemodel/treemodel.cpp +++ b/examples/widgets/itemviews/editabletreemodel/treemodel.cpp @@ -6,24 +6,23 @@ #include +using namespace Qt::StringLiterals; + //! [0] TreeModel::TreeModel(const QStringList &headers, const QString &data, QObject *parent) : QAbstractItemModel(parent) { - QList rootData; + QVariantList rootData; for (const QString &header : headers) rootData << header; - rootItem = new TreeItem(rootData); - setupModelData(data.split('\n'), rootItem); + rootItem = std::make_unique(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 parents; QList 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 columnData; + lineData.split('\t'_L1, Qt::SkipEmptyParts); + QVariantList columnData; columnData.reserve(columnStrings.size()); for (const QString &columnString : columnStrings) columnData << columnString; diff --git a/examples/widgets/itemviews/editabletreemodel/treemodel.h b/examples/widgets/itemviews/editabletreemodel/treemodel.h index 1482846c29a..6fbd296de2c 100644 --- a/examples/widgets/itemviews/editabletreemodel/treemodel.h +++ b/examples/widgets/itemviews/editabletreemodel/treemodel.h @@ -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 rootItem; }; //! [2]