Как использовать Qt WebEngine и QWebChannel?

Я использую новый WebEngine, чтобы играть и учиться. Я пытался найти аналогичные методы, найденные с помощью Qt WebKit: addToJavaScriptWindowObject()

Я обнаружил, что при использовании Qt WebEngine мне приходится использовать QWebChannel для регистрации функций в объекте окна JavaScript. Если это правильно, это приводит меня к следующему вопросу.

Я установил Qt 5.4.0 на свой компьютер. Я заметил, что qwebchannel.js не найден в SDK, установленном на моем компьютере. Я нашел его в источнике Git.

Если у меня есть собственное настольное приложение Qt с QWebEnginePage и QWebEngineView, что мне нужно, чтобы иметь возможность регистрировать функции в объекте окна JavaScript?

Мое настольное приложение автоматически переходит на созданную мной http-страницу. Так что у меня есть доступ к контенту, связанному с QWebEngineView.

Какие шаги нужно предпринять, чтобы я мог сделать эту работу?


person user1185305    schedule 17.02.2015    source источник
comment
ты смог найти решение? Я застрял в той же проблеме, мне нужно какое-то общение с моей веб-страницы на С++ (через js или любым другим способом, если это возможно). Пожалуйста, помогите ..   -  person Pankaj Kushwaha    schedule 09.07.2015


Ответы (4)


В Qt5.6, если вы хотите, чтобы часть C++ и JavaScript взаимодействовали, единственный способ сделать это — использовать QWebChannel в QWebEngineView, как вы сказали. Вы делаете это следующим образом в файле .cpp:

m_pView = new QWebEngineView(this);
QWebChannel * channel = new QWebChannel(page);
m_pView->page()->setWebChannel(channel);
channel->registerObject(QString("TheNameOfTheObjectUsed"), this);

Здесь вы просто говорите, что регистрируете объект с именем TheNameOfTheObjectUsed, который будет доступен на стороне JS. Теперь это часть кода для использования на стороне JS:

new QWebChannel(qt.webChannelTransport, function (channel) {
            // now you retrieve your object
            var JSobject = channel.objects.TheNameOfTheObjectUsed;
        });

Теперь, если вы хотите получить некоторые свойства класса на стороне JS, вам нужен метод на стороне C++, который возвращает строку, целое число, длинное... Вот как это выглядит на стороне C++ , в вашем .h:

Q_INVOKABLE int getInt();
Q_PROPERTY(int myIntInCppSide READ getInt);

И теперь вы получаете вот такое int на стороне JS:

var myIntInJSside= JSobject.myIntInCppSide;

Это очень простое объяснение, и я рекомендую вам посмотреть это видео, которое было очень полезным. мне. Кроме того, вы можете узнать больше об JavaScript API, предоставляемом QWebChannel. , а также документацию по QWebChannel.

Надеюсь, это поможет!

person IAmInPLS    schedule 13.04.2016
comment
Где ты нашел ту часть про qt.webChannelTransport? Я не мог найти его нигде в документации или примере... - person sschober; 12.08.2017
comment
@sschober Вы можете найти его в документе QWebEnginePage: The transport is exposed in the JavaScript world as qt.webChannelTransport, which should be used when using the Qt WebChannel JavaScript API. - person IAmInPLS; 14.08.2017

Я резюмирую ваши вопросы следующим образом:

  1. Нужен ли QWebChannel для регистрации функций JavaScript в WebEngine?
  2. Где я могу найти QWebChannel.js
  3. Как связать JS с C++ и C++ с JS

Во-первых, давайте возьмем простой код для игры:

#include <QApplication>
#include <QDebug>
#include <QWebEngineView>
#include <QWebChannel>

// ... DEFINITIONS HERE

auto main( int argn, char* argv[] )-> int
{
    QApplication app(argn, argv);
    QWebEngineView browser;
    browser.resize(QSize(800,600));
    browser.show();
    browser.load(QUrl("http://www.wikipedia.org"));

    // .. SETUP HERE

    QObject::connect(&browser, &QWebEngineView::loadFinished, [&browser](bool ok)
    { 
        qDebug()<<"Load Finished " << ok;

        // TEST CODE HERE
    ));

    return app.exec();
}

Объяснение: Этот код создает приложение Qt, создает QWebEngineView и устанавливает некоторые минимальные свойства, чтобы сделать его видимым. Страница из «Википедии» loaded внутри, и событие signal/slot подключается для печати некоторого журнала, когда страница наконец загружена.

Как вызывать JS-функции из C++?

Вы можете просто вызвать JS, используя QWebEnginePage::runJavaScript следующим образом. Добавьте этот код в файл TEST CODE HERE.

QString code = QStringLiteral(
R"DELIM(

var links = document.getElementsByTagName('a');
for ( var i=0; i<links.length; ++i)
{
    links[i].style.backgroundColor = 'yellow';
};
)DELIM");
browser.page()->runJavaScript(code, 42);

Объяснение: Этот код выполняет некоторые JS в браузере с идентификатором контекста 42, избегая конфликта с контекстом по умолчанию для страницы с идентификатором 0. Скрипт изменяет цвет фона каждой ссылки на желтый.

Как вызвать C++ из JS?

В этом случае нам нужен механизм QWebChannel для регистрации объектов C++ в JavaScript.

Во-первых, давайте создадим интерфейс C++, вызываемый из JS (в DEFINITION):

class JsInterface: public QObject
{
    Q_OBJECT
public:
    /// Log, for debugging
    Q_INVOKABLE void log(const QString& str) const
    {
        qDebug() << "LOG from JS: " << str;
    }
};
#include "main.moc"

Объяснение: Этот код объявляет и определяет класс QObject с простой функцией log внутри. Важно объявить функцию Q_INVOKABLE, иначе JavaScript не сможет ее найти!. Поскольку объявление находится внутри того же файла, что и остальной код, мы включаем файл auto-moc из QT после (это main.moc, потому что мой файл main.cpp).

Создайте функцию в DEFINITION, которая возвращает содержимое JavaScript QWebChannel.js. Содержимое QWebChannel.js можно найти в вашей библиотеке QT (./5.12.2/Src/qtwebchannel/examples/webchannel/shared/qwebchannel.js или ./Examples/Qt-5.12.2/webchannel/shared/qwebchannel. js). Вы можете загрузить это прямо на своей странице.

В разделе DECLARATION добавить:

QString qWebChannelJs()
{
    return R"DELIMITER(
    // COPY HERE ALL THE FILE
    )DELIMITER";
}

И мы внедряем его в наш код (добавляем в раздел TEST CODE HERE):

browser.page()->runJavaScript(qWebChannelJs(), 42);

Нам нужно настроить QWebChannel на стороне C++ (раздел SETUP):

QWebChannel channel;
JsInterface jsInterface;
browser.page()->setWebChannel(&channel, 42);
channel.registerObject(QString("JsInterface"), &jsInterface);

Объяснение: Мы создаем канал, объект JsInterface и регистрируем их в браузере. Нам нужно использовать тот же идентификатор контекста 42 (но может быть и другое число от 0 до 255).

Наконец, в нашем JS-коде мы получаем доступ к каналу и вызываем функцию интерфейса (добавляем к секции TEST CODE):

QString code2 = QStringLiteral(
R"DELIM(

window.webChannel = new QWebChannel(qt.webChannelTransport, function( channel)
{
    var cpp = channel.objects.JsInterface;
    cpp.log("Hello from JavaScript");
});

)DELIM");
browser.page()->runJavaScript(code2, 42);

Соображения

Стоит отметить, что любой вызов из C++ в JavaScript или из JavaScript в C++ проходит через Inter-Process-Communication (IPC), который является асинхронным. Это означает, что runJavaScript возвращается до выполнения JavaScript и что JavaScript возвращается до выполнения C++ log.

Полный код

#include <QApplication>
#include <QDebug>
#include <QWebEngineView>
#include <QWebChannel>

QString qWebChannelJs()
{
    return R"DELIMITER(
        // TODO INSERT JS code here
    )DELIMITER";
}

class JsInterface: public QObject
{
    Q_OBJECT
public:
    /// Log, for debugging
    Q_INVOKABLE void log(const QString& str) const
    {
        qDebug() << "LOG from JS: " << str;
    }
};
#include "main.moc"

auto main( int argn, char* argv[] )-> int
{
    QApplication app(argn, argv);
    QWebEngineView browser;
    browser.resize(QSize(800,600));
    browser.show();
    browser.load(QUrl("http://www.wikipedia.org"));

    // .. SETUP HERE
    QWebChannel channel;
    JsInterface jsInterface;
    browser.page()->setWebChannel(&channel, 42);
    channel.registerObject(QString("JsInterface"), &jsInterface);

    QObject::connect(&browser, &QWebEngineView::loadFinished, [&browser](bool ok)
    { 
        qDebug()<<"Load Finished " << ok;

        // TEST CODE HERE
        QString code = QStringLiteral(
        R"DELIM(

        var links = document.getElementsByTagName('a');
        for ( var i=0; i<links.length; ++i)
        {
            links[i].style.backgroundColor = 'yellow';
        };

        )DELIM");
        browser.page()->runJavaScript(code, 42);

        browser.page()->runJavaScript(qWebChannelJs(), 42);

        QString code2 = QStringLiteral(
        R"DELIM(                   
        window.webChannel = new QWebChannel(qt.webChannelTransport, function( channel)
        {
            var cpp = channel.objects.JsInterface;
            cpp.log("Hello from JavaScript");
        });

        )DELIM");
        browser.page()->runJavaScript(code2, 42);
    });

    return app.exec();
}

Похожие темы:

Как настроить QWebChannel JS API для использования в QWebEngineView?

Внешняя документация:

https://doc.qt.io/qt-5/qwebengineview.html
https://doc.qt.io/qt-5/qwebchannel.html
https://doc.qt.io/qt-5/qtwebengine-webenginewidgets-contentmanipulation-example.html

person Adrian Maire    schedule 05.06.2020

Qt теперь имеет документацию по этому поводу:

Пример автономного использования Qt WebChannel

Вы должны добавить QWebSocketServer в свое приложение cpp, к которому HTML/Javascript QWebEngineView будет подключаться с помощью WebSocket. Затем используйте QWebChannel для двусторонней связи.

person bendiy    schedule 02.12.2015

Альтернативным и гораздо более простым способом связи со страницей является использование runJavaScript функция:

view->page()->runJavaScript("alert('Hello from C++');");

У него есть свои ограничения: вызов должен быть инициирован со стороны C++, и вы можете получить только синхронный ответ от JS. Но есть и положительная сторона: не требуется никаких изменений базовой веб-страницы.

Доступ к текущей открытой веб-странице можно получить с помощью функции QWebEngineView::page(), как в примере выше. Во время навигации браузер не меняет страницу до тех пор, пока из сети не будет получена следующая, поэтому эта функция возвращает действительный объект страницы в любое время. Но ваш JS все еще может прервать загрузку новой страницы таким образом, что вы окажетесь в document.readyState == 'loading', где дерево DOM еще не построено, а некоторые сценарии на странице, возможно, еще не запущены. В этом случае следует дождаться события DOMContentLoaded.

person user    schedule 03.04.2020