Desktop - GUI

How to Create a PySide6 GUI Layout from a Hand-Drawn Sketch

Ever sketched a UI layout on paper and wondered how to bring it to life with code?

In this tutorial, we’ll walk through the process of transforming a simple hand-drawn UI sketch into a fully structured GUI layout using PySide6, the official Python bindings for the Qt toolkit. Whether you’re prototyping your next app, learning layout management, or just getting started with Python desktop development, this post will help you visualize and implement your design step by step—no backend logic required.

We’ll focus purely on the visual structure of the GUI—arranging buttons, labels, input fields, and text areas to match your original sketch. By the end, you’ll have a clean layout built with QVBoxLayout and QHBoxLayout, and a deeper understanding of how PySide6 handles widget positioning.

If you’re a visual thinker or UI designer entering the world of Python development, this layout-first approach is a great place to begin.

Layout Strategy

Before jumping into the code, let’s take a moment to analyze the sketch and plan out how to structure it using PySide6’s layout system.

Qt (and PySide6 by extension) uses layout managers like QVBoxLayout and QHBoxLayout to arrange widgets in vertical and horizontal flows. By nesting these layouts, you can build complex and responsive UIs—just like stacking building blocks.

Visual Breakdown of the Sketch

My hand-drawn UI sketch can be divided into two main parts:

  1. Top Bar (Navigation Buttons)
    A horizontal row of 5 buttons aligned side by side — best handled by a QHBoxLayout.
  2. Main Content Area
    A three-column structure arranged horizontally:
    • Left Panel: Label, dropdown (QComboBox), radio buttons, checkboxes
    • Center Panel: Large text area (QTextEdit)
    • Right Panel: Label, input field (QLineEdit), button, output label

This calls for a main vertical layout (QVBoxLayout) containing:

  • The top button row
  • The content row with three vertical sub-panels

Layout Plan

Here’s how we’ll nest the layouts:

QVBoxLayout (Main Layout)

├── QHBoxLayout (Top Button Row)
│ ├── QPushButton("Button 1")
│ ├── QPushButton("Button 2")
│ ├── ...

└── QHBoxLayout (Main Content Row)
├── QVBoxLayout (Left Panel)
│ ├── QLabel
│ ├── QComboBox
│ ├── QRadioButtons
│ ├── QCheckBoxes

├── QVBoxLayout (Center Panel)
│ └── QTextEdit

└── QVBoxLayout (Right Panel)
├── QLabel
├── QLineEdit
├── QPushButton
└── QLabel

Why This Structure Works

  • Separation of concerns: Each section of the UI is isolated in its own layout.
  • Scalability: You can easily add or remove widgets within a section without affecting others.
  • Alignment control: You gain precise control over spacing, padding, and alignment using layout methods.

The PySide6 Code

from PySide6.QtWidgets import (
    QApplication, QWidget, QPushButton, QLabel, QComboBox,
    QRadioButton, QCheckBox, QTextEdit, QLineEdit,
    QVBoxLayout, QHBoxLayout, QButtonGroup
)
from PySide6.QtCore import Qt


class CustomWindow(QWidget):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Custom GUI Layout")
        self.resize(900, 600)

        main_layout = QVBoxLayout()
        content_layout = QHBoxLayout()

        # Top Button Bar
        top_button_layout = QHBoxLayout()
        for i in range(5):
            btn = QPushButton(f"Button {i+1}")
            btn.setFixedHeight(40)
            top_button_layout.addWidget(btn)
        main_layout.addLayout(top_button_layout)

        # Left Panel
        left_panel = QVBoxLayout()
        left_panel.addWidget(QLabel("Label"))

        combo = QComboBox()
        combo.addItems(["Option 1", "Option 2", "Option 3"])
        left_panel.addWidget(combo)

        left_panel.addWidget(QLabel("Radio Button"))
        radio_group = QButtonGroup(self)
        radio_yes = QRadioButton("Yes")
        radio_no = QRadioButton("No")
        radio_group.addButton(radio_yes)
        radio_group.addButton(radio_no)
        left_panel.addWidget(radio_yes)
        left_panel.addWidget(radio_no)

        left_panel.addWidget(QLabel("Check Box"))
        for _ in range(3):
            cb = QCheckBox()
            left_panel.addWidget(cb)

        # Center Panel
        center_panel = QVBoxLayout()
        text_area = QTextEdit()
        center_panel.addWidget(text_area)

        # Right Panel
        right_panel = QVBoxLayout()
        right_panel.addWidget(QLabel("Label"))

        input_box = QLineEdit()
        right_panel.addWidget(input_box)

        right_button = QPushButton("Button")
        right_panel.addWidget(right_button)

        right_panel.addWidget(QLabel("Label"))

        # Assemble Panels
        content_layout.addLayout(left_panel)
        content_layout.addLayout(center_panel, stretch=2)
        content_layout.addLayout(right_panel)

        main_layout.addLayout(content_layout)
        self.setLayout(main_layout)


def main():
    app = QApplication([])
    window = CustomWindow()
    window.show()
    app.exec()


if __name__ == "__main__":
    main()

PySide6 Widgets & Layouts Explained

QApplication

Purpose: The backbone of any PySide6 GUI app. It manages the GUI application’s control flow and main settings.

  • You must create one instance per app.
  • It handles the event loop, user input, and window updates.
app = QApplication([])
app.exec()         # Starts the event loop

QWidget

Purpose: The base class for all UI elements.

  • Every button, label, and even the main window inherits from QWidget.
  • You can use it as a standalone window or as a container for other widgets.
class MyApp(QWidget):  # Main window

Common Methods:

  • show() – Displays the widget
  • resize(width, height) – Sets initial size
  • setWindowTitle("Title") – Sets the window title
  • setLayout(layout) – Assigns a layout to the widget

QPushButton

Purpose: A clickable button.

  • Used to trigger actions when clicked.
  • You can connect it to a function with .clicked.connect(...).
button = QPushButton("Click Me")
button.clicked.connect(my_function)

Common Methods:

  • setText("Click Me") – Set the button text
  • clicked.connect(func) – Connects button click to a function
  • setEnabled(True/False) – Enable/disable the button
  • setFixedSize(w, h) – Sets exact size
  • setStyleSheet("...") – CSS styling

QLabel

Purpose: Displays text or an image.

  • Great for showing instructions, output messages, or titles.
  • Use .setText("...") to change its content.
label = QLabel("Hello")
label.setAlignment(Qt.AlignCenter)

Common Methods:

  • setText("Hello") – Set label content
  • text() – Get current text
  • setAlignment(Qt.AlignCenter) – Center-align text
  • setPixmap(pixmap) – Show an image

QComboBox

Purpose: A drop-down list of selectable options.

  • Users can pick one item from a list.
  • Items can be added with .addItem() or .addItems().
combo = QComboBox()
combo.addItems(["Option 1", "Option 2"])

Common Methods:

  • addItem("Option") – Add one option
  • addItems(["A", "B"]) – Add multiple options
  • currentText() – Get selected item text
  • setCurrentIndex(index) – Set selected index
  • clear() – Remove all items

QRadioButton

Purpose: A button that belongs to a group, where only one can be selected at a time.

  • Use it with QButtonGroup to manage mutual exclusivity.
  • Good for “Yes / No”, “Male / Female”, etc.
radio = QRadioButton("Yes")

Common Methods:

  • setChecked(True) – Set selected
  • isChecked() – Check if selected
  • toggled.connect(func) – Run function on toggle
  • setText("Yes") – Set label text

QCheckBox

Purpose: A box the user can check or uncheck.

  • Multiple checkboxes can be selected at the same time.
  • Use .isChecked() to check the state.
checkbox = QCheckBox("Subscribe")

Common Methods:

  • setChecked(True/False) – Set state
  • isChecked() – Get check state
  • stateChanged.connect(func) – Detect change
  • text() – Get label text

QTextEdit

Purpose: A multi-line text input/output area (like a rich text editor).

  • Supports typing, pasting, and formatting.
  • Use .toPlainText() to read the text.
text_area = QTextEdit()

Common Methods:

  • setPlainText("text") – Set contents
  • toPlainText() – Get contents
  • clear() – Clear text
  • setReadOnly(True) – Disable editing
  • append("...") – Add line to bottom

QLineEdit

Purpose: A single-line text input box.

  • Use it for names, emails, search bars, etc.
  • Use .text() to retrieve input.
input_box = QLineEdit()

Common Methods:

  • setText("name") – Set input value
  • text() – Get input value
  • clear() – Erase content
  • setPlaceholderText("Enter name") – Set hint text
  • editingFinished.connect(func) – Trigger on Enter or focus out

QVBoxLayout

Purpose: A layout manager that arranges widgets vertically (top to bottom).

  • Automatically stacks widgets in a column.
  • Use .addWidget() to insert widgets.
layout = QVBoxLayout()
layout.addWidget(label)
layout.addWidget(button)

Common Methods:

  • addWidget(widget) – Add widget vertically
  • addLayout(layout) – Nest another layout
  • setSpacing(px) – Space between widgets
  • setContentsMargins(l, t, r, b) – Padding around edges

QHBoxLayout

Purpose: A layout manager that arranges widgets horizontally (left to right).

  • Used to create rows of widgets.
  • Can be nested inside QVBoxLayout.
row = QHBoxLayout()
row.addWidget(button1)
row.addWidget(button2)

Common Methods: Same as QVBoxLayout, but arranges left to right.

QButtonGroup

Purpose: Groups multiple QRadioButtons (or QCheckBoxes) so only one can be selected.

  • Useful for managing radio buttons.
  • Doesn’t control layout—only logic.
group = QButtonGroup()
group.addButton(radio_yes)
group.addButton(radio_no)

Common Methods:

  • addButton(button) – Add a radio or checkbox to the group
  • checkedButton() – Get currently selected button
  • setExclusive(True) – Only one button active at a time
  • buttonClicked.connect(func) – React when any button is clicked

BONUS: Common Signals

WidgetSignalTriggered When…
QPushButtonclicked.connect()Button is clicked
QRadioButtontoggled.connect()Radio is selected/deselected
QCheckBoxstateChanged.connect()Check state is changed
QLineEditeditingFinished.connect()Input loses focus or Enter is pressed
QComboBoxcurrentIndexChanged.connect()Selected item changes

Summary Chart

WidgetPurpose
QApplicationStarts & manages the app
QWidgetBase class for all UI elements
QPushButtonClickable button
QLabelDisplay text or images
QComboBoxDrop-down menu
QRadioButtonSelect one option in a group
QCheckBoxOn/off toggle box
QTextEditMulti-line text input
QLineEditSingle-line text input
QVBoxLayoutVertical layout manager
QHBoxLayoutHorizontal layout manager
QButtonGroupLogical group for radio buttons

What Is Qt Used For?

from PySide6.QtCore import Qt

This line imports the Qt namespace from the PySide6.QtCore module. It’s a core part of PySide6 used to access constants, enums, and flags related to alignment, keyboard modifiers, mouse buttons, window states, etc.

Qt is a container for a wide variety of enumeration values that you’ll use to control things like:

1. Text Alignment

Used with QLabel, QTextEdit, QLineEdit, etc.

label.setAlignment(Qt.AlignCenter)              # Center (horizontal and vertical)
label.setAlignment(Qt.AlignLeft | Qt.AlignTop)  # Top-left

2. Keyboard Modifiers

Used when detecting keys like Ctrl, Shift, etc.

def keyPressEvent(self, event):
    if event.modifiers() == Qt.ControlModifier:
        print("Ctrl key pressed")

3. Window Flags and States

Control how the window behaves or appears.

self.setWindowFlag(Qt.FramelessWindowHint)   # Removes title bar
self.setWindowState(Qt.WindowMaximized)      # Start in maximized mode

4. Mouse Buttons

Used in mouse event handlers.

def mousePressEvent(self, event):
    if event.button() == Qt.LeftButton:
        print("Left click")

Common Qt Alignment Constants

ConstantMeaning
Qt.AlignLeftAlign left
Qt.AlignRightAlign right
Qt.AlignTopAlign to the top
Qt.AlignBottomAlign to the bottom
Qt.AlignHCenterAlign horizontally center
Qt.AlignVCenterAlign vertically center
Qt.AlignCenterBoth horizontal and vertical center

What the App GUI looks like

Conclusion

Designing a GUI doesn’t have to start with code—it can start with a simple sketch, a clear idea, and a solid layout strategy. In this post, we took a hand-drawn UI concept and translated it into a well-structured layout using PySide6, focusing purely on visual organization without diving into functionality.

By leveraging layout managers like QVBoxLayout and QHBoxLayout, you now have the foundation to build scalable, clean, and visually organized desktop applications. Whether you’re just getting started or planning to add interactivity later, this layout-first approach ensures your GUI is thoughtfully designed from the start.

Leave a Reply

Your email address will not be published. Required fields are marked *