Errors in wxPython app using Grid/ GridCellAttr

Hi, I’m developing application using wxPython (version 4.1.1), which uses grids quite intensively (several hours in a day in a row).

The user recently reported about two cases of application crashing after working for almost whole day, and I suspect, that it somehow related to using GridCellAttributes.

The errors, which are get written to the console before crash looks like follows:

SystemError: <class ‘wx._grid.GridCellAttr.AttrKind’> returned a result with an error set
wx._core.wxAssertionError: C++ assertion “m_hdc” failed at …\src\msw\textmeasure.cpp(61) in wxTextMeasure::BeginMeasuring(): Must not be used with non-native wxDCs

and as well the following one:

wx._core.wxAssertionError: C++ assertion “m_buffer && m_buffer->IsOk()” failed at …\src\common\dcbufcmn.cpp(134) in wxBufferedDC::UnMask(): invalid backing store

I’m not able to reproduce them on my PC, but I use application not so intensively, as the user.

The code in the app for working with grid is based on earlier version of VirtualTable/ MegaTable example.

As well, the issue may be related to https://github.com/wxWidgets/Phoenix/issues/1766

I will be grateful for help/tips, what could be causing such a behaviour.

Many Thanks in Advance

Serhiy Yevtushenko

It’s very easy to get GridCellAtrributes wrong due to reference counting etc. It took me some time to get it bug free. I found the example code from the demo a bit too simple. IIRC it permanently stores GridCellAttr instances while in real world you often create them on the fly.

If nobody sees the same behaviour, it would be good if you could post a stripped down version of your code for review.

I’m pasting my own version of GridTable.GetAttr which keeps a reference of the last two returned GridCellAttr instances. First I tried with IncRef, but that did not work. I have not tested my code with wxPython >4.1.0 yet as this has a problem with gridmovers.

    def GetAttr(self, row, col, kind):
        if self.table is None: return None
        try:
            cell = self.table[row,col]
        except IndexError:
            return None

        attr = grid.GridCellAttr()
        self._attrs.append(attr)
        if len(self._attrs)>2:
            a = self._attrs.pop(0)
        #attr.IncRef()

        if cell is None or cell.read_only and cell.bgcolor is None:
            if (row%2):
                bgcolor = "lightlightgrey"
            else:
                bgcolor = "lightgrey"
        else:
            bgcolor = cell.bgcolor

        if not bgcolor is None:
            if isinstance(bgcolor, ReportTable.Color):
                attr.SetBackgroundColour( wx.Colour(bgcolor.r*255.0, bgcolor.g*255.0, bgcolor.b*255.0) )
            else:
                attr.SetBackgroundColour(colors[bgcolor])

        if cell is None: return attr
        
        if not cell.color is None:
            if isinstance(cell.color , ReportTable.Color):
                attr.SetTextColour( wx.Colour(cell.color.r*255.0, cell.color.g*255.0, cell.color.b*255.0) )
            else:
                attr.SetTextColour(colors[cell.color])

        if cell.bold or cell.light or cell.underline:
            if cell.bold:
                weight = wx.FONTWEIGHT_BOLD
            elif cell.light:
                weight = wx.FONTWEIGHT_LIGHT
            else:
                weight = wx.FONTWEIGHT_NORMAL
            attr.SetFont( self._get_font(weight, cell.underline) )
        if cell.alignment is not None:
            alignment = cell.alignment
        else:
            alignment = self.table.get_column_alignment(col)
        attr.SetAlignment(self._alignment[alignment], wx.ALIGN_TOP)

        return attr

Hi, Dietmar.

Thank a lot for getting back.

My simplified code for handling cell attributes looks like follows (Was initially written for wxPython 3, and was recently adopted to wxPython 4, and has not shown any problems before)

GRID_COLOR_LIGHT_BLUE = "Light Blue"
GRID_COLOR_WHITE = "white"

class MyGridTable(wx.grid.GridTableBase):

    def __init__(
        self,
    ) -> None:

        wx.grid.GridTableBase.__init__(self)
        self.create_cell_attributes()

    def create_cell_attributes(self) -> None:
        self.even_row_style = make_grid_cell_attribute(GRID_COLOR_WHITE)
        self.even_row_odd_col_style = make_grid_cell_attribute(wx.Colour(192, 200, 216, 255))
        self.odd_row_style = make_grid_cell_attribute(GRID_COLOR_LIGHT_BLUE)
        self.odd_row_odd_col_style = make_grid_cell_attribute(wx.Colour(216, 216, 216, 255))
        self.attributes = (
            (self.even_row_style, self.even_row_odd_col_style),
            (self.odd_row_style, self.odd_row_odd_col_style),
        )
        

    def GetAttr(self, row: int, col: int, _kind: Any) -> wx.grid.GridCellAttr:
        attr_selector = self.attributes
        attr = attr_selector[row % 2][col % 2]
        attr.IncRef()
        return attr

def make_grid_cell_attribute(color: wx.Colour) -> wx.grid.GridCellAttr:
    result = wx.grid.GridCellAttr()
    result.SetBackgroundColour(color)
    result.SetFont(wx.Font(10, wx.SWISS, wx.NORMAL, wx.FONTWEIGHT_BOLD))
    return result

I should mention that the application was with several grids, and some of them having more then 100_000 (one hundred thousand) rows.

Would be grateful for suggestions, what could be an issue with the code.

Yes, that’s indeed more or less the same code that the demo is using. So, reference counting and references should not be the root cause, but someting inside wx/Python.

I got today one more crash report.

Below is the curated extract from it.

Traceback (most recent call last):
File “…\lib\site-packages\wx\lib\agw\scrolledthumbnail.py”, line 1723, in OnPaint
self.DrawThumbnail(thmb, self._items[ii], ii)
File “…\lib\site-packages\wx\lib\agw\scrolledthumbnail.py”, line 1597, in DrawThumbnail
dc.DrawBitmap(img, imgRect.x, imgRect.y, True)
wx._core.wxAssertionError: C++ assertion ““bmp.IsOk()”” failed at …\src\msw\dc.cpp(1312) in wxMSWDCImpl::DoDrawBitmap(): invalid bitmap in wxMSWDCImpl::DrawBitmap
Traceback (most recent call last):
File “…\lib\site-packages\wx_utils\wx_utils.py”, line 192, in GetColLabelValue
return self.data_frame.columns[self._get_dataframe_column(col)]
wx._core.wxAssertionError: C++ assertion “m_hdc” failed at …\src\msw\textmeasure.cpp(61) in wxTextMeasure::BeginMeasuring(): Must not be used with non-native wxDCs
wx._core.wxAssertionError: C++ assertion “m_hdc” failed at …\src\msw\textmeasure.cpp(61) in wxTextMeasure::BeginMeasuring(): Must not be used with non-native wxDCs

The above exception was the direct cause of the following exception:

SystemError: <class ‘wx._grid.GridCellAttr.AttrKind’> returned a result with an error set

In this case, first exception is happening in the scrolledthumbnail.py (it was not the case in one of previous crashes).

Is there any way to enable some extended logging in wxPython, so, one would be able to directly related exceptions in python and C++ code?

Thanks in Advance

Serhiy