- 背景
- 运行环境
- qcefview源码阅读
- 代码目录
- 代码结构
- 列举的通讯方式
- 通讯方式一,widget中的函数通知web页面改变
- 通讯方式二、鼠标拖拽事件
- 通讯方式三、c++中invoke函数响应(两种UrlRequest、QueryRequest)
- QCefView中定义的其他方法
- 实践
背景
调研在qt中使用web技术的几种方式,cef3可以单独升级谷歌内核。qcefview是封装了qt和web通信的库。
运行环境
qt5.15.2: qt4.8不支持的太多,qt5.9不支持用例里的随机数类等
vs2017: 2010内置的msvc编译器不支持c++11的Mutex等
qcefview: 2021年9月3日下的github最新的压缩包
chrome内核:cef_binary_89.0.12+g2b76680+chromium-89.0.4389.90_windows32
cmake:准备好代码和目录后直接运行自带的cmake脚本生成工程文件,直接编译成功没其他问题。
qcefview源码阅读
利用了qt元对象系统的反射技术。
qml中调用c++的方法也是一样的,c++注册函数为Q_INVOKABLE...
代码目录
除了chromium的源码外,这个项目代码量都不大,文件二三十个。
+--------------------------------------------------------------+ | QCefViewTest | | 列举了几种通讯方式 | | +----------------------------------------------------+ | | | QCefView(windows) | | | | 封装了对应QCefWindow的操作主要是QCefView类 | | | | +-------------------------------------------+ | | | | | CefViewCore(chromium):对chromium的操作封装 | | | | | | 代码量不大,主要是CefViewBrowserHandler类. | | | | | | 该类继承自CefClient,重载了浏览器ui操作 | | | | | | | | | | | +-------------------------------------------+ | | | | | | | +----------------------------------------------------+ | | | +--------------------------------------------------------------+
代码结构
Outline of QCefView: +--------------------------------------------------------------+ | QCefView(QWidget) | | | | +----------------------------------------------------+ | | | WindowWrapper(QWidget) | | | | | | | | +-------------------------------------------+ | | | | | CefWindow(QWindow) | | | | | | | | | | | | | | | | | | | | | | | +-------------------------------------------+ | | | | | | | +----------------------------------------------------+ | | | +--------------------------------------------------------------+ Remarks: The WindowWrapper and CefWindow are transparent to upper layer user.
列举的通讯方式
也可参考CSDN博文.
通讯方式一,widget中的函数通知web页面改变
//1、连接信号槽函数connect(ui.btn_changeColor, SIGNAL(clicked()), this, SLOT(onBtnChangeColorClicked()));
//2、槽函数
QCefViewTest::onBtnChangeColorClicked()
{cefview->changeColor();
}
//3、具体执行
void
CustomCefView::changeColor()
{QColor color(0,0,0);QCefEvent event("colorChange");//QCefView封装好的event.setStringProperty("color", color.name());broadcastEvent(event);
}
//4、Implementation实例(pImpl_)
bool
QCefView::broadcastEvent(const QCefEvent& event)
{if (pImpl_)return pImpl_->triggerEvent(event.objectName(), event, CefViewBrowserHandler::ALL_FRAMES);return false;
}
//5、发送Event后js中改变主窗体背景色function onColorChanged(event) {var kvlist = [];for (var key in event) {kvlist.push(key + ":" + event[key])}alert(kvlist.join('\n'));document.getElementById("main").style.backgroundColor = event["color"];}
//跟到后面是进程间通讯了,还没看到添加listener的代码,onColorChanged是js中的函数名似乎没用function onLoad() {if (typeof (CallBridge) == 'undefined') {return;}//addEventListener注意CallBridge.addEventListener("colorChange", function (event) {var kvlist = [];//下面这段for循环啥意思for (var key in event) {kvlist.push(key + ":" + event[key])}//alert(kvlist.join('\n'));document.getElementById("main").style.backgroundColor = event["color"];})}
具体看看QCefEvent。
通讯方式二、鼠标拖拽事件
//1、js中的调用function onDragAreaMouseDown() {CallBridge.invokeMethod("onDragAreaMouseDown");}
//2、c++直接在widget中响应这个函数,重载的函数
void
CustomCefView::onInvokeMethodNotify(int browserId, int frameId, const QString& method, const QVariantList& arguments)
{//等于这个方法函数时怎么响应if (0 == method.compare("onDragAreaMouseDown")) {
#if defined(OS_WINDOWS)HWND hWnd = ::GetAncestor((HWND)(this->window()->winId()), GA_ROOT);// get current mouse cursor positionPOINT pt;::GetCursorPos(&pt);// in case the mouse is being captured, try to release it::ReleaseCapture();// simulate that the mouse left button is down on the title area::SendMessage(hWnd, WM_NCLBUTTONDOWN, HTCAPTION, POINTTOPOINTS(pt));
#endifreturn;} else if (
//3、CCefDelegate中调用
void
CCefDelegate::invokeMethodNotify(int browserId,int frameId,const std::string& method,const CefRefPtr<CefListValue>& arguments)
//分发事件中的调用CefViewBrowserHandler为继承CefClient浏览器客户端
bool
CefViewBrowserHandler::DispatchNotifyRequest(CefRefPtr<CefBrowser> browser,CefProcessId source_process,CefRefPtr<CefProcessMessage> message)
通讯方式三、c++中invoke函数响应(两种UrlRequest、QueryRequest)
在CustomCefView类中使用了几种通讯方式,用了响应web UI的交互动作。都是采用invokeMethod函数的方式响应。实现跨线程或者跨进程的通讯,在web中调用qt的响应函数。
主要实现CustomCefView类定制的交互响应函数
//函数原型
bool QMetaObject::invokeMethod ( QObject * obj, const char * member, Qt::ConnectionType type, QGenericReturnArgument ret, QGenericArgument val0 = QGenericArgument( 0 ), QGenericArgument val1 = QGenericArgument(), QGenericArgument val2 = QGenericArgument(), QGenericArgument val3 = QGenericArgument(), QGenericArgument val4 = QGenericArgument(), QGenericArgument val5 = QGenericArgument(), QGenericArgument val6 = QGenericArgument(), QGenericArgument val7 = QGenericArgument(), QGenericArgument val8 = QGenericArgument(), QGenericArgument val9 = QGenericArgument() ) [static]
具体为:
...
QMetaObject::invokeMethod(this,[=]() {QString title("QCef InvokeMethod Notify");QString text = QString("Current Thread: QT_UI\r\n""Method: %1\r\n""Arguments: ...").arg(method);QMessageBox::information(this->window(), title, text);},Qt::QueuedConnection);
...
参数二member直接用匿名函数表示实现了,注意最后打印的线程ID为web ui线程。
UrlRequest简单直接调用invokeMethod
QueryRequest多了一点
QString response = query.reqeust().toUpper();query.setResponseResult(true, response);responseQCefQuery(query);
QCefView中定义的其他方法
QCefView::Implementation实例中的定义
void closeAllBrowsers()
void addLocalFolderResource(const QString& path, const QString& url)
void addArchiveResource(const QString& path, const QString& url, const QString& password)
void navigateToString(const QString& content)
void navigateToUrl(const QString& url)
bool browserCanGoBack()
bool browserCanGoForward()
void browserGoBack()
bool browserIsLoading()
void browserReload()
void browserStopLoad()
bool triggerEvent(const QString& name, const QCefEvent& event, int frameId = CefViewBrowserHandler::MAIN_FRAME)
bool responseQCefQuery(const QCefQuery& query)
void notifyMoveOrResizeStarted()
bool sendEventNotifyMessage(int frameId, const QString& name, const QCefEvent& event)
...
// create the browser,实现了很多chrome内核的原生接口
pQCefViewHandler_ = new CefViewBrowserHandler(pCefDelegate_);
CCefManager::getInstance().initializeCef();才是对CEF真正的初始化者
实践
D:\code\qcefview\QCefView-main\test\QCefViewTest
在此工程上修改使用重载 QCefView。
分两部分工作:
1、js前端框架jEasyUI开发的简单前端
2、js与qt通讯的粘合部分重载QCefView是否都能实现