考虑这个小片段:
import sys
from PyQt5 import QtWidgets
from PyQt5 import QtWidgets
from PyQt5.QtGui import QStandardItemModel
from PyQt5.QtGui import QStandardItem
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QGridLayout
from PyQt5.QtWidgets import QPushButton
from PyQt5.QtWidgets import QWidget
from PyQt5.QtWidgets import QTreeView
from PyQt5.QtWidgets import QAbstractItemView
packages = {
'tree': {
'parent1': ['child1', 'child2', 'child3'],
'parent2': ['child4', 'child5'],
'parent3': ['child6']
},
'metadata': {
'child1': {'description': 'child1 description', 'enabled': True},
'child2': {'description': 'child2 description', 'enabled': False},
'child3': {'description': 'child3 description', 'enabled': True},
'child4': {'description': 'child4 description', 'enabled': False},
'child5': {'description': 'child5 description', 'enabled': True},
'child6': {'description': 'child6 description', 'enabled': True}
}
}
class McveDialog(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.treeview = QTreeView()
# self.treeview.setHeaderHidden(True)
self.treeview.setUniformRowHeights(True)
# self.treeview.setEditTriggers(QAbstractItemView.NoEditTriggers)
# self.treeview.setSelectionMode(QAbstractItemView.ExtendedSelection)
self.model = QStandardItemModel()
self.model.setHorizontalHeaderLabels(['Package', 'Description'])
metadata = packages['metadata']
tree = packages['tree']
for parent, childs in tree.items():
parent_item = QStandardItem(f'{parent}')
parent_item.setCheckState(True)
parent_item.setCheckable(True)
parent_item.setFlags(parent_item.flags() | Qt.ItemIsAutoTristate)
# parent_item.setFlags(parent_item.flags() | Qt.ItemIsUserTristate)
self.model.appendRow(parent_item)
for child in childs:
description = metadata[child]['description']
checked = metadata[child]['enabled']
child_item = QStandardItem(f'{child}')
check = Qt.Checked if checked else Qt.Unchecked
child_item.setCheckState(check)
child_item.setCheckable(True)
# child_item.setFlags(child_item.flags() |Qt.ItemIsAutoTristate)
parent_item.appendRow(child_item)
self.treeview.setModel(self.model)
self.model.itemChanged.connect(self.on_itemChanged)
layout = QGridLayout()
row = 0
layout.addWidget(self.treeview, row, 0, 1, 3)
row += 1
self.but_ok = QPushButton("OK")
layout.addWidget(self.but_ok, row, 1)
self.but_ok.clicked.connect(self.on_ok)
self.but_cancel = QPushButton("Cancel")
layout.addWidget(self.but_cancel, row, 2)
self.but_cancel.clicked.connect(self.on_cancel)
self.setLayout(layout)
self.setGeometry(300, 200, 460, 350)
def on_itemChanged(self, item):
pass
def on_ok(self):
pass
def on_cancel(self):
self.close()
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
dialog = McveDialog()
dialog.setWindowTitle('Mcve dialog')
dialog.show()
sys.exit(app.exec_())
我试图在这里实现的是,当用户选择所有父母的孩子时,父状态将被检查;如果取消选择所有子项,则取消选择父状态;最后,如果选择了某些子项,则父项将被部分检查。 (反之亦然,因此如果用户取消选择父项,则将取消选择其所有子项,如果用户选择父项,则其所有子项将被选中)。
理论上,这种行为应该通过使用Qt::ItemIsAutoTristate标志来实现,其中包含:
项目的状态取决于其子女的状态。这使得 自动管理QTreeWidget中父项的状态 (如果所有孩子都被检查,则检查,如果所有孩子都未选中,则取消选中 未经检查,或部分检查是否只检查了一些孩子。)
但如果你运行上面的代码,你会发现在阅读文档后,这种行为并不是你所期望的。我已经看到有这个bugreport,虽然我不确定它是否与此相关,或者我的代码片段是否遗漏了一些内容。
例如,上面的代码段允许您这样做:
无论如何,问题是,你将如何修复这个小部件,以便它像任何软件包安装程序一样,你可以选择/取消选择/部分选择所有具有共同父级的子包?
答案 0 :(得分:1)
目前看来,if parent
仅适用于ItemIsAutoTristate
类。下面的QTreeWidget
子类为使用QStandardItem
的项目视图提供相同的功能。这是QStandardItemModel
实现的或多或少忠实的端口。它似乎与示例代码一起工作正常,但我还没有测试它死:
QTreeWidget
答案 1 :(得分:1)
正如@ekhumoro的答案和Qt文档中所解释的那样,ItemIsAutoTristate
似乎只为QTreeWidget类实现,只是为了完整性,这里有一个小片段,展示如何在QTreeWidget
上使用该标志可以开箱即用:
import sys
from PyQt5 import QtWidgets
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QGridLayout
from PyQt5.QtWidgets import QPushButton
from PyQt5.QtWidgets import QWidget
from PyQt5.QtWidgets import QTreeWidget
from PyQt5.QtWidgets import QTreeWidgetItem
packages = {
'tree': {
'parent1': ['child1', 'child2', 'child3'],
'parent2': ['child4', 'child5'],
'parent3': ['child6']
},
'metadata': {
'child1': {'description': 'child1 description', 'enabled': True},
'child2': {'description': 'child2 description', 'enabled': False},
'child3': {'description': 'child3 description', 'enabled': True},
'child4': {'description': 'child4 description', 'enabled': False},
'child5': {'description': 'child5 description', 'enabled': True},
'child6': {'description': 'child6 description', 'enabled': True}
}
}
class McveDialog(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.treewidget = QTreeWidget()
self.treewidget.setHeaderLabels(['Package', 'Description'])
metadata = packages['metadata']
tree = packages['tree']
for parent, childs in tree.items():
parent_item = QTreeWidgetItem(self.treewidget)
parent_item.setText(0, parent)
parent_item.setFlags(parent_item.flags() |
Qt.ItemIsAutoTristate | Qt.ItemIsUserCheckable)
parent_item.setCheckState(0, Qt.Checked)
for child in childs:
description = metadata[child]['description']
checked = metadata[child]['enabled']
child_item = QTreeWidgetItem(parent_item)
child_item.setText(0, child)
child_item.setText(
1, packages['metadata'][child]['description'])
check = Qt.Checked if checked else Qt.Unchecked
child_item.setFlags(child_item.flags() |
Qt.ItemIsUserCheckable)
child_item.setCheckState(0, check)
layout = QGridLayout()
row = 0
layout.addWidget(self.treewidget, row, 0, 1, 3)
row += 1
self.but_ok = QPushButton("OK")
layout.addWidget(self.but_ok, row, 1)
self.but_ok.clicked.connect(self.on_ok)
self.but_cancel = QPushButton("Cancel")
layout.addWidget(self.but_cancel, row, 2)
self.but_cancel.clicked.connect(self.on_cancel)
self.setLayout(layout)
self.setGeometry(300, 200, 460, 350)
def on_ok(self):
self.close()
def on_cancel(self):
self.close()
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
dialog = McveDialog()
dialog.setWindowTitle('Mcve dialog')
dialog.show()
sys.exit(app.exec_())