Strange traceback

Hello,

I get tracebacks such as this one:

Traceback (most recent call last):
   File "./Plugins\canvas.py", line 204, in OnTreeISelChanged
     node = self.tree.GetPyData(item)
   File "D:\Python24\Lib\site-packages\wx-2.7.0-msw-unicode\wx\_controls.py", lin
e 5237, in GetItemPyData
     return _controls_.TreeCtrl_GetItemPyData(*args, **kwargs)
wx._core.PyAssertionError: C++ assertion "param" failed at ..\..\src\msw\treectr
l.cpp(1062) in wxTreeCtrl::SetItemData(): failed to change tree items data

It happens on this simple code:

     def OnTreeISelChanged(self, evt):
         item = evt.GetItem()
         node = self.tree.GetPyData(item)

Any idea why this happens? Why is the traceback referring to SetItemData, I am just calling *Get*PyData?

Nitro wrote:

Hello,

I get tracebacks such as this one:

Traceback (most recent call last):
  File "./Plugins\canvas.py", line 204, in OnTreeISelChanged
    node = self.tree.GetPyData(item)
  File "D:\Python24\Lib\site-packages\wx-2.7.0-msw-unicode\wx\_controls.py", lin
e 5237, in GetItemPyData
    return _controls_.TreeCtrl_GetItemPyData(*args, **kwargs)
wx._core.PyAssertionError: C++ assertion "param" failed at ..\..\src\msw\treectr
l.cpp(1062) in wxTreeCtrl::SetItemData(): failed to change tree items data

It happens on this simple code:

    def OnTreeISelChanged(self, evt):
        item = evt.GetItem()
        node = self.tree.GetPyData(item)

Any idea why this happens? Why is the traceback referring to SetItemData, I am just calling *Get*PyData?

         PyObject* GetItemPyData(const wxTreeItemId& item) {
             wxPyTreeItemData* data = (wxPyTreeItemData*)self->GetItemData(item);
             if (data == NULL) {
                 data = new wxPyTreeItemData();
                 data->SetId(item); // set the id
                 self->SetItemData(item, data);
             }
             return data->GetData();
         }

If the item has no data then a new wxPyTreeItemData object is created and assigned to the item, using None for the Python object to store a reference to.

···

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

Hmm, the error is still a bit mysterious to me. I call SetPyData on every item I insert into the tree. Interestingly enough, the assertion does not show when I select items via the keyboard. It only happens when I select an item with the left mouse button.
I inserted some code like this here:

     def Init(self):
         self.tree.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnTreeISelChanged)

     def OnTreeISelChanged(self, evt):
         item = evt.GetItem()
         print 'get ', item, item.m_pItem, item.IsOk()

if I select the item with the keyboard, m_pItem has some value and IsOk() also returns True. If I select the very same item via a mouse click the assertion is given. So it seems to be that item returned by evt.GetItem() is not valid if selected with the mouse.
If I bind EVT_TREE_ITEM_RIGHT_CLICK, then evt.GetItem() also returns the correct value.
I don't bind any other events like ON_LEFT_DOWN or something like that.
The tree ctrl is created via XRC and the panel it is on, is also loaded via XRC with code like this:

     def __init__( self ):
         p = wx.PrePanel()
         # the Create step is done by XRC.
         self.PostCreate(p)

That shouldn't create any problem I hope.

I am on Win XP SP2 and I am still clueless why this happens.

I could reproduce the bug with the TreeCtrl demo, at the end of the mail is the code.

-Matthias

import string
import wx
import images

···

Am 18.10.2006, 23:47 Uhr, schrieb Robin Dunn <robin@alldunn.com>:

Nitro wrote:

Hello,
I get tracebacks such as this one:
Traceback (most recent call last):
  File "./Plugins\canvas.py", line 204, in OnTreeISelChanged
    node = self.tree.GetPyData(item)
  File "D:\Python24\Lib\site-packages\wx-2.7.0-msw-unicode\wx\_controls.py", lin
e 5237, in GetItemPyData
    return _controls_.TreeCtrl_GetItemPyData(*args, **kwargs)
wx._core.PyAssertionError: C++ assertion "param" failed at ..\..\src\msw\treectr
l.cpp(1062) in wxTreeCtrl::SetItemData(): failed to change tree items data
It happens on this simple code:
     def OnTreeISelChanged(self, evt):
        item = evt.GetItem()
        node = self.tree.GetPyData(item)
Any idea why this happens? Why is the traceback referring to SetItemData, I am just calling *Get*PyData?

         PyObject* GetItemPyData(const wxTreeItemId& item) {
             wxPyTreeItemData* data = (wxPyTreeItemData*)self->GetItemData(item);
             if (data == NULL) {
                 data = new wxPyTreeItemData();
                 data->SetId(item); // set the id
                 self->SetItemData(item, data);
             }
             return data->GetData();
         }

If the item has no data then a new wxPyTreeItemData object is created and assigned to the item, using None for the Python object to store a reference to.

#---------------------------------------------------------------------------

class MyTreeCtrl(wx.TreeCtrl):
     def __init__(self, parent, id, pos, size, style, log):
         wx.TreeCtrl.__init__(self, parent, id, pos, size, style)
         self.log = log

     def OnCompareItems(self, item1, item2):
         t1 = self.GetItemText(item1)
         t2 = self.GetItemText(item2)
         #self.log.WriteText('compare: ' + t1 + ' <> ' + t2 + '\n')
         if t1 < t2: return -1
         if t1 == t2: return 0
         return 1

#---------------------------------------------------------------------------

class TestTreeCtrlPanel(wx.Panel):
     def __init__(self, parent, log):
         # Use the WANTS_CHARS style so the panel doesn't eat the Return key.
         wx.Panel.__init__(self, parent, -1, style=wx.WANTS_CHARS)
         self.Bind(wx.EVT_SIZE, self.OnSize)

         self.log = log
         tID = wx.NewId()

         self.tree = MyTreeCtrl(self, tID, wx.DefaultPosition, wx.DefaultSize,
                                wx.TR_HAS_BUTTONS
                                > wx.TR_EDIT_LABELS
                                > wx.TR_MULTIPLE
                                #| wx.TR_HIDE_ROOT
                                , self.log)

         isz = (16,16)
         il = wx.ImageList(isz[0], isz[1])
         fldridx = il.Add(wx.ArtProvider_GetBitmap(wx.ART_FOLDER, wx.ART_OTHER, isz))
         fldropenidx = il.Add(wx.ArtProvider_GetBitmap(wx.ART_FILE_OPEN, wx.ART_OTHER, isz))
         fileidx = il.Add(wx.ArtProvider_GetBitmap(wx.ART_NORMAL_FILE, wx.ART_OTHER, isz))
         smileidx = il.Add(images.getSmilesBitmap())

         self.tree.SetImageList(il)
         self.il = il

         # NOTE: For some reason tree items have to have a data object in
         # order to be sorted. Since our compare just uses the labels
         # we don't need any real data, so we'll just use None below for
         # the item data.

         class X(object):
             def __init__(self):
                 self.f = 7
         someClass = X()

         self.root = self.tree.AddRoot("The Root Item")
         self.tree.SetPyData(self.root, 'root')
         self.tree.SetItemImage(self.root, fldridx, wx.TreeItemIcon_Normal)
         self.tree.SetItemImage(self.root, fldropenidx, wx.TreeItemIcon_Expanded)

         for x in range(15):
             someClass = X()

             child = self.tree.AppendItem(self.root, "Item %d" % x)
             self.tree.SetPyData(child, someClass)
             self.tree.SetItemImage(child, fldridx, wx.TreeItemIcon_Normal)
             self.tree.SetItemImage(child, fldropenidx, wx.TreeItemIcon_Expanded)

             for y in range(5):
                 last = self.tree.AppendItem(child, "item %d-%s" % (x, chr(ord("a")+y)))
                 self.tree.SetPyData(last, None)
                 self.tree.SetItemImage(last, fldridx, wx.TreeItemIcon_Normal)
                 self.tree.SetItemImage(last, fldropenidx, wx.TreeItemIcon_Expanded)

                 for z in range(5):
                     item = self.tree.AppendItem(last, "item %d-%s-%d" % (x, chr(ord("a")+y), z))
                     self.tree.SetPyData(item, None)
                     self.tree.SetItemImage(item, fileidx, wx.TreeItemIcon_Normal)
                     self.tree.SetItemImage(item, smileidx, wx.TreeItemIcon_Selected)

         self.tree.Expand(self.root)
         #self.Bind(wx.EVT_TREE_ITEM_EXPANDED, self.OnItemExpanded, self.tree)
         #self.Bind(wx.EVT_TREE_ITEM_COLLAPSED, self.OnItemCollapsed, self.tree)
         self.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnSelChanged, self.tree)
         #self.Bind(wx.EVT_TREE_BEGIN_LABEL_EDIT, self.OnBeginEdit, self.tree)
         #self.Bind(wx.EVT_TREE_END_LABEL_EDIT, self.OnEndEdit, self.tree)
         #self.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.OnActivate, self.tree)

         #self.tree.Bind(wx.EVT_LEFT_DCLICK, self.OnLeftDClick)
         #self.tree.Bind(wx.EVT_RIGHT_DOWN, self.OnRightDown)
         #self.tree.Bind(wx.EVT_RIGHT_UP, self.OnRightUp)

     def OnRightDown(self, event):
         pt = event.GetPosition();
         item, flags = self.tree.HitTest(pt)
         if item:
             self.log.WriteText("OnRightClick: %s, %s, %s\n" %
                                (self.tree.GetItemText(item), type(item), item.__class__))
             self.tree.SelectItem(item)

     def OnRightUp(self, event):
         pt = event.GetPosition();
         item, flags = self.tree.HitTest(pt)
         if item:
             self.log.WriteText("OnRightUp: %s (manually starting label edit)\n"
                                % self.tree.GetItemText(item))
             self.tree.EditLabel(item)

     def OnBeginEdit(self, event):
         self.log.WriteText("OnBeginEdit\n")
         # show how to prevent edit...
         item = event.GetItem()
         if item and self.tree.GetItemText(item) == "The Root Item":
             wx.Bell()
             self.log.WriteText("You can't edit this one...\n")

             # Lets just see what's visible of its children
             cookie = 0
             root = event.GetItem()
             (child, cookie) = self.tree.GetFirstChild(root)

             while child.IsOk():
                 self.log.WriteText("Child [%s] visible = %d" %
                                    (self.tree.GetItemText(child),
                                     self.tree.IsVisible(child)))
                 (child, cookie) = self.tree.GetNextChild(root, cookie)

             event.Veto()

     def OnEndEdit(self, event):
         self.log.WriteText("OnEndEdit: %s %s\n" %
                            (event.IsEditCancelled(), event.GetLabel()) )
         # show how to reject edit, we'll not allow any digits
         for x in event.GetLabel():
             if x in string.digits:
                 self.log.WriteText("You can't enter digits...\n")
                 event.Veto()
                 return

     def OnLeftDClick(self, event):
         pt = event.GetPosition();
         item, flags = self.tree.HitTest(pt)
         if item:
             self.log.WriteText("OnLeftDClick: %s\n" % self.tree.GetItemText(item))
             parent = self.tree.GetItemParent(item)
             if parent.IsOk():
                 self.tree.SortChildren(parent)
         event.Skip()

     def OnSize(self, event):
         w,h = self.GetClientSizeTuple()
         self.tree.SetDimensions(0, 0, w, h)

     def OnItemExpanded(self, event):
         item = event.GetItem()
         if item:
             self.log.WriteText("OnItemExpanded: %s\n" % self.tree.GetItemText(item))

     def OnItemCollapsed(self, event):
         item = event.GetItem()
         if item:
             self.log.WriteText("OnItemCollapsed: %s\n" % self.tree.GetItemText(item))

     def OnSelChanged(self, event):
         try:
             item = self.item = event.GetItem()
             self.log.WriteText('XXX' + str(item.IsOk()) )
             self.log.WriteText( str(self.tree.GetPyData(item)) )
             self.log.WriteText('YYY')
         except:
             import traceback
             self.log.WriteText(traceback.format_exc())

         if self.item:
             self.log.WriteText("OnSelChanged: %s\n" % self.tree.GetItemText(self.item))
             if wx.Platform == '__WXMSW__':
                 self.log.WriteText("BoundingRect: %s\n" %
                                    self.tree.GetBoundingRect(self.item, True))
             #items = self.tree.GetSelections()
             #print map(self.tree.GetItemText, items)
         event.Skip()

     def OnActivate(self, event):
         if self.item:
             self.log.WriteText("OnActivate: %s\n" % self.tree.GetItemText(self.item))

#---------------------------------------------------------------------------

def runTest(frame, nb, log):
     win = TestTreeCtrlPanel(nb, log)
     return win

#---------------------------------------------------------------------------

overview = """\
A TreeCtrl presents information as a hierarchy, with items that may be
expanded to show further items. Items in a tree control are referenced by
wx.TreeItemId handles.

"""

if __name__ == '__main__':
     import sys,os
     import run
     run.main(['', os.path.basename(sys.argv[0])] + sys.argv[1:])

Nitro wrote:

···

Am 18.10.2006, 23:47 Uhr, schrieb Robin Dunn <robin@alldunn.com>:

Nitro wrote:

Hello,
I get tracebacks such as this one:
Traceback (most recent call last):
  File "./Plugins\canvas.py", line 204, in OnTreeISelChanged
    node = self.tree.GetPyData(item)
  File "D:\Python24\Lib\site-packages\wx-2.7.0-msw-unicode\wx\_controls.py", lin
e 5237, in GetItemPyData
    return _controls_.TreeCtrl_GetItemPyData(*args, **kwargs)
wx._core.PyAssertionError: C++ assertion "param" failed at ..\..\src\msw\treectr
l.cpp(1062) in wxTreeCtrl::SetItemData(): failed to change tree items data
It happens on this simple code:
     def OnTreeISelChanged(self, evt):
        item = evt.GetItem()
        node = self.tree.GetPyData(item)
Any idea why this happens? Why is the traceback referring to SetItemData, I am just calling *Get*PyData?

         PyObject* GetItemPyData(const wxTreeItemId& item) {
             wxPyTreeItemData* data = (wxPyTreeItemData*)self->GetItemData(item);
             if (data == NULL) {
                 data = new wxPyTreeItemData();
                 data->SetId(item); // set the id
                 self->SetItemData(item, data);
             }
             return data->GetData();
         }

If the item has no data then a new wxPyTreeItemData object is created and assigned to the item, using None for the Python object to store a reference to.

Hmm, the error is still a bit mysterious to me. I call SetPyData on every item I insert into the tree. Interestingly enough, the assertion does not show when I select items via the keyboard. It only happens when I select an item with the left mouse button.

Windows can sometimes send some bogus selection events to the tree, you need to always check the validity of the item in the event handler before using it.

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

Ahh, thank you for this explanation. Windows sure has its caveats.

-Matthias

···

Am 23.10.2006, 10:06 Uhr, schrieb Robin Dunn <robin@alldunn.com>:

Nitro wrote:

Am 18.10.2006, 23:47 Uhr, schrieb Robin Dunn <robin@alldunn.com>:

Nitro wrote:

Hello,
I get tracebacks such as this one:
Traceback (most recent call last):
  File "./Plugins\canvas.py", line 204, in OnTreeISelChanged
    node = self.tree.GetPyData(item)
  File "D:\Python24\Lib\site-packages\wx-2.7.0-msw-unicode\wx\_controls.py", lin
e 5237, in GetItemPyData
    return _controls_.TreeCtrl_GetItemPyData(*args, **kwargs)
wx._core.PyAssertionError: C++ assertion "param" failed at ..\..\src\msw\treectr
l.cpp(1062) in wxTreeCtrl::SetItemData(): failed to change tree items data
It happens on this simple code:
     def OnTreeISelChanged(self, evt):
        item = evt.GetItem()
        node = self.tree.GetPyData(item)
Any idea why this happens? Why is the traceback referring to SetItemData, I am just calling *Get*PyData?

         PyObject* GetItemPyData(const wxTreeItemId& item) {
             wxPyTreeItemData* data = (wxPyTreeItemData*)self->GetItemData(item);
             if (data == NULL) {
                 data = new wxPyTreeItemData();
                 data->SetId(item); // set the id
                 self->SetItemData(item, data);
             }
             return data->GetData();
         }

If the item has no data then a new wxPyTreeItemData object is created and assigned to the item, using None for the Python object to store a reference to.

Hmm, the error is still a bit mysterious to me. I call SetPyData on every item I insert into the tree. Interestingly enough, the assertion does not show when I select items via the keyboard. It only happens when I select an item with the left mouse button.

Windows can sometimes send some bogus selection events to the tree, you need to always check the validity of the item in the event handler before using it.