На мой взгляд, в отношении поляны и обратных вызовов приходится прощаться с понятием передачи только одного элемента обратным вызовам. И, таким образом, прекратите использование glade
, чтобы настроить данные пользователя, переданные для определенного обратного вызова.
Я привык передать структуру с именем App
ко всем обратным вызовам, содержащим указатель на каждый элемент ui, который загружается из файла определений ui и уже создан экземпляром GtkBuilder
. Это также останавливает утомительную задачу в glade
щелчка назад и вперед между виджетами только для настройки пользовательских данных обратных вызовов. В дополнение к элементам ui большую часть времени эта структура содержит дополнительные элементы, которые важны во время выполнения на уровне приложения.
Преимущество такого подхода заключается в том, что у вас не возникает соблазнов подумать о том, как реализовать индивидуальный обратный вызов, который должен действовать более чем на один элемент, что часто бывает. Некоторые люди занимаются этим, группируя интересующие виджеты в контейнер. Чтобы передать все виджеты на обратный вызов, они просто передают контейнер. Затем в обратном вызове они получают виджетов, вызывая gtk_container_get_children
или аналогичные функции. Такой подход делает обратные вызовы неразборчивыми и уменьшает удовольствие при редактировании кода.
Если каждый обратный вызов имеет все доступные элементы, которые должны управляться во время выполнения, вам не нужно заботиться о реализации одного обратного вызова, поскольку каждый обратный вызов имеет одинаковую структуру.
Далее я создал вспомогательный макрос, который определяет указатель на уже созданный элемент с именем его идентификатора поляризации. Таким образом, определения элементов в коде всегда синхронизируются с теми, которые отображаются на поляне. Это упрощает переименование виджетов (замените имя во всех источниках + файл glade).
Для иллюстрации этого подхода у меня есть прилагаемые файлы примерной программы. Не бойтесь количества файлов. Разделение программы на логические единицы/модули упрощает программирование. Чтобы получить быстрый просмотр всего проекта я создал репозиторий на GitHub:
https://github.com/o8i12398z12h9h/gtk-sample-app
Просто сравните мой callbacks.c
с файлом (ами) обратного вызова. Мне было бы интересно узнать, как они сравниваются с разборчивостью и структурой и с учетом того, что у вас могут быть элементы-идентификаторы от glade
.
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 - беспорядок. Я загрузил код, который вы предоставили, и я собираюсь использовать его в качестве основы для написания более чистого кода и переосмысления моих обратных вызовов. Еще раз большое спасибо. – Scott
Ваш ответ настолько сложный, хотя, и я не вижу, как это лучше простого 'gtk_builder_connect_signals()'. – TechZilla