I have a QTreeWidget in my gui in which the contents will be cleared whenever it loads in a different set of data and I am trying to tracked what has been checked as User loads in different data set.
Initially, I thought of tracking it using derive_tree_items
method that I created in which it contains the QTreeWidgetItem object, however as soon as I tried to load in a new set of data, the objects that I stored will be lost as they are deleted (expected)..
Currently at a lost what is a better way to 'track' these checkable items? (I may also need to populate them into QMenu + QAction, hence the trackable checking but that will be for next time)
In my code, you can replicate by:
- Click on button 'Data-01'
- Check any objects, eg. I checked 'c102' and 'a102'
- Click on button 'Data-02'
- Click on button 'Data-01' again
- Expecting to see 'c102', 'a102' is checked..
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_())