首页 > 其他分享 >GUI development with Rust and GTK4 阅读笔记

GUI development with Rust and GTK4 阅读笔记

时间:2024-04-09 14:55:05浏览次数:24  
标签:development GTK4 GUI number label state template action parameter

简记

这是我第二次从头开始阅读,有第一次的印象要容易不少。

如果只关心具体的做法,而不思考为什么这样做,以及整体的框架,阅读的过程将会举步维艰。

简略记录 gtk-rs 的书中提到的点。对同一个问题书中所演示了多种处理方法,而且跨度比较大,第一次阅读的时候经常出现忘记之前的内容。

fn signals() -> &'static [Signal] {
	static SIGNALS: OnceLock<Vec<Signal>> = OnceLock::new();
	SIGNALS.get_or_init(|| {
		vec![Signal::builder("max-number-reached")
			.param_types([i32::static_type()])
			.build()]
	})
}
signals:
max-number-reached(i32)

GTK GObject 的缺点

  • Reference cycles
  • Not thread safe

使用异步块/函数一般是通过 glib::spawn_future_local() spawn.

异步函数之间的通信是使用 async_channel

有些异步库依赖 tokio runtime 不能直接通过 GLib 的主线程 spawn。 通过这样一个函数返回的 runtime 来 spawn。

use std::sync::OnceLock;
use tokio::runtime::Runtime;

fn runtime() -> &'static Runtime {
    static RUNTIME: OnceLock<Runtime> = OnceLock::new();
    RUNTIME.get_or_init(|| {
        Runtime::new().expect("Setting up tokio runtime needs to succeed.")
    })
}

设置(Settings)

GTK 应用的设置是通过一个 .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>

设置是在应用关闭后再打开,仍然保持关闭前的状态。这个 GSchema xml 需要编译安装。

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/

settings 的状态绑定到 widget,是调用 settings 的 bind() 方法。把某个属性绑定到某一个属性是通过源对象 bind_property() 方法

第8章 Saving Window State 还是使用 GTK 的设置,只不过在 GTK 的对象系统中使用了自己定义的方法(在 Rust 中我分不出哪个是class structinstance struct,不过我知道状态放在 imp.rs,方法一般是 mod.rs ,如果实际写的时候放在一个文件里面就是把 imp.rs 中的内容放到对应的 mod.rs 并且用 mod imp {} 包裹)。

List Widgets

当 ListView 创建 ListItem 的时候会调 factory.connect_setup 注册的回调,
factory.connect_bind 是将 Model 中的数据绑定到单独的 List Item,

绑定数据到 Widget:

  • 设置 Widget 的效果
    label.set_label(&integer_object.number().to_string());
  • 将数据的属性绑定到 Widget 的属性
integer_object
	.bind_property("number", &label, "label")
	.sync_create()
	.build();
  • connect_setup 里面完成绑定,可以解决上一个解决方案的问题
list_item
	.property_expression("item")
	.chain_property::<IntegerObject>("number")
	.bind(&label, "label", Widget::NONE);
// 拿到 list_item 中的 item (data) 中的 number 属性,绑定到实际的 Widget

Composite Templates

除了直接编写 Rust 代码创造界面,还可以使用 XML 文件描述窗口组件。在 XML 文件里面可以使用直接在 Rust 代码中继承的组件。

如果要手动设置组件中的child的信号处理,需要在 Rust 中声明。

You use it by adding a struct member with the same name as one id attribute in the template.

#[derive(CompositeTemplate, Default)]
#[template(resource = "/org/gtk_rs/example/window.ui")]
pub struct Window {
    #[template_child]
    pub button: TemplateChild<CustomButton>,
}

在XML文件中, signal 标签声明组件的信号的处理函数。

fn class_init(klass: &mut Self::Class) {
	klass.bind_template();
	klass.bind_template_callbacks(); // 使用了 callback 就必须要有这一句
}
<object class="MyGtkAppCustomButton" id="button">
	<signal name="clicked" handler="handle_button_clicked"/>
	<property name="label">Press me!</property>
	<property name="margin-top">12</property>
	<property name="margin-bottom">12</property>
	<property name="margin-start">12</property>
	<property name="margin-end">12</property>  
</object>

而具体的回调函数在哪里定义?在 imp (模)块的结构体的 impl 中定义。

#[gtk::template_callbacks]
impl Window {
    #[template_callback]
    fn handle_button_clicked(button: &CustomButton) {
        // Set the label to "Hello World!" after the button has been clicked on
        button.set_label("Hello World!");
    }
}

In order to access the widget's state we have to add swapped="true" to the signal tag.

要访问 XML 中定义 signal 标签的组件的状态,说直白一点,就是回调函数访问 self (ui文件对应的表达状态的结构体,impl Window 的 Window 就是 self),需要在 XML 中的 signal 中添加 swapped="true"。有了swapped="true" handle_button_clicked() 的第一个参数就可以是 self。

#[gtk::template_callbacks]
impl Window {
    #[template_callback]
    fn handle_button_clicked(&self, button: &CustomButton) {
        let number_increased = self.number.get() + 1;
        self.number.set(number_increased);
        button.set_label(&number_increased.to_string())
    }
}

在 Rust 中移除继承的 GTK widget 中的 Child,通过模板中的回调函数来访问组件中的child,而不在结构体中注册 template_child,。 class_init() 里必须添加 CustomButton::ensure_type();

Actions

app.set_accels_for_action("win.close", &["<Ctrl>W"]); 设置快捷键

创建一个 Action 不只是可以接收参数还可以设置状态。而状态也可以在设置的回调函数里面获取到,并且可以改变。 ActionEntry这里的activate接收的回调函数有 3 个参数。

// Add action "count" to `window` taking an integer as parameter
let action_count = ActionEntry::builder("count")
	.parameter_type(Some(&i32::static_variant_type()))
	.state(original_state.to_variant())
	.activate(move |_, action, parameter| {
		// Get state
		let mut state = action
			.state()
			.expect("Could not get state.")
			.get::<i32>()
			.expect("The variant needs to be of type `i32`.");

		// Get parameter
		let parameter = parameter
			.expect("Could not get parameter.")
			.get::<i32>()
			.expect("The variant needs to be of type `i32`.");

		// Increase state by parameter and store state
		state += parameter;
		action.set_state(&state.to_variant());

		// Update label with new state
		label.set_label(&format!("Counter: {state}"));
	})
	.build();

用 Action 这里少了很多的绑定的操作,这里只声明按钮点击的信号处理和 Action。状态是直接将普通变量 to_variant() 转换一下,不需要声明一个属性,也不需要声明额外的信号处理不同的状态,可以直接在这个注册给 Action 的回调里面做。所以处理用户的输入不止是可以注册一个信号的处理,还可以将信号的处理再委托给 Action。

所有的 Button 都实现了 Actionable 接口,可以给 Button 指定 action_name()action_target() 是指定 Action 的参数。

在 UI (xml)文件 内部可以指定 "action-name" 和 "action-target"。只需在 Rust 中构造组件后注册对应的 Action 即可。

<object class="GtkButton" id="button">
	<property name="label">Press me!</property>
	<property name="action-name">win.count</property>
	<property name="action-target">1</property>
</object>

也就是说,读代码的时候看到 XML 文件里面有指定 action, 那么在 Rust 里面肯定是注册了这个Action处理函数的。

// setup_actions() in impl Window block of mod.rs
self.add_action_entries([action_count]);
// Trait shared by all GObjects
impl ObjectImpl for Window {
    fn constructed(&self) {
        // Call "constructed" on parent
        self.parent_constructed();

        // Add actions
        self.obj().setup_actions();
    }
}

覆盖父类的方法,在imp.rs的对应的trait的实现里实现覆盖的方法。
自定义组件方法,在mod.rs的模块的impl块里定义,自定义的 setup 的方法都在 constructed() 里面调用。

如果要创建菜单,就必须用 Action。一个菜单项符合3种描述:

  • no parameter and no state, or
  • no parameter and boolean state, or
  • string parameter and string state.
    menu 标签声明菜单的 model,在 GtkMenuButton 中指定菜单的 model, 菜单的具体行为是 action 去做的。
   <template class="MyGtkAppWindow" parent="GtkApplicationWindow">
     <property name="title">My GTK App</property>
+    <property name="width-request">360</property>
+    <child type="titlebar">
+      <object class="GtkHeaderBar">
+        <child type ="end">
+          <object class="GtkMenuButton">
+            <property name="icon-name">open-menu-symbolic</property>
+            <property name="menu-model">main-menu</property>
+          </object>
+        </child>
+      </object>
+    </child>

菜单可以很好的显示我们的有状态的action,菜单的action的状态需要通过设置来保存。

设置提供了一个 create_action 的方法可以很方便的创建 action.

标签:development,GTK4,GUI,number,label,state,template,action,parameter
From: https://www.cnblogs.com/wngtk/p/18123965

相关文章

  • 利用matlab的guide制作一个凯撒密码加解密演示界面
    第一步:在命令行窗口输入guide,回车选择新建gui如图所示,两个粉的是可编辑文本,一个按钮,三个写着字和一个白色的框是静态文本先把我们需要的这些东西都拉出来,数量记得到位,布局自己调粉色什么怎么调就自己探索一下,一般拉出来是白色双击其中一个静态文本,会弹出来一个对话框,......
  • m基于yolov2深度学习的车辆检测系统matlab仿真,带GUI操作界面
    1.算法仿真效果matlab2022a仿真结果如下:  2.算法涉及理论知识概要        YOLOv2是一种基于深度学习的实时目标检测算法,由JosephRedmon等人在论文《YOLO9000:Better,Faster,Stronger》中提出。其主要特点是将图像识别任务视为一个回归问题,网络一次前向......
  • 【MATLAB源码-第170期】基于matlab的BP神经网络股票价格预测GUI界面附带详细文档说明
    操作环境:MATLAB2022a1、算法描述基于BP神经网络的股票价格预测是一种利用人工神经网络中的反向传播(Backpropagation,简称BP)算法来预测股票市场价格变化的技术。这种方法通过模拟人脑的处理方式,尝试捕捉股票市场中的复杂非线性关系,以实现对未来股价的预测。本文将详细介绍BP......
  • 微软安全开发流程(Security Development Lifecycle, SDL)
    1.SDL简介SecurityDevelopmentLifecycle(SDL)是微软2004年提出的从安全角度指导软件开发过程的管理模式。SDL是一个安全保证的过程,其重点是软件开发,在开发的所有阶段都引入了安全和隐私的原则。SDL一直都是微软在全公司实施的强制性策略。2.SDL步骤SDL中的方法,试图从安全漏......
  • 01 GUI编程
    GUI编程告诉大家怎么学?这是什么?它怎么玩?我们该如何在平时运用?组件窗口弹窗面板文本框列表框按钮图片监听事件鼠标键盘事件1.简介GUI的核心:SwingAWT不流行的原因:1.界面不美观2.需要jre环境(太大几百兆)那为什么要学习?1.可以写出自己心中想要的一些小......
  • [工具] png图片打包plist工具,手把手教你使用pngPackerGUI_V2.0
    png图片打包plist工具,手把手教你使用pngPackerGUI_V2.0此软件是在pngpacker_V1.1软件基础之后,开发的界面化操作软件,方便不太懂命令行的小白快捷上手使用。1.下载并解压缩软件,得到如下目录,双击打开pngPackerGUI.exe 2.打开pngPackerGUI之后,默认的界面如下: 3.选择目录:选......
  • 嵌入式 GUI 简介
    目前的桌面机操作系统大多有着美观、操作方便、功能齐全的GUI(图形用户界面),例如KDE或者GNOME。GUI(图形用户界面)是指计算机与其使用者之间的对话接口,可以说,GUI是当今计算机技术的重大成就。它的存在为使用者提供了友好便利的界面,并大大地方便了非专业用户的使用,使得人们......
  • `.gitattributes` 文件中的 *.js linguist-language=ts *.tsx linguist-language=ts
    .jslinguist-language=ts和.tsxlinguist-language=ts是针对特定文件类型的元数据标记,它们的作用是在特定的上下文中指示这些文件的实际编程语言类型,而非其扩展名所暗示的语言。具体来说:.jslinguist-language=ts表示尽管文件以.js扩展名结尾,但实际上它应该被视为TypeScr......
  • m基于深度学习的肉类新鲜度检测系统matlab仿真,带GUI操作界面
    1.算法仿真效果matlab2022a仿真结果如下:  2.算法涉及理论知识概要       数据采集:获取肉类样品在不同新鲜度阶段的图像数据,通常使用高分辨率相机拍摄并标注对应的新鲜度等级。       GoogleNet模型因其独特的“inception”模块而得名,这种模块设计......
  • 如何在 Ubuntu 服务器上安装桌面环境 (GUI)
    先以VNC方式远程登录服务器执行命令sudoaptupdate&&sudoaptupgrade#选择1---使用tasksel安装sudoaptinstall-ytaskselsudotaskselinstall-yubuntu-desktop#选择2---使用apt安装sudoaptinstallubuntu-desktopsudoaptinstalllightdm安装lightdm......