python tkinter autobahn python-asyncio

Python-Ejecutando el servidor websocket de Autobahn | Python asyncio en un subproceso o subproceso separado



tkinter python-asyncio (3)

Tengo un programa GUI basado en tkinter que se ejecuta en Python 3.4.1. Tengo varios subprocesos ejecutándose en el programa para obtener datos JSON de varias direcciones URL. Deseo agregar algunas funciones de WebSocket para poder permitir que el programa actúe como un servidor y permitir que varios clientes se conecten a él a través de un WebSocket e intercambien otros datos JSON.

Estoy intentando usar el servidor Autobahn | Python WebSocket para asyncio.

Primero intenté ejecutar el bucle de eventos asyncio en un hilo separado en el programa GUI. Sin embargo, cada intento da ''AssertionError: No hay un bucle de eventos actual en el hilo'' Thread-1 ''.

Luego traté de generar un proceso con el paquete de multiprocesamiento de biblioteca estándar que ejecutó el bucle de eventos asyncio en otro Proceso. Cuando intento esto, no obtengo ninguna excepción, pero el servidor WebSocket tampoco se inicia.

¿Es incluso posible ejecutar un bucle de eventos asyncio en un subproceso de otro programa Python?

¿Hay incluso una manera de integrar un bucle de eventos asyncio en un programa / tkinter actualmente multiproceso?

ACTUALIZACIÓN A continuación se muestra el código real que estoy intentando ejecutar para una prueba inicial.

from autobahn.asyncio.websocket import WebSocketServerProtocol from autobahn.asyncio.websocket import WebSocketServerFactory import asyncio from multiprocessing import Process class MyServerProtocol(WebSocketServerProtocol): def onConnect(self, request): print("Client connecting: {0}".format(request.peer)) def onOpen(self): print("WebSocket connection open.") def onMessage(self, payload, isBinary): if isBinary: print("Binary message received: {0} bytes".format(len(payload))) else: print("Text message received: {0}".format(payload.decode(''utf8''))) ## echo back message verbatim self.sendMessage(payload, isBinary) def onClose(self, wasClean, code, reason): print("WebSocket connection closed: {0}".format(reason)) def start_server(): factory = WebSocketServerFactory("ws://10.241.142.27:6900", debug = False) factory.protocol = MyServerProtocol loop = asyncio.get_event_loop() coro = loop.create_server(factory, ''10.241.142.27'', 6900) server = loop.run_until_complete(coro) loop.run_forever() server.close() loop.close() websocket_server_process = Process(target = start_server) websocket_server_process.start()

La mayor parte es directamente del código de ejemplo de Autobahn | Python para asyncio. Si trato de ejecutarlo como un Proceso, no hace nada, ningún cliente puede conectarse a él, si ejecuto netstat -a no se está utilizando el puerto 6900. Si solo usa start_server () en el programa principal, creará el servidor WebSocket.


"¿Hay incluso una manera de integrar un bucle de eventos asyncio en un programa multithreaded / tkinter actualmente?"

Sí, ejecute su programa tkinter con un bucle de eventos asyncio. Prueba de concepto.

''''''Proof of concept integrating asyncio and tk loops. Terry Jan Reedy Run with ''python -i'' or from IDLE editor to keep tk window alive. '''''' import asyncio import datetime as dt import tkinter as tk loop = asyncio.get_event_loop() root = tk.Tk() # Combine 2 event loop examples from BaseEventLoop doc. # Add button to prove that gui remain responsive between time updates. # Prints statements are only for testing. def flipbg(widget, color): bg = widget[''bg''] print(''click'', bg, loop.time()) widget[''bg''] = color if bg == ''white'' else ''white'' hello = tk.Label(root) flipper = tk.Button(root, text=''Change hello background'', bg=''yellow'', command=lambda: flipbg(hello, ''red'')) time = tk.Label(root) hello.pack() flipper.pack() time.pack() def hello_world(loop): hello[''text''] = ''Hello World'' loop.call_soon(hello_world, loop) def display_date(end_time, loop): print(dt.datetime.now()) time[''text''] = dt.datetime.now() if (loop.time() + 1.0) < end_time: loop.call_later(1, display_date, end_time, loop) else: loop.stop() end_time = loop.time() + 10.1 loop.call_soon(display_date, end_time, loop) # Replace root.mainloop with these 4 lines. def tk_update(): root.update() loop.call_soon(tk_update) # or loop.call_later(delay, tk_update) # Initialize loop before each run_forever or run_until_complete call tk_update() loop.run_forever()

He ejecutado experimentalmente IDLE con esas 4 líneas adicionales, con una desaceleración que solo se nota cuando la sintaxis resalta 1000 líneas.


La respuesta de puede ser correcta, pero crea un nuevo proceso que no es necesario en la mayoría de las situaciones.

Encontré esta pregunta en Google porque yo mismo tuve el mismo problema. He escrito una aplicación en la que quería que una api de websocket no se ejecutara en el hilo principal y esto causó tu problema.

Encontré mi solución alternativa simplemente leyendo sobre los bucles de eventos en la documentación de python y encontré las funciones asyncio.new_event_loop y asyncio.set_event_loop que resolvieron este problema.

No utilicé AutoBahn sino la biblioteca pypi websockets, y aquí está mi solución

import websockets import asyncio import threading class WebSocket(threading.Thread): @asyncio.coroutine def handler(self, websocket, path): name = yield from websocket.recv() print("< {}".format(name)) greeting = "Hello {}!".format(name) yield from websocket.send(greeting) print("> {}".format(greeting)) def run(self): start_server = websockets.serve(self.handler, ''127.0.0.1'', 9091) eventloop = asyncio.new_event_loop() asyncio.set_event_loop(eventloop) eventloop.run_until_complete(start_server) eventloop.run_forever() if __name__ == "__main__": ws = WebSocket() ws.start()


Primero, está obteniendo AssertionError: There is no current event loop in thread ''Thread-1''. porque asyncio requiere que cada subproceso de su programa tenga su propio bucle de eventos, pero solo creará automáticamente un bucle de eventos para usted en el hilo principal. Entonces, si llama a asyncio.get_event_loop una vez en el hilo principal, creará automáticamente un objeto de bucle y lo configurará como el predeterminado para usted, pero si lo llama nuevamente en un hilo secundario, obtendrá ese error. En su lugar, necesita crear / establecer explícitamente el bucle de eventos cuando se inicie el hilo:

loop = asyncio.new_event_loop() asyncio.set_event_loop(loop)

Una vez que hayas hecho eso, deberías poder usar get_event_loop() en ese hilo específico.

Es posible iniciar un bucle de eventos asyncio en un subproceso iniciado mediante multiprocessing :

import asyncio from multiprocessing import Process @asyncio.coroutine def coro(): print("hi") def worker(): loop = asyncio.get_event_loop() loop.run_until_complete(coro()) if __name__ == "__main__": p = Process(target=worker) p.start() p.join()

Salida:

hi

La única advertencia es que si inicia un bucle de eventos tanto en el proceso principal como en el secundario, deberá crear / establecer un nuevo bucle de eventos explícitamente en el secundario si está en una plataforma Unix (debido a un error en Python). ). Debería funcionar bien en Windows, o si usa el contexto de multiprocessing ''spawn''.

Creo que debería ser posible iniciar un bucle de eventos asyncio en un subproceso en segundo plano (o proceso) de su aplicación Tkinter y hacer que ambos, el bucle de eventos tkinter y asyncio , se ejecuten en paralelo. Solo se encontrará con problemas si intenta actualizar la GUI desde el proceso / subproceso en segundo plano.