【问题标题】:Implementing asyncIO with pysimplegui用 pysimplegui 实现 asyncIO
【发布时间】:2021-12-27 14:58:58
【问题描述】:

我正在尝试在 python 中使用 pysimplegui 实现 asyncio。 在这个 GUI 示例中,两个按钮(按钮 2 和按钮 3)模拟了一个很长的任务来完成。

目标

  • 即使调用的函数(通过按钮)需要时间来返回结果,也能够返回 GUI 界面。

预期结果:

  • 如果按钮 2 或按钮 3 或两者都被按下,它们都会继续执行其任务,用户可以返回 GUI 继续执行其他任务。

当前结果:

  • 只要按下按钮 2 或按钮 3,任务就会被阻止并持续到结束,而 GUI 会挂起直到结束。

import PySimpleGUI as sg
import asyncio
import time

sg.theme('Light Blue 3')
# This design pattern simulates button callbacks
# This implementation uses a simple "Dispatch Dictionary" to store events and functions

# The callback functions
async def button1():
    print('Button 1 callback')
    return 'nothing'

async def button2():
    print('Button 2 callback')
    for i in range(1,20):
        await asyncio.sleep(3)
        print(f"Button 2: {i}")
    return f"button2 end"

async def button3():
    print('Button 3 callback')
    for i in range(1,10):
        await asyncio.sleep(3)
        print(f"Button 3: {i}")
    return f"button3: end"


# Lookup dictionary that maps button to function to call
dispatch_dictionary = {'1':button1, '2':button2, '3':button3}
# Layout the design of the GUI
layout = [[sg.Text('Please click a button', auto_size_text=True)],
        [sg.Button('1'), sg.Button('2'), sg.Button('3'), sg.Quit()]]
# Show the Window to the user__TIMEOUT__
window = sg.Window('Button callback example', layout)
# Event loop. Read buttons, make callbacks
while True:
    # Read the Window
    event, values = window.read()
    if event in ('Quit', sg.WIN_CLOSED):
        break
    if event == '__TIMEOUT__':
        continue
    # Lookup event in function dictionary
    if event in dispatch_dictionary:
        func_to_call = dispatch_dictionary[event]   # get function from dispatch dictionary
        print(asyncio.run(func_to_call()))
    else:
        print('Event {} not in dispatch dictionary'.format(event))

window.close()
# All done!
sg.popup_ok('Done')

我以为我按照规则应用了异步/等待。我错过了什么吗?

【问题讨论】:

    标签: python python-asyncio pysimplegui


    【解决方案1】:

    asyncio.run() 执行一个协程并阻塞直到它完成。它确实启动一个并行线程来运行协程。

    你有两个选择:

    1. 不要使用 asyncio,使用 Threading 为每个长操作启动一个新线程。
    2. 在程序开始时启动一个包含异步事件循环的线程,然后使用asyncio.run_coroutine_threadsafe() 将协程从主 GUI 线程调度到事件循环。

    我将在这里解释选项 2.. 程序开头的示例:

    from threading import Thread
    
    def asyncloop(loop):
        # Set loop as the active event loop for this thread
        asyncio.set_event_loop(loop)
        # We will get our tasks from the main thread so just run an empty loop    
        loop.run_forever()
    
    # create a new loop
    loop = asyncio.new_event_loop()
    # Create the new thread, giving loop as argument
    t = Thread(target=asyncloop, args=(loop,))
    # Start the thread
    t.start()
    

    稍后在按钮事件代码中(在主线程中):

    asyncio.run_coroutine_threadsafe(func_to_call(), loop)
    

    这将安排协程在我们创建的线程内作为并行任务运行。

    【讨论】:

    • 注意:我不熟悉 pysimplegui,但通常所有的 GUI 操作都需要在同一个线程中进行。因此,如果您启动新线程或将协程调度到此处描述的工作线程上,您将无法从它们执行 GUI 操作。
    • 感谢@vaizki 的及时回复。我一直不鼓励使用线程来支持 asyncio,因为线程的可扩展性很差。此外: > “asyncio 的一个很酷的优点是它的扩展性比线程好得多。每个任务占用的资源和创建时间都比线程少得多,因此创建和运行更多的任务效果很好。这个例子只是创建了一个为每个站点下载单独的任务,效果很好。”
    • 我怀疑这一点,我自己也使用了 asyncio a LOT。这就是为什么我经历了为异步循环创建一个额外线程的示例。除非您的 GUI 库具有异步 API,否则您不能在同一线程中运行 GUI 事件循环和异步事件循环。
    • 您能否进一步解释,我不确定如何将 asyncio 事件循环与 Pysimplegui 中通常使用的事件循环(当 True)集成。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-07-06
    • 1970-01-01
    • 1970-01-01
    • 2016-09-19
    • 2022-01-08
    • 1970-01-01
    相关资源
    最近更新 更多