sirk390 / wxasync

asyncio support for wxpython
MIT License
76 stars 9 forks source link

async def MainLoop #26

Closed da-dada closed 1 year ago

da-dada commented 1 year ago

at least on a Windows, the app receives events (Idle and UpdateUI) all the time and it behaves more like the original app by changing the last line to

if IS_MAC or evtloop.Pending(): evtloop.ProcessIdle()

sirk390 commented 1 year ago

HI @da-dada, Thanks for the issue, but I don't think this is correct. The original app calls self.ProcessIdle() while nothing is pending. It would make no sense to call evtloop.ProcessIdle() if something is Pending as your code suggests.

image

For async behaviour, we want to spend all the idle time in the event loop instead of sending idle events. Normally, you don't want to use idle messages at all in async.

However, for backward compatibility, it might be necessary to keep them in some cases. This is why, I decided to call ProcessIdle() once per loop (each 5ms).

Could you explain in more detail the issue you are having? Would you like to have more frequent or less frequent ProcessIdle() events?

da-dada commented 1 year ago

first snippet is wxpython second wxasync

`import wx

class Gui(wx.Frame):

def __init__(self, parent):
    super().__init__(parent, title='main loop')
    wx.Button(self)
    self.Centre()
    self.Show()

class WxApp(wx.App):

def __init__(self):
    super().__init__()

def FilterEvent( self, evt):
    print(f'{evt.GetId()} --> {evt}')
    return self.Event_Skip

def OnInit(self):
    Gui(None)
    self.MainLoop()
    return True

WxApp()`

import asyncio import wx from wxasync import WxAsyncApp

class Gui(wx.Frame):

def __init__(self, parent):
    super().__init__(parent, title='main loop async')
    wx.Button(self)
    self.Centre()
    self.Show()

class WxApp(WxAsyncApp):

def __init__(self):
    super().__init__()

def FilterEvent( self, evt):
    print(f'{evt.GetId()} --> {evt}')
    return self.Event_Skip

def OnInit(self):
    Gui(None)
    self.MainLoop()
    return True

async def main(): if app := WxApp(): await app.MainLoop()

asyncio.run(main())

sirk390 commented 1 year ago

Ok, I see the difference, and the UpdateUIEvent so often might be problematic. Do you have any issue with it other than to many messages send?

da-dada commented 1 year ago

I'm not using wxasync (for me async and dialog don't really go together), I just had a look: running a coroutine from wxpython in the main thread instead of using a second thread would be a new feature however, as far as I can judge, my change I suggested does make the loop more compatible to what it ought to be

sirk390 commented 1 year ago

I see the issue. I thought idle events were sent continuously when idle, like in old-fashioned OnIdle handlers. But, as explained on this page: https://docs.wxpython.org/wx.IdleEvent.html ", the event is sent only once when the UI becomes idle.

Your code works a little, but I don't think it is not correct. What do you think about this commit:

https://github.com/sirk390/wxasync/commit/9ecefb0c9f812ba0179c108593a41161f237ee7f

da-dada commented 1 year ago

since ui_idle is used only locally it can be a local variable (faster)