Qt 可编辑的树模型(Tree Model)的一个实例
本實例來自Qt 官方的一個實例(Editable Tree Model Example)
簡介:
本實例是關于怎樣基于模式視圖框架下的 樹模型的實現(xiàn)。
該模型支持可編輯的表單項,自定義表頭,刪除插入行和列,也可以插入子表單項。
在標準數(shù)據(jù)模型中提供了這些函數(shù):
flags()、data()、headerData()、columnCount()、rowCount()。
因為本實例還具有繼承的關系,所以還需要index()、parent()兩個函數(shù)。
一個可編輯的模型還需要實現(xiàn)setData()、和setHeaderData(),同時還需要一個狀態(tài)機制flags()。
由于該實例還需要實現(xiàn)行列變化,所以我們還需要實現(xiàn)insertRows()、insertColumns()、removeRows()、removeColumn()。
設計:
1、樹狀數(shù)據(jù)模型的內部關系:
從上圖中可以看到:樹狀數(shù)據(jù)模型中的每一數(shù)據(jù)項都包含它的父項(parent())和子項(child())信息。
每一個數(shù)據(jù)項(itemData)依據(jù)行和列信息進行存儲。我們可以通過簡單的data()函數(shù)來遍歷整個數(shù)據(jù)項(itemData list);通過setData()來修改數(shù)據(jù)項(itemData)
根節(jié)點(root Item)相當于一個空數(shù)據(jù)索引,用QModelIndex()來指定。
樹狀數(shù)據(jù)模型的訪問:
如圖:數(shù)據(jù)項可以通過行和列以及它的父項信息進行訪問:
QVariant a = model->index(0, 0, QModelIndex()).data();
QVariant b = model->index(1, 0, QModelIndex()).data();
樹狀數(shù)據(jù)模型的存儲結構:
數(shù)據(jù)項的存儲類似QVariant 對象。
通過數(shù)據(jù)索引來構建數(shù)據(jù)項的內在關系:
(Relating items using model indexes)
樹狀模型數(shù)據(jù)項的類定義:
class TreeItem{public:explicit TreeItem(const QVector<QVariant> &data, TreeItem *parent = nullptr);~TreeItem();TreeItem *child(int number);int childCount() const;int columnCount() const;QVariant data(int column) const;bool insertChildren(int position, int count, int columns);bool insertColumns(int position, int columns);TreeItem *parent();bool removeChildren(int position, int count);bool removeColumns(int position, int columns);int childNumber() const;bool setData(int column, const QVariant &value);private:QVector<TreeItem*> childItems;QVector<QVariant> itemData;TreeItem *parentItem;};樹狀數(shù)據(jù)模型類的成員實現(xiàn):
1、構造函數(shù): 構建數(shù)據(jù)項
TreeItem::TreeItem(const QVector<QVariant> &data, TreeItem *parent): itemData(data),parentItem(parent){}2、析構函數(shù): 確保當一個數(shù)據(jù)項刪除的時候,那么它的所有子項都會被刪除。
TreeItem::~TreeItem(){qDeleteAll(childItems);}3、parent(),每個數(shù)據(jù)項都存有指針指向它的父項
TreeItem *TreeItem::parent(){return parentItem;}4、三個函數(shù)提供關于子項的信息:
child()、childCount()、childNumber()
5、columnCount() :
int TreeItem::columnCount() const{return itemData.count();}6、獲取數(shù)據(jù)data():
QVariant TreeItem::data(int column) const{if (column < 0 || column >= itemData.size())return QVariant();return itemData.at(column);}7、修改數(shù)據(jù)
bool TreeItem::setData(int column, const QVariant &value){if (column < 0 || column >= itemData.size())return false;itemData[column] = value;return true;}8、子節(jié)點數(shù)據(jù)的插入
bool TreeItem::insertChildren(int position, int count, int columns){if (position < 0 || position > childItems.size())return false;for (int row = 0; row < count; ++row) {QVector<QVariant> data(columns);TreeItem *item = new TreeItem(data, this);childItems.insert(position, item);}return true;}9、子節(jié)點數(shù)據(jù)的刪除:
bool TreeItem::removeChildren(int position, int count){if (position < 0 || position + count > childItems.size())return false;for (int row = 0; row < count; ++row)delete childItems.takeAt(position);return true;}10、插入列:
bool TreeItem::insertColumns(int position, int columns){if (position < 0 || position > itemData.size())return false;for (int column = 0; column < columns; ++column)itemData.insert(position, QVariant());for (TreeItem *child : qAsConst(childItems))child->insertColumns(position, columns);return true;}樹狀模型類的定義:
class TreeModel : public QAbstractItemModel{Q_OBJECTpublic:TreeModel(const QStringList &headers, const QString &data,QObject *parent = nullptr);~TreeModel();//The constructor and destructor are specific to this model.QVariant data(const QModelIndex &index, int role) const override;QVariant headerData(int section, Qt::Orientation orientation,int role = Qt::DisplayRole) const override;QModelIndex index(int row, int column,const QModelIndex &parent = QModelIndex()) const override;QModelIndex parent(const QModelIndex &index) const override;int rowCount(const QModelIndex &parent = QModelIndex()) const override;int columnCount(const QModelIndex &parent = QModelIndex()) const override;//Read-only tree models only need to provide the above functions. The following public functions provide support for editing and resizing:Qt::ItemFlags flags(const QModelIndex &index) const override;bool setData(const QModelIndex &index, const QVariant &value,int role = Qt::EditRole) override;bool setHeaderData(int section, Qt::Orientation orientation,const QVariant &value, int role = Qt::EditRole) override;bool insertColumns(int position, int columns,const QModelIndex &parent = QModelIndex()) override;bool removeColumns(int position, int columns,const QModelIndex &parent = QModelIndex()) override;bool insertRows(int position, int rows,const QModelIndex &parent = QModelIndex()) override;bool removeRows(int position, int rows,const QModelIndex &parent = QModelIndex()) override;private:void setupModelData(const QStringList &lines, TreeItem *parent);TreeItem *getItem(const QModelIndex &index) const;TreeItem *rootItem;};樹狀模型類的成員實現(xiàn):
1、構造函數(shù): 創(chuàng)建根節(jié)點和表頭數(shù)據(jù)項
TreeModel::TreeModel(const QStringList &headers, const QString &data, QObject *parent): QAbstractItemModel(parent){QVector<QVariant> rootData;for (const QString &header : headers)rootData << header;rootItem = new TreeItem(rootData);setupModelData(data.split('\n'), rootItem);}2、析構函數(shù):
TreeModel::~TreeModel(){delete rootItem;}3、獲取數(shù)據(jù)項:
TreeItem *TreeModel::getItem(const QModelIndex &index) const{if (index.isValid()) {TreeItem *item = static_cast<TreeItem*>(index.internalPointer());if (item)return item;}return rootItem;}4、行統(tǒng)計:
int TreeModel::rowCount(const QModelIndex &parent) const{const TreeItem *parentItem = getItem(parent);return parentItem ? parentItem->childCount() : 0;}5、列統(tǒng)計:
int TreeModel::columnCount(const QModelIndex &parent) const{Q_UNUSED(parent);return rootItem->columnCount();}6、flags()
Qt::ItemFlags TreeModel::flags(const QModelIndex &index) const{if (!index.isValid())return Qt::NoItemFlags;return Qt::ItemIsEditable | QAbstractItemModel::flags(index);}7、創(chuàng)建數(shù)據(jù)索引
QModelIndex TreeModel::index(int row, int column, const QModelIndex &parent) const{if (parent.isValid() && parent.column() != 0)return QModelIndex();TreeItem *parentItem = getItem(parent);if (!parentItem)return QModelIndex();TreeItem *childItem = parentItem->child(row);if (childItem)return createIndex(row, column, childItem);return QModelIndex();}8、parent()
QModelIndex TreeModel::parent(const QModelIndex &index) const{if (!index.isValid())return QModelIndex();TreeItem *childItem = getItem(index);TreeItem *parentItem = childItem ? childItem->parent() : nullptr;if (parentItem == rootItem || !parentItem)return QModelIndex();return createIndex(parentItem->childNumber(), 0, parentItem);}主函數(shù)類:
/**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the examples of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:BSD$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** BSD License Usage ** Alternatively, you may use this file under the terms of the BSD license ** as follows: ** ** "Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are ** met: ** * Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** * Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in ** the documentation and/or other materials provided with the ** distribution. ** * Neither the name of The Qt Company Ltd nor the names of its ** contributors may be used to endorse or promote products derived ** from this software without specific prior written permission. ** ** ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ** ** $QT_END_LICENSE$ ** ****************************************************************************/#ifndef MAINWINDOW_H #define MAINWINDOW_H#include "ui_mainwindow.h"#include <QMainWindow>class MainWindow : public QMainWindow, private Ui::MainWindow {Q_OBJECTpublic:MainWindow(QWidget *parent = nullptr);public slots:void updateActions();private slots:void insertChild();bool insertColumn();void insertRow();bool removeColumn();void removeRow(); };#endif // MAINWINDOW_H主函數(shù)成員實現(xiàn):
/**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the examples of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:BSD$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** BSD License Usage ** Alternatively, you may use this file under the terms of the BSD license ** as follows: ** ** "Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are ** met: ** * Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** * Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in ** the documentation and/or other materials provided with the ** distribution. ** * Neither the name of The Qt Company Ltd nor the names of its ** contributors may be used to endorse or promote products derived ** from this software without specific prior written permission. ** ** ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ** ** $QT_END_LICENSE$ ** ****************************************************************************/#include "mainwindow.h" #include "treemodel.h"#include <QFile>MainWindow::MainWindow(QWidget *parent): QMainWindow(parent) {setupUi(this);const QStringList headers({tr("Title"), tr("Description")});QFile file(":/default.txt");file.open(QIODevice::ReadOnly);TreeModel *model = new TreeModel(headers, file.readAll());file.close();view->setModel(model);for (int column = 0; column < model->columnCount(); ++column)view->resizeColumnToContents(column);connect(exitAction, &QAction::triggered, qApp, &QCoreApplication::quit);connect(view->selectionModel(), &QItemSelectionModel::selectionChanged,this, &MainWindow::updateActions);connect(actionsMenu, &QMenu::aboutToShow, this, &MainWindow::updateActions);connect(insertRowAction, &QAction::triggered, this, &MainWindow::insertRow);connect(insertColumnAction, &QAction::triggered, this, &MainWindow::insertColumn);connect(removeRowAction, &QAction::triggered, this, &MainWindow::removeRow);connect(removeColumnAction, &QAction::triggered, this, &MainWindow::removeColumn);connect(insertChildAction, &QAction::triggered, this, &MainWindow::insertChild);updateActions(); }void MainWindow::insertChild() {const QModelIndex index = view->selectionModel()->currentIndex();QAbstractItemModel *model = view->model();if (model->columnCount(index) == 0) {if (!model->insertColumn(0, index))return;}if (!model->insertRow(0, index))return;for (int column = 0; column < model->columnCount(index); ++column) {const QModelIndex child = model->index(0, column, index);model->setData(child, QVariant(tr("[No data]")), Qt::EditRole);if (!model->headerData(column, Qt::Horizontal).isValid())model->setHeaderData(column, Qt::Horizontal, QVariant(tr("[No header]")), Qt::EditRole);}view->selectionModel()->setCurrentIndex(model->index(0, 0, index),QItemSelectionModel::ClearAndSelect);updateActions(); }bool MainWindow::insertColumn() {QAbstractItemModel *model = view->model();int column = view->selectionModel()->currentIndex().column();// Insert a column in the parent item.bool changed = model->insertColumn(column + 1);if (changed)model->setHeaderData(column + 1, Qt::Horizontal, QVariant("[No header]"), Qt::EditRole);updateActions();return changed; }void MainWindow::insertRow() {const QModelIndex index = view->selectionModel()->currentIndex();QAbstractItemModel *model = view->model();if (!model->insertRow(index.row()+1, index.parent()))return;updateActions();for (int column = 0; column < model->columnCount(index.parent()); ++column) {const QModelIndex child = model->index(index.row() + 1, column, index.parent());model->setData(child, QVariant(tr("[No data]")), Qt::EditRole);} }bool MainWindow::removeColumn() {QAbstractItemModel *model = view->model();const int column = view->selectionModel()->currentIndex().column();// Insert columns in each child of the parent item.const bool changed = model->removeColumn(column);if (changed)updateActions();return changed; }void MainWindow::removeRow() {const QModelIndex index = view->selectionModel()->currentIndex();QAbstractItemModel *model = view->model();if (model->removeRow(index.row(), index.parent()))updateActions(); }void MainWindow::updateActions() {const bool hasSelection = !view->selectionModel()->selection().isEmpty();removeRowAction->setEnabled(hasSelection);removeColumnAction->setEnabled(hasSelection);const bool hasCurrent = view->selectionModel()->currentIndex().isValid();insertRowAction->setEnabled(hasCurrent);insertColumnAction->setEnabled(hasCurrent);if (hasCurrent) {view->closePersistentEditor(view->selectionModel()->currentIndex());const int row = view->selectionModel()->currentIndex().row();const int column = view->selectionModel()->currentIndex().column();if (view->selectionModel()->currentIndex().parent().isValid())statusBar()->showMessage(tr("Position: (%1,%2)").arg(row).arg(column));elsestatusBar()->showMessage(tr("Position: (%1,%2) in top level").arg(row).arg(column));} }總結:花了一個上午的時間分析了這個樹狀數(shù)據(jù)模式的視圖實現(xiàn),抽象數(shù)據(jù)原本就相對難理解,這個模型和數(shù)據(jù)類的定義內容還是挺多的,需要逐步消化。
總結
以上是生活随笔為你收集整理的Qt 可编辑的树模型(Tree Model)的一个实例的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Qt 模型视图框架解读之模型
- 下一篇: Qt模型、视图解读之视图