How to dynamically generate attributes for a QML Item?

60 views Asked by At

In a QML application I'm coding, I am always writing the sames attributes names with different values. For example :

Image{
    source:"qrc:/..."
    Layout.preferredWidth: 124 //Always
    Layout.preferredHeight: 229 //the same
    Layout.alignment: Qt.AlignHCenter //3 lines
    Layout.topMargin: convertHeight(40)
    Layout.bottomMargin: convertHeight(26)
}

would it be possible to define a function that generates dynamic attributes :

function sizeProps(width,height){
    return {
        Layout.preferredWidth : width,
        Layout.preferredHeight : height,
        Layout.alignment: Qt.AlignHCenter
    }
}

and use the dynamically generated attributes like this ?

Image{
    source:"qrc:/..."
    {sizeProps(124,229)}
    Layout.topMargin: convertHeight(40)
    Layout.bottomMargin: convertHeight(26)
}
2

There are 2 answers

4
smr On BEST ANSWER

You can't do this, but you can create a custom component and reuse your new type. [1]

Either by defining an inline component like:

component Img: Image {
    property size size: Qt.size(width, height)  /* default value */

    Layout.preferredWidth: size.width
    Layout.preferredHeight: size.height
    Layout.alignment: Qt.AlignHCenter
}

// usage: 
Img { size{width: 100; height: 200} }

Or defining it in a separate .qml file [3].

Img.qml

Image {
    property size size: Qt.size(width, height)
    
    Layout.preferredWidth: size.width
    Layout.preferredHeight: size.height
    Layout.alignment: Qt.AlignHCenter
}

// usage: (same as inline component)
Img { size{width: 100; height: 200} }

But if you prefer using functions, you can do this too, but I do not recommend it.

function initImgLayout(item, width, height) {
    item.Layout.preferredWidth = width;
    item.Layout.preferredHeight = height;
    item.Layout.alignment = Qt.AlignHCenter;
}

Image {
    Component.onCompleted: initImgLayout(this, 100, 200);
}
1
Stephen Quan On

I want to highlight the Img.qml aspect of smr's answer.

For large QML projects, look for ways to reduce complexity with components. I often subclass QML components with properties tailored for my app.

In the following demo, I choose to make:

  • AppText is a customization of Text
    • AppHeading1 is a customization of AppText
    • AppHeading2 is a customization of AppText
  • AppImage is a customization of Image
    • This includes all your customizations
  • AppPage is a customization of Page combined with ScrollView and ColumnLayout
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
AppPage {
    title: "AppImage Example"
    AppHeading1 { text: "AppImage Example" }
    AppHeading2 { text: "Red Circle" }
    AppImage { source: "red-circle.svg" }
    AppHeading2 { text: "Green Triangle" }
    AppImage { source: "green-triangle.svg" }
}

// AppPage.qml
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
Page {
    default property alias data: columnLayout.data
    ScrollView {
        id: scrollView
        anchors.fill: parent
        ScrollBar.vertical {
            policy: ScrollBar.AlwaysOn
            width: 20
        }
        ColumnLayout {
            id: columnLayout
            width: scrollView.width - 20
        }
    }
}

// AppText.qml
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
Text {
    Layout.alignment: Qt.AlignHCenter
}

// AppHeading1.qml
import QtQuick.Layouts
AppText {
    Layout.topMargin: 10
    font.pointSize: 20
    font.bold: true
    color: "blue"
}

// AppHeading2.qml
import QtQuick.Layouts
AppText {
    Layout.topMargin: 10
    font.pointSize: 14
    color: "orange"
}

// AppImage.qml
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
Image {
    Layout.preferredWidth: 124
    Layout.preferredHeight: 229
    Layout.alignment: Qt.AlignHCenter
    Layout.topMargin: convertHeight(40)
    Layout.bottomMargin: convertHeight(26)
    cache: false
    function convertHeight(h) {
        return h;
    } 
}

// red-circle.svg
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 124 229">
<rect width="124" height="229" fill="lightblue" />
<circle cx="62" cy="115" r="30" fill="red" />
</svg>

// green-triangle.svg
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 124 229">
<rect width="124" height="229" fill="lightblue" />
<path d="M 62 85 L 92 145 L 32 145 Z" fill="green" />
</svg>

You can Try it Online!