Aleph0
Aleph0

Reputation: 6084

Minimal QWebChanel example not working

I have to port my code to QT 5.8. Unfortunately, existing code is not working any longer, as the interface for QWebEngine changed. After altering the easiest portions, by search and replace I was trapped in using the new QWebChannel class.

I just don't get it working properly. In my widgets I allowed the client to easily fill HTML templates with contents using JQuery. This worked quite well by transporting values from my C++ application to a simple Javascript code.

But I wasn't able to use the new Qt 5.8. QWebChannel interface. I also tried to debug it using the --remote-debugging-port=10 command line switch and the Chrome Developer Tools.

qt.webChannelTransport is known to the Chrome Debugger. But it doesn't knew the class QWebChannel? Strangely, the code for $(document).ready(...) is never executed.

If I type qt.webChannelTransport.send("234234") inside the Chrome Developers Tools console the program crashes.

Hence I decided to make a minimal example to illustrate what I wanted to achieve. It should be easy for someone being acquainted with the correct usage. I was also unable to debug the Javascript code using my Visual Studio, even unsure if this is possible in some way?

I'm sorry for all this lengthy code, but it seems to be the closest to get a working example.

Main.cpp

#include <QWebChannel>
#include <QWebEngineView>
#include <QApplication>
#include <QFile>
#include "JsWebPage.h"

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    auto view = new QWebEngineView;

    view->setPage(new JsWebPage);

    QFile file(":/Example.html");
    if (!file.open(QIODevice::ReadOnly)) return false;
    QString content = QString::fromUtf8(file.readAll()); 
    view->setHtml(content);
    file.close();
    //view->page()->setHtml(content);
    view->show();
    return app.exec();
}

JsWebPage.h

#pragma once

#include <QWebEnginePage>

class JsWebPage : public QWebEnginePage {
    Q_OBJECT

public:
    JsWebPage();

    void javaScriptConsoleMessage(JavaScriptConsoleMessageLevel level, const QString &message, int lineNumber, const QString &sourceID) override;

private:
    QWebChannel* mWebChannel = nullptr;
};

JsWebPage.cpp

#include "JsWebPage.h"
#include <QWebChannel>
#include <QWebEngineSettings>
#include <QWebEnginePage>
#include <QDebug>

void JsWebPage::javaScriptConsoleMessage(JavaScriptConsoleMessageLevel level, const QString &message, int lineNumber, const QString &sourceID)
{
    qDebug() << QString("Javascript Console: Line: %1, Source: %2, %3").arg(lineNumber).arg(sourceID).arg(message);
}

JsWebPage::JsWebPage()
{
    settings()->setAttribute(QWebEngineSettings::LocalContentCanAccessRemoteUrls, true);
    settings()->setAttribute(QWebEngineSettings::JavascriptCanOpenWindows, true);

    mWebChannel=new QWebChannel;
    mWebChannel->registerObject(QString("qtObject"), this);
    setWebChannel(mWebChannel);
}

example.js

new QWebChannel(
    qt.webChannelTransport, 
    function(channel) {
        var qtObject = channel.objects.qtObject;
        console.log("Hello world");
        // Code for replacing using jQuery
        // <div id="myTag1"></div>
        // by
        // <div id="myTag1">Content</div>
    }
);

$(document).ready(
    console.log("READY"); // Program never reached this point. Why?
);

Example.html

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
    <script src="jquery.js" type="text/javascript"></script>
    <script src="qwebchannel.js" type="text/javascript"></script>
    <script src="example.js" type="text/javascript"></script>
    <title>Template</title>
</head>
<body id="Body"> 
    Example HTML
    <div id="myTag1"></div>
    <div id="myTag2"></div>
</body>
</html>

Here is what I say after debugging my program with Chrome:

Chrome Developer Tool

Upvotes: 2

Views: 1367

Answers (1)

Aleph0
Aleph0

Reputation: 6084

Using the Chrome Developer Tools I was able to get a running version of my program. The errors where mostly subtle and hard to debug.

I'm posting my code here to help others struggling with the same kind of problems.

Potential errors where:

  • Malformed HTML
  • Javascript files are not found (being at the wrong place)
    • I'm now putting them all in a resource file.
    • Take a look in the Example.html!
  • Javascript errors
    • Chrome Developer Tools are of great use here
  • Javascript extractKeys() Function was called at the wrong place

main.cpp

#include <QWebChannel>
#include <QWebEngineView>
#include <QApplication>
#include <QFile>
#include "JsWebPage.h"

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    auto view = new QWebEngineView;

    view->setPage(new JsWebPage);

    QFile file(":/Example.html");
    if (!file.open(QIODevice::ReadOnly)) return false;
    QString content = QString::fromUtf8(file.readAll());
    view->setHtml(content);
    file.close();
    view->show();
    return app.exec();
}

JsWebPage.h

#pragma once

#include <QWebEnginePage>

class JsWebPage : public QWebEnginePage {
    Q_OBJECT

public:
    JsWebPage();

    void javaScriptConsoleMessage(JavaScriptConsoleMessageLevel level, const QString &message, int lineNumber, const QString &sourceID) override;

signals:
    void extractKeys();

public slots:
    void onKeysExtracted(QStringList keys);

private:

    QWebChannel* mWebChannel = nullptr;
};

JsWebPage.cpp

#include "JsWebPage.h"
#include <QWebChannel>
#include <QWebEngineSettings>
#include <QWebEnginePage>
#include <QDebug>

void JsWebPage::javaScriptConsoleMessage(JavaScriptConsoleMessageLevel level, const QString &message, int lineNumber, const QString &sourceID)
{
    qDebug() << QString("Javascript Console: Line: %1, Source: %2, %3").arg(lineNumber).arg(sourceID).arg(message);
}

void JsWebPage::onKeysExtracted(QStringList keys)
{
    qDebug() << keys;
}

JsWebPage::JsWebPage()
{
    settings()->setAttribute(QWebEngineSettings::LocalContentCanAccessRemoteUrls, true);
    settings()->setAttribute(QWebEngineSettings::JavascriptCanOpenWindows, true);

    mWebChannel=new QWebChannel;
    mWebChannel->registerObject(QString("qtObject"), this);
    setWebChannel(mWebChannel); 
}

Example.html

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta http-equiv="content-type" content="text/html; charset=UTF-8"></meta>
    <script src="qrc:///jquery.js" type="text/javascript"></script>
    <script src="qrc:///qwebchannel.js" type="text/javascript"></script>
    <script src="qrc:///example.js" type="text/javascript"></script>
    <title>Template</title>
</head>
<body id="Body"> 
    Example HTML
    <div id="myTag1"></div>
    <div id="myTag2"></div>
</body>
</html>

example.js

var webChannel=new QWebChannel(
    qt.webChannelTransport, 
    function(channel) {
        extractKeys();
    }
);

function extractKeys() {
    try {
        var valueTable = [];
        $("[id]").each(
            function () {
                valueTable.push($(this).attr("id"));
            }
        );
        webChannel.objects.qtObject.onKeysExtracted(valueTable);
    }
    catch (e) {
        console.log(e);
    }
}

resource.qrc

<!DOCTYPE RCC><RCC version="1.0">
<qresource>
<file>Example.html</file>
<file>example.js</file>
<file>qwebchannel.js</file>
<file>jquery.js</file>
</qresource>
</RCC>

Upvotes: 1

Related Questions