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

13 投票
1 回答
4184 浏览
提问于 2025-04-30 21:05

好的,大家有个关于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搜索了一下,找到了它并尝试安装,但因为链接有问题,安装失败了(我用的是超级用户权限)。接着,我从他们的代码库下载了它并安装了。然后我运行了这个示例,结果出现了一些错误,提示缺少参数在他们的核心模块里。我现在不太清楚具体是什么错误,因为我是在另一台电脑上写的,等我有时间会尽快更新。如果有其他人能帮忙测试一下,我会非常感激。

暂无标签

1 个回答

16

截至2020年,gbulb库似乎已经不再维护了。想要把asyncio和GTK结合起来的人,最好只考虑答案的第二部分(在专门的线程中运行asyncio),或者看看asyncio-glib,这个库用一种更简单、更稳健的方法将asyncio和GTK结合在一起。

下面是原始答案。


gbulb的设计目的是为了在asyncio事件循环(根据PEP 3156的规定)和GLib主循环之间提供一个连接器。不过,当前版本的gbulb在Python 3.4中与asyncio不兼容。要解决这个问题,你可以查看这个分支,而不是使用主版本。(这个问题后来在主版本中修复了。)

如果有一个正常工作的gbulb,修改你的例子以同时接受连接和运行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线程之间进行通信时,必须使用线程安全的函数。这意味着要使用loop.call_soon_threadsafeasyncio.run_coroutine_threadsafe从GTK提交任务到asyncio,以及使用GLib.idle_add从asyncio提交任务到GTK。

撰写回答