Bug/Error with slight change to GridHugeTable.py

When I edit GridHugeTable.py (from demos 4.0.1) to be patched like this:

 class HugeTable(gridlib.GridTableBase):
 
     def __init__(self, log):
         gridlib.GridTableBase.__init__(self)
         self.log = log
 
         self.odd=gridlib.GridCellAttr()
         self.odd.SetBackgroundColour("sky blue")
         self.even=gridlib.GridCellAttr()
         self.even.SetBackgroundColour("sea green")
+        self.filters = gridlib.GridCellAttr()
+        self.filters.SetBackgroundColour("sea green")
+        self.filter_editor = gridlib.GridCellChoiceEditor(['FILTER data', 'WHERE=', 'CONTAINS', '???'], True)
+        self.filters.SetEditor(self.filter_editor)
 
     def GetAttr(self, row, col, kind):
-        attr = [self.even, self.odd][row % 2]
+        if row == 0 :
+            attr = self.filters
+        else:
+            attr = [self.even, self.odd][row % 2]
         attr.IncRef()
         return attr

 
     def GetValue(self, row, col):
+        if row==0:
+            return self.filter_editor.GetValue() if self.filter_editor.IsCreated() else 'FILTER data'
         return str( (row, col) )

and run (with wxPython 4.0.1) and click the first row’s drop-down, then close the Frame… I get this weird error:

Traceback (most recent call last):
  File "Downloads\wxPython-demo-4.0.1\demo\GridHugeTable_Error.py", line 87, in <module>
    app.MainLoop()
  File "C:\Python36\lib\site-packages\wx\core.py", line 2096, in MainLoop
    rv = wx.PyApp.MainLoop(self)
wx._core.wxAssertionError: C++ assertion "GetEventHandler() == this" failed at ..\..\src\common\wincmn.cpp(478) in wxWindowBase::~wxWindowBase(): any pushed event handlers must have been removed

whole source code:

import wx
import wx.grid as gridlib

class HugeTable(gridlib.GridTableBase):

    def __init__(self, log):
        gridlib.GridTableBase.__init__(self)
        self.log = log

        self.odd=gridlib.GridCellAttr()
        self.odd.SetBackgroundColour("sky blue")
        self.even=gridlib.GridCellAttr()
        self.even.SetBackgroundColour("sea green")
        self.filters = gridlib.GridCellAttr()
        self.filters.SetBackgroundColour("sea green")
        self.filter_editor = gridlib.GridCellChoiceEditor(['FILTER data', 'WHERE=', 'CONTAINS', '???'], True)
        self.filters.SetEditor(self.filter_editor)

    def GetAttr(self, row, col, kind):
        if row == 0 :
            attr = self.filters
        else:
            attr = [self.even, self.odd][row % 2]
        attr.IncRef()
        return attr

    # This is all it takes to make a custom data table to plug into a
    # wxGrid.  There are many more methods that can be overridden, but
    # the ones shown below are the required ones.  This table simply
    # provides strings containing the row and column values.

    def GetNumberRows(self):
        return 10000

    def GetNumberCols(self):
        return 10000

    def IsEmptyCell(self, row, col):
        return False

    def GetValue(self, row, col):
        if row==0:
            return self.filter_editor.GetValue() if self.filter_editor.IsCreated() else 'FILTER data'
        return str( (row, col) )

    def SetValue(self, row, col, value):
        self.log.write('SetValue(%d, %d, "%s") ignored.\n' % (row, col, value))


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

class HugeTableGrid(gridlib.Grid):
    def __init__(self, parent, log):
        gridlib.Grid.__init__(self, parent, -1)

        table = HugeTable(log)

        # The second parameter means that the grid is to take
        # ownership of the table and will destroy it when done.
        # Otherwise you would need to keep a reference to it, but that
        # would allow other grids to use the same table.
        self.SetTable(table, True)

        self.Bind(gridlib.EVT_GRID_CELL_RIGHT_CLICK, self.OnRightDown)

    def OnRightDown(self, event):
        print("hello")
        print(self.GetSelectedRows())


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

class TestFrame(wx.Frame):
    def __init__(self, parent, log):
        wx.Frame.__init__(self, parent, -1, "Huge (virtual) Table Demo", size=(640,480))
        grid = HugeTableGrid(self, log)

        grid.SetReadOnly(5,5, True)

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

if __name__ == '__main__':
    import sys
    app = wx.App()
    frame = TestFrame(None, sys.stdout)
    frame.Show(True)
    app.MainLoop()

This happens when there is an incorrect refcount on the editor object, so it is not being properly disconnected from the grid before it is destroyed. There is more info about this here: HOWTO: Deal with reference counts in Grid classes and some additional discussion about it here: https://github.com/wxWidgets/Phoenix/issues/627 and probably other places you can find with Google.

So, in a nutshell, you need to call DecRef on the editor when the program is shutting down, before the grid is destroyed.

This is blowing my mind right now… it feels too much like C and I’m rejecting the reality. :open_mouth:
Hopefully it’s as easy as throwing a DecRef (or loop of them, since in my real case I have a cell editor for each column) in the OnClose handler for the Panel the grid is in.

Yep, the ref-counting in the Grid classes have been a thorn in my side since forever. It looks like there are some changes coming in 4.1 that may help, (fingers crossed!) but they will not make it in until sometime after 4.1.0.

Well, everything I tried got me not much further. The best I was able to do was that opening a single combobox dropdown and closing doesn’t crash… but when I open one after another, different dropdowns/columns, then it crashes. It appears that the refcount isn’t increasing automatically, even though I’m decreasing them in the OnClose method.

Patch and full file attached.

decref_attempt_hugegrid.py (7.1 KB) GridHugeTable_Error.py (6.2 KB)

Do I even need to worry about this error, since it’s on-close? I don’t have any other frames open, and won’t be creating and destroying this Panel within the same process. I’ve burned a full day on attempting to debug this, and I think I am burned out. If I can just leave it to crash on-close… I might be OK with that for now. (until a user says it’s doing something bad for them, anyway).

Tried getting this working for another few hours… and the main difference I noticed between the examples posted by Robin and the HugeGridTable is that he was dealing with non-virtual grid data… while the HugeGridTable is a “virtual” table. So where his examples/comments talk about SetAttr the virtual grid doesn’t have that, and only uses an overridden GetAttr. I tried things like using PushEventHandler on the ComboBox inside the Editor, IncRef DecRef in all sorts of combinations in one method or editor event… but no luck.