Rustで学ぶGTK4
はじめに
編集GTK4とRustを組み合わせたGUIプログラミングについて、基本から実践的な例まで解説します。
開発環境のセットアップ
編集必要なパッケージのインストール
編集# Debian/Ubuntuの場合 sudo apt install libgtk-4-dev build-essential sudo apt install cargo rustc
Cargo.tomlの依存関係
- Cargo.toml
[dependencies] gtk = { version = "0.6", package = "gtk4" } gio = "0.17" glib = "0.17"
基本的なアプリケーション構造
編集最小限のGTK4アプリケーション
編集use gtk::prelude::*; use gtk::{Application, ApplicationWindow}; fn main() { // アプリケーションの作成 let app = Application::builder() .application_id("com.example.FirstApp") .build(); // アプリケーションの起動時の処理 app.connect_activate(build_ui); app.run(); } fn build_ui(app: &Application) { // ウィンドウの作成 let window = ApplicationWindow::builder() .application(app) .title("My GTK App") .default_width(600) .default_height(400) .build(); window.present(); }
ウィジェットの基本
編集ボタンの追加
編集use gtk::{Button, Box}; fn build_ui(app: &Application) { let window = ApplicationWindow::builder() .application(app) .title("Button Example") .build(); let button = Button::builder() .label("Click Me!") .margin_top(12) .margin_bottom(12) .margin_start(12) .margin_end(12) .build(); // クリックイベントの処理 button.connect_clicked(|_| { println!("Button clicked!"); }); window.set_child(Some(&button)); window.present(); }
レイアウトの作成
編集fn create_layout() -> Box { let box_layout = Box::builder() .orientation(gtk::Orientation::Vertical) .spacing(6) .build(); let button1 = Button::with_label("Button 1"); let button2 = Button::with_label("Button 2"); box_layout.append(&button1); box_layout.append(&button2); box_layout }
シグナルとイベント処理
編集シグナルの接続
編集use glib::clone; fn connect_signals(button: &Button, label: &Label) { button.connect_clicked(clone!(@weak label => move |_| { label.set_text("Button was clicked!"); })); }
カスタムシグナル
編集use glib::subclass::Signal; use glib::signal::SignalHandlerId; #[derive(Default)] pub struct CustomWidget { count: Cell<i32>, } #[glib::object_subclass] impl ObjectSubclass for CustomWidget { const NAME: &'static str = "CustomWidget"; type Type = super::CustomWidget; type ParentType = gtk::Widget; } impl ObjectImpl for CustomWidget { fn signals() -> &'static [Signal] { static SIGNALS: Lazy<Vec<Signal>> = Lazy::new(|| { vec![Signal::builder("count-changed") .param_types([i32::static_type()]) .build()] }); SIGNALS.as_ref() } }
GUIコンポーネント
編集エントリー(テキスト入力)
編集use gtk::Entry; fn create_entry() -> Entry { let entry = Entry::builder() .placeholder_text("Enter text...") .build(); entry.connect_changed(|entry| { println!("Text changed: {}", entry.text()); }); entry }
リスト表示
編集use gtk::{ListBox, ListBoxRow}; fn create_list() -> ListBox { let list_box = ListBox::new(); for i in 1..5 { let row = ListBoxRow::new(); let label = Label::new(Some(&format!("Row {}", i))); row.set_child(Some(&label)); list_box.append(&row); } list_box }
データバインディング
編集プロパティバインディング
編集use gtk::Expression; fn bind_properties(source: &Label, target: &Entry) { let expression = Expression::closure(closure!(|src: &Label| { src.text() })); target.bind_property("text", source, "label") .bidirectional() .build(); }
ファイル操作
編集ファイル選択ダイアログ
編集use gtk::FileChooserDialog; fn show_file_dialog(window: &ApplicationWindow) { let dialog = FileChooserDialog::builder() .title("Choose a file") .parent(window) .action(gtk::FileChooserAction::Open) .build(); dialog.add_button("Cancel", gtk::ResponseType::Cancel); dialog.add_button("Open", gtk::ResponseType::Accept); dialog.connect_response(|dialog, response| { if response == gtk::ResponseType::Accept { if let Some(file) = dialog.file() { println!("Selected file: {:?}", file.path()); } } dialog.close(); }); dialog.present(); }
スタイリング
編集CSSの適用
編集fn apply_css(widget: &impl IsA<Widget>) { let css_provider = CssProvider::new(); css_provider.load_from_data(b" button { background: linear-gradient(to bottom, #3498db, #2980b9); color: white; border-radius: 4px; padding: 8px; } "); gtk::style_context_add_provider_for_display( &Display::default().expect("Could not connect to display"), &css_provider, gtk::STYLE_PROVIDER_PRIORITY_APPLICATION, ); }
エラー処理
編集エラーダイアログ
編集use gtk::MessageDialog; fn show_error(window: &ApplicationWindow, message: &str) { let dialog = MessageDialog::builder() .modal(true) .buttons(gtk::ButtonsType::Ok) .message_type(gtk::MessageType::Error) .text(message) .parent(window) .build(); dialog.connect_response(|dialog, _| { dialog.close(); }); dialog.present(); }
非同期処理
編集非同期タスクの実行
編集use glib::MainContext; use std::thread; fn run_async_task<F>(f: F) where F: Future<Output = ()> + 'static, { let main_context = MainContext::default(); main_context.spawn_local(f); } // 使用例 run_async_task(async { // 非同期処理 thread::sleep(Duration::from_secs(2)); println!("Async task completed!"); });
ベストプラクティス
編集アプリケーション構造
編集mod ui; mod data; mod config; struct App { window: ApplicationWindow, config: Config, ui: UiBuilder, } impl App { fn new(app: &Application) -> Self { // アプリケーションの初期化 } fn build_ui(&self) { // UIの構築 } fn connect_signals(&self) { // シグナルの接続 } }
リソース管理
編集use gio::Resource; fn load_resources() { let resource_bytes = include_bytes!("../resources/resources.gresource"); let resource = Resource::from_data(&resource_bytes).unwrap(); gio::resources_register(&resource); }
まとめ
編集GTK4とRustの組み合わせは、型安全で高性能なGUIアプリケーションの開発を可能にします。このガイドで紹介した基本的な概念と実装例を基に、独自のアプリケーション開発を始めることができます。