-
Notifications
You must be signed in to change notification settings - Fork 643
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
async run() #2793
Comments
I think as a generic solution that works with winit and Qt backends, that's going to be very hard. I'd prefer having public API for the winit backend and then folks could use this together with winit-async for example - if that's somehow feasible. For Tokio, we used a thread in cargo-ui: The Tokio runtime is run in a thread and a channel is used to exchange messages between Slint and async functions: https://github.com/slint-ui/cargo-ui/blob/master/src/main.rs For embassy and MCU: I think for the time being that may be possible of you run your own superloop instead of using the |
I believe winit need to take control of the event loop, so there can't be an underlying event loop like Tokio. What we should support however is the ability to run any future in the Slint's event loop #747 |
Hmmm, I don't see how the winit event loop might share CPU time with another async-event loop such as embassy. Right now I am evaluating best solution to manage a HMI on stm32f4 and handling async events from USB and buttons. Instead of using kind of RTOS/C++ to handle different preempt tasks, I would prefer using Rust and its async-feature to handle the IO events from USB, hardware-buttons, HMI-touch events etc. in a single event loop. Here is a comparison AFAICS, an embassy "task" starting a slint Window event loop would block and would starve all other embassy tasks. https://docs.rs/embassy-executor/latest/embassy_executor/attr.task.html So far I dont see how embassy and slint could be combined on stm32f4 :( |
I think the way you'd combine Slint into am embassy task is to write the loop like here in the task and use a timer like I wonder if there's a way to create an example of this that runs on desktop (for testing purposes). |
I wonder how much this also relates to #2763 |
Yes, #2763 is related (just its use case is C++). My use case is using Rust "embassy" xor "tokio" event-engine based on the Rust async feature. |
It would be usefull to know the exact use-case and what we are trying to solve. If you never call I am not super faimilar with these framework, but i guess the loop from https://docs.rs/slint/latest/slint/docs/mcu/index.html#the-event-loop can be converted to an async loop. Something like that async fn my_event_loop() {
//... initialize the window and stuff
loop {
select!{
event = the_touch_driver.query_event() => {
slint::platform::update_timers_and_animations();
window.dispatch_event(event);
}
_ = timer_wait(slint::platform::duration_until_next_timer_update()) => {
slint::platform::update_timers_and_animations();
}
}
// Draw the scene if something needs to be drawn.
window.draw_if_needed(|renderer| {
todo!("render the scene")
});
} Or something similar. We may want to add helpers or examples to use Slint with known runtime. |
This is |
Perhaps you can use |
I'll chek it. |
It seems that the |
Indeed, but |
The proposed solution was implemented by using the macro My solution is based on the It works by combining following methods and |
There is an open PR here |
I'll add my 2 cents here: I am primarily a C# developer with an interest in Rust. I would consider myself an intermediate Rust programmer, but novice Tokio user. I recently discovered Slint and since I was also looking into gRPC at the same time, I thought I'd try to combine them in a small Slint GUI desktop (Windows) app that makes a gRPC call to a server to fetch some data that is displayed in the UI. Shouldn't be too hard, right? Alas, my poor brain imploded when I contemplated how to integrate Slint's synchronous Rust API with a Tokio async-based gRPC client like |
Well, I got it working without a message channel. It's not terrible.. I'm using the Hello World tutorial from use hello_world::greeter_client::GreeterClient;
use hello_world::HelloRequest;
use tokio::task::spawn_blocking;
pub mod hello_world {
tonic::include_proto!("helloworld");
}
slint::include_modules!();
fn main() {
// Create a multi-threaded Tokio Runtime
let runtime = tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()
.unwrap();
// The Tokio Runtime main task just launches a blocking thread for the UI and waits for it to finish.
// This could do other things as well, like starting background initialization tasks, etc.
runtime.block_on(async {
spawn_blocking(run_ui)
.await
.unwrap()
.unwrap()
});
}
fn run_ui() -> Result<(), slint::PlatformError> {
// Initialize UI
let ui = AppWindow::new()?;
// Get a UI weakref for use in the callback
let ui_handle = ui.as_weak();
// Handle the button click
ui.on_send_hello(move || {
// Get the name that the user entered in the text field
let ui = ui_handle.unwrap();
let name = ui.get_name().to_string();
// Another UI weakref for updating it with the response from the gRPC server
let ui_handle = ui.as_weak();
// Spawn a Tokio task to talk to the gRPC server
[runtime](tokio::runtime::Handle::current()).spawn(async move {
// send the request and await the result
let result = send_hello(&name).await.unwrap();
// update the UI with the server response on the UI thread
ui_handle.upgrade_in_event_loop(|ui| {
ui.set_hello_response(result.into());
}).unwrap();
});
});
ui.run()
}
async fn send_hello(name: &str) -> Result<String, Box<dyn std::error::Error>> {
let mut client = GreeterClient::connect("http://[::1]:50051").await?;
let request = tonic::Request::new(HelloRequest { name: name.into() });
let response = client.say_hello(request).await?;
let message = response.into_inner().message;
println!("RESPONSE: {:?}", message);
Ok(message)
} import { Button, VerticalBox, HorizontalBox, LineEdit } from "std-widgets.slint";
export component AppWindow inherits Window {
in-out property <string> name: "";
in-out property <string> hello-response: "";
callback send-hello();
VerticalBox {
HorizontalBox {
Text {
vertical-alignment: TextVerticalAlignment.center;
text: "Name:";
}
le-name := LineEdit {
text: root.name;
edited(text) => {
root.name = text;
}
accepted => {
root.send-hello();
}
}
}
Button {
text: "Send Hello";
clicked => {
root.send-hello();
}
}
Text {
text: "Server says \"\{root.hello-response}\"";
}
}
init => {
le-name.focus();
}
} EDIT: Simplified to not require passing Tokio runtime handle through UI |
instead of blocking call into slint event-loop
HelloWorld::new().unwrap().run().unwrap();
there should be an option to invoke an async run function, so the underlying event loop (embassy or tokio) might have a chance to process other pending operations asynchronously.
The text was updated successfully, but these errors were encountered: