foundations-of-gtk-development

gtk.jpg

更新記錄

item note
20160822 第一版
20161220 新增: GtkWidget Function,Chapter2-3 Button

目錄


GTK

  • The GIMP Toolkit (GTK+) was originally designed for a raster graphics editor called the GNU Image Manipulation Program (GIMP)
  • Three individuals, Peter Mattis, Spencer Kimball, and Josh MacDonald created GTK+ in 1997
  • GTK建立在GDK (GIMP Drawing Kit)的上層, 基本上是將Xlib功能包裝起來
    它被稱為GIMP toolkit是因為原來是寫來開發GIMP
  • GTK+ is an object-oriented application programming interface (API) written in the C programming language.

Chapter2

Chapter2-1 helloworld

hello-world.png
  • 視窗預計為200x200

  • $ cat helloworld.c

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    #include <gtk/gtk.h>

    int main(int argc,
    char *argv[])

    {

    GtkWidget *window;

    gtk_init(&argc,&argv);

    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    gtk_window_set_title (GTK_WINDOW (window), "Hello World");
    gtk_wiget_show(window);

    gtk_main();
    return 0;
    }
  • $cat Makefile

    1
    2
    all:
    gcc -Wall -g helloworld.c -o helloworld `pkg-config --cflags gtk+-2.0` `pkg-config --libs gtk+-2.0`
  • GtkWindow — Toplevel which can contain other widgets

    • gtk_window_new()
      Creates a new GtkWindow
      GTK_WINDOW_TOPLEVEL: A regular window, such as a dialog.
      GTK_WINDOW_POPUP: A special window such as a tooltip.
  • GtkWidget
    gtk_wiget_show()
  • Main loop and Events — Library initialization, main event loop, and events
    gtk_main()
    Runs the main loop until gtk_main_quit() is called.
  • 若要取得gtk_init回傳值,
    則需要使用gtk_init_check(),initialization失敗回傳FALSE,否則為TRUE

  • 其它

    • pkg-config
      pkg-config - Return metainformation about installed libraries
      1
      2
      3
      4
      5
      xx$ pkg-config --cflags gtk+-2.0
      -pthread -I/usr/include/gtk-2.0 -I/usr/lib64/gtk-2.0/include -I/usr/include/pango-1.0 -I/usr/include/atk-1.0 -I/usr/include/cairo -I/usr/include/pixman-1 -I/usr/include/libdrm -I/usr/include/gdk-pixbuf-2.0 -I/usr/include/libpng16 -I/usr/include/pango-1.0 -I/usr/include/harfbuzz -I/usr/include/pango-1.0 -I/usr/include/glib-2.0 -I/usr/lib64/glib-2.0/include -I/usr/include/freetype2 -I/usr/include/libpng16 -I/usr/include/freetype2 -I/usr/include/libpng16

      xx$ pkg-config --libs gtk+-2.0
      -lgtk-x11-2.0 -lgdk-x11-2.0 -lpangocairo-1.0 -latk-1.0 -lcairo -lgdk_pixbuf-2.0 -lgio-2.0 -lpangoft2-1.0 -lpango-1.0 -lgobject-2.0 -lglib-2.0 -lfontconfig -lfreetype
  • libraries

libraries note
-lgtk-x11-2.0 GTK+ (-lgtk): Graphical widgets
-lgdk-x11-2.0 GDK (-lgdk): The standard graphics rendering library
-latk-1.0
-lgio-2.0
-lpangoft2-1.0
-lpangocairo-1.0
-lgdk_pixbuf-2.0 GdkPixbuf (-lgdk_pixbuf): Client-side image manipulation
-lcairo
-lpango-1.0 Pango (-lpango): Font rendering and output
-lfontconfig
-lgobject-2.0 GObject (-lgobject): Object-oriented type system
-lglib-2.0 GLib (-lglib): Data types and utility functions
-lfreetype

GTK+使用GObject hierarchy system

gtk_window_new

  • 產生GtkWidget object
  • return a pointer to a GtkWidget
  • GtkWidget 继承GtkObject物件

  • Object Hierarchy

    object-hierachy-system.png
  • THe Widget hierarchy of GtkWindow

    widget-hierarchy.png
obj note
GObject fundamental type providing common attributes for all libraries based
GInitallyUnowned GInitiallyUnowned should never be accessed by the programmer, since all of its members are private.
GtkObject GtkObject is the base class for all GTK+ objects.
GtkWidget GtkWidget is an abstract base class for all GTK+ widgets
GtkContainer GtkContainer is an abstract class that is used to contain one or more widgets.
GtkBin GtkBin is another abstract class that allows a widget to contain only one child
GtkWindow GtkWindow is the standard window object you saw in LFigure 2-1

Chapter2-1 Extending Hello World

helloworld2.png
  • 新增lable
  • 新增離開功能(即按下x,會離開程式)
  • $cat helloworld2.c

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    #include <gtk/gtk.h>

    static void destroy (GtkWidget*, gpointer);
    static gboolean delete_event (GtkWidget *, GdkEvent *, gpointer);

    int main(int argc,
    char *argv[])

    {

    GtkWidget *window, *label;

    gtk_init(&argc,&argv);

    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    gtk_window_set_title (GTK_WINDOW (window), "Hello World");
    gtk_container_set_border_width (GTK_CONTAINER (window),10);
    gtk_widget_set_size_request (window, 200, 100);

    /* Connect the main window to the destory and delete-event signal */
    g_signal_connect (G_OBJECT(window), "destroy",
    G_CALLBACK(destroy), NULL);

    g_signal_connect (G_OBJECT (window), "delete_event",
    G_CALLBACK(delete_event), NULL);

    /* Create a new GtkLabel widget that is selectable */
    label = gtk_label_new ("Hello World");
    gtk_label_set_selectable (GTK_LABEL (label), TRUE);

    /* Add the label as a child widget of the window. */
    gtk_container_add (GTK_CONTAINER (window), label);
    gtk_widget_show_all (window);

    gtk_main();
    return 0;
    }

    /*Stop the GTK+ main loop function when the window is destroyed. */
    static void
    destroy (GtkWidget *window,
    gpointer data)

    {

    gtk_main_quit();
    }

    /* Return FALSE to destroy the widget. By return TRUE, you can cancel
    * a delete-event */

    static gboolean
    delete_event (GtkWidget *window,
    GdkEvent *event,
    gpointer data)

    {

    return FALSE;
    }
  • GtkLabel
    gtk_label_set_selectable()
    Selectable labels allow the user to select text from the label, for copy-and-paste.

  • GtkWidget-destroy
  • The delete-event signal
    The ::delete-event signal is emitted if a user requests that a toplevel window is closed.
    TRUE to stop other handlers from being invoked for the event.
    FALSE to propagate the event further.
    表示,回傳值決定加否trig destroy signal,當為FALSE時則會產生destroy signal

  • 關閉gtk_label_set_selectable

helloworld2-01.png
1
2
3
label = gtk_label_new (NULL);                                        
//gtk_label_set_markup(GTK_LABEL(label),"<span style=\"italic\">Hello World</span>");
gtk_label_set_markup(GTK_LABEL(label),"<span foreground='blue' size='x-large'>Hello World</span>");
helloworld2-markup-blue.png helloworld2-markup-italic.png

Signals and Callbacks

  • GTK+ is a system that relies on signals and callback functions.
  • GTK本身是個事件驅動的工具, 這意味著它會在gtk_main進入停歇狀態, 一直到一個事件發生, 並且將控制交給適當的函數來處理.

  • Connecting the Signal
    控制權的交出是由signals來決定的.
    ex.當事件發生, 諸如按下滑鼠的一個按鍵, 對應的信號會由該視窗物件所送出.

    • This function will be called when the destroy signal is emitted.
    • G_CALLBAC(funcA)即為設定funcA為接受此信號的function
      1
      2
      g_signal_connect (G_OBJECT (window), "destroy",
      G_CALLBACK (destroy), NULL);
  • There are four parameter to every g_signal_connect()

    • montired for the signal
    • name of signal you want to track
    • callback function will be call when the signal is emitted.
      1
      2
      3
      4
      gulong g_signal_connect (gpointer object,
      const gchar *sign
      GCallback handler
      gpointer data)
      ;

主動發出Signal

GTK的Signal不一定得由事件來發出,您可以主動發出Signal

  • g_signal_emit_by_name()
    you can emit a signal on an object by using its textual name
  • g_signal_stop_emission_by_name()
    stop the current emission of a signal.

Events

For example, the destroy signal is emitted on the widget,
but the delete-event event is first recognized by the underlying
GdkWindow of the widget and then emitted as a signal of the widget.

  • 第一次收到destroy訊號的為delete-event
    • The delete-event signal is emitted when the user tries to close the window.
    • The first instance of an event you encountered was delete-event
    • The first difference in the callback function is the gboolean return value.
      If TRUE is returned from an event callback, GTK+ assumes the event has already been handled and will not continue.
      By returning FALSE, you are telling GTK+ to continue handling the event.
      FALSE is the default return value for the function,
  • 當產生destroy訊號時,若delete_event回傳FALSE,GTK+將會呼叫gtk_widget_destory()

    1
    2
    3
    4
    static gboolean
    callback_function (GtkWidget *widget
    GdkEvent *event,
    gpointer data)
    ;

  • callback function會接收到GdkEvent type

  • 此GdkEventType為union型態(由GdKEventKey,GdkEventButton..等組合而成)
  • Event Structures
    • ex.如button-press-event , struct GdkEventButton
    • The type field will be one of GDK_BUTTON_PRESS, GDK_2BUTTON_PRESS, GDK_3BUTTON_PRESS, and GDK_BUTTON_RELEASE.
1
2
3
4
5
6
7
struct GdkEventButton {
GdkEventType type;
GdkWindow *window;
gint8 send_event;
guint32 time;
...
}

Chapter2-3 Button

  • gtk_widget_set_size_request (window, 200, 100);
    設定視窗最小size

  • gtk_button_new_with_mnemonic
    will initialize a new button with mnemonic label support.
    When the user presses the Alt key along with the specified accelerator key, the button will be activated
    表示按下Alt可以選到此按鈕

  • enum GtkReliefStyle

item note
GTK_RELIEF_NORMAL Draw a normal relief.
GTK_RELIEF_HALF A half relief.
GTK_RELIEF_NONE No relief.

GtkWidget Function

  • gtk_widget_destroy
    It is possible to destroy a widget by explicitly calling gtk_widget_destroy() on the object.

event test

enum signal name
GDK_NOTHING = -1
GDK_DELETE = 0 delete_event
GDK_DESTROY = 1 destory_event
GDK_EXPOSE = 2 expose_event
GDK_MOTION_NOTIFY = 3 motion_notify_event
GDK_BUTTON_PRESS = 4 button_press_event
GDK_2BUTTON_PRESS = 5 2button_press_event
  • GDK_DELETE
    the window manager has requested that the toplevel window be hidden or destroyed, usually when the user clicks on a special icon in the title bar.

  • GDK_DESTROY
    the window has been destroyed.

  • GDK_EXPOSE
    all or part of the window has become visible and needs to be redrawn.

  • 於g_signal_connect使用的名稱
    如果是”event”,則代表所有的事件

  • 若在button_press_callback,回傳TRUE
    將不會有event送到clicked(因此不會打印clicked process)

  • 測試程式
event-demo-01.png
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$ ./event_demo 
button_press_event (x:85,y:121)
clicked process
button_press_event (x:11,y:15)
clicked process
button_press_event (x:117,y:140)
clicked process
button_press_event (x:116,y:140)
button_press_event (x:116,y:140)
button_press_event (x:94,y:102)
clicked process
button_press_event (x:4,y:4)
clicked process
button_press_event (x:191,y:196)
clicked process
button_press_event (x:194,y:9)
clicked process
button_press_event (x:4,y:193)
clicked process
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include <gtk/gtk.h>

void button_clicked_callback(GtkWidget *button, gpointer data){
g_print("clicked process\n");
}

gboolean button_press_callback(
GtkWidget *button, GdkEvent *event, gpointer data){
GdkEventType type = event->type;

if(type == GDK_BUTTON_PRESS){
g_print("button_press_event (x:%d,y:%d)\n",
(gint)event->button.x, (gint) event->button.y);
}

return FALSE;
}

int main(int argc, char *argv[]){
GtkWidget *window;
GtkWidget *button;

gtk_init(&argc,&argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(window),"Hello GTK+!");

button = gtk_button_new_with_label("click me");
gtk_container_add(GTK_CONTAINER(window),button);

g_signal_connect(GTK_OBJECT(button), "event",
G_CALLBACK(button_press_callback),NULL);

g_signal_connect(GTK_OBJECT(button), "clicked",
G_CALLBACK(button_clicked_callback),NULL);

g_signal_connect(GTK_OBJECT(button), "destroy",
G_CALLBACK(gtk_main_quit),NULL);

gtk_widget_show(window);
gtk_widget_show(button);

gtk_main();

return 0;
}

如何設定滑鼠移動事件

GtkWindow預設是不接收滑鼠移動事件,您要使用gtk_widget_events()增加GDK_POINTER_MOTION遮罩,才可以捕捉滑鼠移動事件
事件遮罩(Event Mask)

  • 測試程式
motion-01.png
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <gtk/gtk.h>

gboolean motion_event_handler(
GtkWidget *widget, GdkEventMotion *event, gpointer data){

char pos[20];

sprintf(pos, "(%d, %d)", (int) event->x, (int) event->y);
gtk_window_set_title(GTK_WINDOW(widget),pos);

return FALSE;
}

int main(int argc, char *argv[]){
GtkWidget *window;

gtk_init(&argc, &argv);

window = gtk_window_new(GTK_WINDOW_TOPLEVEL);

g_signal_connect(GTK_OBJECT(window),"destory",
G_CALLBACK(gtk_main_quit),NULL);

gtk_widget_add_events(window, GDK_POINTER_MOTION_MASK);

g_signal_connect(GTK_OBJECT(window), "motion_notify_event",
G_CALLBACK(motion_event_handler),NULL);

gtk_widget_show(window);

gtk_main();

return 0;
}

GtkWidget Functions

  • 設定gtk_widget_set_sensitive為FALSE如下
    ex. gtk_widget_set_sensitive(window,FAlSE);
widget-set-sensitive.png
  • gtk_widget_destroy (GtkWidget *widget)
    destroy a widget by explicitly calling gtk_widget_destroy() on object

  • gtk_widget_set_size_request (GtkWidget *widget,gint width, gint height)
    設定最widget最小的size

  • gtk_widget_grab_focus(GtkWidget *widget)
    You can use gtk_widget_grab_focus() to force a widget to grab keyboard focus

  • gtk_widget_set_sensitive(GtkWidget *widget,gboolean sensitive)
    Often, you will want to set a widget as inactive. By calling gtk_widget_set_sensitive

設定gtk_widget_set_sensitive為FALSE如下
ex. gtk_widget_set_sensitive(window,FAlSE);

GtkWindow Functions

  • gtk_window_set_resizable
    resizing the window

  • gtk_window_set_default_size(GtkWindow *window, gint width, gint height)

  • gtk_window_move(GtkWindow *window,gint x,gint y)
    By default, the position of the window on the screen is calculated with respect to the top-left corner of the screen

  • gtk_window_set_gravity()
    This function defines the gravity of the widget, which is the point that layout calculations will consider (0, 0).
    預設左上角為(0,0),可以使用此function來更改


參考