Qt 5.15中QML語言的新特性

本文翻譯自:New QML language features in Qt 5.15
原文作者:Fabian Kosmale,Qt公司軟件工程師
校審:Kenny Zhang

前言

隨着Qt 6.0即將帶來的重大變化,QML已在5.15中加入了一些新的語言特性。繼續閱讀以瞭解required properties(必備屬性), inline components(內聯組件) 和nullish coalescing(空值合併)。

Required Properties(必備屬性)

有時,您的組件需要設置一些屬性,但沒有合適的默認值。例如,您可能關心按鈕的易訪問性(Accessibility),因此當您創建了一個AccessibleButton控件,它至少應該有一個description屬性。

// AccessibleButton.qml
Button {    
    property string description    
    Accessible.description: description
}

但是,按鈕具有description屬性這一事實並不意味着任何人都會設置它。所以您或您的同事可能會在某個時候用以下代碼實例化組件:

AccessibleButton {    
    onClicked: (mouse)  => { /* fancy business logic here */ }
}

關於易訪問性就講到這裏:現在description屬性只是一個空字符串!當然,您可以爲屬性設置一個默認值,但是用哪個呢?“Button”基本沒用。“您不應該聽到這個”?好吧,至少QA現在可能會針對它。但是,如果QML引擎知道需要設置此屬性,不是更有用嗎?

不幸的是,在Qt 5.15之前沒有辦法強制設置description屬性。但從Qt 5.15開始,這就成爲可能:

Button {    
    required property string description    
    Accessible.description: description
}

現在,如果創建一個AccessibleButton,但沒有設置description屬性,那麼整個應用程序將無法啓動。但如果該組件是動態創建的(例如通過Loader加載),則無法做到這一點。這種情況下,將僅出現運行時警告。

我們還計劃爲qmllint和QtCreator添加更多的工具支持,以便在未設置Required Properties時顯示警告。

Required Properties和Delegates

此外,Required Properties在Delegates中扮演着特殊的角色。正如您可能知道的,Delegates可以通過名稱以及其他屬性,如model和index,直接訪問所提供的模型角色。

ListView {    
    model: root.myModel    
    delegate: Text {        
        id: delegate        
        color: index % 2 ? "gray" : "black"        
        text: description    
    }
}

如果您的Delegates不包含Required Properties,則此處不會發生任何更改。但是,如果它們包含至少一個Required Properties,那麼這些名稱就不能再訪問了。相反,您必須將它們顯式的指定爲Required Properties。

ListView {    
    model: root.myModel    
    delegate: Text {        
        id: delegate        
        required property int index        
        required property string description        
        color: index % 2 ? "gray" : "black"        
        text: description    
    }
}

然後QML引擎將相應設置Required Properties。請注意,如您的model是可編輯的,新方法和舊方法之間有一個重要的區別:使用舊方法,您可以這樣寫代碼:

Text {    
    id: delegate    
    Component.onCompleted: description = "My fancy new text"
}

model也會相應更新。但如果你這樣寫代碼

Text {    
    id: delegate    
    required property string description    
    Component.onCompleted: delegate.description = "My fancy new text"
}

然後,description的綁定將被破壞(QML引擎將會打印警告),model將不會被更新。我們決定採用這種行爲,以確保無論在delegates中或在delegates之外使用,components的行爲不會有太大的差異。此外,我們也不鼓勵任何人對屬性執行命令式賦值(因爲這通常會破壞綁定)。

如果您確實想要更新model的值,當然還有一種辦法可以實現:將model設置爲Required Properties並用以下代碼

Component.onCompleted: model.description= "My fancy new text"

我們建議您始終在Delegates中使用Required Properties。這避免了非限定查找,後者對工具來說是個問題,並往往會降低處理速度。

Inline Components(內聯組件)

Qt 5.15中的另一個新特性是內聯組件。顧名思義,它們允許您在文件中定義一個新組件。基本語法是

component <component name> : BaseType {    
    // declare properties and bindings here
}

在文件內部,您可以通過名稱引用新組件,就像在其自己的文件中定義的一樣。讓我們以LabeledImage組件爲例來說明其工作原理:

// Images.qml
import QtQuick 2.15

Item {
    component LabeledImage: Column {
        property alias source: image.source
        property alias caption: text.text

        Image {
            id: image
            width: 50
            height: 50
        }
        Text {
            id: text
            font.bold: true
        }
    }

    Row {
        LabeledImage {
            id: before
            source: "before.png"
            caption: "Before"
        }
        LabeledImage {
            id: after
            source: "after.png"
            caption: "After"
        }
    }
    property LabeledImage selectedImage: before
}

您也可以在其它文件中引用該組件。在這種情況下,您需要在其名字之前加上包含其組件的名稱:

// LabeledImageBox.qml
import QtQuick 2.15

Rectangle {
    property alias caption: image.caption
    property alias source: image.source
    border.width: 2
    border.color: "black"
    Images.LabeledImage {
        id: image
    }
}

您可能會想,既然QML已經有了組件類型,爲什麼還要使用內聯組件呢?查看前面的示例,我們可以看到,內聯組件使您可以執行組件無法執行的以下操作:您可以在沒有Loader開銷的情況下創建組件實例。您可以在屬性聲明中使用組件類型。您可以在定義組件的文件之外的其他文件中引用該組件。
希望您能和我們一樣方便地找到內聯組件!

Nullish Coalescing(空值合併)

最後一個新語言特性是由我們的實習生Maximilian Goldstein實現的。雖然QML通常只支持EcmaScript 6,但是Maximilian爲一個即將提出的語言特性增加了支持,該特性正在被添加到最新的EcmaScript標準中:空值合併。引用MDN:

空值合併操作符(??)是一個邏輯操作符,當其左側操作數爲空或未定義時,
返回其右側操作數,否則返回其左側操作數。

有關詳細信息,請參閱MDN頁面。下面是一個示例,演示如何在QML中使用它來設置JSON中的屬性,並在未提供屬性的情況下提供合理的默認值。

Item {
    property var settings
    property int brightness: settings.brightness ?? 100
    property color color: settings.color ?? "blue"
    Component.onCompleted: settings = JSON.parse(settingsString)
}

注意我們在設置brightness時不能用“||”代替“??”。因爲settings.brightness可能已經是0,在這種情況下,我們將獲取默認值。

展望

隨着Qt 6的到來,QML將迎來更多改變。除了改進QML引擎的內部機制之外,我們還希望利用靜態類型來生成更快的代碼(包括編譯成C++),並改進我們的工具鏈。此外,雖然我們在最初的6.0版本中側重那些大主題,但我們也會在那些小改進上花時間:optional chaining (https://bugreports.qt.io/browse/QTBUG-77926)或添加對fetch API的支持就是兩個來自Qt社區的特性需求示例,我們會在較高的6.x版本基線中考慮 (不是最初的6.0版本)。您希望在QML中看到什麼其它的特性嗎?請在bug跟蹤系統(https://bugreports.qt.io)中創建一個提議。未來由Qt寫就!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章