在我的gui中有一个QTreeWidget,当它加载到不同的数据集时,内容将被清除,我试图跟踪在不同数据集中用户加载时检查的内容。在
最初,我想用我创建的derive_tree_items
方法来跟踪它,在这个方法中它包含QTreeWidgetItem对象,但是一旦我试图加载一组新的数据,我存储的对象就会在删除时丢失(预期的)。。在
目前在一个丢失,什么是更好的方式'跟踪'这些检查项目?(我可能还需要将它们填充到QMenu+QAction中,因此可以进行跟踪检查,但这将在下次进行)
在我的代码中,您可以通过以下方式进行复制:
IsNewItemRole = QtCore.Qt.UserRole + 1000
class CustomTreeWidgetItem(QtGui.QTreeWidgetItem):
"""Initialization class for QTreeWidgetItem creation.
Args:
widget (QtGui.QTreeWidget): To append items into.
text (str): Input name for QTreeWidgetItem.
is_tristate (bool): Should it be a tri-state checkbox. False by default.
"""
def __init__(self, parent=None, text=None, is_tristate=False, is_new_item=False):
super(CustomTreeWidgetItem, self).__init__(parent)
self.setText(0, text)
# flags = QtCore.Qt.ItemIsEditable | QtCore.Qt.ItemIsUserCheckable
if is_tristate:
# flags |= QtCore.Qt.ItemIsTristate
# Solely for the Parent item
self.setFlags(
self.flags()
| QtCore.Qt.ItemIsTristate
| QtCore.Qt.ItemIsEditable
| QtCore.Qt.ItemIsUserCheckable
)
else:
self.setFlags(
self.flags()
| QtCore.Qt.ItemIsEditable
| QtCore.Qt.ItemIsUserCheckable
)
self.setCheckState(0, QtCore.Qt.Unchecked)
self.setData(0, IsNewItemRole, is_new_item)
def setData(self, column, role, value):
"""Override QTreeWidgetItem setData function.
QTreeWidget does not have a signal that defines when an item has been
checked/ unchecked. And so, this method will emits the signal as a
means to handle this.
Args:
column (int): Column value of item.
role (int): Value of Qt.ItemDataRole. It will be Qt.DisplayRole or
Qt.CheckStateRole
value (int or unicode):
"""
state = self.checkState(column)
QtGui.QTreeWidgetItem.setData(self, column, role, value)
if (role == QtCore.Qt.CheckStateRole and
state != self.checkState(column)):
tree_widget = self.treeWidget()
if isinstance(tree_widget, CustomTreeWidget):
tree_widget.itemToggled.emit(self, column)
class CustomTreeWidget(QtGui.QTreeWidget):
"""Initialization class for QTreeWidget creation.
Args:
widget ():
"""
# itemToggled = QtCore.pyqtSignal(QtGui.QTreeWidgetItem, bool)
itemToggled = QtCore.Signal(QtGui.QTreeWidgetItem, bool)
contentUpdates = QtCore.Signal()
def __init__(self, widget=None):
super(CustomTreeWidget, self).__init__(widget)
self.rename_counter = False
# self.itemToggled.connect(self.handleItemToggled)
self.currentItemChanged.connect(self.selection_item_changed)
self.itemChanged.connect(self.tree_item_changed)
self.itemDoubleClicked.connect(self.tree_item_double_clicked)
def selection_item_changed(self, current, previous):
"""Overrides widget's default signal.
Emiited when current item selection is changed. This will also toggles
the state of `self.add_child_btn`.
If a child item is selected, the "Add Child" button will be disabled.
Args:
current (CustomTreeWidgetItem): Currently selected item.
previous (CustomTreeWidgetItem or None): Previous selected item.
"""
state = True
if not current or current.parent():
state = False
def tree_item_changed(self, item, column):
"""Overrides widget's default signal.
Emitted when the contents of the selected item in the column changes.
Args:
item (CustomTreeWidgetItem): Selected item.
column (int): Column value of the selected item.
"""
if self.rename_counter and self.prev_name != item.text(column):
self.rename_counter = False
item.setData(0, IsNewItemRole, True)
self.contentUpdates.emit()
elif item.checkState(column) == QtCore.Qt.Checked:
print('Item Checked')
elif item.checkState(column) == QtCore.Qt.Unchecked:
print('Item Unchecked')
def tree_item_double_clicked(self, item, column):
"""Overrides widget's default signal.
Emitted when User performs double clicks inside the widget.
Args:
item (CustomTreeWidgetItem): Selected item.
column (int): Column value of the selected item.
"""
self.prev_name = item.text(column)
self.rename_counter = True
def derive_tree_items(self, mode="all"):
all_items = OrderedDict()
root_item = self.invisibleRootItem()
top_level_count = root_item.childCount()
for i in range(top_level_count):
top_level_item = root_item.child(i)
top_level_item_name = str(top_level_item.text(0))
child_num = top_level_item.childCount()
all_items[top_level_item_name] = []
for n in range(child_num):
child_item = top_level_item.child(n)
child_item_name = str(child_item.text(0)) or ""
all_items[top_level_item_name].append(child_item)
return all_items
class MainApp(QtGui.QWidget):
def __init__(self, parent=None):
super(MainApp, self).__init__(parent)
self._diff_highlight = False
self._tree = CustomTreeWidget()
self._tree.header().hide()
# QTreeWidget default signals override
self._tree.contentUpdates.connect(self.update_dictionary)
tree_layout = QtGui.QVBoxLayout()
self.btn1 = QtGui.QPushButton("Data-01")
self.btn2 = QtGui.QPushButton("Data-02")
tree_layout.addWidget(self._tree)
tree_layout.addWidget(self.btn1)
tree_layout.addWidget(self.btn2)
main_layout = QtGui.QHBoxLayout()
main_layout.addLayout(tree_layout)
self.setLayout(main_layout)
self.setup_connections()
def setup_connections(self):
self.btn1.clicked.connect(self.show_data_01)
self.btn2.clicked.connect(self.show_data_02)
def update_dictionary(self):
print '>>> update: ', self._tree.derive_tree_items()
def show_data_01(self):
print '>>> Button1 test'
self._tree.clear()
test_dict1 = {
"itemA" :{
"menuA": ["a101", "a102"],
},
"itemBC": {
"menuC": ["c101", "c102", "c103"],
"menuB": ["b101"]
},
}
for page_name, page_contents in test_dict1.items():
# page_item = PageHeaderItem(self._tree, page_name)
for pk, pv in page_contents.items():
parent = CustomTreeWidgetItem(self._tree, pk, is_tristate=True)
for c in pv:
child = CustomTreeWidgetItem(parent, c)
self._tree.expandAll()
def show_data_02(self):
print '>>> Button2 test'
self._tree.clear()
test_dict2 = {
"itemD" :{
"menuD": ["d100"],
},
}
for page_name, page_contents in test_dict2.items():
# page_item = PageHeaderItem(self._tree, page_name)
for pk, pv in page_contents.items():
parent = CustomTreeWidgetItem(self._tree, pk, is_tristate=True)
for c in pv:
child = CustomTreeWidgetItem(parent, c)
self._tree.expandAll()
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
w = MainApp()
w.show()
sys.exit(app.exec_())
QTreeWidget(像QListWidget和QTableWidget)有它的内部模型;它是对数据模型的某种高级访问,它的实际模型不直接(如容易)访问,也不应该。它们是“简化的”模型视图界面,用于一般用途,不需要高级编辑,但最重要的是,它们只支持自己的、单一的和唯一的模型。如果你不想在其他地方使用更复杂的数据,那就意味着你不需要在其他地方更容易地使用它们的模型来存储数据,除非你想用更复杂的方式来存储数据,这正是你的情况。在
相反,那些QWidgetItemViews提供了一些标准模型和视图中缺少的功能,其中之一就是QTreeWidgets中项目的“自动检查”。
虽然这个特性非常有用,但当您需要在同一个视图上显示不同的数据模型时,它可能是一个seriusPITA;这意味着,为了避免对轮子的修辞改造,最好使用QTreeView/QStandardItemModel对,只实现三态机制,而不是使用可能与QTreeWidget的内部实现冲突的复杂方法。在
分离
QStandardItemModel
子类实例,支持父/子三态这里最重要的一点是,您将为每个数据集使用一个单个数据模型类实例(而不是多个dict+view的模型对),通过简单的
setModel()
在它们之间切换变得更加容易。缺点是前面提到的缺少父/子状态支持,这必须实现;一旦解决了这个逻辑,您将得到多个持久的、唯一的和一致的模型,不管您实际需要多少。在
除了实际的模型内容初始化之外,您只需要将
QStandardItemModel
的两个方法子类化:setData(index, value, role)
被重写以将检查状态应用于子索引:如果角色是Qt.CheckState
,并且索引有任何子级,则对它们应用[unchecked]状态;如果索引有父级,则索引向模型发出dataChanged
信号,确保其视图需要更新(否则复选框可见状态不会更新)正确,直到重新绘制视图)[1]data(index, role)
重写需要“显示”父级的checkstate;不管模型的索引数据是什么:如果它有任何子级,它的状态完全依赖于它们(all/any/none checked),否则它基于默认模型索引的checkstate一旦解决了这个问题,您只需关心将新选择的模型设置为视图,并且所有状态都将保持在切换到另一个模型(如果有的话)之前的状态。在
为了与您的示例保持一定的一致性,我使用了基于dict的模型数据创建逻辑,但是我建议您使用递归方法来添加子子级。在
因为我已经在那里了,我还添加了一个机制来存储每个索引的扩展状态,以获得更好的视图/模型一致性;这不是必需的,但它确实有助于用户体验:-)请记住,这只是为了演示目的而存在的:显然,如果您在不考虑内部expandState dict的情况下添加/删除项,这不会正常工作(或者根本不起作用!)。在
当任何子项的状态发生变化时。这不是一个大问题,但是如果您真的需要避免不必要的dataChanged通知,那么您可能需要添加一个
QtCore.QTimer.singleshot
延迟的dataChanged信号发射,如果父状态已经改变的话。这不是很难实现,但我没有我不认为这个例子真的有必要。在相关问题 更多 >
编程相关推荐