slint-ui / slint

Slint is a declarative GUI toolkit to build native user interfaces for Rust, C++, or JavaScript apps.
https://slint.dev
Other
16.93k stars 568 forks source link

A beginner's doubts #3156

Closed Vjze closed 1 year ago

Vjze commented 1 year ago

I am a beginner in Rust, looking for a practical and beautiful GUI. When I first saw Slint, I fell in love with her. But I have some doubts, why Slint is so much slower than egui f

https://github.com/slint-ui/slint/assets/99671889/dd4e6207-5ad2-4a37-9b3b-6d98a55b666f

or the same query code, a novice question. (my english is poor, sorry) https://gitee.com/weijingze/rust_slint.git

Vadoola commented 1 year ago

I get a 403 forbidden error when trying to access your code.

tronical commented 1 year ago

Thanks for reporting this issue. It looks like populating the large table is slow. It would indeed be interesting to see how the code looks like that you're using the populate the model. Like @Vadoola I can't seem to access the source code :(. Could it be that the repository is not public? (yes, I removed the trailing comma from the URL).

Vjze commented 1 year ago

slint_app.zip

I get a 403 forbidden error when trying to access your code.

Thanks for reporting this issue. It looks like populating the large table is slow. It would indeed be interesting to see how the code looks like that you're using the populate the model. Like @Vadoola I can't seem to access the source code :(. Could it be that the repository is not public? (yes, I removed the trailing comma from the URL).

I get a 403 forbidden error when trying to access your code.

Thanks for reporting this issue. It looks like populating the large table is slow. It would indeed be interesting to see how the code looks like that you're using the populate the model. Like @Vadoola I can't seem to access the source code :(. Could it be that the repository is not public? (yes, I removed the trailing comma from the URL).

https://github.com/Vjze/Rust_demo.git

tronical commented 1 year ago

Thanks for updating the source code - I can see it now. I can't run it (because it requires access to the database), but from a glance at the code I think I can see what the issue might be.

In the callback that's invoked when pressing the button (after selecting for example "Carton"), this function is called:

fn get_carton_result(text: String,app_weak:Weak<App>)  {
    let rt = tokio::runtime::Runtime::new().unwrap();
   let res =  rt.block_on(async {
        let row_data = carton_work(text).await;
        app_weak
        .unwrap()
        .global::<TableView>()
        .set_row_data(row_data.into());
        // app_weak
        //     .unwrap()
        //     .global::<TableView>()
        //     .set_row_data(row_data.into());
    });
//     res
}

The call to rt.block_on will block the main thread and thus make the UI unresponsive.

What I would recommend instead is something like we've done in cargo-ui:

  1. Initialize your TableView's row_data with an empty VecModel.
  2. Create a thread
  3. In that thread you have your long-running operations using tokio. In there it's okay to call block_on. The thread needs a clone of Weak<App>.
  4. When you have received results from your database, use Weak::upgrade_in_event_loop() to invoke a closure in the Slint main thread. In there you get access to ComponentHandle<App>, you can fetch your row_data() and downcast to the VecModel, so that you can push more rows into it, like in this example.

This way the UI thread remains always responsive while your worker thread uses tokio to spawn tasks to interact with your database.

Does that make sense? :)

Vjze commented 1 year ago

Thanks for updating the source code - I can see it now. I can't run it (because it requires access to the database), but from a glance at the code I think I can see what the issue might be.

In the callback that's invoked when pressing the button (after selecting for example "Carton"), this function is called:

fn get_carton_result(text: String,app_weak:Weak<App>)  {
    let rt = tokio::runtime::Runtime::new().unwrap();
   let res =  rt.block_on(async {
        let row_data = carton_work(text).await;
        app_weak
        .unwrap()
        .global::<TableView>()
        .set_row_data(row_data.into());
        // app_weak
        //     .unwrap()
        //     .global::<TableView>()
        //     .set_row_data(row_data.into());
    });
//     res
}

The call to rt.block_on will block the main thread and thus make the UI unresponsive.

What I would recommend instead is something like we've done in cargo-ui:

  1. Initialize your TableView's row_data with an empty VecModel.
  2. Create a thread
  3. In that thread you have your long-running operations using tokio. In there it's okay to call block_on. The thread needs a clone of Weak<App>.
  4. When you have received results from your database, use Weak::upgrade_in_event_loop() to invoke a closure in the Slint main thread. In there you get access to ComponentHandle<App>, you can fetch your row_data() and downcast to the VecModel, so that you can push more rows into it, like in this example.

This way the UI thread remains always responsive while your worker thread uses tokio to spawn tasks to interact with your database.

Does that make sense? :) 屏幕截图 2023-07-27 004825

Thanks for the guide, I'm trying to use multithreading, but I'm getting an error, 'std::rc::Rc<(dyn slint::Model<Data = ModelRc> + 'static)>` cannot be sent between threads safely', Rc cannot implement Send in multiple threads, may I have What about other ways to use set_row_data() directly?

MoutainPL commented 1 year ago

@Vjze I can give you some reference

    app.global::<PanelViewGlobal>().on_clicked({
        let app_weak = app.as_weak();
        move || {
            let app_weak = app_weak.clone();
            //multi-thread
            async_std::task::spawn(async move {
                let mut i = 0;
                loop {
                    if i > 90 {
                        break;
                    }
                    task::sleep(Duration::from_millis(200)).await;
                    app_weak
                        .upgrade_in_event_loop(|ui| {
                            ui.global::<PanelViewGlobal>().set_value(
                                chrono::Local::now()
                                    .format("%Y-%m-%d %H:%M:%S")
                                    .to_string()
                                    .into(),
                            );
                        })                             
                        .unwrap();
                    i += 1;
                }
            });
        }
    });