Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
f9e58ff
python: add .slint code generator and typing metadata
GreyElaina Oct 23, 2025
3a63b47
Merge branch 'master' of https://github.com/slint-ui/slint
GreyElaina Oct 23, 2025
fc38400
Merge branch 'master' of https://github.com/slint-ui/slint
GreyElaina Oct 23, 2025
4e68196
[autofix.ci] apply automated fixes
autofix-ci[bot] Oct 23, 2025
b844bf4
python: fix missing examples.counter.generated
GreyElaina Oct 23, 2025
67747d8
Merge branch 'master' of https://github.com/GreyElaina/slint
GreyElaina Oct 23, 2025
e91cf74
[autofix.ci] apply automated fixes
autofix-ci[bot] Oct 23, 2025
fd249ad
refactor: replace normalize_identifier with _normalize_prop and remov…
GreyElaina Oct 23, 2025
646e92e
[autofix.ci] apply automated fixes
autofix-ci[bot] Oct 23, 2025
6b0dab9
fix: python codegen circual import
GreyElaina Oct 23, 2025
ed7b1ba
fix: ambignous `slint.slint` now `slint.core` via workable maturin co…
GreyElaina Oct 23, 2025
f8ece22
fix: cleanup slint.codegen.__init__
GreyElaina Oct 23, 2025
6adb9de
Refactor python binding module structure
GreyElaina Oct 24, 2025
11275c8
fix: nomalize identity with python keyword
GreyElaina Oct 25, 2025
78ff39d
fix: missing hint for slint.Color
GreyElaina Oct 25, 2025
a5ce4e6
fix(python/cli): expect at least one input
GreyElaina Oct 25, 2025
d24f5dd
fix(python): recorrect python identifier nomi in rust
GreyElaina Oct 25, 2025
449d76a
fix(python): missing inspect import
GreyElaina Oct 25, 2025
35b6b9d
refactor: restructure slint-compiler workflow
GreyElaina Oct 25, 2025
601faff
fix(python/briefcase): improve code formatting
GreyElaina Oct 26, 2025
f6a4972
fix: override SlintToPyValue returning to Any
GreyElaina Oct 27, 2025
cab5a14
fix: update pyo3_stub_gen usage and add gen_stub attributes
GreyElaina Oct 27, 2025
bb07236
feat: export enums & structs
GreyElaina Oct 27, 2025
8bf3aba
feat: add tests for enums, structs, and load file functionality
GreyElaina Oct 27, 2025
a10ff5f
fix(python): assignment breaks typeddict
GreyElaina Oct 27, 2025
145e74f
feat(python): codegen dependency tracking
GreyElaina Oct 27, 2025
a1d822f
fix(python): enable development mode for rust code build
GreyElaina Oct 27, 2025
9e87cca
feat(python): use elaina's pyo3-stub-gen (temporary) and enhance mode…
GreyElaina Oct 27, 2025
6ef211e
fix(python): unexpected builtin enum regenerate; ensure package struc…
GreyElaina Oct 27, 2025
c5affe8
refactor(python): regenerate type stubs
GreyElaina Oct 27, 2025
daa997e
refactor: restructure slint-compiler workflow
GreyElaina Oct 25, 2025
2928dc5
chore: update workflow to use specific Ubuntu versions and add projec…
GreyElaina Oct 28, 2025
7b4e1e6
ci: remove all related to api/python/compiler
GreyElaina Oct 28, 2025
014af7f
[autofix.ci] apply automated fixes
autofix-ci[bot] Oct 28, 2025
2a1329a
Merge branch 'cherry-35b6b9d' into master
GreyElaina Oct 28, 2025
44b28b2
[autofix.ci] apply automated fixes
autofix-ci[bot] Oct 28, 2025
a85b3c5
refactor(python): drop enums & structs export
GreyElaina Oct 29, 2025
687aa66
refactor: revert defination methods de-optional
GreyElaina Oct 29, 2025
2d0c9de
fix(python): adopt loop to standard selector loop behavior
GreyElaina Oct 29, 2025
fe2c273
refactor: remove unused /undecided methods from ComponentInstance class
GreyElaina Oct 29, 2025
35c1ed0
refactor(python): code generation and testing
GreyElaina Oct 29, 2025
40e4c47
refactor(python): rename HasFileno and fd_for_fileobj to protected pr…
GreyElaina Oct 29, 2025
3103702
refactor(python): fix tests global_properties assertions and test_mag…
GreyElaina Oct 29, 2025
9ae9f4d
fix(python): add type ignore for selector attribute in SlintEventLoop
GreyElaina Oct 29, 2025
839f14b
test(python): add tests for emitters package code generation
GreyElaina Oct 29, 2025
826a923
chore(python): add pytest-cov to development dependencies
GreyElaina Oct 29, 2025
a300d54
doc(python): update README.md
GreyElaina Oct 29, 2025
64ea77e
feat(build): implement stable library path copying in build script
GreyElaina Oct 29, 2025
114a4da
feat(python): add stubgen script
GreyElaina Oct 29, 2025
f580172
[autofix.ci] apply automated fixes
autofix-ci[bot] Oct 29, 2025
9864217
fix(python): remove unused imports from interpreter.rs
GreyElaina Oct 29, 2025
ae40bf6
fix(python): mis-replaced repr(Path)
GreyElaina Oct 29, 2025
142c58a
fix(python): make mypy happy
GreyElaina Oct 29, 2025
b4671f6
fix(python): ruff
GreyElaina Oct 29, 2025
e903396
fix(python): mypy, ruff
GreyElaina Oct 29, 2025
7a077a4
fix(python): alright then. run_until_complete() -> T | None
GreyElaina Oct 29, 2025
9bee1ce
Merge branch 'master' into master
GreyElaina Oct 30, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -213,10 +213,11 @@ jobs:
python-version: "3.12"
- name: Install uv
uses: astral-sh/setup-uv@v7
- uses: fjwillemsen/[email protected]
- name: Run python tests
working-directory: api/python/slint
run: nox --default-venv-backend uv
run: uv run pytest tests
env:
MATURIN_PEP517_ARGS: --profile=dev
- name: Run mypy
working-directory: api/python/slint
run: uv run mypy -p tests -p slint
Expand Down
136 changes: 69 additions & 67 deletions api/python/briefcase/src/briefcasex_slint/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
# SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0

from pathlib import Path
import inspect

from briefcase.bootstraps.base import BaseGuiBootstrap

Expand All @@ -10,91 +11,92 @@ class SlintGuiBootstrap(BaseGuiBootstrap):
display_name_annotation = "does not support Android/Web deployment"

def app_source(self):
return """\
import slint
return inspect.cleandoc("""
import slint

class {{ cookiecutter.class_name }}(slint.loader.{{ cookiecutter.module_name }}.resources.app_window.AppWindow):
@slint.callback
def request_increase_value(self):
self.counter = self.counter + 1
class {{ cookiecutter.class_name }}(slint.loader.{{ cookiecutter.module_name }}.resources.app_window.AppWindow):
@slint.callback
def request_increase_value(self):
self.counter = self.counter + 1


def main():
main_window = {{ cookiecutter.class_name }}()
main_window.show()
main_window.run()
"""
def main():
main_window = {{ cookiecutter.class_name }}()
main_window.show()
main_window.run()
""")

def app_start_source(self):
return """\
from {{ cookiecutter.module_name }}.app import main
return inspect.cleandoc("""
from {{ cookiecutter.module_name }}.app import main

if __name__ == "__main__":
main()
"""
if __name__ == "__main__":
main()
""")

def pyproject_table_briefcase_app_extra_content(self):
return """
requires = [
]
test_requires = [
{% if cookiecutter.test_framework == "pytest" %}
"pytest",
{% endif %}
]
"""
return inspect.cleandoc("""
requires = [
]
test_requires = [
{% if cookiecutter.test_framework == "pytest" %}
"pytest",
{% endif %}
]
""")

def pyproject_table_macOS(self):
return """\
universal_build = false
requires = [
"slint",
]
"""
return inspect.cleandoc("""
universal_build = false
requires = [
"slint",
]
""")

def pyproject_table_linux(self):
return """\
requires = [
"slint",
]
"""
return inspect.cleandoc("""
requires = [
"slint",
]
""")

def pyproject_table_windows(self):
return """\
requires = [
"slint",
]
"""
return inspect.cleandoc("""
requires = [
"slint",
]
""")

def pyproject_table_iOS(self):
return """\
requires = [
"slint",
]
"""
return inspect.cleandoc("""
requires = [
"slint",
]
""")

def post_generate(self, base_path: Path) -> None:
target_dir = base_path / self.context["source_dir"] / "resources"
target_dir.mkdir(parents=True, exist_ok=True)
with open(target_dir / "app-window.slint", "w") as slint_file:
slint_file.write(r"""
import { Button, VerticalBox, AboutSlint } from "std-widgets.slint";

export component AppWindow inherits Window {
in-out property<int> counter: 42;
callback request-increase-value();
VerticalBox {
alignment: center;
AboutSlint {}
Text {
text: "Counter: \{root.counter}";
}
Button {
text: "Increase value";
clicked => {
root.request-increase-value();
}
}
}
}
""")
content = inspect.cleandoc("""
import { Button, VerticalBox, AboutSlint } from "std-widgets.slint";

export component AppWindow inherits Window {
in-out property<int> counter: 42;
callback request-increase-value();
VerticalBox {
alignment: center;
AboutSlint {}
Text {
text: "Counter: {root.counter}";
}
Button {
text: "Increase value";
clicked => {
root.request-increase-value();
}
}
}
}
""")
slint_file.write(content)
9 changes: 5 additions & 4 deletions api/python/slint/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ publish = false
rust-version.workspace = true

[lib]
name = "core"
path = "lib.rs"
crate-type = ["cdylib", "rlib"]

Expand All @@ -39,20 +40,20 @@ renderer-skia-opengl = ["slint-interpreter/renderer-skia-opengl"]
renderer-skia-vulkan = ["slint-interpreter/renderer-skia-vulkan"]
renderer-software = ["slint-interpreter/renderer-software"]
accessibility = ["slint-interpreter/accessibility"]
stubgen = []


[dependencies]
i-slint-backend-selector = { workspace = true }
i-slint-core = { workspace = true, features = ["tr"] }
i-slint-common = { workspace = true }
slint-interpreter = { workspace = true, features = ["default", "display-diagnostics", "internal"] }
i-slint-compiler = { workspace = true }
pyo3 = { version = "0.26", features = ["extension-module", "indexmap", "chrono", "abi3-py311"] }
indexmap = { version = "2.1.0" }
chrono = "0.4"
spin_on = { workspace = true }
css-color-parser2 = { workspace = true }
pyo3-stub-gen = { version = "0.9.0", default-features = false }
pyo3-stub-gen = { git = "https://github.com/GreyElaina/pyo3-stub-gen", branch = "main", default-features = false, features = ["infer_signature"] }
smol = { version = "2.0.0" }

[package.metadata.maturin]
python-source = "slint"
smol_str = { workspace = true }
78 changes: 78 additions & 0 deletions api/python/slint/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,84 @@ app.run()

5. Run it with `uv run main.py`

## Code Generation CLI

Use the bundled code generator to derive Python bindings and type stubs from your `.slint` files. The generator lives in the `slint.codegen` module and exposes a CLI entry point.

Run it with `uv`:

```bash
uv run -m slint.codegen generate --input path/to/app.slint --output generated
```

When `--output` is omitted the generated files are written next to each input source.

You can also call the generator from Python:

```python
from pathlib import Path
from slint.codegen.generator import generate_project
from slint.codegen.models import GenerationConfig

slint_file = Path("ui/app.slint")
generate_project(
inputs=[slint_file],
output_dir=Path("generated"),
config=GenerationConfig(
include_paths=[slint_file.parent],
library_paths={},
style=None,
translation_domain=None,
quiet=True,
),
)
```

It's strongly recommended to run the code generator as part of your build process (e.g., `hatch` build hooks), so that the generated bindings are always in sync with your `.slint` files.

## Component Libraries and `library_paths`

Slint supports importing third-party component libraries such as the Material 3 component set under the `@material` namespace. Follow the instructions on [material.slint.dev/getting-started](https://material.slint.dev/getting-started/) to download the library bundle into your project (for example into `material-1.0/`).

When you load a `.slint` file from Python, pass a `library_paths` dictionary that maps the library name to the `.slint` entry file in that bundle:

```python
from pathlib import Path
import slint

root = Path(__file__).parent
ui = slint.load_file(
root / "ui" / "main.slint",
library_paths={
"material": root / "material-1.0" / "material.slint",
},
)
```

The same mapping can be supplied to the code generator via `GenerationConfig` so the generated bindings reference the library correctly:

```python
from pathlib import Path
from slint.codegen.generator import generate_project
from slint.codegen.models import GenerationConfig

slint_file = Path("ui/app.slint")

generate_project(
inputs=[slint_file],
output_dir=Path("generated"),
config=GenerationConfig(
include_paths=[slint_file.parent],
library_paths={"material": Path("material-1.0/material.slint")},
style=None,
translation_domain=None,
quiet=True,
),
)
```

Any library referenced with `import { ... } from "@library-name"` must appear in `library_paths`, and the value should point to the `.slint` file that serves as the library's root entry point.

## API Overview

### Instantiating a Component
Expand Down
8 changes: 5 additions & 3 deletions api/python/slint/brush.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0

use pyo3::prelude::*;
use pyo3_stub_gen::{derive::gen_stub_pyclass, derive::gen_stub_pymethods, impl_stub_type};
use pyo3_stub_gen::{
derive::gen_stub_pyclass, derive::gen_stub_pyclass_complex_enum, derive::gen_stub_pymethods,
impl_stub_type,
};

use crate::errors::PyColorParseError;

Expand Down Expand Up @@ -33,6 +36,7 @@ struct RgbColor {
}

#[derive(FromPyObject)]
#[gen_stub_pyclass_complex_enum]
#[pyclass]
enum PyColorInput {
ColorStr(String),
Expand All @@ -57,8 +61,6 @@ enum PyColorInput {
},
}

impl_stub_type!(PyColorInput = String | RgbaColor | RgbColor);

/// A Color object represents a color in the RGB color space with an alpha. Each color channel and the alpha is represented
/// as an 8-bit integer. The alpha channel is 0 for fully transparent and 255 for fully opaque.
///
Expand Down
Loading
Loading