Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ members = [
'examples/mcu-board-support',
'examples/mcu-embassy',
'examples/uefi-demo',
'examples/async-io',
'demos/weather-demo',
'demos/usecases/rust',
'helper_crates/const-field-offset',
Expand Down
21 changes: 21 additions & 0 deletions examples/async-io/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Copyright © SixtyFPS GmbH <[email protected]>
# SPDX-License-Identifier: MIT

[package]
name = "stockticker"
version = "1.14.0"
authors = ["Slint Developers <[email protected]>"]
edition = "2021"
publish = false
license = "MIT"

[[bin]]
path = "main.rs"
name = "stockticker"

[dependencies]
slint = { path = "../../api/rs/slint" }
async-compat = { version = "0.2.4" }
reqwest = { version = "0.12", features = ["json"] }
serde = "1.0"
serde_json = "1.0"
19 changes: 19 additions & 0 deletions examples/async-io/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<!-- Copyright © SixtyFPS GmbH <[email protected]> ; SPDX-License-Identifier: MIT -->

# Example demonstrating asynchronous API usage with Slint

This example demonstrates how to use asynchronous I/O, by means of issuing HTTP GET requests, within the Slint event loop.

The http GET requests fetch the closing prices of a few publicly traded stocks, and the result is show in the simple Slint UI.

# Rust

The Rust version is contained in [`main.rs`](./main.rs). It uses the `rewquest` crate to establish a network connection and issue the HTTP get requests, using Rusts `async` functions. These are run inside a future run with `slint::spawn_local()`, where we can await for the result of the network request and update the UI directly - as we're being run in the UI thread.

Run the Rust version via `cargo run -p stockticker`.

# Python

The Python version is contained in [`main.py`](./main.py). It uses the `aiohttp` library to establish a network connection and issue the HTTP get requests, using Python's `asyncio` library. The entire request is started from within the `refresh` function that's marked to be `async` and connected to the `refresh` callback in `stockticker.slint`. Slint detects that the callback is async in Python and runs it as a new task.

Run the Python version via `uv run main.py` in the `examples/async-io` directory.
45 changes: 45 additions & 0 deletions examples/async-io/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Copyright © SixtyFPS GmbH <[email protected]>
# SPDX-License-Identifier: MIT

import slint
import asyncio
import aiohttp

Symbol = slint.loader.stockticker.Symbol


async def refresh_stocks(model: slint.ListModel[Symbol]) -> None:
STOOQ_URL = "https://stooq.com/q/l/?s={symbols}&f=sd2t2ohlcvn&h&e=json"
url = STOOQ_URL.format(symbols="+".join([symbol.name for symbol in model]))
async with aiohttp.ClientSession() as session:
async with session.get(url) as resp:
json = await resp.json()
json_symbols = json["symbols"]
for row, symbol in enumerate(model):
data_for_symbol = next(
(sym for sym in json_symbols if sym["symbol"] == symbol.name), None
)
if data_for_symbol:
symbol.price = data_for_symbol["close"]
model.set_row_data(row, symbol)


class MainWindow(slint.loader.stockticker.MainWindow):
def __init__(self):
super().__init__()
self.stocks = slint.ListModel(
[Symbol(name=name, price=0.0) for name in ["AAPL.US", "MSFT.US", "AMZN.US"]]
)

@slint.callback
async def refresh(self):
await refresh_stocks(self.stocks)


async def main() -> None:
main_window = MainWindow()
main_window.refresh()
main_window.show()


slint.run_event_loop(main())
76 changes: 76 additions & 0 deletions examples/async-io/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// Copyright © SixtyFPS GmbH <[email protected]>
// SPDX-License-Identifier: MIT

use slint::Model;

use async_compat::Compat;

slint::slint! {
export { MainWindow, Symbol } from "stockticker.slint";
}

#[derive(serde::Deserialize, Debug, Clone)]
struct JsonSymbol {
symbol: String,
close: f32,
}

#[derive(serde::Deserialize, Debug, Clone)]
struct JsonSymbols {
symbols: Vec<JsonSymbol>,
}

async fn refresh_stocks(model: slint::ModelRc<Symbol>) {
let url = format!(
"https://stooq.com/q/l/?s={}&f=sd2t2ohlcvn&h&e=json",
model.iter().map(|symbol| symbol.name.clone()).collect::<Vec<_>>().join("+")
);

let response = match reqwest::get(url).await {
Ok(response) => response,
Err(err) => {
eprintln!("Error fetching update: {err}");
return;
}
};

let json_symbols: JsonSymbols = match response.json().await {
Ok(json) => json,
Err(err) => {
eprintln!("Error decoding json response: {err}");
return;
}
};

for row in 0..model.row_count() {
let mut symbol = model.row_data(row).unwrap();
let Some(json_symbol) = json_symbols.symbols.iter().find(|s| *s.symbol == *symbol.name)
else {
continue;
};
symbol.price = json_symbol.close;
model.set_row_data(row, symbol);
}
}

fn main() -> Result<(), slint::PlatformError> {
let main_window = MainWindow::new()?;

let model = slint::VecModel::from_slice(&[
Symbol { name: "AAPL.US".into(), price: 0.0 },
Symbol { name: "MSFT.US".into(), price: 0.0 },
Symbol { name: "AMZN.US".into(), price: 0.0 },
]);

main_window.set_stocks(model.clone().into());

main_window.show()?;

slint::spawn_local(Compat::new(refresh_stocks(model.clone()))).unwrap();

main_window.on_refresh(move || {
slint::spawn_local(Compat::new(refresh_stocks(model.clone()))).unwrap();
});

main_window.run()
}
13 changes: 13 additions & 0 deletions examples/async-io/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Copyright © SixtyFPS GmbH <[email protected]>
# SPDX-License-Identifier: MIT

[project]
name = "python"
version = "1.14.0"
description = "Slint Stock Ticker Example for Python"
readme = "README.md"
requires-python = ">=3.12"
dependencies = ["aiohttp>=3.12.15", "slint"]

[tool.uv.sources]
slint = { path = "../../api/python/slint", editable = true }
48 changes: 48 additions & 0 deletions examples/async-io/stockticker.slint
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Copyright © SixtyFPS GmbH <[email protected]>
// SPDX-License-Identifier: MIT

import { Button, VerticalBox, HorizontalBox } from "std-widgets.slint";

export struct Symbol {
name: string,
price: float,
}

export component MainWindow inherits Window {
// The symbols and prices here are just to show something in the live-preview. They'll be replaced
// with fresh data on start-up.
in-out property <[Symbol]> stocks: [
{
name: "AAPL.US",
price: 237.96,
},
{
name: "MSFT.US",
price: 509.24,
},
{ name: "AMZN.US", price: 232.94 },
];
callback refresh();

VerticalBox {
padding: 10px;

for stock in root.stocks: HorizontalBox {
alignment: start;
Text {
text: stock.name;
}

Text {
text: "$\{stock.price.to-fixed(2)}";
}
}

Button {
text: @tr("Refresh");
clicked => {
root.refresh();
}
}
}
}
Loading