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.
Problem:
When the QApplication is executed (calling app.exec_()
) I get the warning:
WARNING: QApplication was not created in the main() thread.
and then the error:
2019-07-17 13:06:19.461 Python[56513:5183122] *** Assertion failure in +[NSUndoManager _endTopLevelGroupings], /BuildRoot/Library/Caches/com.apple.xbs/Sources/Foundation/Foundation-1562/Foundation/Misc.subproj/NSUndoManager.m:361
2019-07-17 13:06:19.464 Python[56513:5183122] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '+[NSUndoManager(NSInternal) _endTopLevelGroupings] is only safe to invoke on the main thread.'
*** First throw call stack:
(
0 CoreFoundation 0x00007fff4e1abded __exceptionPreprocess + 256
1 libobjc.A.dylib 0x00007fff7a273720 objc_exception_throw + 48
...
...
122 libsystem_pthread.dylib 0x00007fff7b53826f _pthread_start + 70
123 libsystem_pthread.dylib 0x00007fff7b534415 thread_start + 13
)
libc++abi.dylib: terminating with uncaught exception of type NSException
Received signal 6
[0x00010a766de6]
[0x7fff7b52cb3d]
...
...
[0x000105a0de27]
[end of stack trace]
It seems like the QApplication always needs to run on the main thread, which is not the case since flask runs resources on background threads. A possible solution i have considered is to run the QApplication as a os subprocess but is not ideal.
Question:
Is it possible to keep it within the Flask app?
Example PyQt class:
import sys
from PyQt5.QtWebEngineWidgets import QWebEnginePage
from PyQt5.QtWebEngineWidgets import QWebEngineProfile
from PyQt5.QtWidgets import QApplication
from PyQt5.QtCore import QUrl
class PyQtWebClient(QWebEnginePage):
def __init__(self, url):
# Pointless variable for showcase purposes
self.total_runtime = None
self.app = QApplication(sys.argv)
self.profile = QWebEngineProfile()
# This is a sample to show the constructor I am actually using, my 'profile' is more complex than this
super().__init__(self.profile, None)
# Register callback to run when the page loads
self.loadFinished.connect(self._on_load_finished)
self.load(QUrl(url))
self.app.exec_()
def _on_load_finished(self):
self.total_runtime = 10
if __name__ == '__main__':
url = "https://www.example.com"
page = PyQtWebClient(url)
Example Flask app.py
from flask import Flask
from flask_restful import Resource, Api
from lenomi import PyQtWebClient
app = Flask(__name__)
api = Api(app)
class TestPyqt5(Resource):
def post(self):
web = PyQtWebClient("http://www.example.com")
# At this point PyQtWebClient should have finished loading the url, and the process is done
print(web.total_runtime)
api.add_resource(TestPyqt5, "/pyqt")
if __name__ == '__main__':
app.run(debug=True)