我們已經討論了 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 則留言