Qt 使用QTreeView实现可复选树

Qt 使用QTreeView实现可复选树

需求描述

我们经常可能有个需求就是:我们需要以树形结构去展示并选择某种数据,如下所示:
在这里插入图片描述
其中要求可以选择树上的节点,且当某节点的被选中后其子节点和父节点要随之联动。

实现分析

我不清楚Qt有没有这样现成的控件支持,据我所知可能没有(了解的人请多指教哟),所以我们可以简易实现一个,由于我们项目很多时候都是采用model+view的形式进行,所以我打算采取直接在QTreeView上做一些功夫:继承自QTreeView,为子类增添一些界面上的特性:即根据模型的选中状态随即改变模型中父子的选中关系。

注意,model+view的模式,是qt的模型视图框架,数据都被存在模型中,视图只是模型的一种呈现形式,是否可选存在于模型中,选中与未选中一个数据项被记录在了模型中,视图本身不记录这些信息,它只通过一些手段显示这些属性。

实现代码

我就简单的粘贴下代码吧,这只是一个快速实现的思路,有什么不妥烦请指教:
代码逻辑是,在为treeView设置模型时,我们连接一个模型数据更改的信号槽,一旦模型改变(节点设置为选中时会触发,当然其他数据改变也会触发,此处不做处理)就去刷新其刷新选中状态:

connect(model, SIGNAL(itemChanged(QStandardItem *)),
            this, SLOT(onStandardItemChanged(QStandardItem *)));

具体怎么刷新模型选中状态可参考下面源码:
可选树视图头文件checkabletreeview.h:

#ifndef CHECKABLETREEVIEW_H
#define CHECKABLETREEVIEW_H

#include <QTreeView>
#include <QStandardItemModel>
/**
 * @brief 三态联动可选树视图
 * @author zhoumo
 * @date 2021107 冬至 冷死
 * @note 该类继承自QTreeView, 仅仅在QTreeView的基础上加了一个支持模型的可选联动功能
 */
class CheckableTreeView : public QTreeView
{
    Q_OBJECT
public:
    CheckableTreeView(QWidget *parent=0);
    ~CheckableTreeView();

    /**
     * @brief 设置树模型
     * @param model 输入 待显示模型
     */
    void setStandardItemModel(QStandardItemModel *model);

private slots:
    /**
     * @brief 模型中数据值改变调用槽
     * @param item 输入 改变的item
     */
    void onStandardItemChanged(QStandardItem *item);

private:
    /**
     * @brief 更新父节点状态
     * @param item 输入 待更新父节点的子节点
     */
    void updateParentState(QStandardItem *item);

};

#endif // CHECKABLETREEVIEW_H

可选树视图cpp文件checkabletreeview.cpp

#include "checkabletreeview.h"
#include <QDebug>
CheckableTreeView::CheckableTreeView(QWidget *parent):
    QTreeView(parent)
{

}

CheckableTreeView::~CheckableTreeView()
{

}

void CheckableTreeView::setStandardItemModel(QStandardItemModel *model)
{
    // 将获取model的改变来设置其父子间的变换关系
    connect(model, SIGNAL(itemChanged(QStandardItem *)),
            this, SLOT(onStandardItemChanged(QStandardItem *)));
    this->setModel(model);

}

void CheckableTreeView::onStandardItemChanged(QStandardItem *item)
{
    /************************
     * 如果item checked
     *      孩子全部checked
     *      父亲检查后更新状态
     * 如果item partly
     *      什么也不做
     *      父亲检查后更新状态
     * 如果item unchecked
     *      孩子全部都设置为unchecked
     *      父亲检查后更新状态
     * ***********************/

    if(NULL != item){
        // 更新孩子的状态
        Qt::CheckState parentState = item->checkState();
        int rowCount = -1;
        if(Qt::Unchecked == parentState){
            rowCount = item->rowCount();
            for(int i=0; i<rowCount; ++i){
                item->child(i)->setCheckState(Qt::Unchecked);
            }
        }else if(Qt::Checked == parentState){
            rowCount = item->rowCount();
            for(int i=0; i<rowCount; ++i){
                item->child(i)->setCheckState(Qt::Checked);
            }
        }
        // 更新父节点的状态
        updateParentState(item);
    }
}

void CheckableTreeView::updateParentState(QStandardItem *item)
{
    /***************************************************
     * 全部checked 则父节点设置为Checked
     * 部分checked 或 存在PartiallyChecked 则父节点设置为PartiallyChecked
     * 无 checked 则父节点设置为Unchecked
     ****************************************************/

    if(NULL == item || NULL == item->parent()){
        return;
    }
    // 统计父节点下选中状态
    QStandardItem *parentItem = item->parent();
    int allChildCount = parentItem->rowCount();
    int checkedItemCount = 0; // 记录孩子有多少处于选中状态
    bool hasPartly = false; // 记录孩子是否存在部分选中状态
    for(int i=0; i<allChildCount; ++i){
        QStandardItem *childItem = parentItem->child(i);
        if(childItem->checkState() == Qt::Checked){
            checkedItemCount++;
        }
        else if(childItem->checkState() == Qt::PartiallyChecked){
            hasPartly = true;
            break;
        }
    }
    // 如果包含部分选中则直接设置为部分选中
    if(hasPartly){
        parentItem->setCheckState(Qt::PartiallyChecked);
    }else{
        // 一个也没选中
        if(0 == checkedItemCount){
            parentItem->setCheckState(Qt::Unchecked);
        }
        // 选中了一部分
        else if(checkedItemCount > 0
                && checkedItemCount < allChildCount){
            parentItem->setCheckState(Qt::PartiallyChecked);
        }
        // 一个也没选中
        else if(checkedItemCount == allChildCount){
            parentItem->setCheckState(Qt::Checked);
        }
    }
}

测试代码:

    // 构建一个树模型
    QStandardItemModel *model = new QStandardItemModel();
    QStandardItem *root = model->invisibleRootItem();
    QStandardItem *itemA = new QStandardItem("A");
    QStandardItem *itemA1 = new QStandardItem("A1");
    QStandardItem *itemA2 = new QStandardItem("A2");
    QStandardItem *itemB = new QStandardItem("B");
    QStandardItem *itemB1 = new QStandardItem("B1");
    QStandardItem *itemB2 = new QStandardItem("B2");
    QStandardItem *itemB21 = new QStandardItem("B21");
    QStandardItem *itemB211 = new QStandardItem("B211");
    QStandardItem *itemB212 = new QStandardItem("B212");
    // 设置为可选
    itemA->setCheckable(true);
    itemA1->setCheckable(true);
    itemA2->setCheckable(true);
    itemB->setCheckable(true);
    itemB1->setCheckable(true);
    itemB2->setCheckable(true);
    itemB21->setCheckable(true);
    itemB211->setCheckable(true);
    itemB212->setCheckable(true);
    itemA->appendRow(itemA1);
    itemA->appendRow(itemA2);
    itemB21->appendRow(itemB211);
    itemB21->appendRow(itemB212);
    itemB2->appendRow(itemB21);
    itemB->appendRow(itemB1);
    itemB->appendRow(itemB2);
    root->appendRow(itemA);
    root->appendRow(itemB);

    // treeView 被提升为自定义的CheckableTreeView
    ui->treeView->setStandardItemModel(model);

效果:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值