On this page

Qt Bridges - Python

QtBridges is a framework that simplifies the integration of Python with QtQuick/QML. It connects Python backends to QML frontends, improving development efficiency. The framework offers an easy, declarative method for linking Python classes, properties, signals, and slots to QML, with minimal Qt knowledge required from the Python developer.

Getting started

Why Qt Bridges?

As a Python developer, you can expose your Python classes to QML without learning the full PySide6 model/view system. QtBridges automatically handles property binding, signal connections, and model registration.

  • Less boilerplate - No manual @Slot, @Property, or signal management required
  • Automatic discovery - Public methods become callable from QML automatically
  • Model integration - Python classes become QML-ready models with simple decorators
  • Cross-language - Consistent API design across Python, Rust, C++, and more

Setting up development environment

Requirements:

  • Python: Minimum version 3.9

Build requirements:

  • CMake: Minimum version 3.18
  • Qt: Minimum version 6.x (with qtpaths available in your PATH)
  • Build system: scikit-build-core is used for building and packaging the project
  • Documentation QDoc (Qt's built-in tool for generating API reference documentation)
  • Testing: pytest framework with pytest-qt plugin

Dependencies: QtBridges automatically installs the required runtime dependencies for Python:

  • PySide6
  • Shiboken6

You don’t need to manually install these; they are pulled in when you install QtBridges.

Installation

pip install QtBridges

Building From source

Build and Install the project

pip install . -Ccmake.define.QT_PATHS=/path/to/qtpaths

Building wheels:

pip wheel . -Ccmake.define.QT_PATHS=/path/to/qtpaths

Optional Build Flags Non-limited-api:

pip wheel . -Ccmake.define.QT_PATHS=/path/to/qtpaths \
            -Cwheel.py-api=py3

Debug build:

pip wheel . -Ccmake.define.QT_PATHS=/path/to/qtpaths \
            -Ccmake.define.CMAKE_INSTALL_DO_STRIP=0 \
            -Ccmake.define.CMAKE_BUILD_TYPE=Debug

Install Development Dependencies Required for building documentation and running tests:

pip install -e ".[dev]"

Build Documentation with QDoc After installing development dependencies, run:

qdoc -project docs/QtBridges.qdoc
qdoc -outputdir docs/html

Running Tests

To run all tests:

pytest .

To run a specific test class or method (for example, only the TestMethodReturn class):

pytest tests/test_method_return.py::TestMethodReturn

To run a specific test with debug logging enabled (shows QtBridges debug output):

QT_LOGGING_RULES="qtbridges.debug=true" pytest tests/test_method_return.py::TestMethodReturn

Running

For running any project, go inside your directory and run the main file:

python main.py

Tutorials

Developer Jokes Example

This tutorial shows how to build a tiny QML app that displays random programming jokes. The backend is written in Python, and the UI is written in QML and QtBridge used to expose Python objects to QML.

What to Build

A small QML app that:

  • Picks a random joke question
  • Shows the answer when the button is pressed
  • Loads a new joke when the button is pressed again
  • Uses neon colors for the answers
  • Gets all joke data from Python

Here is a preview of the UI logic:

  1. On startup → show a random joke question
  2. Press See Answer → show its answer with a random neon color
  3. Press Another One → show a new question

1. Writing the Python Backend

Create a file called main.py.

First create the ClassModel for storing the list of list for questions and answers as well as the simple accessor getItem(row, column).

from QtBridge import bridge_instance, qtbridge

class QuestionModel:
    def __init__(self):
        # Add number of jokes
        self._questions = [
            ["Why do programmers prefer dark mode?",
             "Because light attracts bugs."],
            ["Why do Java developers wear glasses?",
             "Because they can't C#."],
            ["Why did the programmer quit his job?",
             "He didn't get arrays."],
            ...
        ]
    # Method for accessing the data at a specific index
    def getItem(self, row: int, column: int) -> str:
        """Get either the question (0) or answer (1) for a row."""
        return self._questions[row][column]

    @property
    def items(self) -> dict[str]:
        """Optional: expose full dataset."""
        return self._questions

    def data(self) -> dict[str]:
        """Required by QtBridge."""
        return self._questions

Expose the model as QA_model using bridge_instance() and register the QuizModel module.

@qtbridge(module="QuizModel")
def main():
    qa_model = QuestionModel()
    # Expose Python data to QML under the name: QA_model
    bridge_instance(qa_model, name="QA_model")

if __name__ == "__main__":
    main()

2. Writing the QML Frontend

Create a file such as QuizModel.qml and make sure your project imports the Python module. This os the default uri for QtBridge.

import backend 1.0

Full QML Code

import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import backend 1.0

ApplicationWindow {
    visible: true
    width: 440
    height: 200
    title: "Dev Jokes"

    // Neon accent colors for answers
    property var accentColors: ["#FF6F61", "#64FFDA", "#82B1FF", "#FFAB40", "#EA80FC"]

    // Random index
    property int currentIndex: Math.floor(Math.random() * QA_model.rowCount())

    // Boolean for the answer
    property bool showingAnswer: false

    // Simple background layer
    Rectangle {
        anchors.fill: parent
        color: "#1E1E1E"
    }
    // Layout for a text area and button
    ColumnLayout {
        anchors.centerIn: parent
        spacing: 20

        // An area for the questions and answers
        Rectangle {
            id: card
            color: "#1E1E1E"
            Layout.fillWidth: true

            Text {
                id: displayText
                text: QA_model.getItem(currentIndex, 0)  // show question
                anchors.centerIn: parent
                color: "#E0E0E0"
            }
        }

        // Button to show the answer and the next question
        Button {
            id: actionButton
            text: showingAnswer ? "Another One" : "See Answer"

            contentItem: Text {
                text: actionButton.text
                color: "#E0E0E0"
            }

            onClicked: {
                if (!showingAnswer) {
                    // Show answer in random neon color
                    let idx = Math.floor(Math.random() * accentColors.length)
                    displayText.color = accentColors[idx]
                    displayText.text = QA_model.getItem(currentIndex, 1)
                    showingAnswer = true
                } else {
                    // Load new random question
                    displayText.color = "#E0E0E0"
                    currentIndex = Math.floor(Math.random() * QA_model.rowCount())
                    displayText.text = QA_model.getItem(currentIndex, 0)
                    showingAnswer = false
                }
            }
        }
    }
}

3. How the App works

Loading Python data into QML: bridge_instance() exposes Python class instance to QML.

bridge_instance(qa_model, name="QA_model")

This gives a global QML object called QA_model.

Getting the number of jokes: QtBridge automatically exposes a rowCount() function.

QA_model.rowCount()

Getting a question or answer: The custom Python method can be used for that.

QA_model.getItem(row, column)

Column index 0 is for question and column index 1 is for answer.

Choosing a random joke: This will be initially shown on the window.

property int currentIndex: Math.floor(Math.random() * QA_model.rowCount())

Switching between question and answer: QML updates the displayed text dynamically.

displayText.text = QA_model.getItem(currentIndex, 0)   // question
displayText.text = QA_model.getItem(currentIndex, 1)   // answer

Running the example

python examples/quiz_example/main.py

The Example minimal_app

The minimal_app is a simple application that demonstrates the basics of QtBridges for Python. It shows how to expose a simple Python list model to QML and perform basic operations such as insert, delete, move, and edit items.

The application is located in examples/minimal_app folder and can be run after all the prerequisites are installed.

Running the example

python examples/minimal_app/main.py

© 2025 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.