原文链接
https://www.cnblogs.com/myboat/p/14168328.html
使用WebEngineView与WebChannel,实现QT与html数据传输和事件响应。
(1)使用QMake时,在pro文件中加入
QT += webchannel webengine
(2)使用CMake时,在CMakeList.txt中加入
find_package(Qt5 COMPONENTS Widgets WebEngineWidgets WebChannel REQUIRED) target_link_libraries(webEngineTest PRIVATE Qt5::Widgets Qt5::WebEngineWidgets Qt5::WebChannel)
注意在QMake中不需要大小写区分,而CMake时就需要将大小写分开。
在MainWindow.h中加入两个变量
QWebEngineView *webView = nullptr;
QWebChannel *webChannel = nullptr;
在MainWindow.cpp的相关函数中(可以是MainWindow的构造函数,也可以是菜单响应函数中)加入webEngineView
webView = new QWebEngineView(this);
QString fpath = QCoreApplication::applicationDirPath();
webView->load( QUrl("file:///" + fpath + "/test.html"));
webView->show();
加入webChannel
webChannel = new QWebChannel;
webView->page()->setWebChannel(webChannel);
一定要注意:必须将webChannel设置为webEngineVIew的webChannel,才能通过webChannel与网页进行通信。
与html交互的主要工作需要一个QT类实现,这个类需要通过webChannel进行注册才能由js访问
下面是可以由js访问的WebClass类
#ifndef WEBCLASS_H
#define WEBCLASS_H
#include <QObject>
#include <QMessageBox>
class WebClass : public QObject
{
Q_OBJECT
//Q_PROPERTY(QString content MEMBER m_content)
Q_PROPERTY(QString content MEMBER m_content NOTIFY contentChanged) //该属性可由页面访问
public:
WebClass(){};
QString getContent(){return m_content;}
signals:
void contentChanged(QString nc);
public slots:
void jscallme(const QString &text) //该函数是页面端调用的
{
QMessageBox::information(NULL, "jscallme", text);
}
void jscallme() //该函数是页面端调用的
{
QMessageBox::information(NULL, "jscallme", m_content);
}
private:
QString m_content;
};
#endif // WEBCLASS_H
它有几个约束:
(1)必须由QObject继承,并且添加了Q_OBJECT宏
(2)必须将JS可访问的函数设置为public slots
(3)如果JS需要访问其成员变量,除定义该变量(QString m_content;)外需要用Q_PROPERTY宏
Q_PROPERTY(QString content MEMBER m_content NOTIFY contentChanged) //该属性可由页面访问
其中content 为JS访问的变量名,m_content为本类的变量名;
NOTIFY contentChanged可以缺省,它表示当在C++变量发生变化时的发出的消息,该消息可由JS响应。
为了发送该消息,我们必须在WebClass类中声明一个信号函数,即
signals:
void contentChanged(QString nc);
这个函数只能声明不能实现(它会由MOC编译实现),而响应函数由JS中指定:
webobj.contentChanged.connect(updateattribute);
这里webobj是WebClass注册后,在页面中的一个实例,contentChanged则是WebClass中的contentChanged信号函数,这一行将contentChanged信号与响应函数updateattribute进行关联,从而一旦C++对象中m_content成员的值由webobject->setProperty("content", v)函数进行修改(必须使用此函数,其他函数修改了不会引起信号)时,JS就会响应,它可以更新页面中相关元素的值。
(1)加入类成员。我们这里的主类为MainWindow类
WebClass *webobj = nullptr;
(2)注册该成员
在主类的相应位置,注册该成员变量。我们在主类的构造函数中加入:
webChannel = new QWebChannel;
webobj = new WebClass();
webChannel->registerObject("webobj", webobj);
webView->page()->setWebChannel(webChannel);
注意,这里是在webEngineView设置webChannel之前完成了注册交互实体的工作。其中 webChannel->registerObject("webobj", webobj); 函数就是注册函数,"webobj"是JS访问该对象时的对象名, 后面的webobj则是C++对象实例的指针。通过WebChannel的注册工作,WebChannel就知道了该类的结构,并代理JS完成函数调用和成员访问。
该页面中包含有一个特殊的JS文件,它是Qt目录"C:\Qt\Qt5.12.1\Examples\Qt-5.12.1\webchannel\shared"下的qwebchannel.js文件,要使用QWebChannel,必须引用该文件。因为QWebChannel
是由该文件定义,所以必须首先加载该文件。我们将该文件拷贝到html目录下(当然也可以是需要的其他目录),然后编辑例子文件:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
</head>
<body>
<script src="qwebchannel.js"></script>
<script type="text/javascript">
var webChannel = new QWebChannel(qt.webChannelTransport, //这里的webChannel是全局的变量,可以在其它位置访问
function(channel){
var webobj = channel.objects.webobj;
window.foo = webobj; //将此webobj赋给了window.foo,则可以在其他函数中访问该对象(其中foo是任意合法名称,表示给window增加了一个成员)
webobj.content = ‘sdfef中文‘;
webobj.jscallme();
document.getElementById("ctext").innerHTML = webobj.content;
webobj.contentChanged.connect(updateattribute);
});
var updateattribute=function(text)
{
//document.write(text);
//var webobj = webChannel.objects.webobj; //访问全局变量webChannel
alert(window.foo.content); //这里可以访问全局的window.foo,它就是我们注册的webobj
document.getElementById("mytext").innerHTML = text;
//alert(webobj.content);
}
</script>
<p id="mytext">This is my first html</p>
<p id="ctext"></p>
</body>
</html>
(1)页面文件首先包含了qwebchannel.js,它是使用WebChannel的基础;
(2)页面创建了一个QWebChannel实例,作为全局变量,这样别的JS代码也可以访问它了;
(3)QWebChannel实例的构造函数有两个参数,第一个不清楚,且保持原样。第二个参数是一个回调函数(姑且这样称),该函数在创建时调用,函数的参数就是本次创建的QWebChannel实例;
(4)在var webobj = channel.objects.webobj;一行中,channel.objects.webobj是我们在QT主类中注册的webobj对象实例,我们将它赋值给了一个变量,以便于引用;
(5)下面一行window.foo = webobj,则表示为全局的window实例增加一个成员foo(这个名字可以自己定),它是webobj的别名,这样我们可以在别的地方访问webobj;
(6)下面三行是对注册实例webobj的访问
webobj.content = ‘sdfef中文‘; //访问在QT中定义为content的成员变量,其在QT中的成员是m_content;
webobj.jscallme(); //调用该对象的public slots函数
document.getElementById("ctext").innerHTML = webobj.content; //使用该对象的成员为html元素赋值
(7)最后一行最为关键,它将QT的信号与JS的响应函数关联,从而响应QT发出的信号
webobj.contentChanged.connect(updateattribute);
(8)其中updateattribute是JS定义的响应函数名称,下面是它的定义:
var updateattribute=function(text)
{
//document.write(text);
//var webobj = webChannel.objects.webobj; //访问全局变量webChannel
alert(window.foo.content); //这里可以访问全局的window.foo,它就是我们注册的webobj
document.getElementById("mytext").innerHTML = text;
}
需要注意的是,这个响应函数应当与QT中C++的信号函数在参数上保持一致。
可以看出,这里可以访问全局成员window.foo
//main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
5.2 mainwindow类
//mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QtWebEngineWidgets/QWebEngineView>
#include "webclass.h"
#include <QtWebChannel/QWebChannel>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void on_pushButton_clicked();
private:
Ui::MainWindow *ui;
QWebEngineView *webView = nullptr;
QWebChannel *webChannel = nullptr;
WebClass *webobj = nullptr;
};
#endif // MAINWINDOW_H
//mainwindow.cpp
#include "mainwindow.h"
#include "./ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
webView = new QWebEngineView(this);
QString fpath = QCoreApplication::applicationDirPath();
webView->load( QUrl("file:///" + fpath + "/test.html"));
webView->show();
webView->move(0, 60);
webView->resize(this->size().width(), this->size().height() - 60);
//mainLayout->addWidget(webView);
webChannel = new QWebChannel;
webobj = new WebClass();
webChannel->registerObject("webobj", webobj);
webView->page()->setWebChannel(webChannel);
}
MainWindow::~MainWindow()
{
delete ui;
}
int times = 1;
void MainWindow::on_pushButton_clicked()
{
QString t = QString::number(times++);
QString text = "1234567";
text.append(t);
QVariant v(text);
qDebug() << v.typeName() << endl;
webobj->setProperty("content", v);
qDebug() << webobj->getContent() << endl;
}
本类中,on_pushButton_clicked是mainWindow中加入的一个pushbutton的响应函数,这个函数调用webobj->setProperty("content", v);来改变名为content的成员变量的值,并发送contentChanged信号,由页面响应。这里"content"是m_content在JS中的名称,本函数实际上是调用了JS来改变对象webobj的成员变量值,然后触发相关事件响应。
//webclass.h
#ifndef WEBCLASS_H
#define WEBCLASS_H
#include <QObject>
#include <QMessageBox>
class WebClass : public QObject
{
Q_OBJECT
//Q_PROPERTY(QString content MEMBER m_content)
Q_PROPERTY(QString content MEMBER m_content NOTIFY contentChanged) //该属性可由页面访问
public:
WebClass();
QString getContent(){return m_content;}
signals:
void contentChanged(QString nc);
public slots:
void jscallme(const QString &text) //该函数是页面端调用的
{
QMessageBox::information(NULL, "jscallme", text);
}
void jscallme() //该函数是页面端调用的
{
QMessageBox::information(NULL, "jscallme", m_content);
}
private:
QString m_content;
};
#endif // WEBCLASS_H
//webclass.cpp
#include "webclass.h"
WebClass::WebClass()
{
}
WebClass有两个slot可供JS调用,其中一个是带参的。这个参数前面的const是不能去掉的,否则会产生错误:Could not convert argument QJsonValue(string, “sd”) to target type . 可见,WebChannel的通信是通过JSON进行的。
见4.
cmake_minimum_required(VERSION 3.5)
project(ScenarioBuilder LANGUAGES CXX)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_PREFIX_PATH $ENV{QT5_14_0_vs2017_64})
# QtCreator supports the following variables for Android, which are identical to qmake Android variables.
# Check http://doc.qt.io/qt-5/deployment-android.html for more information.
# They need to be set before the find_package(Qt5 ...) call.
#if(ANDROID)
# set(ANDROID_PACKAGE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/android")
# if (ANDROID_ABI STREQUAL "armeabi-v7a")
# set(ANDROID_EXTRA_LIBS
# ${CMAKE_CURRENT_SOURCE_DIR}/path/to/libcrypto.so
# ${CMAKE_CURRENT_SOURCE_DIR}/path/to/libssl.so)
# endif()
#endif()
find_package(Qt5 COMPONENTS Widgets WebEngineWidgets WebChannel REQUIRED)
if(ANDROID)
add_library(ScenarioBuilder SHARED
main.cpp
mainwindow.cpp
mainwindow.h
mainwindow.ui
interactor.h
interactor.cpp
mywebview.cpp
mywebview.h
)
else()
add_executable(ScenarioBuilder
main.cpp
mainwindow.cpp
mainwindow.h
mainwindow.ui
interactor.h
interactor.cpp
mywebview.cpp
mywebview.h
)
endif()
target_link_libraries(ScenarioBuilder PRIVATE Qt5::Widgets Qt5::WebEngineWidgets Qt5::WebChannel)
QT中使用QWebEngineView和QWebChannel与HTML+JS进行互操作
原文:https://www.cnblogs.com/woshizhizhong-tech/p/14856245.html