admin管理员组

文章数量:1598599

首先你需要Qt5.6或者以上的版本才有QWebEngine这个模块, 据说5.9之前的有些bug, 所以我建议使用5.9之后的版本,
注意debug模式下QWebEngineView会很卡很占内存,反应很慢,但是release模式下响应速度很快不卡
这里给大家上一个QCefView的链接: https://cefview.github.io/QCefView/.

(1)首先你需要安装Qt

安装qt我就不多说了,大家伙随便找个博客就行, 但是QT6之后是需要版权的,也就是说商用是收费的(QT老奸巨猾,居然不开源了), 这里有个需要注意的地方,安装的时候需要安装这个几个模块才能使用QWebEngine, 直接上一张别的地方偷来的图哈, 若侵权请联系我删除, 注意了大家伙,这里有个问题哈,有可能2015版本是不带有QWebEngine模块的,所以大家伙最好安装2017(下面三个箭头的那个), 其他的按照下图勾选就可以了, 加入内存充足, 可以直接全部勾选,玩意哪天想用别的模块呢, 我之前就用了Qml模块,哈哈

(2)新建工程

新建一个工程,我这里直接新建一个大家伙照做就是


项目名称随意哈


注意我圈出来的地方,这里选中之后你才可以使用Qt设计师界面

之后一直接点下一步就可以了,
我给我创建好的目录大家伙看一下,这里的WebView文件夹是我创建用来存放html文件的地方,大家可以自由发挥,这里我在html文件的同级文件夹下放了一个qwebchannel.js文件,这个文件是QT提供的文件,用于在前端中创建webChannel()对象使用;稍后在我贴出来的HTML文件中你会看到new QWebChannel…的一段js代码

(3)下面我讲贴出所有的打代码,注意哈, 那些创建项目时候生成,并且我未做任何改动的文件我不会贴出来

(3.1)首先在创建完成项目后需要更改.Pro文件,将QWebEngineView需要的模块引入,之后创建一个pushButton,我是直接使用Qt设计师

点击.pro文件进行修改,这里主要是加载模块加载代码如下(network可加可不加,看你需不需要使用网络地址)

QT       += core gui webenginewidgets webchannel network

点击UI文件到达设计师界面

进入之后拖一个pushButon到任意位置(前提是他不会被别的控件遮住,直接鼠标按住然后拖到画布上就可以了)

右键单机pushButton,然后点击转到槽

选中第一个点击的槽,然后点击ok

这个时候点击编辑,然后点击主界面文件,主语一定要点击主界面CPP文件,不然你会停在UI文件的代码便捷界面

这个时候你会发现你的主界面CPP文件中多出来一个代码块,这就是所谓的点击响应的槽函数,当然,你的函数体是空的,我这里是已经写了代码,大家不用担心

(3.2)现在你要创建一个自定义的类,类名有你自己决定我的自定义类文件如下

右键点击项目选择Add New

选择c++类


之后一直点知道出现ok为止;

我创建的类叫做WebClientBridge,一下是他的头文件和源文件内容,注释详细,大家可以读读注释免得踩坑
web_client_bridge.h
/*
 * 提供给void QWebChannel::registerObject(const QString &id, QObject *object)注册的辅助类;
 * 注册完成后在前端可以通过创建js端的QWebChannel对象调用到这个辅助类中的槽函数或者变量
*/
#ifndef WEB_CLIENT_BRIDGE_H
#define WEB_CLIENT_BRIDGE_H
#include <QObject>
#include <QtWebChannel>
#include <QDebug>
#include "developtool.h"

class WebClientBridge : public QObject
{
    Q_OBJECT
    /*
     * 更在类型之后的就是你要注册的公开变量(属性)的名称
     * 注册的属性名称并不一定要和要改变的成员变量名称一致,但写成一致更容易使用
     *
     * 注册公开变量(属性)是给js调用使用的,c++端还是使用的还是原始变量名或者直接调用相关成员函数
     * 注意c++端直接使用的原始变量名更改数据前端是无法检测到更改的,若需要前端也获取到这个数据的变更,需要使用 NOTIFY 绑定信号,并在 WRITE
     * 绑定的函数中主动发射这个 NOTIFY 绑定的信号,发射之后前端会自动处理这个信号并调用 READ 绑定的函数重新获取一次值,以此来完成数据同步
     * 使用Q_PROPERTY宏板是不区分修饰符的(私有,公有,继承三种都是可以的),只要在当前类中有的成员变量和成员函数都可以板顶
     */
    Q_PROPERTY(QString someattribute_test READ  getSomeattribute_1 WRITE setSomeattribute_1 NOTIFY someattribute_1_change )
    Q_PROPERTY(QString someattribute_2 READ getSomeattribute_2 WRITE setSomeattribute_2 NOTIFY someattribute_2_change)
public:
    explicit WebClientBridge(QObject *parent = nullptr);
    QString someattribute_1;

    QString getSomeattribute_2(){
        qDebug()<<"getSomeattribute_2"<<someattribute_2;
        return someattribute_2;
    }
    void setSomeattribute_2(const QString &new_someattribute_2){
        someattribute_2 = new_someattribute_2;
        qDebug()<<"setSomeattribute_2"<<someattribute_2;
        emit someattribute_2_change();
    }
private:
    //本地调试调试端口
    QString str_port;
    //前开发者窗口对象
    DevelopTool* myDevelopTool;

    QString getSomeattribute_1(){
        qDebug()<<"getSomeattribute_1"<<someattribute_1;
        return someattribute_1;
    }
    void setSomeattribute_1(const QString &new_someattribute_1){
        someattribute_1 = new_someattribute_1;
        qDebug()<<"setSomeattribute_1"<<someattribute_1;
        emit someattribute_1_change();
    }

    QString someattribute_2;

public slots:
    /*
     * (1)前端js调用QT代码的函数,一定要写在public slots:中,注意,形参前的" const "不能被省略,
     * 否则会得到以下错误:无法将参数QJsonValue(string, " sd ")转换为目标类型。
     *
     * (2)若是一个函数只是提供给前端修改变量的值,我们则可以不写这个函数,
     * 而是直接通过宏Q_PROPERTY将变量注册为辅助类的公开变量(相当于直接注册为属性),
     * 这里的公开是指js代码和c++代码都可以直接操作这个变量,并不是指3变量的修饰符public
     * 例如你可以将一个private修饰的变量注册为辅助类的公开变量
    */
    void openDevelopTool();//供给前端调用的打开谷歌开发者界面的接口,这个所有项目中都不用改动,只要在前端特定的情况下(例如按下某个按钮)调用就可以
    void jscall(const QString &datafromjs);//测试例子


signals:
    void someattribute_1_change();
    void someattribute_2_change();

};

#endif // WEBCLASS_H



web_client_bridge.cpp
#include "web_client_bridge.h"
#include <qmessagebox.h>

WebClientBridge::WebClientBridge(QObject *parent) :
    QObject(parent),
    myDevelopTool(nullptr),
    str_port("0518"),
    someattribute_1("someattribute_test"),
    someattribute_2(QString::fromLocal8Bit("someattribute_2_的值呀"))
{
    /*
     * 首先需要设置一个环境变量QTWEBENGINE_REMOTE_DEBUGGING来指定调试页面(谷歌浏览器F12打开的界面)所使用的端口号。例如,将7777端口设为调试端口,可在主窗口初始化方法的最开头添加下面的代码:
     * 这个时候控制台会打印这一句话 Remote debugging server started successfully. Try pointing a Chromium-based browser to http://127.0.0.1:7777
     * 这个时候你直接在浏览器上输入 http://127.0.0.1:7777 或者 http://localhost:7777 就可以进入开发者工具页面
     * 当然你要是觉得这样子不舒服,那你可以再创建一个窗口显示一个web页面,但是QWebEngineView::load()中要填上 http://127.0.0.1:7777 或者 http://localhost:7777这个地址
     * 这里我也进行展示下自己创建一个指定调试页面,若是要在前端通过按键打开,则需要在辅助类(WebClientBridge)提供一个给前端调用的函数,函数中就是创建一个调试界面的窗口,前端在检测到按键之后就调用即可
    */
    qputenv("QTWEBENGINE_REMOTE_DEBUGGING",str_port.toStdString().c_str() );
}

//供给前端调用的打开谷歌开发者界面的接口,这个所有项目中都不用改动,只要在前端特定的情况下(例如按下某个按钮)调用就可以
void WebClientBridge::openDevelopTool()
{
    qDebug()<<"openDevelopTool";
    //防止创建多个DebelopTool界面
    if(!myDevelopTool) myDevelopTool = new DevelopTool("http://127.0.0.1:"+str_port);
    myDevelopTool->show();
}

void WebClientBridge::jscall(const QString &datafromjs)
{
    QMessageBox::information(NULL, QString::fromLocal8Bit("client直接变量调用"), QString::fromLocal8Bit("c端的直接变量调用:%1").arg(someattribute_1));
    qDebug()<<"someattribute_2:"+someattribute_2;
    /*
     * 这里使用进行了属性绑定的函数改变变量的值,在前端也能同步这个值
     * 若直接写 this->someattribute_1 = "111111111111111",则前端中的对应值并未改变
     * 测试流程,先点击前端中的 调用c++的按钮,此时你会发现someattribute_1的值被前端更改并打印,
     * 随后查看控制台你会发现qDebug打印出来的 111111111111111,之后点击client界面上的pushButton,
     * 若代码是setSomeattribute_1( "111111111111111"); 则前端弹出会有someattribute_1:111111111111111
     * 若代码是this->someattribute_1 = "111111111111111";则前端弹出会有someattribute_1:xxxxxxxxx(xxxxxxxxx代表不是111111111111111的意思)
     * */
    setSomeattribute_1( "111111111111111");
    qDebug()<<someattribute_1;
    QMessageBox::information(NULL, QString::fromLocal8Bit("jscallme公开变量掉用"), QString::fromLocal8Bit("前端的公开变量调用:%1").arg(datafromjs));
}


developtool.h
/*
 * 用于显示开发者工具的自定义类
 * 主要是创建一个界面,界面上通过QWebEngineView将本地调试界面加载显示出来
*/
#ifndef DEVELOPTOOL_H
#define DEVELOPTOOL_H

#include <QWidget>
#include <QGridLayout>
#include "qwebengineviewplus.h"
class DevelopTool : public QWidget
{
    Q_OBJECT
public:
    /*参数
     * DevelopTool_Url:     QWebEngineView::load()需要的url参数
     * parent:              父窗口指针,默认为空
    */
    explicit DevelopTool(QString DevelopTool_Url,QWidget *parent = nullptr);

signals:

private:
    const QString DevelopTool_Url;
    QWebEngineViewPlus * m_webView ;//创建加载前端界面的对象(继承自Qt的webenginewidgets模块提供)
};

#endif // DEVELOPTOOL_H


developtool.cpp
#include "developtool.h"

DevelopTool::DevelopTool(QString DevelopTool_Url,QWidget *parent) :
    QWidget(parent) ,
    DevelopTool_Url(DevelopTool_Url)
{
    setWindowTitle(QString::fromLocal8Bit("自定义开发者工具窗口"));
    this->setMinimumSize({1000,600});//设置窗口最小大小
    m_webView = new QWebEngineViewPlus(this);//创建加载前端页面的对象
    m_webView->load(QUrl(this->DevelopTool_Url));//将页面加载到对象上
    QGridLayout* layout = new QGridLayout(this);//创建布局方式(只有在布局上才能添加其他组件)
    layout->addWidget(m_webView);//将显示web页面的窗口加载到网格布局上
    this->setLayout(layout);//将布局设置到当前Qwidget上
}


qwebengineviewplus.h
/*
 * 自定义类,主要是通过继承QWebEngineView重写createWindow函数从而支持链接跳转
*/
#ifndef QWEBENGINEVIEWPLUS_H
#define QWEBENGINEVIEWPLUS_H

#include <QWebEngineView>
#include <QMainWindow>
#include <qurl.h>

class QWebEngineViewPlus : public QWebEngineView
{
    Q_OBJECT
public:
    explicit QWebEngineViewPlus(QWidget *parent = nullptr);

private slots:

    void slot_LinkHovered(const QString& url);

protected:
    //这个函数应该是由底层的QWebEnginePage发起调用的,如果不想新建QWebEngineView,在这儿重写。
    //由于源码中对这一块有些小bug,所以不支持点击链接跳转,这里重写就是为了支持跳转
    QWebEngineView * createWindow(QWebEnginePage::WebWindowType type) override;

private:
    QUrl	url_;

};

#endif // QWEBENGINEVIEWPLUS_H

qwebengineviewplus.cpp
#include "qwebengineviewplus.h"

QWebEngineViewPlus::QWebEngineViewPlus(QWidget *parent):
    QWebEngineView(parent)
{
    setAttribute(Qt::WA_DeleteOnClose);
    //当前端鼠标移动到连接上会触发这个信号,连接自定义槽函数将链接地址获取下来
    connect(this->page(), &QWebEnginePage::linkHovered, this, &QWebEngineViewPlus::slot_LinkHovered);
    //当加载新页面的icon时候会触发这个信号,这里刷新一遍显示icon
    connect(this,&QWebEngineViewPlus::iconChanged,[=](){
        this->window()->setWindowIcon(this->icon());
    });
    //当加载新页面的标题时候会触发这个信号,这里刷新一下标题
    connect(this,&QWebEngineViewPlus::titleChanged,[=](){
        this->window()->setWindowTitle(this->title());
    });
}

void QWebEngineViewPlus::slot_LinkHovered(const QString &url)
{
    //获取视图里面点击的链接地址
    url_ = url;
}

QWebEngineView  *QWebEngineViewPlus::createWindow(QWebEnginePage::WebWindowType type)
{
    //加载链接地址(创建一个新的界面显示一个新的网页)
//    QWebEngineViewPlus* new_QWebEngineViewPlus = new QWebEngineViewPlus();
//    new_QWebEngineViewPlus->load(this->url_);
//    new_QWebEngineViewPlus->show();
//    return new_QWebEngineViewPlus;
    //加载链接地址(不创建新的界面,显示新的网页)
    Q_UNUSED(type)
    //this->load(this->url_);
    this->setUrl(this->url_);
    return 0;
}

mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QWebEngineView>
#include <QtWebChannel>
#include <QtDebug>
#include "web_client_bridge.h"
#include "qwebengineviewplus.h"

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

    QWebEngineViewPlus *webView = nullptr;//创建加载前端界面的对象(继承自Qt的webenginewidgets模块提供)
    QWebChannel *webChannel = nullptr;//创建qt与前端界面通信的对象(Qt的webenginewidgets模块提供)
    WebClientBridge *WebClientBridge_obj;//自定义辅助类
private slots:
    void on_pushButton_clicked();

private:
    Ui::MainWindow *ui;
protected:
    //重载窗口变化响应函数
    void resizeEvent(QResizeEvent *);
};
#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);
    /*
     * 自定义辅助类,这个类的作用就是提供给QWebChannel进行注册,注册之后这个类就可以直接在前端使用,
     * 注意这个一定要在QWebEngineViewPlus对象之前定义,因为这里面有设置调试界面的环境配置,若是在QWebEngineViewPlus之后定义,则会出现调试环境设置失败的问题
    */
    WebClientBridge_obj = new WebClientBridge();
    //初始化前端对象
    webView = new QWebEngineViewPlus(this);//QT提供的类,可以将html文件显示到这个控件上
    //通过文件的相对地址获取文件的绝对地址
    QDir tempDir("./WebView/test.html");
    QString absoluteDir = tempDir.absolutePath();
    webView->load(QUrl(absoluteDir));//设置要显示的html文件(这里QUrl的地址要写绝对地址,否则渲染不出来,据说使用qrc相对地址也可以)
    //webView->load(QUrl("https://baidu/"));//设置要显示的网页文件
    //webView->load(QUrl("http://localhost:8080/"));//设置要显示的本地网页文件
    webView->setMinimumSize(this->size());//设置QWebEngineViewPlus窗口最小大小为主窗口初始化大小
    //webView->move((this->width()-webView->width())/2,(this->height()-webView->height())/2);//使QWebEngineViewPlus窗口相对于整个程序居中
    webChannel = new QWebChannel;//QT提供的类,用于client与web通信的通道
    webChannel->registerObject("WebClientBridge_obj", WebClientBridge_obj);//将自定义类注册到通信通道中
    webView->page()->setWebChannel(webChannel);//最后一步将通信的通道设置到QWebEngineViewPlus控件上
    ui->pushButton->move(100,900);
}

MainWindow::~MainWindow()
{
    delete ui;
}


void MainWindow::on_pushButton_clicked()
{
    //打开开发者工具
    WebClientBridge_obj->openDevelopTool();
    static int count = 0;
    //直接调用前端函数
    webView->page()->runJavaScript(QString("showAlert(%1)").arg(++count));
}

void MainWindow::resizeEvent(QResizeEvent *)
{
    //让web页面的大小始终填满整个窗口,测试on_pushButton_clicked功能时屏蔽,否则会遮住pushButton按钮
    //webView->resize(this->size());
}



main.cpp
#include "mainwindow.h"
#include <QApplication>
#include <qsharedmemory.h>
int main(int argc, char *argv[])
{
    QApplication::setAttribute(Qt::AA_UseOpenGLES);//解决有些电脑上html页面居然是黑屏
    QApplication a(argc, argv);

    QSharedMemory sigleton(a.applicationName());
    if(!sigleton.create(1)) return 0; //多次启动直接退出
    MainWindow w;
    w.show();
    return a.exec();
}

前端HTML文件,这里我只写了一个HTML文件
<!DOCTYPE html>
<html>

<head>
	<script src="./qwebchannel.js">
		//这里是加载Qt提供的QWebChannel的js文件,是相对地址,根据当前HTML文件与你复制的qwebchannel.js文件的相对地址得出src
	</script>
	<script type="text/javascript">
		//捕获全局键盘事件,若将window.onkeydown改成document.onkeydown则只在当前页面捕获
		window.onkeydown = function (event) {
			var keyCode = event.keyCode || event.which || event.charCode;
			var ctrlKey = event.ctrlKey || event.metaKey;
			//捕获ctrl+F12
			if (ctrlKey && keyCode == 123) {
				event.preventDefault();//阻止默认事件
				WebClientBridge_obj.openDevelopTool();//调用辅助类打开开发者工具
			}
			// event.preventDefault(); // 注意:阻止默认事件不能放在外面,会阻止浏览器或者input/textarea的默认事件,应该放在相应的按键组合中去阻止
			return false;
		}

		function showAlert(data) {
			//直接读取c++辅助类中的某个已经注册为公开变量的变量
			//jsInvokeC()中修改了someattribute_test的值,这个值的修改会同步到c++客户端,所以这里读取到的值是已经修改了的值
			alert("someattribute_1:" + WebClientBridge_obj.someattribute_test);
			console.log(
				"===============================>>>>>>>>>>>>>>",
				WebClientBridge_obj.someattribute_test
			);
			alert("someattribute_2:" + data + WebClientBridge_obj.someattribute_2);
		}
		var data = 0;

		function jsInvokeC() {
			//直接修改c++辅助类中的某个已经注册为公开变量的变量
			WebClientBridge_obj.someattribute_test = "someattribute_1的值_" + data + "_test"
			//读取修改后的值并调用辅助类的public slots:修饰的槽函数,将数据传给c++客户端
			WebClientBridge_obj.jscall(data++ + WebClientBridge_obj.someattribute_test);
		}

		// QT交互,创建一个QWebChannel对象,并将c++中注册带辅助类加载到前端
		//需要将qwebchannel.js这个js文件引入到创建QWebChannel对象的文件中,qwebchannel.js定义了QWebChannel对象
		//qwebchannel.js是Qt的WebEngine模块提供的,在qt安装目录下搜索就能找到这个文件(前提是安装qt的时候安装了WebEngine模块)
		new QWebChannel(qt.webChannelTransport,
			function (channel) {
				//这里的window.WebClientBridge_obj是自己定义的一个名称,可以随便取
				//这行代码相当于在前端js和客户端c++进行关联,关联完毕后就可以调用到注册的辅助类的数据和方法
				window.WebClientBridge_obj = channel.objects.WebClientBridge_obj;
			});
	</script>
</head>

<body>
	<div>
		<input type="button" value="调用c++" onclick="jsInvokeC()" style="background-color: aqua;">
	</div>
</body>

</html>

其他相关文章(转载其他人的连接):链接: https://blog.csdn/hitzsf/article/details/109279003.
链接: https://blog.csdn/hitzsf/article/details/109278967.
链接: https://www.jianshu/p/e4ec0ab2f999.

本文标签: QTQWebEngineViewhtml