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*)));