C11—基于QWebEngine实现网页浏览器 2022-03-17

Qt实现网页浏览器

场景介绍

Qt4使用QWebView实现浏览器相关的功能,看网上都说Bug很多,到了Qt5.5,使用QWebEngine模块及其中的类代替QWebView实现浏览器的功能,官方介绍,内部技术修改了,总之现在比较稳定了。关于一些基础配置可以参考基础篇的文章Day29Qt显示Web内容2022-01-05要实现的功能有,浏览器导航栏,包括"前进"、“后退”、”刷新“;可实时刷新界面title和图标;可以查看历史记录,设置主页,查看网页源代码;可实现网页的缩放;实时显示网页加载进度;可以进行超链接跳转。功能之间相对独立,没有复杂的逻辑关系,代码梳理的应该比较明晰了,下面按照顺序介绍开发步骤,并介绍一些注意事项,随后将代码贴上。源代码下载

步骤

0.加载主页,设置主页
加载主页说的是,程序启动的时候,自动进入一个网页,代码中我设置的是我CSDN的主页。windows系统中保存配置文件使用.ini文件或者注册表,linux使用.conf文件作为配置文件,qt使用QSetting类提供了对这类操作的支持。代码中我使用的是ini文件保存了主页的url,程序启动时,读取文件,并使用QWebEngineView的load方法加载即可。设置主页就是在菜单栏中提供”设置主页“选项,用户点击时去修改ini文件,将当前网址栏中的url保存即可。需要注意的是,需要将QWebEngineView类实例化的对象当作一个窗口对象放在界面上才可以显示,程序中放在一个栅格布局中便于布局管理。
1.执行显示当前网址操作
程序接受lineEdit回车按钮信号或者“转到”按钮,执行显示当前网址操作。将两个信号连接至一个槽,在该槽中执行获取网址栏url,并使用load方法加载即可。
2.修改窗口标题
接受webEngineView的titleChange信号,执行修改窗口标题操作。QWebEngineView对象将用title方法返回当前页面title,将其设置到界面title即可。
3.设置进度条
构造一个进度条,设置其样式,QWebEngineView类的loadProgress将返回页面加载进度值,据此值设置进度条进度值;同时当界面加载完成时,QWebEngineView类将发出finished信号,启用定时器,进度条等待5s之后隐藏。另外重写resize事件,当界面大小更改时,显示状态的进度条应随窗口大小改变。
4.刷新网页url
当界面因为用户单击超链接点击发生跳转时,QWebEngineView类将发射urlChanged,并携带参数url表示当前界面的url值,将其设置在网址栏即可。
5.添加界面中工具栏
创建工具栏,并设置停靠,移动的属性,将QWebEngineView类提供的back、forward等QAction添加在界面工具栏中,当作网页浏览器的导航栏。将网址栏和”转到“按钮也添加进工具栏。
6.获取并显示网站图标
此处将使用到Qt Network模块,发出http请求,请求网页的ico图标,并将该图标设置为应用程序图。当然,在获取出错的时候应该设置一个默认的图标。过程中要发出网络请求,并处理网络请求返回的数据,将其转换成QIcon设置为图标即可。对于网络模块不熟悉的可以参考基础专栏中的Day28Qt实现http请求2022-01-06
7.实现网页缩放
使用QWebEngineView类提供的setZoomFactor方法即可实现界面的放大和缩小,范围是0.25~500。同时将缩放信息打印在状态栏,并为缩放操作设置快捷键。
8.查看网页源代码
在QWebView的时代,查看源代码是提供了方法的,大概是webview->page->currentFrame->toHtml();可以直接获取。到了QWebEngine时代,将查看源代码当作了一个QAction来处理,当你设计了QWebEngineView之后,你会发现,页面中右键即可出现”view page source“,即便你已经加载页面成功,单击这个action也没有响应。这是为啥呢?在Qt帮助中,写的很清楚,如下图
在这里插入图片描述
也就是说,必须重新实现creatWindow之后才能使用这个功能。或者说点击才有效。那么怎么检测是否具备执行这个action的条件呢?同样截取自Qt Help,使用这个函数,参数1填写想执行的Action,参数2写入bool值,当Action允许被执行时,参数2将为true,如果环境不具备将返回false。
在这里插入图片描述
在我的程序中,重写了creatWindow,所以可以使用这个QAction。
9.显示历史记录
创建全局指针对象QListwidget用以保存历史记录,界面添加”历史记录“菜单项,当用户单击时,通过history函数返回一个QWebEngineHistory对象,表示浏览历史,获取历史记录,并将其写到listwidget中。items获取所有项目列表,最后非模态形式显示即可。如下:

foreach(QWebEngineHistoryItem item,myView->history()->items()){
        QListWidgetItem *history = new QListWidgetItem(item.title());
        historyList->addItem(history);
    }

当用户点击历史记录项的时候,应该使用listwidget方法确定当前行,配合history()->goToItem(item);方法转到单击的页面。
10.响应超链接
以上0-9步骤完成之后你会发现,界面是无法响应超链接的,当鼠标放在超链上是有部件的状态变化,可是点击却没有反应。有反应说明Qt已经获取到了这个点击信息,不需要你再去根据鼠标位置单击事件做解析。那他的机制是,当你点击超链接,或者运行JavaScript、或其他会弹出界面的操作时,他会去调用虚函数,creatwindow,但默认这个函数中没有内容,因此需要在继承自QWebEngineView的子类中重写。例如在该函数中打印某信息,当你点击超链或者查看源代码时,控制台会打印这句话。在该函数中直接返回this,将实现超链的响应,即网页的跳转。当然该函数提供了一个参数,这个参数是网页传递的,即QWebEnginePage::WebWindowType type,查看帮助可知该枚举类型有四类,可以在重写的creatWindow中根据不同的type执行不同的操作。

以上步骤介绍比较详细了,当然有一些必要的判断操作,提示操作以及错误判断未提及,这是比较容易的。稍后贴出代码。

效果

C11—基于QWebEngine实现网页浏览器 2022-03-17_第1张图片
实测中,各功能正常。

代码如下

ui设计

C11—基于QWebEngine实现网页浏览器 2022-03-17_第2张图片

main函数

#include "mainwindow.h"
#include 
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    return a.exec();
}

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include
#include
#include
#include
#include
#include
#include
#include 
#include 
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 doProcessSetMainPage();
    void doProcessLineEnter();
    void doProcessChangeTitle();
    void doProcessSetMyProgressBar(int p);
    void doProcessHideMyProgressBar(bool isFinished);
    void doProcessRefreshUrl(QUrl url);
    void doProcessGetWebIcon(const QUrl url);
    void doProcessSetWindowIcon();
    void doProcessShowSourceCode();
    void doProcessShowHistory();
    void doProcessGotoHistoryItem(const QModelIndex &index);
    void on_zoomInAction_triggered();
    void on_zoomOutAction_triggered();
    void on_resetZoomAction_triggered();
protected:
    void resizeEvent(QResizeEvent *event);
private:
    Ui::MainWindow *ui;

    webView * myView;
//        QWebEngineView *myView;
    QProgressBar * myProgressBar;
    QNetworkAccessManager * manager;
    QListWidget *historyList;

    void Init();
    void setMyToolBar();
    void loadMainPage();
};
#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);
    Init();
}

MainWindow::~MainWindow()
{
    delete ui;
}
/****************** SLOT(数字对应功能) ****************/
//0.设置主页
void MainWindow::doProcessSetMainPage()
{
    QFile *myFile = new QFile;
    QString filename = "mainpage.ini";
    myFile->setFileName(filename);
    bool isOPen =myFile->open(QIODevice::WriteOnly);
    if(!isOPen){
        //文件打开失败
        QMessageBox::warning(this,"Error Message","Ini Error!");
    }
    else{
        QTextStream out(myFile);
        out<<QString("mainPage=").append(ui->urlEdit->text());
        myFile->close();
        QMessageBox::information(this,"Info","设置成功,重启浏览器生效");
    }
    delete  myFile;
    myFile = NULL;
}
//1.转到当前链接
void MainWindow::doProcessLineEnter()
{
    QString str = ui->urlEdit->text();
    myView->load(QUrl(str));//加载网页
    myView->setFocus();//获取焦点
}
//2.修改窗口标题
void MainWindow::doProcessChangeTitle()
{
//    qDebug()<<"当前标题"<title();
    this->setWindowTitle(myView->title());
}
//3.设置进度条
void MainWindow::doProcessSetMyProgressBar(int p)
{
    myProgressBar->show();
    myProgressBar->setValue(p);
    myProgressBar->setFixedWidth(this->width());
}
//3.隐藏进度条
void MainWindow::doProcessHideMyProgressBar(bool isFinished)
{
    if(!isFinished){
        //出错了
        this->setWindowTitle("Page Error");
        return;
    }
    QTimer *myTimer = new QTimer(this);
    connect(myTimer,&QTimer::timeout,[=](){
        myTimer->stop();
        myProgressBar->hide();
    });
    myTimer->start(5000);//停留5s后隐藏
}
//4.刷新网页url
void MainWindow::doProcessRefreshUrl(QUrl url)
{
    ui->urlEdit->setText(url.toString());
}
//6. 发送网络请求  获取网页相应图标
void MainWindow::doProcessGetWebIcon(const QUrl url)
{
    QNetworkRequest myIconRequest(url);
//    qDebug()<<"网站图标网址为:"<
    QNetworkReply *myIconReply = manager->get(myIconRequest);
    myIconReply->setParent(this);
    connect(myIconReply,SIGNAL(finished()),this,SLOT(doProcessSetWindowIcon()));
}
//6. 网络请求返回成功与否,均设置icon
void MainWindow::doProcessSetWindowIcon()
{
    QIcon icon;
    QNetworkReply *webReply = qobject_cast<QNetworkReply *>(sender());//获取信号发送的指针对象
    if(webReply&&webReply->error()==QNetworkReply::NoError){
        QByteArray daIcon = webReply->readAll();
        QPixmap iconPix;
//        QPixmap iconPix(daIcon);//这个构造不行
        iconPix.loadFromData(daIcon);//数据做转换,先转成pixmap格式
        icon.addPixmap(iconPix);//在通过icon的构造转成icon格式
        webReply->deleteLater();
    }
    else{
//        qDebug()<<"网络出错"<error();
        icon = QIcon(":/res/webIcon.png");
        webReply->deleteLater();
    }
    setWindowIcon(icon);
}
//8.显示网页源代码
void MainWindow::doProcessShowSourceCode()
{
    myView->page()->triggerAction(QWebEnginePage::ViewSource);
}
//9.显示历史记录列表
void MainWindow::doProcessShowHistory()
{
    historyList->clear();
    //history函数返回一个QWebEngineHistory对象,表示浏览历史
    //使用items获取所有项目列表
    //clear可以清空列表
    //count返回列表个数
    foreach(QWebEngineHistoryItem item,myView->history()->items()){
        QListWidgetItem *history = new QListWidgetItem(item.title());
        historyList->addItem(history);
    }
    historyList->show();
}
//转到历史记录项
void MainWindow::doProcessGotoHistoryItem(const QModelIndex &index)
{
    QWebEngineHistoryItem item = myView->history()->itemAt(index.row());
    myView->history()->goToItem(item);
}

/****************** System Slots ****************/
//窗口变化事件捕捉
void MainWindow::resizeEvent(QResizeEvent *event)
{
    if(myProgressBar->isVisible()){
        //已经隐藏
        return;
    }
    else{
        myProgressBar->setFixedWidth(this->width());
    }
}
//7.放大页面 快捷键ctrl++
void MainWindow::on_zoomInAction_triggered()
{
    myView->setZoomFactor(myView->zoomFactor()+0.1);
    ui->statusbar->showMessage(tr("放大 %1%").arg(myView->zoomFactor()*100));
}
//7.缩小页面 快捷键ctrl+-
void MainWindow::on_zoomOutAction_triggered()
{
    myView->setZoomFactor(myView->zoomFactor()-0.1);
    ui->statusbar->showMessage(tr("缩小 %1%").arg(myView->zoomFactor()*100));
}
//7.还原页面 快捷键ctrl+0
void MainWindow::on_resetZoomAction_triggered()
{
    myView->setZoomFactor(1.0);
    ui->statusbar->showMessage(tr("还原 %1%").arg(myView->zoomFactor()*100));
}

/****************** My Function ****************/
void MainWindow::Init()
{
//    myView = new QWebEngineView(this);
    myView = new webView(this);
    ui->gridLayout->addWidget(myView); //添加到布局中
    //连接各界面信号执行相关动作
    //0.加载主页 并修改主页
    this->loadMainPage();
    connect(ui->setMainPageAction,SIGNAL(triggered()),this,SLOT(doProcessSetMainPage()));
    // 1.接受lineEdit回车按钮信号或者“转到”按钮,执行显示当前网址操作
    connect(ui->urlEdit,SIGNAL(returnPressed()),this,SLOT(doProcessLineEnter()));//回车键转到链接
    connect(ui->gotoBtn,SIGNAL(clicked()),this,SLOT(doProcessLineEnter()));
    // 2.接受webEngineView的titleChange信号,执行修改窗口标题操作
    connect(myView,&QWebEngineView::titleChanged,this,&MainWindow::doProcessChangeTitle);//修改title
    // 3.添加进度条,接受loadProgress信号,显示当前进度,任务结束后,接受loadFinish信号,隐藏进度条 */
    myProgressBar = new QProgressBar(ui->statusbar);//将进度条放在状态栏中
    myProgressBar->setStyleSheet("QProgressBar{background-color:#05B8CC;width:20px;"
                                 "border:2px solid grey;border-radius:5px;text-align:center;}");
    connect(myView,&QWebEngineView::loadProgress,this,&MainWindow::doProcessSetMyProgressBar);//获取中
//    connect(QWebEngineView,SIGNAL(loadProgress)) 注意此写法再次不支持,可能是QWebEngineView是qt5.5后出来的模块
    connect(myView,&QWebEngineView::loadFinished,this,&MainWindow::doProcessHideMyProgressBar);//获取完成
    //4.网页改变时,刷新url值到网址栏中
    connect(myView,&QWebEngineView::urlChanged,this,&MainWindow::doProcessRefreshUrl);
    //5.添加界面中工具栏,并添加导航工具进工具栏
    setMyToolBar();
    //6.获取并显示网站图标
    manager = new QNetworkAccessManager(this);
    connect(myView,&QWebEngineView::iconUrlChanged,this,&MainWindow::doProcessGetWebIcon);
    //7.实现网页缩放,见转到槽 范围是0.25~500
    //8.查看网页源代码
    connect(ui->soucreCodeAction,SIGNAL(triggered()),this,SLOT(doProcessShowSourceCode()));
    //9.显示历史记录
    historyList = new QListWidget();
    historyList->setWindowTitle("历史记录");
    historyList->setMinimumWidth(300);
    connect(ui->historyAction,SIGNAL(triggered()),this,SLOT(doProcessShowHistory()));
    connect(historyList,&QListWidget::clicked,this,&MainWindow::doProcessGotoHistoryItem);
}
//5.设置带导航栏功能的工具栏
void MainWindow::setMyToolBar()
{
    QToolBar *toolBar = new QToolBar(this);//将工具栏放入窗口中
    addToolBar(Qt::TopToolBarArea,toolBar);//调整默认放在上面
    toolBar->setAllowedAreas(Qt::LeftToolBarArea);//只允许上停靠
    toolBar->setFloatable(false);//不允许浮动
    toolBar->setMovable(false);//关闭移动  总开关
    //pageAction将返回webEngineView的Action
    toolBar->addAction(myView->pageAction(QWebEnginePage::Back));//后退
    toolBar->addAction(myView->pageAction(QWebEnginePage::Forward));//前进
    toolBar->addAction(myView->pageAction(QWebEnginePage::Reload));//刷新
    toolBar->addAction(myView->pageAction(QWebEnginePage::Stop));//停止
    toolBar->addWidget(ui->urlEdit);//添加网址到工具栏
    toolBar->addWidget(ui->gotoBtn);//添加按钮到工具栏
}
//0.加载主页
void MainWindow::loadMainPage()
{
    QFile *myFile = new QFile;
    QString filename = "mainpage.ini";//读取本地配置文件,该文件放在构建目录下,
    //初始内容为mainPage=https://blog.csdn.net/weixin_40615338?spm=1011.2124.3001.5343
    myFile->setFileName(filename);
    bool isOPen =myFile->open(QIODevice::ReadOnly);
//    qDebug()<errorString();
    if(!isOPen){
        //文件打开失败
        QMessageBox::warning(this,"Error Message","Ini Error!");
    }
    else{
        QTextStream in(myFile);
        myView->load(QUrl(in.readLine().split("=").at(1)));//加载网页
        myView->setFocus();//获取焦点
        myFile->close();
    }
    delete  myFile;
    myFile = NULL;
}

webview.h

自定义的继承自QWebEngineView的子类,为了重写creatwindow函数以实现网页跳转。

#ifndef WEBVIEW_H
#define WEBVIEW_H

#include 
#include 
#include 
class webView : public QWebEngineView
{
    Q_OBJECT
public:
    explicit webView(QWidget *parent = nullptr);
protected:
    QWebEngineView *createWindow(QWebEnginePage::WebWindowType type) override;
signals:

};

#endif // WEBVIEW_H

webview.cpp

#include "webview.h"
webView::webView(QWidget *parent) : QWebEngineView(parent)
{
}
QWebEngineView *webView::createWindow(QWebEnginePage::WebWindowType type)
{
    return this;
}

你可能感兴趣的:(Qt积累——小项目,qt,c++,network)