1.前言
最近,C++和WEB本地混合应用开发模式逐渐流行起来,个人也认为标记语言描述的界面是界面开发的一个发展趋势。WPF、JavaFX,当然也少不了Html。基于Html的界面在开发效率,可移植性上都十分有优势,所以也被很多
目前QT官方的文档中对如何从原来的WebKit迁移至QtWebEngine没有提供足够丰富的文档和指导。
本文记录一些从WebKit迁移至QtWebEngine,实现C++与HTML和JS交互的一些经验和例子。
2.使用QtWebEngine和WebChannel模块
在官方提供的PortingfromQtWebKittoQtWebEngine---QWebEngineView*view=newQUrl("http://qt-project.org/"));
view->show();
2)
在Qt对象中访问web页面元素
//myPlugin指向的对象可在HTML中用名字myPluginObject进行访问
webView->page()->mainFrame()->addToJavaScriptWindowObject("myPluginObject",myPlugin);
//当信号signalEmitted被触发时,调用JavaScript的functionToCall函数
webView->page()->mainFrame()->evaluateJavaScript("myPluginObject.signalEmitted.connect(functionToCall);")
官方推荐的使用方式:
方法一:
runJavaScript(const
QString&scriptSource,FunctorOrLambdaresultCallback)
方法二:使用QtWebChannel方式,这是官方的推荐方式,他可以很方便的实现C++和HTML/JS的双向通信,同时实现C++和HTML/JS的解耦,方便开发人员的分工及系统集成,参见后面的例子。
3)在web页面中访问Qt对象
在web页面中可以通过类似于下的JavaScript代码访问Qt对象:
点击访问Qt对象
官方推荐的使用方式:
使用QtWebChannel方式,这是官方的推荐方式,他可以很方便的实现C++和HTML/JS的双向通信,同时实现C++和HTML/JS的解耦,方便开发人员的分工及系统集成,参见后面的例子。
在QT5.5和QT5.6中,利用Qt的QtWebEngine和WebChannel模块,你完全可以进行本地桌面与web混合应用开发,你可以自由地混合JavaScript,样式表,Web内容和Qt组件。基于Chromium的QtWebChannel模块,该模块提供了在QML/C++和HTML/Javascript之间的一个简单、易用的桥接,从而使得开发能够使用Qt和Web技术进行混合开发,目前QT官方也推荐是用QtWebChannel来桥接C++和HTML,参见Qt
WebChannel–bridgingthegapbetweenC++/QMLandtheweb------
首先,定义一个document对象用于在C++和JS之间传递,该对象实现了信号和槽:
Document.h内容:
#ifndefDOCUMENT_H
#defineDOCUMENT_H
#include
#include
#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;
}
用QTDesigner设计主界面,并实现主界面MainWidget类,内容如下:
mainwidget.h内容:
#ifndefMAINWIDGET_H
#defineMAINWIDGET_H
#include"document.h"
#include
#include
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
#include
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
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
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内容如下:
html
>
//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
html{
height:100%;
width:100%;
}
#input{
width:400px;
margin:010px00;
}
#send{
width:90px;
margin:0;
}
#output{
width:500px;
height:300px;
}
主程序main.cpp的内容如下:
#include"document.h"
#include"mainwidget.h"
#include
intmain(intargc,char*argv[])
{
QApplicationa(argc,argv);
MainWidgetw;
w.show();
returna.exec();
}