现在的位置: 首页 > 综合 > 正文

事件处理@GTK+ 2.0 中文教程连载

2014年02月07日 ⁄ 综合 ⁄ 共 3285字 ⁄ 字号 评论关闭

事件处理

我们已经讨论了 GTK 信号中的高级的事件,如单选菜单项。然而,有时学习一些低级的事件也是有好用的,如鼠标移动或按一个键。在 GTK 中有信号与这些低级事件
联系。这些信号的处理函数有额外的参数,该函数是一个结构指针,包含事件的信息。例如,传递给移动事件处理函数的参数是一个 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()
时,有几点需注意。首先,该函数必须在一个 GTK 构件的 X 窗口创建之前调用。实际上,意味者你应该在创建一个构件之后立即调用该函数。其次,构件必须有一个相关联的 X 窗口。为了提高效益,许多构件类型没有属于自己的窗口,它们绘制在父窗口上。这些构件是:

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

为了捕获这些构件的事件,你需要使用事件盒构件。详见
事件盒

对于我们的绘图程序,我们想知道什么时候鼠标键按下和什么时候鼠标移动,因此我们要用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 >>>
涂鸦板,一个简单的绘图程序 Up 绘图区构件和绘图
【上篇】
【下篇】

抱歉!评论已关闭.