GTK+ 2.0 Tutorial

<<< Previous

Scribble, A Simple Example Drawing Program

Next >>>


Виджет DrawingArea и рисование

Теперь мы приступаем к процессу рисования на экране. Виджет, который мы используем для этого, называется DrawingArea (область рисования). Виджет области рисования - по существу простое окно X. Это чистый холст в котором мы можем рисовать то, что мы хотим. Область рисования создаётся вызовом:

GtkWidget* gtk_drawing_area_new        (void);

Размер виджета по умолчанию может быть определён вызовом:

void       gtk_drawing_area_size       (GtkDrawingArea      *darea,
                                        gint                 width,
                                        gint                 height);

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

Должно быть отмечено, что, когда мы создаем виджет DrawingArea, мы полностью ответственны за отрисовку содержимого. Если наше окно скрыто, то при открытии мы получаем событие экспозиции и должны перерисовать то, что было предварительно скрыто.

Необходимо помнить все, что было нарисовано на экране, чтобы можно было должным образом перерисовать. Кроме того, может быть визуально неприятно, если части окна очищаются и перерисовываются шаг за шагом. Решение этой проблемы заключается в использовании закадрового backing pixmap. Вместо того, чтобы рисовать непосредственно на экране, мы рисуем на изображении сохраненном в памяти сервера, но не отображенном на экране, затем когда изменения изображения или новые части изображения отображены, мы копируем необходимые части на экран.

Создание закадрового pixmap, выполняется функцией:

GdkPixmap* gdk_pixmap_new               (GdkWindow  *window,
                                         gint        width,
                                         gint        height,
                                         gint        depth);

Параметр window определяет окно GDK, от которого этот pixmap берет некоторые из его свойств. width и height определяет размер pixmap. depth определяет глубину цвета (color depth) - число битов в пикселах, для нового окна. Если глубина будет определена как -1, то будет соответствовать глубине window.

Мы создаем pixmap в нашем "configure_event" обработчике. Это событие производится всякий раз, когда окно изменяет размер, включая первоначальное создание.

/* Резервирование pixmap для области рисования */
static GdkPixmap *pixmap = NULL;
/* Создание нового backing pixmap соответствующего размера */
static gint
configure_event (GtkWidget *widget, GdkEventConfigure *event)
{
  if (pixmap)
    gdk_pixmap_unref(pixmap);
  pixmap = gdk_pixmap_new(widget->window,
                          widget->allocation.width,
                          widget->allocation.height,
                          -1);
  gdk_draw_rectangle (pixmap,
                      widget->style->white_gc,
                      TRUE,
                      0, 0,
                      widget->allocation.width,
                      widget->allocation.height);
  return TRUE;
}

Функция gdk_draw_rectangle() очищает pixmap инициализируя белым. Подробней об этом чуть ниже.

Наш обработчик события экспонирования просто копирует необходимую часть pixmap на дисплей (мы определяем область, которую должны перерисовать при использовании события->площадь области для события экспонирования):

/* Перерисовываем экран используя backing pixmap */
static gint
expose_event (GtkWidget *widget, GdkEventExpose *event)
{
  gdk_draw_pixmap(widget->window,
                  widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
                  pixmap,
                  event->area.x, event->area.y,
                  event->area.x, event->area.y,
                  event->area.width, event->area.height);
  return FALSE;
}

Теперь мы знаем как применить обновление экрана используя нашу карту пикселей (pixmap), но как фактически рисовать необходимый нам материал на нашей карте (pixmap)? Есть большое количество вызовов в библиотеках GTK's GDK для рисования drawables. drawable - это просто что то, что может быть нарисовано. Это может быть окно, карта пикселей, или точечный рисунок (черно-белое изображение). Мы уже видели два таких вызова выше gdk_draw_rectangle() и gdk_draw_pixmap(). Вот полный список:

gdk_draw_line ()
gdk_draw_rectangle ()
gdk_draw_arc ()
gdk_draw_polygon ()
gdk_draw_string ()
gdk_draw_text ()
gdk_draw_pixmap ()
gdk_draw_bitmap ()
gdk_draw_image ()
gdk_draw_points ()
gdk_draw_segments ()

См. справочную документацию или файл заголовка <gdk/gdk.h> для получения детальной информации относительно этих функций. Все эти функции имеют те же самые два аргумента. Первый параметр - drawable, второй параметр - graphics context (GC).

Графический контекст инкапсулирует информацию о цвете переднего и заднего фона, а также о толщине линии. GDK имеет полный набор функций для того, чтобы создавать и изменять графические контексты , но сохраняя простоту, мы будем использовать только предопределенные графические контексты. Каждый виджет имеет связанный стиль. (Который может быть изменен в gtkrc файле, см. раздел GTK's rc file.) Он, между прочим, хранит множество графического контекста. Некоторые примеры доступа к этим графическим контекстам:

widget->style->white_gc
widget->style->black_gc
widget->style->fg_gc[GTK_STATE_NORMAL]
widget->style->bg_gc[GTK_WIDGET_STATE(widget)]

Поля fg_gc, bg_gc, dark_gc, и light_gc индексированы параметром типа GtkStateType, который может принимать значения:

GTK_STATE_NORMAL,
GTK_STATE_ACTIVE,
GTK_STATE_PRELIGHT,
GTK_STATE_SELECTED,
GTK_STATE_INSENSITIVE

Например, для GTK_STATE_SELECTED цвет символа имеет значение по умолчанию - белый, а цвет фона - темно-синий.

Наша функция draw_brush(), для фактической отрисовки на дисплей:

/* Рисуем прямоугольник на экране */
static void
draw_brush (GtkWidget *widget, gdouble x, gdouble y)
{
  GdkRectangle update_rect;
  update_rect.x = x - 5;
  update_rect.y = y - 5;
  update_rect.width = 10;
  update_rect.height = 10;
  gdk_draw_rectangle (pixmap,
                      widget->style->black_gc,
                      TRUE,
                      update_rect.x, update_rect.y,
                      update_rect.width, update_rect.height);
  gtk_widget_draw (widget, &update_rect);
}

После того, как мы нарисовали прямоугольник перенесенный на pixmap, вызываем функцию:

void  gtk_widget_draw(GtkWidget           *widget,
                              GdkRectangle        *area);

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

Вот мы и рассмотрели всю программму для рисования, за исключением простых деталей, таких как создание основного окна.


<<< Previous

Home

Next >>>

Event Handling

Up

Adding XInput support