Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. Qt for Python
  4. Shifting elements in a grid layout
Forum Updated to NodeBB v4.3 + New Features

Shifting elements in a grid layout

Scheduled Pinned Locked Moved Unsolved Qt for Python
6 Posts 2 Posters 1.6k Views
  • 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.
  • S Offline
    S Offline
    sam1
    wrote on last edited by
    #1

    I want to have multiple rows of widgets, each with 4 columns. When i get rid of a widget , I want each element to be shifted up so that it maintains having 4 widgets per row, as shown below when widget c is deleted:
    Screenshot 2020-11-09 at 19.47.06.png

    I was thinking a grid layout would be useful, but I'm not sure how to implement it exactly.

    Thanks for the help.

    JonBJ 1 Reply Last reply
    0
    • S sam1

      I want to have multiple rows of widgets, each with 4 columns. When i get rid of a widget , I want each element to be shifted up so that it maintains having 4 widgets per row, as shown below when widget c is deleted:
      Screenshot 2020-11-09 at 19.47.06.png

      I was thinking a grid layout would be useful, but I'm not sure how to implement it exactly.

      Thanks for the help.

      JonBJ Online
      JonBJ Online
      JonB
      wrote on last edited by
      #2

      @sam1
      If you use QGridLayout you have to move all the items yourself in code.

      If you want to take Flow Layout Example, that may be what you are looking for if you want to widgets to flow around.

      S 1 Reply Last reply
      2
      • JonBJ JonB

        @sam1
        If you use QGridLayout you have to move all the items yourself in code.

        If you want to take Flow Layout Example, that may be what you are looking for if you want to widgets to flow around.

        S Offline
        S Offline
        sam1
        wrote on last edited by
        #3

        @JonB Thank you, I'll take a look at that later. Is it also in pyqt though? the link you sent was a c++ implementation. Hopefully this solves my problem

        JonBJ 1 Reply Last reply
        0
        • S sam1

          @JonB Thank you, I'll take a look at that later. Is it also in pyqt though? the link you sent was a c++ implementation. Hopefully this solves my problem

          JonBJ Online
          JonBJ Online
          JonB
          wrote on last edited by
          #4

          @sam1
          I forgot you were Python. As it happens I ported it to Python, and made some fix. I'll try to remember to dig it out tomorrow and post here.

          S 1 Reply Last reply
          0
          • JonBJ JonB

            @sam1
            I forgot you were Python. As it happens I ported it to Python, and made some fix. I'll try to remember to dig it out tomorrow and post here.

            S Offline
            S Offline
            sam1
            wrote on last edited by
            #5

            @JonB Hello, Any chance you can share it here now?

            JonBJ 1 Reply Last reply
            0
            • S sam1

              @JonB Hello, Any chance you can share it here now?

              JonBJ Online
              JonBJ Online
              JonB
              wrote on last edited by JonB
              #6

              @sam1
              Here is a standalone file for you to use.

              #############################################################################
              #
              # This file taken from
              # https://code.qt.io/cgit/qt/qtbase.git/tree/examples/widgets/layouts/flowlayout/flowlayout.cpp?h=5.13
              # Modified/adapted by jon, 10/07/2019, to translate into Python/PyQt
              #
              # Copyright (C) 2016 The Qt Company Ltd.
              # Contact: https://www.qt.io/licensing/
              #
              # This file is part of the examples of the Qt Toolkit.
              #
              # $QT_BEGIN_LICENSE:BSD$
              # Commercial License Usage
              # Licensees holding valid commercial Qt licenses may use this file in
              # accordance with the commercial license agreement provided with the
              # Software or, alternatively, in accordance with the terms contained in
              # a written agreement between you and The Qt Company. For licensing terms
              # and conditions see https://www.qt.io/terms-conditions. For further
              # information use the contact form at https://www.qt.io/contact-us.
              #
              # BSD License Usage
              # Alternatively, you may use this file under the terms of the BSD license
              # as follows:
              #
              # "Redistribution and use in source and binary forms, with or without
              # modification, are permitted provided that the following conditions are
              # met:
              #   * Redistributions of source code must retain the above copyright
              #     notice, this list of conditions and the following disclaimer.
              #   * Redistributions in binary form must reproduce the above copyright
              #     notice, this list of conditions and the following disclaimer in
              #     the documentation and/or other materials provided with the
              #     distribution.
              #   * Neither the name of The Qt Company Ltd nor the names of its
              #     contributors may be used to endorse or promote products derived
              #     from this software without specific prior written permission.
              #
              #
              # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
              # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
              # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
              # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
              # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
              # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
              # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
              # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
              # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
              # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
              # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
              #
              # $QT_END_LICENSE$
              #
              #############################################################################
              
              
              import typing
              
              from PyQt5.QtCore import Qt, QPoint, QRect, QSize
              from PyQt5.QtWidgets import QWidget, QLayout, QLayoutItem, QStyle, QSizePolicy
              
              
              class FlowLayout(QLayout):
                  def __init__(self, parent: QWidget=None, margin: int=-1, hSpacing: int=-1, vSpacing: int=-1):
                      super().__init__(parent)
              
                      self.itemList = list()
                      self.m_hSpace = hSpacing
                      self.m_vSpace = vSpacing
              
                      self.setContentsMargins(margin, margin, margin, margin)
              
                  def __del__(self):
                      # copied for consistency, not sure this is needed or ever called
                      item = self.takeAt(0)
                      while item:
                          item = self.takeAt(0)
              
                  def addItem(self, item: QLayoutItem):
                      self.itemList.append(item)
              
                  def horizontalSpacing(self) -> int:
                      if self.m_hSpace >= 0:
                          return self.m_hSpace
                      else:
                          return self.smartSpacing(QStyle.PM_LayoutHorizontalSpacing)
              
                  def verticalSpacing(self) -> int:
                      if self.m_vSpace >= 0:
                          return self.m_vSpace
                      else:
                          return self.smartSpacing(QStyle.PM_LayoutVerticalSpacing)
              
                  def count(self) -> int:
                      return len(self.itemList)
              
                  def itemAt(self, index: int) -> typing.Union[QLayoutItem, None]:
                      if 0 <= index < len(self.itemList):
                          return self.itemList[index]
                      else:
                          return None
              
                  def takeAt(self, index: int) -> typing.Union[QLayoutItem, None]:
                      if 0 <= index < len(self.itemList):
                          return self.itemList.pop(index)
                      else:
                          return None
              
                  def expandingDirections(self) -> Qt.Orientations:
                      return Qt.Orientations(Qt.Orientation(0))
              
                  def hasHeightForWidth(self) -> bool:
                      return True
              
                  def heightForWidth(self, width: int) -> int:
                      height = self.doLayout(QRect(0, 0, width, 0), True)
                      return height
              
                  def setGeometry(self, rect: QRect) -> None:
                      super().setGeometry(rect)
                      self.doLayout(rect, False)
              
                  def sizeHint(self) -> QSize:
                      return self.minimumSize()
              
                  def minimumSize(self) -> QSize:
                      size = QSize()
                      for item in self.itemList:
                          size = size.expandedTo(item.minimumSize())
              
                      margins = self.contentsMargins()
                      size += QSize(margins.left() + margins.right(), margins.top() + margins.bottom())
                      return size
              
                  def smartSpacing(self, pm: QStyle.PixelMetric) -> int:
                      parent = self.parent()
                      if not parent:
                          return -1
                      elif parent.isWidgetType():
                          return parent.style().pixelMetric(pm, None, parent)
                      else:
                          return parent.spacing()
              
                  def doLayout(self, rect: QRect, testOnly: bool) -> int:
                      left, top, right, bottom = self.getContentsMargins()
                      effectiveRect = rect.adjusted(+left, +top, -right, -bottom)
                      x = effectiveRect.x()
                      y = effectiveRect.y()
                      lineHeight = 0
              
                      for item in self.itemList:
                          wid = item.widget()
                          spaceX = self.horizontalSpacing()
                          if spaceX == -1:
                              spaceX = wid.style().layoutSpacing(QSizePolicy.PushButton, QSizePolicy.PushButton, Qt.Horizontal)
                          spaceY = self.verticalSpacing()
                          if spaceY == -1:
                              spaceY = wid.style().layoutSpacing(QSizePolicy.PushButton, QSizePolicy.PushButton, Qt.Vertical)
              
                          nextX = x + item.sizeHint().width() + spaceX
                          if nextX - spaceX > effectiveRect.right() and lineHeight > 0:
                              x = effectiveRect.x()
                              y = y + lineHeight + spaceY
                              nextX = x + item.sizeHint().width() + spaceX
                              lineHeight = 0
              
                          if not testOnly:
                              item.setGeometry(QRect(QPoint(x, y), item.sizeHint()))
                  
                          x = nextX
                          lineHeight = max(lineHeight, item.sizeHint().height())
              
                      return y + lineHeight - rect.y() + bottom
              

              And then I wrapped it into my own class, with a couple of comments:

              class JFlowLayout(FlowLayout):
                  # flow layout, similar to an HTML `<DIV>`
                  # this is our "wrapper" to the `FlowLayout` sample Qt code we have implemented
                  # we use it in place of where we used to use a `QHBoxLayout`
                  # in order to make few outside-world changes, and revert to `QHBoxLayout`if we ever want to,
                  # there are a couple of methods here which are available on a `QBoxLayout` but not on a `QLayout`
                  # for which we provide a "lite-equivalent" which will suffice for our purposes
              
                  def addLayout(self, layout: QLayout, stretch: int=0):
                      # "equivalent" of `QBoxLayout.addLayout()`
                      # we want to add sub-layouts (e.g. a `QVBoxLayout` holding a label above a widget)
                      # there is some dispute as to how to do this/whether it is supported by `FlowLayout`
                      # see my https://forum.qt.io/topic/104653/how-to-do-a-no-break-qhboxlayout
                      # there is a suggestion that we should not add a sub-layout but rather enclose it in a `QWidget`
                      # but since it seems to be working as I've done it below I'm elaving it at that for now...
              
                      # suprisingly to me, we do not need to add the layout via `addChildLayout()`, that seems to make no difference
                      # self.addChildLayout(layout)
                      # all that seems to be reuqired is to add it onto the list via `addItem()`
                      self.addItem(layout)
              
                  def addStretch(self, stretch: int=0):
                      # "equivalent" of `QBoxLayout.addStretch()`
                      # we can't do stretches, we just arbitrarily put in a "spacer" to give a bit of a gap
                      w = stretch * 20
                      spacerItem = QtWidgets.QSpacerItem(w, 0, QSizePolicy.Expanding, QSizePolicy.Minimum)
                      self.addItem(spacerItem)
              
              1 Reply Last reply
              2

              • Login

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