🐟🐟🐟 实现TreeWidget中的复选框及多级联动

实现方法

  • 添加复选框

QtDesigner绘制出的TreeWidget控件是不具有复选框的功能,需要通过setCheckState(index, state)来对TreeWidget中的Item进行复选框的添加,index指的是列的序号,state则为添加时复选框的三种状态(Qt.Checked,Qt.Unchecked,Qt.PartiallyChecked)。例子中通过setTreeCheckBox()来实现复选框的添加。

  1. # 给树添加复选框
  2. def setTreeCheckBox(self):
  3. root_node = self.treeWidget.topLevelItem(0)
  4. root_node.setCheckState(0, Qt.Unchecked)
  5. for i in range(root_node.childCount()):
  6. parent_node = root_node.child(i)
  7. parent_node.setCheckState(0, Qt.Unchecked)
  8. if parent_node.childCount() > 0:
  9. for j in range(parent_node.childCount()):
  10. child_node = parent_node.child(j)
  11. child_node.setCheckState(0, Qt.Unchecked)
  12. self.treeWidget.expandAll() # 展开树
  • 实现复选框多级联动

复选框的多级联动实现主要是通过TreeWidget中自带的信号(itemChanged (QTreeWidgetItem * item , int column ))来实现。通过接收信号传输的Item对Item进行从上往下的遍历用来联动子节点的状态,以及通过自下而上的遍历来联动父节点的状态。由于在进行节点联动时,所有因为联动而改变状态的节点也会触发信号传输,所以在槽函数中不需要做递归操作。

  1. # 设置节点联动事件
  2. def onItemChange(self, item):
  3. # 联动子节点状态
  4. if item.checkState(0) != Qt.PartiallyChecked:
  5. state = item.checkState(0) # 存储节点状态防止联动时导致状态进行改变
  6. for i in range(item.childCount()):
  7. item.child(i).setCheckState(0, state)
  8. # 联动父节点状态
  9. if item.parent() is not None:
  10. check = 0
  11. uncheck = 0
  12. for j in range(item.parent().childCount()):
  13. state = item.parent().child(j).checkState(0)
  14. if state == Qt.Unchecked:
  15. uncheck += 1
  16. else:
  17. check += 1
  18. if check > 0 and uncheck == 0:
  19. item.parent().setCheckState(0, Qt.Checked) # 设置父节点为全选
  20. elif check == 0 and uncheck > 0:
  21. item.parent().setCheckState(0, Qt.Unchecked) # 设置父节点为未选中
  22. else:
  23. item.parent().setCheckState(0, Qt.PartiallyChecked) # 设置父节点为部分选中

效果展示

🍙 TreeWidget 实现复选框及多级联动 - 图1

UI部分

  1. from PySide2.QtCore import *
  2. from PySide2.QtGui import *
  3. from PySide2.QtWidgets import *
  4. class Ui_Dialog(object):
  5. def setupUi(self, Dialog):
  6. if not Dialog.objectName():
  7. Dialog.setObjectName(u"Dialog")
  8. Dialog.resize(400, 300)
  9. self.horizontalLayout = QHBoxLayout(Dialog)
  10. self.horizontalLayout.setObjectName(u"horizontalLayout")
  11. self.treeWidget = QTreeWidget(Dialog)
  12. __qtreewidgetitem = QTreeWidgetItem(self.treeWidget)
  13. QTreeWidgetItem(__qtreewidgetitem)
  14. __qtreewidgetitem1 = QTreeWidgetItem(__qtreewidgetitem)
  15. QTreeWidgetItem(__qtreewidgetitem1)
  16. QTreeWidgetItem(__qtreewidgetitem1)
  17. __qtreewidgetitem2 = QTreeWidgetItem(__qtreewidgetitem)
  18. QTreeWidgetItem(__qtreewidgetitem2)
  19. QTreeWidgetItem(__qtreewidgetitem2)
  20. self.treeWidget.setObjectName(u"treeWidget")
  21. self.horizontalLayout.addWidget(self.treeWidget)
  22. self.retranslateUi(Dialog)
  23. QMetaObject.connectSlotsByName(Dialog)
  24. # setupUi
  25. def retranslateUi(self, Dialog):
  26. Dialog.setWindowTitle(QCoreApplication.translate("Dialog", u"Dialog", None))
  27. ___qtreewidgetitem = self.treeWidget.headerItem()
  28. ___qtreewidgetitem.setText(0, QCoreApplication.translate("Dialog", u"Test", None));
  29. __sortingEnabled = self.treeWidget.isSortingEnabled()
  30. self.treeWidget.setSortingEnabled(False)
  31. ___qtreewidgetitem1 = self.treeWidget.topLevelItem(0)
  32. ___qtreewidgetitem1.setText(0, QCoreApplication.translate("Dialog", u"\u5168\u90e8\u9879\u76ee", None));
  33. ___qtreewidgetitem2 = ___qtreewidgetitem1.child(0)
  34. ___qtreewidgetitem2.setText(0, QCoreApplication.translate("Dialog", u"\u9879\u76ee1", None));
  35. ___qtreewidgetitem3 = ___qtreewidgetitem1.child(1)
  36. ___qtreewidgetitem3.setText(0, QCoreApplication.translate("Dialog", u"\u9879\u76ee2", None));
  37. ___qtreewidgetitem4 = ___qtreewidgetitem3.child(0)
  38. ___qtreewidgetitem4.setText(0, QCoreApplication.translate("Dialog", u"hello", None));
  39. ___qtreewidgetitem5 = ___qtreewidgetitem3.child(1)
  40. ___qtreewidgetitem5.setText(0, QCoreApplication.translate("Dialog", u"world", None));
  41. ___qtreewidgetitem6 = ___qtreewidgetitem1.child(2)
  42. ___qtreewidgetitem6.setText(0, QCoreApplication.translate("Dialog", u"\u9879\u76ee3", None));
  43. ___qtreewidgetitem7 = ___qtreewidgetitem6.child(0)
  44. ___qtreewidgetitem7.setText(0, QCoreApplication.translate("Dialog", u"bye", None));
  45. ___qtreewidgetitem8 = ___qtreewidgetitem6.child(1)
  46. ___qtreewidgetitem8.setText(0, QCoreApplication.translate("Dialog", u"good", None));
  47. self.treeWidget.setSortingEnabled(__sortingEnabled)

逻辑代码

  1. from PySide2.QtCore import *
  2. from PySide2.QtGui import *
  3. from PySide2.QtWidgets import *
  4. from Test import Ui_Dialog
  5. class TestDialog(QDialog, Ui_Dialog):
  6. def __init__(self):
  7. super().__init__()
  8. self.setupUi(self)
  9. self.callback()
  10. def setupUi(self, Dialog):
  11. super().setupUi(Dialog)
  12. self.setTreeCheckBox()
  13. Dialog.setWindowFlags(Qt.FramelessWindowHint)
  14. # 回调函数
  15. def callback(self):
  16. self.treeWidget.itemChanged.connect(self.onItemChange)
  17. # 给树添加复选框
  18. def setTreeCheckBox(self):
  19. root_node = self.treeWidget.topLevelItem(0)
  20. root_node.setCheckState(0, Qt.Unchecked)
  21. for i in range(root_node.childCount()):
  22. parent_node = root_node.child(i)
  23. parent_node.setCheckState(0, Qt.Unchecked)
  24. if parent_node.childCount() > 0:
  25. for j in range(parent_node.childCount()):
  26. child_node = parent_node.child(j)
  27. child_node.setCheckState(0, Qt.Unchecked)
  28. self.treeWidget.expandAll() # 展开树
  29. # 设置节点联动事件
  30. def onItemChange(self, item):
  31. if item.checkState(0) != Qt.PartiallyChecked:
  32. state = item.checkState(0)
  33. for i in range(item.childCount()):
  34. item.child(i).setCheckState(0, state)
  35. if item.parent() is not None:
  36. check = 0
  37. uncheck = 0
  38. for j in range(item.parent().childCount()):
  39. state = item.parent().child(j).checkState(0)
  40. if state == Qt.Unchecked:
  41. uncheck += 1
  42. else:
  43. check += 1
  44. if check > 0 and uncheck == 0:
  45. item.parent().setCheckState(0, Qt.Checked)
  46. elif check == 0 and uncheck > 0:
  47. item.parent().setCheckState(0, Qt.Unchecked)
  48. else:
  49. item.parent().setCheckState(0, Qt.PartiallyChecked)
  50. if __name__ == '__main__':
  51. import sys
  52. app = QApplication(sys.argv)
  53. window = TestDialog()
  54. window.show()
  55. sys.exit(app.exec_())