这是以前做过的例子,之前是参考的网上的例子,觉得遍历次数太多,效率不行。自己尝试优化修改了一番。小试牛刀,欢迎拍砖。效果如下图:
示例数据做得比较粗糙。来看看是怎么实现的吧。
勾选树控件的节点,其子节点全部选中或全部取消选中
父节点选中或者部分选中
QTreeWidgetItem的复选框,可以通过setCheckState(int column,Qt::CheckState state)方法调出。
勾选状态的改变,通过itemChanged(QTreeWidgetItem *item,int column)
信号触发。
connect(ui->treeWidget,&QTreeWidget::itemChanged,this,&Widget::ItemCheckChanged);
节点勾选状态的改变,1,需要改变其子节点的状态;2 ,需要改变父节点的状态;
子节点的状态改变,通过一个递归就可以实现,要么全部勾选,要么全部不勾选:
void Widget::UpdateChild(QTreeWidgetItem *itm)
{
int childCount = itm->childCount();
if(childCount > 0)
{
for(int i = 0; i < childCount; i++)
{
itm->child(i)->setCheckState(0,itm->checkState(0));
if(itm->child(i)->childCount() > 0)
UpdateChild(itm->child(i));
}
}
}
父节点的状态改变稍稍有点麻烦。存在如下几种情况:
(这个图,我实在是画的难看,就不画了,用这张勉强看看)
子节点勾选,兄弟节点不勾,则父节点半勾选,根节点半勾选;
子节点勾选,兄弟节点勾选,则父节点勾选,根节点待定(如果存在父节点的兄弟,需要递归判断);
子节点不勾选,兄弟节点不勾选,则父节点不勾选,根节点待定(同上);
子节点不勾选,兄弟节点勾选,则父节点半勾选,根节点半勾选。
由上可知,1,4两种情况是确定的,不管有多少层,父节点,根节点一定是半勾选,直接递归往上设置就可以了:
void Widget::SetParentPartiallyChecked(QTreeWidgetItem *itm)
{
QTreeWidgetItem *parent = itm->parent();
if(parent)
{
parent->setCheckState(0,Qt::PartiallyChecked);
SetParentPartiallyChecked(parent);
}
}
剩余2,3这两种情况,需要逐层遍历往上判断。
通过获取子节点及兄弟节点的勾选个数与总的节点数进行判断:
1.勾选数 == 0时,父不勾;
2.勾选数 > 0且 勾选数 < 节点总数时,父半勾;
3.勾选数 == 节点总数时,父勾选。
代码如下:
void Widget::UpdateParent(QTreeWidgetItem *itm)
{
QTreeWidgetItem *parent = itm->parent();
if(parent)
{
int checkedCount = 0;
int itemCount = parent->childCount();
for(int i = 0; i < itemCount; i++)
{
QTreeWidgetItem *item = parent->child(i);
if(Qt::Checked == item->checkState(0))
checkedCount++;
}
if(checkedCount <= 0 )
{
parent->setCheckState(0,Qt::Unchecked);
}
else if(checkedCount > 0 && checkedCount < itemCount )
{
parent->setCheckState(0,Qt::PartiallyChecked);
SetParentPartiallyChecked(parent);
return;
}
else if(checkedCount > 0 && checkedCount == itemCount)
{
parent->setCheckState(0,Qt::Checked);
}
// Recursively iterate up
UpdateParent(parent);
}
}
看起来好像没问题了,但是忽略了这种情况,导致如下问题:
根节点应该是半勾选状态,但实际是未勾选。
这是因为在“勾选数 == 0”这种情况下,忽略了对半勾选状态的判断。
“勾选数 == 0”时,还存在两种情况:
void Widget::UpdateParent(QTreeWidgetItem *itm)
{
QTreeWidgetItem *parent = itm->parent();
if(parent)
{
int checkedCount = 0;
int patiallyCount = 0;
int itemCount = parent->childCount();
for(int i = 0; i < itemCount; i++)
{
QTreeWidgetItem *item = parent->child(i);
if(Qt::Checked == item->checkState(0))
checkedCount++;
else if(Qt::PartiallyChecked == item->checkState(0))
patiallyCount++;
}
if(checkedCount <= 0 )
{
if(patiallyCount > 0)
goto Partially;
else
parent->setCheckState(0,Qt::Unchecked);
}
else if(checkedCount > 0 && checkedCount < itemCount )
{
Partially:
parent->setCheckState(0,Qt::PartiallyChecked);
SetParentPartiallyChecked(parent);
return;
}
else if(checkedCount > 0 && checkedCount == itemCount)
{
parent->setCheckState(0,Qt::Checked);
}
// Recursively iterate up
UpdateParent(parent);
}
}
这样就覆盖了刚刚忽略的情况,实现了gif图的效果。
之前的代码,设置父节点,都是一层层的往上遍历,其实是可以排除一些不必要的遍历。优化整体效率。
鸣谢:
https://blog.csdn.net/xiaosha00000/article/details/80897210