Different behavior of a TreeCtrl while terminating, depending on the position of event binding

Hi Frank,
thank you for your answer.

Frank Niessink:

When your program exists, the items in the tree are deleted one by
one. When the first item is deleted while it is selected, the
selection moves to the second item and you get an event, etc.

That explains the events while terminating in the derived TreeCtrl. But
why are there no events, if i bind the event in the frame? Thats why I
asked.

I have analyzed the termination process of the TreeCtrl with the Bind in
the derived TreeCtrl and discovered another irregularity. You said that
when the program is terminated the items are deleted one by one. When I
save all TreeItems that I have create in a list and calls item.IsOk()
for every item in the list in the OnSelectionChange event, the function
is true for all items even the ones, that should be deleted. This is
fatal when i define relative PyData and tries to read the Data while
termination. Hence I get bad C++ assertions from the wrapper:
I have modified the second test and get now the following output:

    OnSelectionChanged, Item 0 --> 9 8 7 6 5 4 3 2 1 0 // the
OnSelectionChanged event on startup passes, because no item has been deleted
    OnSelectionChanged, Item 1 --> 9 8 7 6 5 4 3 2 1 // the first
OnSelectionChanged event while terminating fails for the first item,
because the item has been deleted already (but item.IsOk() was true!)
    Traceback (most recent call last):
      File "...\test_tree_inh_pydata.py", line 27, in OnTreeSelChanged
        print self.GetItemPyData(item),
      File
"C:\Programme\Python25\Lib\site-packages\wx-2.8-msw-unicode\wx\_controls.py",
line 5270, in GetItemPyData
        return _controls_.TreeCtrl_GetItemPyData(*args, **kwargs)
    wx._core.PyAssertionError: C++ assertion "param" failed at
..\..\src\msw\treectrl.cpp(1064) in wxTreeCtrl::SetItemData(): failed to
change tree items data

I am more and more thinking that this is not the correct behavior, but
rather a bug. What do you think?
I have also rewritten the program on C++ with wxWidgets 2.8.6.1 with a
static event table in the derived TreeCtrl and are no OnSelectionChanged
events occurs while terminating (checked with a breakpoint)

Christian

PS: I am am using WinXP SP2
PS: Here the code of my modified test script:
<test_tree_inh_pydata.py>
#!/usr/bin/env python
# -*- coding: utf-8

import wx

class MyTreeCtrl(wx.TreeCtrl):
    def __init__(self, *args, **kwargs):
        wx.TreeCtrl.__init__(self, *args, **kwargs)
       
        self.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnTreeSelChanged, self)
       
        self._items =
        self.insertTreeItems()
   
    def insertTreeItems(self):
        root = self.AddRoot('')
        for i in xrange(10):
            item = self.AppendItem(root, 'Item %d' % i)
            self.SetItemPyData(item, i)
            self._items.append(item)
        self._items.reverse()
   
    def OnTreeSelChanged(self, event):
        print "OnSelectionChanged,", self.GetItemText(event.GetItem()),
"-->",
        for item in self._items:
            if item and item.IsOk():
                print self.GetItemPyData(item),
        print

class MyFrame(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, None, -1, "MyFrame")
       
        self._tree = MyTreeCtrl(self,
style=wx.TR_DEFAULT_STYLE|wx.TR_HIDE_ROOT)

if __name__ == "__main__":
    app = wx.PySimpleApp()
    frame = MyFrame()
    frame.Show()
    app.MainLoop()

Christian wrote:

Hi Frank,
thank you for your answer.

Frank Niessink:

When your program exists, the items in the tree are deleted one by
one. When the first item is deleted while it is selected, the
selection moves to the second item and you get an event, etc.

That explains the events while terminating in the derived TreeCtrl. But
why are there no events, if i bind the event in the frame? Thats why I
asked.

Because when the events propagate to the frame the frame is already far enough along in its destruction that the event handler is not called. When you bind the event to the tree, then it still processes the event because it hasn't entered the destroy phase yet, it's just removing items.

BTW this sending of selection events upon destruction behavior is an implementation detail of the Windows native tree, it doesn't happen this way using the generic tree widget on the other platforms. So the best thing to do is just return immediately from your event handler if the tree's parent is in the process of being destroyed. You can use the IsBeingDeleted() method to know when to ignore the event.

I have analyzed the termination process of the TreeCtrl with the Bind in
the derived TreeCtrl and discovered another irregularity. You said that
when the program is terminated the items are deleted one by one. When I
save all TreeItems that I have create in a list and calls item.IsOk()
for every item in the list in the OnSelectionChange event, the function
is true for all items even the ones, that should be deleted.

wx.TreeItemId.IsOk doesn't consult the tree when it is called, it only checks if it has a valid ID value. IOW, it checks if the item was valid when the item object was created. It is done this way because the item objects are usually just intended to be a transitory blackbox handle to the real items in the tree. Item validity is used to indicate success or failure of the API call that returns the item. After that it has no meaning.

ยทยทยท

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

Hi Robin,
thank you for your answer.

Robin Dunn wrote:

> You can use the IsBeingDeleted() method to know when to ignore the
> event.wx.TreeItem

That worked partly. When I call self.Parent.IsBeingDeleted() then it
works, but when I call self.IsBeingDeleted(), the function is still
False while this obscure events are progressed.

I have modified my test script, now printing
self.Parent.IsBeingDeleted(), self.IsBeingDeleted(),
"OnSelectionChanged" in the OnSelectionChanged event-binder
<test_tree_inh_isdeleted.py><output>
False False OnSelectionChanged
True False OnSelectionChanged
True False OnSelectionChanged
True False OnSelectionChanged
True False OnSelectionChanged
True False OnSelectionChanged
True False OnSelectionChanged
True False OnSelectionChanged
True False OnSelectionChanged
True False OnSelectionChanged

Should not self.IsBeingDeleted() be True while terminating?

Christian

<test_tree_inh_isdeleted.py>
#!/usr/bin/env python
# -*- coding: utf-8

import wx

class MyTreeCtrl(wx.TreeCtrl):
def __init__(self, *args, **kwargs):
wx.TreeCtrl.__init__(self, *args, **kwargs)

self.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnTreeSelChanged, self)

self.insertTreeItems()

def insertTreeItems(self):
root = self.AddRoot('')
for i in xrange(10):
item = self.AppendItem(root, 'Item %d' % i)

def OnTreeSelChanged(self, event):
print self.Parent.IsBeingDeleted(),
print self.IsBeingDeleted(),
print "OnSelectionChanged"

class MyFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, -1, "MyFrame")

self._tree = MyTreeCtrl(self, style=wx.TR_DEFAULT_STYLE|wx.TR_HIDE_ROOT)

if __name__ == "__main__":
app = wx.PySimpleApp()
frame = MyFrame()
frame.Show()
app.MainLoop()