Home > Net >  Failed to Measure Children for Dynamically Created QML Object
Failed to Measure Children for Dynamically Created QML Object

Time:10-20

Basically, I was trying to recreate IconLabel with some of my own classes in QML. However, it worked, in a limited sense:

import QtQuick 2.15
import QtQuick.Controls.Material 2.15
import QtQuick.Layouts 1.15

Item {
    id: root

    property int spacing: 8
    property string direction: "left"

    property string icon: ""
    property bool is_img: false
    property int icon_size: 24
    property int font_size: 14
    property string text: ""
    property string font: "Roboto"
    property color color: "black"
    property color icon_color: "black"
    property color text_color: "black"


    Component.onCompleted: {
        var cmd = `
        import QtQuick 2.15
        import QtQuick.Controls.Material 2.15
        import QtQuick.Layouts 1.15
        `;
        var isHorizontal = direction === "left" || direction === "right";
        var alignmentCmd = `Layout.alignment: ${isHorizontal ? "Qt.AlignVCenter" : "Qt.AlignHCenter"}`;

        var iconCmd = "";
        if (icon_color === "black")
            icon_color = color;
        if (text_color === "black")
            text_color = color;

        if (root.icon !== "") {
            if (is_img) {
                iconCmd = `MImage {
                    source: "${icon}"
                    Layout.alignment: ${isHorizontal ? "Qt.AlignVCenter" : "Qt.AlignHCenter"}
                    Layout.fillWidth: true
                    Layout.fillHeight: true
                    fillMode: Image.PreserveAspectCrop
                    width: ${icon_size}
                    height: ${icon_size}
                }`;
            } else {
                iconCmd = `MIcon {
                    icon: root.icon
                    color: root.icon_color
                    ${alignmentCmd}
                    width: ${icon_size}
                    height: ${icon_size}
                }`;
            }
        }
        var textCmd = `
        Text {
            text: root.text
            font.pixelSize: root.font_size
            font.family: root.font
            color: root.text_color
            ${alignmentCmd}
        }
        `;
        var componentCmd = direction === "left" || direction === "top" ? iconCmd   textCmd : textCmd   iconCmd;
        cmd  = `
        ${isHorizontal ? "RowLayout" : "ColumnLayout"} {
            spacing: ${isHorizontal ? "root.spacing" : "0"}
            ${componentCmd}
        }`;

        Qt.createQmlObject(cmd, root, "dynamicComponent");
    }
}

It does do everything that I want if I used it alone, like Shown a MWE

import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import QtQuick.Dialogs
import QtQuick.Controls.Material 2.15
import "components"

ApplicationWindow {
    id: root
    visible: true
    width: 640
    height: 480
    title: qsTr("Hello World")

    MIconLabel {
        icon_color: Material.color(Material.Red, Material.Shade500)
        direction: "right"
        icon: "home"
        text: "Home"
    }
    MIconLabel {
        icon_color: "black"
        icon: "menu"
        text: "Menu"
        y: 100
    }
}

But for some reason, it rejects to cooperate with RowLayout or anything of this nature.

Failure Case

import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import QtQuick.Dialogs
import QtQuick.Controls.Material 2.15
import "components"

ApplicationWindow {
    id: root
    visible: true
    width: 640
    height: 480
    title: qsTr("Hello World")

    ColumnLayout {
        MIconLabel {
            icon_color: Material.color(Material.Red, Material.Shade500)
            direction: "right"
            icon: "home"
            text: "Home"
        }
        MIconLabel {
            icon_color: "black"
            icon: "menu"
            text: "Menu"
        }
   }
}

Hence, I ask if there is some way that I can make the parent detect the newly created objects (like refresh or remeasure methods?). I tried to resize the window in hope of repainting the event, but nothing good happens. I also used console.log at the end of the initialization method to print out the dimension of the newly created object, and the dimension of the root object, and the following expression evaluates to True: root.width == object.width == root.childrenRect.width and root.height == object.height == root.childrenRect.height.

Just to explain some details here: MIcon is a wrapper of Text class with the font.family pre-declared as Material Icons. And MImage is basically an wrapper around Image with rounded corners and a placeholder, if no image is provided.

CodePudding user response:

It took sometime to read your code. My reading of your code, I get the feeling it is (1) brutal use of createQmlObject() and I would recommend finding another way to do the same thing. For example, are you aware that most components have an icon.source property with icon.color ? For instance

ItemDelegate {
    icon.source: "..."
    icon.color: "..."
}

I tried to recreate your UI with:

  • ItemDelegate to render both text and icon
  • icon.source and icon.color to colorize an SVG
  • LayoutMirroring to implement both left-to-right and right-to-left patterns
  • Added Frame component to visually check that the dimensions of the attempt

Here's the complete working example:

import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
Page {
    ColumnLayout {
        width: parent.width
        Frame {
            ItemDelegate {
                text: "Home"
                icon.source: "home-32.svg"
                icon.color: "red"
                LayoutMirroring.enabled: true
            }
        }
        Frame {
            ItemDelegate {
                text: "Menu"
                icon.source: "hamburger-32.svg"
                icon.color: "black"
            }
        }
    }
}

//home-32.svg
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><path d="M11 8.5V5H6v8.5L2.5 17H5v12h8v-8h6v8h8V17h2.5L16 3.5zM26 16v12h-6v-8h-8v8H6V16H4.914L7 13.914V6h3v4.914l6-6L27.086 16z"/><path fill="none" d="M0 0h32v32H0z"/></svg>

//hamburger-32.svg
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><path d="M28 7H4V6h24zm0 9H4v1h24zm0 10H4v1h24z"/><path fill="none" d="M0 0h32v32H0z"/></svg>

You can enter image description here

As Stephen Quan suggested, such a brutal amount of createQmlObject is bad. Hence, an alternative solution is proposed, despite it lacks of the ability to create IconLabel in all kinds of directions.

import QtQuick 2.15
import QtQuick.Controls.Material 2.15
import QtQuick.Layouts 1.15

Rectangle {
    id: root
    property color color: "black"

    property alias spacing: rowLayout.spacing
    property alias icon: icon.icon
    property alias text: text.text
    property alias text_size: text.font.pixelSize
    property alias icon_size: icon.size
    property alias font: text.font.family

    property var icon_color: null
    property var text_color: null

    implicitHeight: rowLayout.implicitHeight
    implicitWidth: rowLayout.implicitWidth

    RowLayout {
        id: rowLayout
        anchors.fill: parent

        MIcon {
            id: icon
            icon: root.icon
            size: root.icon_size
            Layout.alignment: Qt.AlignVCenter
        }

        Text {
            id: text
            Layout.alignment: Qt.AlignVCenter
        }
    }

    Connections {
        function onColorChanged() {
            root.icon_color = root.color
            root.text_color = root.color
        }

        function onIcon_colorChanged() {
            icon.color = root.icon_color
        }

        function onText_colorChanged() {
            text.color = root.text_color
        }
    }

    Component.onCompleted: {
        if (root.icon_color == null) {
            root.icon_color = root.color
        }
        if (root.text_color == null) {
            root.text_color = root.color
        }
    }
}

enter image description here

  • Related