qml編碼規范與指導


qml編碼規范與指導

說明

良好的編程規范可以大幅提高一個程序的可讀性、可維護性。編碼規范是大家達成一致的約定,這樣大家的代碼就可以互相看懂,維護起來更容易,思想更暢快的交流,經驗更快的得到傳播。

QML是一種聲明方式設計的語言,用來設計應用程序的界面,包括式樣和表現行為。在QML中,一個用戶界面被制定為一棵樹形的對象模型並且包含了對象的屬性,這棵樹樹根對應的就是qml文件中的根組件。
JavaScript在QML中作為一種腳本語言,對QML進行邏輯方面的編程,也可以說QML是對JavaScript的擴展,提供了js主機環境。
QML中的JavaScript是特殊的JavaScript,是簡化的JavaScript。

Item {
    id:root
    width:800
    height:480
    Rectangle {
        x:200
        y: 60
        width:200
        height:160
        color:“red”
        visible: false
    }
}

Rectangle組件中一些語法的聲明可以寫成JavaScript的方式:

Item {
    id:root
    width:800
    height:480
    property bool visibleState: false
    property bool lightState: false
    Rectangle {
        x: parent.widht/4
        y: parent.height/8
        width:parent.widht/4
        height:parent.height/3
        color:root.lightState ? “red” : "green"
        visible: root.visibleState ? true : false
    }
}

通過以上兩段QML代碼中Rectangle組件屬性的賦值情況可以看出,組件的屬性可以直接賦值,也可通過JavaScript代碼賦值。

指導性原則

統一的代碼風格,減少代碼冗余,增強其代碼可讀性,還可以提升工作效率,讓程序更清晰、健壯。

詳細說明

文件命名

這里的文件分為 *.qml 文件和 *.js 文件。

qml文件命名

因每一個qml文件的使用方法可能不固定,在某些情況下作為程序的入口文件,文件名的首字母可以大寫,也可以小寫,但在另一些情況下可能作為Component(控件,類)來使用,首字母必須大寫。例如:
mainScreen.qml是一個程序界面的入口文件,在后續的維護中,需要在mainScreen.qml中添加一系列的按鈕,在這種情況下,可以重新寫一個繼承於mainScreen的界面入口文件main.qml,此時則需重新命名mainScreen.qml為MainScreen.qml。
為了方便qml文件的維護,避免上述情況的發生,qml文件的命名規則為:

  • 僅由英文字母組成。
  • 以大寫字母開始,遵循大駝峰的命名規則,例如:
    SwithcButton.qml
    ListButton.qml
    MainScreen.qml
    

js文件命名

  • js文件名由小寫的英文字母和_組成。
  • js文件名一律以“ impl ”結尾。
    例如:datetime_helper_impl.js
    這樣便於在qml中導入
    *.js_ 文件時,方便命名別名。

編寫qml文件

import

import的意思是導入模塊,類似java語言中的import包,用來導入qml文件所要用到的內置模塊、自定義插件、控件、js文件等。
qml文件中,import模塊的順序為:

  1. 導入 Qt 內置模塊
  2. 導入框架自定義 Qt Quick 模塊
  3. 導入項目自定義 Qt Quick 模塊
  4. 導入本地 *.qml 組件包
  5. javascript 導入腳本文件
  6. javascript 引用腳本模塊

例如:

// ModuleA.qml

import QtQuick 2.6
import QtQuick.Controls 2.1

import net.phoneyou.EasyUI 1.0 as HUI
import net.phoneyou.EasyUI.Material 1.0 as HUIMaterial
import net.phoneyou.Roshan.MapRender 1.0 as MapRender2D

import your_project_name 1.0
import "path_to_package"

import "my_component_impl.js" as MyComponent
.import "utils.js" as Utils

為了避免不同模塊之間的命名沖突,我們在導入模塊時可以使用“as”關鍵字設置該模塊在本 qml 文件中的“別名”。

對於"as"的相關規則說明:

  • 導入Qt內置模塊的版本號為對應的Qt版本的最高版本號。
  • js文件導入到qml中必須要有別名。
  • 在導入模塊中,本身就有命名沖突時,需給其中至少一個模塊都設置別名
  • 不對 Qt 內置模塊設置別名
  • 對於自定義模塊需設置別名
  • 別名作為模塊在所屬 qml 文件中的代號,需保持文件內的唯一性
  • 別名的命名規則采用“大駝峰”

為了保證 Qt 內部組件和命名空間不被污染以及保持代碼可以順利升級--比如升級 Qt 版本,需要確保不對 Qt 模塊設置別名,而對其他,比如HUI、Roshan以及其他三方的或者項目內的模塊,進行“別名”處理。尤其是那些對常見控件進行定制的模塊。

根組件

qml文件描述的實際上一個由 “UI組件節點” 組成的“樹”。而“根組件”也就是“根節點”,在qml文件有且只有一個。

根組件作為邏輯上的頂層組件,直接承擔起了 qml 文件的所有定義,其屬性、信號、方法即為該 qml 組件的屬性、信號、方法。

其他組件只能作為其子節點存在,對外是不可見的,類似 private 作用域,他們的屬性、信號、方法命名需添加雙下划線("__")前綴,而根組件沒有任何的前后綴。

關於根組件的類型:

  • 如果該qml文件聲明的是對已有組件進行改造的可復用的組件,比如 “ImageButton”,那么該根組件應該為“Button”或者其他的什么“Button”,對應的別名應該設置為“button”或“root”。
  • 如果該qml文件聲明的是一個復合的功能型界面,比如說“ReplaybackPanel”,“DataDelegateItem”,它是一系列組件的組合,一般將根組件設定為“Item”或者“Rectange”,其別名應設置為“root”。

根組件定義

  1. id
  2. 自定義屬性property
  3. 信號signal
  4. JavaScript function
  5. 自帶屬性設置object properties
  6. 子控件child objects
  7. 狀態states
  8. 動畫animation、漸變transitions

例如:


Rectangle {
      id: root
      /*! 鼠標按下狀態,當鼠標按下時為true */
      property bool pressed: false

      /*! 圖片資源 */
      property alias source: photoImage.source

      /*! 鼠標點擊事件 */
      signal clicked

      /*! 返回photoImage的width */
      function doSomething()
      {
          return photoImage.width
      }

      width: {
          if (photoImage.width > 200) {
              photoImage.width;
          } else {
              200;
          }
      }
      height: 480
      color: "gray"

      Rectangle {
           id: photoRect
           x: 20
           y: 20
           width: parent.width
           height: 200
           color: "white"
           Image {
               id: photoImage
               anchors.centerIn: parent
            }
      }
      MouseArea {
          anchors.fill: parent
          onPressed: {
              root.pressed = true
          }
          onClicked: {
              root.clicked()
          }
      }
      states: State {
          name: "selected"
          when: root.pressed
          PropertyChanges {
              target: root.border
               color: "red"
          }
      }

      transitions: Transition {
          from: ""
          to: "selected"
          ColorAnimation {
              target: root.border
              duration: 200
          }
      }
  }

id

在聲明組件時,可以同時聲明其“id”屬性。“id”屬性是該組件的代號,聲明該屬性的好處是,可以被它的作用域為:。

同一qml文件中,每個控件都可以設置id屬性,且不能相同,id相同會有書寫錯誤提示。

具體命名規則如下:

  • 由字母組成,首字母必須小寫,以小駝峰的形式命名。

  • 以控件類型結尾,例如:
    將Button的id設置為:xxxxBtn 或 xxxxButton
    將Text的id設置為: xxxxTxt 或 xxxxText
    將ComboBox的id設置為: xxxxCmb 或 xxxxComboBox
    這樣根據id就可以知道其對應的是什么控件。
    只有在明確知道其控件的縮寫時,才能在id中使用縮小,否則一律使用控件全稱。
    部分控件的縮寫詳見“常用控件的縮寫”

  • 控件默認不設置id屬性,若某個控件的id需要被使用(控件間存在關聯關系),才要設置id。
    例如控件的布局存在相對位置的關系或控件間的屬性在邏輯上存在依賴關系等,則需要設置id,例如:

    Item{
          width: 200
          height: 300
          Button{
              id: saveBtn
              width: 60
              height: 30
              text: "Save"
          }
          Button{
              anchors {
                  top: saveBtn.bottom
                  topMargin: 10
                  left: saveBtn.left
              }
              width: 60
              height: 30
              visible: saveBtn.visible
          }
      }
    

    代碼中的Item和第二個Button沒有被引用到,不必設置id,這樣可避免設置過多的無用id,徒增命名的煩惱。

  • 當子控件要使用父控件的屬性或根據父控件設置相對位置時,不要使用父控件的id,用parent代替id。例如:

Item {
    id: root
    width: 800
    height: 480
    Rectangle{
        id: testRect
        anchors.centerIn: parent
        width: parent.width/2
        height: 200
        color: "red"

        Button{
            id: testBtn
            width: parent.width/2
            height: 40
            text: "1233"
            highlighted: (parent.color === "red") ? false : true
        }

    }
}
  • 一個qml文件中,設置id的個數不要超過7個,多余7個,則要進行qml代碼的拆分,建議設置3到4個。
  • 同一個qml文件中的控件id在文件中是通用的,但是delegate中的控件id除外,delegate中控件id只在delegate中有效,外部訪問無效。
    因為 delegate為Component類型,外界無法訪問。
    通俗點講,就是delegate一般用來承載model數據的顯示,一個id對於多個數據顯示控件,外部在使用id時,無法定位當前使用是哪個數據控件的id。例如:
  ListView {
        id:listView
        anchors.centerIn: parent
        width: 160
        height: 240

        model: Qt.fontFamilies()

        delegate: ItemDelegate {
            id:fontDelegate
            text: modelData
            width: parent.width
            highlighted: ListView.isCurrentItem
            onClicked:{
                listView.currentIndex = index
                console.log("clicked:", modelData)
            }
        }
    }

代碼中ItemDelegate的id名稱fontDelegate,是不能在ItemDelegate之外被使用的。

property屬性

在qml代碼中,為了方便外界使用本自定義控件或者便於qml文件內部控件之間的關聯,常需要自定義一些屬性。
一個property是對象的一個屬性,可以被賦為靜態值或者是綁定到動態表達式上。一個property的值可以被其它的對象讀取。一般情況下,property屬性也可以被其它對象修改,除非該QML類型明確指定該property屬性不能被修改。

  • 屬性的命名規則:
    屬性名稱必須是以小寫字母開頭,僅由字母、數字和下划線組成,由 名詞或形容詞+名詞 的小駝峰形式組合而成。
    當定義的屬性僅限qml內部使用時,才會用到_,這樣的私有屬性的命名必須以兩個下划線(__)開始,例如:

property var __locale: Qt.locale()

自定義屬性

自定義屬性的語法格式如下:


property <propertyType> <propertyName> : value

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

    Rectangle {
        property string someText: “ ”
        onSomeTextChanged: console.log("The someText will be: " + someText)
    }

  • 自定義的屬性必需要有初始值,除了Object Types
  • 常見自定義的屬性類型:
    bool,double,int,real,string,url,var,list
  • bool:true/false。
  • double:雙精度實數,有小數點。
  • int:整數,可以是負數、0、正數。
  • real:實數,有小數點。
  • string:字符串。
  • url:資源路徑,可以是絕對路徑或相對路徑,相對路徑時就會被轉換為URL對象,如果使用了Qt資源系統,就要用“qrc:///”代替“😕”。
  • var:基本類型是通用的類型,可以保存任意類型的值,包括lists和objects:
    任何標准的JavaScript類型都可以使用通用的var類型創建和存儲。
    如果聲明一個屬性用於存儲列表值,但不一定是存儲QML對象類型值,這個時候你就需要聲明var的property屬性。

    property var someNumber: 1.5
    property var someString: "aaa"
    property var someBool: true
    property var someList: [2,5,"one","two"]
    property var someObject: Rectangle {
                                width: 100
                                height: 100
                                color: "red"
                            }
   property var theArray: new Array()
   property var theDate: new Date()

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

    property Item someItem
    property Rectangle someRectangle

這對於自定義QML類型也是適用的。

  • 自定義屬性的調用方式:
    屬性所屬控件id. propertyName
屬性別名
  • 屬性別名就是保存對另一個屬性的引用。不像普通屬性的定義,普通屬性的定義需要分配一個新的,唯一的存儲空間,而一個屬性別名僅僅是連接到了屬性上。
    屬性別名的定義根屬性定義差不多,只是屬性別名需要使用alias關鍵字代替屬性定義中的property類型,右邊的值必須是合法的引用別名:
    屬性別名的語法格式如下:

  property alias <name> : aliasreference

name是我們自定以的屬性名,aliasreference是屬性別名所引用的那個屬性或對象,也就是說屬性別名可以引用自一個單一的屬性,也可以引用自一個復雜的對象。屬性綁定是動態的,但不是雙向的,而通過屬性別名可達到動態、雙向的效果,即修改name和aliasreference中任一個的值,與之關聯的另一個屬性值也會隨之改變。
需要注意的是,如果屬性別名與已有的屬性名相同,就會把已有的屬性覆蓋掉,當我們再使用這個屬性的時候,實際上使用的是我們自定義的那個屬性,例如:


Rectangle {
    id: rootRect
    // 引用屬性
    property alias color: blueRect.color
   // 引用對象
    property alias theRect: blueRect

    color: "red"

    Rectangle {
        id: blueRect
        color: "#1234ff"
    }
}

  • 如果為一個已經存在屬性創建一個同名的屬性別名,這就會覆蓋已經存在的屬性。
    上面的代碼中定義的color屬性,會替代Rectangle原有的color屬性,為了避免已有屬性被覆蓋的問題,定義的屬性別名不能與已有屬性相同。
  • 若所引用的屬性不是已經存在屬性,則定義的屬性別名要與引用的屬性名稱相同。 例如:

Rectangle {
    id: rootRect
    property alias count: nameList.count
    property alias currentIndex: nameList.currentIndex
    color: "red"
    ListView{
        id: nameList
    }
}

Rectangle沒有count和currentIndex屬性,則要將屬性別名與引用屬性名設置成一樣。

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

property alias sureBtn: testBtn
property alias text: sureBtn.text
Button{
    id: testBtn
    width: 100
    height: 40
    text: "1233"
}
Component.onCompleted: console.log("text="+text)

產生的錯誤提示: Invalid alias reference. Unable to find id "sureBtn"

只讀屬性

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


readonly property <propertyType> <propertyName> : <initialValue>

只讀屬性必須在初始化的時候指定值。一旦只讀屬性被初始化了,它就不可能再被賦值了,無論是賦值(使用”=”)還是其它的方式。
例如,下面的Component.onCompleted代碼塊就是非法的:


Item{
    id: root
    readonly property int chiledNum: 10
    width: 100
    height: 40
    Component.onCompleted: root.chiledNum = 20
}

信號

信號就是當某些事件發生的時候從對象類型中發出通知:例如,一個屬性改變,一個動畫開始或者停止,當一個圖片下載完成,或者MouseArea類型當用戶點擊的時候就會發射一個點擊信號。
當一個信號發射了,對象可以通過signal handler被通知。一個signalhandler的定義語法為on 是信號的名稱,首字母要大寫。Signalhandler必須在發射該信號的對象定義的內部實現,並且signalhandler必須包括JavaScript代碼塊,當signal handler被調用的時候該代碼塊就會被執行。
例如,MouseArea對象的定義中可以定義onClicked類型的signal handler,當MouseArea被點擊了該signal handler就會被調用,使得控制台打印出消息:


Item{
    id: root
    width: 100
    height: 40
    MouseArea{
        anchors.fill: parent
        onClicked: console.log("Click!")
    }
}

qml文件中,定義信號signal的語法:


    signal <signalName>[([<type> <parameter name>[, ...]])]

信號一般是相關聯的事件觸發的,信號名由英文字母和_組成,已小駝峰的形式命名。

  • 信號的命名規則遵循屬性的命名規則,以小駝峰的形式命名。
  • 如果信號不帶參數,則一律不使用()
  • 如果信號帶參數,參數要寫參數類型。
    信號參數的命名規則:
    msg+大駝峰形式
    必須以“msg”開頭,便於標識是信號參數。
    例子:

signal clicked
signal hovered(string msgInfo)
signal activated(real msgXPosition, real msgYPosition)

信號的調用方式:信號所屬控件id.信號

js代碼

自定義的js函數,可以帶參數,也可以不帶出參數,且參數不用指明參數類型,因為函數的參數類型默認是萬能的var類型。
函數名由字母和_組成,且首字母必須是小寫,即以小駝峰的形式命名,例如:


function addZero(num){
    if(num < 10)
        return "0"+num
    else
        return num
}

  • 函數返回時不要使用圓括號。
  • 當定義的js函數,僅能在本qml文件使用,即使私有函數時,此函數的命名才用到_, 且是以雙下划線(__)開始 + 小駝峰的方式命名,例如:

function __factorial(a) {
    a = parseInt(a);
    if (a <= 0)
        return 1;
    else
        return a * factorial(a - 1);
}

  • qml文件中js函數的調用:函數所在控件的id.函數名()
js文件

當一個qml文件中的js代碼較多即:js函數代碼超過80行時,應根據其分類或應用邏輯單獨作成一個js文件,再將js文件導入qml文件。
js文件的導入方法:

  • qml中導入的js文件必須要有別名,別名的命名規則:
    以大駝峰的形式命名
    由js文件名中除了_和impl以外的字母組成,例如:

  import "jsfile_impl.js" as Jsflie

  • 在js文件中導入另一個js文件的2種方法 :

.import "filename_imp.js" as Filename
Qt.include "filename.js"`

布爾表達式
  • 在布爾表達式中,邏輯運算符 && 和|| 兩邊的條件表達式根據邏輯需要添加圓括號()
狀態機
  • 在qml中,狀態是定義在state類型中的一系列屬性配置,不同的配置可能有不同的作用,為了方便起見,State元素都有一個when屬性,可以綁定表達式來改變狀態,當綁定的表達式評估為true,狀態改變;當表達式評估為false,狀態會退回到default state。
  • 顯示一些UI控件,隱藏另一些。

  StateGroup {
    states: [
        State {
            name: "xxx"
            when: visible
            PropertyChanges {
                target: xxxBtn
                visible: true
            }
            PropertyChanges {
                target: yyyBtn
                visible: true
            }
            PropertyChanges {
                target: zzzBtn
                visible: fasle
            }
        }
    ]
}

  • 啟動、暫停、停止動畫。

  StateGroup {
    states: [
        State {
            name: "start"
            when: 0 == animationState
            PropertyChanges {
                target: imageAnimation
                running: true
            }
        },
        State {
            name: "pause"
            when: 1 == animationState
            PropertyChanges {
                target: imageAnimation
                paused: true
            }
        },
        State {
            name: "stop"
            when: 2 == animationState
            PropertyChanges {
                target: imageAnimation
                running: false
            }
        }
    ]
}

  • 改變某個特定Item的property的值。

StateGroup {
    states: [
        State {
            name: "color"
            when: bChanged
            PropertyChanges {
                target: rootRect
                color: "red"
            }
        },
        State {
            name: "enabled"
            when: disabled
            PropertyChanges {
                target: rootRect
                enabled: false
            }
        }
    ]
}

  • 顯示一個不同的view或screen。

 StateGroup {
   states: [
       State {
           name: "xxx"
           when: 0 == loaderState
           PropertyChanges {
               target: xxxLoader
               source: "Xxx.qml"
           }
       },
       State {
           name: "yyy"
           when: 1 == loaderState
           PropertyChanges {
               target: xxxLoader
               source: "Yyy.qml"
           }
       },
       State {
           name: "zzz"
           when: 2 == loaderState
           PropertyChanges {
               target: xxxLoader
               source: "Zzz.qml"
           }
       }
   ]
}

  • 在某種新的狀態下執行某些腳本。
// statetest.js
function getColor()
{
    return "green";
}

// test.qml
import "statetest.js" as StateTest
Rectangle{
    id: rect
    width: 50; height: 50
    color: "red"
    MouseArea {
        anchors.fill: parent
        onClicked: rect.state = "changedColor"
    }
StateGroup {
    states:
        State {
            name: "changedColor"
            StateChangeScript{
                name: "myScript"
                script: rect.color = StateTest.getColor()
            }
        }
    }
}
  • 向用戶呈現不同的操作和功能
Rectangle {
    id: rect
    width: 120; height: 120
    color: "black"
    Rectangle { id: myRect; width: 50; height: 50; color: "red" }
    StateGroup {
        states:
            State {
                name: "reanchored"
                AnchorChanges { // 改變 myRect 的anchors屬性
                target: myRect
                anchors.top: rect.top
                anchors.bottom: rect.bottom
            }
            PropertyChanges {
                target: myRect
                anchors.topMargin: 10
                anchors.bottomMargin: 10
            }
        }
    }
    // 鼠標事件
    MouseArea {
        anchors.fill: parent
        onClicked: rect.state = "reanchored"
    }
}
  • 把指定的item換一個item父節點
    Item {
        width: 200
        height: 100
        Rectangle {
            id: redRect
            width: 100
            height: 100
            color: "red"
        }
        Rectangle {
            id: blueRect
            x: redRect.width
            width: 50
            height: 50
            color: "blue"
            states: State {
            name: "reparented"
            ParentChange {
                target: blueRect
                parent: redRect
                x: 10
                y: 10
            }
        }
        MouseArea {
            anchors.fill: parent
            onClicked: blueRect.state = "reparented"
        }
    }
}
  • states中每個state之間的觸發條件與PropertyChanges中的屬性之間不要有關聯,若有關聯,可能會形成死循環。

  • 在qml中使用states時,一定要加上父層StateGroup,這樣可以避免與原始控件中state名相同的state相沖突,產生無法預料的問題。

  • 能用條件運算符替代states的地方,盡量用條件運算符,不要用states。條件運算符的執行速度比state快。

StateGroup {
    states:
        State {
            name: "name"
            when: bChanged
            PropertyChanges {
                target: root
                color: "red"
            }
        }
}

應修改為:

color: bChanged ? "red" : "white"`
  • 同一個State中有多個PropertyChanges,PropertyChanges的target不能設置一樣,否則只執行第一個PropertyChanges。

例如:
錯誤的寫法

StateGroup {
    states:
        State {
            name: "name"
            when: bChanged
            PropertyChanges {
                target: root
                color: "red"
            }
            PropertyChanges {
                target: root
                border.color: "red"
            }
        }
}

應該修改為:

StateGroup {
    states:
        State {
            name: "name"
            when: bChanged
            PropertyChanges {
                target: root
                color: "red"
                border.color: "red"
            }
        }
}

內部組件

與“根組件定義”相同,但是其屬性、信號、方法命名需添加雙下划線("__")前綴。
比如:


    Item {
        id: root
        width: 800
        height: 480
        Item {
            id: subItem

            property bool __updateState:false

            signal __clicked

            function __doSomething(x)
            {
                return x+subItem.x
            }

            width: 400
            height: 400

        }
    }

注釋

為了提高qml代碼的可讀性和便於后期維護,注釋是必不可少的。

文件功能注釋

文件功能注釋是對此qml大概功能的一個概述,若此qml文件還作為控件別的qml文件使用,則還有寫出大概的使用方法。
例如下面是combobox.qml文件功能的注釋:

/*!
將一個model的數據以下拉框列表的形式顯示,通過選擇下拉框列表數據,改變當前顯示的數據
    \qml
       ComboBox {
           width: 200
           model: [ "Banana", "Apple", "Coconut" ]
       }
    \endqml
*/

注釋以“/*!”開始,以“ */” 結束,包含的對文件功能的說明,和控件的使用方法,使用用例以“\qml”開始,以“\endqml”結束。

自定義屬性、信號、方法注釋

  • 自定義的屬性、信號、方法必須添加注釋。
    自定義屬性、信號、方法注釋格式為:“/*! */”,寫在其定義的上一行,例如:
  /*! ComboBox下拉框列表當前選中的index值 */
    property int currentIndex: 0

其他注釋

這里的其它注釋是指除文件功能注釋和自定義屬性、信號、方法注釋外的其它需要注釋的代碼。
代碼注釋格式為:“ // xxxxxxxxx ”,寫在被注釋代碼上方,例如:

// temporarily set fillIndex to new item
fillIndex = __items.length

補充說明

object properties

  • 一行寫一條語句

一般屬性設置的順序為:

id: rootRect
x: 10
y: 10
width: 100
height: 100
...

組屬性的設置

在對控進行屬性設置時,如果有同一類組的多個屬性需要設置,為了提高代碼可讀性,則使用組的提示符,而不是點的運算符號。
例如:

anchors.top: parent.top
anchors.left: parent.left
anchors.topMargin: 10
anchors.leftMargin: 10

應該改寫成:

anchors {
    top: parent.to
    left: parent.left
    topMargin: 10
    leftMargin: 10
}

Qml中和c++中都可以處理的邏輯

在qml文件中,有許多的邏輯處理既可以在qml中完成,也可以在c++中處理,原則是qml代碼只處理界面顯示邏輯,其它邏輯能在c++中處理的,一律在c++中處理,這樣有利於提高qml的加載速度 。

MouseArea{ anchors.fill: parent }的特殊用法

MouseArea除了處理Item的鼠標響應,還有一個典型的用途是,MouseArea { anchors.fill: parent }能夠屏蔽鼠標事件的滲透。
例如:

   // main.qml
   Rectangle{
      width: 800
      height: 480
      Button{
            width: 150
            height: 50
            text: "Button"
            onClicked: {
                if(ld.source === “ ”){
                    ld.source= "TestChild.qml"
                }
            }
      }
      Loader{
        id:ld
      }
   }

   // TestChild.qml
   Rectangle{
      width: 800
      height: 480
   }

當TestChild界面加載后,在TestChild界面上與main界面的Button所處位置的重疊處點擊,會觸發main界面Button的點擊事件,
這就是一個界面的鼠標事件參透到另一界面。為了避免鼠標滲透事件的發生,TestChild.qml應該修改為:


   Rectangle{
      width: 800
      height: 480
      MouseArea { anchors.fill: parent }
   }

注意:MouseArea { anchors.fill: parent }要作為TestChild界面的第一個子控件,才能屏蔽整個TestChild的鼠標滲透,又不影響TestChild界面中其它可操作控件事件。

常用控件的縮寫

1.Button -> btn
1.CheckBox -> chk
1.ComboBox -> cmb
1.RadioButton -> rdo
1.Text -> txt
1.Label -> lbl
1.Image -> img
1.DateTimePicker -> dtp
1.ListView -> lvw
1.ToolTip -> tip
1.GroupBox -> grp
1.Panel -> pnl
1.Calendar -> cal
1.TreeView ->trvw
1.ListView->lvw
1.GridView -> gvw
1.Repeater->rpt
1.Menu ->mnu
1.ProgressBar ->prg

屬性的綁定

所謂屬性綁定,就是建立目標對象對其它對象的依賴關系,當其它對象發生變化時,目標對象也會變化,自動更新相關的屬性值,達到動態屬性的效果,例如:

    Item {
        id: root
        width: 800
        height: 480
        focus: true
        Keys.enabled: true
        // 重新綁定
        Keys.onEnterPressed: redRect.width = Qt.binding(function() {return parent.width/2})
        Rectangle {
            id: redRect
            anchors.fill: parent
            width: parent.width/3  // 綁定
            height: 100
            color: "red"
        }

        Button{
            width: 60
            height: 30
            text: “Button”
            onClicked: redRect.width = 150    // 綁定解除
        }
    }

上述例子中,redRect的width屬性最初綁定到了root的width屬性上,redRect的width會隨着root的width變化而變化;Button按下后,redRect的width屬性綁定解除;Enter鍵按下后,redRect的width屬性重新綁定;Button再次按下后,redRect的width屬性綁定解除。
也就是說如果屬性綁定已經完成,那么在別的地方重新給這個屬性賦值時,不管是賦個靜態值,還是想換個別的屬性綁定表達式,都會破壞原來的綁定關系,而新的值也不會有動態屬性的效果。

Keys的載體

Keys是專門用來處理鍵盤事件KeyEvent的,它定義了許多針對特定按鍵的信號,例如digit0Pressed(KeyEvent event)、spacePressed(KeyEvent event)等,不過使用pressed(KeyEvent event)和released(KeyEvent event)這兩個普通的信號就可以處理大部分按鍵事件了,信號的參數類型是KeyEvent,參數名是event,包含了按鍵的詳細信息。
這里說所的載體是指Keys所屬的父組件,Keys的載體必須是Item,或從Item繼承而來,不能是Window或從Window繼承而來的組件,否則Keys無效。
例如:


 Window {
      visible: true
      width: 360
      height: 360
      Keys.enabled: true;
      Keys.onEscapePressed: Qt.quit()             // 沒有功能: 不退出


 Window {
      visible: true
      width: 360
      height: 360
      Item{
            anchors.fill: parent
            focus: true
            Keys.enabled: true;
            Keys.onEscapePressed: Qt.quit()             // 有功能: 退出
      }

Loader

QML的Loader元素經常備用來動態加載QML組件。可以使用source屬性或者sourceComponent屬性加載。這個元素最有用的地方是它能在qml組件需要的時候再創建,即延遲創建QML的時間。
加載與被加載組件中都有相同的事件,那么需要設置Loader的屬性focus為true,且設置被加載組件 focus: true才能讓事件不被傳播到被加載組件。

main.qml
Item {
    property bool isFirst : false;
    width: 200
    height: 200

    Loader {
        id: pageLoader
        source: "Page2.qml"
        focus: true
    }

    Keys.onPressed: {
        console.log("Captured: ", event.text);
        event.accepted = true;
    }
}


// Page2.qml

Rectangle {
    width: 100
    height: 62
    Text {
        anchors.centerIn: parent
        text: "Page2 Test"
    }
    focus: true
    Keys.onPressed: {
        console.log("Loaded item captured: ", event.text)
        event.accepted = true
    }
}

若將main.qml中Item的focus設置為true,則上述代碼的只有main響應按鍵消息。

  • 如果source或者sourceComponent更改了,任何先前實例化的項目都將被自動銷毀。

  • 將source設置為空字符串或者sourceComponent設置為undefined,則銷毀當前加載的項目,釋放資源,並將Loader設置為空。

  • Loader出來的qml界面,在界面關閉時,一定要清空Loader資源。

qml的目錄結構

在獨立項目或者Roshan插件中,qml的目錄結構都是類似的。
資源

qml.qrc
components
img
views

main.qml

  • components文件夾用來放置項目或插件的專有控件。
  • img文件夾用來放置項目或插件的圖片資源。
  • view文件夾用來放置用來放置除專有控件、圖片資源和main.qml外的其他文件。

model

QML中,model是為ListView,GridView和Repeater等元素來提供要顯示數據的數據模型.這些元素需要一個為模型中的每一項數據生成一個實例的代理組件(delegate component).給代理提供的數據通過叫做角色的數據綁定到代理.
例如下述代碼中,ListModel有兩個角色:text 和 age,與代理模型Text的屬性text的名稱相沖突。
在這種情況下,如果直接在代理中綁定角色名稱,則代理Text的text不會顯示模型中text角色值,而是顯示它自身的屬性值。為了避免這種沖突的產生,在角色前面加上model.即可正常顯示。

Item{
        width: 200
        height: 200
        ListView{
            id: testList
            anchors.fill: parent
            model: testModel
            delegate: Text{
                width: lst.width
                height: 30
               // text: text + "-" + age
                text: model.text + "-" + model.age
            }
        }
        ListModel{
            id: testModel
            ListElement{
                text: "ccw"
                age: 12
            }
            ListElement{
                text: "ym"
                age: 13
            }
            ListElement{
                text: "zfb"
                age: 35
            }
            ListElement{
                text: "ded"
                age: 30
            }
        }
    }

其它

  1. js代碼中,能夠用條件運算符(? :)代替 if...else...地方,要用條件運算符,這樣是代碼更簡潔明了。

  2. js中“”和“=”的區別:
    ==用於一般比較,在比較的時候可以轉換數據類型。
    ===用於嚴格比較,只要類型不匹配就返回flase。

  3. 當一個qml文件中,代碼行達到200至300行時,要從其qml文件中分解出子qml文件。
    這樣可以避免出現控件關聯復雜,篇幅較長的代碼。

  4. 只有一條語句的代碼塊,寫成一行,例如:

  MouseArea { anchors.fill: parent }

在信號處理代碼中,如果也只有一條執行語句,則寫成一行,且不需要{},例如:

    onPressed: {
        rootRect.color= "gray"
    }

應該寫成:

    onPressed: rootRect.color= "gray"


免責聲明!

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



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