Hi Jamie,
James Teh wrote:
Hi.
I’m the NVDA developer Joseph was discussing
this with. First, my thanks to all who have helped with this so far.
Even
though I totally understand that wx.Yield can cause re-entrance (this is clearly documented), this wasn’t previously the case when starting the same timer within that timer’s callback. That is, another timer or
a wx event might get fired within wx.Yield, but the same timer never fired. Instead, the inner call to the timer was dropped. That is what this test case demonstrates. In wxPython 3, you never see the re-entrance message. In wxPython 4, you do.
The question is whether this change is intentional/expected and whether the previous behaviour will be restored. It’s reasonable to argue that we were relying on undefined behaviour (or maybe even a bug), and if that is the
case and this won’t be reverted, we’ll find another way. However, I wanted to confirm this before putting a huge amount of work into “fixing” this.
The wx.PyTimer class is implemented differently in Phoenix than it was in Classic, but I don’t see how that difference could cause this difference in behavior. I’m not aware of changes in the C++ wxTimer that
might lead to this, but that doesn’t mean there aren’t any. (There were
no changes in the MSW implementation of wxTimer, but it could be that one of the changes in how the events are dispatched or something could have had some impact.)
Either way, the way it is working now is how I’ve always expected timers
and yielding to work, so I think that this probably is a case of relying on some undefined behavior.
The reason we use wx.Yield here is that we need to trigger a Windows message pump when sending key presses and checking for
pending accessibility events, since keyboard events and accessibility events depend on the Windows message queue. I guess we could just do our
own message pump, but I’m concerned this would end up processing wx events anyway.
If I understand correctly what you are doing I think you are correct, unless you manage your message queue on a different thread. It’s been a long time since I worked at that level of the Windows API so I may be forgetting something however.
There may be some alternatives though. I’ve not used this before so I don’t know if it will have the effect you are looking for, but I see that the wx.EventLoopBase class has a WakeUp method that sounds interesting. You can call it something like this:
wx.GetApp().GetMainLoop().WakeUp()
Or you can make your own temporary wx event loop with code like this:
evtLoop = wx.GetApp().GetTraits().CreateEventLoop()
with wx.EventLoopActivator(evtLoop):
# automatically restores the old one upon exit from the context manager
Once you have the event loop you can selectively yield for categories of
events with the YieldFor method. Messages received that are not in the given category will be put back on the queue to be processed later. I’m not sure if this will provide enough control for your needs, but it is probably worth investigating.
You can also derive a new class from wx.EventLoopBase and use that instead of the default returned from CreateEventLoop. With a derived class you can implement your own MainLoop and implement your message processing there.
HTH,
Robin
···
–
Robin Dunn
Software Craftsman
http://wxPython.org