シグナル(イベント)
さて、ウィジットを配置することができるようになりましたが、相変わらずきちんと終了しないままです。 ウィンドウを閉じたときにアプリケーションを終了させるようにしましょう。
ウィンドウを閉じたときにアプリケーションを終了させるには、connect_delete_eventに関数を渡します。
最初のHello, Worldウィンドウを閉じたときに終了させるようにするには、以下のように記述します。
extern crate gtk;
use gtk::prelude::*;
fn main() {
gtk::init().expect("Failed to initialize GTK.");
let window = gtk::Window::new(gtk::WindowType::Toplevel);
window.set_title("Hello, World");
window.connect_delete_event(|_, _| {
gtk::main_quit();
Inhibit(false)
});
window.show_all();
gtk::main();
}
渡す関数には、Self(=window)とEventが渡ってきますが、どちらもここでは必要ないので_で無視します。 関数の返り値となる「Inhibit」はデフォルトハンドラを実行するかどうかを意味する値です。ここではどちらでもよいですが、falseを返しておきます。
このように、何かの契機で処理するための仕組みをGtk+ (が利用しているGLib Object System)では「シグナル」といっています。
ウィジットの値をハンドラ中で利用する
先の例に、1行テキスト入力とボタンを追加して、ボタンが押されたらコンソールに内容を表示するようにしてみましょう。
Buttonのクリックに反応するようにするには、connect_clickedを使います。
extern crate gtk;
use gtk::prelude::*;
fn main() {
gtk::init().expect("Failed to initialize GTK.");
let window = gtk::Window::new(gtk::WindowType::Toplevel);
window.set_title("Hello, World");
window.connect_delete_event(|_, _| {
gtk::main_quit();
Inhibit(true)
});
let hbox = gtk::Box::new(gtk::Orientation::Horizontal, 5);
let entry = gtk::Entry::new();
hbox.pack_start(&entry, true, true, 5);
let button = gtk::Button::new_with_label("Say");
button.connect_clicked(move |_| {
println!("Text: {}", entry.get_text().unwrap());
});
hbox.pack_start(&button, false, false, 5);
window.add(&hbox);
window.show_all();
gtk::main();
}
動作させると、このような形になります。
ここで大事なことは、クロージャにmoveキーワードを付与し、button変数の所有権をクロージャに移動させているところです。移動させないと、変数「entry」よりもクロージャのほうが長く生きる可能性があるため、変数の借用(borrow)では実際使うタイミングで変数がいなくなっている可能性があるとコンパイラが判断するためです。
外してコンパイルすると、その旨のエラーが出力されるのでわかります。
src/main.rs:24:28: 26:6 error: closure may outlive the current function, but it borrows
entry
, which is owned by the current function [E0373]
src/main.rs:24 button.connect_clicked(|_| {
src/main.rs:25 println!("Text: {}", entry.get_text().unwrap());
src/main.rs:26 });
src/main.rs:24:28: 26:6 help: runrustc --explain E0373
to see a detailed explanation
src/main.rs:25:30: 25:35 note:entry
is borrowed here
src/main.rs:25 println!("Text: {}", entry.get_text().unwrap());
^~~~~
(略)
src/main.rs:24:28: 26:6 help: to force the closure to take ownership ofentry
(and any other referenced variables), use themove
keyword, as shown:
src/main.rs: button.connect_clicked(move |_| {
src/main.rs: println!("Text: {}", entry.get_text().unwrap());""
src/main.rs: });
メッセージダイアログの表示
今度はprintln!()ではなく、メッセージダイアログを表示してみましょう。
メッセージダイアログを表示する例は、以下のようになります。
extern crate gtk;
use gtk::prelude::*;
fn main() {
gtk::init().expect("Failed to initialize GTK.");
let window = gtk::Window::new(gtk::WindowType::Toplevel);
window.set_title("Hello, World");
window.connect_delete_event(|_, _| {
gtk::main_quit();
Inhibit(true)
});
let hbox = gtk::Box::new(gtk::Orientation::Horizontal, 5);
let entry = gtk::Entry::new();
hbox.pack_start(&entry, true, true, 5);
let button = gtk::Button::new_with_label("Say");
let window_ = window.clone();
button.connect_clicked(move |_| {
let message = format!("Text: {}", entry.get_text().unwrap());
let dialog = gtk::MessageDialog::new(
Some(&window_),
gtk::DialogFlags::empty(),
gtk::MessageType::Info,
gtk::ButtonsType::Ok,
&message
);
dialog.run();
dialog.destroy();
});
hbox.pack_start(&button, false, false, 5);
window.add(&hbox);
window.show_all();
gtk::main();
}
MessageDialog::newの第1引数には、windowのOptionを渡しますが、windowそのものの所有権をmoveで クロージャに渡してしまうと、あとで使うはずのwindowが使えなくなってしまいます。そのため、clone()したものをクロージャで使います。
gtk::Windowのclone()は、Gtk+で利用しているGObjectのリファレンスカウントを増加させるもので、deep copyされるわけではありません。
なお、clone()しないとこのようなエラーが出力されます。clone()が必要かも、とエラーで言っています。
src/main.rs:41:5: 41:11 error: use of moved value:
window
[E0382]
src/main.rs:41 window.add(&hbox);
^~~~~~
src/main.rs:41:5: 41:11 help: runrustc --explain E0382
to see a detailed explanation
src/main.rs:25:28: 37:6 note:window
moved into closure environment here because it has typegtk::auto::window::Window
, which is non-copyable
src/main.rs:25 button.connect_clicked(move |_| {
src/main.rs:26 let message = format!("Text: {}", entry.get_text().unwrap());
src/main.rs:27
src/main.rs:28 let dialog = gtk::MessageDialog::new(
src/main.rs:29 Some(&window),
src/main.rs:30 gtk::DialogFlags::empty(),
...
src/main.rs:25:28: 37:6 help: perhaps you meant to useclone()
?
ここまで来れば、すでに簡単なアプリケーションが作れるようになっているはずです。