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"