Python Forum
[WxPython] Why does an error occur after editing a grid cell?
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
[WxPython] Why does an error occur after editing a grid cell?
#1
There is a dialogue on which there is a panel on which there is a grid.

Each grid column is assigned its own editor (in a class derived from GridTableBase)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
class StatePanel(wx.Panel):
 
    def __init__(self, parent, states):
        super().__init__(parent)
 
        topsizer = wx.FlexGridSizer(rows=1, cols=2, vgap=10, hgap=10)
 
        self.grid = CustomGrid(self, size=(-1, 100))
        self.grid.Bind(wx.grid.EVT_GRID_CELL_CHANGED, self.OnGridCellChange)
        topsizer.Add(self.grid, proportion=1, flag=wx.EXPAND)
 
        self.SetSizer(topsizer)
 
        defaultValue = _StateInfos(0, '0', 'Состояние 0', YELLOW)
 
        self.states = states
        if not self.states:
            self.states.append(defaultValue.copy())
 
        self.table = StateTable(self.states, GetStateTableColnames())
    self.RefreshStates()
 
def RefreshStates(self):
    data = []
    for num, state in enumerate(self.states):
        state.Number = num + 1
        data.append(state)
    self.table.SetData(data)
    self.table.ResetView(self.grid)
 
def OnGridCellChange(self, event):
    row, col = event.GetRow(), event.GetCol()
    colname = self.table.GetColLabelValue(col, False)
    value = self.table.GetValue(row, col)
    message = None
 
    if colname == 'Code':
        if not value:
            message = 'Значение кода не может быть пустым.'
        else:
            for r in range(self.table.GetNumberRows()):
                if r != row:
                    code = self.table.GetValueByName(r, 'Code')
                    if value == code:
                        message = f'Код {value} уже используется.'
                        break
 
    if message is not None:
        wx.CallAfter(showErrorMessage, message)
        event.Veto()
    else:
        event.Skip()
In self.table.ResetView, the StateTable.ResetView method is called

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
class StateTable(wx.grid.GridTableBase):   
    ...   
 
    def ResetView(self, grid):
        """
        (wx.grid.Grid) -> Reset the grid view.   Call this to
        update the grid if rows and columns have been added or deleted
        """
        grid.CloseEditControl()
        grid.BeginBatch()
        for current, new, delmsg, addmsg in [
            (
                self._rows,
                self.GetNumberRows(),
                wx.grid.GRIDTABLE_NOTIFY_ROWS_DELETED,
                wx.grid.GRIDTABLE_NOTIFY_ROWS_APPENDED
            ),
            (
                self._cols,
                self.GetNumberCols(),
                wx.grid.GRIDTABLE_NOTIFY_COLS_DELETED,
                wx.grid.GRIDTABLE_NOTIFY_COLS_APPENDED
            )
        ]:
            if new < current:
                msg = wx.grid.GridTableMessage(self, delmsg, new, current-new)
                grid.ProcessTableMessage(msg)
            elif new > current:
                msg = wx.grid.GridTableMessage(self, addmsg, new-current)
                grid.ProcessTableMessage(msg)
                self.UpdateValues(grid)
        grid.EndBatch()
 
        self._rows = self.GetNumberRows()
        self._cols = self.GetNumberCols()
        # update the column rendering scheme
        self._updateColAttrs(grid)
 
        # update the scrollbars and the displayed part of the grid
        grid.AdjustScrollbars()
        grid.ForceRefresh()
 
    def _updateColAttrs(self, grid):
        for row in range(self.GetNumberRows()):
            for col in range(self.GetNumberCols()):
                editor = None
                renderer = None
                colname = self.GetColLabelValue(col, False)
 
                if col != 0:
                    grid.SetReadOnly(row, col, False)
 
                    if colname == 'Code':
                        editor = wx.grid.GridCellNumberEditor()
                        renderer = wx.grid.GridCellNumberRenderer()
 
                    elif colname == 'Text':
                        editor = wx.grid.GridCellTextEditor()
                        renderer = wx.grid.GridCellStringRenderer()
 
                    elif colname == 'Text colour':
                        editor = GridCellColourEditor()
                        renderer = GridCellColourRenderer()
 
                else:
                    grid.SetReadOnly(row, col, True)
 
                grid.SetCellEditor(row, col, editor)
                grid.SetCellRenderer(row, col, renderer)
If I edit any cell and close the dialog, an error occurs

Error:
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 The above exception was the direct cause of the following exception: Traceback (most recent call last): File "C:\Python37-32\lib\site-packages\wx\lib\agw\customtreectrl.py", line 8183, in OnInternalIdle if not self.HasAGWFlag(TR_MULTIPLE) and not self.GetSelection(): File "C:\Python37-32\lib\site-packages\wx\lib\agw\customtreectrl.py", line 3534, in HasAGWFlag return bool(self._agwStyle & flag) SystemError: <class 'bool'> returned a result with an error set
I saw the topic: https://github.com/wxWidgets/Phoenix/issues/627

I did everything as suggested in the solution, but this does not solve the problem.

The problem is solved only if in the OnGridCellChange method at the end call self.table._updateColAttrs (self.grid).

Why?
Reply
#2
It solved my problem

Table:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class StateTable(CustomTable):
 
    ...
 
    def _updateColAttrs(self, grid):
        for row in range(self.GetNumberRows()):
            for col in range(self.GetNumberCols()):
                editor = None
                renderer = None
                colname = self.GetColLabelValue(col, False)
 
                if col != 0:
                    grid.SetReadOnly(row, col, False)
 
                    if colname == 'Code':
                        editor = wx.grid.GridCellNumberEditor()
                        renderer = wx.grid.GridCellNumberRenderer()
                    ...
 
                else:
                    grid.SetReadOnly(row, col, True)
 
                # !!!!!! DECISION !!!!!!
                currentEditor = grid.GetCellEditor(row, col)
                currentEditor.DecRef()
 
                grid.SetCellEditor(row, col, editor)
                grid.SetCellRenderer(row, col, renderer)
            self.ResizeRow(grid, row)
Grid:

1
2
3
4
5
6
7
8
9
10
11
12
class StatePanel(wx.Panel):
 
    def __init__(self, parent, states):
        ...
 
        self.grid = CustomGrid(self, size=(-1, 120))
        self.grid.Bind(wx.EVT_WINDOW_DESTROY, self.OnGridDestroy)
        ...
 
    def OnGridDestroy(self, event):
        self.table._updateColAttrs(self.grid)
        event.Skip()
Reply
#3
Thanks for sharing answer so others with same problem can benefit.
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  [WxPython] Grid. How to close the cell editor? ioprst 1 3,266 Dec-03-2019, 09:28 AM
Last Post: ioprst
  Get value of wx grid cell ian 1 9,683 Jul-15-2017, 03:04 AM
Last Post: Larz60+

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020