Search code examples
javascriptpythonqtwebkitweb-worker

Add python class to web worker's context in PyQt


I'm writing an application in PyQt5 with webkit, and I'd like to access a Python class via JavaScript, inside a Web Worker.

My code, right now, does this:

import sys
import time

from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWebKit import *
from PyQt5.QtWebKitWidgets import *
from PyQt5.QtWidgets import *


class Foo(QObject):
    @pyqtSlot(result=str)
    def bar(self):
        return "baz"


class Window(QWidget):
    def __init__(self):
        super(Window, self).__init__()
        self.view = QWebView(self)

        self.setupInspector()

        self.splitter = QSplitter(self)
        self.splitter.setContentsMargins(800, 600, 0, 0)
        self.splitter.setOrientation(Qt.Vertical)

        self.layout = QVBoxLayout(self)
        self.layout.setContentsMargins(800, 600, 0, 0)
        self.layout.addWidget(self.splitter)

        self.view.setLayout(self.layout)

        self.foo = Foo(self)
        self.view.page().mainFrame().addToJavaScriptWindowObject("foo", self.foo)

    def setupInspector(self):
        page = self.view.page()
        page.settings().setAttribute(QWebSettings.DeveloperExtrasEnabled, True)
        self.webInspector = QWebInspector(self)
        self.webInspector.setPage(page)

        shortcut = QShortcut(self)
        shortcut.setKey(Qt.Key_F12)
        shortcut.activated.connect(self.toggleInspector)
        self.webInspector.setVisible(False)

    def toggleInspector(self):
        self.webInspector.setVisible(not self.webInspector.isVisible())


def main():
    app = QApplication(sys.argv)
    window = Window()
    window.show()
    window.view.load(QUrl(sys.argv[1]))
    app.exec_()


if __name__ == "__main__":
    main()

I load an html file, with a file:/ URL, like this: python3 browser.py file:/path-to-html/file.html.

If I run the inspector, I can run foo.bar() and I get "baz" in return.

I'd like to run that inside a worker. If I create a javascript file with:

self.postMessage(foo.test());

and, in the inspector, I create a worker with:

var worker = new Worker('file:/same-path-as-the-html/file.js');

I get ReferenceError: Can't find variable: foo, which, to me, seems like it means foo wasn't added by Qt to the worker's scope.


Solution

  • Looks like it is indeed impossible to add Qt objects to web-workers, as they live in their own scope, while the Qt objects are always added to the page DOM.

    See also this answer.