QTreeWidget树的三态操作

https://blog.csdn.net/csxiaoshui/article/details/50328097

在工作中我们会经常使用到树状结构,例如Windows的资源管理器中的目录结构就用到了类似的控件。在Qt中可以使用QTreeWidget来实现类似的效果。

Qt中提供的QTreeWidget功能相对比较少,在实际中经常需要扩展它的功能,比如本文将要谈到的三态树的问题。


首先我们先创建一个QTreeWidget的对象,并添加一些节点:

  1. void WidgetTreeTEST::createDirectoryStruct()
  2. {
  3. QTreeWidgetItem *topLevelDirectory = new QTreeWidgetItem();
  4. topLevelDirectory->setText( 0, "DIR");
  5. topLevelDirectory->setCheckState( 0, Qt::Checked);
  6. topLevelDirectory->setIcon( 0, QIcon( ":/Resources/folder.png"));
  7. //Direcoty C
  8. QTreeWidgetItem *directoryC = new QTreeWidgetItem();
  9. directoryC->setText( 0, "LocalDrive C");
  10. directoryC->setCheckState( 0, Qt::Checked);
  11. directoryC->setIcon( 0, QIcon( ":/Resources/folder.png"));
  12. QTreeWidgetItem *file1 = new QTreeWidgetItem();
  13. file1->setText( 0, "file1");
  14. file1->setCheckState( 0, Qt::Checked);
  15. file1->setIcon( 0, QIcon( ":/Resources/file.png"));
  16. QTreeWidgetItem *file2 = new QTreeWidgetItem();
  17. file2->setText( 0, "file2");
  18. file2->setCheckState( 0, Qt::Checked);
  19. file2->setIcon( 0, QIcon( ":/Resources/file.png"));
  20. directoryC->addChild(file1);
  21. directoryC->addChild(file2);
  22. //Directory D
  23. QTreeWidgetItem *directoryD = new QTreeWidgetItem();
  24. directoryD->setText( 0, "LocalDrive D");
  25. directoryD->setCheckState( 0, Qt::Checked);
  26. directoryD->setIcon( 0, QIcon( ":/Resources/folder.png"));
  27. QTreeWidgetItem *file3 = new QTreeWidgetItem();
  28. file3->setText( 0, "file3");
  29. file3->setCheckState( 0, Qt::Checked);
  30. file3->setIcon( 0, QIcon( ":/Resources/file.png"));
  31. directoryD->addChild(file3);
  32. //Direcoty E
  33. QTreeWidgetItem *directoryE = new QTreeWidgetItem();
  34. directoryE->setText( 0, "LocalDrive E");
  35. directoryE->setCheckState( 0, Qt::Checked);
  36. directoryE->setIcon( 0, QIcon( ":/Resources/folder.png"));
  37. QTreeWidgetItem *file4 = new QTreeWidgetItem();
  38. file4->setText( 0, "file4");
  39. file4->setCheckState( 0, Qt::Checked);
  40. file4->setIcon( 0, QIcon( ":/Resources/file.png"));
  41. QTreeWidgetItem *file5 = new QTreeWidgetItem();
  42. file5->setText( 0, "file5");
  43. file5->setCheckState( 0, Qt::Checked);
  44. file5->setIcon( 0, QIcon( ":/Resources/file.png"));
  45. QTreeWidgetItem *direcotryE1 = new QTreeWidgetItem();
  46. direcotryE1->setText( 0, "file6");
  47. direcotryE1->setCheckState( 0, Qt::Checked);
  48. direcotryE1->setIcon( 0, QIcon( ":/Resources/folder.png"));
  49. QTreeWidgetItem *file6 = new QTreeWidgetItem();
  50. file6->setText( 0, "file6");
  51. file6->setCheckState( 0, Qt::Checked);
  52. file6->setIcon( 0, QIcon( ":/Resources/file.png"));
  53. direcotryE1->addChild(file6);
  54. directoryE->addChild(file4);
  55. directoryE->addChild(file5);
  56. directoryE->addChild(direcotryE1);
  57. QList<QTreeWidgetItem*> topLevelItemList;
  58. topLevelItemList << directoryC << directoryD << directoryE;
  59. topLevelDirectory->addChildren(topLevelItemList);
  60. addTopLevelItem(topLevelDirectory);
  61. }
创建的场景结构如下图所示:


在QTreeWidget中树结构的每一项都是一个QTreeWidgetItem,通过对它的设置可以修改树的显示效果,对于树的三态切换需要处理QTreeWidget中的一个信号:


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

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

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

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

具体实现如下:

设置子节点和父节点的状态

  1. void WidgetTreeTEST::setChildCheckState(QTreeWidgetItem *item, Qt::CheckState cs)
  2. {
  3. if(!item) return;
  4. for ( int i= 0;i<item->childCount();i++)
  5. {
  6. QTreeWidgetItem* child=item->child(i);
  7. if(child->checkState( 0)!=cs)
  8. {
  9. child->setCheckState( 0, cs);
  10. }
  11. }
  12. setParentCheckState(item->parent());
  13. }
  14. void WidgetTreeTEST::setParentCheckState(QTreeWidgetItem *item)
  15. {
  16. if(!item) return;
  17. int selectedCount= 0;
  18. int childCount = item->childCount();
  19. for ( int i= 0;i<childCount;i++)
  20. {
  21. QTreeWidgetItem* child= item->child(i);
  22. if(child->checkState( 0)==Qt::Checked)
  23. {
  24. selectedCount++;
  25. }
  26. }
  27. if(selectedCount == 0) {
  28. item->setCheckState( 0,Qt::Unchecked);
  29. } else if (selectedCount == childCount) {
  30. item->setCheckState( 0,Qt::Checked);
  31. } else {
  32. item->setCheckState( 0,Qt::PartiallyChecked);
  33. }
  34. }
在信号的响应槽函数中:

  1. void WidgetTreeTEST::itemChangedSlot(QTreeWidgetItem* item, int column)
  2. {
  3. if(Qt::PartiallyChecked!=item->checkState( 0))
  4. setChildCheckState(item,item->checkState( 0));
  5. if(Qt::PartiallyChecked==item->checkState( 0))
  6. if(!isTopItem(item))
  7. item->parent()->setCheckState( 0,Qt::PartiallyChecked);
  8. }
  9. bool WidgetTreeTEST::isTopItem(QTreeWidgetItem* item)
  10. {
  11. if(!item) return false;
  12. if(!item->parent()) return true;
  13. return false;
  14. }
槽函数中根据PartiallyChecked进行分类:因为只有组节点(包含子节点的节点)才可能有partiallyChecked的状态,当组节点被设置为partiallyChecked的时候它的父节点也会被设置为这种类型的状态,并且当组节点设置为PartiallyChecked的时候对子节点的状态没有任何影响。

最后结果如下图所示:




猜你喜欢

转载自blog.csdn.net/chenyijun/article/details/80913969