此文参考诸多文章,主线参考霍亚飞老师编写的第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
此节完整引用自霍亚飞老师的第3版《Qt Creater快速入门》第20章。
Qt WebEngine中的功能被划分到了3个不同的模块:
Qt WebEngine Widgets模块的架构如图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提供了一个功能比较完整的例子,在欢迎页搜索Simple Explorer即可新建这一项目,项目写的清晰而复杂,初看项目条理十分清晰,深究下来基本不懂【笔者这活还是不瓷实】。还是参考霍老师的书,写出一个简单的Demo。
新建一个项目,pro文件中添加"QT += webenginewidgets",头文件中添加包含 #include
QWebEngineView *view = new QWebEngineView(this);
view->load(QUrl("http://www.qter.org/"));
view->show();
这三行就可以加载一个页面。此时有三个问题:
解决页面大小的问题,需要用到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页面也会随窗口大小而变化。
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吧。
如前面所说,如果网页内的链接在本窗口内打开的话是没有问题的,但如果链接要求在新标签页或新窗口中加载的话,就需要子类化QWebEngineView,然后重新实现其中的createWindow()函数,并创建一个新的窗口。
===========================================未完待续 2021.05.04
2021.06.06更新
多窗口显示相对简单,此处不讲原理(笔者也没弄明白<_>!),只贴一下如何实现。
此节大部分参考自霍亚飞老师编写的[第3版《Qt Cteater快速入门》]
1、新建一个C++类,此处笔者命名为zwebengineview,然后作如下修改:
头文件zwebengineview.h
#ifndef ZWEBENGINEVIEW_H
#define ZWEBENGINEVIEW_H
#include
#include
#include
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
#include "zwebengineview.h"//添加引用
#include
#include
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更新
基于上述实践,笔者开始以为重写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就写到这儿了。