8

I try to add cells to my GridLayout by using a Repeater. My data is stored in a model and containing two properties per element:

  • Title
  • Value

My goal is to get a GridLayout containing the Title in first cell and the Value in the second cell of each row.

GridLayout {
    id: someId
    columns: 2
    rowSpacing: 5
    columnSpacing: 5
    anchors.margins: 5
    anchors.left: parent.left
    anchors.right: parent.right

    Repeater {
        model: myModel
        Label {
            text: modelData.title
        }
        TextArea {
            text: modelData.value
        }
    }
}

But QML Repeater allows only one element. Any ideas how I could get the layout I want?

+------------+---------------------------------------+
|            |                                       |
|  title0    |         value0                        |
|            |                                       |
|            |                                       |
+------------+---------------------------------------+
|            |                                       |
|            |                                       |
|  title1    |         value1                        |
|            |                                       |
|            |                                       |
+------------+---------------------------------------+
|            |                                       |
|  title2    |         value2                        |
|            |                                       |
|            |                                       |
+------------+---------------------------------------+
BaCaRoZzo
  • 7,502
  • 6
  • 51
  • 82
Marcus
  • 1,910
  • 2
  • 16
  • 27

4 Answers4

17

You can simply use two Repeaters within a GridLayout, as follows:

import QtQuick 2.5
import QtQuick.Window 2.2
import QtQuick.Layouts 1.1
import QtQuick.Controls 1.4

Window {
    width: 600; height: 400; visible: true

    GridLayout {
        id: grid
        anchors.fill: parent
        columns: 2
        rowSpacing: 5
        columnSpacing: 5
        anchors.margins: 5
        // example models
        property var titles: [ "title1", "title2", "title3", "title4", "title5" ]
        property var values: [ "value1", "value2", "value3", "value4", "value5" ]

        Repeater {
            model: grid.titles
            Label {
                Layout.row: index
                Layout.column: 0
                Layout.fillWidth: true
                Layout.fillHeight: true
                text: modelData
            }
        }

        Repeater {
            model: grid.values
            TextArea {
                Layout.row: index
                Layout.column: 1
                Layout.fillWidth: true
                Layout.fillHeight: true
                text: modelData
            }
        }
    }
}

The index parameter is freely available and store the current row of the model.

By using the Layout.fillWidth attached property you can control the width of the single column.

Of course, each cell that belongs to a column has the same size of all the other cells of that column, unlike what happens using two Column components.

This solution has a few drawbacks, but it's good if your purpose is mainly to print plain data from a model.

BaCaRoZzo
  • 7,502
  • 6
  • 51
  • 82
skypjack
  • 49,335
  • 19
  • 95
  • 187
  • 1
    just care i run in to a bug https://bugreports.qt.io/browse/QTBUG-65121, if it doesn't work for you, you can always the combination of a row with 2 column inside – Paltoquet Jul 11 '19 at 07:34
6

You can use GridLayout.flow to specify in which order the cells should be filled, i.e. row- (GridLayout.LeftToRight) or column-wise (GridLayout.TopToBottom). Note that you should specify the number of rows when using GridLayout.TopToBottom.

Using this solution, the (simplified) example of skypjack would become:

import QtQuick 2.5
import QtQuick.Window 2.2
import QtQuick.Layouts 1.1
import QtQuick.Controls 1.4

Window {
    width: 600; height: 400; visible: true

    GridLayout {
        anchors.fill: parent

        // specify the flow and number of rows
        flow: GridLayout.TopToBottom
        rows: repeater.count

        Repeater {
            id: repeater
            model: [ "title1", "title2", "title3", "title4", "title5", "title6" ] // example model
            Label {
                Layout.fillWidth: true
                Layout.fillHeight: true
                text: modelData
            }
        }

        Repeater {
            model: [ "value1", "value2", "value3", "value4", "value5", "value6" ]  // example model
            TextArea {
                Layout.fillWidth: true
                Layout.fillHeight: true
                text: modelData
            }
        }
    }
}
m7913d
  • 10,244
  • 7
  • 28
  • 56
4

The model-view principle assumes that each model node displays by different delegate component object. So I advice you to listen to @BaCaRoZzo's comment and do that with Column instead of GridLayout. Sure, QML is very flexible and you can do something like that:

Component {
    id: labelDelegate
    Label { text: myList.get(_index / 2).title }
}

Component {
    id: textAreaDelegate
    TextArea { text: myList.get(_index / 2).value }
}

ListModel {
    id: myList
    ListElement {title: "title1"; value: "value1"}
    ListElement {title: "title2"; value: "value2"}
    ListElement {title: "title3"; value: "value3"}
}

GridLayout {
    anchors.fill: parent
    columns: 2
    Repeater {
        model: myList.count * 2
        delegate: Loader {
            property int _index: index
            sourceComponent: {
                if(index % 2)
                    return textAreaDelegate;
                else
                    return labelDelegate;
            }
        }
    }
}

but that's too weird to use it in real project.

folibis
  • 12,048
  • 6
  • 54
  • 97
  • Ok, thanks. I'll try the column-based solution as suggested by @BaCaRoZzo. Even I'm not sure if the result is what i want. I think the first cell of each row will have a different width. Lets see if I'm wrong (hope so ^^ ) – Marcus Oct 07 '15 at 04:14
  • The suggested version to populate the Columns work pretty good, but the resulting Layout isn't what I want. The first Cell of each row has a different width. So this is not really a solution for problem. I'll try the wired solution of @folibis ... if it works it isn't wired enough :) – Marcus Oct 07 '15 at 05:34
  • Naaah, don't use it, I've posted it as example of how you should not do. So, in case of using `Column` you can set same width for every `Label`. Or, more correctly, you should review the model. – folibis Oct 07 '15 at 06:25
1

Nest as many elements as you want inside an Item in the Repeater's delegate and re-parent them to the GridLayout when the Item completes.

GridLayout {
    id: grid
    anchors.centerIn: parent
    columns: 2
    rowSpacing: 5
    columnSpacing: 5
    anchors.margins: 5

    Repeater {
        model: [
            { title: "title1 that's long", value: "value1" },
            { title: "title2 medium",      value: "value2" },
            { title: "title3",             value: "value3" }
        ]
        delegate: Item {
            Component.onCompleted: {
                while (children.length) { children[0].parent = grid; }
            }

            Label {
                Layout.alignment: Qt.AlignRight
                color: "gray"
                text: modelData.title
            }
            TextArea {
                Layout.fillWidth: true
                font.bold: true
                text: modelData.value
            }
        }
    }
}

Screenshot of running QML code

pixelgrease
  • 1,940
  • 23
  • 26