====== Appunti sulla gestione della memoria in Gtkrs e Rust ======
Autore: **//Fabio Di Matteo//** \\ Ultima revisione: **// 30/04/2026 14:08 //** // //
Alcuni esempi che mostrano alcuni casi tipici di gestione della memoria.
Miei progetti che implementano le conoscenze affrontate in questo articolo:
* [[https://www.freemedialab.org/code/project.php?p=video_cutter|Progetto Gtkrs completo di esempio ]]
===== Due button che incrementano una stessa variabile intera =====
use gtk4::prelude::*;
use gtk4::{Application, ApplicationWindow, glib, Button,Box,Orientation};
use std::rc::Rc;
use std::cell::Cell;
const APP_ID: &str = "org.gtk_rs.test_vari";
fn main() -> glib::ExitCode {
// Create a new application
let app = Application::builder().application_id(APP_ID).build();
// Connect to "activate" signal of `app`
app.connect_activate(build_ui);
// Run the application
app.run()
}
fn build_ui(app: &Application) {
let button0 = Button::builder()
.label("Press me!")
.margin_top(12)
.margin_bottom(12)
.margin_start(12)
.margin_end(12)
.build();
let button1 = Button::builder()
.label("Press me, I'm the button two!'")
.margin_top(12)
.margin_bottom(12)
.margin_start(12)
.margin_end(12)
.build();
let gtk_box = Box::builder()
.orientation(Orientation::Vertical)
.build();
gtk_box.append(&button0);
gtk_box.append(&button1);
let counter = Rc::new(Cell::new(0));
let count = counter.clone();
button0.connect_clicked(move |button0| {
count.set(count.get()+1);
button0.set_label(&count.get().to_string());
});
let count = counter.clone();
button1.connect_clicked(move |button1| {
count.set(count.get()+1);
button1.set_label(&count.get().to_string());
});
let window = ApplicationWindow::builder()
.application(app)
.title("My GTK App")
.child(>k_box)
.build();
// Present window
window.present();
}
===== Interagire con un widget in piu' callbacks =====
Si deve clonare il riferimento al widget ogni volta prima di usarlo. Ecco come:
...
let label0 = Label::builder()
.label("Ciao MOndo")
.margin_top(15)
.build();
let label_clone = label0.clone();
button0.connect_clicked(move |button0| {
count.set(count.get()+1);
label_clone.set_label(format!("Hai cliccato {} volte",&count.get().to_string()).as_str());
button0.set_label(&count.get().to_string());
});
let label_clone = label0.clone();
button1.connect_clicked(move |button1| {
count.set(count.get()+1);
label_clone.set_label(format!("Hai cliccato {} volte",&count.get().to_string()).as_str());
button1.set_label(&count.get().to_string());
});
Come detto clono label0 in label_clone prima di riutilizzarlo.
==== Altro esempio di interazione ====
Apro una seconda finestra al click di un Button.
...
let window2 = Window::builder()
.title("Seconda finestra")
.width_request(700)
.visible(false)
.build();
let button2 = Button::builder().label("Apre seconda finestra").build();
box0.append(&button2);
let w2_clone = window2.clone();
button2.connect_clicked(move |_| {
w2_clone.present();
});
...
==== Intercettare chiusura finestra e cambiare comportamento ====
window2.connect_close_request(|w| {
w.hide();
return glib::Propagation::Stop;
});
===== Modificare i widget da thread esterni al ciclo principale delle Gtk =====
{{:programmazione:rust:gtkrs:gtkrs_widgets_from_thread.png?400|}}
Per fare questo uso i messaggi. Non modifico i widgets direttamente da altri thread. Dunque sul ciclo principale avro' un timeout che ogni tot di tempo controlla se ci sono messaggi che poi userĂ per modificare i widget.
Uso i classici **mpsc::channel**.
let (tx, rx) = mpsc::channel::();
Ecco **tutto** il codice commentato:
use gtk4::prelude::*;
use gtk4::{Application, ApplicationWindow, glib, Box,Label,Entry,gio };
use std::sync::mpsc;
use std::thread;
use std::time::Duration;
const APP_ID: &str = "org.gtk_rs.hello";
fn main() -> glib::ExitCode {
// Create a new application
let app = Application::builder().application_id(APP_ID).build();
// Connect to "activate" signal of `app`
app.connect_activate(build_ui);
// Run the application
app.run()
}
fn build_ui(app: &Application) {
let box0 = Box::builder()
.orientation(gtk4::Orientation::Vertical)
.build();
let entry0=Entry::builder()
.build();
let label = Label::builder()
.label("Incrementiamo?")
.use_markup(true)
.build();
box0.append(&label);
box0.append(&entry0);
// Siamo nel ciclo principale delle Gtk. Resto in ascolto (polling)
// per la ricezine dei messaggi. In questo modo mi evito di modificare
// i widgets da altri thread.
let (tx, rx) = mpsc::channel::();
glib::timeout_add_local(Duration::from_millis(100), move || {
// Legge tutti i messaggi disponibili
// Qui si potrebbero filtrare i messaggi per fare cose diverse
while let Ok(msg) = rx.try_recv() {
let m = msg.clone(); //prima di usare il messaggio lo clono
label.set_label(&m);
}
glib::ControlFlow::Continue
});
gio::spawn_blocking(move || {
let mut t =0;
// Un loop che ogni secondo invia un messaggio con una stringa
// al ciclo principale. Il cicloprincipale ha un timeout che ogni
// lancia una chiusura per l'elaborazione del messaggio ogni n decimi di secondo
loop{
t=t+1;
if tx.send(format!("Count: {}", t)).is_err() { break; }
let wait_secs = Duration::from_secs(1);
thread::sleep(wait_secs);
}
});
let window = ApplicationWindow::builder()
.application(app)
.title("My GTK App")
.width_request(500)
.child(&box0)
.build();
// Present window
window.present();
}