我注意到 Glade 只允许您设置要在 GTK 回调的用户数据部分中传递的对象。
有什么办法可以传递一个整数值吗?
我有一组菜单项,我想指向同一个回调函数,但是对于一小部分代码,我需要确定哪个菜单项是调用回调的菜单项。
注意:我所有的信号都是使用 glade_xml_signal_autoconnect 自动设置的,我更愿意保持这种方式。
在我看来,关于空地和回调,我们必须告别仅将一个元素传递给回调的概念。因此停止使用glade
以配置传递给特定回调的用户数据。
我习惯于将一个名为的结构传递App
给所有回调,其中包含指向从 ui 定义文件加载并已由GtkBuilder
. 这也停止了glade
在小部件之间来回单击以设置回调的用户数据的繁琐任务。除了 ui 元素之外,大多数情况下,此结构还包含在应用程序级别运行时重要的其他元素。
这种方法的一个好处是,您不会想如何实现一个单独的回调,该回调应该作用于多个元素,这通常是这种情况。有些人通过将感兴趣的小部件分组到一个容器中来解决这个问题。为了将所有小部件传递给回调,它们只需传递容器。然后在回调中,他们通过调用gtk_container_get_children
或类似函数获取小部件。这种方法使回调难以辨认并减少了编辑代码时的乐趣。
如果每个回调都具有应在运行时操作的所有可用元素,则您不必关心实现单个回调,因为每个回调共享相同的结构。
此外,我创建了一个辅助宏,它定义了一个指向已实例化元素的指针,其名称为 glade id。这样,代码中的元素定义总是与 glade 中显示的元素同步。这使得小部件的重命名非常容易(替换所有源中的名称 + 林间空地文件)。
为了说明这种方法,我附加了一个示例程序的文件。不要害怕文件的数量。将程序划分为逻辑单元/模块使编程变得更加简单。为了快速查看整个项目,我在 github 上创建了一个 git 存储库:
只需将我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>