標籤:ack 封裝 ane 嚴格 ror 介面 全域對象 log border
Qt提供了對Javascript的良好支援, 如果查閱過文檔你就知道Qt有兩個不同的Js封裝引擎:
QScriptEngine出現的比較早(自Qt4.3始),基於WebKit的JavaScriptCore引擎,提供的api相對來說比較豐富,但是已經被官方標註為deprecated;QJSEngine則是從Qt5.0開始提供,基於Google的V8引擎,是官方建議使用的版本。至於為什麼QScriptEngine會被Qt廢棄,各種原因就比較複雜了,有興趣的朋友可以看這個連結,我這裡簡要概括講一下Qt js模組的實現曆史及原因:
- QScriptEngine---使用自建的js引擎:
功能落後、運行非常慢
。
- QScriptEngine---使用JavaScriptCore引擎(WebKit的主引擎):
- 由此封裝提供的JS API
暴露了實現細節
。
- 由於設計使用方式的不同
一些原有函數無法實現(例如QScriptContext)
。
- JavaScriptCore變化太大,
沒有一個穩定的API
來供QtScript實現想要的功能,每一次引擎的變化都需要QtScript模組內部進行大的調整。
- QScriptEngine---使用V8引擎:
V8對外提供的API穩定
,可嵌入
到程式中;但是V8與JavaScriptCore內部細節不同,QtScript API的某些概念無法自然映射到V8上,用V8實現相同效能的舊介面需要相當大的投入,然而QML團隊無法接受這樣的投入花費。
- 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());}
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());}
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()
3. 運行結果
完整代碼見連結。
Qt---Javascript/Qt互動、指令碼化