GTK下Notebook的信号绑定

发布于 2022-09-05 16:03:12 字数 206 浏览 13 评论 8

Notebook下有10个信号:change_current_page, create_window, focus_tab, move_focus_out, page_added, page_removed, page_reordered,
reorder_tab, select_page, switch_page。
这些信号我怎样也不能绑定到回调函数上,不知道哪里错了,有谁能教教我。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

傲鸠 2022-09-15 19:34:36

{:2_172:}
我明白了。
这让我想起了看Sqlite3的源码。sqlite3_exec()函数的call_back也是用同样的原理完成回调的参数传递。我的想法还是不够OPEN,以前看过的东西都没能运用起来。{:2_180:}

谢谢nketc。你给我上了一课。

ヤ经典坏疍 2022-09-15 19:27:03

本帖最后由 nketc 于 2011-03-14 21:20 编辑

回复 7# fengtom_lcdtv

    对了一半。

所以我也需要定义一个my_function(GtkNotebook *notebook, gint arg1, gpointer user_data)的函数。然后通过
g_signal_connect(GTK_OBJECT(notebook), "change_current_page", G_CALLBACK(my_function), (gpointer)(data));
来连接这个信号。

是正确的,可是下面就不对了。我们应该按照gtk文档中signal callback的原型来定义函数,然后通过 g_signal_connect注册为回调。
你又把 my_function定义成了这个样子:

  1. void my_function(GtkNotebook *widget, gpointer data)
  2. {javascript:;
  3.        user_function(widget, data.arg1, data.user_data);    // ??????????
  4. }

复制代码就不对了。

看看gtk定义 change-current-page的地方:

  1.   notebook_signals[CHANGE_CURRENT_PAGE] =
  2.     g_signal_new (I_("change-current-page"),
  3.                   G_TYPE_FROM_CLASS (gobject_class),
  4.                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
  5.                   G_STRUCT_OFFSET (GtkNotebookClass, change_current_page),
  6.                   NULL, NULL,
  7.                   _gtk_marshal_BOOLEAN__INT,
  8.                   G_TYPE_BOOLEAN, 1,
  9.                   G_TYPE_INT);

复制代码在这个文件http://git.gnome.org/browse/gtk+/tree/gtk/gtknotebook.c的970行,其中_gtk_marshal_BOOLEAN__INT就指明了callback的原型。
在glib的http://git.gnome.org/browse/glib/tree/gobject/gclosure.c 第788行就是调用我们注册的回调函数的地方:

  1.       marshal (closure,
  2.                return_value,
  3.                n_param_values, param_values,
  4.                invocation_hint,
  5.                marshal_data);

复制代码这儿的 marshal 这个函数指针就是定义信时的_gtk_marshal_BOOLEAN__INT, _gtk_marshal_BOOLEAN__INT的代码为:

  1. 679 void
  2. 680 _gtk_marshal_BOOLEAN__INT (GClosure     *closure,
  3. 681                            GValue       *return_value G_GNUC_UNUSED,
  4. 682                            guint         n_param_values,
  5. 683                            const GValue *param_values,
  6. 684                            gpointer      invocation_hint G_GNUC_UNUSED,
  7. 685                            gpointer      marshal_data)
  8. 686 {
  9. 687   typedef gboolean (*GMarshalFunc_BOOLEAN__INT) (gpointer     data1,
  10. 688                                                  gint         arg_1,
  11. 689                                                  gpointer     data2);
  12. 690   register GMarshalFunc_BOOLEAN__INT callback;
  13. 691   register GCClosure *cc = (GCClosure*) closure;
  14. 692   register gpointer data1, data2;
  15. 693   gboolean v_return;
  16. 694
  17. 695   g_return_if_fail (return_value != NULL);
  18. 696   g_return_if_fail (n_param_values == 2);
  19. 697
  20. 698   if (G_CCLOSURE_SWAP_DATA (closure))
  21. 699     {
  22. 700       data1 = closure->data;
  23. 701       data2 = g_value_peek_pointer (param_values + 0);
  24. 702     }
  25. 703   else
  26. 704     {
  27. 705       data1 = g_value_peek_pointer (param_values + 0);
  28. 706       data2 = closure->data;
  29. 707     }
  30. 708   callback = (GMarshalFunc_BOOLEAN__INT) (marshal_data ? marshal_data : cc->callback);
  31. 709
  32. 710   v_return = callback (data1,
  33. 711                        g_marshal_value_peek_int (param_values + 1),
  34. 712                        data2);
  35. 713
  36. 714   g_value_set_boolean (return_value, v_return);
  37. 715 }

复制代码标记为 708的这一行的  callback 就是 我们用 g_signal_connect 注册的那个回调。
明白了吧?看到了把:这个callback就是按照gtk文档中 change-current-page 回调的原型调用的!

下面是个例子:

  1. #include <gtk/gtk.h>
  2. gboolean notebook_page_change (GtkNotebook *notebook, gint arg1, gpointer data);
  3. int main (int argc, char* argv[])
  4. {
  5.   GtkWidget *window;
  6.   GtkWidget *notebook;
  7.   GtkWidget *label;
  8.   gint       i;
  9.   gchar     *text;
  10.   gtk_init (&argc, &argv);
  11.   window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  12.   gtk_widget_set_size_request (window, 400, 300);
  13.   notebook = gtk_notebook_new ();
  14.   for (i=0; i<3; i++)
  15.   {
  16.     text = g_strdup_printf("hello label %02d", i + 1);
  17.     label = gtk_label_new (text);
  18.         
  19.         gtk_notebook_append_page (GTK_NOTEBOOK (notebook), label, NULL);
  20.         g_free (text);
  21.   }
  22.   g_signal_connect (notebook, "change-current-page",
  23.                     G_CALLBACK (notebook_page_change), (gpointer)255);
  24.   gtk_container_add (GTK_CONTAINER(window), notebook);
  25.   gtk_widget_show_all (window);
  26.   gtk_main ();
  27.   return 0;
  28. }
  29. gboolean
  30. notebook_page_change (GtkNotebook *notebook, gint arg1, gpointer data)
  31. {
  32.   g_print("notebook=%p arg1=%d data=%dn", notebook, arg1, (gint)data);
  33.   return FALSE;
  34. }

复制代码在 notebook_page_change加个断点,gdb一下:

  1. #0  notebook_page_change (notebook=0x8087030, arg1=1, data=0xff) at notebook.c:45
  2. #1  0x002932ab in _gtk_marshal_BOOLEAN__INT (closure=0x8086e00, return_value=0xbfffe7b0,
  3.     n_param_values=2, param_values=0x80eec98, invocation_hint=0xbfffe710, marshal_data=0x0)
  4.     at gtkmarshalers.c:710
  5. #2  0x00bc4ada in g_closure_invoke (closure=0x8086e00, return_value=0xbfffe7b0, n_param_values=2,
  6.     param_values=0x80eec98, invocation_hint=0xbfffe710) at gclosure.c:767
  7. #3  0x00bdc39d in signal_emit_unlocked_R (node=<value optimized out>, detail=<value optimized out>,
  8.     instance=0x8087030, emission_return=0xbfffe7b0, instance_and_params=0x80eec98) at gsignal.c:3252
  9. #4  0x001921c0 in gtk_binding_entry_activate (entry=0x8083798, object=0x8087030) at gtkbindings.c:653
  10. #5  0x00193880 in binding_activate (binding_set=0x8083690, entries=0x80ee550, object=0x8087030,
  11.     is_release=0, unbound=0xbfffe84c) at gtkbindings.c:1525
  12. #6  0x001939f2 in gtk_bindings_activate_list (object=0x8087030, entries=0x80ee550, is_release=0)
  13.     at gtkbindings.c:1586
  14. #7  0x00193c43 in gtk_bindings_activate_event (object=0x8087030, event=0x80e2268)
  15.     at gtkbindings.c:1671
  16. #8  0x00410054 in gtk_widget_real_key_press_event (widget=0x8087030, event=0x80e2268)
  17.     at gtkwidget.c:5731
  18. #9  0x00292342 in _gtk_marshal_BOOLEAN__BOXED (closure=0x8073bd8, return_value=0xbfffea84,
  19.     n_param_values=2, param_values=0x8097aa0, invocation_hint=0xbfffea70, marshal_data=0x410030)
  20.     at gtkmarshalers.c:85
  21. #10 0x00bc3147 in g_type_class_meta_marshal (closure=0x8073bd8, return_value=0xbfffea84,
  22.     n_param_values=2, param_values=0x8097aa0, invocation_hint=0xbfffea70, marshal_data=0xd8)
  23.     at gclosure.c:878
  24. #11 0x00bc4a00 in g_closure_invoke (closure=0x8073bd8, return_value=0xbfffea84, n_param_values=2,
  25.     param_values=0x8097aa0, invocation_hint=0xbfffea70) at gclosure.c:767
  26. #12 0x00bdc0c6 in signal_emit_unlocked_R (node=<value optimized out>, detail=<value optimized out>,
  27. ---Type <return> to continue, or q <return> to quit---

复制代码够清晰了吧。

注意:
change-current-page 这个信号需要按 Ctrl+PageDown/PageUP 才能触发。pagedown的时候 回调的第二个参数(arg1)是1,
pageup的时候是-1,不论pagedown还是pageup 第三个参数都是我们写给 g_signal_connect的最后一个:255.
另外 "change-current-page" 和 "change_current_page" gtk不做区分。

下面是运行结果:

Screenshot-4.png (11.9 KB, 下载次数: 5)

下载附件

2011-03-14 21:17 上传

予囚 2022-09-15 18:10:59

首先感谢nketc的耐心指点。
你说的有句话我理解得不清楚“gtk中定义signal的地方同时也指明了该signal回调的原型,gtk会按照这个原型调用你注册的函数。”
我们还举信号“change-current-page”的例子:
所谓“gtk中定义signal的地方同时也指明了该signal回调的原型”,我觉得是
gboolean            user_function        (GtkNotebook *notebook,
                                                        gint         arg1,
                                                        gpointer     user_data)      : Action
另外“gtk会按照这个原型调用你注册的函数。”,那么就是说GTK会按照上面的 user_function来处理信号“change-current-page”,所以我也需要定义一个my_function(GtkNotebook *notebook, gint arg1, gpointer user_data)的函数。然后通过
g_signal_connect(GTK_OBJECT(notebook), "change_current_page", G_CALLBACK(my_function), (gpointer)(data));
来连接这个信号。
我可以自定义一个结构体data,里面包括成员变量:arg1与user_data。但是后面该怎么做呢?
void my_function(GtkNotebook *widget, gpointer data)
{
       user_function(widget, data.arg1, data.user_data);    // ??????????
}
这里搞不清楚。

另外,我没有接触过Win32 SDK编程。希望你能详细讲解一下。谢谢!

太阳哥哥 2022-09-15 18:04:46

回复 5# fengtom_lcdtv

    看来你对什么是回调不是很理解。比如某个软件模块要求的回调原型是 void foo(int a, int b),你向这个软件模块注册回调的时候该模块保存了你注册的函数的地址,比如说保存到了 callback。该模块在需要的时候如下调用 (*callback)(2, 3);
   不知道这样说你明白了没有。

   gtk中定义signal的地方同时也指明了该signal回调的原型,gtk会按照这个原型调用你注册的函数。

  
    至于你说的 g_signal_connect 的时候只能给 callback传递一个参数,其实回调的参数没有一个是你传递的(你有像一般的函数调用一样调用过回调吗?),回调是gtk内部调用的,至于给回调传递什么样的参数是定义signal的时候就确定了,而参数的值根据你程序的执行情况确定。一般gtk的signal回调最后都有一个自定义的参数,利用它用户可以传递一些上下文信息。你说的只能传一个参数,就是这个自定义的参数。gtk负责把你写给g_signal_connect的最后一个参数传递给你注册的回调的最后一个形参。

    不知道你有没有接触过  Win32 SDK编程,那里面的窗口过程函数 就是一个典型的回调。

荒芜了季节 2022-09-15 08:08:30

举例来说:信号"change-current-page"
gboolean            user_function        (GtkNotebook *notebook,
                                                        gint         arg1,
                                                        gpointer     user_data)      : Action
所以,连接该信号需要传递两个参数:(arg1, user_data)。
而g_signal_connect()只能给回调函数g_callback传递一个参数。
请问该怎么解决?

惯饮孤独 2022-09-14 22:38:31

g_signal_connect 用法没错,但是signal的callback原型错了,请参考:http://library.gnome.org/devel/g ... ebook-create-window

痴情换悲伤 2022-09-14 20:16:18

g_signal_connect(GTK_OBJECT(notebook), "change_current_page", G_CALLBACK(notebook_single_check), (gpointer)(1));
        g_signal_connect(GTK_OBJECT(notebook), "create_window", G_CALLBACK(notebook_single_check), (gpointer)(2));
        g_signal_connect(GTK_OBJECT(notebook), "focus_tab", G_CALLBACK(notebook_single_check), (gpointer)(3));
        g_signal_connect(GTK_OBJECT(notebook), "move_focus_out", G_CALLBACK(notebook_single_check), (gpointer)(4));
        g_signal_connect(GTK_OBJECT(notebook), "page_added", G_CALLBACK(notebook_single_check), (gpointer)(5));
        g_signal_connect(GTK_OBJECT(notebook), "page_removed", G_CALLBACK(notebook_single_check), (gpointer)(6));
        g_signal_connect(GTK_OBJECT(notebook), "page_reordered", G_CALLBACK(notebook_single_check), (gpointer)(7));
        g_signal_connect(GTK_OBJECT(notebook), "reorder_tab", G_CALLBACK(notebook_single_check), (gpointer)();
        g_signal_connect(GTK_OBJECT(notebook), "select_page", G_CALLBACK(notebook_single_check), (gpointer)(9));
        g_signal_connect(GTK_OBJECT(notebook), "switch_page", G_CALLBACK(notebook_single_check), (gpointer)(10));

void notebook_single_check(GtkNotebook *widget, gpointer data)
{
        switch((gint)(data))
        {
                case 1:                g_print("change-current-pagen";        break;
                case 2:                g_print("create-windown";        break;
                case 3:                g_print("focus-tabn";        break;
                case 4:                g_print("move-focus-outn";        break;
                case 5:                g_print("page-addedn";        break;
                case 6:                g_print("page-removedn";        break;
                case 7:                g_print("page-reorderedn";        break;
                case 8:                g_print("reorder-tabn";        break;
                case 9:                g_print("select-pagen";        break;
                case 10:        g_print("switch-pagen";        break;
                default:        g_print("signal error %dn", (gint)(data));        break;
        }
}

用g_signal_connect连接信号,用函数void notebook_single_check(GtkNotebook *widget, gpointer data)检验连接的信号。
应该是g_signal_connect用错了吧!但我也没有找到其它适合的函数做连接信号用。
到底是哪里错了呢?

南渊 2022-09-14 05:39:39

你代码怎么写的?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文