13

Okey guys question concerning asyncio and Gtk+. How can I run code below in Gtk.main loop ? I searched for example but couldn't find any.

#!/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()

EDIT:

Okey I should write my experiance with gbulb. First I searched it using pip3 . I found it and tried to install it but it failed(I was using super user for installation) due bad links. Next I downloaded it from their repository and installed it .I got this example I run it and got some errors for missing arguments in their core module.I don't know really which error is it cause Iam writing this from different PC I'll update is ASAP. Also I would be grateful if anybody else could test it.

harisk92
  • 1,088
  • 1
  • 14
  • 24
  • To update the above, `pip3 install gbulb` works fine, and the counter demo in the Examples section on [Github](https://github.com/nathan-hoad/gbulb) runs without a hitch. – jcoppens Jun 04 '17 at 21:22

1 Answers1

16

As of 2020, the gbulb library appears to be unmaintained. Anyone wishing to integrate asyncio and GTK should only consider the second part of the answer (showing asyncio running in a dedicated thread) or take a look at asyncio-glib, which integrates asyncio and GTK using an approach more minimalistic and robust than that taken by gbulb.

Original answer follows below.


The gbulb library is designed to provide a connector between the asyncio event loop as specified by PEP 3156 and the GLib main loop implementation. However, the current master of gbulb is broken for asyncio as shipped with Python 3.4. To fix this, you can check out this fork instead of the master. (The problem was later fixed upstream.)

With a working gbulb, it is trivial to modify your example to both 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()

Another possibility is to run the asyncio event loop in a different thread:

#!/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()

This has the advantage of not requiring gbulb at all (it is not clear how well gbulb has been tested in production). One does need to be careful, however, to use thread-safe functions to communicate between the GUI (main) thread and the asyncio thread. This means using loop.call_soon_threadsafe or asyncio.run_coroutine_threadsafe to submit things to asyncio from GTK, and GLib.idle_add to submit things to GTK from asyncio.

user4815162342
  • 141,790
  • 18
  • 296
  • 355
  • Note: The first example contains the `@asyncio.coroutine` decorator, which has since been deprecated and should be changed to `async def`. This makes the `yield` statement invalid inside the async function. I haven't figured out how to solve this. Also note that the `...unchanged...` in the secon example has the same `yield` issue. – jcoppens Aug 28 '23 at 13:39
  • 1
    @jcoppens If you change `@asyncio.coroutine` to `async def`, simply also change `yield from` to `await`. (The answer uses the older syntax because that was used in the question, and because both the question and the answer are from back when `async def` was a new addition to Python.) – user4815162342 Aug 28 '23 at 18:05