实现QT与HTML页面通信

1.  前言

最近,C++和WEB本地混合应用开发模式逐渐流行起来,个人也认为标记语言描述的界面是界面开发的一个发展趋势。WPF、Java FX,当然也少不了Html。基于Html的界面在开发效率,可移植性上都十分有优势,所以也被很多程序采用

随着HTML5技术风生水起,Qt开发团队用近一年的时间开发了一个全新的基于Chromium的浏览器引擎Qt WebEngine,以支持面向未来的Hybrid应用开发,并完全支持桌面和嵌入式平台。此外,Qt WebEngine不仅提供了易于使用的跨平台API,还完全集成了Qt的图形库,允许网页内容进行叠加,并与Qt用户界面或OpenGL图形效果混合。Qt 5.4仍然包含老旧的Qt WebKit模块,但在其后发布的版本中将停止对于WebKit的支持,对此,Qt团队建议开发者将项目统一从WebKit迁移至Qt WebEngine,而对于需要Web能力的新项目,最好直接采用Qt WebEngine开发。在现在下载的QT5.6 beta预编译版本中已经没有了Qt WebKit模块。


目前QT官方的文档中对如何从原来的WebKit迁移至QtWebEngine没有提供足够丰富的文档和指导。

本文记录一些从WebKit迁移至QtWebEngine,实现C++与HTML和JS交互的一些经验和例子。

2.  使用Qt WebEngine和WebChannel模块

在官方提供的Porting from QtWebKit to QtWebEngine---https://wiki.qt.io/Porting_from_QtWebKit_to_QtWebEngine文档中给出了从WebKit迁移至QtWebEngine的一些建议和指导,其中提到QWebFrame 已经被合并到QwebEnginePage中,Qt WebEngine不能和QnetworkAccessManager交互等。

原来在Webkit中使用的交互集成的类和方法也不能使用了,原来Webkit中常用的交互方式如下:

1)    Qt代码中加载并显示该页面

QWebView view; 

view.load(QUrl("test.html")); 

view.show();

 

官方推荐的使用方式:

 

QWebEngineView*view = newQWebEngineView(parent);

    view->load(QUrl("http://qt-project.org/"));

   view->show();

 

2)    Qt对象中访问web页面元素

// myPlugin指向的对象可在HTML中用名字myPluginObject进行访问 

webView->page()->mainFrame()->addToJavaScriptWindowObject("myPluginObject",myPlugin); 

// 当信号signalEmitted被触发时,调用JavaScriptfunctionToCall函数 

webView->page()->mainFrame()->evaluateJavaScript("myPluginObject.signalEmitted.connect(functionToCall);")

 

官方推荐的使用方式:

方法一:QWebEnginePagerunJavaScript(const QString & scriptSource, FunctorOrLambdaresultCallback)

方法二:使用QtWebChannel方式,这是官方的推荐方式,他可以很方便的实现C++和HTML/JS的双向通信,同时实现C++和HTML/JS的解耦,方便开发人员的分工及系统集成,参见后面的例子。

3)    web页面中访问Qt对象

web页面中可以通过类似于下的JavaScript代码访问Qt对象:

<ahref="javascript:document.getElementByIdx_x("myLabel").setText("通过JavaScript访问Qt对象");"mce_href="javascript:document.getElementByIdx_x("myLabel").setText("通过JavaScript访问Qt对象");">点击访问Qt对象</a>

 

官方推荐的使用方式:

使用QtWebChannel方式,这是官方的推荐方式,他可以很方便的实现C++和HTML/JS的双向通信,同时实现C++和HTML/JS的解耦,方便开发人员的分工及系统集成,参见后面的例子。

在QT5.5和QT5.6中,利用Qt的Qt WebEngine和WebChannel模块,你完全可以进行本地桌面与web混合应用开发,你可以自由地混合JavaScript,样式表,Web内容和Qt组件。基于Chromium Qt WebEngine是一个非常成熟的web浏览引擎。你可以在C++ 中执行JavaScript,或者在网页中集成C++对象,并且通过JavaScript和这些对象进行交互。

一个现代的HTML渲染引擎只是混合开发的一半,另一半就是本地应用和渲染对象的交互。QT的Chromium Qt WebEngine 集成提供了这种解决方案,当然目前还不是很完善。

从QT5.4开始就新增了Qt WebChannel模块,该模块提供了在QML/C++ 和 HTML/Javascript之间的一个简单、易用的桥接,从而使得开发能够使用Qt和Web技术进行混合开发,目前QT官方也推荐是用QtWebChannel来桥接C++和HTML,参见Qt WebChannel – bridging the gap betweenC++/QML and the web------https://www.youtube.com/watch?v=KnvnTi6XafA,这是2014年Qt开发者大会上的一段视屏演讲。

通过QtWebChannel能够实现C++/QML和HTML/javascript客户端之间进行无缝桥接,目前主要支持两种方式的桥接。

  • l  客户机可以是任何支持WebSockets的JavaScript runtime机器和应用,客户机可以是独立的应用或浏览器。也就是说QtWebChannel是基于WebSockets协议之上。
  • l  通过IPC的方式实现C++/QML和HTML/javascript客户端之间进行通信。在QT应用内嵌入HTML或JS页面的情况下使用基于IPC的WebChannel通信效率更高效。

 

在官方演进路线中提到将来的集成解决方案将支持以下特性:

  • l  使用object标签嵌入Qt Widgets组件。这可以让使用C++代码的Qt组件包含在网页中,作为网页的部分外观。
  • l  在JavaScript中访问C++对象。你可以在JavaScript环境中插入C++对象,让网页脚本直接访问你的数据结构。
  • l  在Qt中执行JavaScript。你可以在C++调用网页环境中的JavaScript函数,触发网页事件。
  • l  共享客户端存储。在JavaScript和C++中你都具有访问数据库的能力,这样当下线时也能共享大量数据。

 

3.  QtWebChannel实现C++和HTML/javascript页面之间通信实例

在实例中在一个界面里实现集成QT C++组件和QWebEnginePage中嵌入的HTML页面通信,左边的lineedit组件中输入的内容通过一个document对象又QtWebChannel传到web页面,并又js解析在HTML页面上显示,右边HTML页面中的INPUT标签中输入的内容也由document对象通过QtWebChannel传到QT C++界面。通过QtWebChannel使得JS能够接收到QT C++中传出的对象,及由C++发出的信号,并与相对应的方法关联,C++也能介绍到由JS传来的对象,并调用相应的槽函数。

 

工程清单如下:


首先,定义一个document对象用于在C++和JS之间传递,该对象实现了信号和槽:

 

Document.h内容:

 

#ifndefDOCUMENT_H
#defineDOCUMENT_H
 
#include<QObject>
#include<QString>
#include"ui_mainwidget.h"
 
namespaceUi{
classMainWidget;
}
 
classDocument:publicQObject
{
    Q_OBJECT
    Q_PROPERTY(QStringtextMEMBERs_textNOTIFYsendText)
 
public:
    explicitDocument(QObject*parent=nullptr):QObject(parent){}
 
    voidsetSendTextText(constQString&text);
    voidsetUi(Ui::MainWidget*ui);
 
publicslots:
    voidreceiveText(constQString&r_text);
 
signals:
    voidsendText(constQString&text);
 
private:
    voiddisplayMessage(constQString&message);
    QStrings_text;
    QStringrecieve_text;
    Ui::MainWidget*mainUi;
};
 
#endif//DOCUMENT_H

 

Document.cpp内容:

 

#include"document.h"
 
voidDocument::setSendTextText(constQString&text)
{
    s_text=text;
    emitsendText(s_text);
}
 
voidDocument::displayMessage(constQString&message)
{
      mainUi->editor->appendPlainText(message);
}
 
/*!
    ThisslotisinvokedfromtheHTMLclientsideandthetextdisplayedontheserverside.
*/
voidDocument::receiveText(constQString&r_text)
{
    displayMessage(QObject::tr("Receivedmessage:%1").arg(r_text));
}
 
voidDocument::setUi(Ui::MainWidget*ui)
{
    mainUi=ui;

}

 

用QT Designer设计主界面,并实现主界面MainWidget类,内容如下:

 

mainwidget.h内容:

 

#ifndefMAINWIDGET_H
#defineMAINWIDGET_H
 
#include"document.h"
 
#include<QWidget>
#include<QString>
 
namespaceUi{
classMainWidget;
}
 
classMainWidget:publicQWidget
{
    Q_OBJECT
 
public:
    explicitMainWidget(QWidget*parent=0);
    ~MainWidget();
 
//publicQ_SLOTS:
//    voidsetEnabled(bool);
 
privateslots:
    voidon_pushButton_clicked();
 
private:
    boolisModified()const;
 
    Ui::MainWidget*ui;
    Documentm_content;
 
};
 
#endif//MAINWIDGET_H

 

mainwidget.cpp内容:

 

#include"mainwidget.h"
#include"ui_mainwidget.h"
#include"previewpage.h"
#include"document.h"
 
#include<QFile>
#include<QWebChannel>
 
MainWidget::MainWidget(QWidget*parent):
    QWidget(parent),
    ui(newUi::MainWidget)
{
    ui->setupUi(this);
 
    PreviewPage*page=newPreviewPage(this);
    ui->preview->setPage(page);
    m_content.setUi(ui);
 
    QWebChannel*channel=newQWebChannel(this);
    channel->registerObject(QStringLiteral("content"),&m_content);
    page->setWebChannel(channel);
 
    ui->preview->setUrl(QUrl("qrc:/index.html"));
 
    ui->editor->setPlainText("hello...\n");
}
 
MainWidget::~MainWidget()
{
    deleteui;
}
 
boolMainWidget::isModified()const
{
    returnui->editor->document()->isModified();
}
 
voidMainWidget::on_pushButton_clicked()
{
 
    m_content.setSendTextText(ui->lineEdit->text());
 
}

 

再定义一个PreviewPage类用于加载HTML页面,在主界面MainWidget类初始化的时候,将他主界面中的WebEngineView初始化为该实例对象,主要初始化代码如下:

PreviewPage*page=newPreviewPage(this);  //创建实例对象

    ui->preview->setPage(page);                 //将对象设置到主界面

ui->preview->setUrl(QUrl("qrc:/index.html"));                               //设置载入的HTML页面

 

previewpage.h内容:

 

#ifndefPREVIEWPAGE_H
#definePREVIEWPAGE_H
 
#include<QWebEnginePage>
 
classPreviewPage:publicQWebEnginePage
{
    Q_OBJECT
public:
    explicitPreviewPage(QObject*parent=nullptr):QWebEnginePage(parent){}
 
protected:
    boolacceptNavigationRequest(constQUrl&url,NavigationTypetype,boolisMainFrame);
};
 
#endif//PREVIEWPAGE_H

 

previewpage.cpp内容:

 

#include"previewpage.h"

 

#include<QDesktopServices>

 

boolPreviewPage::acceptNavigationRequest(constQUrl&url,

                                         QWebEnginePage::NavigationType/*type*/,

                                         bool/*isMainFrame*/)

{

    //Onlyallowqrc:/index.html.

    if(url.scheme()==QString("qrc"))

       returntrue;

    QDesktopServices::openUrl(url);

    returnfalse;

}

 

使用的web页面index.html内容如下:

 

<!DOCTYPEhtml>

<html>

    <head>

       <metahttp-equiv="Content-Type"content="text/html;charset=utf-8"/>

       <scripttype="text/javascript"src="./qwebchannel.js"></script>

       <scripttype="text/javascript">

           //BEGINSETUP

           functionoutput(message)

           {

                varoutput=document.getElementById("output");

                output.innerHTML=output.innerHTML+message+"\n";

           }

           window.οnlοad=function(){

 

           output("settingupQWebChannel.");

           newQWebChannel(qt.webChannelTransport,function(channel){

                //makedialogobjectaccessibleglobally

                varcontent=channel.objects.content;

 

                document.getElementById("send").οnclick=function(){

                    varinput=document.getElementById("input");

                    vartext=input.value;

                    if(!text){

                        return;

                    }

 

                    output("Sentmessage:"+text);

                    input.value="";

                    content.receiveText(text);

                }

 

                content.sendText.connect(function(message){

                    output("Receivedmessage:"+message);

                });

 

                content.receiveText("Clientconnected,readytosend/receivemessages!");

                output("ConnectedtoWebChannel,readytosend/receivemessages!");

           });

           }

 

           //ENDSETUP

       </script>

       <styletype="text/css">

           html{

                height:100%;

                width:100%;

           }

           #input{

                width:400px;

                margin:010px00;

           }

           #send{

                width:90px;

                margin:0;

           }

           #output{

                width:500px;

                height:300px;

           }

       </style>

    </head>

    <body>

       <textareaid="output"></textarea><br/>

       <inputid="input"/><inputtype="submit"id="send"value="Send"οnclick="javascript:click();"/>

    </body>

</html>

 

主程序main.cpp的内容如下:

 

#include"document.h"

#include"mainwidget.h"

#include<QApplication>

 

intmain(intargc,char*argv[])

{

    QApplicationa(argc,argv);

    MainWidgetw;

    w.show();

 

    returna.exec();

}

文中使用的例子可以到链接:http://download.csdn.net/detail/liuyez123/9402132地址下载。

发布了24 篇原创文章 · 获赞 50 · 访问量 35万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 编程工作室 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览