Создать прослушиватель для изменений Dom в Qml WebEngineView (Mutation Observer) - PullRequest
1 голос
/ 09 июля 2019

У меня есть html-страница, содержащая это:

<div id="ajaxloader" style="display: none;">
        <div>
          hello world
        </div>
</div>

я могу прочитать его отображаемое значение с помощью document.getElementById('ajaxloader').style.display
Я хочу получать уведомления, когда его отображение изменяется на block или none.
В настоящее время я использую тупое решение с использованием таймера:

Timer{
        running : true
        repeat : true
        interval: 500
        onTriggered: {
            var js = "document.getElementById('ajaxloader').style.display"
            webView.runJavaScript(js,x=>{
                  if(x=="block"){
                       // we catch the change but its really bad solution                   
                  }
            })
        }
    }  

Я ищу способ отследить такого рода изменения в DOM, что-то называется Mutation Observer, но я не уверенкак внедрить его в WebEngineView QML.
все, что мне нужно, это способ отследить изменения, произошедшие в WebEngineView, или событие, чтобы отловить CRUD, происходящие в движке или в любом случае лучше, чем этот таймер!
UPDATE:
например, у нас есть веб-двигатель, который переходит на google.com, и после завершения загрузки он меняет текст поиска на «hello world», мы хотим отследить это изменение без использования таймера, на реальных веб-сайтах это изменение фактически происходит сФункции CRUD (запросы ajax) или другие способы:

WebChannel{
    id:_channel
}
WebEngineView{
    id:webEngine
    height: parent.height
    width: parent.width
    webChannel: _channel
    url : "www.google.com"
    onNewViewRequested: {
        request.openIn(webEngine)
    }
    objectName: "webView"
    profile.httpCacheType: WebEngineProfile.NoCache

    onLoadingChanged: {
        if(loadRequest.status == WebEngineView.LoadSucceededStatus){
            var js = "document.querySelector('input[role=combobox]').value = 'hello world'"
            webEngine.runJavaScript(js,y=>{})
        }
    }

}  

не забудьте инициализировать движок в c ++, без него не получится: QtWebEngine::initialize(); и другие вещи, импортируемые plнам нужно добавить это в файл pro QT += webengine webengine-private webenginecore webenginecore-private
сейчас, если я использую метод таймера, который я хочу отложить в сторону, он должен выглядеть следующим образом:

Timer{
    running : true
    repeat : true
    interval : 500
    onTriggered:{
         var js = "document.querySelector('input[role=combobox]').value"
         webEngine.runJavaScript(js,y=>{console.log(y)});
         // now i have to check y to see if its equals to hello world or what ever which is really bad idea to use a timer here
    }
}  

например, вы можете наблюдать изменения вВвод Google, как это:

var targetNode = document.querySelector('input[role=combobox]')
targetNode.oninput = function(e){this.setAttribute('value',targetNode.value)}
var config = { attributes: true, childList: true, subtree: true };
var callback = function(mutationsList, observer) {
    for(var mutation of mutationsList) {
        if (mutation.type == 'childList') {
            console.log('A child node has been added or removed.');
        }
        else if (mutation.type == 'attributes') {
            console.log('The ' + mutation.attributeName + ' attribute was modified.');
        }
    }
};

// Create an observer instance linked to the callback function
var observer = new MutationObserver(callback);

// Start observing the target node for configured mutations
observer.observe(targetNode, config); 

Ответы [ 2 ]

3 голосов
/ 13 июля 2019

Стратегия состоит в том, чтобы загрузить qwebchannel.js при загрузке страницы, а затем внедрить другой скрипт, который устанавливает соединение с экспортируемым объектом, используя Qt WebChannel

Нет необходимости создавать QObject в C ++, вы можете использовать QtObject.

main.cpp

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QtWebEngine>

int main(int argc, char *argv[])
{
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);

    QGuiApplication app(argc, argv);
    QtWebEngine::initialize();

    QString JsWebChannel;
    QFile file(":///qtwebchannel/qwebchannel.js");
    if(file.open(QIODevice::ReadOnly))
        JsWebChannel = file.readAll();

    QQmlApplicationEngine engine;
    engine.rootContext()->setContextProperty("JsWebChannel", JsWebChannel);
    const QUrl url(QStringLiteral("qrc:/main.qml"));
    QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
                     &app, [url](QObject *obj, const QUrl &objUrl) {
        if (!obj && url == objUrl)
            QCoreApplication::exit(-1);
    }, Qt::QueuedConnection);
    engine.load(url);

    return app.exec();
}

main.qml

import QtQuick 2.12
import QtQuick.Window 2.12
import QtWebChannel 1.13
import QtWebEngine 1.1

Window {
    visible: true
    width: 640
    height: 480
    title: qsTr("Hello World")

    QtObject{
        id: listener
        WebChannel.id: "listener"
        property string text: ""
        onTextChanged: console.log(text) 

        property string script: "
            var listener;
            new QWebChannel(qt.webChannelTransport, function (channel) {
                listener = channel.objects.listener;

                var targetNode = document.querySelector('input[role=combobox]')
                targetNode.oninput = function(e){this.setAttribute('value',targetNode.value)}
                var config = { attributes: true, childList: true, subtree: true };
                var callback = function(mutationsList, observer) {
                    for(var mutation of mutationsList) {
                        if (mutation.type == 'childList') {
                            console.log('A child node has been added or removed.');
                        }
                        else if (mutation.type == 'attributes') {
                            console.log('The ' + mutation.attributeName + ' attribute was modified.');
                            listener.text = targetNode.value; // update qproperty
                        }
                    }
                };

                // Create an observer instance linked to the callback function
                var observer = new MutationObserver(callback);

                // Start observing the target node for configured mutations
                observer.observe(targetNode, config); 
            });
        "
    }
    WebChannel{
        id: channel
        registeredObjects: [listener]
        function inject(){
            webEngine.runJavaScript(JsWebChannel);
            webEngine.runJavaScript(listener.script);
        }
    }

    WebEngineView {
        id:webEngine
        url : "https://www.google.com/"
        profile.httpCacheType: WebEngineProfile.NoCache
        webChannel: channel
        anchors.fill: parent
        onLoadingChanged: {
            if(loadRequest.status == WebEngineView.LoadSucceededStatus){
                console.log("Page has successfully loaded")
                channel.inject()
            }
        }
    }
}
2 голосов
/ 12 июля 2019

Если вы хотите, чтобы JavaScript вызывал функции C ++, вам необходимо использовать модуль Qt WebChannel.

Это позволит вам представить любой производный объект QObject в javascript, что позволит вам вызывать любые функции слотов для экспонированного объекта из javascript.

В вашем случае вы можете использовать в javascript MutationObserver и использовать функцию C ++ в качестве обратного вызова.

Вот базовый пример:

// C++
class MyObject: public QObject {
    Q_OBJECT
public slots:
    void mySlot();
}

int main() {
   ...

   // Create the channel and expose the object.
   // This part can also be done in QML
   QWebChannel channel;

   MyObject obj;
   channel.registerObject(QStringLiteral("myobject"), &obj);
   ...
}

// js
myobject = channel.objects.myobject;
observer = new MutationObserver(() => { myobject.mySlot(); }); // Will call MyObject::mySlot() in C++
observer.observe(...);

Если вы хотите больше подробностей, вы можете взглянуть на этот пример: https://doc.qt.io/qt-5/qtwebchannel-standalone-example.html

В документации Qt есть и другие примеры, но имейте в виду, что js-часть Qt WebChannel работает в QWebEngine или любом веб-браузере. Поэтому некоторые примеры, которые вы найдете, могут отличаться от того, что вам нужно сделать.

Также вы не можете передавать любой тип аргумента в вызовах функций через границы C ++ / js

...