If a CustomTreeCtrl
is configured to use checkboxes and the TR_AUTO_CHECK_PARENT
style is specified, then, if you check all the child items under a parent item, the parent item (and its parent item, etc, if appropriate) is/are automatically checked. This appears to work correctly.
However, if you then uncheck one of the child items, the parent item is not unchecked. This can be demonstrated using the following app:
import wx
import wx.lib.agw.customtreectrl as CT
class MyFrame(wx.Frame):
def __init__(self, parent):
wx.Frame.__init__(self, parent, -1, "CustomTreeCtrl")
agw_style = (wx.TR_DEFAULT_STYLE |
CT.TR_HAS_BUTTONS |
CT.TR_AUTO_CHECK_CHILD |
CT.TR_AUTO_CHECK_PARENT)
tree = CT.CustomTreeCtrl(self, agwStyle=agw_style)
root = tree.AddRoot("Root", ct_type=1)
for i in range(1, 3):
child = tree.AppendItem(root, "Folder %d" % i, ct_type=1)
for j in range(1, 4):
tree.AppendItem(child, "File %d-%d" % (i, j), ct_type=1)
tree.ExpandAll()
if __name__ == '__main__':
app = wx.App(0)
frame = MyFrame(None)
frame.Show()
app.MainLoop()
Is it just me, or do others find this behaviour illogical?
The automatic checking of the parent items is handled by the CustomTreeCtrl.AutoCheckParent()
method. That method is called both when a child item is checked and when it is unchecked. It contains a while loop which compares each child of the parent with the value of the checked
parameter. If any of the child items doesn’t have the same checked state, the method returns without doing anything.
That is fine when the checked
parameter is True
, but when it is False
, the method will return if any of the child items’ checked state is True
so that the parent item will not be unchecked unless all the child items are unchecked. That does not seem correct to me as the tree then appears to be in an inconsistent state.
In the app below I derived a subclass of CustomTreeCtrl
that overrides the AutoCheckParent()
method to only execute the while loop if the checked
parameter is True. I prefer this behaviour compared to the original.
Does anyone have any comments on this suggestion?
import wx
import wx.lib.agw.customtreectrl as CT
class MyCustomTreeCtrl(CT.CustomTreeCtrl):
def AutoCheckParent(self, item, checked):
"""
Traverses up the tree and checks/unchecks parent items.
:param `item`: an instance of :class:`GenericTreeItem`;
:param bool `checked`: ``True`` to check an item, ``False`` to uncheck it.
:note: This method is meaningful only for checkbox-like and radiobutton-like items.
"""
parent = item.GetParent()
if not parent or parent.GetType() != 1:
return
if checked:
(child, cookie) = self.GetFirstChild(parent)
while child:
if child.GetType() == 1 and child.IsEnabled():
if checked != child.IsChecked():
return
(child, cookie) = self.GetNextChild(parent, cookie)
self.CheckItem2(parent, checked, torefresh=True)
self.AutoCheckParent(parent, checked)
class MyFrame(wx.Frame):
def __init__(self, parent):
wx.Frame.__init__(self, parent, -1, "CustomTreeCtrl")
agw_style = (wx.TR_DEFAULT_STYLE |
CT.TR_HAS_BUTTONS |
CT.TR_AUTO_CHECK_CHILD |
CT.TR_AUTO_CHECK_PARENT)
tree = MyCustomTreeCtrl(self, agwStyle=agw_style)
root = tree.AddRoot("Root", ct_type=1)
for i in range(1, 3):
child = tree.AppendItem(root, "Folder %d" % i, ct_type=1)
for j in range(1, 4):
tree.AppendItem(child, "File %d-%d" % (i, j), ct_type=1)
tree.ExpandAll()
if __name__ == '__main__':
app = wx.App(0)
frame = MyFrame(None)
frame.Show()
app.MainLoop()