GTK+ 2.0 Tutorial |
||
---|---|---|
Scribble, A Simple Example Drawing Program |
Сигналы 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;
}
|
Scribble, A Simple Example Drawing Program |
The DrawingArea Widget, And Drawing |