Manthri Anvesh
Manthri Anvesh

Reputation: 95

Run Qt and Websockets server in same event loop

I have a websockets server running in python, and also wanted to show some GUI using PyQt5. Both websockets and PyQt5 has to run forever in the same thread, but i'm not sure how i can do it.

below is the code i implement to create and start the server.

import websockets
import asyncio


async def server_extentions(websocket, path):
    try:
        while (True):
            request = await websocket.recv()
            response = "blabla"
            await websocket.send(response)
    except websockets.ConnectionClosed as exp:
        print("connection closed.")

evt_loop = asyncio.get_event_loop()
start_server = websockets.serve(server_extentions, '127.0.0.1', 5588, loop=evt_loop)
try:
    evt_loop.run_until_complete(start_server)
    evt_loop.run_forever()
finally:
    evt_loop.run_until_complete(evt_loop.shutdown_asyncgens())
    evt_loop.close()

below is some gui i show in my application


from PyQt5.QtWidgets import QProgressBar, QWidget, QLabel, QApplication
from PyQt5 import QtCore


class DownloadProgress(QWidget):

    def __init__(self, parent=None):
        super().__init__(parent)
        self.init_ui()

    def init_ui(self):
        self.progress_bar = QProgressBar(self)
        self.progress_bar.setGeometry(30, 40, 200, 25)
        self.progress_bar.setValue(0)

        self.label_status_msg = QLabel()
        self.label_status_msg.setAlignment(QtCore.Qt.AlignCenter)
        self.label_status_msg.setGeometry(30, 80, 200, 25)
        self.label_status_msg.setText("starting")

        self.setGeometry(300, 300, 280, 170)
        self.setWindowTitle('Bla Bla')

    def set_progress_value(self, value: int):
        self.progress_bar.setValue(value)

    def set_status_msg(self, msg: str):
        self.label_status_msg.setText(msg)

app = QApplication([])


dp = DownloadProgress()
dp.set_status_msg("download in progress.")
dp.set_progress_value(20)
dp.show()

app.exec()


shower me some light, to run both the tasks in same event loop.

note: i definitely want them in same thread.

Thanks in advance.

Upvotes: 2

Views: 1619

Answers (1)

Mikhail Gerasimov
Mikhail Gerasimov

Reputation: 39576

app.exec() runs QT's event loop, which you can't do since you have to run asyncio's event loop with run_forever().

Fortunately Qt provides processEvents which you can call periodically alongside asyncio's event loop to make Qt work:

You can call this function occasionally when your program is busy performing a long operation (e.g. copying a file).


Remove app.exec() and alter code following way:

app = QApplication([])

async def qt_loop():    
    while True:
        app.processEvents()     # allow Qt loop to work a bit
        await asyncio.sleep(0)  # allow asyncio loop to work a bit

qt_loop_task = asyncio.ensure_future(qt_loop())  # task will work alongside with the server


evt_loop = asyncio.get_event_loop()    
start_server = websockets.serve(server_extentions, '127.0.0.1', 5588, loop=evt_loop)
try:
    evt_loop.run_until_complete(start_server)
    evt_loop.run_forever()
finally:
    # I'll leave to you job to kill qt_loop_task 
    # see: https://stackoverflow.com/a/37345564/1113207
    evt_loop.run_until_complete(evt_loop.shutdown_asyncgens())
    evt_loop.close()

I didn't check it, but I presume it should work.

Good idea is to enable debug mode while testing to make sure everything is fine.

Upvotes: 3

Related Questions