GObject基础讲解
gtk吧
全部回复
仅看楼主
吧务
level 14
daleclack 楼主
在官方教程中有对GObject的较完整教程,但该教程仅有英文版,本贴是本人对原始教程的翻译与理解
原文地址:https://docs.gtk.org/gobject/tutorial.html
2023年03月25日 03点03分 1
吧务
level 11
[滑稽]没人呐,官方教程那真的太基础了,第一次看真的没看懂[小乖]
2023年03月30日 00点03分 2
吧务
level 14
daleclack 楼主
既然有人回复,我就更新一下[滑稽]
一:GObject类型定义
GObject利用c语言的特性实现了部分面向对象的属性,自定义的GObject分为可继承和不可继承的类型,这部分概念与面向对象编程中的继承概念类似,自定义GObject需要在头文件中通过G_DECLARE_FINAL_TYPE或者G_DECLARE_DERIVABLE_TYPE宏声明可继承或者不可继承的GObject类型
2023年04月03日 14点04分 3
吧务
level 11
[滑稽]
2023年04月04日 04点04分 4
吧务
level 11
真就更新了一下啊[滑稽]
2023年04月06日 08点04分 5
正在思考接下来更新什么[滑稽]
2023年04月09日 23点04分
level 1
官方教程看不大懂
2023年06月21日 05点06分 6
等期末周结束应该能更新
2023年06月21日 09点06分
看不懂就对了,看懂了才是怪事。写得太简略、晦涩、跳脱。预设读者懂很多基础知识。
2024年11月11日 12点11分
level 12
烂尾了么?
2023年10月26日 12点10分 7
考完研看是否有可能拯救一下吧(
2023年10月26日 13点10分
吧务
level 11
突发奇想我也想更新这个挖坑,但发现有谁会看这部分讲解呢[狂汗]语言初学者的话,那要讲的东西太多了,语言熟练使用者都能自己学的吧,夹在中间的感觉跟初学者差不多,讲的东西也挺多。考虑过像你这样直接讲怎么使用的,就怕遇到不知道怎么装库、怎么编译,还有构建系统等问题。原理方面我是不懂,只会大致怎么用而已,信号那东西太复杂了。
2023年11月01日 15点11分 8
这就是我在20年刚看官方代码的感受了,我当时也是这种夹在中间的感觉,可惜现在得考研,暂时还没法更新
2023年11月01日 22点11分
吧务
level 11
如果真的想学GObject,就先给自己灌输一个理念——GObject是简单并且有用的[升起]
说实话,样板代码写多了真的挺烦的,虽然已经用宏代替很多内容了[乖]接下来我就开更吧
2023年11月02日 10点11分 9
加油,我今年12月之后应该也能更一些的
2023年11月02日 11点11分
吧务
level 11
先从源文件名称开始,使用gobject源文件名称一般以名称空间(或模块名)+类名组成,如ly-object.h,ly-object.c。
接下来的示例,按照官方教程来进行。我先给出代码示例,然后逐步解释说明,先是头文件的内容:
1. 头文件保护,如果使用的是C99及以上的标准也可以使用#pragma once。
2. 包含头文件glib-object.h,提供用于定义新类型的宏
3. G_BEGIN_DECLS和G_END_DECLS没有任何内容,可写可不写
4. #define LY_TYPE_FILE (ly_file_get_type()),写法是名称空间(或模块名)+TYPE+类名,字母全大写使用下划线进行连接;然后是名称空间(或模块名)+类名+get_type(),字母全小写且使用下划线连接,后面的get_type这个是不能修改为其他名称的。
5. G_DECLARE_FINAL_TYPE(LyFile, ly_file, LY, FILE, GObject),C语言的规矩嘛必须先声明才能使用,这里我声明了一个类,类名称是LyFile,这里我们看看这个宏需要哪些参数。
ModuleObjName:这里是你需要声明的类的名字,类名不能以简单的如File形式,必须在签名加上名称空间(或模块名),并且采取每个单词首字母大写,各单词中间无下划线等其他字符连接;
module_obj_name: 这里是该类所有类函数的前缀,形如名称空间(或模块名)加类名,且各单词使用小写,使用下划线进行连接,包括上面第4点那个ly_file_get_type()也是这样的,这是约定俗成,所以如果编写GObject相关的内容,代码的命名风格均是如此,请牢记;
MODULE:这是名称空间(或模块名),使用全大写;
OBJECT_NAME:这是类名称,使用全大写;
ParentName:这是父类名称,这个跟java一样,GObject是所有类的父类。
6. 下面大致看看这个宏展开之后的内容,大致看看就行,不细说了
这里主要关注的内容是:
1. 内部声明的结构体名称均为完整的类名称前面加一个下划线的形式;
2. 使用宏来声明新类型的方式只有两个,分别是这里我们使用到的声明不可被继承类型的G_DECLARE_FINE_TYPE(),另一个是G_DECLARE_DERIVABLE_TYPE(),还有一个是用来声明接口的G_DECLARE_INTERFACE(),它们的参数解释都是一样的,这里需要关注的是FINAL_TYPE()已经为我们定义好了这个叫类的类(什么乱七八糟的名字啊)?也就是后面加了一个Class的结构体,如LyFileClass,这个的主要作用是存放这个类的析构函数(就是c++的那个析构函数)、set/get函数等,因为是自定义的类型,该如何释放这个类创建出来的对象所占用各种资源等这些是需要自定义的(开始扩散出去了,一下子多了好多定义啊[啊]),这个先简单提一下,不是这一块的内容。这里需要关注的是结构体需要声明名称,还要定义它的结构(啊?怎么表达)未实现的部分均需要自己另外去定义,但也不要重复定义。(这一点好乱啊)
这个宏开头声明了一个获取这个类唯一标识的函数,后面的则是方便使用宏,如可以使用g_autoptr帮助你自动释放离开作用域对象所占用的资源;还有方便类型转换,在使用时,经常会出现GObject *类型的对象或者gpointer(实际上就是void *)转换成自己的定义的或他人已经定义的类时调用,在转换时会帮你校验这个对象是否真的属于该类,即使传递NULL也没有关系。
头文件部分就先到这里吧。
2023年11月02日 11点11分 10
擦,忘了放宏实际替换的内容的图片了,楼下见
2023年11月02日 11点11分
吧务
level 11
接10楼后续图片
2023年11月02日 11点11分 11
画质压缩有点惨啊[狂汗]
2023年11月02日 11点11分
吧务
level 11
接下来相应的c文件了,老规矩先放源码图片
1. 包含刚才写的头文件,这应该都知道吧。
2. 定义结构体内容,这里我使用FINAL_TYPE()来声明的,缺少struct _LyFile结构的内容,这部分可以放在头文件中,这样就可以将类的细节暴露给使用者,这里我们将实现放在c文件,这样就可以实现数据隐藏,这里先只讲FINAL_TYPE()。结构的第一个成员必须是父类的对象,如GObject parent,还要主要这里没有指针的那个" * "; 如果定义的是struct _LyFileClass,则第一个成员是GObjectClass parent_class; 我这里加了G_GNUC_UNUSED是给代码检查工具用,可加可不加。
3. G_DEFINE_TYPE(LyFile, ly_file, G_TYPE_OBJECT), 头文件中我们只是声明了这个类,接下来就是定义这个类,定义的方式有很多,用到时再做解释。
TN: 这里写完整的类的名称,如LyFile;
t_n: 这里跟声明时一样,写该类所有类函数的前缀,形如名称空间(或模块名)加类名,且各单词使用小写,使用下划线进行连接。声明的地方主要是给get_type()用的,这里主要时给下面的class_init()和init()用的;
T_P: 这里写父类的类型,在头文件中我们第一句就定义了LY_TYPE_FILE,LyFile的父类是GObject,按照约定俗成,这里填写G_TYPE_OBJECT。
这里不把这个宏展开看了,挺费事的。
后面就是两个init函数,注意函数前缀
class_init(LyFileClass *class),这里主要用来设置自定义的析构函数等内容的;
init(LyFile *self),这里可以说是类的构造函数;
LyFile *ly_file_new(void),这个是用来快速生成这个类的对象,要注意使用gobject一般都是生成的指针的,所有这个类的函数第一个参数也是这个类的指针,并且这个参数的名称一般是self。使用g_object_new()来生成相应类型的对象,这个函数的参数数量是不确定,可以根据需要往后填写,但最后一个参数必须是NULL,让函数知道到底要解析多少数量的参数。
2023年11月02日 12点11分 12
这里就是最简单的一个用例,明天尝试更新给类添加属性,添加方法。
2023年11月02日 12点11分
这里几乎都是样板代码,这部分的内容弄一个类就要写一遍的,可以死记硬背吧[阴险]
2023年11月02日 12点11分
@LuoYu0401 理论上应该可以
2023年11月02日 15点11分
吧务
level 11
要给类添加属性,一种是直接添加到struct _LyFile {GObject parent;}里,但一定要保证这个parent是在第一的位置上。
gobject还提供了另一种给类添加属性的方式,顺便给类加一个方法进去,先看头文件,新增了一个构造函数和一个类方法,新增的类方法跟这次讲的属性有关联
接下来是c文件,c文件增了很多样板代码,一点点往下看
先是在struct _LyFile里增加了一个id的属性,还声明定义了一个LyFilePrivate的结构体,将定义类的方式由G_DEFINE_TYPE改为了G_DEFINE_TYPE_WITH_PRIVATE.新增加的这个结构体就是用来存放类属性的,命名方式为完整类名+Private。
后面一部分的内容:
1. 定义了一个匿名枚举,开头一个PROP_0是用来占位,最后一个是用来说明这个类对外提供了多少个属性,注意是对外,也就是你可以在LyFilePrivate定义很多个属性,但你只提供一部分属性供外部get/set,剩下的则是内部使用,枚举名字任意,能清楚表达意思就行;
2. 定义了一个FILE_PROPERTY的静态变量,这个变量主要用来存储对外提供的属性的相关信息,如属性的类型、字符串名、默认值等, 注意一定要将首元素设置为NULL,第一个是保留给gobject内部使用的,这也是为什么上面需要一个PROP_0来占位的原因。
3. 之后定义了get/set两个函数,具体的内容之后在看;
4. 在class_init()中将定义好的get/set函数赋值给GObjectClass,这里的原因是gobject提供多种方式来获取或设置这些属性,那么就需要设置恰当的规则来引导gobject正确行动。目前还没有提供gobject这些属性的各种信息,接下来就是了,给上面的定义的静态变量初始化相应的元素,用到的就是g_param_spec_xxxx()这类函数,如果是属性是int类型,则使用g_param_spec_int()来初始化,第一个参数是这个属性的字符串名字,第二和第三个参数我一般都是NULL(这俩我也不太懂,nick好像是别名的意思),之后的参数不同类型也不同的解释,数值类的一般是最小值、最大值、默认值,字符串则只有一个默认值了,最后一个是属性的标志,主要用来说明这个属性的是否可读可写,是否仅在构造对象的时候初始化,是否允许发出通知(这个涉及到更后面的内容——信号)等。然后将这些属性信息提供给gobject,也就是最后一步g_object_class_install_properties()
现在回过头来看看get/set函数的实现,参数必须跟图片上的保持一致,
这部分的内容很简单:
gobject在生成对象时,自动生成一个LyFilePrivate变量,并分配相应的存储空间,G_DEFINE_TYPE_WITH_PRIVATE()给我们提供了如何访这个变量的方法,ly_file_get_instance_private(),也就是类方法前缀ly_file+get_instance_private()形式,注意这个方法的参数是LyFile对象,但get/set函数提供的参数是GObject对象,所以需要进行类型转换,不建议使用强制转换,而是使用相应的宏LY_FILE(),这个宏还会在运行时做检查,如果不能正确转换则会产生错误信息并打印在控制台,但不会退出程序。之后就是利用先前定义好的匿名枚举来筛选想要对哪个属性进行操作,get属性则将属性的设置给value,set属性则从value取出值再赋值给相应的属性,这里name属性由于是字符串,在设置时使用g_strdup(等同于strdup),会分配额外的内存,在重新设置之前需要释放掉原先的资源。
接下来看看在头文件中新增的两个函数的实现,
1. 新的构造函数,带有一个uid的参数,依然使用g_object_new()来生成,最后多了一个“uid”和uid的值,这就是给属性赋初值,并且这个是成对出现,"uid"则是这个属性的字符串名字,uid则是将要赋的初值,当然最后的NULL别忘了加上,如果还要加上name,使用g_object_new(LY_TYPE_FILE, "uid", uid, "name", "LuoYi", NULL);
2. 新增的类方法,上面的讲过了,再提个醒,类方法的第一个参数一般都是LyFile *self
等等这就完了?上面那个name分配的内存只在重新设置的时候释放,然后又申请了新的内存,当这个对象使用完,name分配的内存什么时候释放呢,gobject又不知道我们申请了这块内存啊。这里有两种方式来实现
一个使用dispose,一个使用finalize,这俩都是用来清理的,但只有finalize算是析构函数,根据官方的解释当对象使用完之后,finalize只会调用一次,并且再dispose之后,而dispose可能会被多次调用,使用dispose时就需要做校验了,不然可能会出多次释放同一块内存了,最后再加上调用父类的同名函数,来释放父类的占用的资源,我的习惯是只finalize。最后别忘了在class_init()中,将相应函数赋值给gobjectclass哦。
明天周六了还要上班,然后休息,休息,休息。周一晚上休息之前再尝试更新一下吧
2023年11月03日 14点11分 13
哦豁,发现的错误,第二张图片,也就是Private的结构体缺少一个char *name;[小乖]
2023年11月04日 09点11分
吧务
level 11
这部分也基本上是样板代码。
至于gobject为什么要搞get/set函数等一大堆东西,现在讲的内容里应该看不出来,(好家伙,忘记讲g_object_get()跟g_object_set()这俩函数了)[阴险],方便应用的话直接在struct _LyFile中定义就行了, get/set那些都有特殊用途(我也不知到该怎么解释,学gstreamer的时候这东西的用处就很大了)
楼中楼还有字数限制,那我直接更新g_object_get跟g_object_set函数吧
这个就是通用的get/set对象属性的,get/set其实跟构造函数一样,使用类似键值对形式,如g_object_set(G_OBJECT(ly_file), "uid", 111, "name", "LuoYi", NULL)这样就可以大批量的赋值,主要最后的NULL别忘了
关于get先看看官方的说明(浏览器开的暗色模式,不要介意)
这里要注意的是获取的内容如果是字符串,或者gobject对象(包括继承gobject的),使用完都需要释放资源。
2023年11月03日 14点11分 14
主要是给脚本语言自醒用的
2024年03月27日 06点03分
level 1
现在kde使用wayland gui性能越来越好了桌面体验直逼windows 如果再把细节做好点 linux会把windows的pc操作系统份额吃光
2024年03月15日 13点03分 15
希望如此吧哈哈哈
2024年03月19日 11点03分
1