Qt---Javascript/Qt交互、腳本化


Qt提供了對Javascript的良好支持, 如果查閱過文檔你就知道Qt有兩個不同的Js封裝引擎:

  • QScriptEngine
  • QJSEngine

QScriptEngine出現的比較早(自Qt4.3始),基於WebKit的JavaScriptCore引擎,提供的api相對來說比較豐富,但是已經被官方標注為deprecated;QJSEngine則是從Qt5.0開始提供,基於谷歌的V8引擎,是官方建議使用的版本。至於為什么QScriptEngine會被Qt廢棄,各種原因就比較復雜了,有興趣的朋友可以看這個鏈接,我這里簡要概括講一下Qt js模塊的實現歷史及原因:

  1. QScriptEngine---使用自建的js引擎:功能落后、運行非常慢
  2. QScriptEngine---使用JavaScriptCore引擎(WebKit的主引擎):
    • 由此封裝提供的JS API暴露了實現細節
    • 由於設計使用方式的不同一些原有函數無法實現(例如QScriptContext)
    • JavaScriptCore變化太大,沒有一個穩定的API來供QtScript實現想要的功能,每一次引擎的變化都需要QtScript模塊內部進行大的調整。
  3. QScriptEngine---使用V8引擎:V8對外提供的API穩定可嵌入到程序中;但是V8與JavaScriptCore內部細節不同,QtScript API的某些概念無法自然映射到V8上,用V8實現相同性能的舊接口需要相當大的投入,然而QML團隊無法接受這樣的投入花費。
  4. QJSEngine-------使用V8引擎。

1. QScriptEngine VS QJSEngine

從兩個主要的引擎類上來說,相比QScriptEngine,雖然QJSEngine出來的遲,但是核心的功能(加粗)也是支持的,僅在其他一些小功能上有所欠缺(未加粗):

  • 執行腳本字符串。
  • 引擎全局變量配置。
  • 異常處理。
  • Js對象創建
  • Qt類與Js的交互集成。
  • Js擴展。
  • 自定義C++類(非Qt內建)。
  • C++函數與Js的交互集成。
  • Long-running腳本優化處理。
  • 調試跟蹤。

但是畢竟對JavaScriptCore引擎的封裝比較成熟,從QScriptEngine衍生出的技術支持肯定是比較豐富,使用也較為方便。例如QtScript模塊同時包含QScriptClassPropertyIterator類來提供java風格的屬性遍歷功能、QScriptContext類來提供上下文信息,等等。但是隨着Qt新版本的發布,QJsEngine肯定是越來越成熟的。需要注意的是,這兩個應該都不能與Qt的Web模塊交互使用(親測QJSEngine與QWebEngineView交互無效),畢竟都分成了兩個不同的模塊。

2. QJSEngine介紹

這里我們簡單學習QJSEngine,一如既往,我們通過一個小例子來學習當前js引擎提供的主要功能, 實際上使用非常簡單。

2.1 執行腳本

QJSValue QJSEngine::evaluate(const QString &program, const QString &fileName = QString(), int lineNumber = 1)

我們只需要把包含js代碼的字符串傳給 QJSEngine::evaluate()這個函數,就可以直接執行該js代碼。該函數的后兩個參數是可選的文件名和行號,會在js出錯的時候包含在出錯信息里。示例程序中當用戶點擊執行按鈕,我們直接就執行用戶輸入的js代碼:

void MainWindow::on_buttonEvaluateJs_clicked(bool)
{
    ui->lineEditJsResult->setText(
                m_jsEngine.evaluate(ui->lineEditEvaluateJs->text()).toString());
    ui->lineEditJsResult->setEnabled(ui->buttonEvaluateJs->isEnabled());
}

evaluate-js

2.2 配置引擎的全局變量(C++/Js交互)

QJSValue QJSEngine::globalObject() const
QJSValue QJSEngine::newObject()
void QJSValue::setProperty(const QString &name, const QJSValue &value)

通過globalObject()函數我們獲得Js引擎的全局變量,這個變量是一個QJSValue,是Qt對Js數據類型的一個封裝,基本上支持所有Js對象的操作。例如,我們可以判斷兩個QJSValue是否相等、是否嚴格相等、設置屬性、設置原型等。全局對象就是一個可以在Js代碼中直接使用的Js變量,通常我們做的就是在C++代碼里設置全局變量的屬性,然后在Js中直接使用。

newObject()函數用來新建一個Js對象,示例中我們在新建的Js對象上分別設置3個屬性(setProperty())為用戶輸入的左操作數、右操作數和運算符,然后把這個對象設置為全局對象的一個屬性,接着我們在Js代碼中直接調用這3個屬性來進行計算:

void MainWindow::on_buttonEvaluatePropertyCalculateResult_clicked(bool)
{
    auto jsObject = m_jsEngine.newObject();
    jsObject.setProperty("leftOperand", ui->lineEditPropertyLeft->text());
    jsObject.setProperty("rightOperand", ui->lineEditPropertyRight->text());
    m_jsEngine.globalObject().setProperty("cppObject", jsObject);

    ui->lineEditEvaluatePropertyResult->setText(m_jsEngine.evaluate(
        "cppObject.leftOperand" +
        ui->lineEditPropertyOperator->text() +
        "cppObject.rightOperand").toString());
    ui->lineEditEvaluatePropertyResult->setEnabled(
        ui->buttonEvaluatePropertyCalculateResult->isEnabled());
}

calculate-js

2.3 Qt/Js交互(腳本化)

QJSValue newQObject(QObject *object)

Signals and slots, properties and children of object are available as properties of the created QJSValue.

通過newQObject()這個函數,我們可以將Qt類封裝成Js對象,集成到Js引擎中。Qt類的信號槽屬性子對象可以在Js中通過屬性來使用,Qt提供強大的本地功能支持,Js提供靈活的使用方式,想想就很激動。我們可以借此在Js中操控導出的Qt對象、更改界面外觀、實現程序功能的腳本化。

示例中我們導出街面上的一個QPushButton,把它設置為Js引擎全局對象的一個屬性:

m_jsEngine.globalObject().setProperty("cppButton", m_jsEngine.newQObject(ui->buttonChangeInJs));

當用戶點擊這個按鈕的時候,我們讀取本地的Js文件到QString中並執行這段代碼,該Js代碼會調用setStyleSheet()函數(注意這是一個slot)來更改這個按鈕的外觀樣式:

void MainWindow::on_buttonChangeInJs_clicked(bool)
{
    QFile jsFile(":/js/demo.js");
    if (jsFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
        auto jsStr = QString::fromStdString(jsFile.readAll().toStdString());
        auto jsResult = m_jsEngine.evaluate(jsStr);

        if (jsResult.isError())
            ui->buttonChangeInJs->setText(jsResult.toString());
    }
}

function func() {
    cppButton.setStyleSheet('QPushButton { background-color: qlineargradient(\
                                                x0:0, y0:0, x1:1, y1:1, \
                                                stop: 0.0 #111111,\
                                                stop: 0.2 #222222,\
                                                stop: 0.4 #444444,\
                                                stop: 0.6 #888888,\
                                                stop: 0.8 #aaaaaa,\
                                                stop: 1.0 #ffffff);\
                                        color:white;}\
                            QPushButton:hover { border:2px solid blue;\
                                                padding:1ex; }\
                            QPushButton:pressed { background-color: qlineargradient(\
                                                x0:0, y0:0, x1:1, y1:1, \
                                                stop: 0.0 #ff1111,\
                                                stop: 0.2 #22ff22,\
                                                stop: 0.4 #4444ff,\
                                                stop: 0.6 #88ee88,\
                                                stop: 0.8 #aaeeaa,\
                                                stop: 1.0 #ffffff); }')
    cppButton.text = 'Changed in JS'
}
func()

change-button-js

3. 運行結果

result1-link result2-link result3-link

完整代碼見鏈接


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM