解讀QML之四


解讀QML之四

QML對象屬性

        每一個QML對象類型都定義了一系列屬性。每創建一個該對象類型的實例,該實例的這些屬性也自動被創建了。接下來我們討論幾種不同類型的屬性。

id屬性

           每一個QML對象類型都有一個唯一確定的id屬性。這個屬性是由QML語言自身提供的,並且在QML對象類型中不能被重定義和重載。

        我們必須為id屬性指定一個值允許該對象被唯一標示並且可用於被其它對象引用。Id屬性值必須以小寫字母或者下划線開始,只能包含字母,數字和下划線等字符。

        下面是一個TextInput對象和一個Text對象,TextInput對象的id屬性值為”myTextInput”。Text對象的text屬性值通過myTextInput.text被設置為和TextInput對象的text屬性值一樣。


         我們以后可以在該組件可見域內通過id屬性引用該對象。因此id屬性值在組件可見域內必須是唯一的。

         一旦對象實例被創建了,那么id屬性值就不能被改變。Id屬性看起來像普通屬性,但是並非是真正的普通屬性,我們對它應用特殊的語義,例如:在上面的例子中我們不能使用myTextInput.id。

property屬性

        一個property是對象的一個屬性,可以被賦為靜態值或者是綁定到動態表達式上。一個property的值可以被其它的對象讀取。一般情況下,property屬性也可以被其它對象修改,除非該QML類型明確指定該property屬性不能被修改。

 【定義property屬性】

   一個property屬性可以在C++中定義,並且通過Q_PROPERTY注冊到QML類型系統。當然,我們也可以在QML文檔中通過如下語法自定義對象的property屬性:


         通過這種方式,一個對象可以將一些特定的值暴露給其它對象,或者是更加簡便的維護一些內部狀態。

         Property屬性的名稱必須以小寫字母開頭,且只能包含字母,數字和下划線。JavaScript的保留關鍵字不能作為property屬性的名稱。Default關鍵字是可選的,對於default以及default屬性修改者的詳細信息稍后討論。

         定義一個自定義的property屬性也就為該property屬性隱式的創建了一個value-change信號,也就是關聯了一個名為 on<PropertyName>Changed的signal handler。<PropertyName>就是property屬性的名稱,而且首字母要大寫。

         例如:下面就定義了兩個property屬性,並且實現了其signalhandler:


【自定義property屬性的合法類型】

      QML基本類型中的枚舉類型都可以作為自定義property屬性類型。例如:下面都是合法的property屬性聲明:


        一些QtQuick模塊提供的基本類型是不能作為property類型的,除非在QML文檔中導入QtQuick模塊。

       var基本類型是通用的類型,可以保存任意類型的值,包括lists和objects:


        另外,任何的QML對象類型都可以被用作property屬性類型。例如:


        這對於自定義QML類型也是適用的。如果在ColorfulButton.qml文件中定義了一個QML類型,那么ColorfulButton類型的property屬性也是合法的。

  【合法的property屬性值】

我們可以通過兩種方式為定義的property屬性的值:

  *初始化

  *賦值

  值可以是靜態值也可以是綁定表達式。

  {初始化}

    Property屬性初始化:


     我們可以在定義property屬性的時候,也進行初始化賦值:


       初始化賦值舉例如下:


{賦值}

     我們可以使用JavaScript代碼給property屬性賦值,如下:


     舉例如下:


【合法的property值】

     正如之前提到的,我們可以給property屬性賦兩類值:靜態值和綁定表達式:


     示例:


        在許多情況下,string類型的值可以自動被轉換為許多不同類型的值,因為QML提供了string類型到其它類型的轉換(這也就是為什么你可以給color屬性賦值”red”)。

         必須特別注意,綁定到一個表達式的情況下,右邊的表達式必須的Qt.binding()函數的返回值,該函數返回合適的值類型。一個表達式可以在property屬性初始化的時候直接賦值,而不需要使用那個函數(實際上使用函數還會導致錯誤)。

 【類型安全】

       屬性都是類型安全的。給屬性賦值必須是類型匹配的。

       例如,下面的賦值就會導致一個錯誤:


         如果在運行時的時候給屬性賦類型錯誤的值,那么賦值也不會成功還會產生一個錯誤。

        正如在之前提到過的,一些屬性類型並沒有很好的類型來表達其值,這個時候QML引擎提供了很好的string類型轉換。例如:color屬性,該屬性存儲 的值類型應該是color類型而不是string類型,但是你卻可以給它指定string類型的值,而不會產生錯誤。

【特殊的property屬性類型】

     對象列表類型的property屬性

     List類型的property屬性也可以被賦予QML對象類型列表的值。賦值的形式如下:


        例如:Item類型有一個狀態屬性,可以用於保存State對象類型的列表。下面的代碼用於初始化該列表:


        如果列表僅僅包含一項,那么方括號可以省略:


        我們可以像下面這樣定義對象列表類型的property屬性:


      List類型的屬性聲明舉例如下:


       如果你希望聲明一個屬性用於存儲列表值,但不一定是存儲QML對象類型值,這個時候你就需要聲明var的property屬性。

【分組的屬性】

        在某些情況下,我們可以將屬性根據邏輯分為子屬性組。這些子屬性可以通過”.”或者是組來賦值。

        例如:Text類型有一個font的組屬性。在下面,第一個Text對象使用”.”初始化font的值,第二個則是使用組來賦值:


        組屬性類型都是基本的類型。這些基本類型一部分是由QML語言提供的,另外一部分則是由Qt Quick模塊提供的。

 【屬性別名】

         屬性別名就是保存對另一個屬性的引用。不像普通屬性的定義,普通屬性的定義需要分配一個新的,唯一的存儲空間,而一個屬性別名僅僅是連接到了屬性上。

         屬性別名的定義根屬性定義差不多,只是屬性別名需要使用alias關鍵字代替屬性定義中的property類型,右邊的值必須是合法的引用別名:


        不像普通的屬性,一個別名僅僅可以引用對象或者是對象的屬性。

        例如:下面的Button類型有一個buttonText屬性別名,鏈接到子Text對象的text屬性:


       下面的代碼會創建一個Button,並且定義文本字符串:


        修改了buttonText,就直接修改了textItem.text的值,它不會修改其它的值。如果buttonText不是屬性別名,那么修改它的值是不會修改顯示的文本的,因為屬性綁定不是雙向的。

【考慮屬性別名】

     只有在組件完全初始化之后屬性別名才會被激活。如果未初始化的別名被引用了就會產生錯誤。另外,為一個屬性別名產生屬性別名也會導致錯誤:


        我們可以為一個已經存在屬性創建一個同名的屬性別名,這就會覆蓋已經存在的屬性。例如:下面的QML類型有一個color屬性別名,跟Rectangle內建的屬性Rectangle::color屬性:


default屬性】

         一個對象定義可以包含一個default屬性。如果一個對象()子對象定義在另一個對象(父對象)之內而沒有賦值給該父對象的任何屬性,那么該子對象就是該父對象default屬性的值。聲明為default屬性需要使用default關鍵字。

        例如下面的MyLabel.qml文件中的對象就有一個someText的默認屬性:


       我們可以在MyLabel對象定義中為someText默認屬性賦值:


        上面兩個等同於下面一個:


       然而,由於someText屬性被標記為默認屬性,因此我們就沒有必要顯示的將Text對象賦值給這個默認屬性。

       你也可能注意到了子對象可以添加到任何基於Item的類型,而不需要明確將它們添加到子屬性上。這是因為Item的默認屬性是data屬性,任何添加到Item中的對象都自動添加到它的子對象列表。

        默認屬性對於重新指定子對象是十分有用的。可以看看TabWidget示例,該示例就是使用默認屬性自動重新指定TabWidget的子對象作為它的子對象列表。

 【只讀屬性】

   任何對象的定義都可以使用readonly關鍵字定義只讀屬性,使用下面的語法:


       只讀屬性必須在初始化的時候指定值。一旦只讀屬性被初始化了,它就不可能再被賦值了,無論是賦值(使用”=”)還是其它的方式。

       例如,下面的Component.onCompleted代碼塊就是非法的:


注意:一個只讀的屬性是不能聲明為默認屬性或者是屬性別名的。

 【屬性修改對象】

       屬性可以有屬性值修改對象與它們關聯。我們可以像如下的這樣定義屬性修改對象實例,與特定的屬性關聯:


        需要注意的是,上面的語法實際上一個對象聲明,將會實例化一個對象操作已存在的屬性。

       特定的屬性修改類型只能應用到特定的屬性類型上,但是這並不是被語言強制的。例如:QtQuick模塊提供的NumberAnimation類型僅僅影 響數字類型(例如int或者real)屬性。如果將NumberAnimation使用到非數字類型屬性將不會引起錯誤,但是非數字屬性將不會產生動畫。 屬性修改類型的動作是與特定屬性的實現緊密相關的。

 Signal屬性

        信號就是當某些事件發生的時候從對象類型中發出通知:例如,一個屬性改變,一個動畫開始或者停止,或者當一個圖片下載完成。例如,MouseArea類型當用戶點擊的時候就會發射一個點擊信號。

        當一個信號發射了,對象可以通過signal handler被通知。一個signalhandler的定義語法為on<Signal>,<Signal>是信號的名稱,首字 母要大寫。Signalhandler必須在發射該信號的對象定義的內部實現,並且signalhandler必須包括JavaScript代碼塊,當 signal handler被調用的時候該代碼塊就會被執行。

         例如,MouseArea對象的定義中可以定義onClicked類型的signal handler,當MouseArea被點擊了該signal handler就會被調用,使得控制台打印出消息:


【定義Signal屬性】

        我們可以使用Q_SIGNAL在C++的類中定義Signal屬性並注冊到QML類型系統。可選的,我們也可以在QML文檔中使用如下語法定義signal屬性:


        在一個類型塊中定義兩個同名的信號或者方法將會產生錯誤。然而,我們可以使用已經存在的信號類型定義新的信號(這會導致之前的信號不可見)

       下面就是一個信號定義的示例:


         如果一個signal沒有參數,那么”()”就是可選的。如果使用了參數,那么參數的類型就必須指定,例如上面的actionPerformed信號中的string和val參數。允許使用的參數類型根之前描述的定義property屬性中的一樣。

       要發射一個信號,那么就會調用一個方法。任何和信號關聯的signal handler都會在信號被發射的時候執行,並且handlers可以使用定義的信號參數的名字和獲取預期的參數。

【屬性改變信號】

        QML類型也提供內建的屬性改變信號,當屬性值被改變的時候這些信號就會被發射。接下來我們就來介紹信號的好處以及如何使用。

Signal Handler屬性】

        Signal handlers是特殊的方法屬性類型,當信號發射了,與信號關聯的特定方法就會被調用。在QML中添加一個信號就會自動添加一個與之關聯的 signalhandler,默認情況下該函數就會是空實現。客戶端可以提供自己的實現來實現程序邏輯:

       考慮下面的SquareButton類型,該類型是SquareButton.qml文件中定義的,定義了兩個信號:activated和deactived:


        這些信號可以在同一目錄下的其他QML文件中的任何SquareButton對象獲取,客戶端也提供了signalhandler實現:


【屬性改變Signal Handler

        屬性改變的Signal handler語法如下on<Property>Changed,<Property>就是屬性的名稱,首字母要大寫。例如,盡 管TextInput類型文檔沒有定義textChanged信號,但是這個信號因為TextInput有一個text屬性而可用。因此我們也就可以實現 onTextChanged signal handler,當屬性值被改變的時候就會調用該signal handler:


方法屬性

           一個對象的方法就是在執行一些處理或者觸發一些事件的時候被調用的。當一個方法連接到一個信號時,當信號被發射的時候,這些方法就會被執行。

【定義方法屬性】

        我們可以通過在C++的類的函數使用Q_INVOKABLE注冊到QML類型系統或者是在C++類中注冊為Q_SLOT。可選的,我們也可以在QML文檔中自定義方法添加到對象中:


       方法可以添加到QML類型以定義單獨的可重用的JavaScript代碼。這些方法可以在內部被調用或者是被外部對象調用。

       不像信號,這里不用定義參數的類型,因為它們默認為var類型。

        在同一個類型塊中視圖定義兩個同名的方法或者是信號都會導致錯誤。然而,我們可以使用已存在的方法的名稱定義新的方法(這會導致已存在的方法不可用)

        下面的Rectangle類型有一個calculateHeight()方法,當指定height值的時候該方法就會被調用。

         如果方法有參數,那么我們可以在方法中通過參數名訪問它們。例如,MouseArea被點擊,那么就會調用moveTo()方法,可以使用newX和newY設置文本的新位置:


附加屬性和附加SignalHandler

        附加屬性和附加的信號處理機制允許對象使用那些對象不可引用的外部屬性和信號處理者。這允許對象訪問那些與單個對象關聯的屬性和信號。

         一個QML類型實現可以選擇創建附加的特定屬性和信號。實例化這種類型就會在運行時創建,允許這些對象訪問屬性和信號。

引用附加的屬性和handlers語法如下:


        例如:ListView類型有一個附加的屬性ListView.isCurrentItem,該屬性可以被ListView的每一個代理訪問。該屬性可以被每一個獨立的代理對象來決定當前選擇的是哪一個條目:


        在這種情況下,附加類型的名稱是ListView和屬性isCurrentItem,附加屬性使用ListView.isCurrentItem引用。

        附加的signal handler也可以使用這種方式引用。例如:附加的signal handler是Component.isCompleted,當組件的創建過程完成后可以被用來執行一些JavaScript代碼。在下面的示例中,一 旦ListModel被完全創建,Component.onCompleted signal handler就會被自動執行:


         附加的類型名是Component以及completed信號,附加的signal handler可以這樣引用:Component.isCompleted。

 【訪問附加屬性和Signal Handler的注意點】

        一個常見的錯誤是將設附加的屬性和signal handler可以直接從子對象中訪問。這並不是問題。附加類型的實例僅僅添加到特定的對象上,而不是對象以及其所有子對象之上。

       例如:下面的示例是之前的附加屬性的改編版示例。在這次中,代理是一個Item,Rectangle就是該Item的子對象:


        這並不像預期中那樣工作,因為ListView.isCurrentItem僅僅是附加到根代理對象,而不是它的子對象。由於Rectangle是代理的 子對象,而不是代理本身,他不能使用ListView.isCurrentItem訪問isCurrentItem附加屬性。因此,rectangle必 須通過根代理對象訪問isCurrentItem屬性:


         現在就可以通過 delegateItem.ListView.isCurrentItem正確引用到代理的isCurrentItem附加屬性。


免責聲明!

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



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