在 Rust 中使用 GTK+ 框架開發桌面應用程式,需要熟悉 GTK+ 的元件和訊號機制。本文將示範如何建立一個包含滾動視窗、列表框和按鈕的應用程式,並講解如何處理使用者輸入和更新介面。透過逐步的程式碼範例,讀者可以學習如何使用 GTK+ 構建具有互動功能的使用者介面。此外,文章還提供圖表說明,以更清晰地展現元件之間的關係和資料流程,幫助讀者更容易理解 GTK+ 框架的運作方式。

簡介

在這個範例中,我們將建立一個簡單的 GTK 應用程式,展示如何使用 GTK 建立一個帶有滾動視窗、列表框和按鈕的圖形使用者介面。

建立滾動視窗和列表框

首先,我們需要建立一個滾動視窗和列表框。滾動視窗將用於容納列表框,列表框將用於顯示專案。

let scrolled_window = gtk::ScrolledWindow::new(gtk::Adjustment::NONE, gtk::Adjustment::NONE);
let listbox = gtk::ListBox::new();
scrolled_window.add(&listbox);

建立按鈕

接下來,我們需要建立兩個按鈕:新增按鈕和刪除按鈕。新增按鈕將用於新增新專案到列表框中,刪除按鈕將用於刪除選中的專案。

let hbox = gtk::Box::new(gtk::Orientation::Horizontal, 5);
let add_button = gtk::Button::with_label("Add");
let delete_button = gtk::Button::with_label("Delete");
hbox.add(&add_button);
hbox.add(&delete_button);

新增按鈕的功能

當新增按鈕被點選時,我們需要彈出一個對話方塊,讓使用者輸入新專案的名稱和數量。然後,我們將這個新專案新增到列表框中。

add_button.connect_clicked(move |_| {
    let dialog = gtk::Dialog::with_buttons(Some("Add Item"), Some(&window), gtk::DialogFlags::MODAL, &[("Ok", ResponseType::Ok), ("Cancel", ResponseType::Cancel)]);
    dialog.set_default_response(ResponseType::Ok);
    let content_area = dialog.content_area();
    let entry = gtk::Entry::new();
    content_area.add(&entry);
    let spin_button = gtk::SpinButton::with_range(0.0, 100.0, 1.0);
    content_area.add(&spin_button);
    dialog.connect_response(move |dialog, resp| {
        if resp == ResponseType::Ok {
            let text = entry.text();
            if !text.is_empty() {
                model.append(&RowData::new(&text, spin_button.value() as u32));
            }
        }
        dialog.close();
    });
    dialog.show_all();
});

刪除按鈕的功能

當刪除按鈕被點選時,我們需要刪除選中的專案。

delete_button.connect_clicked(move |_| {
    let selected = listbox.selected_row();
    if let Some(selected) = selected {
        let idx = selected.index();
        model.remove(idx as u32);
    }
});

組裝介面

最後,我們需要組裝介面,將滾動視窗、列表框、新增按鈕和刪除按鈕新增到主視窗中。

vbox.pack_start(&hbox, false, false, 0);
vbox.pack_start(&scrolled_window, true, true, 0);
window.add(&vbox);
window.show_all();

圖表翻譯:

  graph LR
    A[主視窗] --> B[滾動視窗]
    B --> C[列表框]
    A --> D[新增按鈕]
    A --> E[刪除按鈕]
    D --> F[對話方塊]
    F --> G[輸入框]
    F --> H[旋轉按鈕]
    E --> I[刪除選中的專案]

GTK 框架的 Rust 實作:打造桌面應用

簡介

GTK 是一個廣泛使用的 GUI 框架,尤其是在 Linux 平臺上。雖然 GTK 的檔案和資源相對較少,但仍然可以使用 Rust 來開發桌面應用。在本章中,我們將探討如何使用 GTK 框架在 Rust 中建立一個基本的桌面應用。

建立 GTK 應用

首先,需要新增 gtk 依賴到 Cargo.toml 中:

[dependencies]
gtk = { version = "0.16.0", features = ["v3_22"] }

然後,建立一個新的 Rust 專案,並新增必要的 import:

use gtk::glib;
use gtk::prelude::*;
use std::cell::RefCell;
use std::collections::HashMap;
use std::rc::Rc;

建立主介面

建立一個 build_ui 函式來構建主介面:

fn build_ui(application: &gtk::Application) {
    // 建立主視窗
    let window = gtk::Window::new(gtk::WindowType::Toplevel);
    window.set_title("Grocery List App");
    window.set_default_size(350, 70);

    // 建立垂直盒子
    let vbox = gtk::Box::new(gtk::Orientation::Vertical, 0);

    // 新增垂直盒子到主視窗
    window.add(&vbox);

    // 顯示所有元件
    window.show_all();
}

執行應用

建立一個 main 函式來執行應用:

fn main() {
    // 建立一個新的 GTK 應用
    let application = gtk::Application::new(
        Some("com.grocery_list"),
        Default::default(),
    );

    // 連線 UI 和應用
    application.connect_activate(build_ui);

    // 執行應用
    application.run(Default::default());
}

測試應用

執行 cargo run 命令來測試應用。應該會顯示一個類似於圖 7.2 的視窗。

新增專案對話方塊

建立一個 add_item_dialog 函式來新增專案對話方塊:

fn add_item_dialog(window: &gtk::Window) {
    // 建立對話方塊
    let dialog = gtk::Dialog::new(
        Some("Add Item"),
        Some(window),
        gtk::DialogFlags::Modal,
        &[
            ("_OK", gtk::ResponseType::Ok),
            ("_Cancel", gtk::ResponseType::Cancel),
        ],
    );

    // 新增對話方塊內容
    let entry = gtk::Entry::new();
    dialog.get_content_area().add(&entry);

    // 顯示對話方塊
    dialog.show_all();
}

編輯專案對話方塊

建立一個 edit_item_dialog 函式來編輯專案對話方塊:

fn edit_item_dialog(window: &gtk::Window) {
    // 建立對話方塊
    let dialog = gtk::Dialog::new(
        Some("Edit Item"),
        Some(window),
        gtk::DialogFlags::Modal,
        &[
            ("_OK", gtk::ResponseType::Ok),
            ("_Cancel", gtk::ResponseType::Cancel),
        ],
    );

    // 新增對話方塊內容
    let entry = gtk::Entry::new();
    dialog.get_content_area().add(&entry);

    // 顯示對話方塊
    dialog.show_all();
}

刪除專案

建立一個 delete_item 函式來刪除專案:

fn delete_item(window: &gtk::Window) {
    // 刪除專案邏輯
}

重點事項

  • GTK 是一個廣泛使用的 GUI 框架,尤其是在 Linux 平臺上。
  • 可以使用 Rust 來開發桌面應用。
  • 建立 GTK 應用需要新增 gtk 依賴到 Cargo.toml 中。
  • 建立主介面需要使用 build_ui 函式。
  • 執行應用需要使用 main 函式。

練習

  1. 建立一個可以建立多個視窗的應用。
  2. 建立一個具有進度條的應用,並在按下開始按鈕時啟動進度條。

答案

  1. 建立一個新專案,並新增必要的 import。然後,建立一個 build_ui 函式來構建主介面,並新增多個視窗。
  2. 建立一個新專案,並新增必要的 import。然後,建立一個 build_ui 函式來構建主介面,並新增進度條和開始按鈕。

建立多視窗應用程式的使用者介面

首先,我們需要建立一個主視窗,然後在其中新增按鈕和其他元件。以下是建立主視窗的步驟:

建立主視窗

fn create_main_window(application: &gtk::Application) -> gtk::ApplicationWindow {
    let window = gtk::ApplicationWindow::new(application);
    window.set_title("多視窗應用程式");
    window.set_default_size(400, 320);
    window.set_position(gtk::WindowPosition::Center);
    window.show_all();
    window
}

建立子視窗

fn create_sub_window(
    application: &gtk::Application,
    title: &str,
    main_window_entry: &gtk::Entry,
    id: usize,
    windows: &Rc<RefCell<HashMap<usize, glib::WeakRef<gtk::Window>>>,
) {
    let window = gtk::Window::new(gtk::WindowType::Toplevel);
    application.add_window(&window);
    window.set_title(title);
    window.set_default_size(400, 320);

    // 當子視窗關閉時,移除其對應的專案
    glib::clone!(@weak windows => @default-return Inhibit(false), move |_, _| {
        windows.borrow_mut().remove(&id);
        Inhibit(false)
    });

    // 建立按鈕,當按鈕被點選時,通知主視窗
    let button = gtk::Button::with_label(&format!("通知主視窗 ID {}", id));
    button.connect_clicked(move |_| {
        main_window_entry.buffer().set_text(&format!("子視窗 {} 被點選", id));
    });
    window.add(&button);
    window.show_all();

    // 將子視窗新增到雜湊表中
    windows.borrow_mut().insert(id, window.downgrade());
}

建立使用者介面

fn build_ui(application: &gtk::Application) {
    // 建立主視窗
    let main_window = create_main_window(application);

    // 建立子視窗
    let mut id = 0;
    let windows = Rc::new(RefCell::new(HashMap::new()));
    let main_window_entry = gtk::Entry::new();
    main_window.add(&main_window_entry);

    // 建立按鈕,當按鈕被點選時,建立新的子視窗
    let button = gtk::Button::with_label("建立新視窗");
    button.connect_clicked(move |_| {
        let title = format!("子視窗 {}", id);
        create_sub_window(application, &title, &main_window_entry, id, &windows);
        id += 1;
    });
    main_window.add(&button);

    main_window.show_all();
}

以上程式碼建立了一個多視窗應用程式,當按鈕被點選時,會建立新的子視窗,並在主視窗中顯示通知。每個子視窗都有一個按鈕,當按鈕被點選時,會通知主視窗。

建立多視窗應用程式

首先,我們需要初始化一個集合來儲存子視窗,使用 HashMap 來儲存子視窗的 ID 和對應的 gtk::Window 參照。這個 HashMap 將被包裹在 RcRefCell 中,以便於分享和修改。

let windows: Rc<RefCell<HashMap<usize, glib::WeakRef<gtk::Window>>>> =
Rc::new(RefCell::new(HashMap::new()));

接下來,我們需要建立一個主視窗,並將其儲存在 window 變數中。這個主視窗將被用來顯示通知。

let window = create_main_window(application);

為了方便地更新所有子視窗的標題,我們需要建立一個輸入欄位,允許使用者輸入新的標題。這個輸入欄位將被新增到主視窗中。

let windows_title_entry = gtk::Entry::new();
windows_title_entry.set_placeholder_text(Some("Update all sub-window's titles"));

當輸入欄位的內容發生變化時,我們需要更新所有子視窗的標題。為此,我們需要連線輸入欄位的 changed 訊號,並使用 glib::clone! 宏來捕捉 windowswindows_title_entry 的弱參照。

windows_title_entry.connect_changed(move |_| {
    let text = windows_title_entry.buffer().text();
    for window in windows.borrow().values() {
        if let Some(w) = window.upgrade() {
            w.set_title(&text);
        }
    }
});

為了顯示通知,我們需要建立一個新的輸入欄位,並將其新增到主視窗中。

let entry = gtk::Entry::new();
entry.set_editable(false);
entry.set_placeholder_text(Some("Event notifications will be sent here"));

現在,我們需要建立一個按鈕來建立新的子視窗。當按鈕被點選時,我們需要建立一個新的子視窗,並將其新增到 windows 中。

let button = gtk::Button::with_label("Create a new window");
glib::clone!(@weak windows_title_entry, @weak entry, @weak application => move |_| {
    let id = generate_new_id(&windows.borrow());
    create_sub_window(&application, &windows_title_entry.buffer().text(), &entry, id, &windows);
});

最後,我們需要將所有的部件新增到主視窗中,並顯示主視窗。

let layout = gtk::Box::new(gtk::Orientation::Vertical, 5);
layout.add(&windows_title_entry);
layout.add(&button);
layout.add(&entry);
window.add(&layout);
window.show_all();

這樣,我們就完成了多視窗應用程式的建立。當使用者點選建立新視窗按鈕時,將會建立一個新的子視窗,並將其新增到 windows 中。當子視窗被點選時,將會顯示通知在主視窗中。

GTK+ 進階教程:建立進度條應用

介紹

在這個教程中,我們將學習如何使用 GTK+ 建立一個簡單的進度條應用。這個應用將包括一個主視窗、一個進度條和一個開始按鈕。當使用者按下開始按鈕時,進度條將開始執行,並在完成時顯示一個完成視窗。

建立專案

首先,讓我們建立一個新的 GTK+ 專案。開啟終端,執行以下命令:

cargo new progress_tracker
cd progress_tracker
cargo add gtk

定義結構體

src/main.rs 中,新增以下程式碼:

use gtk::prelude::*;
use gtk::{gio, glib};
use std::cell::{RefCell, Cell};
use std::rc::Rc;
use std::thread;
use std::time::Duration;

pub struct Application {
    pub widgets: Rc<Widgets>,
}

pub struct Widgets {
    pub window: gtk::ApplicationWindow,
    pub header: Header,
    pub view_stack: gtk::Stack,
    pub main_view: MainView,
    pub complete_view: CompleteView,
}

pub struct Header {
    container: gtk::HeaderBar,
}

pub struct MainView {
    pub container: gtk::Grid,
    pub progress: gtk::ProgressBar,
    pub button: gtk::Button,
}

pub struct CompleteView {
    pub container: gtk::Grid,
}

實作 CompleteView

讓我們從 CompleteView 開始。這個視窗將顯示一個標籤,指示任務已完成。

impl CompleteView {
    pub fn new() -> Self {
        let label = gtk::Label::new(None);
        label.set_markup("Task complete");
        label.set_halign(gtk::Align::Center);
        label.set_valign(gtk::Align::Center);
        label.set_vexpand(true);
        label.set_hexpand(true);

        let container = gtk::Grid::new();
        container.set_vexpand(true);
        container.set_hexpand(true);
        container.add(&label);

        Self { container }
    }
}

實作 MainView

現在,讓我們實作 MainView。這個視窗將包含一個進度條和一個開始按鈕。

impl MainView {
    pub fn new() -> Self {
        let progress = gtk::ProgressBar::new();
        progress.set_text(Some("Progress Bar"));
        progress.set_show_text(true);
        progress.set_hexpand(true);

        let button = gtk::Button::new();
        button.set_label("start");
        button.set_halign(gtk::Align::Center);

        let container = gtk::Grid::new();
        container.attach(&progress, 0, 0, 1, 1);
        container.attach(&button, 0, 1, 1, 1);

        Self {
            container,
            progress,
            button,
        }
    }
}

建立主視窗

現在,讓我們建立主視窗。

fn main() {
    // 建立 GTK+ 應用
    let application = gtk::Application::new(
        Some("com.example.progress-tracker"),
        Default::default(),
    );

    // 建立主視窗
    let window = gtk::ApplicationWindow::new(application, Default::default());
    window.set_title("Progress Tracker");
    window.set_default_size(300, 200);

    // 建立 Header
    let header = Header {
        container: gtk::HeaderBar::new(),
    };

    // 建立 MainView 和 CompleteView
    let main_view = MainView::new();
    let complete_view = CompleteView::new();

    // 建立 view_stack
    let view_stack = gtk::Stack::new();
    view_stack.add(&main_view.container);
    view_stack.add(&complete_view.container);

    // 建立 Widgets
    let widgets = Widgets {
        window: window.clone(),
        header,
        view_stack,
        main_view,
        complete_view,
    };

    // 建立 Application
    let application = Application {
        widgets: Rc::new(widgets),
    };

    // 顯示主視窗
    window.show_all();
}

執行應用

現在,讓我們執行應用。

cargo run

這將啟動主視窗,顯示進度條和開始按鈕。當你按下開始按鈕時,進度條將開始執行,並在完成時顯示完成視窗。

圖表翻譯:

  graph LR
    A[主視窗] --> B[進度條]
    B --> C[開始按鈕]
    C --> D[完成視窗]

這個圖表顯示了主視窗、進度條、開始按鈕和完成視窗之間的關係。

內容解密:

這個應用使用 GTK+ 建立了一個簡單的進度條介面。當使用者按下開始按鈕時,進度條將開始執行,並在完成時顯示完成視窗。這個應用使用了 GTK+ 的 gtk::ProgressBargtk::Button 類別來建立進度條和開始按鈕。

GTK+ 應用程式開發:進度條和視窗管理

在本節中,我們將深入探討如何使用 GTK+ 建立一個具有進度條和視窗管理的應用程式。首先,我們需要建立一個 MainView 來管理我們的應用程式的主視窗。

從使用者經驗視角來看,使用 Rust 與 GTK+ 框架構建桌面應用程式,雖然能提供高度客製化的介面設計和效能最佳化,但開發過程仍面臨一些挑戰。本文深入剖析了 GTK+ 框架在 Rust 中的應用,涵蓋了滾動視窗、列表框、按鈕、多視窗管理以及進度條等核心元件的建立與整合。透過程式碼範例與圖表,逐步闡明瞭 UI 構建的流程與邏輯。

分析 GTK+ 的 Rust 生態可以發現,雖然社群持續發展,但相關教學資源和檔案仍相對匱乏,開發者需要花費更多時間探索和解決問題。此外,GTK+ 的訊號與回呼機制雖然強大,但也增加了程式碼的複雜度,尤其在處理多視窗和非同步操作時,需要更謹慎地管理狀態和資源,避免潛在的記憶體洩漏或效能瓶頸。對於習慣物件導向程式設計的開發者而言,適應 Rust 的所有權和借用系統也需要一定的學習曲線。

展望未來,隨著 Rust 語言的普及和 GTK+ 框架的持續演進,預期會有更多工具和函式函式庫出現,簡化 GTK+ 應用程式的開發流程。跨平臺支援和更完善的開發者工具將是未來發展的重點。而結合 Rust 的安全性與 GTK+ 的跨平臺特性,可望在桌面應用程式開發領域開創新的局面。

對於有意使用 Rust 和 GTK+ 開發桌面應用程式的團隊,玄貓建議優先關注核心功能的實作,並逐步探索 GTK+ 的進階特性。同時,積極參與社群討論和貢獻程式碼,將有助於加速 GTK+ 在 Rust 生態的成熟,並提升整體開發體驗。