【问题标题】:GTK pass user data to callback using GladeGTK 使用 Glade 将用户数据传递给回调
【发布时间】:2013-07-31 10:32:55
【问题描述】:

我注意到 Glade 只允许您设置要在 GTK 回调的用户数据部分中传递的对象。

有什么方法可以传递一个整数值吗?

我有一组菜单项,我想指向同一个回调函数,但是对于一小部分代码,我需要确定哪个菜单项是调用回调的菜单项。

注意: 我所有的信号都是使用 glade_xml_signal_autoconnect 自动设置的,我更愿意保持这种方式。

【问题讨论】:

    标签: c callback gtk signals glade


    【解决方案1】:

    在我看来,关于 glade 和回调,我们必须告别仅将一个元素传递给回调的概念。因此停止使用glade 以配置传递给特定回调的用户数据。

    我习惯于将一个名为 App 的结构传递给所有回调,其中包含指向从 ui 定义文件加载并已由 GtkBuilder 实例化的每个 ui 元素的指针。这也停止了 glade 在小部件之间来回单击以设置回调的用户数据的繁琐任务。除了 ui 元素之外,大多数情况下,此结构还包含在应用程序级别运行时很重要的其他元素。

    这种方法的一个好处是,您不会想如何实现一个单独的回调,该回调通常会作用于多个元素。有些人通过将感兴趣的小部件分组到一个容器中来解决这个问题。为了将所有小部件传递给回调,它们只需传递容器。然后在回调中,他们通过调用gtk_container_get_children 或类似函数来获取小部件。这种方法使回调难以辨认,并减少了编辑代码时的乐趣。

    如果每个回调都包含应在运行时操作的所有可用元素,则您不必关心实现单个回调,因为每个回调共享相同的结构。

    此外,我创建了一个辅助宏,它定义了一个指向已实例化元素的指针,该元素的名称为它的 glade id。这样,代码中的元素定义总是与 glade 中显示的元素同步。这使得小部件的重命名非常容易(替换所有源中的名称 + 林间空地文件)。

    为了说明这种方法,我附加了一个示例程序的文件。不要害怕文件的数量。将程序划分为逻辑单元/模块使编程变得更加简单。为了快速查看整个项目,我在 github 上创建了一个 git 存储库:

    https://github.com/o8i12398z12h9h/gtk-sample-app

    只需将我的callbacks.c 与您的回调文件进行比较即可。我很想知道它们在易读性和结构方面的比较情况,并考虑到您可能仍然记住 glade 的元素 ID。


    callbacks.c:

    #include "app.h"
    
    void
    button1_clicked_cb (GtkButton * button, App * app)
    {
        GET_UI_ELEMENT (GtkEntry, entry1);
    
        if (gtk_entry_get_text_length (entry1) == 0)
            gtk_entry_set_text (entry1, "test");
        else
            gtk_entry_set_text (entry1, "");
    }
    
    void
    button2_clicked_cb (GtkButton * button, App * app)
    {
        gboolean active;
    
        GET_UI_ELEMENT (GtkSpinner, spinner1);
        GET_UI_ELEMENT (GtkWidget, eventbox1);
    
        g_object_get (G_OBJECT (spinner1), "active", &active,
                      NULL);
    
        if (active) {
            gtk_spinner_stop (spinner1);
            gtk_widget_override_background_color (eventbox1,
                                                  GTK_STATE_FLAG_NORMAL,
                                                  app->
                                                  active_color);
        }
        else {
            gtk_spinner_start (spinner1);
            gtk_widget_override_background_color (eventbox1,
                                                  GTK_STATE_FLAG_NORMAL,
                                                  app->
                                                  inactive_color);
        }
    }
    
    void
    button3_clicked_cb (GtkButton * button, App * app)
    {
        GdkRGBA bg = { 0, 0, 1, 1 };
    
        GET_UI_ELEMENT (GtkWidget, eventbox1);
    
        gtk_widget_override_background_color (eventbox1,
                                              GTK_STATE_FLAG_NORMAL,
                                              &bg);
    }
    
    void
    button4_clicked_cb (GtkButton * button, App * app)
    {
        const gchar *str;
    
        GET_UI_ELEMENT (GtkLabel, label1);
    
        str = gtk_label_get_text (label1);
    
        if (strcmp (str, "label") == 0) {
            gtk_label_set_text (label1, "NewText");
        }
        else {
            gtk_label_set_text (label1, "label");
        }
    }
    
    void
    button5_clicked_cb (GtkButton * button, App * app)
    {
        GET_UI_ELEMENT (GtkWidget, button1);
        GET_UI_ELEMENT (GtkWidget, button2);
        GET_UI_ELEMENT (GtkWidget, button4);
    
        g_signal_emit_by_name (button1, "clicked", app);
        g_signal_emit_by_name (button2, "clicked", app);
        g_signal_emit_by_name (button4, "clicked", app);
    }
    

    main.c:

    #include "app.h"
    
    int
    main (int argc, char *argv[])
    {
        App *app;
    
        app = (App *) g_new (App, 1);
    
        gtk_init (&argc, &argv);
    
        app_init (app);
    
        GET_UI_ELEMENT (GtkWidget, window1);
    
        gtk_widget_show_all (window1);
    
        gtk_main ();
    
        return 0;
    }
    

    app.c:

    #include "app.h"
    
    GObject *
    app_get_ui_element (App * app, const gchar * name)
    {
        const gchar *s;
        GSList *list;
    
        list = app->objects;
    
        do {
            s = gtk_buildable_get_name (list->data);
    
            if (strcmp (s, name) == 0) {
                return list->data;
            }
    
        } while (list = g_slist_next (list));
    
        return NULL;
    }
    
    void
    app_init_colors (App * app)
    {
        GdkRGBA active_color = { 1, 0, 0, 1 };
        GdkRGBA inactive_color = { 0, 1, 0, 1 };
    
        app->active_color = g_new0 (GdkRGBA, 1);
        app->inactive_color = g_new0 (GdkRGBA, 1);
    
        app->active_color = gdk_rgba_copy (&active_color);
        app->inactive_color = gdk_rgba_copy (&inactive_color);
    }
    
    
    void
    app_init (App * app)
    {
        GError *err = NULL;
    
        app->definitions = gtk_builder_new ();
    
        gtk_builder_add_from_file (app->definitions,
                                   UI_DEFINITIONS_FILE, &err);
    
        if (err != NULL) {
            g_printerr
                ("Error while loading app definitions file: %s\n",
                 err->message);
            g_error_free (err);
            gtk_main_quit ();
        }
    
        gtk_builder_connect_signals (app->definitions, app);
    
        app->objects = gtk_builder_get_objects (app->definitions);
    
        app_init_colors (app);
    }
    

    app.h:

    #ifndef __APP__
    #define __APP__
    
    #include <gtk/gtk.h>
    
    #define UI_DEFINITIONS_FILE "ui.glade"
    
    #define GET_UI_ELEMENT(TYPE, ELEMENT)   TYPE *ELEMENT = (TYPE *) \
                                                app_get_ui_element(app, #ELEMENT);
    
    typedef struct app_
    {
        GtkBuilder *definitions;
        GSList *objects;
    
        GdkRGBA *active_color;
        GdkRGBA *inactive_color;
    
    } App;
    
    void app_init (App * );
    GObject * app_get_ui_element (App * , const gchar * );
    
    #endif
    

    ui.glade:

    <?xml version="1.0" encoding="UTF-8"?>
    <interface>
      <!-- interface-requires gtk+ 3.0 -->
      <object class="GtkWindow" id="window1">
        <property name="can_focus">False</property>
        <property name="border_width">20</property>
        <signal name="destroy" handler="gtk_main_quit" swapped="no"/>
        <child>
          <object class="GtkGrid" id="grid1">
            <property name="visible">True</property>
            <property name="can_focus">False</property>
            <property name="row_spacing">10</property>
            <property name="column_spacing">20</property>
            <child>
              <object class="GtkSpinner" id="spinner1">
                <property name="visible">True</property>
                <property name="can_focus">False</property>
              </object>
              <packing>
                <property name="left_attach">1</property>
                <property name="top_attach">1</property>
                <property name="width">2</property>
                <property name="height">1</property>
              </packing>
            </child>
            <child>
              <object class="GtkEventBox" id="eventbox1">
                <property name="height_request">50</property>
                <property name="visible">True</property>
                <property name="can_focus">False</property>
                <child>
                  <placeholder/>
                </child>
              </object>
              <packing>
                <property name="left_attach">1</property>
                <property name="top_attach">2</property>
                <property name="width">2</property>
                <property name="height">1</property>
              </packing>
            </child>
            <child>
              <object class="GtkEntry" id="entry1">
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="invisible_char">•</property>
                <property name="invisible_char_set">True</property>
              </object>
              <packing>
                <property name="left_attach">1</property>
                <property name="top_attach">0</property>
                <property name="width">2</property>
                <property name="height">1</property>
              </packing>
            </child>
            <child>
              <object class="GtkLabel" id="label1">
                <property name="visible">True</property>
                <property name="can_focus">False</property>
                <property name="label" translatable="yes">label</property>
              </object>
              <packing>
                <property name="left_attach">1</property>
                <property name="top_attach">3</property>
                <property name="width">2</property>
                <property name="height">1</property>
              </packing>
            </child>
            <child>
              <object class="GtkButtonBox" id="buttonbox1">
                <property name="visible">True</property>
                <property name="can_focus">False</property>
                <property name="spacing">10</property>
                <property name="layout_style">center</property>
                <child>
                  <object class="GtkButton" id="button1">
                    <property name="label" translatable="yes">toggle entry</property>
                    <property name="use_action_appearance">False</property>
                    <property name="visible">True</property>
                    <property name="can_focus">True</property>
                    <property name="receives_default">True</property>
                    <property name="use_action_appearance">False</property>
                    <signal name="clicked" handler="button1_clicked_cb" swapped="no"/>
                  </object>
                  <packing>
                    <property name="expand">False</property>
                    <property name="fill">True</property>
                    <property name="position">0</property>
                  </packing>
                </child>
                <child>
                  <object class="GtkButton" id="button2">
                    <property name="label" translatable="yes">toggle spinner + bg</property>
                    <property name="use_action_appearance">False</property>
                    <property name="visible">True</property>
                    <property name="can_focus">True</property>
                    <property name="receives_default">True</property>
                    <property name="use_action_appearance">False</property>
                    <signal name="clicked" handler="button2_clicked_cb" swapped="no"/>
                  </object>
                  <packing>
                    <property name="expand">False</property>
                    <property name="fill">True</property>
                    <property name="position">1</property>
                  </packing>
                </child>
                <child>
                  <object class="GtkButton" id="button3">
                    <property name="label" translatable="yes">set bg</property>
                    <property name="use_action_appearance">False</property>
                    <property name="visible">True</property>
                    <property name="can_focus">True</property>
                    <property name="receives_default">True</property>
                    <property name="use_action_appearance">False</property>
                    <signal name="clicked" handler="button3_clicked_cb" swapped="no"/>
                  </object>
                  <packing>
                    <property name="expand">False</property>
                    <property name="fill">True</property>
                    <property name="position">2</property>
                  </packing>
                </child>
                <child>
                  <object class="GtkButton" id="button4">
                    <property name="label" translatable="yes">toggle label</property>
                    <property name="use_action_appearance">False</property>
                    <property name="visible">True</property>
                    <property name="can_focus">True</property>
                    <property name="receives_default">True</property>
                    <property name="use_action_appearance">False</property>
                    <signal name="clicked" handler="button4_clicked_cb" swapped="no"/>
                  </object>
                  <packing>
                    <property name="expand">False</property>
                    <property name="fill">True</property>
                    <property name="position">3</property>
                  </packing>
                </child>
                <child>
                  <object class="GtkButton" id="button5">
                    <property name="label" translatable="yes">toggle everything</property>
                    <property name="use_action_appearance">False</property>
                    <property name="visible">True</property>
                    <property name="can_focus">True</property>
                    <property name="receives_default">True</property>
                    <property name="use_action_appearance">False</property>
                    <signal name="clicked" handler="button5_clicked_cb" swapped="no"/>
                  </object>
                  <packing>
                    <property name="expand">False</property>
                    <property name="fill">True</property>
                    <property name="position">4</property>
                  </packing>
                </child>
              </object>
              <packing>
                <property name="left_attach">0</property>
                <property name="top_attach">5</property>
                <property name="width">4</property>
                <property name="height">1</property>
              </packing>
            </child>
            <child>
              <object class="GtkSeparator" id="separator1">
                <property name="visible">True</property>
                <property name="can_focus">False</property>
              </object>
              <packing>
                <property name="left_attach">0</property>
                <property name="top_attach">4</property>
                <property name="width">4</property>
                <property name="height">1</property>
              </packing>
            </child>
            <child>
              <placeholder/>
            </child>
            <child>
              <placeholder/>
            </child>
            <child>
              <placeholder/>
            </child>
            <child>
              <placeholder/>
            </child>
            <child>
              <placeholder/>
            </child>
            <child>
              <placeholder/>
            </child>
            <child>
              <placeholder/>
            </child>
            <child>
              <placeholder/>
            </child>
          </object>
        </child>
      </object>
    </interface>
    

    【讨论】:

    • 感谢您提供如此完整的答案。正如你所说的那样,我什至没有考虑过这样做,与你的相比,我的 callback.c 文件是一团糟。我已经下载了您提供的代码,我将使用它作为编写一些更简洁的代码和重新考虑我的回调的基础。再次,非常感谢。
    • 你的答案太复杂了,我看不出它比简单的gtk_builder_connect_signals()更好。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-06-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多