2015年6月30日 星期二

[Qt] 004 - QML語法介紹


QML Syntax

QML Object

下圖為一個簡單的QML文件,內容與前幾篇文章大同小異,該文件是由Qt Creator建立專案自動產生的,我加上了幾行程式碼來輔助接下來的說明。


QML文件中所看到的RectangleMouseAreaText這些用大括號{ ... }所包起來的區塊,稱為QML Object (在Qt 4.x時代稱為QML element,參考此連結),此篇文章開始我以Qt 5.x的統一用法,稱為QML Object

每一個QML文件包括兩個部分,第一個部分是最上面的 import,表示該文件需要引用那些模組或命名空間(引用的對象可以是內建的模組,也可以是指定的客製化模組,或是指定資料夾的自創QML文件,甚至是JavaScript文件也可以,請參考此連結)。
第二個部份就是QML Object,每一個QML文件只會有一個 root QML object,意即文件的最外層只會有一個 QML Object,此連結所看到的有兩個 root QML Object是不被允許的,所有其他的QML Object都會被包覆在此root object中,這些被包覆的QML Object都是此root object的子物件。
一般來說,寫在同一層的物件,彼此就是兄弟(Sibling)物件,除非特別指定父(parent)物件;以上圖來說,最外層的Rectangle裡面有一個MouseArea跟一個Text,在這個MouseAreaText沒有特別指定父物件的情況下,其parent就是上一層的Rectangle
而同一層物件若有多個,先被寫的會先被繪製(render)出來,物件會有類似圖層的概念,一個一個往上疊;你可以試著連續寫兩個Rectangle,兩個物件在同一層,大小位置都一樣但顏色不同,從結果可以發現,後寫的Rectangle會蓋在先寫的Rectangle上面。


Object Attributes

QML Object就和HTML或XML裡的Tag(標籤)一樣,具有多個attribute與對應的value,這些attribute區分為以下幾類(參考連結)

  • id : 就是 id ( identifier ),以物件導向的程式語言來解釋,前面這些定義好的QML Object就是類別,而這些類別的實體就是物件,每個物件會有自己的名字(Object Name),這個 id的角色就像是Object Name,是獨一無二的唯一識別碼,不過這個id值是可有可無的,如果我們沒有給一個id值,實際上會自動配給一個值,如果需要的話,通常我們會設定id值,方便我們在其他QML Object中需要存取該物件的內容(property或其他)。
  • property : 屬性,每個QML Object都有特定的屬性,每個屬性都有對應的值,屬性都定義在QML Object的文件中,以Rectangle來說(線上文件),widthheight就是property,在線上文件中並看不到Rectangle有定義這兩個屬性,那是因為Rectangle繼承自Item這個QML Object(線上文件),該屬性是來自其物件。除了物件本身定義的屬性之外,我們也可以自行定義屬性,上圖中第4行與第5行就是自定義的屬性,第5行是在Qt Creator編輯時打上property後可以自行產生的一行程式碼,type的地方要填上這個屬性的類型,像是bool、int之類的,name的地方填上該屬性的名稱,然後可以給該屬性assign一個初始值(value)。type的部分,可以參考此連結,看QML有定義哪些屬性值的類型。alias是一種特別的類型,以圖中的第4行為例,mouseArea是自定義的屬性名稱,該屬性的值為mouseArea,表示Rectangle在中,可以用mouseArea這個屬性來代表mouseArea這個id所對應的物件。這邊要特別注意的是,屬性名稱必須以小寫英文字母做為開頭,且名稱只能用英文字母、數字和底線。
  • signal : 訊號,signal是用在物件中發生某些事件時通知使用的,一般會搭配該事件觸發時的後續動作(即下一個attribute : signal handler),其實這就是一般的callback function,我們會透過事先註冊(register)某些事件,在該事件發生時觸發callback function的執行。在Qt裡面稱為signal and slot,我們會另開篇幅說明。以MouseArea這個QML Object來說明(線上文件),具有一個signal稱為clicked,會在滑鼠被點擊時發出訊號讓對應的signal handler做後續處理。signal是可以帶參數的,有帶參數的signal在觸發時就會將參數傳給signal handler使用。
  • signal handler : 通常是以method的型式存在,處理對應signal所觸發的行為,其名稱會跟以signal透過一定的規則來命名,以上面MouseArea提到的clicked這個signal來說,其signal handler的名稱會是onClicked,以此類推,<signalName>的signal Handler就會是on<SignalName>
  • method : 方法,基本上就是一種函式(function),在QML裡的function看起來就跟JavaScript function大同小異,寫法也差不多,除此之外,method也可以是C++的function,這部分牽涉到QML如何跟Qt C++做溝通,我們在之後的文章會介紹。
  • attached properties and attached signal handler : 某一些特定的QML Object在執行時間,具有某些附加的屬性或signal handler讓物件存取,這樣的attribute從名稱也大概看得出來,參考此連結的說明,舉例來說,ListView.isCurrentItem是一種attached properties,用來判斷該Item是否為目前ListView列表中所在的Item;Component.onCompleted是一種attached signal handler,其method內容就是當QML Object實體產生或繪製完成時所需執行的function。這類型的attribute用法比較特別,注意到寫法不是使用Object Name(即前面提到的id)而是Object Type(即QML Object名稱,如ListView)。

QML語法大致介紹到這邊,各類型的attribute在之後文章或範例程式中也會陸續出現。




2015年6月20日 星期六

[Qt] 003 - QML 與Qt Quick


QML 與 Qt Quick

QML

這篇文章我們要來了解一下什麼是QML,QML的全名是Qt Markup Language,是Qt官方開發出來的一種描述使用者介面的語言,於Qt 4.6時所發布。在Qt 4.6以前,Qt在使用者介面的建構方式類似於大多數框架(如Android)的原理,其檔案為附檔名".ui"的檔案,它是一種自定義的XML檔案,透過對應模組的解析,讓底層依照檔案內的定義值繪製圖形(如Button)在頁面上。

這樣的建構方式,對程式開發人員而言,要花不少的時間在調整UI的大小或位置,而QML的設計目的,就是希望語法簡單明瞭,讓UI設計人員也可以輕易學習,省去協同開發上所耗費的整合時間。從QML的語法看起來,有點類似CSS的樣子,同時又支援JavaScript作為其程式邏輯的部分,相信對於稍微有些邏輯概念、但無任何程式語言開發基礎的對象,應該是能輕易理解且上手的。

QML本身是一種結構化的描述式語言,概念上與易懂的HTML類似,所謂的描述式語言就是透過簡單易懂的語法來描述畫面上的元件(包括UI與非UI元件),我們以上一篇文章所看到的QML檔案來做講解,下圖右是呈現的畫面,左邊則是程式碼的部分

這段程式碼引用(import)了QtQuick 2.2跟QtQuick.Window 2.1兩個模組
程式碼描述了畫面中有一個Window,該Window是可見的(visible: true),其寬(width)和高(height)都是360(Pixels)
在這個Window中有一塊MouseArea用來偵測滑鼠事件,該滑鼠區塊佔滿整個父物件(anchors.fill: parent),父物件即Window,當MouseArea中的onClicked(單擊)事件發生時離開程式(Qt.quit();)
另外,Window中還有一個文字區塊(Text),這個文字區塊顯示的文字是"Hello World"(前面加上的qsTr()函式是用來做翻譯檔使用的,之後會另闢文章說明),這個文字區塊置於父物件的中央(anchors.centerIn: parent)
很簡單吧!?利用簡單易懂的文字就可以描述這整個畫面的組成與互動行為。


我們簡單的來改寫一下這個QML檔,看一下執行的畫面有甚麼改變,首先如下圖改寫第17行並加入第18~20行的程式碼,執行畫面如下圖右。這幾行就是針對文字的部分,將其顯示文字改為"Hello QtQuick2",並更改其字體大小(36 pixels),且加上粗體(font.bold: true)與顏色(color: "blue")。


到此為止應該都還非常簡單明瞭,再稍微改寫一下,這次針對MouseArea的部分,我們看到程式碼在onClicked事件中,再次改掉字的顏色("#FF0000",即紅色)與內容(qsTr("Dobule click to quit")),然後把原本離開程式(Qt.quit();)的部分改放置於雙擊(onDoubleClicked)事件中。


好的,QML先介紹到此為止,下一篇文章會再繼續針對QML語法與組成方式的部分做進一步介紹。


Qt Quick

首先,先釐清一下兩個名詞的關係,QML與Qt Quick,很多人會把這兩個名詞混淆,或者說QML就等於Qt Quick,這樣的說法以比較寬鬆的角度來看或許是對的,但嚴格來說是不一樣的。我舉一個例子,相信大家就能明瞭它們的關係。各位應該都知道JavaScript吧!?那如果對Web開發有點經驗的話,應該也聽過甚至使用過jQuery,它們的關係是什麼?JavaScript是一種"語言",而jQuery是採用JavaScript這種語言寫成的一種"框架",這樣說明應該很清楚吧!?

沒錯,以此類推,QML是一種語言,Qt Quick是採用QML這種語言寫成的一種"框架",我們可以視為Qt Quick就是一堆QML物件的集成,我們也可以自行定義並集成一些QML物件,讓它變成一個模組(或稱套件)來使用。

剛才看到的程式碼中,引用了兩個QML模組,分別是QtQuick 2.2與QtQuick.Window 2.1,我們試著把第二行註解掉(QML的註解與C/C++相同,單行用"//",多行用"/* ... */"),可看到原本Window文字呈現黑色,且文字下方有紅色的錯誤提示,表示Qt Creator無法解析該QML物件(Window),因該物件是屬於QtQuick.Window 2.1這個模組中的,被我們註解掉,在沒有引入該模組的情況下,自然無法解析。若強行編譯並執行專案,會無法執行,同時可在下方看到出錯原因(如圖紅框處)。



Qt Quick Application 與 Qt Quick UI

我們在前一篇文章中所建立的專案是一個Qt Quick應用程式(Application),所謂的應用程式,表示該專案編譯的產出是應用程式,也就是一個執行檔,可以單獨運行,運行起來包括使用者介面,這個使用者介面是使用Qt Quick製作而成的。

在建立專案的過程中,選擇專案類型時,其中一個選項是"Qt Quick UI",選擇這個選項的話,該專案就只會有QML檔案,無法單獨運行。這類型的專案就是只有UI的部分,專案中只會有QML檔,沒有C++的原始碼,必須透過Qt內建的應用程式來開啟執行。


建立此類型專案,跟建立Qt Quick Application一樣,都要選擇Qt Quick元件組合。選單中可選擇的主要包括兩類,一類是Qt Quick Control 1.x,這個元件組合主要是UI控制物件,像是Button、Radio Box等等;另一類就是基本的Qt Quick (2.x 或 1.1)。


如果選擇Qt Quick 2.4,可以注意到下面的說明,Qt Quick 2.4有別於之前的模組,會有附檔名為".ui.qml"的檔案,這類型的檔案在Qt Creator中,預設會用Qt Quick Designer這款所見即所得的編輯工具來開啟。同時你可以注意到,這類型的專案,不需要透過編譯就可以用"QML Scene"這套內建工具來執行。


如果你選擇Qt Quick 1.1,與上面相同,都不需要經過編譯就可以透過內建工具來執行專案,差別在於Qt 1.x所採用的工具稱為"QML Viewer",關於"QML Scene"與"QML Viewer"的差異,在文章後半段會說明。


假設我們這邊先建立一個使用Qt Quick 2.4的Qt Quick UI專案,你可以看到,專案內的檔案,除了一個.qmlproject的檔案(角色與前篇文章所看到的.pro檔相同),還有一個主要的QML檔(HelloQtQuick.qml)以及一個自動產生的MainForm.ui.qml檔。



試著把滑鼠移到import後面的QtQuick 2.4上面,會跑出提示文字顯示該模組的所在路徑,在這個專案中,會自動產生一個主要的QML檔案(與專案名稱相同),內容很單純,就是一個MainForm的QML物件,裡面定義了一個函式。


點擊MainForm.ui.qml時,預設會以Qt Quick Designer來開啟,此工具為一所見即所得的編輯器,可以透過簡單的拖曳UI元件,並設定相關參數(如長寬、位置等),用法跟一般專用開發工具(如Visual Studio和Android Studio)很像,不過我們這邊不特別介紹此工具,而是以介紹QML編寫為主。


切換到編輯模式看一下MainForm.ui.qml看一下原始碼的部分,它是由一個Rectangle組成,裡面有一個MouseArea跟一個Text區塊,其實和上篇文章的畫面是一樣的。剛才我們看到主要的QML檔(HelloQtQuick.qml)裡只有一個名為MainForm的QML物件,這個MainForm是自定義的QML物件,也就是MainForm.ui.qml所定義的。其實這個檔案的檔名可以改為MainForm.qml,之所以檔名為xxx.ui.qml的目的是給Qt Creator判別使用Qt Quick Designer來開啟。

你可以注意到,在這邊的MouseArea中並沒有定義任何滑鼠事件,但實際上專案運行時還是能在單擊(onClicked)時離開程式,這邊使用了別名(alias)來達成這個目的,onClicked事件的定義則寫在HelloQtQuick.qml裡,關於別名(alias)的使用方式,後續會有相關篇幅說明。



Qt Quick 1 與 Qt Quick 2

在Qt 4.x時期官方所提供的Qt Quick框架為Qt Quick 1,所有的QML物件都集成在Qt Quick之中,到了Qt 5之後,Qt Quick框架則為Qt Quick 2,在Qt Quick 2中,將QML物件依照其物件類型和功能特性區分成幾個子模組,除此之外還提供了其他QML模組(QtWebKit)方便開發所使用,可參考此連結。Qt Quick 1與Qt Quick 2的差異在於底層繪製引擎(Render Engine)有所不同,然而在QML語法上是不變的,也當然是向下相容的。


QML Viewer 與 QML Scene

文章開頭的範例,是開啟了一個Qt Quick UI的專案,使用Qt Quick 2.4模組,從專案的執行設定可以看到,會使用"QML Scene"執行(如下圖紅框處)。前面也有提到,此類專案無法單獨運興,就是要靠QML Scene來解析QML檔案。


試著執行一下方才建立的專案,可以看到視窗標題有"qmlscene"的字樣


QML Scene可以視為QML的瀏覽器,就像是Web Browser與HTML文件的關係一樣,透過QML Scene載入QML文件,就可以依照QML內的定義來呈現結果。
QML Scene可解析使用Qt Quick 2.x相關模組的QML文件,相對於Qt Quick 2之於QML Scene,Qt Quick 1則是使用QML Viewer來解析;意即,使用了Qt Quick 1.x模組的QML文件,是無法用QML Scene來載入的,必須過QML Viewer來呈現其結果。

我們可以試著新建一個Qt Quick UI專案,選擇使用Qt Quick 1.1模組


自動產生的程式碼跟前面選擇Qt Quick 2的是一樣的,差別在引用的模組版本為QtQuick 1.1


專案開啟後,可以在執行設定看到,會使用"QML Viewer"執行(如下圖紅框處,因為中文版所以顯示QML檢視器)


透過Qt Creator來執行此類型的專案,就跟直接開啟QML Scene或QML Viewer來載入QML文件的結果是一樣的。QML Scene或QML Viewer這兩個內建應用程式,在Qt安裝時就會一併安裝至指定目錄,目錄在Qt的安裝目錄下(下圖示我的安裝目錄,一開始裝了Qt 5.3跟Qt 5.4,相對在mingw4.9跟mingw4.8的bin資料夾下都會有)


試著使用QML Scene來載入剛才建立的專案中的QML文件(HelloQtQuick.qml),我們可看到跟直接在Qt Creator上執行該專案的結果相同。



現在回到Qt Creator,我們先刻意把MainForm.ui.qml檔中 import 的模組改為 QtQuick 1.0,這時候Qt Creator會偵測到錯誤,因為在建立專案的時候是選擇Qt Quick 2.x,卻在QML文件中 import了Qt Quick 1.x,這種情況下會無法執行該專案,這是Qt Creator自動偵測的必然結果。


這時候,我們一樣用QML Scene來再開啟一次 HelloQtQuick.qml,就會發現無法正常呈現。

接著,我們開啟QML Viewer來載入我們剛才修改的QML檔(MainForm.ui.qml),是可以正常呈現的。






反之,你也可以試著將剛才修改的 import 模組改回 Qt Quick 2.3,然後再用QML Viewer去載入該文件,看看有甚麼樣的結果。

從以上說明,我們可以大概了解Qt Quick Application跟Qt Quick UI的不同,前一篇文章我們建立的是Qt Quick Application專案,與此篇文章所建立的Qt Quick UI專案執行結果相同,差別在Qt Quick Application有C++原始碼的部分,且可編譯成執行檔;而Qt Quick UI專案須透過QML Scene或QML Viewer執行。
我們可以視為Qt Quick Application中存在一個功能與QML Scene/QML Viewer類似的解析器,透過編譯將此解析器與QML文件包裹起來,變成一個可獨立運行的執行檔,這個解析器就是C++程式碼的部分,我們之後再針對此部分另闢一篇文章說明。