GTK+ 2.0 Tutorial

<<< Previous

Scribble, A Simple Example Drawing Program

Next >>>


Обработка событий

Сигналы GTK для действий высшего уровня (high-level) мы уже обсудили, такие как выбор пункта меню. Но иногда полезно использовать сигналы низкого уровня (lower-level), такие как передвижение курсора мышки, или нажатие клавиши. Обработчики для этих сигналов имеют дополнительный параметр, который является указателем на структуру, содержащую информацию о событии. Например, обработчики события движения передают указатель на структуру GdkEventMotion, которая частично выглядит так:

struct _GdkEventMotion { GdkEventType type; GdkWindow *window; guint32 time; gdouble x; gdouble y; ... guint state; ... };

type - устанавливает тип события, в этом случае GDK_MOTION_NOTIFY, window - окно в котором произошло событие. x и y - координаты произошедшего события. state - определяет состояние модификатора, когда произошло событие ( то есть определяет, какие модифицирующие клавиши и кнопки мыши были нажаты). Это - поразрядное ИЛИ, как например:

GDK_SHIFT_MASK GDK_LOCK_MASK GDK_CONTROL_MASK GDK_MOD1_MASK GDK_MOD2_MASK GDK_MOD3_MASK GDK_MOD4_MASK GDK_MOD5_MASK GDK_BUTTON1_MASK GDK_BUTTON2_MASK GDK_BUTTON3_MASK GDK_BUTTON4_MASK GDK_BUTTON5_MASK

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

void gtk_widget_set_events (GtkWidget *widget, gint events);

Вторая часть определяет события, которыми мы интересуемся. Это - поразрядное ИЛИ констант, которые определяют различные типы событий. Типы события:

GDK_EXPOSURE_MASK GDK_POINTER_MOTION_MASK GDK_POINTER_MOTION_HINT_MASK GDK_BUTTON_MOTION_MASK GDK_BUTTON1_MOTION_MASK GDK_BUTTON2_MOTION_MASK GDK_BUTTON3_MOTION_MASK GDK_BUTTON_PRESS_MASK GDK_BUTTON_RELEASE_MASK GDK_KEY_PRESS_MASK GDK_KEY_RELEASE_MASK GDK_ENTER_NOTIFY_MASK GDK_LEAVE_NOTIFY_MASK GDK_FOCUS_CHANGE_MASK GDK_STRUCTURE_MASK GDK_PROPERTY_CHANGE_MASK GDK_PROXIMITY_IN_MASK GDK_PROXIMITY_OUT_MASK

Есть несколько нюансов которые нужно соблюсти вызывая gtk_widget_set_events(). Первое - нужно вызвать эту функцию перед тем как будет создано окно X для виджета GTK. Практически вы должны вызвать функцию сразу после создания виджета. Во вторых - виджет должен иметь ассоциированное X окно(X window).  Для эффективности, многие типы виджетов не имеют собственного окна, а отображаются в окне их родителя. Вот эти виджеты:

GtkAlignment GtkArrow GtkBin GtkBox GtkImage GtkItem GtkLabel GtkPixmap GtkScrolledWindow GtkSeparator GtkTable GtkAspectFrame GtkFrame GtkVBox GtkHBox GtkVSeparator GtkHSeparator

Чтобы перехватывать события для этих виджетов, вы должны использовать виджет EventBox. См. раздел EventBox.

Для нашей программы рисования, мы должны знать, когда кнопка мыши нажата и мышь перемещена, таким образом мы определяем GDK_POINTER_MOTION_MASK и GDK_BUTTON_PRESS_MASK. Мы также должны знать, когда перерисовать наше окно, таким образом мы определяем GDK_EXPOSURE_MASK. Хотя нам необходимо уведомление об изменении размера нашего окна через конфигурацию события, мы не должны определять передачу флага GDK_STRUCTURE_MASK, потому что это автоматически определено для всех окон.

Однако, как оказывается есть проблема с определением GDK_POINTER_MOTION_MASK. Это заставит сервер добавлять новое событие передвижения в очередь событий каждый раз, когда пользователь перемещает мышь. Вообразите, что нам требуется 0.1 секунды, чтобы обработать событие движения, но в очередь X сервера поступает новое событие передвижения каждые 0.05 секунды. Если пользователь рисовал в течении 5 секунд, нам потребуется ещё 5 секунд для перехвата после освобождения кнопки мышки! Но нам нужно получить только одно событие движения для каждого случая, который мы обрабатываем. Это можно сделать определив GDK_POINTER_MOTION_HINT_MASK.

Мы определяем GDK_POINTER_MOTION_HINT_MASK, когда сервер в первый раз сообщает нам о передвижении указателя после входа в наше окно, или после нажатия или освобождения кнопки. Последующие события движения будут подавлены, пока мы явно не спросим положение указателя, используя функцию:

GdkWindow* gdk_window_get_pointer (GdkWindow *window, gint *x, gint *y, GdkModifierType *mask);

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

Код установки событий для нашего окна выглядит так:

gtk_signal_connect (GTK_OBJECT (drawing_area), "expose_event", (GtkSignalFunc) expose_event, NULL); gtk_signal_connect (GTK_OBJECT(drawing_area),"configure_event", (GtkSignalFunc) configure_event, NULL); gtk_signal_connect (GTK_OBJECT (drawing_area), "motion_notify_event", (GtkSignalFunc) motion_notify_event, NULL); gtk_signal_connect (GTK_OBJECT (drawing_area), "button_press_event", (GtkSignalFunc) button_press_event, NULL); gtk_widget_set_events (drawing_area, GDK_EXPOSURE_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_BUTTON_PRESS_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK);

Мы сохраним "expose_event" и "configure_event" обработчики позже. Обработчики "motion_notify_event" и "button_press_event" довольно просты:

static gint button_press_event (GtkWidget *widget, GdkEventButton *event) { if (event->button == 1 && pixmap != NULL) draw_brush (widget, event->x, event->y); return TRUE; } static gint motion_notify_event (GtkWidget *widget, GdkEventMotion *event) { int x, y; GdkModifierType state; if (event->is_hint) gdk_window_get_pointer (event->window, &x, &y, &state); else { x = event->x; y = event->y; state = event->state; } if (state & GDK_BUTTON1_MASK && pixmap != NULL) draw_brush (widget, x, y); return TRUE; }

<<< Previous

Home

Next >>>

Scribble, A Simple Example Drawing Program

Up

The DrawingArea Widget, And Drawing