Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I create a QML GridLayout with items of proportionate sizes?

Tags:

qt

qml

Potentially related: https://stackoverflow.com/a/30860285/3363018

I've been trying to create a QML layout with items that span variable numbers of rows and columns. So, e.g., a rectangle that spans two rows and four columns, one to the right of it that spans one row and two columns, and one underneath that spans three rows and five columns. A generic attempt at creating this is below:

import QtQuick 2.5
import QtQuick.Layouts 1.2
import QtQuick.Controls 1.4

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

    GridLayout {
        anchors.fill: parent

        columns: 5
        rows: 2

        Rectangle {
            Layout.column: 0
            Layout.columnSpan: 4
            Layout.row: 0
            Layout.rowSpan: 2

            Layout.fillHeight: true
            Layout.fillWidth: true

            color: "red"
        }

        Rectangle {
            Layout.column: 4
            Layout.columnSpan: 1
            Layout.row: 0
            Layout.rowSpan: 2

            Layout.fillHeight: true
            Layout.fillWidth: true

            color: "green"
        }

        Rectangle {
            Layout.column: 0
            Layout.columnSpan: 5
            Layout.row: 2
            Layout.rowSpan: 3

            Layout.fillHeight: true
            Layout.fillWidth: true

            color: "blue"
        }
    }
}

which results in this:

Attempt at weighted grid layout

As you can see, Layout.rowSpan and Layout.columnspan don't seem to be working. Instead, the top two rows seem to take up 1:1 rather than 4:1 and the top part vs the bottom part are 1:1 rather than 2:3. I suspect this is related to Layout.fillHeight and Layout.fillWidth. The documentation states:

If this property is true, the item will be as wide as possible while respecting the given constraints.

but I'm not sure what "the given constraints" are in this context. I had hoped it would include the row and column span but it seems not.

I could probably directly calculate the item widths and heights (or maybe Layout.preferredWidth and Layout.preferredHeight) but that would seem to make Layout.rowSpan and Layout.columnSpan completely superfluous. Is there a way to automatically calculate the heights based on grid rows and columns?

like image 399
Dylan Avatar asked Dec 01 '15 18:12

Dylan


2 Answers

Description for top-right rect has rows and columns transposed. Should be:

one to the right of it that spans one column and two rows

The number of rows is incorrect. Should be:

columns: 5
rows: 5

Specify preferred widths and heights as percentages or units. I'm using units below because it is easier to relate to your 4:1 and 2:3 proportions:

GridLayout {
    anchors.fill: parent

    columns: 5
    rows: 5                        // was 2

    Rectangle {
        Layout.column: 0
        Layout.columnSpan: 4
        Layout.row: 0
        Layout.rowSpan: 2
        Layout.preferredWidth: 4   // 4 of 5 cols
        Layout.preferredHeight: 2  // 2 of 5 rows
        Layout.fillHeight: true
        Layout.fillWidth: true

        color: "red"
    }

    Rectangle {
        Layout.column: 4
        Layout.columnSpan: 1
        Layout.row: 0
        Layout.rowSpan: 2
        Layout.preferredWidth: 1   // 1 of 5 cols
        Layout.preferredHeight: 2  // 2 of 5 rows
        Layout.fillHeight: true
        Layout.fillWidth: true

        color: "green"
    }

    Rectangle {
        Layout.column: 0
        Layout.columnSpan: 5
        Layout.row: 2
        Layout.rowSpan: 3
        Layout.preferredHeight: 3  // 3 of 5 rows
        Layout.fillHeight: true
        Layout.fillWidth: true

        color: "blue"
    }
}

Screenshot showing expected proportions

Below is minimal code using percentages to achieve the same result:

GridLayout {
    anchors.fill: parent
    columns: 2

    Rectangle {
        Layout.fillWidth: true
        Layout.fillHeight: true
        Layout.preferredWidth: 80
        Layout.preferredHeight: 40
        color: "red"
    }

    Rectangle {
        Layout.fillWidth: true
        Layout.fillHeight: true
        Layout.preferredWidth: 20
        Layout.preferredHeight: 40

        color: "green"
    }

    Rectangle {
        Layout.fillWidth: true
        Layout.fillHeight: true
        Layout.columnSpan: 2
        Layout.preferredWidth: 100
        Layout.preferredHeight: 60

        color: "blue"
    }
}
like image 96
pixelgrease Avatar answered Sep 20 '22 15:09

pixelgrease


I ran into the same problem as you did but I think it's easy to misunderstand what the GridLayout is doing. If you have grid with 5 columns and 2 rows, you get something like this where each O represents a cell:

grid
OOOOO
OOOOO

The rowSpan determines how TALL an object is.

The columnSpan determines how WIDE an object is.

You are wanting to fit these in this grid:

r1      r2     r3
OOOO    OO   OOOOO
OOOO         OOOOO
             OOOOO

It's not hard to see but they won't fit.

This was my solution to a 12x12 grid and should be easy to follow. GridLayout doesn't seem to automatically know what width and height to assign to its children. But that's okay, you can define that easily. Essentially, I'm passing in the rectangles to my two short two functions. You could pass rowSpan or columnSpan but I prefer this way because I don't have to remember which function takes what:

GridLayout {
    id : grid
    anchors.fill: parent
    rows    : 12
    columns : 12
    property double colMulti : grid.width / grid.columns
    property double rowMulti : grid.height / grid.rows
    function prefWidth(item){
        return colMulti * item.Layout.columnSpan
    }
    function prefHeight(item){
        return rowMulti * item.Layout.rowSpan
    }

    Rectangle {
        color : 'red'
        Layout.rowSpan   : 10
        Layout.columnSpan: 2
        Layout.preferredWidth  : grid.prefWidth(this)
        Layout.preferredHeight : grid.prefHeight(this)
    }
    Rectangle {
        color : 'yellow'
        Layout.rowSpan   : 10
        Layout.columnSpan: 10
        Layout.preferredWidth  : grid.prefWidth(this)
        Layout.preferredHeight : grid.prefHeight(this)
    }
    Rectangle {
        id : greenRect
        color : 'green'
        Layout.rowSpan : 2
        Layout.columnSpan : 12
        Layout.preferredWidth  : grid.prefWidth(this)
        Layout.preferredHeight : grid.prefHeight(this)
    }
}

Result here:

QML Grid Layout

like image 42
Wolf Avatar answered Sep 20 '22 15:09

Wolf