https://blog.csdn.net/csxiaoshui/article/details/50328097
在工作中我们会经常使用到树状结构,例如Windows的资源管理器中的目录结构就用到了类似的控件。在Qt中可以使用QTreeWidget来实现类似的效果。
Qt中提供的QTreeWidget功能相对比较少,在实际中经常需要扩展它的功能,比如本文将要谈到的三态树的问题。
首先我们先创建一个QTreeWidget的对象,并添加一些节点:
-
void WidgetTreeTEST::createDirectoryStruct()
-
{
-
QTreeWidgetItem *topLevelDirectory = new QTreeWidgetItem();
-
topLevelDirectory->setText( 0, "DIR");
-
topLevelDirectory->setCheckState( 0, Qt::Checked);
-
topLevelDirectory->setIcon( 0, QIcon( ":/Resources/folder.png"));
-
-
//Direcoty C
-
QTreeWidgetItem *directoryC = new QTreeWidgetItem();
-
directoryC->setText( 0, "LocalDrive C");
-
directoryC->setCheckState( 0, Qt::Checked);
-
directoryC->setIcon( 0, QIcon( ":/Resources/folder.png"));
-
-
QTreeWidgetItem *file1 = new QTreeWidgetItem();
-
file1->setText( 0, "file1");
-
file1->setCheckState( 0, Qt::Checked);
-
file1->setIcon( 0, QIcon( ":/Resources/file.png"));
-
-
QTreeWidgetItem *file2 = new QTreeWidgetItem();
-
file2->setText( 0, "file2");
-
file2->setCheckState( 0, Qt::Checked);
-
file2->setIcon( 0, QIcon( ":/Resources/file.png"));
-
-
directoryC->addChild(file1);
-
directoryC->addChild(file2);
-
-
//Directory D
-
QTreeWidgetItem *directoryD = new QTreeWidgetItem();
-
directoryD->setText( 0, "LocalDrive D");
-
directoryD->setCheckState( 0, Qt::Checked);
-
directoryD->setIcon( 0, QIcon( ":/Resources/folder.png"));
-
-
QTreeWidgetItem *file3 = new QTreeWidgetItem();
-
file3->setText( 0, "file3");
-
file3->setCheckState( 0, Qt::Checked);
-
file3->setIcon( 0, QIcon( ":/Resources/file.png"));
-
-
directoryD->addChild(file3);
-
-
//Direcoty E
-
QTreeWidgetItem *directoryE = new QTreeWidgetItem();
-
directoryE->setText( 0, "LocalDrive E");
-
directoryE->setCheckState( 0, Qt::Checked);
-
directoryE->setIcon( 0, QIcon( ":/Resources/folder.png"));
-
-
QTreeWidgetItem *file4 = new QTreeWidgetItem();
-
file4->setText( 0, "file4");
-
file4->setCheckState( 0, Qt::Checked);
-
file4->setIcon( 0, QIcon( ":/Resources/file.png"));
-
-
QTreeWidgetItem *file5 = new QTreeWidgetItem();
-
file5->setText( 0, "file5");
-
file5->setCheckState( 0, Qt::Checked);
-
file5->setIcon( 0, QIcon( ":/Resources/file.png"));
-
-
QTreeWidgetItem *direcotryE1 = new QTreeWidgetItem();
-
direcotryE1->setText( 0, "file6");
-
direcotryE1->setCheckState( 0, Qt::Checked);
-
direcotryE1->setIcon( 0, QIcon( ":/Resources/folder.png"));
-
-
QTreeWidgetItem *file6 = new QTreeWidgetItem();
-
file6->setText( 0, "file6");
-
file6->setCheckState( 0, Qt::Checked);
-
file6->setIcon( 0, QIcon( ":/Resources/file.png"));
-
direcotryE1->addChild(file6);
-
-
directoryE->addChild(file4);
-
directoryE->addChild(file5);
-
directoryE->addChild(direcotryE1);
-
-
QList<QTreeWidgetItem*> topLevelItemList;
-
topLevelItemList << directoryC << directoryD << directoryE;
-
topLevelDirectory->addChildren(topLevelItemList);
-
addTopLevelItem(topLevelDirectory);
-
}
在QTreeWidget中树结构的每一项都是一个QTreeWidgetItem,通过对它的设置可以修改树的显示效果,对于树的三态切换需要处理QTreeWidget中的一个信号:
当某一个节点被选中或者取消选中的时候需要处理以下情况:
1. 处理该节点的子节点(如果它有子节点),它的子节点的状态(Check或者Uncheck)和它一样 (如果它的子节点中有目录,那么还需要递归处理子节点的子节点)
2. 处理该节点的父节点,父节点会根据当前它子节点的状态来调整自身的状态(如果该父节点还有父节点,那么还需要递归处理父节点的父节点)
在QTreeWidget中的itemChanged事件会一直递归的调用,也就是说如果我们设置了子节点的状态(使用程序设置,或者手动点击),那么被设置的节点会继续调用itemChanged信号,根据这个特点,我们在编写代码的过程中不需要考虑递归的问题,只需要设置一个层级的处理即可。
具体实现如下:
设置子节点和父节点的状态
-
void WidgetTreeTEST::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 WidgetTreeTEST::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);
-
}
-
}
-
void WidgetTreeTEST::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);
-
}
-
-
-
bool WidgetTreeTEST::isTopItem(QTreeWidgetItem* item)
-
{
-
if(!item) return false;
-
if(!item->parent()) return true;
-
return false;
-
}
最后结果如下图所示: