<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>Python GUIs - qgridlayout</title><link href="https://www.pythonguis.com/" rel="alternate"/><link href="https://www.pythonguis.com/feeds/qgridlayout.tag.atom.xml" rel="self"/><id>https://www.pythonguis.com/</id><updated>2020-06-19T09:00:00+00:00</updated><subtitle>Create GUI applications with Python and Qt</subtitle><entry><title>Laying Out Multiple Widgets in a Scrollable Grid with PyQt5 — How to dynamically arrange group boxes in a grid layout that scrolls and resizes cleanly</title><link href="https://www.pythonguis.com/faq/pyqt5-gridlayout-issues-arranging-stuffs-non-format/" rel="alternate"/><published>2020-06-19T09:00:00+00:00</published><updated>2020-06-19T09:00:00+00:00</updated><author><name>Martin Fitzpatrick</name></author><id>tag:www.pythonguis.com,2020-06-19:/faq/pyqt5-gridlayout-issues-arranging-stuffs-non-format/</id><summary type="html">When building UIs you can sometimes find yourself needing to display a variable number of widgets arranged in a tidy grid &amp;mdash; say, three columns and as many rows as the data requires. And when there are more items than fit on screen, you want the whole thing to scroll.</summary><content type="html">
            &lt;p&gt;When building UIs you can sometimes find yourself needing to display a variable number of widgets arranged in a tidy grid &amp;mdash; say, three columns and as many rows as the data requires. And when there are more items than fit on screen, you want the whole thing to scroll.&lt;/p&gt;
&lt;p&gt;This is a common pattern, but it's easy to run into trouble. If you set fixed sizes on parent containers, adding more items can cause everything to squash together or overflow. In this tutorial, we'll walk through how to set up a &lt;code&gt;QGridLayout&lt;/code&gt; inside a &lt;code&gt;QScrollArea&lt;/code&gt; so your grid grows naturally and stays scrollable, no matter how many items you add.&lt;/p&gt;
&lt;h2 id="the-problem-with-fixed-geometry"&gt;The problem with fixed geometry&lt;/h2&gt;
&lt;p&gt;A tempting approach when starting out is to use &lt;code&gt;setGeometry()&lt;/code&gt; on every widget &amp;mdash; placing them at exact pixel positions. This works when you know exactly how many items you'll have, but as soon as the number changes (say, from 15 to 30 or 300), things fall apart. Widgets overlap, get clipped, or the container doesn't grow to fit them all.&lt;/p&gt;
&lt;p&gt;The solution is to let Qt's layout system handle the sizing and positioning for you. That's what &lt;a href="https://www.pythonguis.com/tutorials/pyqt-layouts/"&gt;layouts&lt;/a&gt; are for &amp;mdash; they arrange child widgets automatically and respond to changes in content and window size.&lt;/p&gt;
&lt;h2 id="setting-up-the-scrollable-grid"&gt;Setting up the scrollable grid&lt;/h2&gt;
&lt;p&gt;Let's start with a minimal example: a &lt;code&gt;QMainWindow&lt;/code&gt; containing a &lt;code&gt;QScrollArea&lt;/code&gt;, which holds a widget with a &lt;code&gt;QGridLayout&lt;/code&gt;. We'll populate it with some placeholder group boxes.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;import sys

from PyQt5.QtWidgets import (
    QApplication, QMainWindow, QWidget, QScrollArea,
    QFrame, QGridLayout, QGroupBox, QHBoxLayout,
    QPushButton, QLabel,
)


class Window(QMainWindow):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.setGeometry(100, 100, 920, 560)

        # Central widget with its own layout
        central_widget = QWidget(self)
        central_layout = QGridLayout(central_widget)

        # Scroll area
        scroll_area = QScrollArea(central_widget)
        scroll_area.setWidgetResizable(True)
        central_layout.addWidget(scroll_area, 0, 0)

        # A content widget that will live inside the scroll area
        scroll_content = QWidget()
        scroll_content_layout = QGridLayout(scroll_content)

        # The frame that holds our grid of cards
        staff_frame = QFrame(scroll_content)
        staff_frame_layout = QGridLayout(staff_frame)

        # The actual grid where group boxes go
        self.staff_grid = QGridLayout()
        staff_frame_layout.addLayout(self.staff_grid, 0, 0)

        scroll_content_layout.addWidget(staff_frame, 0, 0)
        scroll_area.setWidget(scroll_content)

        self.setCentralWidget(central_widget)

        # Populate the grid
        self.create_boxes()

    def create_boxes(self):
        number = 30
        columns = 3

        for index in range(number):
            row = index // columns
            col = index % columns

            box = QGroupBox()
            box.setFixedSize(250, 130)
            self.staff_grid.addWidget(box, row, col)

            layout = QHBoxLayout()
            box.setLayout(layout)

            label = QLabel(f"Staff #{index + 1}")
            layout.addWidget(label)


if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = Window()
    window.show()
    sys.exit(app.exec_())
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Run this and you'll see 30 group boxes arranged in a 3-column grid, inside a scrollable area. Try changing &lt;code&gt;number = 30&lt;/code&gt; to &lt;code&gt;number = 100&lt;/code&gt; or &lt;code&gt;number = 7&lt;/code&gt; &amp;mdash; the scroll area adjusts automatically.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Grid of staff boxes in a scroll area" src="https://www.pythonguis.com/static/faq/pyqt5-gridlayout-issues-arranging-stuffs-non-format/e9wPRVxM11eCH6rtNAfVjslG8t0.jpeg" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/faq/pyqt5-gridlayout-issues-arranging-stuffs-non-format/e9wPRVxM11eCH6rtNAfVjslG8t0.jpeg?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/faq/pyqt5-gridlayout-issues-arranging-stuffs-non-format/e9wPRVxM11eCH6rtNAfVjslG8t0.jpeg?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/faq/pyqt5-gridlayout-issues-arranging-stuffs-non-format/e9wPRVxM11eCH6rtNAfVjslG8t0.jpeg?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/faq/pyqt5-gridlayout-issues-arranging-stuffs-non-format/e9wPRVxM11eCH6rtNAfVjslG8t0.jpeg?tr=w-600 600w" loading="lazy" width="375" height="500"/&gt;&lt;/p&gt;
&lt;p&gt;There are a few things making this behave properly:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;setWidgetResizable(True)&lt;/code&gt;&lt;/strong&gt; on the &lt;code&gt;QScrollArea&lt;/code&gt; tells it to resize its internal widget to fill available space, while still allowing scrolling when the content exceeds the visible area. For more on how &lt;code&gt;QScrollArea&lt;/code&gt; works, see our &lt;a href="https://www.pythonguis.com/tutorials/qscrollarea/"&gt;dedicated QScrollArea tutorial&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;No &lt;code&gt;setGeometry()&lt;/code&gt; on inner widgets.&lt;/strong&gt; Instead of fixing positions manually, we let the &lt;code&gt;QGridLayout&lt;/code&gt; calculate where each group box goes. The layout grows as items are added, and the scroll area responds.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;setFixedSize(250, 130)&lt;/code&gt;&lt;/strong&gt; on each group box gives them a consistent card-like appearance, but the &lt;em&gt;frame&lt;/em&gt; and &lt;em&gt;scroll content&lt;/em&gt; are not fixed &amp;mdash; they grow as needed.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="summary"&gt;Summary&lt;/h2&gt;
&lt;p&gt;The main takeaways for building dynamic grid layouts in PyQt5:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Use layouts instead of &lt;code&gt;setGeometry()&lt;/code&gt;&lt;/strong&gt; for anything that needs to adapt to varying content. If you're new to PyQt5 layouts, our &lt;a href="https://www.pythonguis.com/tutorials/pyqt-layouts/"&gt;PyQt5 layouts tutorial&lt;/a&gt; covers the different layout types in detail.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Put a &lt;code&gt;QGridLayout&lt;/code&gt; inside a &lt;code&gt;QScrollArea&lt;/code&gt;&lt;/strong&gt; with &lt;code&gt;setWidgetResizable(True)&lt;/code&gt; so the content scrolls naturally as it grows.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Calculate row and column from a single index&lt;/strong&gt; using &lt;code&gt;//&lt;/code&gt; and &lt;code&gt;%&lt;/code&gt; to handle any number of items, including non-multiples of the column count.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you want to take your layouts further with custom-designed interfaces, you can also use &lt;a href="https://www.pythonguis.com/tutorials/qt-designer-gui-layout/"&gt;Qt Designer to visually build your GUI layout&lt;/a&gt; and then populate it programmatically.&lt;/p&gt;
            &lt;p&gt;For an in-depth guide to building Python GUIs with PyQt5 see my book, &lt;a href="https://www.martinfitzpatrick.com/pyqt5-book/"&gt;Create GUI Applications with Python &amp; Qt5.&lt;/a&gt;&lt;/p&gt;
            </content><category term="pyqt5"/><category term="layout"/><category term="qgridlayout"/><category term="qscrollarea"/><category term="python"/><category term="qt"/><category term="qt5"/></entry></feed>