Layouts in Qt5.6 vs Qt.5.7



  • Hi all,

    I just ran into an issue with Layouts as I switched from Qt5.6.2 to Qt5.7.1. The following code works as expected in Qt5.6.2. The delegates of the 1st ColumnLayout have equal heights:

    import QtQuick 2.6
    import QtQuick.Window 2.2
    import QtQuick.Layouts 1.3
    
    Window {
      visible: true
      width: 640
      height: 480
      color: "black"
    
      ColumnLayout {
        anchors.fill: parent
    
        Rectangle {
          Layout.fillHeight: true
          Layout.fillWidth: true
          Layout.preferredHeight: .1
        }
    
        ColumnLayout {
          Layout.preferredHeight: .1
          Rectangle {
            Layout.fillHeight: true
            Layout.fillWidth: true
          }
          Rectangle {
            Layout.fillHeight: true
            Layout.fillWidth: true
          }
        }
      }
    }
    

    The same code in Qt.5.7.1 and above (I tested it upto Qt5.9.1) shows a window with a broken layout. The 2nd ColumnLayout fills up almost all space of the window.

    Is that a bug or did the API change so I have to rewrite the code for Qt5.7.1?



  • @DuBu Do you mean that you want the first Rectangle and the second ColumnLayout to have same height? And the two inner Rectangles have 1/2 of the height of the first Rectangle? Why do you have both Layout.fillHeight and Layout.preferredHeight for the first Rectangle?

    You can try to wrap the second, inner ColumnLayout inside an Item:

    ColumnLayout {
            anchors.fill: parent
            ...
            Item {
                Layout.fillHeight: true
                Layout.fillWidth: true
                ColumnLayout {
                    anchors.fill: parent
    

    In an older thread I told that even though Layout items are Items they don't behave as other Items when they are first-level children of Layouts. I consider it a bug.



  • @Eeli-K said in Layouts in Qt5.6 vs Qt.5.7:

    @DuBu Do you mean that you want the first Rectangle and the second ColumnLayout to have same height? And the two inner Rectangles have 1/2 of the height of the first Rectangle?

    Yes, that’s what I mean.

    Why do you have both Layout.fillHeight and Layout.preferredHeight for the first Rectangle?

    Cause that’s what I think how it works. I have to activate fillHeight to make preferredHeight work in a way I want It to. I don’t want preferredHeight to be the actual height. I want it to just act as a ratio. (Therefore the value of 0.1)

    You can try to wrap the second, inner ColumnLayout inside an Item:

    We have already so many (visible) items I don’t want to add more just to workaround something I don’t understand.

    In an older thread I told that even though Layout items are Items they don't behave as other Items when they are first-level children of Layouts. I consider it a bug.

    Ok, that makes sense. Cause it worked in Qt5.6 I’ll consider it as a bug introduced with 5.7.



  • @DuBu said in Layouts in Qt5.6 vs Qt.5.7:

    Why do you have both Layout.fillHeight and Layout.preferredHeight for the first Rectangle?

    Cause that’s what I think how it works. I have to activate fillHeight to make preferredHeight work in a way I want It to. I don’t want preferredHeight to be the actual height. I want it to just act as a ratio. (Therefore the value of 0.1)

    If you read the documentation you see it's not a ratio, it's similar to height, implicitHeight etc. "This property holds the preferred height of an item in a layout. If the preferred height is -1 it will be ignored, and the layout will use implicitHeight instead. The default is -1."

    If you want to have a ratio you have to use binding, e.g.

    Layout.preferredHeight: someOtherItemWhichCanBeUsedForCalculatingThisHeight.height/10 //tenth of the other height
    

    If the items inside the layout all have Layout.fillHeight:true the available space is divided evenly for them. If some of the items have Layout.preferredHeight or implicitHeight they will be used first and the rest of the available space is for the remaining items which have fillHeight.

    preferredHeight and fillHeight are incompatible, it may be an unspecified implementation detail which one is actually used. PreferredHeight sets the height explicitly and you can't logically set the height and let the layout engine calculate it at the same time. For your purposes using only fillHeight should be enough, but if it doesn't work well you can use bindings and calculate the heights based on the window height.

    import QtQuick 2.6
    import QtQuick.Window 2.2
    import QtQuick.Layouts 1.3
    
    Window {
        visible: true
        width: 640
        height: 480
        color: "black"
    
        ColumnLayout {
            anchors.fill: parent
            id: topLevelLayout
            Rectangle {
                color: "red"
                Layout.fillWidth: true
                Layout.preferredHeight: topLevelLayout.height/2
            }
            ColumnLayout {
                Layout.preferredHeight: topLevelLayout.height/2
                Rectangle {
                    color: "blue"
                    Layout.fillHeight: true
                    Layout.fillWidth: true
                }
                Rectangle {
                    color: "green"
                    Layout.fillHeight: true
                    Layout.fillWidth: true
                }
            }
    
        }
    }
    


  • @Eeli-K said in Layouts in Qt5.6 vs Qt.5.7:

    If you read the documentation you see it's not a ratio, it's similar to height, implicitHeight etc. "This property holds the preferred height of an item in a layout. If the preferred height is -1 it will be ignored, and the layout will use implicitHeight instead. The default is -1."

    If you want to have a ratio you have to use binding, e.g.

    Layout.preferredHeight: someOtherItemWhichCanBeUsedForCalculatingThisHeight.height/10 //tenth of the other height
    

    If the items inside the layout all have Layout.fillHeight:true the available space is divided evenly for them. If some of the items have Layout.preferredHeight or implicitHeight they will be used first and the rest of the available space is for the remaining items which have fillHeight.

    preferredHeight and fillHeight are incompatible, it may be an unspecified implementation detail which one is actually used. PreferredHeight sets the height explicitly and you can't logically set the height and let the layout engine calculate it at the same time. For your purposes using only fillHeight should be enough, but if it doesn't work well you can use bindings and calculate the heights based on the window height.

    I read the documentation and I know how it's meant to be used and maybe it's an unspecified implementation, but that's what I found out: If I use both preferredHeight and fillHeight together, then the items are scaled in preferredHeight ratios. And that's an (undocumented) Feature I really appreciate.
    See the changed preferredHeights in the example:

    import QtQuick 2.6
    import QtQuick.Window 2.2
    import QtQuick.Layouts 1.3
    
    Window {
        visible: true
        width: 640
        height: 480
        color: "black"
    
        ColumnLayout {
            anchors.fill: parent
            Rectangle {
                color: "red"
                Layout.fillWidth: true
                Layout.fillHeight: true
                Layout.preferredHeight: .2
            }
            ColumnLayout {
                // fillWidth and fillHeight are enabled by default in QtQuick Layouts 
                // Layout.fillWidth: true
                // Layout.fillHeight: true
                Layout.preferredHeight: .1
                Rectangle {
                    color: "blue"
                    Layout.fillHeight: true
                    Layout.fillWidth: true
                }
                Rectangle {
                    color: "green"
                    Layout.fillHeight: true
                    Layout.fillWidth: true
                }
            }
        }
    }
    

    The red rectangle is now always 4 times as high as the other two no matter how high the window is. That's because the red rectangle is always twice as high as the second ColumnLayout and the 2 rectangles inside share the height of the parent equally. If one uses fillHeight with preferredHeight the value of preferredHeight is no longer the height in pixels. It's used as a scaling factor. That's how I use it in almost all my layouts.



  • @DuBu said in Layouts in Qt5.6 vs Qt.5.7:

    I read the documentation and I know how it's meant to be used and maybe it's an unspecified implementation, but that's what I found out: If I use both preferredHeight and fillHeight together, then the items are scaled in preferredHeight ratios. And that's an (undocumented) Feature I really appreciate.

    That's very interesting indeed, and I'm quite sure it's undocumented. I was wrong about using both preferred... and fill... properties. In "Qt Quick Layouts Overview" document page there's an example which uses both. Playing with different properties give interesting results. It seems that minimum... and maximum... can also be used as the base for relative size calculations. In Layout attached properties docs it is told that layouts have a bit different defaults compared to other items. And that leads to them behaving differently in a seemingly undocumented way. Now I think it may be logical but still poorly documented.

    And it looks like your original problem can be solved by changing 0.1 values to 1 or 100. I don't know why; maybe because it's meant to be the amount of pixels which can be expected to be at least 1 and usually an integer value. Anyways it's reasonable to use integers in ratio values, you usually see e.g. 1:2:3 (which means the first is 1/6, the second 2/6 and the third 3/6 of the whole), not 0.1:0.2:0.3.



  • @Eeli-K said in Layouts in Qt5.6 vs Qt.5.7:

    And it looks like your original problem can be solved by changing 0.1 values to 1 or 100. I don't know why; maybe because it's meant to be the amount of pixels which can be expected to be at least 1 and usually an integer value. Anyways it's reasonable to use integers in ratio values, you usually see e.g. 1:2:3 (which means the first is 1/6, the second 2/6 and the third 3/6 of the whole), not 0.1:0.2:0.3.

    Yes, I know, but I wanted to use floating point values, because of the next thing I found out:
    Any single preferredHeight can't be higher then the common parent item. I don't know what goes on under the hood, if it gets higher it breaks the ratios.
    Therefore, I thought I do something like that:

    property int refWindowHeight: 1080
    property real refWindowHalfHeight: refWindowHeight / 2  // = 540
    property real refWindowQuarterHeight: refWindowHeight / 4  // = 270
    .
    .
    .
    // The item should be half as high as the window:
    // First approach:
    // Layout.preferredHeight: refWindowHalfHeight
    // but that's not working: if the windows height gets below 540, the ratios break
    // Second approach:
    Layout.preferredHeight: refWindowHalfHeight / refWindowHeight => 0.5
    .
    .
    .
    // The Item should be a quarter as high as the window:
    Layout.preferredHeight: refWindowQuarterHeight / refWindowHeight => 0.25
    

    Maybe I can try to figure out which ratio is the smallest, set it to 1 and calculate the others accordingly.



  • @DuBu I'm not sure what you mean. Isn't your last proposed solution:

    Maybe I can try to figure out which ratio is the smallest, set it to 1 and calculate the others accordingly.

    the same as my (for example) 1:2:3? Second, why do you have refWindowHeight, refWindowHalfHeight and then calculate the final ratio? Don't you use the parent item's height directly instead of using refWindowHeight? And my previous suggestion about using bindings is simple and I don't see a reason why it wouldn't work:

    import QtQuick 2.6
    import QtQuick.Window 2.2
    import QtQuick.Layouts 1.3
    
    Window {
        id: window
        visible: true
        width: 640
        height: 480
        color: "black"
    
        ColumnLayout {
            anchors.fill: parent
    
            Rectangle {
                Layout.fillHeight: true
                Layout.fillWidth: true
                Layout.preferredHeight: window.height/2 //or *0.5, 50%
            }
    
            ColumnLayout {
                Layout.preferredHeight: window.height/2
                Rectangle {
                    Layout.fillHeight: true
                    Layout.fillWidth: true
                }
                Rectangle {
                    Layout.fillHeight: true
                    Layout.fillWidth: true
                }
            }
        }
    }
    

    In that way the preferred values are always directly correct. Actually Layout.fillHeight wouldn't even be needed then.



  • @Eeli-K said in Layouts in Qt5.6 vs Qt.5.7:

    @DuBu I'm not sure what you mean. Isn't your last proposed solution:

    Maybe I can try to figure out which ratio is the smallest, set it to 1 and calculate the others accordingly.

    the same as my (for example) 1:2:3?

    Yes, your suggestion lead me to think about to use integers. I should have marked it as an answer to your proposal.

    Second, why do you have refWindowHeight, refWindowHalfHeight and then calculate the final ratio? Don't you use the parent item's height directly instead of using refWindowHeight? And my previous suggestion about using bindings is simple and I don't see a reason why it wouldn't work.
    In that way the preferred values are always directly correct. Actually Layout.fillHeight wouldn't even be needed then.

    Yes, all your proposals should work.
    But I don't want to use the parent item's height or any bindings, cause my main goal which makes me think more about it than is actually necessary to just make it work is speed.
    We have more then 150 such layouts in one window on a HD screen on an embedded device with an ARM core.
    My idea was to just use constants like refWindowHeight to pre-calculate the ratios which should never change even when the window and/or the parent items are resized. The constant ratios should be used by the Layout component internally to calculate the actual sizes of its child items. Hopefully the component is written in C++ and does its calculations much faster than the js vm in qml. One of my design goals is to avoid as much as possible calculations in qml (js). I read somewhere QtQuick executes the code which is written in C++ and has to start a js vm just to process a single js expression.



  • @DuBu In that case the startup time is probably more critical. Do you know that with your proposal you will create bindings anyways, and even more than with a simpler solution? You really should do benchmarking or testing before optimizing. Do the layouts change their size after creating? If not, and speed is really important, maybe you should just use absolute positioning and sizing - just calculate everything beforehand. That could be faster.



  • @Eeli-K said in Layouts in Qt5.6 vs Qt.5.7:

    @DuBu In that case the startup time is probably more critical.

    The startup time is fairly long but it's not critical cause the app gets started when the device powers up and it's the only app the device is running.

    Do you know that with your proposal you will create bindings anyways, and even more than with a simpler solution?

    Yes, I know. But these bindings never need to be updated cause the dependent values never change. So I don't mind to use bindings in that case.
    Is there a simpler solution in terms of speed and maintainability? For me (and for the processor which has to process that ;-) ) it's pretty simple to have something like that:
    (Our designer designs the layout for a 1920x1080 screen and defines the sizes of inner parts in pixels.)

    Item {
      Layout.preferredHeight: 540
    }
    Item {
      Layout.preferredHeight: 270
    }
    Item {
      Layout.preferredHeight: 270
    }
    

    Or, if I want to create a reusable component:

    // MyComponent.qml
    ColumnLayout {
      implicitHeight: 200
      Item {
        Layout.preferredHeight: 125
      }
      Item {
        Layout.preferredHeight: 75
      }
    }
    

    (That looks like a good example for using the actual pixels instead of just the ratios which in this case would be 1.66667 to 1.)

    You really should do benchmarking or testing before optimizing.

    I wouldn't call it optimising thinking about a simple way to describe the heights of items.

    Do the layouts change their size after creating?

    Yes, they do.

    If not, and speed is really important, maybe you should just use absolute positioning and sizing - just calculate everything beforehand. That could be faster.

    In my opinion every calculation/expression I can avoid to do in QML makes the code faster. Assuming the guys at Qt did a good job implementing their components.

    Back to my original problem: If it was an undocumented behaviour in Qt5.6 it was really cool and the way I would like to describe my layouts. If it is broken since Qt5.7 I would vote for it to be fixed. If it has no chance to be fixed I tend to write my own layout component in C++. (What would be a good idea cause the way GridLayout works is not really useful anyway.)


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.