QTreeWidget/QTreeView三态设置

工作中经常遇到树形结构且需要设置其勾选状态,状态包括选中,半选,取消选择三种状态
1.当选中子节点时:

  • 子节点的处理
    判断若选中的节点不是半选中状态,则判断子节点与父节点状态是否一致,不一致的则设置一致
    若该节点为半选中状态,则其父节点设置为半选中状态
  • 父节点处理
    先得到父节点所有的孩子节点,判断子节点勾选的总数selectCount
    selectCount = childrenCount;则该父节点需要设置为选中状态
    selectCount = 0 ;则该父节点需要设置为未选中状态
    否则设置为半选中状态
    在QTreeWidget中树结构的每一项都是一个QTreeWidgetItem,通过对它的设置可以修改树的显示效果,对于树的三态切换需要处理QTreeWidget中的一个信号:

当某一个节点被选中或者取消选中的时候需要处理以下情况:

  1. 处理该节点的子节点(如果它有子节点),它的子节点的状态(Check或者Uncheck)和它一样 (如果它的子节点中有目录,那么还需要递归处理子节点的子节点)

  2. 处理该节点的父节点,父节点会根据当前它子节点的状态来调整自身的状态(如果该父节点还有父节点,那么还需要递归处理父节点的父节点)

在QTreeWidget中的itemChanged事件会一直递归的调用,也就是说如果我们设置了子节点的状态(使用程序设置,或者手动点击),那么被设置的节点会继续调用itemChanged信号,根据这个特点,我们在编写代码的过程中不需要考虑递归的问题,只需要设置一个层级的处理即可。

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

sample:
treewidgettest.cpp

#include "treewidgettest.h"

TreeWidgetTest::TreeWidgetTest(QWidget *parent)
    : QTreeWidget(parent)
{
    ui.setupUi(this);
	InitData();
	expandAll();
	setHeaderHidden(true);
	connect(this, SIGNAL(itemChanged(QTreeWidgetItem *, int)), this, SLOT(ItemChangedSlot(QTreeWidgetItem *, int)));
}

void TreeWidgetTest::InitData()
{
	QTreeWidgetItem* topLevelDirectory = new QTreeWidgetItem();
	topLevelDirectory->setText(0, "China");
	topLevelDirectory->setCheckState(0, Qt::Checked);
	topLevelDirectory->setIcon(0, QIcon(":/TreeWidgetTest/folder.jpeg"));
 
	QTreeWidgetItem* directoryC = new QTreeWidgetItem();
	directoryC->setText(0, "AnHui");
	directoryC->setCheckState(0, Qt::Checked);
	directoryC->setIcon(0, QIcon(":/TreeWidgetTest/folder.jpeg"));

	QTreeWidgetItem* file1 = new QTreeWidgetItem();
	file1->setText(0, "HeFei");
	file1->setCheckState(0, Qt::Checked);
	file1->setIcon(0, QIcon(":/TreeWidgetTest/file.jpeg"));

	QTreeWidgetItem* file2 = new QTreeWidgetItem();
	file2->setText(0, "Liuan");
	file2->setCheckState(0, Qt::Checked);
	file2->setIcon(0, QIcon(":/TreeWidgetTest/file.jpeg"));

	directoryC->addChild(file1);
	directoryC->addChild(file2);

	QTreeWidgetItem* directoryD = new QTreeWidgetItem();
	directoryD->setText(0, "HeNan");
	directoryD->setCheckState(0, Qt::Checked);
	directoryD->setIcon(0, QIcon(":/TreeWidgetTest/folder.jpeg"));

	QTreeWidgetItem* file3 = new QTreeWidgetItem();
	file3->setText(0, "ZhengZhou");
	file3->setCheckState(0, Qt::Checked);
	file3->setIcon(0, QIcon(":/TreeWidgetTest/file.jpeg"));

	directoryD->addChild(file3);

	QTreeWidgetItem* directoryE = new QTreeWidgetItem();
	directoryE->setText(0, "JIANG_SU");
	directoryE->setCheckState(0, Qt::Checked);
	directoryE->setIcon(0, QIcon(":/TreeWidgetTest/folder.jpeg"));

	QTreeWidgetItem* file4 = new QTreeWidgetItem();
	file4->setText(0, "SuZhou");
	file4->setCheckState(0, Qt::Checked);
	file4->setIcon(0, QIcon(":/TreeWidgetTest/file.jpeg"));

	QTreeWidgetItem* file5 = new QTreeWidgetItem();
	file5->setText(0, "WuXi");
	file5->setCheckState(0, Qt::Checked);
	file5->setIcon(0, QIcon(":/TreeWidgetTest/file.jpeg"));

	QTreeWidgetItem* direcotryE1 = new QTreeWidgetItem();
	direcotryE1->setText(0, "Nanjing");
	direcotryE1->setCheckState(0, Qt::Checked);
	direcotryE1->setIcon(0, QIcon(":/TreeWidgetTest/folder.jpeg"));

	QTreeWidgetItem* file6 = new QTreeWidgetItem();
	file6->setText(0, "JiangbeiXinqu");
	file6->setCheckState(0, Qt::Checked);
	file6->setIcon(0, QIcon(":/TreeWidgetTest/file.jpeg"));
	direcotryE1->addChild(file6);

	directoryE->addChild(file4);
	directoryE->addChild(file5);
	directoryE->addChild(direcotryE1);

	QList<QTreeWidgetItem*> topLevelItemList;
	topLevelItemList << directoryC << directoryD << directoryE;
	topLevelDirectory->addChildren(topLevelItemList);
	addTopLevelItem(topLevelDirectory);
}

void TreeWidgetTest::SetChildCheckState(QTreeWidgetItem* item, Qt::CheckState cs)
{
	if (!item) return;
	for (int i = 0; i < item->childCount(); i++)
	{
		QTreeWidgetItem* child = item->child(i);
		// 这里避免重复设置的问题
		if (child->checkState(0) != cs)
		{
			child->setCheckState(0, cs);
		}
	}
	SetParentCheckState(item->parent());
}

void TreeWidgetTest::SetParentCheckState(QTreeWidgetItem* item)
{
	if (!item) return;
	int selectedCount = 0;
	int childCount = item->childCount();
	for (int i = 0; i < childCount; i++)
	{
		QTreeWidgetItem* child = item->child(i);
		if (child->checkState(0) == Qt::Checked)
		{
			selectedCount++;
		}
	}

	if (selectedCount == 0) {
		item->setCheckState(0, Qt::Unchecked);
	}
	else if (selectedCount == childCount) {
		item->setCheckState(0, Qt::Checked);
	}
	else {
		item->setCheckState(0, Qt::PartiallyChecked);
	}
}

bool TreeWidgetTest::IsTopItem(QTreeWidgetItem* item)
{
	if (!item) 
		return false;
	if (!item->parent()) 
		return true;
	return false;
}

void TreeWidgetTest::ItemChangedSlot(QTreeWidgetItem* item, int column)
{
	if (Qt::PartiallyChecked != item->checkState(0))
		SetChildCheckState(item, item->checkState(0));
	// 此种情况不是人工点击的,而是子项设置的父节点才会这种状态
	if (Qt::PartiallyChecked == item->checkState(0))
		if (!IsTopItem(item))
			item->parent()->setCheckState(0, Qt::PartiallyChecked);
}

头文件treewidgettest.h

#pragma once

#include <QtWidgets/QWidget>
#include <QTreeWidget>
#include "ui_treewidgettest.h"

class TreeWidgetTest : public QTreeWidget
{
    Q_OBJECT

public:
    TreeWidgetTest(QWidget *parent = Q_NULLPTR);
    void InitData();
    void SetChildCheckState(QTreeWidgetItem* item, Qt::CheckState cs);
    void SetParentCheckState(QTreeWidgetItem* item);
    bool IsTopItem(QTreeWidgetItem* item);
private:
    Ui::TreeWidgetTestClass ui;

public slots:
    void ItemChangedSlot(QTreeWidgetItem* item, int column);
};

main.cpp

#include "treewidgettest.h"
#include <QtWidgets/QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    TreeWidgetTest w;
    w.show();
    return a.exec();
}

  • 1
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值