I'm just starting out with javascript and Qt so bear with me
My issue is with the runJavaScript
method. I can't seem to get the callback function to run with a returned value that's delayed. For example, the following prints None
.
js = '''
function returnHello(){
var i = 0;
var wait = setInterval(function() { //arbitrary delay
i++
if(i>2){
return('hello')
}
}, 10);
}
returnHello();
'''
def test(a):
print(a)
mw.reviewer.web.page().runJavaScript(js, test)
I suspect it has something to do with how javascript runs asynchronously, and I tried playing around with javascript callback methods, but if there's any delay in returning values, the Qt python callback method always seems to accept the undefined
default javascript return value.
I've been scouring the internet for answers, so any help would be great!
It seems that you do not know how an asynchronous task works, an asynchronous function does not return internal information since at the time of evaluating the assignment, the task has not yet been executed nor is it blocked until it is executed. For example, the variable "wait" is a timer ID that is used to cancel the execution of the timer. Therefore runJavaScript will not work in this case.
One possible solution is to use Qt WebChannel, based on my previous answer I have implemented the following solution:
import os
import sys
from PyQt5 import QtCore, QtWidgets, QtWebEngineWidgets, QtWebChannel
from jinja2 import Template
class Element(QtCore.QObject):
def __init__(self, name, parent=None):
super(Element, self).__init__(parent)
self._name = name
@property
def name(self):
return self._name
def script(self):
raise NotImplementedError
class TestObject(Element):
def script(self):
_script = r"""
function returnHello(){
var i = 0;
var id_timer = setInterval(function() { //arbitrary delay
i++
if(i>2){
{{name}}.test('hello')
}
}, 10);
}
returnHello();
"""
return Template(_script).render(name=self.name)
@QtCore.pyqtSlot(str)
def test(self, a):
print(a)
class WebEnginePage(QtWebEngineWidgets.QWebEnginePage):
def __init__(self, *args, **kwargs):
super(WebEnginePage, self).__init__(*args, **kwargs)
self.loadFinished.connect(self.onLoadFinished)
self._objects = []
def add_object(self, obj):
self._objects.append(obj)
@QtCore.pyqtSlot(bool)
def onLoadFinished(self, ok):
if ok:
self.load_qwebchannel()
self.load_objects()
def load_qwebchannel(self):
file = QtCore.QFile(":/qtwebchannel/qwebchannel.js")
if file.open(QtCore.QIODevice.ReadOnly):
content = file.readAll()
file.close()
self.runJavaScript(content.data().decode())
if self.webChannel() is None:
channel = QtWebChannel.QWebChannel(self)
self.setWebChannel(channel)
def load_objects(self):
if self.webChannel() is not None:
objects = {obj.name: obj for obj in self._objects}
self.webChannel().registerObjects(objects)
_script = r"""
{% for obj in objects %}
var {{obj}};
{% endfor %}
new QWebChannel(qt.webChannelTransport, function (channel) {
{% for obj in objects %}
{{obj}} = channel.objects.{{obj}};
{% endfor %}
});
"""
self.runJavaScript(Template(_script).render(objects=objects.keys()))
for obj in self._objects:
if isinstance(obj, Element):
self.runJavaScript(obj.script())
class WebPage(QtWebEngineWidgets.QWebEngineView):
def __init__(self, parent=None):
super().__init__(parent)
page = WebEnginePage(self)
self.setPage(page)
test_object = TestObject("test_object", self)
page.add_object(test_object)
self.load(QtCore.QUrl("https://stackoverflow.com/"))
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
web = WebPage()
web.show()
sys.exit(app.exec_())