Sunday, December 25, 2011

Draw A Simple Rectangle Using GTK


本想用GTK繪製一些幾何地圖,然而發現網路上多只有Reference,而實作類似的簡易GTK程式不多,於是寫了一個當作練習。

其中GtkWidget *window表示最外層的視窗,GtkWidget *drawingArea被包在window裡面,為可以畫畫的地方(這裡在上面畫一個Rectangle)。


#include <"gtk/gtk.h">

// Three main elements of this program.
GtkWidget *window;
GtkWidget *drawingArea;
GdkPixmap *pixMap;

// Functions
static void createWindow();
static void createDrawingArea();
static void layoutSetting();
static void show();

static void draw();

// Event handlers
static gboolean expose_event(GtkWidget *widget, GdkEventExpose *event);
static gboolean configure_event(GtkWidget *widget, GdkEventConfigure *event);
static gboolean delete_event(GtkWidget *widget, GdkEvent *event, gpointer data);
static void destroy(GtkWidget *widget, gpointer data);

/*
 * Main Function
 */
int main(int argc, char **argv){

 gtk_init(&argc, &argv);

 //initializations
 createWindow();
 createDrawingArea();

 //setup layout
 layoutSetting();

 //show
 show();
 draw();

 gtk_main();

 return 0;
}

/*
 * Function : createWindow
 * Setup window as a new gtk_window, and connect the "delete_event" and
 * "destroy" signal to handlers.
 */
static void createWindow(){

 window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
 gtk_container_set_border_width(GTK_CONTAINER(window), 40);
 gtk_window_set_title(GTK_WINDOW(window), "Blocking Practice");

 //event handling
 g_signal_connect(window, "delete-event", G_CALLBACK(delete_event), NULL);
 g_signal_connect(window, "destroy", G_CALLBACK(destroy), NULL);

}

/*
 * Function : createDrawingArea
 * Setup the drawingArea, and connect the "expose_event" and "configure_event"
 * events to their handlers.
 */
static void createDrawingArea(){

 drawingArea = gtk_drawing_area_new();

 //event handling
 gtk_signal_connect(GTK_OBJECT(drawingArea), "expose_event",
   (GtkSignalFunc)expose_event, NULL);
 gtk_signal_connect(GTK_OBJECT(drawingArea), "configure_event",
   (GtkSignalFunc)configure_event, NULL);

 gtk_widget_set_events(drawingArea, GDK_EXPOSURE_MASK);

 //setup size
 gtk_drawing_area_size(drawingArea, 600, 600);

}

/*
 * Function : layoutSetting
 * Contain "drawingArea" into "window".
 */
static void layoutSetting(){
 gtk_container_add(GTK_CONTAINER(window), drawingArea);
}

/*
 * Function : show
 * Show the elements.
 */
static void show(){
 gtk_widget_show(drawingArea);
 gtk_widget_show(window);
}

/*
 * Function : delete_event
 * Event handler for the "delete_event" from "window"
 */
static gboolean delete_event(GtkWidget *widget, GdkEvent *event, gpointer data){
 g_print("Quit!\n:");
 gtk_main_quit();
 return FALSE;
}

/*
 * Function : destroy
 * Event handler for the "destroy" from "window"
 */
static void destroy(GtkWidget *widget, gpointer data){
 gtk_main_quit();
}

/*
 * Function : draw
 * Draw a sample rectangle located at (100,150) with 20-width and 
 * 30-height.
 */
static void draw(){

 //setup a gdk rectangle
 GdkRectangle rect;
 rect.x = 100;
 rect.y = 150;
 rect.width = 20;
 rect.height = 30;

 //draw on "drawingArea"
 gdk_draw_rectangle(pixMap,
   drawingArea->style->white_gc,
   TRUE,
   rect.x, rect.y,
   rect.width, rect.height);
 gtk_widget_queue_draw_area(drawingArea, rect.x, rect.y, rect.width, rect.height);

}

/*
 * Function : expose_event
 * Event handler for the "expose_event" from "drawingArea"
 */
static gboolean expose_event(GtkWidget *widget, GdkEventExpose *event){
 gdk_draw_drawable(widget->window,
   widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
   pixMap,
   event->area.x, event->area.y,
   event->area.x, event->area.y,
   event->area.width, event->area.height);
 return FALSE;
}

/*
 * Function : configure_event
 * Event handler for the "configure_event" from "drawingArea"
 */
static gboolean configure_event(GtkWidget *widget, GdkEventConfigure *event){

 if(pixMap) g_object_unref(pixMap);

 pixMap = gdk_pixmap_new(widget->window,
   widget->allocation.width,
   widget->allocation.height,
   -1);

 gdk_draw_rectangle(pixMap,
   widget->style->black_gc,
   TRUE,
   0, 0,
   widget->allocation.width,
   widget->allocation.height);

 return TRUE;

}

3 comments:

  1. 先吐嘈 coding style:
    1. 函式命名小寫底限分隔還是駱駝背,選一個一致使用, i.e.
    不要又 expose_event 又 createWindow
    Linux 下 C 程式,小寫底限分隔為主流(受 GNU 與 Linux Kernel 影響)

    2. 函式開始的大括號,寫在下一行
    不要再『發明』自己的風格。重點是從主流的 Linux kernel 或 GNU coding style 中選一個一致使用,不同程式語言如 C, C++, Java 與 Javascript coding style 不要混。

    你的 show() 內可用 gtk_widget_show_all:
    http://developer.gnome.org/gtk/2.24/GtkWidget.html#gtk-widget-show-all

    程式寫了很長,還沒寫到 OpenGL 啊
    GtkDrawingArea 的範例,你執行 gtk-demo 就有一個

    ReplyDelete
  2. 謝謝學長的建議,關於命名、括號我會改正,另外OpenGL一事是糟糕的筆誤,已經修改。

    ReplyDelete
  3. 我有一個用 gtk 與 cairo 話螢幕上鍵盤的 Python 程式:
    http://itrs.tw/hg/keyshow/file/567100a3b03f/itrs/keyshow.py
    給你參考一下。這是幾年前寫的,有些地方不建議你模仿,但畫圖的滿單純的

    ReplyDelete