Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. QML and Qt Quick
  4. Centering GridView inside of a window
Forum Updated to NodeBB v4.3 + New Features

Centering GridView inside of a window

Scheduled Pinned Locked Moved Solved QML and Qt Quick
5 Posts 3 Posters 1.1k Views 2 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • cubicapC Offline
    cubicapC Offline
    cubicap
    wrote on last edited by cubicap
    #1

    Hello,
    I am writing an application, that tries to imitate Windows metro (is it still called like that?). Anyways, now I want the home screen of the app to look like the one in Windows 10 settings (the modern one). Yes, I use my PC in the Czech language and no, I don't normally use light mode :).

    0_1568054229567_settings.PNG

    0_1568054233095_regset.PNG

    It doesn't look particularly nice but in the future, I will do proper styling. Also, I don't want a search bar, to list the differences. The thing I have a problem with, is the layout of the buttons.

    Here is the source code, which I describe below.

    import QtQuick 2.2
    import QtQuick.Layouts 1.12
    import QtQuick.Controls 2.0
    import QtQuick.Controls.Universal 2.3
    
    ApplicationWindow {
    
        visible: true
        width: 800
        height: 600
        title: qsTr("Windows Configurator")
        Universal.theme: Universal.System
        Universal.accent: Universal.Cobalt
    
        StackView {
            id: stack
            anchors.fill: parent
            initialItem: homeGrid
    
            Pane {
                id: home
    
                anchors.fill: parent
    
                property var cellHeight: 100
                property var cellWidth: 240
                property var maxPerLine: homeGrid.model.count
                property var minWidthToSwitch: 0
    
                onWidthChanged: {
                    var margin = 0;
                    if(home.width < cellWidth) {
                        margin = 0;
                    }
                    else if(home.width / cellWidth < maxPerLine) {
                        margin = home.width % cellWidth;
                    }
                    else {
                        margin = home.width - cellWidth * maxPerLine;
                    }
    
                    homeGrid.anchors.leftMargin = margin / 2;
                    homeGrid.anchors.rightMargin = margin / 2;
                }
    
                GridView {
                    id: homeGrid
    
                    anchors.fill: parent
    
                    cellHeight: home.cellHeight
                    cellWidth: home.cellWidth
                    boundsBehavior: Flickable.StopAtBounds
    
                    delegate: Item {
                        height: GridView.view.cellHeight
                        width: GridView.view.cellWidth
                        Button {
                            height: parent.height * 0.8
                            width: parent.width * 0.8
                            anchors.verticalCenter: parent.verticalCenter
                            anchors.horizontalCenter: parent.horizontalCenter
                            text: model.title
    
                            onClicked: {
                                stack.push(Qt.resolvedUrl(page));
                            }
                        }
                    }
    
                    model: ListModel {
                        ListElement {
                            title: "Registry"
                            page: "qrc:/Registry.qml"
                        }
                        ListElement {
                            title: "Programs"
                            page: "qrc:/Programs.qml"
                        }
                        ListElement {
                            title: "About"
                            page: "qrc:/About.qml"
                        }
                    }
                }
            }
        }
    }
    

    To layout the buttons, I use GridView to which I give a list of all buttons to show. The problem comes, when I want to center the buttons inside the window. For this, I made a Pane (id:pane) which contains only the GridView and every time its width changes, it changes margins of the GridView to center it. I don't like this solution and think it is not the best either but it is the only thing I was able to get working.

    The problem, however, comes when I push new content to the StackView. Everything seems to work fine but I get a message in the console saying:
    qrc:/Main.qml:46:13: QML GridView: StackView has detected conflicting anchors. Transitions may not execute properly.
    It is pointing on the line opening the GridView. When I delete the line in GridView anchors.fill: parent, the message doesn't pop up anymore but the buttons are not centered anymore.

    When I try to add padding inside the pane (I just replace

    homeGrid.anchors.leftMargin = margin / 2;
    homeGrid.anchors.rightMargin = margin / 2;
    

    with the respective paddings), I get yet another error message qrc:/Main.qml:20:9: QML Pane: Binding loop detected for property "implicitWidth" this time pointing on the line opening the Pane (id: pane) and the items are not centered again (probably because the padding was not applied because of the error?).

    The questions I have are:
    Is there a better way to implement the layout I want?
    What is the error with conflicting anchors caused by?

    Thank you for any advice

    jsulmJ 1 Reply Last reply
    0
    • T Tom_H

      @cubicap In your first example, you have initialItem: homeGrid. It should be initialItem: home. But you don't need the Pane, you can just put the GridView in the StackView directly. Likewise, in the second example, you don't need the ColumnLayout.

      You can also center the GridView with anchors.centerIn: parent.

      cubicapC Offline
      cubicapC Offline
      cubicap
      wrote on last edited by cubicap
      #5

      @tom_h Thank you very much.
      I tried anchors.centerIn: parent before and it did not work probably because I set the wrong initial item 🤦. It probably also was the cause of the anchor conflict I mentioned in the first post.

      The code below is the final outcome and the solution is definitely much better than using anchors+margins. I still ended up wrapping the GridView in a Pane, as I will need the onWidthChanged signal in the future and want it to be separate from the StackView. Also, the only way I found that allowed me to actually center the grid was to change its width manually to nearest lower multiple of cellWidth because otherwise it just filled all the space available and started from left.

              Pane {
                  id: home
      
                  anchors.fill: parent
      
                  property var cellHeight: 100
                  property var cellWidth: 240
                  property var maxPerLine: homeGrid.model.count
                  property var minWidthToSwitch: 0
      
                  onWidthChanged: {
                      if(width < cellWidth) {
                          homeGrid.width = width;
                      }
                      else if(width / cellWidth < maxPerLine) {
                          homeGrid.width = Math.floor(width / cellWidth) * cellWidth;
                      }
                      else {
                          homeGrid.width = cellWidth * maxPerLine;
                      }
                  }
      
                  GridView {
                      id: homeGrid
      
                      anchors.horizontalCenter: parent.horizontalCenter
                      anchors.top: parent.top
                      anchors.bottom: parent.bottom
      
                      cellHeight: home.cellHeight
                      cellWidth: home.cellWidth
      
                      boundsBehavior: Flickable.StopAtBounds
      
                      delegate: Item {
                          height: GridView.view.cellHeight
                          width: GridView.view.cellWidth
                          Button {
                              height: parent.height * 0.8
                              width: parent.width * 0.8
                              anchors.verticalCenter: parent.verticalCenter
                              anchors.horizontalCenter: parent.horizontalCenter
                              text: model.title
      
                              onClicked: {
                                  stack.push(Qt.resolvedUrl(page));
                              }
                          }
                      }
      
                      model: ListModel {
                          ListElement {
                              title: "Registry"
                              page: "qrc:/Registry.qml"
                          }
                          ListElement {
                              title: "Programs"
                              page: "qrc:/Programs.qml"
                          }
                          ListElement {
                              title: "About"
                              page: "qrc:/About.qml"
                          }
                      }
                  }
              }
      

      EDIT: I got the conflicting anchors thing again but removing anchors.fill: parent from the pane did not break anything and solved the problem.

      1 Reply Last reply
      0
      • cubicapC cubicap

        Hello,
        I am writing an application, that tries to imitate Windows metro (is it still called like that?). Anyways, now I want the home screen of the app to look like the one in Windows 10 settings (the modern one). Yes, I use my PC in the Czech language and no, I don't normally use light mode :).

        0_1568054229567_settings.PNG

        0_1568054233095_regset.PNG

        It doesn't look particularly nice but in the future, I will do proper styling. Also, I don't want a search bar, to list the differences. The thing I have a problem with, is the layout of the buttons.

        Here is the source code, which I describe below.

        import QtQuick 2.2
        import QtQuick.Layouts 1.12
        import QtQuick.Controls 2.0
        import QtQuick.Controls.Universal 2.3
        
        ApplicationWindow {
        
            visible: true
            width: 800
            height: 600
            title: qsTr("Windows Configurator")
            Universal.theme: Universal.System
            Universal.accent: Universal.Cobalt
        
            StackView {
                id: stack
                anchors.fill: parent
                initialItem: homeGrid
        
                Pane {
                    id: home
        
                    anchors.fill: parent
        
                    property var cellHeight: 100
                    property var cellWidth: 240
                    property var maxPerLine: homeGrid.model.count
                    property var minWidthToSwitch: 0
        
                    onWidthChanged: {
                        var margin = 0;
                        if(home.width < cellWidth) {
                            margin = 0;
                        }
                        else if(home.width / cellWidth < maxPerLine) {
                            margin = home.width % cellWidth;
                        }
                        else {
                            margin = home.width - cellWidth * maxPerLine;
                        }
        
                        homeGrid.anchors.leftMargin = margin / 2;
                        homeGrid.anchors.rightMargin = margin / 2;
                    }
        
                    GridView {
                        id: homeGrid
        
                        anchors.fill: parent
        
                        cellHeight: home.cellHeight
                        cellWidth: home.cellWidth
                        boundsBehavior: Flickable.StopAtBounds
        
                        delegate: Item {
                            height: GridView.view.cellHeight
                            width: GridView.view.cellWidth
                            Button {
                                height: parent.height * 0.8
                                width: parent.width * 0.8
                                anchors.verticalCenter: parent.verticalCenter
                                anchors.horizontalCenter: parent.horizontalCenter
                                text: model.title
        
                                onClicked: {
                                    stack.push(Qt.resolvedUrl(page));
                                }
                            }
                        }
        
                        model: ListModel {
                            ListElement {
                                title: "Registry"
                                page: "qrc:/Registry.qml"
                            }
                            ListElement {
                                title: "Programs"
                                page: "qrc:/Programs.qml"
                            }
                            ListElement {
                                title: "About"
                                page: "qrc:/About.qml"
                            }
                        }
                    }
                }
            }
        }
        

        To layout the buttons, I use GridView to which I give a list of all buttons to show. The problem comes, when I want to center the buttons inside the window. For this, I made a Pane (id:pane) which contains only the GridView and every time its width changes, it changes margins of the GridView to center it. I don't like this solution and think it is not the best either but it is the only thing I was able to get working.

        The problem, however, comes when I push new content to the StackView. Everything seems to work fine but I get a message in the console saying:
        qrc:/Main.qml:46:13: QML GridView: StackView has detected conflicting anchors. Transitions may not execute properly.
        It is pointing on the line opening the GridView. When I delete the line in GridView anchors.fill: parent, the message doesn't pop up anymore but the buttons are not centered anymore.

        When I try to add padding inside the pane (I just replace

        homeGrid.anchors.leftMargin = margin / 2;
        homeGrid.anchors.rightMargin = margin / 2;
        

        with the respective paddings), I get yet another error message qrc:/Main.qml:20:9: QML Pane: Binding loop detected for property "implicitWidth" this time pointing on the line opening the Pane (id: pane) and the items are not centered again (probably because the padding was not applied because of the error?).

        The questions I have are:
        Is there a better way to implement the layout I want?
        What is the error with conflicting anchors caused by?

        Thank you for any advice

        jsulmJ Offline
        jsulmJ Offline
        jsulm
        Lifetime Qt Champion
        wrote on last edited by
        #2

        @cubicap Instead of doing it manually you should use layouts: https://doc.qt.io/qt-5/qtquicklayouts-index.html

        https://forum.qt.io/topic/113070/qt-code-of-conduct

        1 Reply Last reply
        2
        • cubicapC Offline
          cubicapC Offline
          cubicap
          wrote on last edited by cubicap
          #3

          Thank you.
          Unfortunately, I am not able to make it work either.

          This is the new code:

                  ColumnLayout {
                      id: home
                      anchors.fill: parent
          
                      property var cellHeight: 100
                      property var cellWidth: 240
                      property var maxPerLine: homeGrid.model.count
          
                      GridView {
                          id: homeGrid
          
                          Layout.alignment: Qt.AlignHCenter
          
                          cellHeight: home.cellHeight
                          cellWidth: home.cellWidth
                          boundsBehavior: Flickable.StopAtBounds
          
                          onWidthChanged: {
                              console.log(width);
                          }
          
                          delegate: Item {
                              height: GridView.view.cellHeight
                              width: GridView.view.cellWidth
                              Button {
                                  height: parent.height * 0.8
                                  width: parent.width * 0.8
                                  anchors.verticalCenter: parent.verticalCenter
                                  anchors.horizontalCenter: parent.horizontalCenter
                                  text: model.title
          
                                  onClicked: {
                                      stack.push(Qt.resolvedUrl(page));
                                  }
                              }
                          }
          
                          model: ListModel {
                              ListElement {
                                  title: "Registry"
                                  page: "qrc:/Registry.qml"
                              }
                              ListElement {
                                  title: "Programs"
                                  page: "qrc:/Programs.qml"
                              }
                              ListElement {
                                  title: "About"
                                  page: "qrc:/About.qml"
                              }
                          }
                      }
                  }
          

          I replaced the Pane by a ColumnLayout, hoping, I could use Layout.alignment: Qt.AlignHCenter to center the grid inside of a window. I don't know why but the grid keeps resizing with the layout and so I can not align it as it already fills the whole space. I am not even able to set the width of the grid to be constant, it just sets, triggers event on the new width and imediatelly resets back to the width of the window.
          I see that when I put just a few buttons into the layout, they are centered properly but in this case, it does not.

          I also tried to put the buttons inside of a GridLayout but it did not line wrap and it also stretched to fill the whole window which is not a thing I expect. I want the buttons to have constant spacing, constant dimensions and wrap to the next line, if necessary. And indeed to be centered.

          T 1 Reply Last reply
          0
          • cubicapC cubicap

            Thank you.
            Unfortunately, I am not able to make it work either.

            This is the new code:

                    ColumnLayout {
                        id: home
                        anchors.fill: parent
            
                        property var cellHeight: 100
                        property var cellWidth: 240
                        property var maxPerLine: homeGrid.model.count
            
                        GridView {
                            id: homeGrid
            
                            Layout.alignment: Qt.AlignHCenter
            
                            cellHeight: home.cellHeight
                            cellWidth: home.cellWidth
                            boundsBehavior: Flickable.StopAtBounds
            
                            onWidthChanged: {
                                console.log(width);
                            }
            
                            delegate: Item {
                                height: GridView.view.cellHeight
                                width: GridView.view.cellWidth
                                Button {
                                    height: parent.height * 0.8
                                    width: parent.width * 0.8
                                    anchors.verticalCenter: parent.verticalCenter
                                    anchors.horizontalCenter: parent.horizontalCenter
                                    text: model.title
            
                                    onClicked: {
                                        stack.push(Qt.resolvedUrl(page));
                                    }
                                }
                            }
            
                            model: ListModel {
                                ListElement {
                                    title: "Registry"
                                    page: "qrc:/Registry.qml"
                                }
                                ListElement {
                                    title: "Programs"
                                    page: "qrc:/Programs.qml"
                                }
                                ListElement {
                                    title: "About"
                                    page: "qrc:/About.qml"
                                }
                            }
                        }
                    }
            

            I replaced the Pane by a ColumnLayout, hoping, I could use Layout.alignment: Qt.AlignHCenter to center the grid inside of a window. I don't know why but the grid keeps resizing with the layout and so I can not align it as it already fills the whole space. I am not even able to set the width of the grid to be constant, it just sets, triggers event on the new width and imediatelly resets back to the width of the window.
            I see that when I put just a few buttons into the layout, they are centered properly but in this case, it does not.

            I also tried to put the buttons inside of a GridLayout but it did not line wrap and it also stretched to fill the whole window which is not a thing I expect. I want the buttons to have constant spacing, constant dimensions and wrap to the next line, if necessary. And indeed to be centered.

            T Offline
            T Offline
            Tom_H
            wrote on last edited by
            #4

            @cubicap In your first example, you have initialItem: homeGrid. It should be initialItem: home. But you don't need the Pane, you can just put the GridView in the StackView directly. Likewise, in the second example, you don't need the ColumnLayout.

            You can also center the GridView with anchors.centerIn: parent.

            cubicapC 1 Reply Last reply
            1
            • T Tom_H

              @cubicap In your first example, you have initialItem: homeGrid. It should be initialItem: home. But you don't need the Pane, you can just put the GridView in the StackView directly. Likewise, in the second example, you don't need the ColumnLayout.

              You can also center the GridView with anchors.centerIn: parent.

              cubicapC Offline
              cubicapC Offline
              cubicap
              wrote on last edited by cubicap
              #5

              @tom_h Thank you very much.
              I tried anchors.centerIn: parent before and it did not work probably because I set the wrong initial item 🤦. It probably also was the cause of the anchor conflict I mentioned in the first post.

              The code below is the final outcome and the solution is definitely much better than using anchors+margins. I still ended up wrapping the GridView in a Pane, as I will need the onWidthChanged signal in the future and want it to be separate from the StackView. Also, the only way I found that allowed me to actually center the grid was to change its width manually to nearest lower multiple of cellWidth because otherwise it just filled all the space available and started from left.

                      Pane {
                          id: home
              
                          anchors.fill: parent
              
                          property var cellHeight: 100
                          property var cellWidth: 240
                          property var maxPerLine: homeGrid.model.count
                          property var minWidthToSwitch: 0
              
                          onWidthChanged: {
                              if(width < cellWidth) {
                                  homeGrid.width = width;
                              }
                              else if(width / cellWidth < maxPerLine) {
                                  homeGrid.width = Math.floor(width / cellWidth) * cellWidth;
                              }
                              else {
                                  homeGrid.width = cellWidth * maxPerLine;
                              }
                          }
              
                          GridView {
                              id: homeGrid
              
                              anchors.horizontalCenter: parent.horizontalCenter
                              anchors.top: parent.top
                              anchors.bottom: parent.bottom
              
                              cellHeight: home.cellHeight
                              cellWidth: home.cellWidth
              
                              boundsBehavior: Flickable.StopAtBounds
              
                              delegate: Item {
                                  height: GridView.view.cellHeight
                                  width: GridView.view.cellWidth
                                  Button {
                                      height: parent.height * 0.8
                                      width: parent.width * 0.8
                                      anchors.verticalCenter: parent.verticalCenter
                                      anchors.horizontalCenter: parent.horizontalCenter
                                      text: model.title
              
                                      onClicked: {
                                          stack.push(Qt.resolvedUrl(page));
                                      }
                                  }
                              }
              
                              model: ListModel {
                                  ListElement {
                                      title: "Registry"
                                      page: "qrc:/Registry.qml"
                                  }
                                  ListElement {
                                      title: "Programs"
                                      page: "qrc:/Programs.qml"
                                  }
                                  ListElement {
                                      title: "About"
                                      page: "qrc:/About.qml"
                                  }
                              }
                          }
                      }
              

              EDIT: I got the conflicting anchors thing again but removing anchors.fill: parent from the pane did not break anything and solved the problem.

              1 Reply Last reply
              0

              • Login

              • Login or register to search.
              • First post
                Last post
              0
              • Categories
              • Recent
              • Tags
              • Popular
              • Users
              • Groups
              • Search
              • Get Qt Extensions
              • Unsolved