В настоящее время я пытаюсь изучить 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
}

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

2
MaggoT 30 Июн 2012 в 00:36

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);
}
5
Community 20 Июн 2020 в 12:12
Спасибо за отличный ответ, пока я буду использовать фиксированное количество датчиков, а позже посмотрю, как создавать их динамически.
 – 
MaggoT
1 Июл 2012 в 01:19