QTreeWidget 树形多级复选框的实现

QTreeWidget 中可以直接将item项设置成可勾选的CheckBox形态,进而可以实现多层级的复选框结构。每个item都有三种状态:勾选,未勾选,部分勾选。

然而,由于各层级item的勾选状态相互之间是联动的,每一项item的状态改变会同步导致其子项和父项的状态发生变化。这个更新状态的功能是需要我们自己实现的。

参考了一些文章但发现还是有bug,所以在他们的基础上进一步完善了一下,目前没发现bug。

直接上函数实现的源代码: 

//创建树形控件
void Dialog::init()
{
    ui->treeWidget->clear();    //初始化树形控件
    ui->treeWidget->setHeaderHidden(true);  //隐藏表头

    //定义第一个树形组 爷爷项
    QTreeWidgetItem* group1 = new QTreeWidgetItem(ui->treeWidget);
    group1->setText(0, QStringLiteral("Tree"));    //树形控件显示的文本信息
    group1->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable);   //设置树形控件子项的属性
    group1->setCheckState(0, Qt::Unchecked); //初始状态没有被选中

    //父亲项
    QTreeWidgetItem* subItem11 = new QTreeWidgetItem(group1);
    subItem11->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable);
    subItem11->setText(0, "subItem11");  //设置子项显示的文本
    subItem11->setCheckState(0, Qt::Unchecked); //设置子选项的显示格式和状态

    QTreeWidgetItem* subItem12 = new QTreeWidgetItem(group1);
    subItem12->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable);
    subItem12->setText(0, "subItem12");
    subItem12->setCheckState(0, Qt::Unchecked);

    QTreeWidgetItem* subItem13 = new QTreeWidgetItem(group1);
    subItem13->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable);
    subItem13->setText(0, "subItem13");
    subItem13->setCheckState(0, Qt::Unchecked);

    //儿子项
    QTreeWidgetItem* group2 = new QTreeWidgetItem(subItem13);
    group2->setText(0, "group2");
    group2->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable);
    group2->setCheckState(0, Qt::Unchecked);

    QTreeWidgetItem* group3 = new QTreeWidgetItem(subItem13);
    group3->setText(0, "group3");
    group3->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable);
    group3->setCheckState(0, Qt::Unchecked);

    //孙子项
    QTreeWidgetItem* subItem21 = new QTreeWidgetItem(group2);   //指定子项属于哪一个父项
    subItem21->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable);
    subItem21->setText(0, "subItem21");
    subItem21->setCheckState(0, Qt::Unchecked);

    QTreeWidgetItem* subItem22 = new QTreeWidgetItem(group2);
    subItem22->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable);
    subItem22->setText(0, "subItem22");
    subItem22->setCheckState(0, Qt::Unchecked);

    QTreeWidgetItem* subItem23 = new QTreeWidgetItem(group2);
    subItem23->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable);
    subItem23->setText(0, "subItem23");
    subItem23->setCheckState(0, Qt::Unchecked);

    ui->treeWidget->expandAll();  //展开树

}

//更新父亲项的状态
void Dialog::updateParentItem(QTreeWidgetItem* item)
{
    QTreeWidgetItem *parent = item->parent();
    if (parent == nullptr)
        return;

    int nSelectedCount = 0;
    int childCount = parent->childCount();

    for (int i = 0; i < childCount; i++) //判断有多少个子项被选中
    {
        QTreeWidgetItem* childItem = parent->child(i);
        if (childItem->checkState(0) == Qt::Checked)
        {
            nSelectedCount++;
        }
    }

    if (nSelectedCount <= 0){  //如果没有子项被选中,还要向下检查是否有某一级子孙项被勾选
        if (isThereCheckedDescendant(item)){
            parent->setCheckState(0, Qt::PartiallyChecked); //如果有则设置父项为部分勾选状态
        }else{
            parent->setCheckState(0, Qt::Unchecked); //如果没有则设置父项为未勾选状态
        }
    }else if (nSelectedCount > 0 && nSelectedCount < childCount){    //如果有部分子项被选中,父项设置为部分选中状态,即用灰色显示
        parent->setCheckState(0, Qt::PartiallyChecked);
    }else if (nSelectedCount == childCount){    //如果子项全部被选中,父项则设置为选中状态
        parent->setCheckState(0, Qt::Checked);
    }

    updateParentItem(parent);
}

//检查是否有某一级子孙项被勾选
bool Dialog::isThereCheckedDescendant(QTreeWidgetItem* item)
{
    int childCount = item->childCount(); //返回子项的个数
    if(childCount > 0){
        for(int i = 0; i < childCount; i++){
            if(item->child(i)->checkState(0) == Qt::Checked || item->child(i)->checkState(0) == Qt::PartiallyChecked){
                return true;
            }else{
                isThereCheckedDescendant(item->child(i));
            }
        }
        return false;
    }else{
        return false;
    }
}

//更新儿子项的状态
void Dialog::updateChildItem(QTreeWidgetItem *item, int &n)
{
    int childCount = item->childCount(); //返回子项的个数
    if(childCount > 0){
        for(int i = 0; i < childCount; i++)
        {
            if(n == 0){
                item->child(i)->setCheckState(0, Qt::Checked);
            }else if(n == 1){
                item->child(i)->setCheckState(0, Qt::Unchecked);
            }
            if(item->child(i)->childCount() > 0){
                updateChildItem(item->child(i), n);
            }
        }
    }else{
        return;
    }
}

//槽函数
void Dialog::onTreeItemChanged(QTreeWidgetItem* item)
{
    if (Qt::Checked == item->checkState(0)){
        int n = 0;
        updateChildItem(item, n);
    }else if (Qt::Unchecked == item->checkState(0)){
        int n = 1;
        updateChildItem(item, n);
    }
    updateParentItem(item);
}

在构造函数中调用,connect时使用了itemClicked信号:

init();
connect(ui->treeWidget, SIGNAL(itemClicked(QTreeWidgetItem*, int)), this, SLOT(onTreeItemChanged(QTreeWidgetItem*)));

 

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值