Qt之QTreeView的简单使用(含源码+注释)

一、QTreeView操作示例图

1.节点的添加删除示例图

下图为节点添加删除示例图,其中包含添加顶级节点、添加子节点、移除节点等操作;源码在本文第三节(源码含详细注释)。
在这里插入图片描述

2.节点的值的获取与修改

下图为节点对节点值的操作,其中包含获取值、设置值等;源码在本文第三节(源码含详细注释)。
在这里插入图片描述
提示:不会使用Qt设计师设计界面的小伙伴点击这里

二、QTreeView(个人理解)

这里我们将QTreeView和QTableView对比一下

  1. 二者都是类似MVC(Model View Controller)模式,其中都包含Delegate,请查看Qt代理的实现(按钮篇)Qt代理的实现(常规控件篇)
  2. 二者都使用QStandardItemModel存放数据;
  3. QTreeView也存在一个继承其QTreeWidget的子类;
  4. QTreeView会唯一不同的是会操作item的子节点。

三、源码

CMainWindow.h

#ifndef CMAINWINDOW_H
#define CMAINWINDOW_H

#include <QMainWindow>
#include <QStandardItemModel>       //数据模型类

namespace Ui {
class CMainWindow;
}

class CMainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit CMainWindow(QWidget *parent = 0);
    ~CMainWindow();

private slots:

    /**
     * @brief on_getCurNodeDataBtn_clicked 获取当前节点值
     */
    void on_getCurNodeDataBtn_clicked();

    /**
     * @brief on_setCurNodeDataBtn_clicked 设置当前节点值
     */
    void on_setCurNodeDataBtn_clicked();

    /**
     * @brief on_addTopNodeBtn_clicked 添加顶级节点
     */
    void on_addTopNodeBtn_clicked();

    /**
     * @brief on_addChildNodeBtn_clicked 添加子节点
     */
    void on_addChildNodeBtn_clicked();

    /**
     * @brief on_removeCurNodeBtn_clicked 移除当前节点
     */
    void on_removeCurNodeBtn_clicked();

private:
    Ui::CMainWindow     *ui;

    QStandardItemModel  *m_pModel;      //数据模型对象指针
};

CMainWindow.cpp

#include "CMainWindow.h"
#include "ui_CMainWindow.h"

CMainWindow::CMainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::CMainWindow)
{
    ui->setupUi(this);
    //设置窗口标题
    this->setWindowTitle("QTreeView的简单使用");

    //===============数据模型(QStandardItemModel)===============
    //建立数据模型对象空间并指定父对象
    m_pModel = new QStandardItemModel(ui->treeView);
    //将数据模型设置到树形视图上
    ui->treeView->setModel(m_pModel);
    //设置水平表头列平均分
    ui->treeView->header()->setSectionResizeMode(QHeaderView::Stretch);
    //添加列标题
    m_pModel->setHorizontalHeaderLabels(QStringList() << "one" << "two");

}

CMainWindow::~CMainWindow()
{
    //! 析构函数:
    //! 有些小伙伴会发现我没有析构model对象,
    //! 那是因为我在获取对象空间的时候指定了父对象,
    //! 当其父对象析构时,会先析构其子对象为指针的对象。
    delete ui;
}

void CMainWindow::on_getCurNodeDataBtn_clicked()
{
    //获取当前行列
    QModelIndex curIndex = ui->treeView->currentIndex();
    int row = curIndex.row();
    int column = curIndex.column();

    //当前位置包含-1值返回
    if( -1 == row || -1 == column)
    {
        return;
    }

    ///不获取item的情况///
    /// 仅需要获取文本时使用
    //! 通过index获取值,默认的data获取的是Qt::DisplayRole的值,
    //! 通常来说拿到的值就是显示的值
    ui->valueEdit->setText(curIndex.data().toString());

}

void CMainWindow::on_setCurNodeDataBtn_clicked()
{
    //获取当前行列
    QModelIndex curIndex = ui->treeView->currentIndex();
    int row = curIndex.row();
    int column = curIndex.column();

    //当前位置包含-1值返回
    if( -1 == row || -1 == column)
    {
        return;
    }

    ///需要获取item的情况///
    /// \brief curItem 当需要拿到具体item/位置时使用
    /// 比如设置item值

    QStandardItem *curItem;
    int parentRow = curIndex.data(Qt::UserRole + 1).toInt();
    //判断顶级节点值选择相应操作
    if(-1 == parentRow)
    {
        curItem = m_pModel->item(row, column);
    }
    else
    {
        //获取当前位置的顶级节点
        QStandardItem *parentItem = m_pModel->item(parentRow);
        //通过顶级节点获取子节点
        curItem = parentItem->child(row, column);
    }

    //获取值编辑框中的值并设置到item上
    curItem->setText(ui->valueEdit->text());
}

void CMainWindow::on_addTopNodeBtn_clicked()
{
    int index = m_pModel->rowCount();
    QList<QStandardItem *> topList;
    //链表容器添加顶级节点
    topList << new QStandardItem(QString("顶级节点:%1-1").arg(index + 1))
            << new QStandardItem(QString("顶级节点:%1-2").arg(index + 1));

    //! 设置Qt::UserRole + n: 用户自定义值
    //! 当n值不同时,相当于不同的键值,同理不同键则可设置不同值
    //! 此处设置data是为了获取顶级节点值,通过该值判断和做出对应的操作
    topList[0]->setData(-1, Qt::UserRole + 1); //设置父节点行,当值为-1时则当前为顶级节点
    topList[1]->setData(-1, Qt::UserRole + 1);
    //添加顶级节点
    m_pModel->appendRow(topList);
}

void CMainWindow::on_addChildNodeBtn_clicked()
{
    //获取当前行列
    QModelIndex curIndex = ui->treeView->currentIndex();
    int row = curIndex.row();
    int column = curIndex.column();
    int parentRow = curIndex.data(Qt::UserRole + 1).toInt();

    //当前行列值包含-1值或当前节点非顶级节点时返回
    if( -1 == row || -1 == column || -1 != parentRow)
    {
        return;
    }

    //获取指定行的首个item
    QStandardItem *curTopItem = m_pModel->item(row);

    //为顶级节点添加子节点
    QList<QStandardItem *> childList;
    childList << new QStandardItem(QString("子节点:%1-1").arg(curTopItem->rowCount()))
              << new QStandardItem(QString("子节点:%1-2").arg(curTopItem->rowCount()));
    //设置item的data
    childList[0]->setData(row, Qt::UserRole + 1);
    childList[1]->setData(row, Qt::UserRole + 1);
    //添加子节点
    curTopItem->appendRow(childList);
}

void CMainWindow::on_removeCurNodeBtn_clicked()
{
    //获取当前行列
    QModelIndex curIndex = ui->treeView->currentIndex();
    int row = curIndex.row();
    int column = curIndex.column();

    //当前行列值包含-1值或当前节点非顶级节点时返回
    if( -1 == row || -1 == column)
    {
        return;
    }

    int parentRow = curIndex.data(Qt::UserRole + 1).toInt();
    //判断顶级节点值选择相应的移除操作
    if(-1 == parentRow)
    {
        m_pModel->removeRow(row);
    }
    else
    {
        //移除某个子节点需要找到其顶级节点
        QStandardItem *parentItem = m_pModel->item(parentRow);
        parentItem->removeRow(row);
    }

}

CMainWindow.ui

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>CMainWindow</class>
 <widget class="QMainWindow" name="CMainWindow">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>485</width>
    <height>374</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>CMainWindow</string>
  </property>
  <widget class="QWidget" name="centralWidget">
   <layout class="QGridLayout" name="gridLayout">
    <item row="0" column="0" colspan="3">
     <widget class="QTreeView" name="treeView"/>
    </item>
    <item row="1" column="0">
     <widget class="QPushButton" name="addTopNodeBtn">
      <property name="text">
       <string>添加顶级节点</string>
      </property>
     </widget>
    </item>
    <item row="1" column="1">
     <widget class="QPushButton" name="addChildNodeBtn">
      <property name="text">
       <string>添加子节点</string>
      </property>
     </widget>
    </item>
    <item row="1" column="2">
     <widget class="QPushButton" name="removeCurNodeBtn">
      <property name="text">
       <string>移除当前节点</string>
      </property>
     </widget>
    </item>
    <item row="2" column="0">
     <widget class="QPushButton" name="getCurNodeDataBtn">
      <property name="text">
       <string>获取当前节点数据</string>
      </property>
     </widget>
    </item>
    <item row="2" column="1" rowspan="2" colspan="2">
     <widget class="QLineEdit" name="valueEdit">
      <property name="sizePolicy">
       <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
        <horstretch>0</horstretch>
        <verstretch>0</verstretch>
       </sizepolicy>
      </property>
     </widget>
    </item>
    <item row="3" column="0">
     <widget class="QPushButton" name="setCurNodeDataBtn">
      <property name="text">
       <string>设置当前节点数据</string>
      </property>
     </widget>
    </item>
   </layout>
  </widget>
  <widget class="QMenuBar" name="menuBar">
   <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
     <width>485</width>
     <height>23</height>
    </rect>
   </property>
  </widget>
  <widget class="QToolBar" name="mainToolBar">
   <attribute name="toolBarArea">
    <enum>TopToolBarArea</enum>
   </attribute>
   <attribute name="toolBarBreak">
    <bool>false</bool>
   </attribute>
  </widget>
  <widget class="QStatusBar" name="statusBar"/>
 </widget>
 <layoutdefault spacing="6" margin="11"/>
 <resources/>
 <connections/>
</ui>

四、拓展:上级节点的获取与判断

本文对于节点的判断是通过data设置用户定义值操作的,还有一种方法,是通过获取当前位置的QModelIndex对象获取其父对象的QModelIndex判断,代码如下(下方代码仅适用于本文):
提示:据说以下方法不严谨,如手动指定父类对象时。

void CMainWindow::parentIndex()
{
    //获取当前节点的QModelIndex对象
    QModelIndex index = ui->treeView->currentIndex();
    //获取当前节点父对象的QModelIndex对象
    QModelIndex parentIndex = index.parent();
    //判断其父节点的行列值,顶级节点的父节点行列值为-1
    if(-1 == parentIndex.row() || -1 == parentIndex.column())
    {
        qDebug() << "当前节点为顶级节点";
    }
    else
    {
        qDebug() << "当前节点为子节点";
    }

}

总结

QTreeView在判断节点位置时需要注意,并且要设置子节点需要线拿到其父节点才行;每个子节点只有通过其父节点行位置为0的item才可设置;QTreeView设置代理的方式和QTableView一样,需要注意的是,设置的列会贯穿子节点(子节点的对应列同样会生效)。
又是一周,明天继续搬砖了,加油!

相关文章

Qt之QTableView的简单使用(含源码+注释)
Qt之QListView的简单使用(含源码+注释)
Qt代理的实现(按钮篇,含源码+注释)
Qt代理的实现(常规控件篇,含源码+注释)
Qt之QTableView设置多列表头复选框(自定义QHeaderView)、单元格复选框(含源码+注释)
Qt之QSortFilterProxyModel的简单使用(QTableView搜索功能,含源码+注释)

友情提示——哪里看不懂可私哦,让我们一起互相进步吧
(创作不易,请留下一个免费的赞叭 谢谢 ^o^/)

注:文章为作者编程过程中所遇到的问题和总结,内容仅供参考,若有错误欢迎指出。
注:如有侵权,请联系作者删除

  • 32
    点赞
  • 173
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 13
    评论
Qt Creator中,使用QTreeView加载XML数据的过程与在Python中的过程类似。以下是一个简单的示例: 1. 在Qt Creator中创建一个新的Qt Widgets Application项目。 2. 在项目中添加一个QTreeView控件(例如,将其拖放到主窗口的中心部分)。 3. 在项目中添加一个XML文件,并将其命名为“data.xml”(或者您自己喜欢的其他名称)。 4. 在XML文件中添加数据(例如,以下是一个示例XML文件): ```xml <?xml version="1.0" encoding="UTF-8"?> <root> <item name="Item 1"> <subitem name="Subitem 1-1" /> <subitem name="Subitem 1-2" /> </item> <item name="Item 2"> <subitem name="Subitem 2-1" /> <subitem name="Subitem 2-2" /> </item> </root> ``` 5. 在Qt Creator的代码编辑器中打开主窗口类的头文件,并添加以下代码: ```cpp #include <QMainWindow> #include <QStandardItemModel> #include <QTreeView> namespace Ui { class MainWindow; } class MainWindow : public QMainWindow { Q_OBJECT public: explicit MainWindow(QWidget *parent = nullptr); ~MainWindow(); private: Ui::MainWindow *ui; QStandardItemModel *m_model; // 模型指针 void loadXmlData(QString filename); // 加载XML数据的函数 }; ``` 6. 在主窗口类的源文件中添加以下代码: ```cpp #include "mainwindow.h" #include "ui_mainwindow.h" #include <QFile> #include <QXmlStreamReader> #include <QStandardItem> #include <QStandardItemModel> MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow), m_model(new QStandardItemModel(this)) { ui->setupUi(this); // 加载XML数据 loadXmlData("data.xml"); // 将模型设置为QTreeView的数据源 ui->treeView->setModel(m_model); ui->treeView->expandAll(); // 展开所有项 } MainWindow::~MainWindow() { delete ui; } void MainWindow::loadXmlData(QString filename) { QFile file(filename); if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { return; } QXmlStreamReader xml_reader(&file); QStandardItem *root_item = m_model->invisibleRootItem(); while (!xml_reader.atEnd()) { xml_reader.readNext(); if (xml_reader.isStartElement()) { if (xml_reader.name() == "item") { QStandardItem *item = new QStandardItem(xml_reader.attributes().value("name").toString()); root_item->appendRow(item); while (!xml_reader.atEnd()) { xml_reader.readNext(); if (xml_reader.isEndElement() && xml_reader.name() == "item") { break; } if (xml_reader.isStartElement() && xml_reader.name() == "subitem") { QString name = xml_reader.attributes().value("name").toString(); QStandardItem *subitem = new QStandardItem(name); item->appendRow(subitem); } } } } } } ``` 在上述代码中,我们在主窗口类中添加了一个名为“loadXmlData”的私有函数,该函数负责加载XML数据并将其添加到QStandardItemModel中。我们还在构造函数中将模型设置为QTreeView的数据源,并使用“expandAll”函数展开所有项。 注意,我们使用QXmlStreamReader来解析XML数据。我们也使用QStandardItem类来创建QTreeView的数据项,并将它们添加到模型中。 7. 运行程序并查看QTreeView中显示的XML数据。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lw向北.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值