Qt WebEngine

Qt WebEngine

此文参考诸多文章,主线参考霍亚飞老师编写的第3版《Qt Cteater快速入门》第20章。因理解不深,此文将持续更新(2021.05.01)。

Qt WebEngine模块基于Chromium项目,在windows平台上需要使用MSVC 2013以上版本构建,笔者使用的是Qt 5.15.2+MSVC2019的环境。Qt提供了一个Simpler Exploer的例子,是一个功能比较完善的项目,此文写作时没有吃透。另外,360安全卫士会对项目进行拦截,起初以为是Simple Exploer的代码触发了360,后来发现是QWebEngine模块本身,即使新建一个空白项目,只要有#include <QWebEngine<>>这一行,就会被360拦截。所以运行项目或学习测试时请关闭360[what can I say]。

一、Qt WebEngine架构

此节完整引用自霍亚飞老师的第3版《Qt Creater快速入门》第20章。
Qt WebEngine中的功能被划分到了3个不同的模块:

  1. Qt WebEngine Widgets模块,用来创建C++ Widgets部件的Web程序;
  2. Qt WebEngine模块,用来创建基本Qt Quick的Web程序;
  3. Qt WebEnigne Core模块,用来与Chromium交互。
    而且网页渲染和JavaScript的执行从GUI进程分离到了Qt WebEngine进程(Qt WebEngine Process),其架构如图1所示。
    图1 Qt WebEngine 架构图
    |:-------------------------------------------------------图1 Qt WebEngine架构图-------------------------😐

Qt WebEngine Widgets模块的架构如图2所示。
图2
|:-------------------------------------------------------图2 Qt WebEngine Widgets架构图-------------------------😐

其中视图View(QWebEngineView)是模块中的主要窗体类组件,可以用在各种应用中加载Web内容。而页面Page(QWebEnginePage)包含在View中,它包含了Web页面的主框架,主要负责Web内容、浏览历史History(QWebEngineHistory)和菜单动作Action。View与Page十分相似,它们提供了一组相同的函数。配置Profile(QWebEngineProfile)用于区分不同的Page,属于同一个Web引擎配置的所有网页都会共享设置Settings、脚本Script和Cookies。

Qt WebEngine Core模块基于Chromium项目,Chromium提供了自己的网络和绘图引擎,而且开发紧密结合了其所依赖的模块,因此,对于最新的HTML5规范,Qt Webengine比以前的Qt WebKit提供了更好的支持。要清楚的是,虽然Qt WebEngine基于Chromium,但是其中并没有包含或使用Goole Chrome浏览器提供的服务或者加载项。Chromimu和Chrome的详细区别可参见相关文档。

二、基于Qt WebEngine Widgets的网页浏览器

Qt提供了一个功能比较完整的例子,在欢迎页搜索Simple Explorer即可新建这一项目,项目写的清晰而复杂,初看项目条理十分清晰,深究下来基本不懂【笔者这活还是不瓷实】。还是参考霍老师的书,写出一个简单的Demo。

2.1 显示一个网页

新建一个项目,pro文件中添加"QT += webenginewidgets",头文件中添加包含 #include <QWebEngineView<>。

QWebEngineView *view = new QWebEngineView(this);
view->load(QUrl("http://www.qter.org/"));
view->show();

这三行就可以加载一个页面。此时有三个问题:

  1. 页面大小不随窗口而改变;
  2. 链接中必须带有“http://”,“www.baidu.com”这样是打不开的;
  3. 页面中的链接,如果是在本窗口中打开的,可以点开显示,但如果是新建标签页或新建窗口打开的页面,是打不开的。

解决页面大小的问题,需要用到QResizeEvent这个函数,头文件中声明:

void resizeEvent(QResizeEvent *);

在窗口界面中添加一个tabWidget部件,设定窗口布局,我们在tab页面中加载view:

//头文件中定义QWebEngineView *view;
view = new QWebEngineView(ui->tab);
view->load(QUrl("http://www.qter.org/"));
view->show();

void MainWindow::resizeEvent(QResizeEvent *)
{
    view->resize(ui->tab->size());
}

这样窗口变化的时候,网页也会变化。不过::::加载速度真的慢哦::::
2021.05.07更新补充:
上述代码是把view放在了QTabWidget中,如果是view = new QWebEngineView(this),用主窗口代替Tab的话,不使用QResizeEvent页面也会随窗口大小而变化。

2.2 相关信号槽

Qt的帮助文档中列出的QWebEngineView的信号槽如下:
Signals:

 void iconChanged(const QIcon &icon)
 void iconUrlChanged(const QUrl &url)
 void loadFinished(bool ok)
 void loadProgress(int progress)
 void loadStarted()
 void renderProcessTerminated(QWebEnginePage::RenderProcessTerminationStatus terminationStatus, int exitCode)
 void selectionChanged()
 void titleChanged(const QString &title)
 void urlChanged(const QUrl &url)

Public Slots:

 void back()
 void forward()
 void reload()
 void stop()

这里列出的信号就是QWebEngineView加载的过程。由loadStarted()开始,每当一个网页元素(例如一张图片或一个脚本等)加载完成时,都会发射loadProcess()信号;最后当加载全部完成后,会发射loadFinished()信号,如果加载成功,该信号的参数为true。

这里列出几个槽函数的写法:

输入网址加载页面:

void MainWindow::on_lineEdit_website_returnPressed()//网址输入
{
    /**
     * @brief 网址要以http://开头,www.baidu.com是打不开的,http://www.baidu.com才可以
     */
    QString urltemp = ui->lineEdit_website->text();
    QUrl url;
    if (urltemp.mid(0, 3) == "www")
    {
        url = QUrl("http://" + urltemp);
        ui->lineEdit_website->setText("http://" + urltemp);
    }else if (urltemp.mid(0, 4) == "http"){
        url = QUrl(urltemp);
    }else{
        if (urltemp.right(3) == "com")
        {
            url = QUrl("http://www." + urltemp);
            ui->lineEdit_website->setText("http://www." + urltemp);
        }else{
            url = QUrl("http://www." + urltemp + ".com");
            ui->lineEdit_website->setText("http://www." + urltemp + ".com");
        }
    }
    view->load(url);
    view->setFocus();
}

页面加载进度:

void MainWindow::setProcess(int p)//加载进度
{
    ui->progressBar->setValue(p);
    process = p;
    this->adjustTitle();
}

设置标题:

void MainWindow::adjustTitle()//设置标题
{
    QString title = view->title();
    if (process <= 0 || process >= 100)
    {
        setWindowTitle(title);//窗口标题更改
        ui->tabWidget->setTabText(0, title);//标签页标题更改
    }else{
        setWindowTitle(QString("%1(%2)").arg(title).arg(process));
        ui->tabWidget->setTabText(0, QString("%1(%2)").arg(title).arg(process));
    }
}

加载完成之后:

void MainWindow::finishiLoading(bool finished)//加载完成
{
    if (finished)
    {
        process = 100;
    }else{
        setWindowTitle("web page loading error!");
    }
}

到这里的的效果如下图所示:
在这里插入图片描述
前进和后退的操作是一个pageAction,直接为工具栏添加Action是这样写:

ui->mainToolBar->addAction(view->pageAction(QWebPageAction::Back));

如果是给QPushButton添加一个Action呢?我这里采用这种方式,参看demo吧。

2.3 多窗口或多标签页显示

如前面所说,如果网页内的链接在本窗口内打开的话是没有问题的,但如果链接要求在新标签页或新窗口中加载的话,就需要子类化QWebEngineView,然后重新实现其中的createWindow()函数,并创建一个新的窗口。
===========================================未完待续 2021.05.04
2021.06.06更新

2.3.1 多窗口显示

多窗口显示相对简单,此处不讲原理(笔者也没弄明白<_>!),只贴一下如何实现。
此节大部分参考自霍亚飞老师编写的[第3版《Qt Cteater快速入门》]
1、新建一个C++类,此处笔者命名为zwebengineview,然后作如下修改:
在这里插入图片描述
头文件zwebengineview.h

#ifndef ZWEBENGINEVIEW_H
#define ZWEBENGINEVIEW_H

#include <QWidget>
#include <QWebEngineView>
#include <QDebug>

class MainWindow;//添加类的前置声明
class zwebengineview : public QWebEngineView
{
    Q_OBJECT
public:
    explicit zwebengineview(QWidget *parent = 0);
protected:
    QWebEngineView *createWindow(QWebEnginePage::WebWindowType type) override;
private:
    MainWindow *mainwindow;
};

#endif // ZWEBENGINEVIEW_H

这里声明一个createWindow()函数和MainWindow对象,因为在createWindow()中要创建一个MainWindow对象作为新的窗口。然后修改zwebengineview.cpp:

#include "zwebengineview.h"
#include "mainwindow.h"

zwebengineview::zwebengineview(QWidget *parent)
    : QWebEngineView(parent)
{
}

QWebEngineView *zwebengineview::createWindow(QWebEnginePage::WebWindowType type)
{
    mainwindow = new MainWindow(this);
    mainwindow->show();
    return mainwindow->createView();
}

在createWindow()函数的定义中创建了mainWindow实例并进行显示,该函数有一个QWebEnginePage::WebWindowType枚举类型参数,该类型共包含3个值,分别是QWebEnginePage::WebBrowserWindow(全新的浏览器窗口)、QWebEnginePage::WebBrowserTab(浏览器标签页)、QWebEnginePage::WebDialog(网页对话框)。可以在这里通过判断type类型显示不同的窗口。该函数需要返回一个QWebEngineView对象指针,会在返回的QWebEngineView对象中显示需要打开的网页内容,这里调用了MainWindow类的createView()函数,具体这样:

添加createView()函数定义:

zwebengineview *MainWindow::createView()
{
    return view;
}

mainwindow.h中添加声明:

class zwebengineview;
zwebengineview *createView()
zwebengineview *view;

添加引用:

#include "zwebengineview.h"

贴一下相对完整的写法:
mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include "zwebengineview.h"//添加引用
#include <QDebug>
#include <QWebEnginePage>

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class zwebengineview;//前置声明

class MainWindow : public QMainWindow
{
    Q_OBJECT

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

    zwebengineview *createView();//创建新窗口的函数

    int process;//进度条
    void resizeEvent(QResizeEvent *);//页面随窗口变化

protected slots:
    void setProcess(int);//加载进度条
    void finishiLoading(bool);//加载完成
    void adjustTitle();//设置标题
    void adjustWebsite();//网址输入框改变

private slots://下面是按钮,各位自定
    void on_lineEdit_website_returnPressed();
    void on_btn_test_clicked();

private:
    Ui::MainWindow *ui;
    zwebengineview *view;//创建对象
};
#endif // MAINWINDOW_H

maindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    this->setWindowState(Qt::WindowMaximized);//窗口最大化
    process = 0;//进度条初始为0

    ui->lineEdit_website->setText("http://www.qter.org/");//初始页面
    view = new zwebengineview(this->ui->tab);
    connect(view, &QWebEngineView::loadProgress, this, &MainWindow::setProcess);//加载进度
    connect(view, &QWebEngineView::loadFinished, this, &MainWindow::finishiLoading);//加载完成
    connect(view, &QWebEngineView::urlChanged, this, &MainWindow::adjustWebsite);//网页标题

    view->load(QUrl("http://www.qter.org/"));
    view->show();
}

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

void MainWindow::resizeEvent(QResizeEvent *)//页面变化
{
    view->resize(ui->tabWidget->size());
}


void MainWindow::finishiLoading(bool finished)//加载完成
{
    if (finished)
    {
        process = 100;
    }else{
        setWindowTitle("web page loading error!");
    }
}
void MainWindow::on_lineEdit_website_returnPressed()//网址输入
{
    /**
     * @brief 网址要以http://开头,www.baidu.com是打不开的,http://www.baidu.com才可以
     */
    QString urltemp = ui->lineEdit_website->text();
    QUrl url;
    if (urltemp.mid(0, 3) == "www")
    {
        url = QUrl("http://" + urltemp);
        ui->lineEdit_website->setText("http://" + urltemp);
    }else if (urltemp.mid(0, 4) == "http"){
        url = QUrl(urltemp);
    }else{
        if (urltemp.right(3) == "com")
        {
            url = QUrl("http://www." + urltemp);
            ui->lineEdit_website->setText("http://www." + urltemp);
        }else{
            url = QUrl("http://www." + urltemp + ".com");
            ui->lineEdit_website->setText("http://www." + urltemp + ".com");
        }
    }
    /// QUrl turl = QUrl::fromUserInput(ui->lineEdit_website->text()); ///自动补全网址,只能补上http
    view->load(url);
    view->setFocus();
}

void MainWindow::setProcess(int p)//加载进度
{
    ui->progressBar->setValue(p);
    process = p;
    this->adjustTitle();
}

void MainWindow::adjustTitle()//设置标题
{
    QString title = view->title();
    if (process <= 0 || process >= 100)
    {
        setWindowTitle(title);//窗口标题更改
        ui->tabWidget->setTabText(0, title);//标签页标题更改
    }else{
        setWindowTitle(QString("%1(%2)").arg(title).arg(process));
        ui->tabWidget->setTabText(0, QString("%1(%2)").arg(title).arg(process));
    }
}

void MainWindow::adjustWebsite()//链接改变时改变网址输入栏的网址
{
    ui->lineEdit_website->setText(view->url().toString());
}

void MainWindow::on_btn_test_clicked()
{
}

zwebengineview *MainWindow::createView()
{
    return view;
}

zwebengineview的写法上面有了,到这里可以看一下效果:
在这里插入图片描述
这里有一点,虽然是多窗口打开页面,但是多窗口打开页面之后,任务栏中并不会多增加一个图标,Alt+Tab快捷键也不能切换多窗口,如何实现切换呢?

有同学需要源码,https://download.csdn.net/download/weixin_43193739/19420818。未完善,仅供参考。

=======================================================未完待续 2021.06.06

2021.06.10更新

2.3.2 多标签显示

基于上述实践,笔者开始以为重写createWindow函数时添加对tab的支持就可以,所以有了以下努力。
zwebengineview.cpp

QWebEngineView *zwebengineview::createWindow(QWebEnginePage::WebWindowType type)
{
    mainWindow = new mainWindow(this);
    switch (type) {
    case QWebEnginePage::WebBrowserBackgroundTab: {//在新标签页中打开页面,但是不显示
        return mainWindow ->createBackgroundTab();
    }
    case QWebEnginePage::WebBrowserTab: {//在新标签页中打开页面,直接显示
        return mainWindow ->createTab();
    }
    case QWebEnginePage::WebBrowserWindow: {//创建新窗口显示页面
        mainWindow ->show();
        return mainWindow ->createWindow();
    }
    }
    return nullptr;
}

然后在mainWindow中新建createTab()函数,用于在tab中加载页面:

void MainWindow::createTab()
{
    ui->tabWidget->insetTab(num, view, title);
    view->show();
    return view;
}

这样操作的结果是可以在新标签页中打开页面,但是只能在新标签页中打开页面,之前的标签页中的页面就会变成空白页面。在进行了诸多尝试之后,QT官方的simplebrowser例子将这一功能实现,过程相对比较复杂。官方写的例子相当好,浏览器的基本功能差不多都有了,也很流畅,但是很不爽的一点就是UI界面采用代码编写,这让习惯用creater设计功能布局界面的笔者很难受<_>!所以就简单写一下官方例子实现的过程。

如何打开官方例子,欢迎界面直接搜索simplebrowser就有了,当然可以把这个例子直接复制到其他文件夹,然后进行修改。
在这里插入图片描述
这张图显示的就是官方的套路,BrowserWindow是主界面,由Browser进行管理,TabWidget实现多标签显示功能,对网页的相关操作也在这里实现。这里的BrowserWindow和TabWidget均不用UI设计,所以也没有.ui文件,笔者尝试多次都无法实现用UI设计把这个功能做出来。。。水平不够<_>!期待大神补充吧。

OK就写到这儿了。

  • 10
    点赞
  • 127
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值