The Role of new Languages in the Future of the Qt Ecosystem.


Dr. Cristián Maureira-Fredes
maureira.dev
@cmaureir

What is the best

programming language?

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

Languages in the

Qt Project

1995

2011*

2010

2025

The approach so far:

Bindings

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

What has been the motivation?

  • The need of GUI frameworks
  • C++ flexibility for bindings
  • Language showcase
  • Qt's popularity ⭐

What other languages

bring to the table

What Python and Rust

bring to the table

Python's syntax


        #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())

Python's heterogeneity

Web Development
Data
Science

Embedded systems

Python: package distribution

More Qt for Python

Rust and C++ (1/2)


    #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);
    }
    

Rust and with C++ (2/2)


    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();
    }
    

CXX-Qt Rust ⇄ C++ bindings


    #[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

This worked well for

Qt developers

...also for KDE developers

Source: develop.kde.org/docs/getting-started/python/

Source: develop.kde.org/docs/getting-started/rust/

Session at the end of the day

What about people that's

unaware of Qt?

Python's case (from before)


        #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())

Python's case


    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())

This might feel

not-natural and extra work

We force languages to

handle Qt/C++ details

How can we include other languages

without breaking the language paradigm?

The development team

  • Ahmed El Khazari
  • Juha Vuolle
  • Karsten Lamm-Heimrich
  • Lena Biliaieva
  • Magdalena Stojek
  • Matthias Rauter
  • Miguel Costa
  • Piotr Wiercinski
  • Shyamnath Premnadh
  • Yuri Knigavko
  • Vladimir Minenko (PM)
  • Cristián Maureira-Fredes (TL)
  • + More

The new approach

Split Backend from UI:

Reduce Qt footprint from backend

Language independence

Internal process

  • QMetaObjectBuilder -> Runtime generation of QML-types
    • Methods, Signals, Slots, Properties, etc.
  • QQmlPrivate:qmlregister for instantiable types (Still in progress)

Implementation "detail"

Decorators, Macros, Observable types, etc

Package distribution

Wheels (Python), Crates (Rust), NuGet (C#), ...

Development environment

Different languages, different IDEs.

⚠️Please note

The target users are non-Qt developers.

Not everything in Qt will be possible in Qt bridges.

A sneak peak

of one application

Base QtQuick application


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
    }
}

Why C# (1/2)


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}");
  }
}
    

Why C#? (2/2)

Qt Bridges (C#) - Minimal application


// 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;
    }
  }
}
    

Why Swift? (1/2)


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()
  
  • Safety and Performance
  • Modern syntax
  • Type Safety and type inference
  • Optionals
  • Memory management (Automatic Reference Counting, ARC)
  • Functional programming
  • Protocol-Oriented Programming
  • Interoperability with Objective-C
  • Generics, Pattern matching, etc

Why Swift? (2/2)

  • It's the default language for macOS development
  • Previous effort on binding it for Qt Quick apps
  • Efforts to bring it to Linux and Windows

Qt Bridges (Swift) - Minimal application


// 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)

  

Why Java? (1/2)


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);
  }
}
  

Why Java? (2/2)

  • Already in the ecosystem (Android)
  • Automotive experience
  • Code generator (Qt Jenny)
  • Active 3rd party bindings (Qt Jambi)

Qt Bridges (Java) - Minimal application


// 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();
    }
  }
}
  

Interoperability with Kotlin for free*

Why Python? (1/2)


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()
  

Why Python? (2/2)

  • Previous experience with Qt for Python (PySide)
  • Python remains the most popular language
  • Interoperability with many technologies

Qt Bridges (Python) - Minimal application


// 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
  

Why Rust? (1/2)


fn add_numbers(num1: i32, num2: i32) -> i32 {
    num1 + num2
}

fn main() {
  let result = add_numbers(40, 2);

  println!("The sum is: {}", result);
}
  

Why Rust? (2/2)

  • Previous experience (qtmetaobject-rs, CXX-Qt, etc)
  • Language popularity
  • Ecosystem
  • Tooling

Qt Bridges (Rust) - Minimal application


// 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);
}

  

Note

The previous snippets are only for one specific example. More examples are being worked on, and the API will probably change.

Brief FAQ

(based on common questions)

What about <lang>?😠

One of the goals is to enable people to add more bridges

What about <feature>?🤔

It can be that some features will not be considered. Remember the scope!

What if I need more?😢

If Qt Bridges is not enough, developers will be pointed to any current binding or C++.

So no more bindings?💀

The development of binding is still on-going!

Why it's only QtQuick?😒

QtQuick is a more modern and attractive way of creating UI.

Will you support everything in QML?🤨

Not at the moment, it depends on the adoption and scope.

Can I combine all the languages?🧪

Not for TP, but backend interchangeable

Do you want to brainwash C++ developers?😵‍💫

Yes No

Will this be Open Source?👐

Of course, the main goal is to expand our community.

When will this be released?🚀

Our initial plan is to release by the end of the year (TP)

To summarize...

What does it mean to have more languages with Qt Bridges?

Qt Bridges means

more communities

Qt Bridges means

new ideas

Qt Bridges means

new expertise

Qt Bridges means

a better Qt ecosystem

Let's bring Qt

everywhere

Q&A

The Role of new Languages in the Future of the Qt Ecosystem.


Dr. Cristián Maureira-Fredes
@cmaureir