終於到了 QT 學習系列的動機篇了,之所以要寫這幾篇 QT 文章,就是因為學了 QT 內如何用JavaScript與QT互動,才想把這一系列的知識紀錄下來 (因為太神奇啦~)。
本篇是承上一篇「QT 學習筆記 – 導入 Dragdrop 與畫線」的程式,再加入使用 JavaScript 移動 ICON 的功能。JavaScript 所能支援的功能,在 QT 端要有相應的 function 來支援,不是完全只靠 JS 即可。另外,目前 JS 只能呼叫 QT 內的 function,無法存取變數,這點我也還沒弄明白,但也足以讓 JS 發揮其功用了。
JS 功能展示
本文的展示,是利用一段 JS 來隨機移動一個 ICON 的位置。由於在 click event 中,會佔用 UI 的 thread,所以無法直接讓 icon 慢慢的移動。如果不加任何 delay,圖示會一次就移到指定位置,如果加 delay 的話,整個畫面會卡住。如果讀者有需要,可以試著把 JS 移到另一個 thread 看看。
JS的程式碼也很易懂,隨機產生一個 0~200的數字,然後移動名為 car 的圖示到該位置。其中的 drag 物件是由 QT 中傳遞過來的,drag物件有一個 moveIcon() 的 function 可以移動指定名稱的 icon。
1 2 |
var pos = Math.floor(Math.random() * Math.floor(200)); drag.moveIcon("car", pos, pos*2) |
程式碼修改
.pro 檔修改
要在 QT 中使用 JS 來控制,需引入 QJSEngine 這個 Library,在 .pro 檔案要加入 qml。
新增元件
在這個範例中會加入2個 QPlainTextEdit元件,與1個button。一個 jsEdit 元件用來放置 JS 程式,另一個 errmsg 用來放置執行錯誤的訊息。
dragwidget.cpp
在此檔中,會加入要給 JS 使用的 function,JS 中目前發現只能使用QT中的function,變數則無法與QT中共用。這裡我們加入的是 moveIcon 的 function,來移動指定的圖示,另外再呼叫 update() 來更新畫面。
1 2 3 4 5 6 7 8 9 10 |
void DragWidget::moveIcon(QString name, int x, int y) { if(name == "car") { carIcon->move(x,y); } if(name == "boat") { boatIcon->move(x,y); } update(); } |
dragwidget.h
要給 JS 使用的 function 和 class,需要加特別的宣告。class 要加上 Q_OBJECT,而 function 要加上 Q_INVOKABLE,沒有加的話會導致呼叫不到。
mainwindow.cpp
在最開頭的地方要加入 #include <QJSEngine> 來引入 header 檔,在 click slot 裡面加入執行 JS 的程式碼。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
void MainWindow::on_pushButton_clicked() { QJSEngine myEngine; # 加入 QJS print console 的功能,才可以在JS程式內使用 print 來 # 印訊息到 console 上 myEngine.installExtensions(QJSEngine::ConsoleExtension); # 將 dragwidget 轉換成 QJS 使用的 QJSValue 物件 'dragItem' QJSValue dragItem = myEngine.newQObject(drag); # 產生一個 QJS 的 array ,準備加入自己的資料 QJSValue nodes = myEngine.newArray(); # 將資料填入 array 內 nodes.setProperty(0, "car"); nodes.setProperty(1, "boat"); # 將剛剛產生的物件,設定進 QJS Engine 內,並給與分別命名為 drag 與 nodes myEngine.globalObject().setProperty("drag", dragItem); myEngine.globalObject().setProperty("nodes", nodes); # 最後執行 jsEdit 內的程式碼 QJSValue retval = myEngine.evaluate(ui->jsEdit->toPlainText()); # 把錯誤訊息填到 errmsg 內 if(retval.isError()) { ui->errmsg->setPlainText( "script error,Line " + retval.property("lineNumber").toString()+ ":" + retval.toString()); } else ui->errmsg->setPlainText(""); } |
JS 在執行時是單線程的,所以 button 按下後,會執行到結束為止。
重編譯執行
當第一次在 dragwidget.h 內加入 Q_OBJECT 後,要將專案完整清除再編譯,不然會看到 「undefined reference to vtable」的錯誤。建議將編譯輸出目錄整個砍掉重編會比較保險,我在這卡了很久。清除也沒法解決,最後是砍掉輸出目錄才 ok的。
JS程式碼
第一個程式碼在開頭有介紹過了,就是隨機產生座標,然後呼叫剛傳入的 drag 物件裡的 moveIcon() 來移動圖示。
1 2 |
var pos = Math.floor(Math.random() * Math.floor(200)); drag.moveIcon("car", pos, pos*2) |
另一個程式碼,展示了存取 array 並印出其內容到 console。
1 2 3 4 |
for(var i=0;i<nodes.length;i++) { print(nodes[i]); } |
結語
在一個程式語言內,再嵌入另一個語言,是個很有趣的技巧。在一般的 C 上,也可嵌入 lua 語言。威力很強大,應用則要看場景了。當母程式夠大夠複雜才會有這種用途吧,我心裡是這樣想的,不過這是個有趣的技巧,多瞭解也好。
下載
本文的原始碼 QJS 範例。