slint/api/python/slint
Simon Hausmann 2adb86de14 Python: Be conservative about attaching to the Python interpreter
We call Python::attach in a few places. When invoked from the Slint event loop, those should be fine. But there are some that may fail, for example during gc. Fall back to using try_attach and return default values instead of causing a panic.

Fixes #9984
2025-11-07 10:28:47 +00:00
..
slint CI: Fix formatting 2025-10-14 08:52:48 +02:00
stub-gen Python: Make space for additional python modules 2025-06-25 20:43:09 +02:00
tests Python: Add support for async callbacks with @slint.callback 2025-10-14 07:25:33 +02:00
.gitignore Python: Make space for additional python modules 2025-06-25 20:43:09 +02:00
async_adapter.rs Python: Initial support for asyncio 2025-09-19 08:23:34 +02:00
brush.rs Python: Make space for additional python modules 2025-06-25 20:43:09 +02:00
build_docs.py Python: Make space for additional python modules 2025-06-25 20:43:09 +02:00
Cargo.toml core: re-export the tr crate when the tr feature is activated 2025-09-29 12:50:22 +02:00
errors.rs Python: Make space for additional python modules 2025-06-25 20:43:09 +02:00
image.rs Python: Add support for importing foreign image buffers 2025-08-01 16:08:09 +02:00
interpreter.rs Python: Add support for async callbacks with @slint.callback 2025-10-14 07:25:33 +02:00
lib.rs Python: Be conservative about attaching to the Python interpreter 2025-11-07 10:28:47 +00:00
models.rs Python: Be conservative about attaching to the Python interpreter 2025-11-07 10:28:47 +00:00
noxfile.py Python: Initial support for asyncio 2025-09-19 08:23:34 +02:00
pyproject.toml Bump version number to 1.15.0 2025-10-24 14:28:17 +00:00
README.md Python: Improve exception handling for the coroutine passed to slint.run_event_loop() 2025-09-19 10:10:19 +02:00
thirdparty.hbs Python: Make space for additional python modules 2025-06-25 20:43:09 +02:00
timer.rs Python: Extend Timer docs with an example how to avoid garbage collection and thus stopped timer 2025-09-24 16:56:45 +02:00
value.rs Python: Fix interaction with garbage collector when models are held in structs and properties 2025-10-12 11:05:50 +02:00

Slint-python (Beta)

Slint is a UI toolkit that supports different programming languages. Slint-python is the integration with Python.

Warning Slint-python is in a beta phase of development: The APIs while mostly stable, may be subject to further changes. Any changes will be documented in the ChangeLog.

You can track the progress for the Python integration by looking at python-labelled issues at https://github.com/slint-ui/slint/labels/a%3Alanguage-python .

Slint Language Manual

The Slint Language Documentation covers the Slint UI description language in detail.

Prerequisites

Installation

Install Slint with uv or pip from the Python Package Index:

uv add slint

The installation uses binaries provided for macOS, Windows, and Linux for various architectures. If your target platform is not covered by binaries, uv will automatically build Slint from source. If that happens, you will then need some software development tools on your machine, as well as Rust.

Quick Start

  1. Create a new project with uv init.
  2. Add the Slint Python package to your Python project: uv add slint
  3. Create a file called app-window.slint:
import { Button, VerticalBox } from "std-widgets.slint";

export component AppWindow inherits Window {
    in-out property<int> counter: 42;
    callback request-increase-value();
    VerticalBox {
        Text {
            text: "Counter: \{root.counter}";
        }
        Button {
            text: "Increase value";
            clicked => {
                root.request-increase-value();
            }
        }
    }
}
  1. Create a file called main.py:
import slint

# slint.loader will look in `sys.path` for `app-window.slint`.
class App(slint.loader.app_window.AppWindow):
    @slint.callback
    def request_increase_value(self):
        self.counter = self.counter + 1

app = App()
app.run()
  1. Run it with uv run main.py

API Overview

Instantiating a Component

The following example shows how to instantiate a Slint component in Python:

app.slint

export component MainWindow inherits Window {
    callback clicked <=> i-touch-area.clicked;

    in property <int> counter;

    width: 400px;
    height: 200px;

    i-touch-area := TouchArea {}
}

The exported component is exposed as a Python class. To access this class, you have two options:

  1. Call slint.load_file("app.slint"). The returned object is a namespace, that provides the MainWindow class as well as any other explicitly exported component that inherits Window:

    import slint
    components = slint.load_file("app.slint")
    main_window = components.MainWindow()
    
  2. Use Slint's auto-loader, which lazily loads .slint files from sys.path:

    import slint
    # Look for for `app.slint` in `sys.path`:
    main_window = slint.loader.app.MainWindow()
    

    Any attribute lookup in slint.loader is searched for in sys.path. If a directory with the name exists, it is returned as a loader object, and subsequent attribute lookups follow the same logic.

    If the name matches a file with the .slint extension, it is automatically loaded with load_file and the namespace is returned.

    If the file name contains a dash, like app-window.slint, an attribute lookup for app_window tries to locate app_window.slint and then fall back to app-window.slint.

Accessing Properties

Properties declared as out or in-out in .slint files are visible as properties on the component instance.

main_window.counter = 42
print(main_window.counter)

Accessing Globals

Global Singletons are accessible in Python as properties in the component instance.

For example, this Slint code declares a PrinterJobQueue singleton:

export global PrinterJobQueue {
    in-out property <int> job-count;
}

Access it as a property on the component instance by its name:

print("job count:", instance.PrinterJobQueue.job_count)

Note: Global singletons are instantiated once per component. When declaring multiple components for export to Python, each instance has their own associated globals singletons.

Setting and Invoking Callbacks

Callbacks declared in .slint files are visible as callable properties on the component instance. Invoke them as functions to invoke the callback, and assign Python callables to set the callback handler.

In Slint, callbacks are defined using the callback keyword and can be connected to another component's callback using the <=> syntax.

my-component.slint

export component MyComponent inherits Window {
    callback clicked <=> i-touch-area.clicked;

    width: 400px;
    height: 200px;

    i-touch-area := TouchArea {}
}

The callbacks in Slint are exposed as properties and that can be called as functions.

main.py

import slint

component = slint.loader.my_component.MyComponent()
# connect to a callback

def clicked():
    print("hello")

component.clicked = clicked
// invoke a callback
component.clicked();

Another way to set callbacks is to sub-class and use the @slint.callback decorator:

import slint

class Component(slint.loader.my_component.MyComponent):
    @slint.callback
    def clicked(self):
        print("hello")

component = Component()

The @slint.callback() decorator accepts a name argument, if the name of the method does not match the name of the callback in the .slint file. Similarly, a global_name argument can be used to bind a method to a callback in a global singleton.

Type Mappings

Each type used for properties in the Slint Language translates to a specific type in Python. The following table summarizes the mapping:

.slint Type Python Type Notes
int int
float float
string str
color slint.Color
brush slint.Brush
image slint.Image
length float
physical_length float
duration float The number of milliseconds
angle float The angle in degrees
structure dict/Struct When reading, structures are mapped to data classes, when writing dicts are also accepted.
array slint.Model

Arrays and Models

You can set array properties from Python by passing subclasses of slint.Model.

Use the slint.ListModel class to construct a model from an iterable:

component.model = slint.ListModel([1, 2, 3]);
component.model.append(4)
del component.model[0]

When sub-classing slint.Model, provide the following methods:

    def row_count(self):
        """Return the number of rows in your model"""

    def row_data(self, row):
        """Return data at specified row"""

    def set_row_data(self, row, data):
        """For read-write models, store data in the given row. When done call set.notify_row_changed:"
        ..."""
        self.notify_row_changed(row)

When adding or inserting rows, call notify_row_added(row, count) on the super class. Similarly, when removing rows, notify Slint by calling notify_row_removed(row, count).

Structs

Structs declared in Slint and exposed to Python via export are then accessible in the namespace that is returned when instantiating a component.

app.slint

export struct MyData {
    name: string,
    age: int
}

export component MainWindow inherits Window {
    in-out property <MyData> data;
}

main.py

The exported MyData struct can be constructed as follows:

import slint
# Look for for `app.slint` in `sys.path`:
main_window = slint.loader.app.MainWindow()

data = slint.loader.app.MyData(name = "Simon")
data.age = 10
main_window.data = data

Enums

Enums declared in Slint and exposed to Python via export are then accessible in the namespace that is returned when instantiating a component. The enums are subclasses of enum.Enum.

app.slint

export enum MyOption {
    Variant1,
    Variant2
}

export component MainWindow inherits Window {
    in-out property <MyOption> data;
}

main.py

Variants of the exported MyOption enum can be constructed as follows:

import slint
# Look for for `app.slint` in `sys.path`:
main_window = slint.loader.app.MainWindow()

value = slint.loader.app.MyOption.Variant2
main_window.data = value

Asynchronous I/O

Use Python's asyncio library to write concurrent Python code with the async/await syntax.

Slint's event loop is a full-featured asyncio event loop. While the event loop is running, asyncio.get_event_loop() returns a valid loop. To run an async function when starting the loop, pass a coroutine to slint.run_event_loop().

For the common use case of interacting with REST APIs, we recommend the aiohttp library.

Known Limitations

  • Pipes and sub-processes are only supported on Unix-like platforms.

Third-Party Licenses

For a list of the third-party licenses of all dependencies, see the separate Third-Party Licenses page.