37

I'm trying to create a QML object that acts like a wrapper for other objects. Here's my QML file (Container.qml):

Item {
    property string label
    property Item control

    Row {
        Label {
            text: label
        }

        // Not sure how to display the control assigned to the control property
    }
}

What I would like to do (in my QML that consumes this component) is something like this:

Container {
    label: "My Label"
    control: Textbox {
        text: "My Value"
    }
}

When fed that QML the result (in the interface) should be something resembling the output from this QML:

Item {
    Row {
        Label {
            text: "My Label"
        }
        Textbox {
            text: "My Value"
        }
    }
}

Is this possible? When I try to do this I get "cannot assign object to property" when assigning an Item to the control property. I've searched the Qt forums and Googled this mercilessly, but no success. If anybody knows the answer it would be greatly appreciated.

Thanks

Jack

Jack Benson
  • 645
  • 2
  • 6
  • 8

4 Answers4

44

There is much better solution:

/* MyObject.qml */

Rectangle {
    default property alias data /* name can be any */ : inner_space.data

    /* ... You can put other elements here ... */
    Item {
       id: inner_space

       /* ... Params ... */
    }
    /* ... You can put other elements here ... */
}

And now we can do all we want!

/* main.qml */

Rectangle {
    MyObject {
        Button {
             /* ... */
        }
    }
}

Thanks to user bobbaluba for suggesting to use the data property rather than children.

Dan Hulme
  • 14,779
  • 3
  • 46
  • 95
s.maks
  • 2,583
  • 1
  • 23
  • 27
  • Does this work for non visual items, like TableViewColumn? http://stackoverflow.com/questions/26527103/how-propagate-tableviewcolumn-list-to-child-tableview – Anton Oct 23 '14 at 14:28
  • "Thanks to user bobbaluba for suggesting to use the data property rather than children." for me only `children` worked, I was trying to create a generic item delegate and with `parent` it wasn't treated as part of the item – ניר Apr 25 '22 at 12:54
  • Note: For corrected showing inner content, after "id: inner_space" necessary append: "width: inner_space.childrenRect.width" and "height: inner_space.childrenRect.height". – Xintrea Sep 24 '22 at 12:05
38

You can dynamically load items using the Loader element, and then set the 'control' property to be an alias that directly refers to the loader's sourceComponent property.

So your Container.qml could look like this:

Item {
    property string label
    property alias control : loader.sourceComponent

    width: 200; height: 200

    Row {
        Label { text: label }
        Loader { id: loader }
    }
}

Now when you assign a graphical item to the 'control' property, it will automatically be displayed by the Loader.

Tarod
  • 6,732
  • 5
  • 44
  • 50
blam
  • 1,125
  • 9
  • 5
  • Wow, that Loader object looks great! Wish I would have known about that a long time ago! That is a much more elegant solution. Thanks for sharing! – Jack Benson Feb 18 '11 at 12:48
2

You can create a container for other items using a custom property:

Item
{
    id: root

    property list<Item> rectList: [
        Rectangle
        {
            parent: root
        },
        Rectangle
        {
            parent: root
        },
        Rectangle
        {
            parent: root
        },
        Rectangle
        {
            parent: root
        }
    ]
}

Note that the parent of the Rectangle Items is set manually so they will be visual children of the container.

You can assess them as a javascript array

for ( var i = 0; i < root.rectList.length; i++ )
{
   var rect = root.rectList[i];
   rect.visible = true;
}

or

rect.rectList[1].visible = false;
Jay
  • 13,803
  • 4
  • 42
  • 69
  • This actually works - I never expected we could use "templated lists" in QML - where did you find this? Is this documented *anywhere* ??? – Felix Jun 22 '18 at 20:46
  • It's not documented specifically that I recall. It's emergent from the design and integration of javascript with QObject. The specifics about the assigned parent I had to learn by trial and error though – Jay Jun 23 '18 at 00:52
1

Been using QML for about a month now, no idea really how to do this I'm afraid.

Best plan is to figure out all possible things that the control (Textbox in your example) could be, create an instance of each component in your row, and set a corresponding state on your Item. The state would then take care of making the desired component visible or invisible as appropriate.

Edit

Just thought. (Haven't tried this but give it a go!) In your Item's Component.onCompleted: handler, try calling control.createObject(rowID) where rowID is an id for your Row object, (which you want to be the parent of control).

funkybro
  • 8,432
  • 6
  • 39
  • 52
  • 1
    Thanks for the response! The solution (as I understand it) would work, but is less than ideal. All of those extra hidden controls just to render one control...seems like a waste. I'll keep plugging away. If I find anything I'll be sure to post back. – Jack Benson Feb 17 '11 at 12:31
  • 1
    That certainly seems like it should work. I misunderstood the documentation on dynamically creating objects. Your brief explanation clarifies that for me. I'm still getting the `"cannot assign object to property"` error though. It doesn't make sense, because I know you [can assign Items to properties](http://doc.qt.nokia.com/4.7-snapshot/qml-extending-types.html). For example, the rectangle accepts a Gradient (not an Item, I know, but an Object). I think your answer is correct, I just don't understand why this won't work! :) Thanks for your help! If you have more insights, let me know! – Jack Benson Feb 17 '11 at 16:16