1 属性
GObject系统提供属性。属性是由实例保存的值,实例是GObject的后代,它们对其他实例开放。可以通过他们的名字访问他们。
例如,GtkWindow具有"title"、“default-width”、"default-height"等属性。字符串"title"是属性的名称。属性的名称是一个字符串,以字母开头,然后是字母、数字、破折号(‘-’)或下划线(‘_’)。破折号和下划线用作分隔符,但它们不能混合使用。使用dash比使用underscore更高效。例如,“value”、"double"和"double-value"是正确的属性名。“_value”或“-value”不正确。
属性的值有各种类型。"title"属性的类型是string。"default-width"和"default-height"的类型都是int。
属性是通过GObject中定义的函数来设置和获取的。
- 属性可以通过几个GObject函数来设置。通常使用g_object_new和g_object_set。
- 属性可以通过几个GObject函数获得。通常使用G_object_get。
上面的函数属于GObject,但它们可以用于GObject的任何子类对象。下面是一个GtkWindow的例子,它是GObject的子类对象。
创建一个实例,用g_object_new设置其属性。
GtkWindow *win;
/* 创建对象的时候就给属性赋值 */
win = g_object_new (GTK_TYPE_WINDOW, "title", "Hello", "default-width", 800, "default-height", 600, NULL);
上面的例子创建了一个GtkWindow的实例并设置了属性。
- “title"属性被设置为"Hello”;
- "default-width"属性被设置为800。
- "default-height"属性被设置为600。
g_object_new的最后一个参数是NULL,表示属性列表的结束。
如果您已经创建了一个GtkWindow实例,并且希望设置其标题,则可以使用g_object_set。
GtkWindow *win;
win = g_object_new (GTK_TYPE_WINDOW, NULL);
g_object_set (win, "title", "Good bye", NULL);
可以使用g_object_get获取属性的值。
GtkWindow *win;
char *title;
int width, height;
win = g_object_new (GTK_TYPE_WINDOW, "title", "Hello", "default-width", 800, "default-height", 600, NULL);
g_object_get (win, "title", &title, "default-width", &width, "default-height", &height, NULL);
g_print ("%s, %d, %d\n", title, width, height);
g_free (title);
本节的其余部分是关于在GObject的后代中实现属性。它分为两部分。
- 注册一个属性
- 定义set_property和get_property类方法来实现g_object_set和g_object_get。
2 GParamSpec
GParamSpec是一个基本对象。
GParamSpec和GObject没有父子关系。GParamSpec保存变量信息。“ParamSpec”是“Parameter specification”的缩写。
例如,
double_property = g_param_spec_double ("value", "val", "Double value",
-G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
G_PARAM_READWRITE);
该函数创建一个GParamSpec实例,更准确地说,是一个GParamSpecDouble实例。GParamSpecDouble是GParamSpec的一个子类。
该实例包含以下信息:
- 值类型是double。函数g_param_spec_double的后缀是double,表示类型。
- 名字是"value"。
- 别称是“val”。
- 描述是"Double value"。
- 最小值为-G_MAXDOUBLE。G_MAXDOUBLE是double类型中可以保存的最大值。在GLib(2.68.1)参考手册——G_MAXDOUBLE和G_MINDOUBLE中描述。你可能认为double的最小值是G_MINDOUBLE,但事实并非如此。G_MINDOUBLE是double中可以保存的最小正值。
- 最大值为G_MAXDOUBLE。
- 默认值为0.0。
- G_PARAM_READWRITE是一个标志。G_PARAM_READWRITE表示该参数可读可写。
有关更多信息,请参阅GObject API参考。
注册GObject属性时,使用GParamSpec。这是从src/tdouble6中的tdouble.c中提取的。
#define PROP_DOUBLE 1
static GParamSpec *double_property = NULL;
static void
t_double_class_init (TDoubleClass *class) {
GObjectClass *gobject_class = G_OBJECT_CLASS (class);
gobject_class->set_property = t_double_set_property;
gobject_class->get_property = t_double_get_property;
double_property = g_param_spec_double ("value", "val", "Double value", -G_MAXDOUBLE, G_MAXDOUBLE, 0.0, G_PARAM_READWRITE);
g_object_class_install_property (gobject_class, PROP_DOUBLE, double_property);
}
double_property是一个静态变量。GParamSpec实例被分配给double_property。
g_object_class_install_property安装一个属性。该函数必须在set_property和get_property方法被覆盖后调用。这些方法将在后面解释。g_object_class_install_property函数参数是TDoubleClass类,PROP_DOUBLE(属性id)和GParamSpec实例。属性id用于标识tdouble.c中的属性。它是一个正整数。
3 重写set_property和get_property类方法
属性值因实例而异。因此,该值被存储到对象的每个实例中。
g_object_set被赋予一个值作为参数并存储该值。但是g_object_set如何知道要存储的实例?g_object_set在创建对象之前被编译。因此,g_object_set根本不知道将值存储在哪里。这部分需要由重写对象的编写者编写。
g_object_set首先检查属性和值,然后根据值创建GValue(泛型值)。它调用类中set_property指向的函数。请看下面的图表。
GObjectClass类中的set_property在GObject程序中指向g_object_do_set_property,它是通过编译gobject .c生成的。TDoubleClass结构的GObjectClass部分(它与TDoubleClass相同,因为TDoubleClass没有自己的区域)是通过复制GObjectClass的内容来初始化的。因此,TDoubleClass类中的set_property指向GObject程序中的g_object_do_set_property。但是g_object_do_set_property并不将值存储到TDouble实例。TDouble对象的编写器使t_double_set_property在TDouble .c中作为函数。并将t_double_set_property的地址赋给TDoubleClass类中的set_property。在图中用红色曲线表示。因此,g_object_set调用t_double_set_property而不是g_object_do_set_property(红色虚线曲线),该值将存储在TDouble实例中。
t_double_set_property和t_double_get_property的程序将在后面显示。
4 GValue
GValue是泛型值。GValue由类型和值组成。
类型是任何Gtype。下表显示了一些GType,但不是全部。
如果GValue值的类型为G_TYPE_DOUBLE,则可以使用g_value_get_double函数获取value。
GValue value;
value = ... ... ... (a GValue object is assigned. Its type is double.)
double v;
v = g_value_get_double (&value);
相反,可以使用g_value_set_double设置Gvalue值
g_value_set_double (value, 123.45);
5 t_double_set_property and t_double_get_property
g_object_set从属性变量中的值生成GValue。并调用类中set_property所指向的函数。函数在GObjectClass结构中声明。
struct _GObjectClass
{
... ... ...
... ... ...
/* overridable methods */
void (*set_property) (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec);
void (*get_property) (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec);
... ... ...
... ... ...
};
t_double_set_property只是从GValue值中获取值,并将其存储到TDouble实例中。
1 static void
2 t_double_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) {
3 TDouble *self = T_DOUBLE (object);
4
5 if (property_id == PROP_DOUBLE) {
6 self->value = g_value_get_double (value);
7 } else
8 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
9 }
- 3:将对象转换为TDouble对象self。
- 6:设置self->value。分配的值是通过g_value_get_double函数获得的。
用相同的方法,t_double_get_property 存储 self->value 到 GValue。
1 static void
2 t_double_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) {
3 TDouble *self = T_DOUBLE (object);
4
5 if (property_id == PROP_DOUBLE)
6 g_value_set_double (value, self->value);
7 else
8 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
9
10 }
6 notify信号
当属性被设置时,GObject发出“notify”信号。当你将“notify”信号连接到你的处理程序时,你可以指定一个细节,这是属性的名称。细节用分隔符“::”添加到信号名称中。
g_signal_connect (G_OBJECT (d1), "notify::value", G_CALLBACK (notify_cb), NULL);
如果不指定详细信息,则每当设置任何属性时都会调用处理程序。通常细节是设置好的。
通知信号并不意味着属性的值被改变了。即使设置了相同的值,它也会被触发。您可能希望仅在属性实际更改时才发出通知信号。在这种情况下,使用G_PARAM_EXPLICIT_NOTIFY标记定义GPramSpec。然后,通知信号不会自动发出。相反,您可以调用g_object_notify_by_pspec函数,在属性值实际更改时显式发出“notify”信号。
可以为属性设置setter和getter。但如果只是在setter中设置实例成员,则不会发出notify信号。
void
t_double_set_value (TDouble *d, double value) {
g_return_if_fail (T_IS_DOUBLE (d));
d->value = value; /* Just set d->value. No "notify" signal is emitted. */
}
如果用户想要捕捉“notify”信号,他们一定会感到困惑。一个解决方案是在setter中使用g_object_set。此时,即使用户使用了setter函数,也会发出notify信号。
void
t_double_set_value (TDouble *d, double value) {
g_return_if_fail (T_IS_DOUBLE (d));
g_object_set (d, "value", value, NULL); /* Use g_object_set. "notify" signal will be emitted. */
}
另一种解决方案是使用g_object_notify_by_pspec显式地发出信号。无论如何,如果你为你的属性创建setter,注意通知信号。
7 定义多个属性
如果定义了多个属性,请使用属性id数组。最好去看Gtk源文件,比如gtklabel.c。GtkLabel有18个属性。
在src/tdouble6目录中有一个例子。
翻译自:GObject tutorial