You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Operating system: Any wxPython version & source: Latest PYPI Python version & source: 3.5+
Coroutines are very helpful for GUI developing as this technique allows time-consuming tasks running along with the main event loop, while keep the program logic in a synchronous-like manner. wx.Yield() will no longer be necessary after the introduction of coroutines. A piece of sample code demonstrating the integration is attached in the end.
Because of the usefulness I think it will be great to add these helper classes into somewhere under the wx.lib package, while I have some questions. How should I submit the patch, is a pull request enough for a patch, and in which sub-package should these classes be located? Thank you very much!
#!/usr/bin/env python3importasyncioimportconcurrent.futuresimportthreadingimporttimefromasyncio.eventsimportAbstractEventLoopfromasyncio.futuresimportFuturefromtypingimportOptional, Callable, Any, TypeimportwxclassWxTimerHandle(asyncio.TimerHandle):
__slots__='call_later',
classWxEventLoop(asyncio.AbstractEventLoop):
def__init__(self, app: wx.AppConsole):
self._closed=Falseself._app=appself._default_executor=Noneself._debug=Falseself._exception_handler=Noneself._task_factory=Nonedefrun_forever(self) ->None:
self._app.MainLoop()
defstop(self) ->None:
self._app.ExitMainLoop()
defis_running(self) ->bool:
returnself._app.GetMainLoop() isnotNonedefclose(self) ->None:
executor=self._default_executorifexecutorisnotNone:
self._default_executor=Noneexecutor.shutdown(wait=False)
self._closed=Truedef_timer_handle_cancelled(self, handle: WxTimerHandle) ->None:
handle.call_later.Stop()
defcall_soon(self, callback: Callable[..., Any], *args, context=None) ->None:
self.call_soon_threadsafe(callback, *args)
defcall_at(self, when, callback: Callable[..., Any], *args, context=None) ->WxTimerHandle:
returnself.call_later(when-self.time(), callback, *args, context)
defcall_later(self, delay: float, callback: Callable[..., Any], *args: Any) ->WxTimerHandle:
handle=WxTimerHandle(delay*1000+self.time(), callback, args, self)
handle.call_later=wx.CallLater(int(delay*1000), callback, *args)
returnhandledeftime(self) ->float:
returntime.monotonic()
defcreate_future(self) ->asyncio.Future:
returnasyncio.Future(loop=self)
defcreate_task(self, coro) ->asyncio.Task:
ifself._task_factoryisNone:
returnasyncio.Task(coro, loop=self)
else:
returnself._task_factory(self, coro)
defcall_soon_threadsafe(self, callback: Callable[..., Any], *args, context=None) ->None:
wx.CallAfter(callback, *args)
defrun_in_executor(self, executor: concurrent.futures.ThreadPoolExecutor, func: Callable[..., Any], *args) ->asyncio.Future:
ifexecutorisNone:
executor=self._default_executorifexecutorisNone:
executor=concurrent.futures.ThreadPoolExecutor()
self._default_executor=executorreturnasyncio.wrap_future(executor.submit(func, *args), loop=self)
defset_default_executor(self, executor: concurrent.futures.ThreadPoolExecutor) ->None:
self._default_executor=executordefget_exception_handler(self):
returnself._exception_handlerdefset_exception_handler(self, handler):
self._exception_handler=handlerdefdefault_exception_handler(self, context):
print('Got exception: '+repr(context))
defcall_exception_handler(self, context):
ifself._exception_handlerisNone:
self.default_exception_handler(context)
else:
self._exception_handler(self, context)
defget_debug(self) ->bool:
returnself._debugdefset_debug(self, enabled: bool) ->None:
self._debug=enableddefrun_until_complete(self, future):
raiseNotImplementedErrordefis_closed(self) ->bool:
returnself._closedasyncdefshutdown_asyncgens(self):
raiseNotImplementedErrordefset_task_factory(self, factory) ->None:
self._task_factory=factorydefget_task_factory(self):
returnself._task_factoryclassWxEventLoopPolicy(asyncio.AbstractEventLoopPolicy):
def__init__(self, app: Type[wx.AppConsole], delegate: asyncio.AbstractEventLoopPolicy=asyncio.get_event_loop_policy()):
self._app=appself._loop=Noneself._delegate=delegatedefget_event_loop(self) ->AbstractEventLoop:
ifthreading.current_thread() isthreading.main_thread():
ifself._loopisNone:
self._loop=WxEventLoop(self._app())
returnself._loopelse:
returnself._delegate.get_event_loop()
defset_event_loop(self, loop: AbstractEventLoop) ->None:
self._delegate.set_event_loop(loop)
defnew_event_loop(self) ->AbstractEventLoop:
returnself._delegate.new_event_loop()
defget_child_watcher(self) ->Any:
returnself._delegate.get_child_watcher()
defset_child_watcher(self, watcher: Any) ->None:
self._delegate.set_child_watcher(watcher)
def_bind_async(self, event, handler):
def_handler(event):
asyncio.ensure_future(handler(event))
self.Bind(event, _handler)
wx.EvtHandler.BindAsync=_bind_asyncdefmain():
asyncio.set_event_loop_policy(WxEventLoopPolicy(app=wx.App))
asyncio.get_event_loop().set_debug(True)
def_another_loop_thread():
nonlocalanother_loopanother_loop=asyncio.new_event_loop()
asyncio.set_event_loop(another_loop)
another_loop.run_forever()
another_loop.close()
another_loop=Noneanother_loop_thread=threading.Thread(target=_another_loop_thread)
another_loop_thread.start()
asyncdefon_close(event):
defstop_and_close():
another_loop.stop()
another_loop.call_soon_threadsafe(stop_and_close)
frame.Destroy()
frame=wx.Frame(None, title='Coroutine Integration in wxPython', size=wx.Size(800, 600))
frame.BindAsync(wx.EVT_CLOSE, on_close)
frame.CreateStatusBar()
frame.GetStatusBar().StatusText='Ready'counter=1asyncdefon_click(event):
deflog(message: str):
frame.GetStatusBar().StatusText=messageprint(message)
nonlocalcountercount=' ['+str(counter) +']'counter+=1log('Starting the event handler'+count)
awaitasyncio.sleep(1) # Sleep in the current event looplog('Running in the thread pool'+count)
# time.sleep is used to emulate synchronous time-consuming tasksawaitasyncio.get_event_loop().run_in_executor(None, time.sleep, 1)
log('Running in another loop'+count)
# Socket operations are theoretically unsupported in WxEventLoop# So a default event loop in a separate thread is sometime required# asyncio.sleep is used to emulate these asynchronous tasksawaitasyncio.wrap_future(asyncio.run_coroutine_threadsafe(asyncio.sleep(1), another_loop))
log('Ready'+count)
button=wx.Button(frame, label='\n'.join([
'Click to start the asynchronous event handler',
'The application will remain responsive while the handler is running',
'Try click here multiple times to launch multiple coroutines',
'These coroutines will not conflict each other as they are all in the same thread',
]))
button.BindAsync(wx.EVT_BUTTON, on_click)
frame.Show()
asyncio.get_event_loop().run_forever()
another_loop_thread.join()
if__name__=='__main__':
main()
The text was updated successfully, but these errors were encountered:
gzxu
linked a pull request
Dec 8, 2018
that will
close
this issue
Operating system: Any
wxPython version & source: Latest PYPI
Python version & source: 3.5+
Coroutines are very helpful for GUI developing as this technique allows time-consuming tasks running along with the main event loop, while keep the program logic in a synchronous-like manner.
wx.Yield()
will no longer be necessary after the introduction of coroutines. A piece of sample code demonstrating the integration is attached in the end.Because of the usefulness I think it will be great to add these helper classes into somewhere under the
wx.lib
package, while I have some questions. How should I submit the patch, is a pull request enough for a patch, and in which sub-package should these classes be located? Thank you very much!The text was updated successfully, but these errors were encountered: