Replies: 7 comments 2 replies
-
|
Beta Was this translation helpful? Give feedback.
-
OK, I've figured it's possible to unsafe impl Send and Sync for App, so the following version kind of works (even if I remove the Mutex and Arc): use std::sync::{Arc, Mutex};
use slint::SharedString;
use slint::Weak;
slint::slint! {
import { Button, VerticalBox } from "std-widgets.slint";
export component App inherits Window {
in-out property<string> logs;
callback start_button_pressed <=> startButton.clicked;
init() => {
logsInput.focus();
}
title: "IPC Server";
width: 550px;
height: 400px;
VerticalLayout {
spacing: 8px;
padding: 8px;
startButton := Button {
height: 28px;
text: "Start Server";
}
Rectangle {
border-width: 1px;
border-color: black;
border-radius: 10px;
GridLayout {
spacing: 8px;
padding: 8px;
logsInput := TextInput {
single-line: false;
read-only: true;
wrap: TextWrap.word-wrap;
text: logs;
}
}
}
}
}
}
unsafe impl Send for App {}
unsafe impl Sync for App {}
fn main() {
let app: App = App::new().unwrap();
let weak: Weak<App> = app.as_weak();
app.on_start_button_pressed(move || {
let app: App = weak.upgrade().unwrap();
let log = format!(
"{logs}\nStarting the server on 127.0.0.1:2020...",
logs = app.get_logs().clone()
);
app.set_logs(SharedString::from(log.clone()));
let shared_app = Arc::new(Mutex::new(app));
let shared_app = Arc::clone(&shared_app);
let message_received_callback = move |message: &str| {
let app = shared_app.lock().unwrap();
let log = format!(
"{logs}\nReceived a client message: {message}'",
logs = app.get_logs().clone(),
message = message
);
app.set_logs(SharedString::from(log.clone()));
println!("Received a client message: {}", message);
};
let message_received_callback = Arc::new(message_received_callback);
match sg_ipc::start_server("127.0.0.1:2020", message_received_callback) {
Ok(_) => {
let app: App = weak.upgrade().unwrap();
let log = format!(
"{logs}\nSuccessfully started the server!",
logs = app.get_logs().clone()
);
app.set_logs(SharedString::from(log));
}
Err(err) => {
let app: App = weak.upgrade().unwrap();
let log = format!("{logs}\n{err}", logs = app.get_logs(), err = err);
app.set_logs(SharedString::from(log));
}
}
});
app.run().unwrap();
} But, there is an issue. I see the logs immediately printed to the terminal, like this:
But, the GUI refuses to update for a noticeable time (like a few seconds). I'm not sure what I am doing wrong. |
Beta Was this translation helpful? Give feedback.
-
You can spawn a thread and use invoke_from_event_loop or Weak::upgrade_in_event_loop to report messages to the main thread. So your code could like this (untested) fn main() {
let app: App = App::new().unwrap();
let weak: Weak<App> = app.as_weak();
app.on_start_button_pressed(move || {
let app: App = weak.upgrade().unwrap();
let log: String = format!(
"{logs}\nStarting the server on 127.0.0.1:2020...",
logs = app.get_logs()
);
app.set_logs(SharedString::from(log));
// ##### start a thread #####
std::thread::spawn(move || {
let message_received_callback = |message: &str| {
let message = message.to_owned();
// ##### use upgrade_in_event_loop() to communicate back with the main thread #####
weak.upgrade_in_event_loop(move |app| {
let log: String = format!(
"{logs}\n: 'Received a client message: {message}'...",
logs = app.get_logs(),
message = message
);
app.set_logs(SharedString::from(log));
println!("Received message from client: {}", log);
});
};
let message_received_callback = Arc::new(message_received_callback);
match sg_ipc::start_server("127.0.0.1:2020", message_received_callback) {
Ok(_) => {
let log: String = format!(
"{logs}\nSuccessfully started the server!",
logs = app.get_logs()
);
app.set_logs(SharedString::from(log));
}
Err(err) => {
let app: App = weak.upgrade().unwrap();
let log: String = format!("{logs}\n{err}", logs = app.get_logs(), err = err);
app.set_logs(SharedString::from(log));
}
}
}
}); |
Beta Was this translation helpful? Give feedback.
-
Thank you very much for fixing the example. I'm getting:
This makes sense since message_received_callback is defined in the scope of the thread brackets: std::thread::spawn(move || {
let message_received_callback = move |message: &str| {
let log = format!(
"{logs}\nReceived a client message: {message}'",
logs = app.get_logs(),
message = message
);
app.set_logs(SharedString::from(log));
weak.upgrade_in_event_loop(move |app| {
let log = format!(
"{logs}\nReceived a client message: {message}'",
logs = app.get_logs(),
message = message
);
app.set_logs(SharedString::from(log));
});
println!("Received a client message: {}", message);
};
});
let message_received_callback = Arc::new(message_received_callback); How can I work around this and still be able to create and Arch from it in order to be passed? |
Beta Was this translation helpful? Give feedback.
-
Also, for the sake of testing, I removed the thread and did the following: let message_received_callback = move |message: &str| {
let log = format!(
"{logs}\nReceived a client message: {message}'",
logs = app.get_logs(),
message = message
);
app.set_logs(SharedString::from(log));
weak.upgrade_in_event_loop(move |app| {
let log = format!(
"{logs}\nReceived a client message: {message}'",
logs = app.get_logs(),
message = message
);
app.set_logs(SharedString::from(log));
});
println!("Received a client message: {}", message);
};
let message_received_callback = Arc::new(message_received_callback); But, again I'll get:
Then I tried to do the same thing as I did for the App in order to be able to create the Arc: unsafe impl Send for App {}
unsafe impl Sync for App {}
unsafe impl Send for Weak<App> {}
unsafe impl Sync for Weak<App> {} But, I get:
Same thing with Weak: unsafe impl<T> Send for Weak<T> {}
unsafe impl<T> Sync for Weak<T> {} |
Beta Was this translation helpful? Give feedback.
-
Also, I've noticed the GUI updates if I move the mouse over it. So, it has nothing to do with the time (waiting a few seconds for the GUI). So, it's kind of weird that the app.set_logs does not update inside the callback. |
Beta Was this translation helpful? Give feedback.
-
Thank you very much for all the suggestions. I've tried to do the following:
pub fn start_server<F>(address: &str, message_received_callback: Arc<F>) -> Result<(), IpcError>
where
F: Fn(&str) + std::marker::Send + std::marker::Sync + 'static,
{
match TcpListener::bind(address) {
Ok(listener) => {
if let Some(stream) = listener.incoming().next() {
match stream {
Ok(stream) => {
let message_received_callback = message_received_callback.clone();
return handle_client(stream, message_received_callback);
}
Err(err) => {
return Err(IpcError::new(format!("{}", err).as_str()));
}
}
}
Ok(())
}
Err(err) => {
return Err(IpcError::new(format!("{}", err).as_str()));
}
}
} And, now my main looks like this as you've suggested: unsafe impl Send for App {}
unsafe impl Sync for App {}
fn main() {
let app: App = App::new().unwrap();
let weak: Weak<App> = app.as_weak();
app.on_start_button_pressed(move || {
let app: App = weak.upgrade().unwrap();
let log = format!(
"{logs}\nStarting the server on 127.0.0.1:2020...",
logs = app.get_logs()
);
app.set_logs(SharedString::from(log));
let weak: Weak<App> = weak.to_owned();
std::thread::spawn(move || {
let message_received_callback = move |message: &str| {
let log = format!(
"{logs}\nReceived a client message: {message}'",
logs = app.get_logs(),
message = message
);
app.set_logs(SharedString::from(log));
println!("Received a client message: {}", message);
};
let message_received_callback = Arc::new(message_received_callback);
match sg_ipc::start_server("127.0.0.1:2020", message_received_callback) {
Ok(_) => {
let _ = weak.upgrade_in_event_loop(move |app| {
let log = format!(
"{logs}\nSuccessfully started the server!",
logs = app.get_logs()
);
app.set_logs(SharedString::from(log));
});
}
Err(err) => {
let _ = weak.upgrade_in_event_loop(move |app| {
let log = format!("{logs}\n{err}", logs = app.get_logs(), err = err);
app.set_logs(SharedString::from(log));
});
}
}
});
});
app.run().unwrap();
} But, still, I get the same behavior. The UI won't update until I move the mouse over the window. If I remove the impl for Send and Sync, then I get a very lengthy error, but just one for example:
If I don't do that extra, weak copy: let weak: Weak<App> = weak.to_owned();
std::thread::spawn(move || { I get:
I guess the last one is because of the nested closures. |
Beta Was this translation helpful? Give feedback.
-
I'm new to Rust. So, forgive me if my question seems a bit novice.
I have the following code in my main:
And, I also have a library with two functions start_server and handle_client:
Basically, what I want to achieve is I start a TCP server. It receives messages from clients and I want that message to be shown in my GUI app. But, I get a lengthy error from rustc telling me slint::App and all the children they won't implement Send and Sync. I've tried many things ranging from wrapping it inside an Arc (which seems not to work because Arc implements Send only if T is both Send and Sync), to sending over channels, and I did not manage to make this work.
So, my question is how to share slint::App with other threads and capture it in a callback closure. What is the pragmatic way to do so?
Beta Was this translation helpful? Give feedback.
All reactions