Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Communicate with html/javascript using QWebEngineView

I need to get a dynamic content, that is being loaded by a ajax js call.

I really don't know how to use PyQt but i was hoping i could do this. The HTML is something like:

<a href="#" id="id" onclick="A4J.AJAX.Submit('j_id0:j_id1:j_id110',event,{'similarityGroupingId':'j_id0:j_id1:j_id110:j_id582:0:j_id584'});return false;">NETHERLANDS</a>`

I can render the page with PyQt using this simple code:

def render(source_html):

    import sys
    from PyQt5.QtCore import QEventLoop
    from PyQt5.QtWidgets import QApplication
    from PyQt5.QtWebEngineWidgets import QWebEngineView

    class Render(QWebEngineView):
        def __init__(self, html):
            self.html = None
            self.app = QApplication(sys.argv)
            QWebEngineView.__init__(self)
            self.loadFinished.connect(self._loadFinished)
            self.setHtml(html)

            while self.html is None:
                self.app.processEvents(QEventLoop.ExcludeUserInputEvents | QEventLoop.ExcludeSocketNotifiers | QEventLoop.WaitForMoreEvents)
            self.app.quit()

        def _callable(self, data):
            self.html = data

        def _loadFinished(self, result):
            self.page().toHtml(self._callable)

    return Render(source_html).html

import requests
sample_html = requests.get('https://riverbankcomputing.com/software/pyqt/').text
print(render(sample_html))

How can i run that 'onclick' and get the content?

like image 783
raul ferreira Avatar asked Jan 26 '17 16:01

raul ferreira


1 Answers

This is an old question, but...

To run PyQt functions from javascript:

While there may be numerous ways of doing this, I've solved it by using QWebChannel, and then calling js functions from your html, then use the web channel to communicate with Qt.

You will need qwebchannel.js. I got mine from the Qt5 examples directory on my local machine. The same file exists on the web in many places. I'll leave it up to you to find it.

Much of this method is described here: http://doc.qt.io/qt-5/qtwebchannel-javascript.html

In your __init__, create a web channel:

self.webchannel = QtWebChannel.QWebChannel(self)

then set your webengineview's main page to use the channel, and register the object you want to be shared between PyQt and js:

self.page().setWebChannel(self.webchannel)
self.webchannel.registerObject('MyChannel', self)

In your .js (or the javascript section of your .html), setup the web channel:

var MyChannel = null;
new QWebChannel(qt.webChannelTransport, function(channel) {
                MyChannel = channel.objects.MyChannel;
});

(This is where qwebchannel.js comes into play. Your js or html file must be able to include it. For me, I loaded <script src="scripts/qwebchannel.js"></script> before doing any other js

Now you're setup to make calls from js to PyQt via the channel, but what can you call? Anything that's decorated as a PyQt slot. So, for example, if, in javascript, you wanted to call a "foo" function in Render that takes a string as an argument, then you would create it (as a member of Render) as such:

@QtCore.pyqtSlot(str)
def foo(self, some_tring):
    print ("Some string: %s" % some_string)

...and then in the js file (or in your index.html), you would simply make a call to MyChannel.foo('whatever'). You could do that as an onclick, or you can do it from the body of another function that you call from onclick.

Talking through MyChannel.foo('whatever'): You call MyChannel because that was the name you assigned to the object you registered with the channel (in python), and the name you used in js when you created the new QWebChannel. When you created the registration, you passed self as the object to register - so the channel, identified by MyChannel, is your Render object. You can only signal through the channel, so anything you "call" must be a slot - hence the decorator.


Alternatively, if you want to call a js function from PyQt, it's a bit easier. In that case, you simply call

self.page().runJavaScript('someJsFunction("whatever");')

If you need to do something with the response from that, because it's called async, you'll need to setup a response handler:

self.page().runJavaScript('someJsFunction("whatever");', self.__callback)

...and then define the callback (likely as a member of Render):

def __callback(self, response):
    if response:
        print ("Handling JS response: %s", response)
like image 85
Brian K Avatar answered Nov 10 '22 04:11

Brian K