Search code examples
pythonpyqt5ipythonimporterrorqtwebengine

How to import QtWebEngineWidgets after QApplication has been created


I am trying to get around this error:

QtWebEngineWidgets must be imported before a QCoreApplication instance is created.

It is pretty self-explanatory, but I am trying to make a GUI to be used within iPython which may be imported after a QApplication instance has been created.

How can I get around this error and create a PyQt5 GUI that can show HTML pages and is able to be imported even after the user has used a QApplication instance (such as through matplotlib)?

I tried this but still get the same error:

from PyQt5 import QtWidgets
import seaborn as sns
sns.boxplot([1],[1])
QtWidgets.QApplication.instance().quit()
from PyQt5 import QtWidgets, QtWebEngineWidgets

Solution

  • There is a hack for working around this problem, but it may not be totally robust.

    The idea is to delete all C++ references to the QApplication (if it exists), so that the web-engine module can be imported safely. This can be done using sip, but there are some caveats: if a third-party module has saved a python reference to the old QApplication and then tries to use it, it will raise an error like this:

    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    RuntimeError: wrapped C/C++ object of type QApplication has been deleted
    

    In practice, it's hard to predict how likely this is to happen, since you cannot legislate for how other python libraries are written. The webengine_hack function in the script below tries to do as much as possible to reduce the likelihood of the problems mentioned above:

    def webengine_hack():
        from PyQt5 import QtWidgets
        app = QtWidgets.QApplication.instance()
        if app is not None:
            import sip
            app.quit()
            sip.delete(app)
        import sys
        from PyQt5 import QtCore, QtWebEngineWidgets
        QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_ShareOpenGLContexts)
        app = QtWidgets.qApp = QtWidgets.QApplication(sys.argv)
        return app
    
    try:
        # just for testing
        from PyQt5 import QtWidgets
        app = QtWidgets.QApplication([''])
        from PyQt5 import QtWebEngineWidgets
    except ImportError as exception:
        print('\nRetrying webengine import...')
        app = webengine_hack()
        from PyQt5 import QtWebEngineWidgets
    
    view = QtWebEngineWidgets.QWebEngineView()
    view.setHtml('<h1>Hello World</h1>')
    view.show()
    
    app.exec()