QCefView(2)—— 實現Qt與JS之間的通訊

目錄

 

前言   

主要項目文件

Qt與JS通訊

1、Qt發送消息給JS部分:

2、Qt接收JS消息部分:

3、JS接收Qt消息部分:

4、JS發送消息給Qt:

程序運行效果圖:


前言   

   在上一篇(QCefView(1)—— CMAKE項目、庫文件生成和項目測試)同大家一起分享了QCefView的編譯環境搭建、庫文件編譯,和測試項目運行等,本章,主要和大家分享和研究下基於QCefView實現的Qt與JS通訊的部分。

主要項目文件

在測試項目中我們看到 customcefview.h文件中:

#ifndef CUSTOMCEFVIEW_H
#define CUSTOMCEFVIEW_H

#include <include/QCefView.h>
#include "configurationTool/configuration.h"
#include "MessageDistribution.h"

class CustomCefView : public QCefView
{
	Q_OBJECT

public:
	CustomCefView(const QString& url, QWidget *parent);
	~CustomCefView();

    /*通過事件註冊的方式,將消息發送給JS*/
	void changeColor(); // 背景顏色發生改變
	void languageChange(QString language); // 語言發生改變的槽函數

/*接收JS端發送給Qt的消息*/
protected slots:
	virtual void onQCefUrlRequest(const QString& url) override;
	virtual void onQCefQueryRequest(const QCefQuery& query) override;
	virtual void onInvokeMethodNotify(int browserId, int frameId, const QString& method, const QVariantList& arguments) override;
	void onLoadingStateChanged(bool isLoading, bool canGoBack, bool canGoForward) override;
	void onLoadStart() override;
	void onLoadEnd(int httpStatusCode) override;
	void onLoadError(int errorCode, const QString &errorMsg, const QString &failedUrl) override;
private:
	Configuration configuration;        // 通訊配置工具
	MessageDistributionHandle msgHandle;// 消息分發處理
};

#endif // CUSTOMCEFVIEW_H

    對應的customcefview.cpp 中,我們來看如何實現事件註冊,以及接收到JS消息後,Qt如何處理的:

#include <windows.h>
#include <QMessageBox>
#include <QColor>
#include "customcefview.h"


CustomCefView::CustomCefView(const QString& url, QWidget* parent)
  : QCefView(url, parent)
{}

CustomCefView::~CustomCefView() {}


/// 註冊一個窗體顏色改變的事件==》Qt發送消息到網頁端,網頁端接受該事件消息後響應
void CustomCefView::changeColor()
{
  qsrand(::GetTickCount());
  QColor color(qrand());

 /****************************************************************/
  QCefEvent event("colorChangedEvent");
  event.setStringProperty("color", color.name()); // 定義事件屬性參數
  broadcastEvent("colorChange", event);           // 廣播事件
 /****************************************************************/
}

//// 註冊一個語言槽函數
void CustomCefView::languageChange(QString language) {
	QCefEvent event("languageChangedEvent");
	event.setStringProperty("language", language);
	broadcastEvent("languageChange", event);
}


void CustomCefView::onQCefUrlRequest(const QString& url)
{
  QString title("QCef Url Request ...[function] onQCefUrlRequest");
  QString text = QString("Current Thread: QT_UI\r\n"
                         "Url: %1")
                   .arg(url);

  QMessageBox::information(this->window(), title, text);
}

/// 接收由JS發送過來的消息並回包
void CustomCefView::onQCefQueryRequest(const QCefQuery& query)
{
  QString title("QCef Query Request");
  QString text = QString("Current Thread: QT_UI\r\n"
                         "Query: %1")
                   .arg(query.reqeust());

  QMessageBox::information(this->window(), title, text);

  QString response = query.reqeust().toUpper();
  query.setResponseResult(true, response);
  responseQCefQuery(query);
}


/// 接受由JS發送的消息
void  CustomCefView::onInvokeMethodNotify(int browserId, int frameId, const QString& method, const QVariantList& arguments)
{
    /** 注意:JS端通過調用 invokeMethod 方法,實際上會有兩個參數變量傳遞過來,
     ** 一個是 const QString& method, 通過method我們可以進行消息過濾,在Qt中進行消息分發處理
     ** 另一個是const QVariantList& arguments,通過變量類型我們可知,這個參數同時可以傳遞多種 
     ** 不同類型的數據到Qt中來,我們可以通過解析消息體,做具體的業務處理。
    **/
	qDebug() << "[browserId]:" << browserId << " [frameId]:" << frameId << " [method]:" << method << "[arguments]:" << arguments;


  if (0 == method.compare("onDragAreaMouseDown")) { // for test
    HWND hWnd = ::GetAncestor((HWND)getCefWinId(), GA_ROOT);

    // get current mouse cursor position
    POINT 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));
    return;
  }
  else if (0 == method.compare("configurationTool")) { // 調用通訊配置工具
	  configuration.show();
  }
  else if (0 == method.compare("openLuaEditor")) { // 打開lua腳本編輯器
	  QString Luaparam(arguments.last().value<QString>());
	  msgHandle.luaScriptEditHandle(Luaparam);
	  
  }
  else {
	  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);
  }
}


void CustomCefView::onLoadingStateChanged(bool isLoading, bool canGoBack, bool canGoForward)
{
	qDebug() << "isLoading:" << isLoading << "canGoBack" << canGoBack << "canGoForward" << canGoForward;
}

void CustomCefView::onLoadStart()
{
	qDebug() << "onLoadStart";
}

void CustomCefView::onLoadEnd(int httpStatusCode)
{
	qDebug() << "onLoadEnd" << httpStatusCode;
}

void CustomCefView::onLoadError(int errorCode, const QString &errorMsg, const QString &failedUrl)
{
	qDebug() << "onLoadError" << errorCode << errorMsg << failedUrl;
}

Qt與JS通訊

1、Qt發送消息給JS部分:

void CustomCefView::changeColor()
{
  qsrand(::GetTickCount());
  QColor color(qrand());

 /***********************以註冊消息事件的方式將消息從Qt發送到JS*******/
  QCefEvent event("colorChangedEvent");
  event.setStringProperty("color", color.name()); // 定義事件屬性參數
  broadcastEvent("colorChange", event);           // 廣播事件
 /****************************************************************/
}

2、Qt接收JS消息部分:

從上面代碼中可以看到,實際上有三種接收的方式,這裏我們同樣只關注因爲invokeMethod()方法JS發送給Qt的消息處理

void  CustomCefView::onInvokeMethodNotify(int browserId, int frameId, const QString& method, const QVariantList& arguments)
{
    /** 注意:JS端通過調用 invokeMethod 方法,實際上會有兩個參數變量傳遞過來,
     ** 一個是 const QString& method, 通過method我們可以進行消息過濾,在Qt中進行消息分發處理
     ** 另一個是const QVariantList& arguments,通過變量類型我們可知,這個參數同時可以傳遞多種 
     ** 不同類型的數據到Qt中來,我們可以通過解析消息體,做具體的業務處理。
    **/
	qDebug() << "[browserId]:" << browserId << " [frameId]:" << frameId << " [method]:" << method << "[arguments]:" << arguments;


  if (0 == method.compare("onDragAreaMouseDown")) { // for test
    HWND hWnd = ::GetAncestor((HWND)getCefWinId(), GA_ROOT);

    // get current mouse cursor position
    POINT 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));
    return;
  }
  else if (0 == method.compare("configurationTool")) { // 調用通訊配置工具
	  configuration.show();
  }
  else if (0 == method.compare("openLuaEditor")) { // 打開lua腳本編輯器
	  QString Luaparam(arguments.last().value<QString>());
	  msgHandle.luaScriptEditHandle(Luaparam);
	  
  }
  else {
	  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);
  }
}

 

對照我們看看在html中如何處理Qt與JS通訊部分具體的消息收發的:

<html>
<head>
    <script>
        // 捕獲Qt發送給JS事件信號的函數
        function onColorChanged(event) {
            document.getElementById("main").style.backgroundColor = event["color"];
            alert(event["color"]);
        }

        function onLanguageChanged(event) {
            OpenPopupEx(event["language"]); // ==》接收到Qt語言改變的消息
            QCefClient.invokeMethod("JsGetLanguageChanged", 1, false, "arg3");//==>返回確認信息給Qt
        }

        /// 在頁面加載時增加事件監聽
        function onLoad() {
            if (typeof (QCefClient) == 'undefined') {
                return;
            }

            // 增加顏色改變事件的監聽
            QCefClient.addEventListener("colorChange", onColorChanged);
            QCefClient.addEventListener("languageChange", onLanguageChanged);
        }

        /// 在此說明,invokeMethod("funcname",...)中可以自定義傳入多個參數類型
        /// 在Qt中將以QVariantList& arguments 變量接收 ==> eg: [arguments]: (QVariant(int, 1), QVariant(bool, false), QVariant(QString, "configurationTool"))
        function onInvokeMethodClicked() {
            QCefClient.invokeMethod("TestMethod", 1, false, "arg3");
        }

        /// 打開通訊配置工具
        function onOpenConfigurationTool() {
            QCefClient.invokeMethod("configurationTool", 1, false, "configurationTool");
        }

        /// 打開lua腳本編輯器
        function onOpenLuaEditor() {
            QCefClient.invokeMethod("openLuaEditor", 1, false, document.getElementById("luamessage").value);
        }

        function onDragAreaMouseDown() {
            QCefClient.invokeMethod("onDragAreaMouseDown");
        }


        /// 使用QCefQuery 發送信息給Qt
        function onQCefQueryClicked() {
            var query = {
                request: document.getElementById("message").value,
                onSuccess: function (response) {
                    alert(response);
                },
                onFailure: function (error_code, error_message) {
                    alert(error_message);
                }
            }
            window.QCefQuery(query);
        }


        /**
         * getFuntionName()
         * @param {Function} func
         * @return {String}
         */
        function getFunctionName(func) {
            if (typeof func == 'function' || typeof func == 'object') {
                var name = ('' + func).match(/function\s*([\w\$]*)\s*\(/);
            }
            return name && name[1];
        }
        /**
         * trace
         * @param [int] [count=10]
         * @return {String}
         */
        trace = function () {
            var stack = [],
                caller = arguments.callee.caller;

            while (caller) {
                stack.unshift(getFunctionName(caller));
                caller = caller && caller.caller;
            }
            return 'functions on stack:' + '\n' + stack.join('\n');
        }

        function OpenPopup() {
            var popup = window.open("", "PopupWindow", "width=800, height=600");
            popup.document.write("<p>This is popup window.</p>");
        }

        function OpenPopupEx(msg) {
            var popup = window.open("", "PopupWindow", "width=800, height=600");
            popup.document.write("<p>" + msg + "</p>");
        }


    </script>
</head>

<body onload="onLoad()" id="main">
    <h1 align="center" style="font-size:12pt; font-family:MS Shell Dlg 2;">Web Area</h1>
    <!--<div align="center" id="dragarea" style="height:40px; background-color: gray; color: white;" onmousedown="onDragAreaMouseDown()">
        <span style="font-size:18pt; font-family:MS Shell Dlg 2;">you can drag this window in this area!</span>
    </div>-->
    <div align="center">
        <label> Test Case for InvokeMethod </label>
        <br />
        <input type="button" value="Invoke Method" onclick="onInvokeMethodClicked()" />
        <br />
        <br />
        <label> Test Case for QCefQuery </label>
        <br />
        <textarea id="message" style="width:320px; height:120px;">this message will be processed by native code.</textarea>
        <br />
        <input type="button" value="Query" onclick="onQCefQueryClicked()" />
        <br />
        <br />
        <label>Test Case for QCefUrlQuery</label>
        <br />
        <!--<a href="qcef://test/a/b">qcef://test/a/b</a>-->
        <a href="http://www.baidu.com/">百度</a>
        <br />
        <br />
        <button onclick="OpenPopup()">Open Popup</button> <input type="button" value="Open ConfigurationTool" onclick="onOpenConfigurationTool()" />
        <input type="button" value="Open LuaEditor" onclick="onOpenLuaEditor()" />
        <br />
        <textarea id="luamessage" style="width:600px; height:300px;">{"data" : { "rows" : { "flag_addr_label_id" : "0","font_size" : 11,"id" : 0,"name" : "test","save_bit_address_flag" : 0, "text" :"getHmiModel()","trigger_addr_label_ids" :"","trigger_addr_labels" : "","trigger_type" : 0,"type" : 1,"var_list" : ""}}}"
</textarea>
        <br />
    </div>
</body>
</html>

3、JS接收Qt消息部分:

 function onLoad() {
            if (typeof (QCefClient) == 'undefined') {
                return;
            }

            // 增加顏色改變事件的監聽
            QCefClient.addEventListener("colorChange", onColorChanged);
            QCefClient.addEventListener("languageChange", onLanguageChanged);
        }

在onLoad() 方法中實際上是做了一個事件監聽的,對應我們在Qt端註冊的 ”colorChange“ 和"languageChange",當Qt端調用changeColor ()和languageChange()方法時,會觸發對應的事件,JS端捕獲該事件並做處理,實際上就是實現了Qt(發)==》JS(收)的一個通訊過程。

4、JS發送消息給Qt:

在QCefView封裝中,我們可以看到,JS發送消息給Qt實際上是有三種方式的,這裏我們只重點關注invokeMethod() 方法:

        function onOpenConfigurationTool() {
            QCefClient.invokeMethod("configurationTool", 1, false, "configurationTool");
        }

從上可以看到,在調用invokeMethod()方法時候,傳遞了4個參數,分別是:

method :   [ string ]   "configurationTool"

arguments:  [ int ] 1

                    [ bool ]  false

                   [ string ]   "configurationTool"

結合我們在Qt講到的接收JS消息和對應處理,一目瞭然了。

 

程序運行效果圖:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章