Skip to content Skip to sidebar Skip to footer

How To Execute Pyqt5 Application On A Resful Call

Context: I have a Flask application serving a resource POST /start. The logic to be executed involves a PyQt5 QWebEnginePage loading a URL and returning certain data about it. Pro

Solution 1:

Resource executes the post, get, etc methods in secondary threads to avoid that the thread where flask is executed does not block, and therefore the QApplication is running in that secondary thread that Qt prohibits generating that error.

In this case the solution is.

  • Create a class that handles requests through QWebEnginePage running on the main thread.

  • Make the flask run on a secondary thread so that it does not block the Qt eventloop.

  • Send the information through signals between the post method and the class that handles the requests.

Considering this I have implemented an example where you can make requests to pages via the API, obtaining the HTML of that page

lenomi.py

from functools import partial

from PyQt5 import QtCore, QtWebEngineWidgets


classSignaller(QtCore.QObject):
    emitted = QtCore.pyqtSignal(object)


classPyQtWebClient(QtCore.QObject):
    @QtCore.pyqtSlot(Signaller, str)defget(self, signaller, url):
        self.total_runtime = None
        profile = QtWebEngineWidgets.QWebEngineProfile(self)
        page = QtWebEngineWidgets.QWebEnginePage(profile, self)
        wrapper = partial(self._on_load_finished, signaller)
        page.loadFinished.connect(wrapper)
        page.load(QtCore.QUrl(url))

    @QtCore.pyqtSlot(Signaller, bool)def_on_load_finished(self, signaller, ok):
        page = self.sender()
        ifnotisinstance(page, QtWebEngineWidgets.QWebEnginePage) ornot ok:
            signaller.emitted.emit(None)
            return

        self.total_runtime = 10
        html = PyQtWebClient.download_html(page)
        args = self.total_runtime, html
        signaller.emitted.emit(args)

        profile = page.profile()
        page.deleteLater()
        profile.deleteLater()

    @staticmethoddefdownload_html(page):
        html = ""
        loop = QtCore.QEventLoop()

        defcallback(r):
            nonlocal html
            html = r
            loop.quit()

        page.toHtml(callback)
        loop.exec_()
        return html

app.py

import sys
import threading
from functools import partial

from flask import Flask
from flask_restful import Resource, Api, reqparse

from PyQt5 import QtCore, QtWidgets

from lenomi import PyQtWebClient, Signaller


app = Flask(__name__)
api = Api(app)
parser = reqparse.RequestParser()


classTestPyqt5(Resource):
    def__init__(self, client):
        self.m_client = client

    defpost(self):
        parser.add_argument("url", type=str)
        args = parser.parse_args()
        url = args["url"]
        if url:
            total_runtime, html, error = 0, "", "not error"defcallback(loop, results=None):
                if results isNone:
                    nonlocal error
                    error = "Not load"else:
                    nonlocal total_runtime, html
                    total_runtime, html = results
                loop.quit()

            signaller = Signaller()
            loop = QtCore.QEventLoop()
            signaller.emitted.connect(partial(callback, loop))
            wrapper = partial(self.m_client.get, signaller, url)
            QtCore.QTimer.singleShot(0, wrapper)
            loop.exec_()

            return {
                "html": html,
                "total_runtime": total_runtime,
                "error": error,
            }


qt_app = Nonedefmain():

    global qt_app
    qt_app = QtWidgets.QApplication(sys.argv)

    client = PyQtWebClient()
    api.add_resource(
        TestPyqt5, "/pyqt", resource_class_kwargs={"client": client}
    )

    threading.Thread(
        target=app.run,
        kwargs=dict(debug=False, use_reloader=False),
        daemon=True,
    ).start()

    return qt_app.exec_()


if __name__ == "__main__":
    sys.exit(main())
curl http://localhost:5000/pyqt -d "url=https://www.example.com" -X POST

Output:

{"html": "<!DOCTYPE html><html><head>\n    <title>Example Domain</title>\n\n    <meta charset=\"utf-8\">\n    <meta http-equiv=\"Content-type\" content=\"text/html; charset=utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n    <style type=\"text/css\">\n    body {\n        background-color: #f0f0f2;\n        margin: 0;\n        padding: 0;\n        font-family: \"Open Sans\", \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n\n    }\n    div {\n        width: 600px;\n        margin: 5em auto;\n        padding: 50px;\n        background-color: #fff;\n        border-radius: 1em;\n    }\n    a:link, a:visited {\n        color: #38488f;\n        text-decoration: none;\n    }\n    @media (max-width: 700px) {\n        body {\n            background-color: #fff;\n        }\n        div {\n            width: auto;\n            margin: 0 auto;\n            border-radius: 0;\n            padding: 1em;\n        }\n    }\n    </style>    \n</head>\n\n<body>\n<div>\n    <h1>Example Domain</h1>\n    <p>This domain is established to be used for illustrative examples in documents. You may use this\n    domain in examples without prior coordination or asking for permission.</p>\n    <p><a href=\"http://www.iana.org/domains/example\">More information...</a></p>\n</div>\n\n\n</body></html>", "total_runtime": 10, "error": "not error"}

Post a Comment for "How To Execute Pyqt5 Application On A Resful Call"