Closed abulka closed 5 years ago
Thanks Andy for all the good feedback and issues. If you can provide me a link to your app, it would help to understand the problem better. Currently, I could fix it by checking if the object is in the structure before deleting it, but that feels a bit like coding by trial and error.
I've gotten a small repro case going, which should be easier to debug. Its the same repro case I created for #2 with an additional button for popping up the frame.
import wx
from wxasync import AsyncBind, WxAsyncApp, StartCoroutine
import asyncio
from asyncio.events import get_event_loop
import time
import wx.lib.newevent
SomeNewEvent, EVT_SOME_NEW_EVENT = wx.lib.newevent.NewEvent()
SomeNewEventAsync, EVT_SOME_NEW_EVENT_ASYNC = wx.lib.newevent.NewEvent()
class TestFrame(wx.Frame):
def __init__(self, parent=None):
super(TestFrame, self).__init__(parent)
vbox = wx.BoxSizer(wx.VERTICAL)
button1 = wx.Button(self, label="AsyncBind (original wxasync example)")
button2 = wx.Button(self, label="Start/stop clock via StartCoroutine()")
button3 = wx.Button(self, label="Emit Custom Event (sync)")
button4 = wx.Button(self, label="Emit Custom Event (async)")
button5 = wx.Button(self, label="Show Frame")
self.edit = wx.StaticText(self, style=wx.ALIGN_CENTRE_HORIZONTAL|wx.ST_NO_AUTORESIZE)
self.edit_timer = wx.StaticText(self, style=wx.ALIGN_CENTRE_HORIZONTAL|wx.ST_NO_AUTORESIZE)
vbox.Add(button1, 2, wx.EXPAND|wx.ALL)
vbox.Add(button2, 2, wx.EXPAND|wx.ALL)
vbox.Add(button3, 2, wx.EXPAND|wx.ALL)
vbox.Add(button4, 2, wx.EXPAND|wx.ALL)
vbox.Add(button5, 2, wx.EXPAND|wx.ALL)
vbox.AddStretchSpacer(1)
vbox.Add(self.edit, 1, wx.EXPAND|wx.ALL)
vbox.Add(self.edit_timer, 1, wx.EXPAND|wx.ALL)
self.SetSizer(vbox)
self.Layout()
self.clock_on = False
"""Original direct binding - works ok"""
AsyncBind(wx.EVT_BUTTON, self.async_callback, button1)
"""
Regular method calls StartCoroutine() - works ok
No need for async/await syntax except on the final async method! Turtle avoidance success
because no need for async/await syntax turtles all the way up the calling chain.
PROVISO: WxAsyncApp() object must be created first, frame creation within OnInit fails.
"""
self.Bind(wx.EVT_BUTTON, self.regular_func_starts_coroutine, button2)
"""
Regular method broadcast a custom event - works ok
But this is the synchronous version. Its the async version we want to get working.
"""
self.Bind(wx.EVT_BUTTON, self.regular_func_raises_custom_event, button3)
self.Bind(EVT_SOME_NEW_EVENT, self.callback) # bind custom event to synchronous handler - works OK
"""
Regular method broadcast a custom event - doesn't work
bind custom event to asynchronous handler - doesn't work
"""
self.Bind(wx.EVT_BUTTON, self.regular_func_raises_custom_async_event, button4)
AsyncBind(EVT_SOME_NEW_EVENT_ASYNC, self.async_callback, self) # don't specify id
"""Show popup frame"""
self.Bind(wx.EVT_BUTTON, self.on_show_frame, button5)
def regular_func_raises_custom_event(self, event):
print("trigger demo via custom event")
# Create and post the event
evt = SomeNewEvent(attr1="hello", attr2=654)
wx.PostEvent(self, evt)
def regular_func_raises_custom_async_event(self, event):
print("trigger async demo via custom event")
# Create and post the event
evt = SomeNewEventAsync(attr1="hello", attr2=654)
wx.PostEvent(self, evt)
def regular_func_starts_coroutine(self, event):
self.clock_on = not self.clock_on
if self.clock_on:
print(f"triggering an async call via StartCoroutine()")
StartCoroutine(self.update_clock, self)
else:
print("clock flag off, coroutine will stop looping, drop through and complete")
def on_show_frame(self, event): # not used
"""manually build a frame with inner html window, no sizer involved"""
class MyPopupFrame(wx.Frame):
def __init__(self, parent, title):
super(MyPopupFrame, self).__init__(parent, title=title)
frm = MyPopupFrame(parent=self, title="Simple Popup Frame")
frm.Show()
def callback(self, event):
self.edit.SetLabel("Button clicked (synchronous)")
wx.SafeYield()
time.sleep(1)
self.edit.SetLabel("Working (synchronous)")
wx.SafeYield()
time.sleep(1)
self.edit.SetLabel("Completed (synchronous)")
wx.SafeYield()
time.sleep(1)
self.edit.SetLabel("")
async def async_callback(self, event):
self.edit.SetLabel("Button clicked")
await asyncio.sleep(1)
self.edit.SetLabel("Working")
await asyncio.sleep(1)
self.edit.SetLabel("Completed")
await asyncio.sleep(1)
self.edit.SetLabel("")
async def update_clock(self):
while self.clock_on:
self.edit_timer.SetLabel(time.strftime('%H:%M:%S'))
await asyncio.sleep(0.5)
self.edit_timer.SetLabel("")
app = WxAsyncApp()
frame = TestFrame()
frame.Show()
app.SetTopWindow(frame)
loop = get_event_loop()
loop.run_until_complete(app.MainLoop())
I've added both parameters object and source in AsyncBind similar to when you do "object.Bind(EVT_BUTTON, source=self.button)"
Can you check if this commit works to fix you exceptions messages? https://github.com/sirk390/wxasync/commit/816344de3df88806cbc7e64a92ce9e479965013b
Nice, seems to be fixed now, thank you.
An official PyPi release with these changes would be great at some stage. :-)
My wxasync based app, (wxPython 4.04, Python 3.7.1) is generating exceptions as I shut down. The error happens only if my app ever opens another frame (like the wxPython PrintFramework window or a Help window). I don't get the error immediately though - only when exiting the main app and shutting down the main app/frame.
If I never open another frame from my main wxPython app, then the shutdown happens cleanly.
The two exceptions seem to be related, in that a bound object cannot be found. I haven't had time to create a simple repro case yet.
and