# QtChromeDemo
**Repository Path**: delete_h/QtChromeDemo
## Basic Information
- **Project Name**: QtChromeDemo
- **Description**: 使用Qt5.10自带的QtWebEngine, WebChannel进行C++和Js通信,同时进行一般浏览器需要功能的简单尝试。
- **Primary Language**: C++
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 4
- **Created**: 2022-04-13
- **Last Updated**: 2022-04-13
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# QtChromeDemo
使用Qt5.10自带的QtWebEngine, WebChannel进行C++和Qt通信,同时进行一般浏览器需要功能的简单尝试。
# 依赖环境
> Qt5.10 MSVC 2015
> Flash 的Pep插件,即Chrome版本的Flash
运行时,需要将index.html复制到release/debug目录下。
# 主要内容
## 1. Cpp Call js
```cpp
m_pView->page()->runJavaScript("showmsg('I come from cpp call js scripts!');");
```
## 2. WebChannel
```cpp
// 声明变量
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
```
## 3. Web新建窗口拦截
```cpp
// 通过继承 QWebEngineView::createwindows进行拦截,这里修改为永远不新建
class MyWebView : public QWebEngineView{
Q_OBJECT
public:
void contextMenuEvent(QContextMenuEvent *event) {
qDebug()<<"contextMenuEvent:"<globalPos();
event->accept();
}
QWebEngineView *createWindow(QWebEnginePage::WebWindowType type) {
qDebug()<<"createWindow:"<setUrl(QUrl("https://baidu.com"));
return false;
}
return true;
}
};
```
## 5. Url resource 拦截
```cpp
// 通过继承 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 ";break;
case 15:rsrct="ResourceTypeServiceWorker, // the main resource of a service worker.";break;
case 16:rsrct="ResourceTypeUnknown";break;
default : rsrct="未知类型";break;
}
// 这里拦截 url resource 请求
qDebug()<setRequestInterceptor(webInterceptor);
```
## 6. 下载拦截
```cpp
auto profile = QWebEngineProfile::defaultProfile();
QObject::connect(
profile, SIGNAL(downloadRequested(QWebEngineDownloadItem*)),
this, SLOT(downloadRequested(QWebEngineDownloadItem*)));
// 下载的处理
void Dialog::downloadRequested(QWebEngineDownloadItem *download)
{
qDebug()<id()<url()<mimeType();
QString path = QFileDialog::getSaveFileName(this, tr("Save as"), download->path());
if (path.isEmpty())
return;
download->setPath(path);
//download->accept();
download->cancel();
// 通过Download对象可以获取到下载的进度信息
}
```
## 7. 右键菜单
```cpp
class MyWebView : public QWebEngineView{
Q_OBJECT
public:
void contextMenuEvent(QContextMenuEvent *event) {
QMenu *menu = page()->createStandardContextMenu(); // 通过这个获取标准web菜单
const QList 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());
}
};
```
## 8. Qt控件与Web叠加渲染
```cpp
auto a2 = new QLabel(this);
a2->setText("Hello, world!
");
a2->setGeometry(50,50, 200, 100);
a2->setStyleSheet("color: red; background-color: rgba(10,10,100,160);"); // 半透明控件
a2->show();
```
## 9. Qt/JS函数有返回值
默认的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:
```js
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中函数的参数和返回值都是有要求的。