wxNotebook and DeleteAllPages()

Hello all,
I’m updating older code that was running on python 2.7 and wxpython 2.8.12 to now run on python 3.6 and wxPhoenix. I have run into the following error messages:

<class ‘wx._core.PaintEvent’> returned a result with an error set

wx._core.wxAssertionError: C++ assertion “(int)m_pages.Count() == (int)::SendMessageW((((HWND)GetHWND())), (0x1300 + 4), 0, 0L)” failed at …\src\msw\notebook.cpp(343) in wxNotebook::GetPageCount():

This occurs when I have an active wxNotebook (with any number of pages) where I try to create a new page, after having called the DeleteAllPages() method. Did the behavior of wxNotebook change somewhere between 2.8.12 and 4.0, or is this a bug in the wxNotebook?

PyMapper Developer wrote:

Hello all,
I’m updating older code that was running on python 2.7 and wxpython 2.8.12 to now run on python 3.6 and wxPhoenix. I have run into the following error messages:

<class
‘wx._core.PaintEvent’> returned a result with an error set

wx._core.wxAssertionError:
C++ assertion “(int)m_pages.Count() == (int)::SendMessageW((((HWND)GetHWND())), (0x1300 + 4), 0, 0L)” failed at
…\src\msw\notebook.cpp(343) in wxNotebook::GetPageCount():

This occurs when I have an active wxNotebook (with any number of pages) where I try to create a new page, after having called the DeleteAllPages() method. Did the behavior of wxNotebook change somewhere between 2.8.12 and 4.0, or is this a bug in the wxNotebook?

It’s hard to say for sure but it’s possible that there are some internal
values (in wx or the native libs) that are in an inconsistent state immediately after a DeleteAllPages and the notebook isn’t ready for new pages again until it has cleaned those up. If that’s the case then it may help to not add any new pages until after the pending events have been processed. Using wx.CallAfter can help you with that.

The fact that the error is detected while the in the wx.PainEvent code is a red flag for me too… It is possible that the context that you are
calling DeleteAllPages in that is the trouble. Do you call it from the EVT_PAINT handler? From another thread?

···


Robin Dunn

Software Craftsman

http://wxPython.org

I’m in the same thread, and not calling from EVT_PAINT. I tried wx.CallAfter, but it returned the same errors.

···

On Tuesday, April 25, 2017 at 8:45:24 PM UTC-6, Robin Dunn wrote:

PyMapper Developer wrote:

Hello all,
I’m updating older code that was running on python 2.7 and wxpython 2.8.12 to now run on python 3.6 and wxPhoenix. I have run into the following error messages:

<class
‘wx._core.PaintEvent’> returned a result with an error set

wx._core.wxAssertionError:
C++ assertion “(int)m_pages.Count() == (int)::SendMessageW((((HWND) GetHWND())), (0x1300 + 4), 0, 0L)” failed at
…\src\msw\notebook.cpp(343) in wxNotebook::GetPageCount():

This occurs when I have an active wxNotebook (with any number of pages) where I try to create a new page, after having called the DeleteAllPages() method. Did the behavior of wxNotebook change somewhere between 2.8.12 and 4.0, or is this a bug in the wxNotebook?

It’s hard to say for sure but it’s possible that there are some internal
values (in wx or the native libs) that are in an inconsistent state immediately after a DeleteAllPages and the notebook isn’t ready for new pages again until it has cleaned those up. If that’s the case then it may help to not add any new pages until after the pending events have been processed. Using wx.CallAfter can help you with that.

The fact that the error is detected while the in the wx.PainEvent code is a red flag for me too… It is possible that the context that you are
calling DeleteAllPages in that is the trouble. Do you call it from the EVT_PAINT handler? From another thread?


Robin Dunn

Software Craftsman

http://wxPython.org

Please create a small runnable sample that demonstrates the problem.

···


Robin Dunn

Software Craftsman

http://wxPython.org

Sample attached. This sample crashes when I call DeleteAllPages() on the button click.
Here is the code for reference:

import sys

import wx

notebook_test.py (1.11 KB)

···

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

class MainFrame(wx.Frame):

“”""""

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

def init(self):

“”“Constructor”""

wx.Frame.init(self, None, title=“Generic Notebook”, size=(800,600))

bSizer = wx.BoxSizer(wx.HORIZONTAL)

self.testNotebook = wx.Notebook(self)

nbPanel = wx.Window(self.testNotebook)

self.testNotebook.AddPage(nbPanel, “test1”)

self.testNotebook.AddPage(nbPanel, “test2”)

self.testNotebook.AddPage(nbPanel, “test3”)

deleteButton = wx.Button(self, label=“delete all then add page”)

deleteButton.Bind(wx.EVT_BUTTON, self.DeletePages)

bSizer.Add(deleteButton,0,0)

bSizer.Add(self.testNotebook,1,wx.EXPAND,10)

self.SetSizer(bSizer)

self.Show()

return

def DeletePages(self, event):

self.testNotebook.DeleteAllPages()

otherPanel = wx.Window(self.testNotebook)

self.testNotebook.AddPage(otherPanel, “NewPanel”)

return

if name == “main”:

app = wx.App(False)

frame = MainFrame()

app.MainLoop()

On Wednesday, April 26, 2017 at 9:57:29 AM UTC-6, Robin Dunn wrote:

On Wednesday, April 26, 2017 at 8:29:03 AM UTC-7, PyMapper Developer wrote:

I’m in the same thread, and not calling from EVT_PAINT. I tried wx.CallAfter, but it returned the same errors.

Please create a small runnable sample that demonstrates the problem.


Robin Dunn

Software Craftsman

http://wxPython.org

PyMapper Developer wrote:

Sample attached. This sample crashes when I call DeleteAllPages() on
the button click.
Here is the code for reference:

    self.testNotebook = wx.Notebook(self)
    nbPanel = wx.Window(self.testNotebook)
    self.testNotebook.AddPage(nbPanel, "test1")
    self.testNotebook.AddPage(nbPanel, "test2")
    self.testNotebook.AddPage(nbPanel, "test3")

This is the problem. You are adding the same page window as 3 different
notebook pages. When you do DeleteAllPages then it is trying to destroy
the window 3 times, which will cause invalid and possibly garbage memory
to be accessed the 2nd and 3rd times.

···

--
Robin Dunn
Software Craftsman
http://wxPython.org

Okay, that makes sense. So, in changing the code to add three new panels instead

nbPanel1 = wx.Window(self.testNotebook)
nbPanel2 = wx.Window(self.testNotebook)
nbPanel3= wx.Window(self.testNotebook)
self.testNotebook.AddPage(nbPanel1, “test1”)
self.testNotebook.AddPage(nbPanel2, “test2”)
self.testNotebook.AddPage(nbPanel3, “test3”)

I end up with this error instead once I try to do the DeleteAllPages()

wx._core.wxAssertionError: C++ assertion “(int)m_pages.Count() == (int)::SendMessageW((((HWND)GetHWND())), (0x1300 + 4), 0, 0L)” failed at …\src\msw\notebook.cpp(343) in wxNotebook::GetPageCount():

Visually, I see the Notebook in the frame get the new page right before it crashes.

···
self.testNotebook = wx.Notebook(self)
nbPanel1 = wx.Window(self.testNotebook) 
nbPanel2 = wx.Window(self.testNotebook) 
nbPanel3= wx.Window(self.testNotebook) 
self.testNotebook.AddPage(nbPanel1, "test1")
self.testNotebook.AddPage(nbPanel2, "test2")
self.testNotebook.AddPage(nbPanel3, "test3")

This is the problem. You are adding the same page window as 3 different

notebook pages. When you do DeleteAllPages then it is trying to destroy

the window 3 times, which will cause invalid and possibly garbage memory

to be accessed the 2nd and 3rd times.


Robin Dunn

Software Craftsman

http://wxPython.org

PyMapper Developer wrote:

Okay, that makes sense. So, in changing the code to add three new panels instead

nbPanel1 = wx.Window(self.testNotebook)
nbPanel2 = wx.Window(self.testNotebook)
nbPanel3= wx.Window(self.testNotebook)
self.testNotebook.AddPage( nbPanel1,
“test1”)
self.testNotebook.AddPage(nbPanel2, “test2”)

self.testNotebook.AddPage(nbPanel3, “test3”)

I
end up with this error instead once I try to do the DeleteAllPages()

wx._core.wxAssertionError:
C++ assertion “(int)m_pages.Count() == (int)::SendMessageW((((HWND)GetHWND())), (0x1300 + 4), 0, 0L)” failed at
…\src\msw\notebook.cpp(343) in wxNotebook::GetPageCount():

Visually,
I see the Notebook in the frame get the new page right before it crashes.

Ok, it looks like the wrong DeleteAllPages method is being called on Windows, it works fine on the other platforms. On Windows it is calling
only the base class method and so the tabs in the native widget are not
being removed. I’ll fix that problem for the next release. In the meantime you can call DeletePage for each tab.

def DeletePages(self, event):

    #self.testNotebook.DeleteAllPages()

    while self.testNotebook.GetPageCount():

        self.testNotebook.DeletePage(0)



    otherPanel = wx.Window(self.testNotebook)

    self.testNotebook.AddPage(otherPanel, "NewPanel")
···


Robin Dunn

Software Craftsman

http://wxPython.org

Great! Thanks much!

···

On Wednesday, April 26, 2017 at 7:12:36 PM UTC-6, Robin Dunn wrote:

PyMapper Developer wrote:

Okay, that makes sense. So, in changing the code to add three new panels instead

nbPanel1 = wx.Window(self.testNotebook)
nbPanel2 = wx.Window(self.testNotebook)
nbPanel3= wx.Window(self.testNotebook)
self.testNotebook.AddPage( nbPanel1,
“test1”)
self.testNotebook.AddPage(nbPanel2, “test2”)

self.testNotebook.AddPage(nbPanel3, “test3”)

I
end up with this error instead once I try to do the DeleteAllPages()

wx._core.wxAssertionError:
C++ assertion “(int)m_pages.Count() == (int)::SendMessageW((((HWND) GetHWND())), (0x1300 + 4), 0, 0L)” failed at
…\src\msw\notebook.cpp(343) in wxNotebook::GetPageCount():

Visually,
I see the Notebook in the frame get the new page right before it crashes.


Robin Dunn

Software Craftsman

http://wxPython.org

Ok, it looks like the wrong DeleteAllPages method is being called on Windows, it works fine on the other platforms. On Windows it is calling
only the base class method and so the tabs in the native widget are not
being removed. I’ll fix that problem for the next release. In the meantime you can call DeletePage for each tab.

def DeletePages(self, event):

    #self.testNotebook.DeleteAllPages()

    while self.testNotebook.GetPageCount():

        self.testNotebook.DeletePage(0)



    otherPanel = wx.Window(self.testNotebook)

    self.testNotebook.AddPage(otherPanel, "NewPanel")

Hello Robin,

Has this been fixed yet?

If I do “notebook.DeleteAllPages()”, and afterward do “notebook.AddPage”

under 32-bit Windows 7, Python 3.6.5, wxPython 4.0.3, I get:

wx._core.wxAssertionError: C++ assertion “(int)m_pages.Count() == (int)::SendMessageW((((HWND)GetHWND())), (0x1300 + 4), 0, 0L)” failed at …\src\msw\notebook.cpp(343) in wxNotebook::GetPageCount():

No error if I use your workaround, and delete one page at a time.

It looks like I may have combined this issue and a similar one with the AUI notebooks in my mind, so only one of them was addressed by the fix I made in wxWidgets. I’ll take a look at this one again.

https://github.com/wxWidgets/Phoenix/issues/928

···

On Sunday, July 8, 2018 at 10:16:18 AM UTC-7, ABC wrote:

Hello Robin,

Has this been fixed yet?

If I do “notebook.DeleteAllPages()”, and afterward do “notebook.AddPage”

under 32-bit Windows 7, Python 3.6.5, wxPython 4.0.3, I get:

wx._core.wxAssertionError: C++ assertion “(int)m_pages.Count() == (int)::SendMessageW((((HWND)GetHWND())), (0x1300 + 4), 0, 0L)” failed at …\src\msw\notebook.cpp(343) in wxNotebook::GetPageCount():

No error if I use your workaround, and delete one page at a time.

Robin