Threads communication : blocked event with wxPostEvent

Hello,

I am using wxPython on Windows, using the following setup :
- Windows XP Home Edition
- ActiveState ActivePython 2.2.224
- wxPython 2.4.0.7u, or wxPython 2.4.2.4u (latest binary available)

I have attached a sample test application that demonstrates the blocked event behavior. The application consists of two threads : a main GUI thread, and a worker thread that posts events to the GUI thread using wxPostEvent. The test application places an icon in the tray, that can popup a menu.

When the tray popup menu is opened, the GUI thread stops receiving the events posted by the worker thread. Once that the menu is closed (no selection in menu is made), the blocked event is still *not* received by the GUI thread.

Now, if the GUI thread includes a timer (start the application with the --timer argument), the timer event - when triggered - unlocks the GUI thread events queue and the worker thread's event is received just after the timer event.

So, the blocked event issue seems really to come down to the GUI thread (wxPython) event queue, which is locked by GUI events such as popup menu, and not correctly unlocked afterwards. In my humble opinion the most correct behavior should be that the worker thread posted event is received by the application, even while the popup menu is opened. But at least, the posted event should be received once that the popup menu is closed.

Did I miss anything, or is this a bug?

I thank you in advance, and I wish you a good day!

Thierry

BlockedEvent.py (5.48 KB)

Thierry Thévoz wrote:

Hello,

I am using wxPython on Windows, using the following setup :
- Windows XP Home Edition
- ActiveState ActivePython 2.2.224
- wxPython 2.4.0.7u, or wxPython 2.4.2.4u (latest binary available)

I have attached a sample test application that demonstrates the blocked event behavior. The application consists of two threads : a main GUI thread, and a worker thread that posts events to the GUI thread using wxPostEvent. The test application places an icon in the tray, that can popup a menu.

When the tray popup menu is opened, the GUI thread stops receiving the events posted by the worker thread.

Yes. The posted events are processed in the app's idle event handler, but since the menu on Win32 uses it's own event loop the app's event loop is blocked and so there are no idle events and so the pending event queue is not processed.

Once that the menu is closed (no selection in menu is made), the blocked event is still *not* received by the GUI thread.

So the app does not recieve any new events and so the event queue does not "become empty" (that's when idle events are sent.)

Now, if the GUI thread includes a timer (start the application with the --timer argument), the timer event - when triggered - unlocks the GUI thread events queue and the worker thread's event is received just after the timer event.

Then the event queue does "become empty" and so the idle event is sent and then the pending events queue is processed.

So, the blocked event issue seems really to come down to the GUI thread (wxPython) event queue, which is locked by GUI events such as popup menu, and not correctly unlocked afterwards. In my humble opinion the most correct behavior should be that the worker thread posted event is received by the application, even while the popup menu is opened.

There is a patch at sourceforge that is being reviewed that will fix this. There are still a few cases that it doesn't handle correctly (or something like that) so it needs a bit more work, but hopefully the fix will get in to 2.5 soon.

But at least, the posted event should be received once that the popup menu is closed.

This part is easy to fix. Just call wxWakeUpIdle after your PopupMenu call.

···

--
Robin Dunn
Software Craftsman
http://wxPython.org Java give you jitters? Relax with wxPython!

I have written a bug report on this at
http://sourceforge.net/tracker/?group_id=9863&atid=109863

[ 805257 ] wxPostEvent queued until menus released by user

However, my events were delivered after the menu was released.
I was using the menus attached to the frame, though, and not a pop-up menu.

Robind made the following comment to the bug report.
"
This is not a wxPython specific problem but a general
problem in wxMSW. (And there is probably already a bug
report about it, but I don;t have time to hunt for it right
now...)

Basically IIUC, menus have a nested event loop while they
are active and we don't have a way to send the idle events
while it is active. Thre have been discussions on wx-dev
about possible ways to resolve this, but nothing implemented
yet.
"

···

-----------------------------------------------------------

Hello,

I am using wxPython on Windows, using the following setup :
- Windows XP Home Edition
- ActiveState ActivePython 2.2.224
- wxPython 2.4.0.7u, or wxPython 2.4.2.4u (latest binary available)

I have attached a sample test application that demonstrates the blocked
event behavior. The application consists of two threads : a main GUI
thread, and a worker thread that posts events to the GUI thread using
wxPostEvent. The test application places an icon in the tray, that can
popup a menu.

When the tray popup menu is opened, the GUI thread stops receiving the
events posted by the worker thread. Once that the menu is closed (no
selection in menu is made), the blocked event is still *not* received by
the GUI thread.

Now, if the GUI thread includes a timer (start the application with the
--timer argument), the timer event - when triggered - unlocks the GUI
thread events queue and the worker thread's event is received just after
the timer event.

So, the blocked event issue seems really to come down to the GUI thread
(wxPython) event queue, which is locked by GUI events such as popup
menu, and not correctly unlocked afterwards. In my humble opinion the
most correct behavior should be that the worker thread posted event is
received by the application, even while the popup menu is opened. But at
least, the posted event should be received once that the popup menu is
closed.

Did I miss anything, or is this a bug?

I thank you in advance, and I wish you a good day!

Thierry

Robin Dunn wrote:

Thierry Thévoz wrote:

Hello,

I am using wxPython on Windows, using the following setup :
- Windows XP Home Edition
- ActiveState ActivePython 2.2.224
- wxPython 2.4.0.7u, or wxPython 2.4.2.4u (latest binary available)

I have attached a sample test application that demonstrates the blocked event behavior. The application consists of two threads : a main GUI thread, and a worker thread that posts events to the GUI thread using wxPostEvent. The test application places an icon in the tray, that can popup a menu.

When the tray popup menu is opened, the GUI thread stops receiving the events posted by the worker thread.

Yes. The posted events are processed in the app's idle event handler, but since the menu on Win32 uses it's own event loop the app's event loop is blocked and so there are no idle events and so the pending event queue is not processed.

Ok

Once that the menu is closed (no selection in menu is made), the blocked event is still *not* received by the GUI thread.

So the app does not recieve any new events and so the event queue does not "become empty" (that's when idle events are sent.)

Ok, I understand the mechanism now

Now, if the GUI thread includes a timer (start the application with the --timer argument), the timer event - when triggered - unlocks the GUI thread events queue and the worker thread's event is received just after the timer event.

Then the event queue does "become empty" and so the idle event is sent and then the pending events queue is processed.

Ok

So, the blocked event issue seems really to come down to the GUI thread (wxPython) event queue, which is locked by GUI events such as popup menu, and not correctly unlocked afterwards. In my humble opinion the most correct behavior should be that the worker thread posted event is received by the application, even while the popup menu is opened.

There is a patch at sourceforge that is being reviewed that will fix this. There are still a few cases that it doesn't handle correctly (or something like that) so it needs a bit more work, but hopefully the fix will get in to 2.5 soon.

But at least, the posted event should be received once that the popup menu is closed.

This part is easy to fix. Just call wxWakeUpIdle after your PopupMenu call.

Perfect. I implemented this and it wakes up perfectly my idle events. Are you aware of any other GUI operations other than menus that would exhibit the same behavior, and after which I should also call wxWakeUpIdle?

I thank you for your answer, which is both extremely precise and helpful. Many thanks!
I wish you a good day!

Thierry

Jeff Britton wrote:

I have written a bug report on this at
wxWidgets download | SourceForge.net

[ 805257 ] wxPostEvent queued until menus released by user

However, my events were delivered after the menu was released.
I was using the menus attached to the frame, though, and not a pop-up menu.

Robind made the following comment to the bug report.
"
This is not a wxPython specific problem but a general
problem in wxMSW. (And there is probably already a bug
report about it, but I don;t have time to hunt for it right
now...)

Basically IIUC, menus have a nested event loop while they
are active and we don't have a way to send the idle events
while it is active. Thre have been discussions on wx-dev
about possible ways to resolve this, but nothing implemented
yet.
"

Thanks! Robin effectively quoted this problem and stated that they have a patch at Sourceforge that they are working on right now.

In the mean time, I'll be using the workaround proposed by Robin : call wxWakeUpIdle after the PopupMenu call so that the events posted to my idle queue get processed.

Again, thank you for your answer. I wish you a good day!

Thierry

Thierry Thévoz wrote:

Perfect. I implemented this and it wakes up perfectly my idle events. Are you aware of any other GUI operations other than menus that would exhibit the same behavior, and after which I should also call wxWakeUpIdle?

I can't think of any. The weak point in your case was that dismissing the menu didn't cause any events in the main window and so the idle handler didn't run. Other popup menus, menubar menus, or moving/sizing the main frame (other cases where the idle handler can be blocked) will all cause at least some message to be sent to the visible windows (even if it is just a paint) and so the idle handler will always run then.

···

--
Robin Dunn
Software Craftsman
http://wxPython.org Java give you jitters? Relax with wxPython!