Dr. Cristián Maureira-Fredes
maureira.dev
@cmaureir
Source TIOBE index August 2025
Most Popular technologies (Professionals) - Source StackOverflow Survey 2025
Most Popular technologies (Learning) - Source StackOverflow Survey 2025
Most Popular technologies (All) - Source StackOverflow Survey 2025
| Name | Language | Latest version | Last commit | License | Company |
|---|---|---|---|---|---|
| PySide | Python | 6.9.2 | - | LGPLv3/Commercial | The Qt Company |
| PyQt | Python | 6.9.1 | - | GPLv3/Commercial | Riverbank Computing |
| CXX-Qt | Rust | 6.x* | - | MIT/Apache 2.0 | KDAB |
| qmetaobject-rs | Rust | 6.5.0 | 2025.01.18 | MIT | Woboq |
| QtJambi | Java/Kotlin | 6.9.2 | 2025.08.27 | GPLv3/LGPLv2 | Omix Visualization |
| QML.jl | Julia | 6.5.2 | 2023.08.20 | MIT | JuliaGraphs |
| NodeGui | Node.js | 6.6.0 | 2025.06.30 | MIT | NodeGui |
| nimqt | Nim | 6.4.2 | 2024.11.5 | GPL2 | - |
| QML-zig | Zig | 6.4.2 | 2025.05.09 | Apache 2.0 | - |
(updated on Sep 2nd, 2025)
Other 24 projects (some inactive) in wiki.qt.io/Language_Bindings
Snapshot from https://youtu.be/kiw4fQH9vb8
Volker Hilsheimer
(Akademy 2022)
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QPushButton>
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
private slots:
void handleButton();
private:
QPushButton *m_button;
};
#endif // MAINWINDOW_H
#include "mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
m_button = new QPushButton("My Button", this);
connect(m_button, SIGNAL(clicked()), this, SLOT(handleButton()));
}
void MainWindow::handleButton()
{
m_button->setText("Ready");
}
#include
#include "mainwindow.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
MainWindow mainWindow;
mainWindow.show();
return app.exec(d);
}
import sys
from PySide6.QtCore import Slot
from PySide6.QtWidgets import QApplication, QMainWindow, QPushButton
class MainWindow(QMainWindow):
def __init__(self, parent=None):
QMainWindow.__init__(self, parent)
self.button = QPushButton("My Button", self)
self.button.clicked.connect(self.handle_button)
@Slot()
def handle_button(self):
self.button.setText("Ready")
if __name__ == "__main__":
app = QApplication(sys.argv)
mainWindow = MainWindow()
mainWindow.show()
sys.exit(app.exec())

#include <iostream>
using namespace std;
int main() {
int nums[] = {1,2,3,4,5};
int five = nums[1] + nums[2];
cout << "Result: " << five";
}
fn main() {
let arr: [i32; 5] = [1, 2, 3, 4, 5];
let five = arr[1] + arr[2];
println!("Result: {}", five);
}
class Person {
public:
Person(string n, int a);
void talk();
string name;
int age;
};
Person::Person(string n, int a){
name = n;
age = a;
}
void Person::talk(){
cout << name << " says Hello!\n";
}
int main() {
Person maria("Maria", 22);
maria.talk();
}
// declare a struct
struct Person {
name: String,
age: i32
}
impl Person {
fn speak(&self){
println!("{} says Hello!",
self.name);
}
}
fn main() {
let maria = Person{
name: String::from("Maria"),
age: 5
};
maria.speak();
}
#[cxx_qt::bridge]
pub mod qobject {
unsafe extern "C++" {
include!("cxx-qt-lib/qstring.h");
type QString = cxx_qt_lib::QString;
}
unsafe extern "RustQt" {
#[qobject]
#[qml_element]
#[qproperty(i32, number)]
#[qproperty(QString, string)]
type MyObject = super::MyObjectRust;
}
unsafe extern "RustQt" {
#[qinvokable]
fn increment_number(self: Pin<&mut MyObject>);
#[qinvokable]
fn say_hi(self: &MyObject,
string: &QString,
number: i32);
}
}
Snippet from: github.com/KDAB/cxx-qt/tree/main/examples/qml_minimal
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QPushButton>
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
private slots:
void handleButton();
private:
QPushButton *m_button;
};
#endif // MAINWINDOW_H
#include "mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
m_button = new QPushButton("My Button", this);
connect(m_button, SIGNAL(clicked()), this,
SLOT(handleButton()));
}
void MainWindow::handleButton()
{
m_button->setText("Ready");
}
#include <QApplication>
#include "mainwindow.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
MainWindow mainWindow;
mainWindow.show();
return app.exec(d);
}
import sys
from PySide6.QtCore import Slot
from PySide6.QtWidgets import (
QApplication, QMainWindow, QPushButton
)
class MainWindow(QMainWindow):
def __init__(self, parent=None):
QMainWindow.__init__(self, parent)
self.b = QPushButton("My Button", self)
self.b.clicked.connect(self.handle_button)
@Slot()
def handle_button(self):
self.b.setText("Ready")
if __name__ == "__main__":
app = QApplication(sys.argv)
mainWindow = MainWindow()
mainWindow.show()
sys.exit(app.exec())
import sys
from PySide6.QtCore import Slot
from PySide6.QtWidgets import (
QApplication, QMainWindow, QPushButton
)
class MainWindow(QMainWindow):
def __init__(self, parent=None):
QMainWindow.__init__(self, parent)
self.b = QPushButton("My Button", self)
self.b.clicked.connect(self.handle_button)
@Slot()
def handle_button(self):
self.b.setText("Ready")
if __name__ == "__main__":
app = QApplication(sys.argv)
mainWindow = MainWindow()
mainWindow.show()
sys.exit(app.exec())
QMetaObjectBuilder -> Runtime generation of QML-typesQQmlPrivate:qmlregister for instantiable types (Still in progress)Decorators, Macros, Observable types, etc
Wheels (Python), Crates (Rust), NuGet (C#), ...
Different languages, different IDEs.
The target users are non-Qt developers.
Not everything in Qt will be possible in Qt bridges.
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import QtQml.Models
import minimalapp
ApplicationWindow {
id: root
Timer {
id: clearStatusTimer
interval: 1500
onTriggered: statusDisplay.text = ""
}
Connections {
target: Backend
function onDuplicateFound(duplicate) {
statusDisplay.text = `List already contains entry ${duplicate}!`
clearStatusTimer.restart();
}
}
width: 640
height: 480
visible: true
title: qsTr("Minimal QML app")
ColumnLayout {
id: mainLayout
anchors.fill: parent
ListView {
id: lv
model: Backend.list
delegate: Control {
id: ld
required property var model
required property int index
width: lv.width
implicitHeight: Math.max(textLabel.implicitHeight, deleteButton.implicitHeight)
RowLayout {
anchors.fill: parent
Label {
property bool inEditMode: false
id: textLabel
text: inEditMode ? "" : `Item ${ld.index+1}: ${ld.model.edit}`
Layout.fillWidth: true
TextInput {
id: editField
visible: textLabel.inEditMode
anchors.fill: parent
onEditingFinished: {
textLabel.inEditMode = false
}
onAccepted: {
ld.model.edit = text
}
}
}
RoundButton {
text: "✏️"
down: textLabel.inEditMode || pressed
onReleased: () => {
if (textLabel.inEditMode) {
textLabel.inEditMode = false
} else {
editField.text = `${ld.model.edit}`
textLabel.inEditMode = true
editField.forceActiveFocus()
editField.selectAll()
}
}
}
RoundButton {
id: deleteButton
text: "🗑️" // could use icons if we could ensure we have them
onReleased: () => { lv.model.removeRow(ld.index) }
}
}
}
Layout.fillHeight: true
Layout.preferredWidth: mainLayout.width
}
RowLayout {
implicitHeight: Math.max(input.implicitHeight, submitButton.implicitHeight)
TextField {
id: input
placeholderText: "Enter string to add"
function submit() {
Backend.addString(input.text)
input.clear()
}
onAccepted: submit()
Layout.preferredWidth: mainLayout.width * 0.8
}
Button {
id: submitButton
text: "Add text"
enabled: input.text !== ""
onReleased: input.submit()
}
}
}
footer: Text {
id: statusDisplay
}
}
using System;
class Program
{
static int AddNumbers(int num1, int num2)
{
return num1 + num2;
}
static void Main(string[] args)
{
int result = AddNumbers(40, 2);
Console.WriteLine($"The sum is {result}");
}
}
// Backend.cs
using Qt.Quick;
namespace Qt.DotNet.QmlNext.MinimalApp
{
[QmlElement(Singleton = true)]
public class Backend
{
public UniqueStringListModel List { get; } = new();
public Backend()
{
List.DuplicateFound += OnDuplicateFound;
}
public bool AddString(string newValue)
{
return List.AddString(newValue);
}
public delegate void DuplicateFoundHandler(object sender,
DuplicateFoundArgs args);
public event DuplicateFoundHandler DuplicateFound;
private void OnDuplicateFound(object sender,
DuplicateFoundArgs args)
{
DuplicateFound?.Invoke(this, args);
}
}
public class DuplicateFoundArgs : EventArgs
{
public string Value { get; set; }
public override string ToString()
{
return Value;
}
}
}
// Main.cs
using Qt.Quick;
namespace Qt.DotNet.QmlNext.MinimalApp
{
public class Program
{
public static void Main(string[] args)
{
Qml.LoadFromModule("Main");
Qml.WaitForExit();
}
}
}
import Foundation
func addNumbers(_ num1: Int, _ num2: Int) -> Int {
return num1 + num2
}
func main() {
let result = addNumbers(40, 2)
print("The sum is: \(result)")
}
main()
// main.swift (1/2)
@QtBridgeable
public class ListModel {
@Published var list: [String] = ["test string"]
@Published var duplicateStringFound : String = ""
public func editString(index: Int, newString: String) {
if list.contains(newString) {
postDuplicateNotification(with: newString)
} else {
list[index] = newString
}
}
public func addString(newString: String) {
if list.contains(newString) {
postDuplicateNotification(with: newString)
} else {
list.append(newString)
}
}
public func removeString(index: Int) {
list.remove(at: index)
}
private func postDuplicateNotification(with duplicateString: String) {
duplicateStringFound = duplicateString
}
}
// main.swift (2/2)
var app = QApp()
var model = ListModel()
app.addInitialProperty(name: "listModel", model: model)
let qmlPath = Bundle.module.url(forResource: "main",
withExtension: "qml")!.path
app.setRootQml(path: qmlPath)
app.run(argc: CommandLine.argc,
argv: CommandLine.unsafeArgv)
public class Program {
public static int addNumbers(int num1, int num2) {
return num1 + num2;
}
public static void main(String[] args) {
int result = addNumbers(40, 2);
System.out.println("The sum is " + result);
}
}
// Backend.java
package org.qt.bridge;
import org.qt.bridge.core.QtObject;
import org.qt.bridge.core.QtState;
import org.qt.bridge.core.QtStateModelList;
@QMLRegistrable(name = "Backend")
public class ViewModel extends QtObject {
@BindConnection
QCallback qtCallback;
private final Repository db = new Repository();
private final QtState<Integer> size = new QtState<>(this,0);
private final QtStateModelList<String> list = new \
QtStateModelList<>(db.fetchAll());
{
list.observe(item -> size.update(item.size()));
}
public void addString(String item) {
if (item.isBlank()) {
if (qtCallback != null) {
qtCallback.blankFound();
}
return;
}
if (list.contains(item)) {
if (qtCallback != null) {
qtCallback.duplicateFound(item);
}
return;
}
list.addState(item);
}
public void update(int index, String updatedValue) {
if (list.contains(updatedValue)) {
if (qtCallback != null) {
qtCallback.duplicateFound(updatedValue);
}
return;
}
list.updateStateAt(index, updatedValue);
}
public void remove(int index) {
list.removeStateAt(index);
if (qtCallback != null) {
qtCallback.erased();
}
}
}
// QCallback
package org.qt.bridge;
public interface QCallback {
void duplicateFound(String item);
void erased();
void blankFound();
}
// Main.java
package org.qt.bridge;
import org.qt.bridge.app.QtQuickApplication;
public class Main {
public static void main(String[] args) {
final QtQuickApplication app = new QtQuickApplication(args);
app.registerQmlSingleton(new ViewModel());
app.execute();
}
}
def add_numbers(a: int, b: int) -> int:
return a + b
def main():
result = add_numbers(40, 2)
print(f"The sum is {result}")
if __name__ == "__main__":
main()
// main.py (1/2)
import sys
from PySide6.QtGui import QGuiApplication
from PySide6.QtQml import QQmlApplicationEngine
from QtBridges import AutoQmlBridge, updateQML
class StringModel:
def __init__(self):
self._items = ["Apple", "Banana"]
@updateQML
def add_string(self, value: str):
if value in self._items:
print(f"Duplicate found: {value}")
return False
self._items.append(value)
return True
def delete_string(self, value: str):
if value in self._items:
self._items.remove(value)
print(f"Deleted item: {value}")
print(self._items)
return True
return False
def set_item(self, index: int, value: str):
if 0 <= index < len(self._items):
self._items[index] = value
print(f"Item at index {index} set to {value}")
print(self._items)
return True
return False
@property
def items(self) -> list[str]:
return self._items
def data(self) -> list[str]:
return self._items
// main.py (2/2)
if __name__ == "__main__":
app = QGuiApplication(sys.argv)
engine = QQmlApplicationEngine()
# Register with QML
string_model = StringModel()
AutoQmlBridge(string_model, data_type="List")
engine.addImportPath(sys.path[0])
engine.loadFromModule(".", "Main")
if not engine.rootObjects():
sys.exit(-1)
sys.exit(app.exec())
fn add_numbers(num1: i32, num2: i32) -> i32 {
num1 + num2
}
fn main() {
let result = add_numbers(40, 2);
println!("The sum is: {}", result);
}
// backend.rs
use qt_gen::qobject_impl;
use qt_type_lib::{QVariant, QStringList,
QModelIndex, QSignalBlocker};
#[derive(Default)]
pub struct Backend {
}
#[qobject_impl(Base = QStringListModel)]
impl Backend {
#[overridden]
fn set_data(&mut self, index: &QModelIndex,
value: &QVariant, role: i32) -> bool {
if let Ok(value_str) = String::try_from(value) {
if self.string_list().contains(&value_str) {
self.duplicate_found(&value_str);
return false;
}
}
self.base_set_data(index, value, role)
}
#[qslot]
fn add_string(&mut self, value: &str) {
if self.string_list().contains(&value) {
self.duplicate_found(&value);
}
else {
self.append_prechecked_string(value);
}
}
fn append_prechecked_string(&mut self, new_value: &str) {
let current_count = self.row_count(&QModelIndex::default());
self.begin_insert_rows(&QModelIndex::default(),
current_count, current_count);
{
let _signal_blocker = QSignalBlocker::new(
<Self as bridge::QObjectProxy>::get_qobject(self));
let mut new_list = self.string_list();
new_list.append(new_value);
// decrements refcount of list
self.set_string_list(&QStringList::default());
self.set_string_list(&new_list);
}
self.end_insert_rows();
}
#[qsignal]
fn duplicate_found(&self, duplicate: &str);
}
// main.rs
use bridge::QObjectHolder;
use qt_type_lib::{QGuiApplication, QQmlApplicationEngine, QVariantMap};
mod backend;
use backend::Backend;
fn main() {
let mut backend = QObjectHolder::new(Backend::default());
let _app = QGuiApplication::new();
let mut qml_engine = QQmlApplicationEngine::new();
let mut properties = QVariantMap::default();
properties.insert("backend", &backend.as_qobject_mut().into());
qml_engine.pin_mut().set_initial_properties(&properties);
let main_qml = include_bytes!("Main.qml");
qml_engine.pin_mut().load_data(main_qml);
QGuiApplication::exec();
}
The previous snippets are only for one specific example. More examples are being worked on, and the API will probably change.
(based on common questions)
One of the goals is to enable people to add more bridges
It can be that some features will not be considered. Remember the scope!
If Qt Bridges is not enough, developers will be pointed to any current binding or C++.
The development of binding is still on-going!
QtQuick is a more modern and attractive way of creating UI.
Not at the moment, it depends on the adoption and scope.
Not for TP, but backend interchangeable
Yes No
Of course, the main goal is to expand our community.
Our initial plan is to release by the end of the year (TP)
Dr. Cristián Maureira-Fredes
@cmaureir
Akademy, Berlin 2025 | The Qt Company | Cristián @cmaureir