Understanding PushEventHandler and event propagation

Dear,

While working on a larger program, I got somewhat lost in the way events are propagated up the chain.

My use case is the following: in a wx.Frame that represents a document, there is a wx.MenuItem (say, wx.ID_ABOUT), that should not be handled by the document Frame, but rather by its parent Frame (the app main window, if you want). But, for the main Frame, I wanted to use a separate controller object (a wx.EvtHandler subclass) to contain some generic logic, like showing the AboutBox, and add that with wx.Window.PushEventHander. The solution I put together crashes if the controller object tries to handle the menu event, but it works is if the event is handled by the main window.

Suspecting there is something wrong with my understanding of PushEventHandler, I looked into the example on the wiki, and modified it a bit, see attached. And then I got completely lost.

When you run this with line 31 in place, I expect to see first a dialog “in EvtHandler” and then one “in Panel”. But the first one is showing twice, and I don’t see why.

Then, I get an error on closing the app:

python3 test_pusheventhandler.py

test_pusheventhandler.py:12: DeprecationWarning: NewId() is deprecated

ID_FOR_THE_BTN = wx.NewId()

Mouse down at (51, 16) on Button

Button clicked in EvtHandler

Button clicked in EvtHandler

Button clicked in Panel

Traceback (most recent call last):

File “test_pusheventhandler.py”, line 70, in

app.MainLoop()

File “/usr/local/lib/python3.7/site-packages/wx/core.py”, line 2134, in MainLoop

rv = wx.PyApp.MainLoop(self)

wx._core.wxAssertionError: C++ assertion “GetEventHandler() == this” failed at /Users/robind/projects/buildbots/macosx-vm6/dist-osx-py37/Phoenix/ext/wxWidgets/src/common/wincmn.cpp(478) in ~wxWindowBase(): any pushed event handlers must have been removed

Any help with either would be appreciated.

Best regards,

Jan

test_pusheventhandler.py (1.87 KB)

Dear,

While working on a larger program, I got somewhat lost in the way events are propagated up the chain.

My use case is the following: in a wx.Frame that represents a document, there is a wx.MenuItem (say, wx.ID_ABOUT), that should not be handled by the document Frame, but rather by its parent Frame (the app main window, if you want). But, for the main Frame, I wanted to use a separate controller object (a wx.EvtHandler subclass) to contain some generic logic, like showing the AboutBox, and add that with wx.Window.PushEventHander. The solution I put together crashes if the controller object tries to handle the menu event, but it works is if the event is handled by the main window.

Suspecting there is something wrong with my understanding of PushEventHandler, I looked into the example on the wiki, and modified it a bit, see attached. And then I got completely lost.

When you run this with line 31 in place, I expect to see first a dialog “in EvtHandler” and then one “in Panel”. But the first one is showing twice, and I don’t see why.

Because you pushed the EvtHandler on both the button and the panel. So when the button click happens in the button it sends the event to the EvtHandler and then propagates to the parent. When the panel gets it then it first sends the event to its pushed EvtHandler and then the panel’s own EVT_BUTTON event binding get’s the event and the panel’s OnButton is called.

Then, I get an error on closing the app:

It is now required to pop pushed EvtHandlers before the window they are pushed onto is destroyed. IIRC, this change happened sometime in the 2.9.x series.

···

On Tuesday, July 10, 2018 at 11:26:40 AM UTC-7, jan.leys@gmail.com wrote:

Robin

Dear,

While working on a larger program, I got somewhat lost in the way events are propagated up the chain.

My use case is the following: in a wx.Frame that represents a document, there is a wx.MenuItem (say, wx.ID_ABOUT), that should not be handled by the document Frame, but rather by its parent Frame (the app main window, if you want). But, for the main Frame, I wanted to use a separate controller object (a wx.EvtHandler subclass) to contain some generic logic, like showing the AboutBox, and add that with wx.Window.PushEventHander. The solution I put together crashes if the controller object tries to handle the menu event, but it works is if the event is handled by the main window.

Suspecting there is something wrong with my understanding of PushEventHandler, I looked into the example on the wiki, and modified it a bit, see attached. And then I got completely lost.

When you run this with line 31 in place, I expect to see first a dialog “in EvtHandler” and then one “in Panel”. But the first one is showing twice, and I don’t see why.

Because you pushed the EvtHandler on both the button and the panel. So when the button click happens in the button it sends the event to the EvtHandler and then propagates to the parent. When the panel gets it then it first sends the event to its pushed EvtHandler and then the panel’s own EVT_BUTTON event binding get’s the event and the panel’s OnButton is called.

Thanks, that clarified the whole example.

Then, I get an error on closing the app:

It is now required to pop pushed EvtHandlers before the window they are pushed onto is destroyed. IIRC, this change happened sometime in the 2.9.x series.

It doesn’t seem to be documented clearly in either the wxwidgets or wxpython references. I may try to add a sentence at an appropriate place, once I have found out where and how.

Meanwhile, I wanted to update the example code to add it to the wxpython wiki, but it seems what I thought to be the logical approach for calling PopEventHandler is not that logical, or I failed to implement it correctly. Anyway, if the script attached is run, it still gives the assert failure.

Jan

test_pusheventhandler.py (2.92 KB)

···

On Wednesday, July 11, 2018 at 8:37:51 PM UTC+2, Robin Dunn wrote:

On Tuesday, July 10, 2018 at 11:26:40 AM UTC-7, jan....@gmail.com wrote:

Non top-level windows do not typically get the EVT_CLOSE event. I’ve always wished that the did, but it’s not to be. It may work to use EVT_WINDOW_DESTROY but that might be sent too late. Probably the safest would be to catch EVT_CLOSE in your frame, and then from there call a method in the panel to pop the handlers.

···

On Thursday, July 12, 2018 at 5:38:14 AM UTC-7, jan.leys@gmail.com wrote:

On Wednesday, July 11, 2018 at 8:37:51 PM UTC+2, Robin Dunn wrote:

It is now required to pop pushed EvtHandlers before the window they are pushed onto is destroyed. IIRC, this change happened sometime in the 2.9.x series.

It doesn’t seem to be documented clearly in either the wxwidgets or wxpython references. I may try to add a sentence at an appropriate place, once I have found out where and how.

Meanwhile, I wanted to update the example code to add it to the wxpython wiki, but it seems what I thought to be the logical approach for calling PopEventHandler is not that logical, or I failed to implement it correctly. Anyway, if the script attached is run, it still gives the assert failure.

Jan

Robin

Both appear to be working. If there are no further comments, I’ll update the wiki page.

Jan

···

On 12 Jul 2018, at 20:35, Robin Dunn robin@alldunn.com wrote:

On Thursday, July 12, 2018 at 5:38:14 AM UTC-7, jan.leys@gmail.com wrote:

On Wednesday, July 11, 2018 at 8:37:51 PM UTC+2, Robin Dunn wrote:

It is now required to pop pushed EvtHandlers before the window they are pushed onto is destroyed. IIRC, this change happened sometime in the 2.9.x series.

It doesn’t seem to be documented clearly in either the wxwidgets or wxpython references. I may try to add a sentence at an appropriate place, once I have found out where and how.

Meanwhile, I wanted to update the example code to add it to the wxpython wiki, but it seems what I thought to be the logical approach for calling PopEventHandler is not that logical, or I failed to implement it correctly. Anyway, if the script attached is run, it still gives the assert failure.

Jan

Non top-level windows do not typically get the EVT_CLOSE event. I’ve always wished that the did, but it’s not to be. It may work to use EVT_WINDOW_DESTROY but that might be sent too late. Probably the safest would be to catch EVT_CLOSE in your frame, and then from there call a method in the panel to pop the handlers.