如何捕捉和处理 QWebEnginePage 内容的变化?

How to catch and handle QtWebEnginePage content changing?

在 Qt 的 (现已弃用) QWebPage class 中,有一个信号 contentsChanged() that was called whenever the html content of the web page was changed by either the user editing the page or the page being programmatically changed. There doesn't seem to be an equivalent signal in the newer QtWebEnginePage classes. Given that the method of making the web page editable from the Qt application has changed from the QWebPage::setContentEditable method to the native html5 javascript contenteditable,这不是'真的让我感到惊讶。

我的问题: 有没有办法 "listen" 改变 QtWebEnginePage 的内容?这是我现在必须以某种方式从 javascript 处理的事情吗?

请注意,我查看了 Qt 提供的移植信息,但没有发现任何让我印象深刻的相关信息...

不幸的是,QtWebEngine 与其他 Qt 工具的集成比 QtWebKit 差得多。很多 QtWebKit 能够开箱即用的事情只能通过 JavaScript 和 QtWebEngine 的诡计来实现。我能够在自己的项目中解决这个特殊问题,但这并不容易。

首先,我必须通过 QWebChannel 设置 C++ 代码和 JavaScript 代码之间的交互。在这个答案中涵盖的话题太大了,但从好的方面来说,关于这个的文档或多或少是好的:参见 Qt WebChannel JavaScript API and its examples。最后从 C++ 代码你应该能够做像

这样的事情
webChannel->registerObject(QStringLiteral("myObject"), myObject);

其中 webChannel 是指向 QWebChannel 的指针,myObject 是指向 QObject 具有 public 插槽的子类的指针。此操作将允许 JavaScript 代码调用 myObject 的 public 插槽,从而实现 JavaScript 和 C++ 之间的连接。

JavaScript 的 "native" 跟踪 DOM 变化的解决方案是 MutationObserver。它是一个实例化的 JavaScript 对象,目的是监听 DOM 的变化。在每个 DOM 突变中,观察者调用在构造时提供给它的回调函数。为了通知 C++ 代码 DOM 的变化,回调应该调用暴露给 JavaScript 的某个对象的插槽。在此插槽内的 C++ 代码中,您可以根据需要对内容更改做出反应 - 如果 C++ 代码的多个部分可能想要订阅 contentsChanged 信号,为它们提供这样的信号,从插槽中发出myObject.

这是 JavaScript 端的 MutationObserver 设置的简单示例:

var observer = MutationObserver(function(mutations, observer) {
    myObject.onContentChanged();
});

observer.start = function() {
    this.observe(document, {
        subtree: true,
        attributes: true,
        childList: true,
        characterData: true,
        characterDataOldValue: true
    });
}

observer.stop = function() {
    this.disconnect();
}

虽然这种方法的设置比 QtWebKitcontentsChanged 信号要复杂得多,但最终我发现它更灵活,因为 MutationObserver 你可以看到 确切地 在 DOM 中发生了什么变化,并进行一些高级处理:

  1. 忽略您不感兴趣的更改。
  2. 记住之前的 DOM 状态,以便进一步 undo/redo 实施。
  3. 在 DOM 需要通过您手动执行的 JavaScript 代码更改的时间段暂停收听 DOM 更改 - 有时 not[=49 会很方便=] 将此类更改通知 C++ 代码,因为 C++ 代码很难区分由文本编辑引起的更改和由某些脚本执行引起的更改。
  4. 记录确切发生的更改 - 是否添加、删除或更改了某些节点等。