QWebEngine_C++_交互
参考网址:http://blog.csdn.net/liuyez123/article/details/50509788
ZC: 该文章里面的:
“
<ahref="javascript:document.getElementByIdx_x("myLabel").setText("通过JavaScript访问Qt对象");"mce_href="javascript:document.getElementByIdx_x("myLabel").setText("通过JavaScript访问Qt对象");">点击访问Qt对象</a>
”
貌似使用的机制是 "WebKik & QWebPluginFactory",貌似在WebEngine中暂时(至20160926为止)还没有...
https://www.google.com.hk 搜索 “getElementByIdx_x("myLabel").setText QT”得到 仅有的4个结果:
http://www.programgo.com/article/54841979099/
http://blog.hehehehehe.cn/a/10652.htm
http://www.360doc.com/content/16/0405/18/13792507_548095953.shtml
http://blog.inet198.cn/?liuyez123/article/details/50509788
1、我的代码:
1.1、pro
QT += core gui \
webenginewidgets webchannel
CONFIG += c++11
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TARGET = QWebEngine_JiaoHu
TEMPLATE = app
SOURCES += main.cpp\
mainwindow.cpp \
document.cpp \
previewpage.cpp
HEADERS += mainwindow.h \
document.h \
previewpage.h
FORMS += mainwindow.ui
1.2、main.cpp ==> 默认
1.3、mainwindow.h 和 mainwindow.cpp
1.3.1、mainwindow.h
#ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> #include "document.h" namespace Ui { class MainWindow; } class MainWindow : public QMainWindow { Q_OBJECT public: explicit MainWindow(QWidget *parent = 0); ~MainWindow(); private slots: void on_pushButton_clicked(); private: Ui::MainWindow *ui; private: bool isModified() const; Document m_content; }; #endif // MAINWINDOW_H
1.3.2、mainwindow.cpp
#include "mainwindow.h" #include "ui_mainwindow.h" #include "previewpage.h" #include <QWebChannel> MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); // *** PreviewPage *page = new PreviewPage(this); ui->preview->setPage(page); m_content.setUi(ui); QWebChannel *channel = new QWebChannel(this); channel->registerObject(QStringLiteral("content"), &m_content); page->setWebChannel(channel); //ui->preview->setUrl(QUrl("qrc:/index.html")); ui->preview->setUrl(QUrl::fromUserInput("F:/ZZ_Qt5/QWebEngine_JiaoHu/html/index.html")); ui->editor->setPlainText("hello...\n"); } MainWindow::~MainWindow() { delete ui; } bool MainWindow::isModified() const { return ui->editor->document()->isModified(); } void MainWindow::on_pushButton_clicked() { m_content.setSendTextText(ui->lineEdit->text()); }
1.4、previewpage.h 和 previewpage.cpp ==> 用于在 打开网页的时候,做一下处理(相当于过滤层) :如果网址 如何要求就通过(打开网页),如果 网址不符合要求 就拒绝(不打开网页)
1.4.1、previewpage.h
#ifndef PREVIEWPAGE_H #define PREVIEWPAGE_H #include <QWebEnginePage> class PreviewPage : public QWebEnginePage { Q_OBJECT public: explicit PreviewPage(QObject *parent = nullptr) : QWebEnginePage(parent) {} protected: bool acceptNavigationRequest(const QUrl &url, NavigationType type, bool isMainFrame); }; #endif // PREVIEWPAGE_H
1.4.2、previewpage.cpp
#include "previewpage.h" #include <QDesktopServices> bool PreviewPage::acceptNavigationRequest(const QUrl &url, QWebEnginePage::NavigationType /*type*/, bool /*isMainFrame*/) { // Only allow qrc:/index.html. if (url.scheme() == QString("qrc")) return true; //QDesktopServices::openUrl(url); // ZC: 这一句代码,会打开 本地的默认浏览器,显示相关的网页 //return false; // ZC: 返回 false的话,就不打开 相应的网页 return true; // ZC: 返回 false的话,就打开 相应的网页 }
1.5、document.h 和 document.cpp ==> 这个是 JS和C++交互的类
1.5.1、document.h
#ifndef DOCUMENT_H #define DOCUMENT_H #include <QObject> #include <QString> #include "ui_mainwindow.h" namespace Ui { class MainWidget; } class Document : public QObject { Q_OBJECT Q_PROPERTY(QString text MEMBER s_text NOTIFY sendText) public: explicit Document(QObject *parent = nullptr) : QObject(parent) {} void setSendTextText(const QString &text); void setUi(Ui::MainWindow *ui); public slots: void receiveText(const QString &r_text); signals: void sendText(const QString &text); private: void displayMessage(const QString &message); QString s_text; QString recieve_text; Ui::MainWindow *mainUi; }; #endif // DOCUMENT_H
1.5.2、document.cpp
#include "document.h" #include <QDebug> void Document::setSendTextText(const QString &text) { s_text = text; qDebug() << "Document::setSendTextText" << s_text; emit sendText(s_text); } void Document::displayMessage(const QString &message) { mainUi->editor->appendPlainText(message); } /*! This slot is invoked from the HTML client side and the text displayed on the server side. */ void Document::receiveText(const QString &r_text) { displayMessage(QObject::tr("Received message: %1").arg(r_text)); } void Document::setUi(Ui::MainWindow *ui) { mainUi = ui; }
1.6、网页 源码
1.6.1、index.html
<!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="./qwebchannel.js"></script> <script type="text/javascript"> //BEGIN SETUP function output(message) { var output = document.getElementById("output"); output.innerHTML = output.innerHTML + message + "\n"; } window.onload = function() { output("setting up QWebChannel."); new QWebChannel(qt.webChannelTransport, function(channel) { // make dialog object accessible globally var content = channel.objects.content; document.getElementById("send").onclick = function() { var input = document.getElementById("input"); var text = input.value; if (!text) { return; } output("Sent message: " + text); input.value = ""; content.receiveText(text); } content.sendText.connect(function(message) { output("Received message: " + message); }); content.receiveText("Client connected, ready to send/receive messages!"); output("Connected to WebChannel, ready to send/receive messages!"); }); } //END SETUP </script> <style type="text/css"> html { height: 100%; width: 100%; } #input { width: 400px; margin: 0 10px 0 0; } #send { width: 90px; margin: 0; } #output { width: 500px; height: 300px; } </style> </head> <body> <textarea id="output"></textarea><br /> <input id="input" /><input type="submit" id="send" value="Send" onclick="javascript:click();" /> </body> </html>
1.6.2、qwebchannel.js
/**************************************************************************** ** ** Copyright (C) 2015 The Qt Company Ltd. ** Copyright (C) 2014 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Milian Wolff <milian.wolff@kdab.com> ** Contact: http://www.qt.io/licensing/ ** ** This file is part of the QtWebChannel module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL21$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see http://www.qt.io/terms-conditions. For further ** information use the contact form at http://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 or version 3 as published by the Free ** Software Foundation and appearing in the file LICENSE.LGPLv21 and ** LICENSE.LGPLv3 included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** As a special exception, The Qt Company gives you certain additional ** rights. These rights are described in The Qt Company LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ "use strict"; var QWebChannelMessageTypes = { signal: 1, propertyUpdate: 2, init: 3, idle: 4, debug: 5, invokeMethod: 6, connectToSignal: 7, disconnectFromSignal: 8, setProperty: 9, response: 10, }; var QWebChannel = function(transport, initCallback) { if (typeof transport !== "object" || typeof transport.send !== "function") { console.error("The QWebChannel expects a transport object with a send function and onmessage callback property." + " Given is: transport: " + typeof(transport) + ", transport.send: " + typeof(transport.send)); return; } var channel = this; this.transport = transport; this.send = function(data) { if (typeof(data) !== "string") { data = JSON.stringify(data); } channel.transport.send(data); } this.transport.onmessage = function(message) { var data = message.data; if (typeof data === "string") { data = JSON.parse(data); } switch (data.type) { case QWebChannelMessageTypes.signal: channel.handleSignal(data); break; case QWebChannelMessageTypes.response: channel.handleResponse(data); break; case QWebChannelMessageTypes.propertyUpdate: channel.handlePropertyUpdate(data); break; default: console.error("invalid message received:", message.data); break; } } this.execCallbacks = {}; this.execId = 0; this.exec = function(data, callback) { if (!callback) { // if no callback is given, send directly channel.send(data); return; } if (channel.execId === Number.MAX_VALUE) { // wrap channel.execId = Number.MIN_VALUE; } if (data.hasOwnProperty("id")) { console.error("Cannot exec message with property id: " + JSON.stringify(data)); return; } data.id = channel.execId++; channel.execCallbacks[data.id] = callback; channel.send(data); }; this.objects = {}; this.handleSignal = function(message) { var object = channel.objects[message.object]; if (object) { object.signalEmitted(message.signal, message.args); } else { console.warn("Unhandled signal: " + message.object + "::" + message.signal); } } this.handleResponse = function(message) { if (!message.hasOwnProperty("id")) { console.error("Invalid response message received: ", JSON.stringify(message)); return; } channel.execCallbacks[message.id](message.data); delete channel.execCallbacks[message.id]; } this.handlePropertyUpdate = function(message) { for (var i in message.data) { var data = message.data[i]; var object = channel.objects[data.object]; if (object) { object.propertyUpdate(data.signals, data.properties); } else { console.warn("Unhandled property update: " + data.object + "::" + data.signal); } } channel.exec({type: QWebChannelMessageTypes.idle}); } this.debug = function(message) { channel.send({type: QWebChannelMessageTypes.debug, data: message}); }; channel.exec({type: QWebChannelMessageTypes.init}, function(data) { for (var objectName in data) { var object = new QObject(objectName, data[objectName], channel); } // now unwrap properties, which might reference other registered objects for (var objectName in channel.objects) { channel.objects[objectName].unwrapProperties(); } if (initCallback) { initCallback(channel); } channel.exec({type: QWebChannelMessageTypes.idle}); }); }; function QObject(name, data, webChannel) { this.__id__ = name; webChannel.objects[name] = this; // List of callbacks that get invoked upon signal emission this.__objectSignals__ = {}; // Cache of all properties, updated when a notify signal is emitted this.__propertyCache__ = {}; var object = this; // ---------------------------------------------------------------------- this.unwrapQObject = function(response) { if (response instanceof Array) { // support list of objects var ret = new Array(response.length); for (var i = 0; i < response.length; ++i) { ret[i] = object.unwrapQObject(response[i]); } return ret; } if (!response || !response["__QObject*__"] || response.id === undefined) { return response; } var objectId = response.id; if (webChannel.objects[objectId]) return webChannel.objects[objectId]; if (!response.data) { console.error("Cannot unwrap unknown QObject " + objectId + " without data."); return; } var qObject = new QObject( objectId, response.data, webChannel ); qObject.destroyed.connect(function() { if (webChannel.objects[objectId] === qObject) { delete webChannel.objects[objectId]; // reset the now deleted QObject to an empty {} object // just assigning {} though would not have the desired effect, but the // below also ensures all external references will see the empty map // NOTE: this detour is necessary to workaround QTBUG-40021 var propertyNames = []; for (var propertyName in qObject) { propertyNames.push(propertyName); } for (var idx in propertyNames) { delete qObject[propertyNames[idx]]; } } }); // here we are already initialized, and thus must directly unwrap the properties qObject.unwrapProperties(); return qObject; } this.unwrapProperties = function() { for (var propertyIdx in object.__propertyCache__) { object.__propertyCache__[propertyIdx] = object.unwrapQObject(object.__propertyCache__[propertyIdx]); } } function addSignal(signalData, isPropertyNotifySignal) { var signalName = signalData[0]; var signalIndex = signalData[1]; object[signalName] = { connect: function(callback) { if (typeof(callback) !== "function") { console.error("Bad callback given to connect to signal " + signalName); return; } object.__objectSignals__[signalIndex] = object.__objectSignals__[signalIndex] || []; object.__objectSignals__[signalIndex].push(callback); if (!isPropertyNotifySignal && signalName !== "destroyed") { // only required for "pure" signals, handled separately for properties in propertyUpdate // also note that we always get notified about the destroyed signal webChannel.exec({ type: QWebChannelMessageTypes.connectToSignal, object: object.__id__, signal: signalIndex }); } }, disconnect: function(callback) { if (typeof(callback) !== "function") { console.error("Bad callback given to disconnect from signal " + signalName); return; } object.__objectSignals__[signalIndex] = object.__objectSignals__[signalIndex] || []; var idx = object.__objectSignals__[signalIndex].indexOf(callback); if (idx === -1) { console.error("Cannot find connection of signal " + signalName + " to " + callback.name); return; } object.__objectSignals__[signalIndex].splice(idx, 1); if (!isPropertyNotifySignal && object.__objectSignals__[signalIndex].length === 0) { // only required for "pure" signals, handled separately for properties in propertyUpdate webChannel.exec({ type: QWebChannelMessageTypes.disconnectFromSignal, object: object.__id__, signal: signalIndex }); } } }; } /** * Invokes all callbacks for the given signalname. Also works for property notify callbacks. */ function invokeSignalCallbacks(signalName, signalArgs) { var connections = object.__objectSignals__[signalName]; if (connections) { connections.forEach(function(callback) { callback.apply(callback, signalArgs); }); } } this.propertyUpdate = function(signals, propertyMap) { // update property cache for (var propertyIndex in propertyMap) { var propertyValue = propertyMap[propertyIndex]; object.__propertyCache__[propertyIndex] = propertyValue; } for (var signalName in signals) { // Invoke all callbacks, as signalEmitted() does not. This ensures the // property cache is updated before the callbacks are invoked. invokeSignalCallbacks(signalName, signals[signalName]); } } this.signalEmitted = function(signalName, signalArgs) { invokeSignalCallbacks(signalName, signalArgs); } function addMethod(methodData) { var methodName = methodData[0]; var methodIdx = methodData[1]; object[methodName] = function() { var args = []; var callback; for (var i = 0; i < arguments.length; ++i) { if (typeof arguments[i] === "function") callback = arguments[i]; else args.push(arguments[i]); } webChannel.exec({ "type": QWebChannelMessageTypes.invokeMethod, "object": object.__id__, "method": methodIdx, "args": args }, function(response) { if (response !== undefined) { var result = object.unwrapQObject(response); if (callback) { (callback)(result); } } }); }; } function bindGetterSetter(propertyInfo) { var propertyIndex = propertyInfo[0]; var propertyName = propertyInfo[1]; var notifySignalData = propertyInfo[2]; // initialize property cache with current value // NOTE: if this is an object, it is not directly unwrapped as it might // reference other QObject that we do not know yet object.__propertyCache__[propertyIndex] = propertyInfo[3]; if (notifySignalData) { if (notifySignalData[0] === 1) { // signal name is optimized away, reconstruct the actual name notifySignalData[0] = propertyName + "Changed"; } addSignal(notifySignalData, true); } Object.defineProperty(object, propertyName, { configurable: true, get: function () { var propertyValue = object.__propertyCache__[propertyIndex]; if (propertyValue === undefined) { // This shouldn't happen console.warn("Undefined value in property cache for property \"" + propertyName + "\" in object " + object.__id__); } return propertyValue; }, set: function(value) { if (value === undefined) { console.warn("Property setter for " + propertyName + " called with undefined value!"); return; } object.__propertyCache__[propertyIndex] = value; webChannel.exec({ "type": QWebChannelMessageTypes.setProperty, "object": object.__id__, "property": propertyIndex, "value": value }); } }); } // ---------------------------------------------------------------------- data.methods.forEach(addMethod); data.properties.forEach(bindGetterSetter); data.signals.forEach(function(signal) { addSignal(signal, false); }); for (var name in data.enums) { object[name] = data.enums[name]; } } //required for use with nodejs if (typeof module === 'object') { module.exports = { QWebChannel: QWebChannel }; }
2、Z01.html
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <script type="text/javascript" > <!-- window.onload = function() { }; --> </script> </head> <body> <ahref="javascript:document.getElementByIdx_x("myLabel").setText("通过JavaScript访问Qt对象");"mce_href="javascript:document.getElementByIdx_x("myLabel").setText("通过JavaScript访问Qt对象");">点击访问Qt对象</a> </body> </html>
3、