在Gtk主循环中运行的Asyncio调用
好的,大家有个关于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 个回答
截至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_threadsafe
或asyncio.run_coroutine_threadsafe
从GTK提交任务到asyncio,以及使用GLib.idle_add
从asyncio提交任务到GTK。