在《》中我們提到 QML 文法和 Json 類似,請參考《》檢視 Json 文法。當然這裡我們是期望從零開始也能學會 QML ,是以呢,你也可以直接往下看。
QML 檔案的字尾是 qml ,其實就是個文本檔案。下面是 一個簡單的 QML 檔案:
這個簡單的 QML 檔案的開始是 import 語句,如 import QtQuick 2.0 這句,會引入 QtQuick 2.0 子產品,哇,真是廢話!接着廢話吧。 import 和 C++ 中的 #include 類似,與 Java 中的 import 效果一樣,與 JavaScript 中的……唐僧了,打住。
Rectangle{ } 語句,定義了一個類型為 Rectangle 的對象。如果你看了《》一文中有關 Json 的文法描述,應該已經知道對象要用一對花括号來描述。沒錯, QML 裡也是這樣,不過呢,花括号前要寫上對象的類型。就這麼簡單!
示例 QML 文檔中有兩個對象,一個是 Rectangle ,一個是 Image 。
在花括号之間,是對象的屬性描述(還可以有其它的,後面再說),屬性是以 "property: value" 形式指定的,這點和 Json 一樣。如你所見, Rectangle 對象有 width 、 color 等屬性。
屬性可以分行書寫,此時語句後可以不要 ";" 号,不過筆者建議 C++ 程式猿都加上 ";" ,這會避免你患上精神分裂症。當然,也可以把多個屬性寫在一行内,多個屬性之間必須以 ";" 分割。如下所示:
我強烈建議你不要這麼幹!除非有代碼意外的原因,比如排版需要,比如老闆覺得你代碼行數太多……
在《》中筆者已經提到, QML 支援 JavaScript 表達式。比如你可以這樣改寫 Rectangle 對象的寬、高屬性:
我隻是示意啊,你在實際項目中可别這麼寫,這種行為往不好聽裡說,有點兒腦殘……當然我也可以舉一個有意義的示例:
在這個示例中我指定了按鈕風格中的背景矩形,在按鈕有焦點時邊框寬度為 2 沒有焦點時寬度為 1 。語句 "border.width: control.activeFocus ? 2 : 1" 使用了 JavaScript 的 "?:" 三元雲算法( C++ 中貌似也有……)。
另外,慧眼如你,可能已經注意到,上面的表達式中我使用了 "control.activeFocus" ,沒錯,在表達式中可以引用其它對象及其屬性。當你這麼做的時候,待指派的屬性就和你所引用的對象的那個屬性建立了關聯,當被引用屬性發生變化時,表達式的值會重新計算,而待指派的屬性也會變化。
也許你心中已經有了疑問:如何引用一個對象呢?答案是:通過對象的 id 值來引用一個對象。看這裡:
上面的示例中,退出按鈕使用 id( openFile )引用了打開按鈕。
我的乖呀,anchors 是什麼東東……先别管它,下一篇會講到。
在 QML 中,注釋與 C++ 中一樣,單行以 "//" 開始,多行以 "/*" 開始以 "*/" 結束。
注釋是不被執行的,添加注釋可對代碼進行解釋或者提高其可讀性。注釋同樣還可用于防止代碼執行,這對跟蹤問題是非常有用的。
使用注釋的示例 QML :
其實, QML 中的屬性,就是我們非常熟悉的 C++ 中的成員變量……
屬性名的首字母一般以小寫開始,如我們看煩了的 width 屬性。
如果屬性名以多個單詞表示,那麼第二個及以後的單詞,首字母大寫。
可以在 QML 文檔中使用的類型大概有三類:
由 QML 語言本身提供的類型
使用 QML 子產品注冊 C++ 類型
由 QML 子產品提供的類型
我們先看 QML 語言提供的基本類型。
QML 支援的基本類型包括整型、實數型、布爾、字元串、顔色、清單等等。這些基本類型有些是和 JavaScript 語言的基本類型對應的。
還是之前的示例,修改了一下,通過注釋标注了屬性類型:
注意, QML 中屬性是有類型安全檢測的,也就是說你隻能指定與屬性類型比對的值,否則會報錯。
請使用 Qt 助手的索引模式,以"qml basic types " 為關鍵字檢索,找到 QML Basic Types 頁面來檢視完整的類型清單和每個類型的詳情。
Qt 的 QML 子產品還未 QML 引入的很多 Qt 相關的類型,如 Qt 、 QtObject 、Component 、 Connections 、 Binding 等,請使用 Qt 助手檢索 "qt qml qml types" 來了解。
之前在介紹表達式時提到了 id 屬性,這裡展開描述一下。
一個對象的 id 屬性是唯一的,在同一個 QML 檔案中不同對象的 id 屬性的值不能重複。當給一個對象指定了 id ,就可以在其它對象或腳本中通過 id 來引用該對象。在“表達式”一節中我們已經示範了如何通過 id 來引用一個對象。
請注意, id 屬性的值,首字元必須是小寫字母或下劃線并且不能包含字母、數字、下劃線以外的字元。
清單屬性類似于下面這樣:
清單是包含在方括号内,以逗号分隔的清單元素。看起來是不是挺熟悉?在《》中,我們舉過 Json 數組的例子,再看看:
其實清單和 JavaScript 的數組是類似的,其通路方式也一樣:
length 屬性提供了清單内元素的個數
清單内的元素通過數組下标來通路([index])
值得注意的是,清單内隻能包含 QML 對象,不能包含任何基本類型(如整型、布爾型)。這點與 Json 是不一樣的。下面是通路清單的示例:
如果你一個清單内隻有一個元素,也可以省略方括号。如下所示:
不過筆者還是建議你始終使用方括号,哪怕其中隻有一個元素。
有沒有什麼問題?有就要說啊,悶在心裡會憋壞自己的。好吧,你不說我就說了。在我們通路清單的示例中出現了一個新的内容,Component.onCompleted :{} ,這是什麼東東呢?接下來我們就來講它。
信号處理器,其實等價于 Qt 中的槽。但是我們沒有看到類似 C++ 中的明确定義的函數……沒錯,就是這樣,你的的确确隻看到了一對花括号!對啦,這是 JavaScript 中的代碼塊。其實呢,你可以了解為它是一個匿名函數。而 JavaScript 中的函數,其實具名的代碼塊。函數的好處是你可以在其它地方根據名字調用它,而代碼塊的好處是,除了定義它的地方,沒人能調用它,一句話,它是私有的。代碼塊就是一系列語句的組合,它的作用就是使語句序列一起執行。
讓我們回頭再看信号處理器,它的名字還有點兒特别,一般是 on{Signal} 這種形式。比如 Qt Quick 中的 Button 元素有一個信号 clicked() ,那麼你要可能會寫出這樣的代碼:
上面的 QML 代碼其實已經是一個簡單 QML 應用了,這個應用在視窗的左下角放了個退出按鈕,當使用者點選它時會觸發按鈕的 clicked() 信号,而我們定義了信号處理器來響應 clicked() 信号——調用 Qt.quit() 退出應用。
你看到了,當信号是 clicked() 時,信号處理器就命名為 onClicked 。就這麼簡單,以 on 起始後跟信号名字(第一個字母大寫)。
在某些情況下使用一個 ‘.‘ 符号或分組符号把相關的屬性形成一個邏輯組。分組屬性可寫以下這樣:
也可以這樣寫:
其實呢,可以這麼了解,font 屬性的類型本身是一個對象,這個對象又有 pixelSize / bold / italic / underline 等等屬性。對于類型為對象的屬性值,可以使用 "." 操作符展開對象的每一個成員對其指派,也可以通過分組符号(一對花括号)把要指派的成員放在一起給它們指派。對于後者,其形式就和對象的定義一樣了,起碼看起來木有差別。是以呢,又可以這麼了解上面的示例: Text 對象内聚合了 font 對象。 OK ,就是聚合。
屬性真難搞!到現在還沒講完,不但你煩了,我也快坐不住了。我保證,這是最後一個要點了,不過也是最複雜最難以了解的屬性了。對于這種玩意兒,我一向的做法時,不能了解的話就接受,你就當它生來如此,存在即合理,隻要學會怎麼用它就 OK 了。
在 QML 語言的文法中,有一個附加屬性(attached properties)和附加信号處理器(attached signal handlers)的概念,這是附加到一個對象上的額外的屬性。從本質上講,這些屬性是由附加類型(attaching type)來實作和提供的,它們可能被附加到另一種類型的對象上。附加屬性與普通屬性的差別在于,對象的普通屬性是由對象本身或其基類(或沿繼承層級向上追溯的祖先們)提供的。
舉個例子,下面的 Item 對象使用了附加屬性和附加信号處理器:
你看, Item 對象可以通路和設定 Keys.enabled 和 Keys.onReturnPressed 的值。 enabled 是 Keys 對象的一個屬性。 onReturnPressed 其實是 Keys 對象的一個信号。對于附加信号處理器,和前面講到的普通信号處理器又有所不同。普通信号處理器,你先要知道信号名字,然後按照 on{Signal} 的文法來定義信号處理器的名字;而附加信号處理器,你隻要通過附加類型名字引用它,把代碼塊指派給它即可。
最後說下 Keys 對象,它是 Qt Quick 提供的,專門供 Item 處理按鍵事件的對象。它定義了很多針對特定按鍵的信号,比如上面的 onReturnPressed ,還定義了更為普通的 onPressed 和 onReleased 信号,一般地,你可以使用這兩個信号來處理按鍵(請對照 Qt C++ 中的 keyPressEvent 和 keyReleaseEvent 來了解)。它們有一個名字是 event 的 KeyEvent 參數,包含了按鍵的詳細資訊。如果一個按鍵被處理, event.accepted 應該被設定為 true 以免它被繼續傳遞。
下面是使用 onPressed 信号的一個示例,它檢測了左方向鍵:
好啦,關于 QML 語言的基礎性介紹就到這裡,相信現在你已經可以看懂簡單的 QML 文檔了。有的同學可能又疑問了,這節還有一些東東隻見用不見講啊,比如 Rectangle / Text / Image / Item / Button / Component / Qt 等等,抱歉,現在隻能揣着糊塗裝明白了,下一篇我們會講這些東西。