GTK使用Glade将用户数据传递回调

时间:2013-07-31 10:32:56

标签: c callback gtk signals glade

我注意到Glade只允许你设置一个在GTK回调的用户数据部分内传递的对象。

有没有办法可以传递一个整数值?

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

注意: 我的所有信号都是用glade_xml_signal_autoconnect自动设置的,我会按照这种方式保留它。

1 个答案:

答案 0 :(得分:4)

在我看来,关于林间空地和回调,人们不得不告别将一个元素传递给回调的概念。因此,请停止使用glade以配置传递给特定回调的用户数据。

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

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

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

此外,我创建了一个辅助宏,它定义了一个指向已经实例化的元素的指针,该元素具有其glade id的名称。这样,代码中的元素定义始终与glade中显示的元素定义同步。这使得小部件的重命名变得非常容易(替换所有来源中的名称+ 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>
相关问题