Ril*_*hes 3 python tkinter python-3.x python-asyncio python-3.5
我正在使用 Python3.5 和 Tkinter 创建一个加密货币交换 API 客户端。我有几个显示,我想每 10 秒异步更新一次。我可以Tk.after()在这个例子中使用像每 10 秒更新一次显示
def updateLoans():
offers = dd.loanOffers()
demands = dd.loanDemands()
w.LoanOfferView.delete(1.0, END)
w.LoanDemandView.delete(1.0, END)
w.LoanOfferView.insert(END, offers)
w.LoanDemandView.insert(END, demands)
print('loans refreshed')
root.after(10000, updateLoans)
Run Code Online (Sandbox Code Playgroud)
为了使该after方法每 10 秒持续更新一次,该函数updateLoans()需要作为可调用对象传递到after()函数内部。
现在难倒我的部分,当我使这个函数与 python 的新 async 和 await 关键字异步时
async def updateLoans():
offers = await dd.loanOffers()
demands = await dd.loanDemands()
w.LoanOfferView.delete(1.0, END)
w.LoanDemandView.delete(1.0, END)
w.LoanOfferView.insert(END, offers)
w.LoanDemandView.insert(END, demands)
print('loans refreshed')
root.after(10000, updateLoans)
Run Code Online (Sandbox Code Playgroud)
这里的问题是我不能在after方法的参数中等待可调用对象。所以我收到运行时警告。RuntimeWarning: coroutine 'updateLoans' was never awaited.
我最初的函数调用IS放在一个事件循环内。
loop = asyncio.get_event_loop()
loop.run_until_complete(updateLoans())
loop.close()
Run Code Online (Sandbox Code Playgroud)
显示器最初填充得很好,但永远不会更新。
如何使用Tk.after异步持续更新 tkinter 显示?
tk.after接受一个普通的函数,而不是一个协程。要运行协程以完成,您可以使用run_until_complete,就像您第一次一样:
loop = asyncio.get_event_loop()
root.after(10000, lambda: loop.run_until_complete(updateLoans()))
Run Code Online (Sandbox Code Playgroud)
另外,不要调用loop.close(),因为您将再次需要循环。
updateLoans()由于网络缓慢或远程服务问题而需要很长时间,它将使 GUI 完全无响应。一个好的 GUI 应用程序会希望避免这种情况。
虽然 Tkinter 和 asyncio还不能共享一个事件循环,但完全有可能在单独的线程中运行 asyncio 事件循环。主线程然后运行 GUI,而专用的 asyncio 线程运行所有 asyncio 协程。当事件循环需要通知 GUI 刷新某些内容时,它可以使用如下所示的队列。另一方面,如果 GUI 需要告诉事件循环做某事,它可以调用call_soon_threadsafe或run_coroutine_threadsafe。
示例代码(未经测试):
gui_queue = queue.Queue()
async def updateLoans():
while True:
offers = await dd.loanOffers()
demands = await dd.loanDemands()
print('loans obtained')
gui_queue.put(lambda: updateLoansGui(offers, demands))
await asyncio.sleep(10)
def updateLoansGui(offers, demands):
w.LoanOfferView.delete(1.0, END)
w.LoanDemandView.delete(1.0, END)
w.LoanOfferView.insert(END, offers)
w.LoanDemandView.insert(END, demands)
print('loans GUI refreshed')
# http://effbot.org/zone/tkinter-threads.htm
def periodicGuiUpdate():
while True:
try:
fn = gui_queue.get_nowait()
except queue.Empty:
break
fn()
root.after(100, periodicGuiUpdate)
# Run the asyncio event loop in a worker thread.
def start_loop():
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
loop.create_task(updateLoans())
loop.run_forever()
threading.Thread(target=start_loop).start()
# Run the GUI main loop in the main thread.
periodicGuiUpdate()
root.mainloop()
# To stop the event loop, call loop.call_soon_threadsafe(loop.stop).
# To start a coroutine from the GUI, call asyncio.run_coroutine_threadsafe.
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
4370 次 |
| 最近记录: |