How do I pass multiple variables as data with gtk signals
我有一个小程序,其中 gtk 信号回调函数需要 2 或 3 个变量。
我不想制作这些全局变量(项目的整个目标是整洁),我不想制作一个完整的结构,这样我就可以发送一个小部件和一个编译的正则表达式一个函数。
据我所知,g_signal_connect 只允许单个数据变量。
最有效的方法可能是指向所讨论的两个对象的 void 指针数组吗?像这样?
1 2 3 4
| void * data[2];
data[0] = widget;
data[1] = compiledregex;
g_signal_connect(save,"clicked",G_CALLBACK(callbackfunction),data); |
当然你可以使用空指针数组,但是如果你想传递不同类型的值(尤其是类型长于sizeof(void *)的值),你不能使用数组。对于这些,您几乎肯定希望将它们package在一个结构中并将结构的地址作为数据参数传递。
示例:
1 2 3 4 5
| struct my_struct *data = malloc(sizeof(*data ));
data ->field_one = value_one ;
data ->field_two = value_two ; /* etc. */
g_signal_connect (save ,"clicked", callback , data ); |
当然,不要忘记在回调函数中添加free(data)(假设它是一次性使用的)。
编辑:因为您想要一个带有 void ** 的示例,所以在这里(这很难看,我不建议您使用它——因为为原始类型分配单元素数组会浪费您的射击或因为将非指针投射到 void * 是不好的做法......):
1 2 3 4 5 6 7
| void **data = malloc(sizeof(data [0]) * n_elements );
type1 *element1_ptr = malloc(sizeof(first_item ));
*element1_ptr = first_item ;
data [0] = element1_ptr ;
/* etc. */ |
释放他们:
1 2 3 4 5
| int i ;
for (i = 0; i < n_elements ; i ++)
free(data [i ]);
free(data ); |
- 但是指针不总是相同的大小吗?我不能只将指针传递给数组中的变量并在回调函数中取消引用它们吗?
-
@JV 在实践中,指针的大小相同,因此 sizeof(void *) == sizeof(char *) == sizeof(whatever *),但这不能由 C 保证。
-
此外,为每个单个值的指针加上 ecnlosing 数组调用 malloc() 会很麻烦 - 最好只使用一次(因为内存分配是一项昂贵的操作)。
-
我仍然宁愿不使用结构,您可以在答案中添加一个 void ** 示例吗?
-
从这个示例中,您确实应该能够使用 void ** 对其进行返工。
-
我遇到了段错误,我不知道为什么......我认为我的阵列在信号连接后被破坏......
-
@JV 您是否在使用 malloc() (正如我告诉您的那样)?如果不是,那很自然 - 自动变量,包括局部数组,在它们的范围被撕裂时失效(例如从函数返回)。
-
哦,我明白了,我将不得不 malloc 正则表达式以及数组(不是小部件,反正由 Gtk 处理) - 是的,我明白为什么结构会是有益的。尽管如此,它仍然需要您定义结构以供一次性使用。在某处不存在鄙视这一点的最佳实践吗?
-
@JV 被称为面向对象的编程设计 :) (也可能是哈希表)
-
最后一个问题。为什么我们将结构初始化为指针而不是将其初始化为结构并稍后在回调中引用它?
-
@JV你的意思是struct mystruct data; data.value = 1; g_signal_connect(..., &data)?
-
对,就是这样。他们不应该有相同的结果吗?
-
@JV 我刚刚向你解释过。自动变量在创建它们的函数返回时被释放。
-
但是你不能 malloc 结构本身而不是它的指针,还是不可能?
-
不,不是。你会怎么做?
-
无论如何,你为什么要分配"结构本身"? g_signal_connect 的第三个参数足够大,只能容纳一个指针,而不是整个结构......
您可以使用 g_object_set_data() 和 g_object_get_data()。首先,设置数据:
1 2
| g_object_set_data(G_OBJECT(my_edit),"my_label", my_label);
g_object_set_data(G_OBJECT(my_edit),"age", GINT_TO_POINTER(age)); |
在回调中你可以像这样获取数据:
1 2
| gint age = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widget),"age"));
GtkWidget *my_label = g_object_get_data(G_OBJECT(widget),"my_label"); |
扩展 H2CO3 的答案,您还可以将数据(顺便说一下,我强烈建议使用结构)设置为在信号处理程序断开连接时自动释放:
1
| g_signal_connect_data (save ,"clicked", callback , data , (GClosureNotify )free, 0); |