Run an async web server along with PyQt5
-
Hello,
I'm trying to run an async web server like Sanic along side my PyQt5 app, but the web server api requires that i run a function.run
which runs an event loop, and i'm having trouble running it along side the PyQt5 event loop:I tried to run it inside a
Qthread
but even then it makes the app freeze because the.run
function never returns until the app is closed.My example
Qthread
class:class api_server(qtc.QObject): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.api = Sanic(name='test') self.api.add_route(self.test, "/") def run(self): self.api.run(host="0.0.0.0", port=8080) async def test(self,request): return json({"hello": "world"})
Then in my main
qtw.QMainWindow
init method I'm doing the following:self._api_server_thread = qtc.QThread() self._api_server = api.api_server() self._api_server_thread.started.connect(self._api_server.run) self._api_server.moveToThread(self._api_server_thread) self._api_server_thread.start()
Regards
-
Hi,
Why do you need a full blown web server as part of your GUI application ?
That said you may have to consider using the Python subprocess module rather than multithreading.
-
Hi,
Why do you need a full blown web server as part of your GUI application ?
That said you may have to consider using the Python subprocess module rather than multithreading.
@SGaist said in Run an async web server along with PyQt5:
Hi,
Why do you need a full blown web server as part of your GUI application ?
To provide an api for another app running on a different machine and since my gui app is already connected the
sqlite
database i want to run the server from the same code to keep resource usage to a minimumThat said you may have to consider using the Python subprocess module rather than multithreading.
That means i need to run the server from a different python file which means i can't reference the server from inside my app code.
There was a proposed qt component a couple of years ago which would have been perfect for my use case, sadly it was never merged upstream
Regards
-
That doesn't answer my question about the why.
Understanding the why may help found how.
From memory QHttpServer is a separate module. If you really want it in Python, you can create the necessary bindings yourself.
-
That doesn't answer my question about the why.
Understanding the why may help found how.
From memory QHttpServer is a separate module. If you really want it in Python, you can create the necessary bindings yourself.
@SGaist said in Run an async web server along with PyQt5:
That doesn't answer my question about the why.
Understanding the why may help found how.
The gui app is connected to an
SQLITE
database, along side the main app function, it should also provide an api that can be called from a different machine, this api can be used to control the main app(provide records from the database, delete, insert as well as running functions that changes the Widget appearance which is why i need the server to be called from my pyqt app codeFrom memory QHttpServer is a separate module. If you really want it in Python, you can create the necessary bindings yourself.
That would be too much of a hassle and i have no prior experience in that space
Regards
-
In that case, I would suggest to have these two cleanly separated and have your GUI connect to the API server as well for example through a local socket.
They can both connect to the same database if required. Establish a protocol for whatever needs to be changed in your GUI based on what is called your API server.
This has the added advantage that your GUI can crash without killing the server and vice-versa.
-
Hello,
I'm trying to run an async web server like Sanic along side my PyQt5 app, but the web server api requires that i run a function.run
which runs an event loop, and i'm having trouble running it along side the PyQt5 event loop:I tried to run it inside a
Qthread
but even then it makes the app freeze because the.run
function never returns until the app is closed.My example
Qthread
class:class api_server(qtc.QObject): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.api = Sanic(name='test') self.api.add_route(self.test, "/") def run(self): self.api.run(host="0.0.0.0", port=8080) async def test(self,request): return json({"hello": "world"})
Then in my main
qtw.QMainWindow
init method I'm doing the following:self._api_server_thread = qtc.QThread() self._api_server = api.api_server() self._api_server_thread.started.connect(self._api_server.run) self._api_server.moveToThread(self._api_server_thread) self._api_server_thread.start()
Regards
@rhx9 A simple solution is to use qasync:
# https://github.com/sanic-org/sanic/blob/main/examples/run_async.py import asyncio from signal import signal, SIGINT import sys from sanic import Sanic from sanic.response import json from PyQt5.QtCore import QDateTime, Qt, QTimer from PyQt5.QtWidgets import QApplication, QLabel import qasync app = Sanic(__name__) @app.route("/") async def test(request): return json({"hello": "world"}) def main(): qtapp = QApplication(sys.argv) loop = qasync.QEventLoop(qtapp) asyncio.set_event_loop(loop) w = QLabel(alignment=Qt.AlignmentFlag.AlignCenter) w.resize(640, 480) w.show() def handle_timeout(): w.setText(QDateTime.currentDateTime().toString()) timer = QTimer() timer.setInterval(1000) timer.timeout.connect(handle_timeout) timer.start() handle_timeout() server = app.create_server(host="0.0.0.0", port=8000, return_asyncio_server=True) loop = asyncio.get_event_loop() task = asyncio.ensure_future(server) server = loop.run_until_complete(task) loop.run_until_complete(server.startup()) signal(SIGINT, lambda s, f: loop.stop()) try: loop.run_forever() finally: loop.stop() if __name__ == "__main__": main()
-
@rhx9 A simple solution is to use qasync:
# https://github.com/sanic-org/sanic/blob/main/examples/run_async.py import asyncio from signal import signal, SIGINT import sys from sanic import Sanic from sanic.response import json from PyQt5.QtCore import QDateTime, Qt, QTimer from PyQt5.QtWidgets import QApplication, QLabel import qasync app = Sanic(__name__) @app.route("/") async def test(request): return json({"hello": "world"}) def main(): qtapp = QApplication(sys.argv) loop = qasync.QEventLoop(qtapp) asyncio.set_event_loop(loop) w = QLabel(alignment=Qt.AlignmentFlag.AlignCenter) w.resize(640, 480) w.show() def handle_timeout(): w.setText(QDateTime.currentDateTime().toString()) timer = QTimer() timer.setInterval(1000) timer.timeout.connect(handle_timeout) timer.start() handle_timeout() server = app.create_server(host="0.0.0.0", port=8000, return_asyncio_server=True) loop = asyncio.get_event_loop() task = asyncio.ensure_future(server) server = loop.run_until_complete(task) loop.run_until_complete(server.startup()) signal(SIGINT, lambda s, f: loop.stop()) try: loop.run_forever() finally: loop.stop() if __name__ == "__main__": main()
@eyllanesc qasync is awesome, i use it to embed a asyncio tcp server with my qml application.
yet it did not support python 3.11 for right now as this issue described, does qasync has a substitude which work with python 3.11?
i also noticed that asyncio example is in discussion mentioned in 08. December 2022 development notes, yet i can not find any official asyncio example until now