Reputation: 303
I am have a gtk-rs
application where a Gtk::Entry
is used to get user input when the user hits a submit button. Everything compiles and runs however I can not get the user input outside of the method that handles the button press.
I can check that the input is collected properly from inside the button click handler however getting the string outside of the method is not working at the moment.
I have found similar questions and spent a lot of time googling this issue but so far I have not found a solution to this problem. I actually haven't even found anyone asking the same question which makes me think either I am asking the wrong question or I am trying to do this in a very bad way.
So the code is here. I want to get the user_input
variable outside of the button.connect_clicked
method.
Code:
use gtk::prelude::*;
use gtk::{Button, Orientation};
use std::cell::RefCell;
use std::rc::Rc;
pub fn login_view() -> (gtk::Box, String) {
let button = Button::builder()
.label("Submit")
.margin_top(6)
.margin_bottom(12)
.margin_start(12)
.margin_end(12)
.build();
let input = gtk::Entry::builder()
.placeholder_text("input")
.margin_top(12)
.margin_bottom(6)
.margin_start(12)
.margin_end(12)
.build();
let login_box = gtk::Box::builder()
.orientation(Orientation::Vertical)
.build();
login_box.append(&input);
login_box.append(&button);
let mut user_input = Rc::new(RefCell::new(String::new()));
let user_clone = user_input.clone();
button.connect_clicked(move |_| {
user_input.replace((format!("{}", input.text())));
println!("user input = {}", user_input.borrow().to_string());
input.set_text("");
});
println!("user_clone: {}", user_clone.borrow().to_string());
return (login_box, user_clone.borrow().to_string());
}
output:
user_clone:
user input = dsfdsfds // this line repeats as many times as you hit the button.
This could potentially be an async problem where println!("user_clone: {}", user_clone.borrow().to_string());
is called before the user input is collected and then never runs again but if that is the case I also don't know how to handle that situation because the connect_clicked
method doesn't really complete, it just runs every time the button is clicked.
The end goal is to get the input value and store it in a struct. So anyway I can get this input value into a struct that has already been declared would be ideal.
Upvotes: 0
Views: 309
Reputation: 27249
You already started to go down the right path, you used a Rc<RefCell<String>>
to share ownership, and retain mutability.
So instead of returning just the contents at the end of the function you could return the whole user_clone
, but you'll quickly realize that
graphical applications are usually multithreaded for a reason and thus the Rc<...>
would need to be Send
or Sync
or both. So I suggest to instead switch to Arc<Mutex<String>>
(or replace Mutex
for RwLock
)
use gtk::prelude::*;
use gtk::{Application, ApplicationWindow, Button, Orientation};
use std::sync::{Arc, Mutex};
pub fn login_view() -> (gtk::Box, Arc<Mutex<String>>) {
// ...
let user_input = Arc::new(Mutex::new(String::new()));
button.connect_clicked({
let user_input = Arc::clone(&user_input);
move |_| {
let mut user_input = user_input.lock().unwrap();
*user_input = input.to_string();
println!("user input = {}", user_input);
input.set_text("");
}
});
println!("user_clone: {}", user_input.lock().unwrap().to_string());
return (login_box, user_input);
}
fn run_ui(app: &Application) {
let (view, string) = login_view();
let window = ApplicationWindow::builder()
.application(app)
.title("Hello Gtk")
.child(&view)
.build();
// example use from another thread
std::thread::spawn(move || loop {
std::thread::sleep_ms(1000);
println!("user_input: {}", string.lock().unwrap());
});
window.present();
}
You can then put that returned Arc<Mutex<String>>
in the struct where you wanted the original String
and whenever you read from it you will have the latest version.
Upvotes: 1