Событие отрисовки C/GTK+3 для нескольких виджетов

В настоящее время я пытаюсь изучить GTK + 3/cairo, используя C. Я написал небольшое приложение, которое рисует грань датчика и стрелку в области рисования gtk, используя cairo.

До сих пор это работало хорошо, я пытался создать несколько областей рисования (несколько датчиков), которые имеют одну и ту же функцию обратного вызова для события рисования, это также хорошо работает со статическими значениями.

Теперь мой вопрос: я хотел бы иметь возможность рисовать несколько датчиков, каждый со своими значениями. Но как мне узнать в моей функции обратного вызова, какой датчик (область рисования) испустил сигнал отрисовки? Я предполагаю, что это также включает в себя то, как я «создаю и храню» данные для датчиков, чтобы их свойства можно было получить из обратного вызова отрисовки.

Вероятно, мне следует создать структуру, содержащую данные для датчика, и создать несколько таких. Это то, что я понял, просматривая различные проекты, использующие GTK, но это слишком сложно для меня, чтобы полностью понять, как это работает.

Вот как я создаю области рисования:

gaugearea1 = gtk_drawing_area_new();
gtk_box_pack_start(GTK_BOX(hbox), gaugearea1, FALSE, FALSE, 5);
gtk_widget_set_size_request(gaugearea1, 300, 300);
gtk_widget_realize(gaugearea1);
g_signal_connect(gaugearea1, "draw", G_CALLBACK(draw_event), NULL);

И функция обратного вызова написана так, довольно стандартно.

static gboolean draw_event(GtkWidget *widget, cairo_t *cr)
{
    GdkWindow *win;
    win = gtk_widget_get_window(widget);

    // Draw all arcs/lines using cr
}

Буду очень признателен за любые советы о том, как подойти к чему-то подобному.


person MaggoT    schedule 29.06.2012    source источник


Ответы (1)


GtkDrawingArea, который излучает сигнал, — это просто widget, которое вы получаете в своем обратном вызове. Просто приведите его к соответствующему типу, если это необходимо.

Но внимательно посмотрите на документы из сигнала draw:

Сигнал розыгрыша

gboolean     user_function                  (GtkWidget    *widget,
                                            CairoContext *cr,
                                            gpointer      user_data) : Run Last

В вашей функции draw_event отсутствует последний параметр, gpointer user_data.

И это использование последнего параметра NULL вызова g_signal_connect(). Таким образом, вы можете поместить здесь указатель на структуру со всеми необходимыми данными.

Или вы можете использовать функцию g_object_set_data() для прикрепления указателя к виджету, но я бы не рекомендовал это для такого простого приложения.

Если у вас фиксированное количество датчиков, все хорошо: просто создайте такое же количество структур, но если у вас динамическое количество датчиков, вам придется создавать структуры в голове, поэтому возникает новая проблема: когда вы освобождаетесь? данные? Ответ находится в g_signal_connect_data(): эта функция получает дополнительный обратный вызов, который вызывается, когда структура больше не нужна.

Что-то вроде следующего:

struct GaugeData
{
    /* your data here */
};
static void gauge_data_free(gpointer ptr, GClosure *clo)
{
    struct GaugeData *data = ptr;
    /* free extra resources, if needed */
    g_free(data);
}
static gboolean draw_event(GtkWidget *widget, cairo_t *cr, gpointer ptr)
{
    struct GaugeData *data = ptr;

    // Draw all arcs/lines using cr and data
}

void CreateOneGauge()
{
    gaugearea1 = gtk_drawing_area_new();
    struct GaugeData *data = g_new(GaugeData, 1);
    /* init the data */

    /* ... */
    g_signal_connect_data(gaugearea1, "draw", G_CALLBACK(draw_event), 
                data, gauge_data_free, 0);
}
person rodrigo    schedule 29.06.2012
comment
Спасибо за отличный ответ, я пока буду использовать фиксированное количество датчиков и позже посмотрю на их динамическое создание. - person MaggoT; 01.07.2012