Build Advanced GUI Applications with PyQt6 and Python

This article walks through setting up a Python development environment for Qt6, demonstrates core and advanced PyQt6 widgets, layout management, dialogs, graphics view, database integration, multithreading, MVC architecture, custom widgets, and a complete simple calculator example, concluding with best‑practice guidelines.

Data STUDIO
Data STUDIO
Data STUDIO
Build Advanced GUI Applications with PyQt6 and Python

Setting Up the Development Environment

Install the latest Python version and add it to the system PATH. Then install either PyQt6 or PySide6 via pip, and optionally install the Qt Designer tools:

pip install PyQt6
pip install PySide6
pip install PyQt6-tools   # for PyQt6
pip install pyside6-tools   # for PySide6

Basic Widgets

QPushButton

A clickable button that triggers actions.

from PyQt6.QtWidgets import QApplication, QPushButton, QWidget, QVBoxLayout

def on_click():
    print('Button clicked!')

app = QApplication([])
window = QWidget()
layout = QVBoxLayout()
button = QPushButton('Click Me')
button.clicked.connect(on_click)
layout.addWidget(button)
window.setLayout(layout)
window.show()
app.exec()

QLabel

Displays static text or images.

from PyQt6.QtWidgets import QApplication, QLabel, QWidget, QVBoxLayout

app = QApplication([])
window = QWidget()
layout = QVBoxLayout()
label = QLabel('Hello, PyQt6!')
layout.addWidget(label)
window.setLayout(layout)
window.show()
app.exec()

Layout Management

Use QVBoxLayout for vertical stacking and QHBoxLayout for horizontal stacking.

from PyQt6.QtWidgets import QApplication, QPushButton, QVBoxLayout, QHBoxLayout, QWidget

app = QApplication([])
window = QWidget()

v_layout = QVBoxLayout()
h_layout = QHBoxLayout()

btn1 = QPushButton('Button 1')
btn2 = QPushButton('Button 2')

v_layout.addWidget(btn1)
h_layout.addWidget(btn2)

window.setLayout(v_layout)   # or window.setLayout(h_layout)
window.show()
app.exec()

Dialogs and Windows

QMessageBox

Shows a simple message dialog.

from PyQt6.QtWidgets import QApplication, QMessageBox

app = QApplication([])
msg_box = QMessageBox()
msg_box.setText('This is a message box.')
msg_box.exec()

QDialog with Buttons

from PyQt6.QtWidgets import QApplication, QDialog, QDialogButtonBox, QVBoxLayout

app = QApplication([])

dialog = QDialog()
buttonBox = QDialogButtonBox(QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel)
buttonBox.accepted.connect(dialog.accept)
buttonBox.rejected.connect(dialog.reject)
layout = QVBoxLayout()
layout.addWidget(buttonBox)

dialog.setLayout(layout)

dialog.exec()

Advanced Widgets

QTableView (Model‑View)

Displays tabular data using a custom model.

from PyQt6.QtWidgets import QApplication, QTableView, QWidget, QVBoxLayout
from PyQt6.QtCore import QAbstractTableModel, Qt

class MyTableModel(QAbstractTableModel):
    def rowCount(self, parent=None):
        return 5  # example rows
    def columnCount(self, parent=None):
        return 3  # example columns
    def data(self, index, role):
        if role == Qt.ItemDataRole.DisplayRole:
            return f"Cell ({index.row()}, {index.column()})"

app = QApplication([])
window = QWidget()
layout = QVBoxLayout()
table_view = QTableView()
model = MyTableModel()
table_view.setModel(model)
layout.addWidget(table_view)
window.setLayout(layout)
window.show()
app.exec()

Graphics View Framework

Uses QGraphicsView and QGraphicsScene for custom 2D graphics.

from PyQt6.QtWidgets import QApplication, QGraphicsScene, QGraphicsView

app = QApplication([])
scene = QGraphicsScene()
scene.addText('Hello, PyQt6 Graphics View!')
view = QGraphicsView(scene)
view.show()
app.exec()

Database Integration (SQLite)

from PyQt6.QtSql import QSqlDatabase, QSqlQuery

db = QSqlDatabase.addDatabase('QSQLITE')
db.setDatabaseName(':memory:')
if db.open():
    query = QSqlQuery()
    query.exec('CREATE TABLE people (id INTEGER PRIMARY KEY, name TEXT)')

Multithreading with QThread

Keep the UI responsive by moving long‑running tasks to a separate thread.

from PyQt6.QtCore import QThread, pyqtSignal
from PyQt6.QtWidgets import QApplication, QLabel
import time

class Worker(QThread):
    updated = pyqtSignal(str)
    def run(self):
        time.sleep(5)  # simulate long task
        self.updated.emit('Task Finished')

app = QApplication([])
label = QLabel('Starting...')
label.show()
worker = Worker()
worker.updated.connect(lambda text: label.setText(text))
worker.start()
app.exec()

Advanced PyQt6 Concepts

Model‑View‑Controller (MVC) Architecture

Separates data, view, and controller for maintainable code.

from PyQt6.QtWidgets import QApplication, QMainWindow, QListWidget, QVBoxLayout, QWidget, QPushButton

class Model:
    def __init__(self, data):
        self.data = data

class View(QMainWindow):
    def __init__(self, model):
        super().__init__()
        self.model = model
        self.setWindowTitle('MVC Example')
        self.listWidget = QListWidget()
        for entry in self.model.data:
            self.listWidget.addItem(entry)
        self.addButton = QPushButton('Add Item')
        layout = QVBoxLayout()
        layout.addWidget(self.listWidget)
        layout.addWidget(self.addButton)
        container = QWidget()
        container.setLayout(layout)
        self.setCentralWidget(container)

class Controller:
    def __init__(self, model, view):
        self.model = model
        self.view = view
        self.view.addButton.clicked.connect(self.addItem)
    def addItem(self):
        itemText = f"Item {len(self.model.data) + 1}"
        self.model.data.append(itemText)
        self.view.listWidget.addItem(itemText)

if __name__ == '__main__':
    app = QApplication([])
    model = Model(['Item 1', 'Item 2', 'Item 3'])
    view = View(model)
    controller = Controller(model, view)
    view.show()
    app.exec()

Custom Widgets

Subclass QWidget or other widgets and override paint events.

from PyQt6.QtWidgets import QApplication, QPushButton, QWidget, QVBoxLayout
from PyQt6.QtGui import QPainter, QColor

class ColorButton(QPushButton):
    def __init__(self, color, parent=None):
        super().__init__(parent)
        self.color = color
    def paintEvent(self, event):
        painter = QPainter(self)
        painter.setBrush(QColor(self.color))
        painter.drawRect(self.rect())

if __name__ == '__main__':
    app = QApplication([])
    window = QWidget()
    layout = QVBoxLayout()
    layout.addWidget(ColorButton('red'))
    layout.addWidget(ColorButton('green'))
    window.setLayout(layout)
    window.show()
    app.exec()

Simple Calculator Example

A step‑by‑step guide to building a basic calculator with buttons, a read‑only display, and expression evaluation.

from PyQt6.QtWidgets import QApplication, QWidget, QPushButton, QVBoxLayout, QLineEdit
import sys

class SimpleCalculator(QWidget):
    def __init__(self):
        super().__init__()
        self.setWindowTitle('Simple Calculator')
        self.create_ui()
    def create_ui(self):
        self.layout = QVBoxLayout()
        self.display = QLineEdit()
        self.display.setReadOnly(True)
        self.layout.addWidget(self.display)
        self.create_buttons()
        self.setLayout(self.layout)
    def create_buttons(self):
        buttons = ['7','8','9','/','4','5','6','*','1','2','3','-','0','.','=', '+']
        for btn_text in buttons:
            btn = QPushButton(btn_text)
            btn.clicked.connect(self.on_click)
            self.layout.addWidget(btn)
    def on_click(self):
        button = self.sender()
        text = button.text()
        if text == '=':
            try:
                result = str(eval(self.display.text()))
                self.display.setText(result)
            except Exception:
                self.display.setText('Error')
        else:
            self.display.setText(self.display.text() + text)

if __name__ == '__main__':
    app = QApplication(sys.argv)
    calc = SimpleCalculator()
    calc.show()
    sys.exit(app.exec())

Design Principles and Best Practices

Modular design: organize code into modules and classes.

Leverage Qt’s signal‑and‑slot mechanism for communication.

Manage resources carefully, especially graphics and database connections.

Prioritize user experience: keep interfaces intuitive and responsive.

Real‑World Application Examples

Applications such as VLC Media Player, Calibre, and Anki illustrate Qt6’s versatility, handling multimedia, large data libraries, and spaced‑repetition algorithms.

Conclusion

Combining Python’s simplicity with Qt6’s comprehensive GUI toolkit enables developers to create cross‑platform desktop applications that are both visually appealing and functionally robust.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

MVCmultithreadingWidgetsPython GUIPyQt6Qt6Custom Widgets
Data STUDIO
Written by

Data STUDIO

Click to receive the "Python Study Handbook"; reply "benefit" in the chat to get it. Data STUDIO focuses on original data science articles, centered on Python, covering machine learning, data analysis, visualization, MySQL and other practical knowledge and project case studies.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.