我们已经讨论了 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指出事件发生时的状态(按下了那个修正键或滑鼠键)。它是如下值的位元OR:
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); |
第二个参数为我们感兴趣的事件。它为不同类型事件的位元OR。事件型别的列表如下:
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; } |
1 則留言
Comments are closed.