在Gtk主循环中运行的Asyncio调用

13

大家好,关于 asyncio 和 Gtk+ 的问题。 如何在 Gtk.main 循环中运行下面的代码?我搜索了示例但没有找到任何内容。

#!/usr/bin/python3.4

import asyncio

@asyncio.coroutine
def client_connected_handler(client_reader, client_writer):
    print("Connection received!")
    client_writer.write(b'Hello')
    while True:
        data = yield from client_reader.read(8192)
        if not data:
            break
        if 'EXIT' in data.decode():
            print("Closing server")
            break   
        print(data)
        client_writer.write(data)
    print('Server is closed')


loop = asyncio.get_event_loop()
Server=asyncio.start_server(client_connected_handler, 'localhost', 2222)
server=loop.run_until_complete(Server)
loop.run_forever()

编辑:

好的,我应该写一下我的使用 gbulb 的经验。 首先,我使用 pip3 进行搜索。虽然找到了它,但在安装过程中出现了错误(我用超级用户进行了安装),导致链接不良好。 接下来,我从他们的存储库中下载并安装了它。我得到了这个示例,运行它时,在其核心模块中缺少参数而出现了一些错误。我真的不知道是哪个错误,因为我是从另一台电脑编写这篇文章的,我会尽快更新的。如果有人能够测试它,我将不胜感激。


为了更新上述内容,pip3 install gbulb 可以正常工作,并且在 Github 的示例部分中的计数器演示可以顺利运行。 - jcoppens
1个回答

16
截至2020年,gbulb库似乎未维护。任何希望集成asyncio和GTK的人应该只考虑答案的第二部分(展示在专用线程中运行的asyncio),或者看一下asyncio-glib,它使用比gbulb更简洁且更稳健的方法集成了asyncio和GTK。
下面是原始回答。
"The gbulb library is designed to connect the asyncio event loop specified by PEP 3156 with the GLib main loop implementation. However, the current version of gbulb is incompatible with asyncio in Python 3.4. To solve this problem, you can use this fork instead of the original version. (The issue has been fixed in the latest version.)

If you have a working gbulb, it is easy to modify your code to accept incoming connections and run GTK:"

#!/usr/bin/python3

import gi
gi.require_version("Gtk", "3.0")

import asyncio, gbulb
from gi.repository import Gtk
asyncio.set_event_loop_policy(gbulb.GLibEventLoopPolicy())

@asyncio.coroutine
def client_connected_handler(client_reader, client_writer):
    print("Connection received!")
    client_writer.write(b'Hello')
    while True:
        data = yield from client_reader.read(8192)
        if not data:
            break
        if 'EXIT' in data.decode():
            print("Closing server")
            break   
        print(data)
        client_writer.write(data)
    print('Server is closed')

loop = asyncio.get_event_loop()
loop.run_until_complete(
    asyncio.start_server(client_connected_handler, 'localhost', 2222))

w = Gtk.Window()
w.add(Gtk.Label('hey!'))
w.connect('destroy', Gtk.main_quit)
w.show_all()

loop.run_forever()

另一种可能性是在不同的线程中运行asyncio事件循环:

#!/usr/bin/python3

import asyncio, threading

import gi
gi.require_version("Gtk", "3.0")

from gi.repository import Gtk

async def client_connected_handler(client_reader, client_writer):
    # ... unchanged ...

def run_asyncio():
    loop = asyncio.new_event_loop()
    asyncio.set_event_loop(loop)
    loop.run_until_complete(
        asyncio.start_server(client_connected_handler, 'localhost', 2222))
    loop.run_forever()

threading.Thread(target=run_asyncio).start()

w = Gtk.Window()
w.add(Gtk.Label('hey!'))
w.connect('destroy', Gtk.main_quit)
w.show_all()

Gtk.main()

这种方法的优点是完全不需要使用gbulb(目前尚不清楚gbulb在生产环境中的测试情况如何)。但是需要注意,必须使用线程安全的函数在GUI(主)线程和asyncio线程之间进行通信。这意味着从GTK向asyncio提交任务时要使用loop.call_soon_threadsafeasyncio.run_coroutine_threadsafe,而向GTK提交任务时要使用GLib.idle_add

注意:第一个示例中包含了已经被弃用的@asyncio.coroutine装饰器,应该改为async def。这使得异步函数内的yield语句无效。我还没有找到解决方法。另外,请注意第二个示例中的...unchanged...也存在相同的yield问题。 - jcoppens
1
如果你将@asyncio.coroutine改为async def,那么只需将yield from改为await即可。(答案使用了旧的语法,因为问题中使用了旧的语法,并且问题和答案都是在async def作为Python的新功能之前提出的。) - user4815162342

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接