草庐IT

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

lw向北. 2023-08-26 原文

文章目录

一、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);
    }

}

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

本文对于节点的判断是通过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^/)

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

有关Qt之QTreeView的简单使用(含源码+注释)的更多相关文章

  1. ruby - 如何使用 Nokogiri 的 xpath 和 at_xpath 方法 - 2

    我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div

  2. ruby - 使用 RubyZip 生成 ZIP 文件时设置压缩级别 - 2

    我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看ruby​​zip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d

  3. ruby - 为什么我可以在 Ruby 中使用 Object#send 访问私有(private)/ protected 方法? - 2

    类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc

  4. ruby-on-rails - 使用 Ruby on Rails 进行自动化测试 - 最佳实践 - 2

    很好奇,就使用ruby​​onrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提

  5. ruby - 在 Ruby 中使用匿名模块 - 2

    假设我做了一个模块如下:m=Module.newdoclassCendend三个问题:除了对m的引用之外,还有什么方法可以访问C和m中的其他内容?我可以在创建匿名模块后为其命名吗(就像我输入“module...”一样)?如何在使用完匿名模块后将其删除,使其定义的常量不再存在? 最佳答案 三个答案:是的,使用ObjectSpace.此代码使c引用你的类(class)C不引用m:c=nilObjectSpace.each_object{|obj|c=objif(Class===objandobj.name=~/::C$/)}当然这取决于

  6. ruby - 使用 ruby​​ 和 savon 的 SOAP 服务 - 2

    我正在尝试使用ruby​​和Savon来使用网络服务。测试服务为http://www.webservicex.net/WS/WSDetails.aspx?WSID=9&CATID=2require'rubygems'require'savon'client=Savon::Client.new"http://www.webservicex.net/stockquote.asmx?WSDL"client.get_quotedo|soap|soap.body={:symbol=>"AAPL"}end返回SOAP异常。检查soap信封,在我看来soap请求没有正确的命名空间。任何人都可以建议我

  7. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  8. ruby-on-rails - 'compass watch' 是如何工作的/它是如何与 rails 一起使用的 - 2

    我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t

  9. ruby - 使用 ruby​​ 将 HTML 转换为纯文本并维护结构/格式 - 2

    我想将html转换为纯文本。不过,我不想只删除标签,我想智能地保留尽可能多的格式。为插入换行符标签,检测段落并格式化它们等。输入非常简单,通常是格式良好的html(不是整个文档,只是一堆内容,通常没有anchor或图像)。我可以将几个正则表达式放在一起,让我达到80%,但我认为可能有一些现有的解决方案更智能。 最佳答案 首先,不要尝试为此使用正则表达式。很有可能你会想出一个脆弱/脆弱的解决方案,它会随着HTML的变化而崩溃,或者很难管理和维护。您可以使用Nokogiri快速解析HTML并提取文本:require'nokogiri'h

  10. ruby - 在 64 位 Snow Leopard 上使用 rvm、postgres 9.0、ruby 1.9.2-p136 安装 pg gem 时出现问题 - 2

    我想为Heroku构建一个Rails3应用程序。他们使用Postgres作为他们的数据库,所以我通过MacPorts安装了postgres9.0。现在我需要一个postgresgem并且共识是出于性能原因你想要pggem。但是我对我得到的错误感到非常困惑当我尝试在rvm下通过geminstall安装pg时。我已经非常明确地指定了所有postgres目录的位置可以找到但仍然无法完成安装:$envARCHFLAGS='-archx86_64'geminstallpg--\--with-pg-config=/opt/local/var/db/postgresql90/defaultdb/po

随机推荐