GTK+ 2.0 教學-事件處理

我們已經討論了 GTK 信號中的進階的事件,如選單項目被選取。然而,有時學習一些低級的事件也是有好用的,如滑鼠移動或按一個鍵。在 GTK 中有信號與這些低級事件聯繫。這些信號的處理函式有額外的參數,該函式是一個結構指標,包含事件的訊息。例如,傳遞給移動事件處理函式的參數是一個 GdkEventMotion 型別的結構指標,如下:

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

type會設置為事件的型別,如移動事件是GDK_MOTION_NOTIFY,window是發生事件的視窗。xy給出事件的座標。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_MASKGDK_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;
}

感謝你看到這裡,很快就可以離開了,但最好的獎勵行動就是按一下幫我分享或留言,感恩喔~

點我分享到Facebook

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *