ItemHasChildren method of tree controls faulty with root item?

Hi,

I think I have found a problem with the ItemHasChildren method of both wx.gizmo.TreeListCtrl and wx.TreeCtrl. The problem occurs when ItemHasChildren is called for the tree root item but the root is not displayed (wx.TR_HIDE_ROOT). Specifically, the ItemHasChildren doesn't return the expected boolean results according to whether the root has any children or not. The gizmo TreeListCtrl returns True either way, and the TreeCtrl raises a PyAssertionError.

I am using Python 2.5.1 (r251:54863, Apr 18 2007, 08:51:08) [MSC v.1310 32 bit (Intel)] on win32; with wxPython version 2.8.7.1 (same problem on 2.8.6.1)

The following script demonstrates the problem:

import wx.gizmos

class TestFrame(wx.Frame):
    """
    Demonstration that the ItemHasChildren() method does not work properly
    on the root item of a tree if the root is hidden. Test 2 demonstrates
    the problem. Test 4 cannot be run (see Traceback included) below.
    """
    def __init__(self):
        wx.Frame.__init__(self, None, title="tree", size=(400,500))
        """
        Let's make 4 trees to test the 4 cases of:
        gizmo TreeListCtrl vs standard TreeCtrl
        x
        root displayed or wx.TR_HIDE_ROOT set
        """
        self.panel = wx.Panel(self)
        szrMain = wx.BoxSizer(wx.VERTICAL)
        #1
        self.treegizroot = wx.gizmos.TreeListCtrl(self.panel, -1,
                                      style=wx.TR_FULL_ROW_HIGHLIGHT,
                                                  size=(150,150))
        self.testTreeList(self.treegizroot,
                          "1 - Gizmo tree list with root displayed")
        #2
        self.treegiznoroot = wx.gizmos.TreeListCtrl(self.panel, -1,
                                      style=wx.TR_FULL_ROW_HIGHLIGHT | \
                                      wx.TR_HIDE_ROOT, size=(150,150))
        self.testTreeList(self.treegiznoroot,
                          "2 - Gizmo tree list with root hidden")
        #3
        self.treeroot = wx.TreeCtrl(self.panel, -1,
                                      style=wx.TR_FULL_ROW_HIGHLIGHT,
                                                  size=(150,150))
        self.testTree(self.treeroot, "3 - Tree with root displayed")
        #4
        self.treenoroot = wx.TreeCtrl(self.panel, -1,
                                      style=wx.TR_FULL_ROW_HIGHLIGHT | \
                                      wx.TR_HIDE_ROOT, size=(150,150))
        #self.testTree(self.treenoroot, "4 - Tree with root hidden")
        """
        Traceback (most recent call last):
          File "C:\Projects\python\storage\test_tree_has_children.py", line 92, in <module>
            frame = TestFrame()
          File "C:\Projects\python\storage\test_tree_has_children.py", line 40, in __init__
            self.testTree(self.treenoroot, "4 - Tree with root hidden")
          File "C:\Projects\python\storage\test_tree_has_children.py", line 81, in testTree
            if tree.ItemHasChildren(root) \
          File "C:\Python25\Lib\site-packages\wx-2.8-msw-unicode\wx\_controls.py", line 5332, in ItemHasChildren
            return _controls_.TreeCtrl_ItemHasChildren(*args, **kwargs)
        wx._core.PyAssertionError: C++ assertion "tvItem->hItem != ((HTREEITEM)(ULONG_PTR)-0x10000)" failed at ..\..\src\msw\treectrl.cpp(797) in wxTreeCtrl::DoGetItem(): can't retrieve virtual root item
        """
        szrMain.Add(self.treegiznoroot)
        szrMain.Add(self.treegizroot)
        szrMain.Add(self.treenoroot)
        szrMain.Add(self.treeroot)
        self.panel.SetSizer(szrMain)
        szrMain.SetSizeHints(self)
        self.Fit()

    def testTreeList(self, treelist, test_label):
        """
        Test gizmo tree list controls.
        Does ItemHasChildren say the root has children even when
        it doesn't (yet)?
        """
        treelist.AddColumn("Col 1")
        treelist.AddColumn("Col 2")
        treelist.SetMainColumn(0)
        treelist.SetColumnWidth(0, 100)
        treelist.SetColumnWidth(1, 500)
        root = treelist.AddRoot("root")
        message = "ItemHasChildren says it has children" \
                  if treelist.ItemHasChildren(root) \
                  else "ItemHasChildren says it has NO children"
        wx.MessageBox("%s - child not added yet: %s" % \
                      (test_label, message))
        treelist.AppendItem(root, "child")
        message = "ItemHasChildren says it has children" \
                  if treelist.ItemHasChildren(root) \
                  else "ItemHasChildren says it has NO children"
        wx.MessageBox("%s - just added child: %s" % (test_label, message))

    def testTree(self, tree, test_label):
        """
        Test tree controls
        Does ItemHasChildren say the root has children even when
        it doesn't (yet)?
        """
        root = tree.AddRoot("root")
        message = "ItemHasChildren says it has children" \
                  if tree.ItemHasChildren(root) \
                  else "ItemHasChildren says it has NO children"
        wx.MessageBox("%s - child not added yet: %s" % \
                      (test_label, message))
        tree.AppendItem(root, "child")
        message = "ItemHasChildren says it has children" \
                  if tree.ItemHasChildren(root) \
                  else "ItemHasChildren says it has NO children"
        wx.MessageBox("%s - just added child: %s" % (test_label, message))
       app = wx.PySimpleApp(redirect=True, filename='output.txt')
frame = TestFrame()
frame.Show()
app.MainLoop()

···

-----------------------------------------------------------------------------------

In the meantime I use the following instead:

def ItemHasChildren(tree, parent):
    item, cookie = tree.GetFirstChild(parent)
    return True if item else False

Have I understood everything correctly? Is this a bug? If so, is here the right place to raise it?

All the best, Grant

--
___________________________________

Dr Grant Paton-Simpson
Director, Paton-Simpson & Associates Ltd

www.p-s.co.nz

16 Summit Drive, Mt Albert, Auckland 1025

(09) 849-6696
(09) 849-6699

___________________________________

Grant Paton-Simpson wrote:

Hi,

I think I have found a problem with the ItemHasChildren method of both wx.gizmo.TreeListCtrl and wx.TreeCtrl. The problem occurs when ItemHasChildren is called for the tree root item but the root is not displayed (wx.TR_HIDE_ROOT). Specifically, the ItemHasChildren doesn't return the expected boolean results according to whether the root has any children or not. The gizmo TreeListCtrl returns True either way, and the TreeCtrl raises a PyAssertionError.

I agree that it doesn't make much sense, but IIUC the only things you can do with a hidden root item is add/remove/get the children. For the native widget Windows doesn't allow fetching or setting any properties (such as the has-children flag apparently) for the node, which is the source of the assertion you saw.

In the meantime I use the following instead:

def ItemHasChildren(tree, parent):
   item, cookie = tree.GetFirstChild(parent)
   return True if item else False

That sounds like a good workaround.

···

--
Robin Dunn
Software Craftsman
http://wxPython.org Java give you jitters? Relax with wxPython!