使用Qt5.10自带的QtWebEngine, WebChannel进行C++和Qt通信,同时进行一般浏览器需要功能的简单尝试。
Qt5.10 MSVC 2015
Flash 的Pep插件,即Chrome版本的Flash
运行时,需要将index.html复制到release/debug目录下。
m_pView->page()->runJavaScript("showmsg('I come from cpp call js scripts!');");
// 声明变量
class MyCppObj : public QObject{
Q_OBJECT
public:
MyCppObj(QObject* p):QObject(p){
}
public slots:
void t1(QString msg){
QMessageBox::warning(NULL,"I am a Qt native MsgBox","Msg: "+msg);
}
signals:
void sigNotify(QString msg);
};
// 加入WebChannel
// 早期的 WebView,使用 void QWebFrame::addToJavaScriptWindowObject(const QString& name...
// 新版本通过WebChannel来实现双向通信。
QWebChannel *pWebChannel = new QWebChannel(m_pView->page());
m_pObj = new MyCppObj(NULL);
pWebChannel->registerObject("a1", m_pObj);
m_pView->page()->setWebChannel(pWebChannel);
// cpp call js
emit m_pObj->sigNotify("I come from Cpp call qt sig whcih connect js func! ");
// js call Cpp
<script type="text/javascript" src="qrc:///qtwebchannel/qwebchannel.js"></script>
<script type="text/javascript">
function showmsg(message)
{
var output = document.getElementById("output");
output.innerHTML = output.innerHTML + message + "\n";
}
//Web initial loading
window.onload = function() {
showmsg("I am from web, call js func!");
new QWebChannel(qt.webChannelTransport, function(channel) {
//Get Qt interact object
var a1 = channel.objects.a1;
//Web send message to Qt
document.getElementById("send").onclick = function() {
var input = document.getElementById("input");
if (!input.value) {
return;
}
//Web use the interface of Qt
a1.t1(input.value); // js call cpp
}
//Web connect the Qt signal, then Qt can call "output" function
a1.sigNotify.connect(function(str) {
showmsg(str);
});
});
}
</script>
// 通过继承 QWebEngineView::createwindows进行拦截,这里修改为永远不新建
class MyWebView : public QWebEngineView{
Q_OBJECT
public:
void contextMenuEvent(QContextMenuEvent *event) {
qDebug()<<"contextMenuEvent:"<<event->globalPos();
event->accept();
}
QWebEngineView *createWindow(QWebEnginePage::WebWindowType type) {
qDebug()<<"createWindow:"<<type;
return this;
}
};
// 通过继承 QWebEnginePage::acceptNavigationRequest进行拦截
class MyWebPage : public QWebEnginePage{
Q_OBJECT
public:
MyWebPage(QWebEngineProfile* p, QObject* p2)
:QWebEnginePage(p, p2){
}
public:
bool acceptNavigationRequest(const QUrl &url, NavigationType type, bool isMainFrame) {
qDebug()<<Q_FUNC_INFO<<url.toString()<<type<<isMainFrame;
// 这里拦截 url site 请求
if( url.toString().contains("qq.com") ){
return false;
}
if( url.toString().contains("360.com") ){
this->setUrl(QUrl("https://baidu.com"));
return false;
}
return true;
}
};
// 通过继承 QWebEngineUrlRequestInterceptor::interceptRequest进行拦截
// 也可以通过这里进行资源替换
class WebUrlRequestInterceptor : public QWebEngineUrlRequestInterceptor
{
Q_OBJECT
public:
WebUrlRequestInterceptor(QObject *p = Q_NULLPTR)
:QWebEngineUrlRequestInterceptor(p){
}
void interceptRequest(QWebEngineUrlRequestInfo &info){
QString rsrct = "";
switch(info.resourceType()){
case 0:rsrct="ResourceTypeMainFrame = 0, // top level page";break;
case 1:rsrct="ResourceTypeSubFrame, // frame or iframe";break;
case 2:rsrct="ResourceTypeStylesheet, // a CSS stylesheet";break;
case 3:rsrct="ResourceTypeScript, // an external script";break;
case 4:rsrct="ResourceTypeImage, // an image (jpg/gif/png/etc)";break;
case 5:rsrct="ResourceTypeFontResource, // a font";break;
case 6:rsrct="ResourceTypeSubResource, // an other subresource.";break;
case 7:rsrct="ResourceTypeObject, // an object (or embed) tag for a plugin,";break;
case 8:rsrct="ResourceTypeMedia, // a media resource.";break;
case 9:rsrct="ResourceTypeWorker, // the main resource of a dedicated worker.";break;
case 10:rsrct="ResourceTypeSharedWorker, // the main resource of a shared worker.";break;
case 11:rsrct="ResourceTypePrefetch, // an explicitly requested prefetch";break;
case 12:rsrct="ResourceTypeFavicon, // a favicon";break;
case 13:rsrct="ResourceTypeXhr, // a XMLHttpRequest";break;
case 14:rsrct="ResourceTypePing, // a ping request for <a ping>";break;
case 15:rsrct="ResourceTypeServiceWorker, // the main resource of a service worker.";break;
case 16:rsrct="ResourceTypeUnknown";break;
default : rsrct="未知类型";break;
}
// 这里拦截 url resource 请求
qDebug()<<Q_FUNC_INFO<<": "<<info.requestMethod() <<info.requestUrl().toString()<<rsrct;
}
};
// 安装监听
TWebEngineUrlRequestInterceptor *webInterceptor = new TWebEngineUrlRequestInterceptor();
QWebEngineProfile::defaultProfile()->setRequestInterceptor(webInterceptor);
auto profile = QWebEngineProfile::defaultProfile();
QObject::connect(
profile, SIGNAL(downloadRequested(QWebEngineDownloadItem*)),
this, SLOT(downloadRequested(QWebEngineDownloadItem*)));
// 下载的处理
void Dialog::downloadRequested(QWebEngineDownloadItem *download)
{
qDebug()<<download->id()<<download->url()<<download->mimeType();
QString path = QFileDialog::getSaveFileName(this, tr("Save as"), download->path());
if (path.isEmpty())
return;
download->setPath(path);
//download->accept();
download->cancel();
// 通过Download对象可以获取到下载的进度信息
}
class MyWebView : public QWebEngineView{
Q_OBJECT
public:
void contextMenuEvent(QContextMenuEvent *event) {
QMenu *menu = page()->createStandardContextMenu(); // 通过这个获取标准web菜单
const QList<QAction*> actions = menu->actions();
auto it = std::find(actions.cbegin(), actions.cend(), page()->action(QWebEnginePage::OpenLinkInThisWindow));
if (it != actions.cend()) {
(*it)->setText(tr("Open Link in This Tab"));
++it;
QAction *before(it == actions.cend() ? nullptr : *it);
menu->insertAction(before, page()->action(QWebEnginePage::OpenLinkInNewWindow));
menu->insertAction(before, page()->action(QWebEnginePage::OpenLinkInNewTab));
}
menu->popup(event->globalPos());
}
};
auto a2 = new QLabel(this);
a2->setText("<h1>Hello, world!</h1>");
a2->setGeometry(50,50, 200, 100);
a2->setStyleSheet("color: red; background-color: rgba(10,10,100,160);"); // 半透明控件
a2->show();
默认的Demo中,Qt函数或者js函数有返回值,则返回undefined。
原因是:
Note that all communication between the HTML client and the QML/C++ server is asynchronous. Properties are cached on the HTML side. Furthermore keep in mind that only QML/C++ data types which can be converted to JSON will be (de-)serialized properly and thus accessible to HTML clients. http://blog.csdn.net/xsolver/article/details/15505465
解决办法见Demo:
new QWebChannel(yourTransport, function(channel) {
// Connect to a signal:
channel.objects.foo.mySignal.connect(function() {
// This callback will be invoked whenever the signal is emitted on the C++/QML side.
console.log(arguments);
});
// To make the object known globally, assign it to the window object, i.e.:
window.foo = channel.objects.foo;
// Invoke a method:
foo.myMethod(arg1, arg2, function(returnValue) {
// This callback will be invoked when myMethod has a return value. Keep in mind that
// the communication is asynchronous, hence the need for this callback.
console.log(returnValue);
});
// Read a property value, which is cached on the client side:
console.log(foo.myProperty);
// Writing a property will instantly update the client side cache.
// The remote end will be notified about the change asynchronously
foo.myProperty = "Hello World!";
// To get notified about remote property changes,
// simply connect to the corresponding notify signal:
foo.onMyPropertyChanged.connect(function(newValue) {
console.log(newValue);
});
// One can also access enums that are marked with Q_ENUM:
console.log(foo.MyEnum.MyEnumerator);
});
即,在js函数的最后一个参数之后,加上一个function, 返回值会以函数参数方式提供给该函数。
原因就是,这个通信过程是异步的,返回值不能直接返回。
同时,只有能够被JSON序列化的数据类型可以传递到HTML端,所以Qt中函数的参数和返回值都是有要求的。
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。