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
qtpathsavailable 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 QtBridgesBuilding From source
Build and Install the project
pip install . -Ccmake.define.QT_PATHS=/path/to/qtpathsBuilding wheels:
pip wheel . -Ccmake.define.QT_PATHS=/path/to/qtpathsOptional Build Flags Non-limited-api:
pip wheel . -Ccmake.define.QT_PATHS=/path/to/qtpaths \
-Cwheel.py-api=py3Debug build:
pip wheel . -Ccmake.define.QT_PATHS=/path/to/qtpaths \
-Ccmake.define.CMAKE_INSTALL_DO_STRIP=0 \
-Ccmake.define.CMAKE_BUILD_TYPE=DebugInstall 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/htmlRunning 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::TestMethodReturnTo run a specific test with debug logging enabled (shows QtBridges debug output):
QT_LOGGING_RULES="qtbridges.debug=true" pytest tests/test_method_return.py::TestMethodReturnRunning
For running any project, go inside your directory and run the main file:
python main.pyTutorials
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:
- On startup → show a random joke question
- Press See Answer → show its answer with a random neon color
- 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._questionsExpose 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.0Full 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) // answerRunning the example
python examples/quiz_example/main.pyThe 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.