Hybrid Qt Development: boosting your projects with Python


Dr. Cristiรกn Maureira-Fredes
@cmaureir

PSA

You can find the slides in qtinfo.dev/qtforpython_qtdevcon22

Python and Qt
are roughly the same age

Besides their maturity,
both stand out for one common goal:

simplicity

Is Python simpler than C++?


        def add(a: int, b: int) -> int:
          return a + b

        add(30, 12)
        # 42
        

        # n -> int
        if n&~-n<1:
            # ???
        

        _="_=%r;print(_%%_)";print(_%_)
        

so what's the deal? ๐Ÿ˜ฎโ€๐Ÿ’จ

Python popularity (1/3)

Python popularity (2/3)

The 2021 update
  • 3rd Most Popular ๐Ÿ“ˆ
  • 6th Most Loved ๐Ÿ“‰
  • 1st Most Wanted ๐ŸŽ‰
  • 1st Tiobe Index ๐ŸŽ‰

One can argue, those numbers are not really representative.

Python popularity (3/3)

The 2022 update

?

But what's the relation with C++?

Many of the most popular Python modules, rely on compiled languages, like Fortran, C, or C++.

What you might know

The official set of Python bindings for the Qt framework.

...but not only that ๐Ÿค”.

Things that /maybe/ you didn't know ๐Ÿ˜ฎ

Bridge to other Python modules (1/2)

Bridge to other Python modules (2/2)

"Pythonizing" Qt ๐Ÿ


            # Common Qt structure
            # - Using setter/getter
            # - No writable properties

            table = QTableWidget()
            table.setColumnCount(2)

            button = QPushButton("Add")
            button.setEnabled(False)

            layout = QVBoxLayout()
            layout.addWidget(table)
            layout.addWidget(button)
            layout.setSpacing(2)
            

            from __feature__ import (
              snake_case
            )

            table = QTableWidget()
            table.set_column_count(2)

            button = QPushButton("Add")
            button.set_enabled(False)

            layout = QVBoxLayout()
            layout.add_widget(table)
            layout.add_widget(button)
            layout.set_spacing(2)
            

            from __feature__ import (
              snake_case, true_property
            )

            table = QTableWidget()
            table.column_count = 2

            button = QPushButton("Add")
            button.enabled = False

            layout = QVBoxLayout()
            layout.add_widget(table)
            layout.add_widget(button)
            layout.spacing = 2
            

Opaque Containers ๐Ÿš€ (1/2)

  • Python Containers need to have a C++ counterpart
  • Using signatures with C++ containers, requires copying ๐Ÿ˜จ

Opaque Containers ๐Ÿš€ (2/2)

  • QPointListvs list(QList) vs deque(QList)
  • 1 million elements
  • Adding and Poping

Extreme case, but not impossible ๐Ÿ˜…

Project distribution and packaging ๐Ÿ“ฆ

Compatible with many freezers and compilers

Name License Qt 6 Qt 5 Linux macOS Windows
fbs GPL partial โœ… โœ… โœ… โœ…
PyInstaller GPL partial โœ… โœ… โœ… โœ…
cx_Freeze MIT โœ… โœ… โœ… โœ… โœ…
py2exe MIT partial partial โŒ โŒ โœ…
py2app MIT โœ… โœ… โŒ โœ… โŒ
briefcase BSD3 โœ… โœ… โœ… โœ… โœ…
Nuitka MIT โœ… โœ… โœ… โœ… โœ…

Does everyone likes vanilla? ๐Ÿจ

Not only vanilla Python ๐Ÿจ

  • Python implementation (in Python)
  • Has a JIT
  • On average 4.2 faster than CPython

The mandelbrot example

bugreports.qt.io/browse/PYSIDE-535


          # Python
          1/8, max ite: 96, time: 4.111s
          2/8, max ite: 288, time: 7.179s
          3/8, max ite: 1056, time: 22.156s
          4/8, max ite: 4128, time: 82.706s
          5/8, max ite: 16416, time: 371.348s
          

...don't forget about Shiboken

Binding generation process

Binding generation process

Binding generation process

Binding generation process

Binding generation process

Exposing C++ code to Python

Custom Python Bindings (1/2)

  • Library files:
    • CMakeLists.txt
    • test.cpp
    • test.h
  • Shiboken files:
    • bindings.h (right top)
    • bindings.xml (right bottom)

            #include "test.hpp"
<typesystem package="simple"> <function signature="hello()"/> </typesystem>

Custom Python Bindings (2/2)

Custom Python Bindings (with Qt)

Simple typesystem

<typesystem package="wiggly"> <load-typesystem name="typesystem_widgets.xml" generate="no"/> <object-type name="WigglyWidget"/> </typesystem>

Python usage

from wiggly import WigglyWidget as WigglyWidgetCPP
from wigglywidget import WigglyWidget as WigglyWidgetPY

# ...
widget_py = WigglyWidgetPY(self)
widget_cpp = WigglyWidgetCPP(self)
# ...
layout.addWidget(widget_py)
layout.addWidget(widget_cpp)
          

  • Top: Widget written in Python
  • Bottom: Widget written in C++ (exposed to Python)

Extending C++ applications with Python ๐Ÿ’ซ

Shiboken Wizard ๐Ÿง™

Overview

And many other features โš™๏ธ

  • Reduce generated wrapper code
  • Automatic container conversions and primitive types
  • Free functions (variadic functions and flexible arg checking)

Can we scrypt more?

QtScrypt (1/2)

  • Inspired by QtScript, not a port
  • A dynamic way to interact with Python from C++
  • Enabling Python modules within C++ ๐Ÿ
  • proof of concept

QtScrypt (2/2)


// [0, 1, 8, 27, 64, 125, 216, 343, 512, 729]
e.execute("print([i**3 for i in range(10)])");

// QVariant(QString, "Hello This Is A Test")
qDebug() << e.evaluate("f'hello this is a test'.title()");

//QVariant(double, 4950)
qDebug() << e.evaluate("sum(i for i in range(100))")

QScryptModule mod("super"); // super.py
QScryptFunction f1("add_three_numbers", &mod);

QVariantList args1;
args1 << 5 << 6 << 9;
// QVariant(double, 20)
qDebug() << f1.call(args1);
          

Future

  • Improve compatibility in the Python ecosystem
  • Improve cross compilation โš™๏ธ
  • PoC on mobile ๐Ÿ“ฑ
  • Continue with the WebAssembly efforts ๐Ÿš€
  • ...and probably your own requests!

Resources

Communication channels

More platforms at wiki.qt.io/Qt_for_Python#Community

Hybrid Qt Development: boosting your projects with Python


Dr. Cristiรกn Maureira-Fredes @cmaureir