设置(Settings)

我们现在已经学会了多种处理状态的方法。 但是,每次我们关闭应用程序时,它都消失了。 让我们学习如何通过在 gio::Settings 中存储 Switch 的状态来使用它。

一开始,我们必须创建一个GSchema xml 文件,以描述我们的应用程序计划在设置中存储的数据类型。

文件名:listings/settings/1/org.gtk_rs.Settings1.gschema.xml

<?xml version="1.0" encoding="utf-8"?>
<schemalist>
  <schema id="org.gtk_rs.Settings1" path="/org/gtk_rs/Settings1/">
    <key name="is-switch-enabled" type="b">
      <default>false</default>
      <summary>Default switch state</summary>
    </key>
  </schema>
</schemalist>

让我们一步一步来。 这个id与我们在创建应用程序时使用的应用程序 ID 相同。

文件名:listings/settings/1/main.rs

use gio::Settings;
use gtk::prelude::*;
use gtk::{gio, glib, Align, Application, ApplicationWindow, Switch};

const APP_ID: &str = "org.gtk_rs.Settings1";

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) {
    // Initialize settings
    let settings = Settings::new(APP_ID);

    // Get the last switch state from the settings
    let is_switch_enabled = settings.boolean("is-switch-enabled");

    // Create a switch
    let switch = Switch::builder()
        .margin_top(48)
        .margin_bottom(48)
        .margin_start(48)
        .margin_end(48)
        .valign(Align::Center)
        .halign(Align::Center)
        .state(is_switch_enabled)
        .build();

    switch.connect_state_set(move |_, is_enabled| {
        // Save changed switch state in the settings
        settings
            .set_boolean("is-switch-enabled", is_enabled)
            .expect("Could not set setting.");
        // Allow to invoke other event handlers
        glib::Propagation::Proceed
    });

    // Create a window
    let window = ApplicationWindow::builder()
        .application(app)
        .title("My GTK App")
        .child(&switch)
        .build();

    // Present window
    window.present();
}

path 必须以正斜杠字符 ('/') 开头和结尾,并且不能包含两个连续的斜杠字符。 创建 path 时,我们建议使用id,将 '.' 替换为 '/',并在开头和结尾添加 '/'。

我们只想存储一个name为 "is-switch-enabled" 的键。 这是一个布尔值,因此其类型为 "b"(其他选项请参见 GVariant Format Strings)。 我们还将其默认值设为 false(完整语法请参见 GVariant Text Format)。 最后,我们添加一个摘要。

现在,我们需要复制并编译 schema.

在 Linux 或 macOS 机器上执行以下命令即可安装 schema:

mkdir -p $HOME/.local/share/glib-2.0/schemas
cp org.gtk_rs.Settings1.gschema.xml $HOME/.local/share/glib-2.0/schemas/
glib-compile-schemas $HOME/.local/share/glib-2.0/schemas/

或者在 Windows 上运行:

mkdir C:/ProgramData/glib-2.0/schemas/
cp org.gtk_rs.Settings1.gschema.xml C:/ProgramData/glib-2.0/schemas/
glib-compile-schemas C:/ProgramData/glib-2.0/schemas/

我们通过指定应用程序 ID 来初始化 Settings 对象。

文件名:listings/settings/1/main.rs

use gio::Settings;
use gtk::prelude::*;
use gtk::{gio, glib, Align, Application, ApplicationWindow, Switch};

const APP_ID: &str = "org.gtk_rs.Settings1";

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) {
    // Initialize settings
    let settings = Settings::new(APP_ID);

    // Get the last switch state from the settings
    let is_switch_enabled = settings.boolean("is-switch-enabled");

    // Create a switch
    let switch = Switch::builder()
        .margin_top(48)
        .margin_bottom(48)
        .margin_start(48)
        .margin_end(48)
        .valign(Align::Center)
        .halign(Align::Center)
        .state(is_switch_enabled)
        .build();

    switch.connect_state_set(move |_, is_enabled| {
        // Save changed switch state in the settings
        settings
            .set_boolean("is-switch-enabled", is_enabled)
            .expect("Could not set setting.");
        // Allow to invoke other event handlers
        glib::Propagation::Proceed
    });

    // Create a window
    let window = ApplicationWindow::builder()
        .application(app)
        .title("My GTK App")
        .child(&switch)
        .build();

    // Present window
    window.present();
}

然后我们获取设置密钥,并在创建 Switch 时使用它。

文件名:listings/settings/1/main.rs

use gio::Settings;
use gtk::prelude::*;
use gtk::{gio, glib, Align, Application, ApplicationWindow, Switch};

const APP_ID: &str = "org.gtk_rs.Settings1";

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) {
    // Initialize settings
    let settings = Settings::new(APP_ID);

    // Get the last switch state from the settings
    let is_switch_enabled = settings.boolean("is-switch-enabled");

    // Create a switch
    let switch = Switch::builder()
        .margin_top(48)
        .margin_bottom(48)
        .margin_start(48)
        .margin_end(48)
        .valign(Align::Center)
        .halign(Align::Center)
        .state(is_switch_enabled)
        .build();

    switch.connect_state_set(move |_, is_enabled| {
        // Save changed switch state in the settings
        settings
            .set_boolean("is-switch-enabled", is_enabled)
            .expect("Could not set setting.");
        // Allow to invoke other event handlers
        glib::Propagation::Proceed
    });

    // Create a window
    let window = ApplicationWindow::builder()
        .application(app)
        .title("My GTK App")
        .child(&switch)
        .build();

    // Present window
    window.present();
}

最后,我们保证,只要点击开关,开关状态就会保存在设置中。

文件名:listings/settings/1/main.rs

use gio::Settings;
use gtk::prelude::*;
use gtk::{gio, glib, Align, Application, ApplicationWindow, Switch};

const APP_ID: &str = "org.gtk_rs.Settings1";

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) {
    // Initialize settings
    let settings = Settings::new(APP_ID);

    // Get the last switch state from the settings
    let is_switch_enabled = settings.boolean("is-switch-enabled");

    // Create a switch
    let switch = Switch::builder()
        .margin_top(48)
        .margin_bottom(48)
        .margin_start(48)
        .margin_end(48)
        .valign(Align::Center)
        .halign(Align::Center)
        .state(is_switch_enabled)
        .build();

    switch.connect_state_set(move |_, is_enabled| {
        // Save changed switch state in the settings
        settings
            .set_boolean("is-switch-enabled", is_enabled)
            .expect("Could not set setting.");
        // Allow to invoke other event handlers
        glib::Propagation::Proceed
    });

    // Create a window
    let window = ApplicationWindow::builder()
        .application(app)
        .title("My GTK App")
        .child(&switch)
        .build();

    // Present window
    window.present();
}

现在,即使关闭应用程序,Switch 也会保留其状态。 但我们可以做得更好。 开关有一个属性 "active",而 Settings 允许我们将属性与特定设置绑定。 因此,我们就来做这件事。

我们可以删除初始化 Switch 之前的 boolean 调用以及 connect_state_set 调用。 然后,我们通过指定键、对象和属性名称将设置绑定到属性。

文件名:listings/settings/2/main.rs

use gio::Settings;
use gtk::prelude::*;
use gtk::{gio, glib, Align, Application, ApplicationWindow, Switch};

const APP_ID: &str = "org.gtk_rs.Settings2";

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) {
    // Initialize settings
    let settings = Settings::new(APP_ID);

    // Create a switch
    let switch = Switch::builder()
        .margin_top(48)
        .margin_bottom(48)
        .margin_start(48)
        .margin_end(48)
        .valign(Align::Center)
        .halign(Align::Center)
        .build();

    settings
        .bind("is-switch-enabled", &switch, "active")
        .build();

    // Create a window
    let window = ApplicationWindow::builder()
        .application(app)
        .title("My GTK App")
        .child(&switch)
        .build();

    // Present window
    window.present();
}

只要有一个属性与一个设置很好地对应,你可能就会想把它绑定到设置上。 在其他情况下,通过 getter 和 setter 方法与设置交互往往是正确的选择。