關於drawing_area繪圖區的顯示問題

關於drawing_area繪圖區的顯示問題
http://hi.baidu.com/dalier/blog/item/20b0b101b830bbd7267fb50d.html

關鍵詞:configure_event , expose_event , gtk_drawing_area_new , gdk_draw_drawable , gtk_widget_queue_draw_area , button-press-event ,
爲了記住如何使用drawing_area,特意編寫了一個小例子。

功能:
1.在繪圖區上點擊鼠標左鍵、中鍵和右鍵,可以畫出不同的圖形;
2.按“clear"按鈕,可以重新繪製。比較簡單,主要是用來演示如何在 drawing_area上繪製圖形,
  注意在每次調用my_draw(..)之後是如何在繪圖區上顯示出來圖形的。

/* eg_drawing_area.c */
#include <gtk/gtk.h>

static GdkPixmap *pixmap = NULL;
GdkGC *my_gc_red;
GdkColor color;

int my_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->white_gc ,
                  TRUE , 0 , 0 ,
                  widget->allocation.width ,
                  widget->allocation.height);

    my_gc_red = gdk_gc_new(widget->window);
    color.red = 30000;
    color.green = 0;
    color.blue = 30000;
    gdk_gc_set_rgb_fg_color(my_gc_red , &color);    

    return 0;
}

int my_expose_event(GtkWidget *widget , GdkEventExpose *event , gpointer data){
    gdk_draw_drawable(widget->window ,
              widget->style->fg_gc[GTK_WIDGET_STATE(widget)] ,
              pixmap , 0 , 0 , 0 , 0 ,
              widget->allocation.width ,
              widget->allocation.height);    

    return 0;
}

int my_draw(GtkWidget *widget , double x , double y , int type){
    switch(type){
        case 1:
            gdk_draw_rectangle(pixmap , widget->style->black_gc, FALSE, x, y, 10, 10);
            gtk_widget_queue_draw_area(widget, x, y, 10, 10);
            break;
        case 2:
            gdk_draw_rectangle(pixmap ,my_gc_red ,TRUE,x , y , 15 , 15);
            gtk_widget_queue_draw_area(widget ,x , y , 15 , 15);
            break;
        case 3:
            gdk_draw_line(pixmap , my_gc_red ,x , y , x + 15 , y - 15);
            gtk_widget_queue_draw_area(widget ,x , y - 15 , 15 , 15);
            break;
        default:
            printf("....\n");
            break;
    }
    return 0;
}

int my_button_press_event(GtkWidget *widget , GdkEventButton *event){
      my_draw(widget , event->x , event->y , event->button);    
      printf("x=%lf y=%lf\n" , event->x , event->y);
      return 0;    
}

int my_clear(GtkWidget *widget , GtkWidget *area){
      gdk_draw_rectangle(pixmap ,
              area->style->white_gc ,
              TRUE , 0 , 0 ,
              area->allocation.width ,
              area->allocation.height);
      gtk_widget_queue_draw(area);

      return 0;
}

int my_quit(GtkWidget *widget , gpointer data){
      gtk_main_quit();
      return 0;
}

int main(int argc , char *argv[]){
      GtkWidget *window;
      GtkWidget *drawing_area;
      GtkWidget *vbox;
      GtkWidget *bt_clear;
      GtkWidget *bt_quit;

      gtk_init(&argc , &argv);
    
      window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
      gtk_widget_set_name(window , "Test drawing_area");
      gtk_widget_set_size_request(window , 300 , 200);
      gtk_container_set_border_width(GTK_CONTAINER(window) , 2);
      gtk_window_set_resizable(GTK_WINDOW(window) , FALSE);

      vbox = gtk_vbox_new(FALSE , 0);
      gtk_container_add(GTK_CONTAINER(window) , vbox);
      gtk_widget_show(vbox);
    
      g_signal_connect(G_OBJECT(window) , "destroy" ,
              G_CALLBACK(my_quit) , NULL);

      drawing_area = gtk_drawing_area_new();
      gtk_box_pack_start(GTK_BOX(vbox) , drawing_area , TRUE , TRUE , 0);
      gtk_widget_show(drawing_area);
      g_signal_connect(G_OBJECT(drawing_area) , "configure_event" ,
              G_CALLBACK(my_configure_event) , NULL);
      g_signal_connect(G_OBJECT(drawing_area) , "expose_event" ,
              G_CALLBACK(my_expose_event) , NULL);
      g_signal_connect(G_OBJECT(drawing_area) , "button_press_event" ,
              G_CALLBACK(my_button_press_event) , NULL);
      gtk_widget_set_events(drawing_area , GDK_BUTTON_PRESS_MASK);
    
      bt_clear = gtk_button_new_with_label("clear");
      gtk_box_pack_start(GTK_BOX(vbox) , bt_clear , FALSE , FALSE , 0);
      g_signal_connect(G_OBJECT(bt_clear) , "clicked" ,
              G_CALLBACK(my_clear) , drawing_area);
      gtk_widget_show(bt_clear);
    
      bt_quit = gtk_button_new_with_label("quit");
      gtk_box_pack_start(GTK_BOX(vbox) , bt_quit , FALSE , FALSE , 0);
      g_signal_connect(G_OBJECT(bt_quit) , "clicked" ,
              G_CALLBACK(my_quit) , window);
      gtk_widget_show(bt_quit);

      gtk_widget_show(window);

      gtk_main();

      return 0;
}

簡單的說,就是每次在pixmap上繪製,然後把pixmap貼到drawing_area上去。要點如下:

1.把pixmap貼到drawing_area的函數:
    gdk_draw_drawable(widget->window ,                      //目的地
        widget->style->fg_gc[GTK_WIDGET_STATE(widget)],        //剩餘部分的填充方式
        pixmap ,                                            //源
        0 , 0 ,                                             //源的左上角座標XY
        0 , 0 ,                                             //目的地的左上角座標XY
        widget->allocation.width ,                          //取源的寬
        widget->allocation.height);                         //取源的高

2.顯示drawing_area的特定區域:
    gtk_widget_queue_draw_area(widget , //這就是draw_area
        x , y ,                         //draw_area的要顯示的矩形區域的左上角座標XY
        15 , 15);                       //矩形區域的寬和高

3.顯示drawing_area的全部區域:
    gtk_widget_queue_draw(widget);

4.爲了演示方便,加入了鼠標的操作:

int my_button_press_event(GtkWidget *widget , GdkEventButton *event){
    //event->button的值1,2,3分別對應鼠標左鍵、中鍵、右鍵
      my_draw(widget , event->x , event->y , event->button);     
      printf("x=%lf y=%lf\n" , event->x , event->y);       //注意這裏的x,y是double類型的
      return 0;    
}

另外要注意的是,若想捕獲信號button-press-event的話,需要添加函數gtk_widget_set_events(drawing_area , GDK_BUTTON_PRESS_MASK)


有找到一篇類似的文章,注意在drawing_area上響應key_press_event的用法,
先獲取焦點:
    GTK_WIDGET_SET_FLAGS (drawing_area, GTK_CAN_FOCUS);
    gtk_widget_grab_focus (drawing_area);

代碼如下:

/* GTK - The GIMP Toolkit
* Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.   See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/

/*
* Modified by the GTK+ Team and others 1997-2000.   See the AUTHORS
* file for a list of people on the GTK+ Team.   See the ChangeLog
* files for a list of changes.   These files are distributed with
* GTK+ at ftp://ftp.gtk.org/pub/gtk/.
*/

//#include <config.h>
#include <stdio.h>
#include "gtk/gtk.h"

/* Backing pixmap for drawing area */

static GdkPixmap *pixmap = NULL;

/* Information about cursor */

static gint cursor_proximity = TRUE;
static gdouble cursor_x;
static gdouble cursor_y;

/* Unique ID of current device */
static GdkDevice *current_device;

/* Erase the old cursor, and/or draw a new one, if necessary */
static void
update_cursor (GtkWidget *widget,   gdouble x, gdouble y)
{
   static gint cursor_present = 0;
   gint state = !current_device->has_cursor && cursor_proximity;

   if (pixmap != NULL)
     {
       if (cursor_present && (cursor_present != state ||
         x != cursor_x || y != cursor_y))
{
    gdk_draw_drawable (widget->window,
         widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
         pixmap,
         cursor_x - 5, cursor_y - 5,
         cursor_x - 5, cursor_y - 5,
         10, 10);
}

       cursor_present = state;
       cursor_x = x;
       cursor_y = y;

       if (cursor_present)
{
    gdk_draw_rectangle (widget->window,
          widget->style->black_gc,
          TRUE,
          cursor_x - 5, cursor_y -5,
          10, 10);
}
     }
}

/* Create a new backing pixmap of the appropriate size */
static gint
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->white_gc,
         TRUE,
         0, 0,
         widget->allocation.width,
         widget->allocation.height);

   return TRUE;
}

/* Refill the screen from the backing pixmap */
static gint
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;
}

/* Draw a rectangle on the screen, size depending on pressure,
    and color on the type of device */
static void
draw_brush (GtkWidget *widget, GdkInputSource source,
      gdouble x, gdouble y, gdouble pressure)
{
   GdkGC *gc;
   GdkRectangle update_rect;

   switch (source)
     {
     case GDK_SOURCE_MOUSE:
       gc = widget->style->dark_gc[GTK_WIDGET_STATE (widget)];
       break;
     case GDK_SOURCE_PEN:
       gc = widget->style->black_gc;
       break;
     case GDK_SOURCE_ERASER:
       gc = widget->style->white_gc;
       break;
     default:
       gc = widget->style->light_gc[GTK_WIDGET_STATE (widget)];
     }

   update_rect.x = x - 10 * pressure;
   update_rect.y = y - 10 * pressure;
   update_rect.width = 20 * pressure;
   update_rect.height = 20 * pressure;
   gdk_draw_rectangle (pixmap, gc, TRUE,
         update_rect.x, update_rect.y,
         update_rect.width, update_rect.height);
   gtk_widget_queue_draw_area (widget,
          update_rect.x, update_rect.y,
          update_rect.width, update_rect.height);
   gdk_window_process_updates (widget->window, TRUE);
}

static guint32 motion_time;

static void
print_axes (GdkDevice *device, gdouble *axes)
{
   int i;
 
   if (axes)
     {
       g_print ("%s ", device->name);
      
       for (i=0; i<device->num_axes; i++)
g_print ("%g ", axes[i]);

       g_print ("\n");
     }
}

static gint
button_press_event (GtkWidget *widget, GdkEventButton *event)
{
   current_device = event->device;
   cursor_proximity = TRUE;

   if (event->button == 1 && pixmap != NULL)
     {
       gdouble pressure = 0.5;

       print_axes (event->device, event->axes);
       gdk_event_get_axis ((GdkEvent *)event, GDK_AXIS_PRESSURE, &pressure);
       draw_brush (widget, event->device->source, event->x, event->y, pressure);
      
       motion_time = event->time;
     }

   update_cursor (widget, event->x, event->y);

   return TRUE;
}

static gint
key_press_event (GtkWidget *widget, GdkEventKey *event)
{
   if ((event->keyval >= 0x20) && (event->keyval <= 0xFF))
     printf("I got a %c\n", event->keyval);
   else
     printf("I got some other key\n");

   return TRUE;
}

static gint
motion_notify_event (GtkWidget *widget, GdkEventMotion *event)
{
   GdkTimeCoord **events;
   int n_events;
   int i;

   current_device = event->device;
   cursor_proximity = TRUE;

   if (event->state & GDK_BUTTON1_MASK && pixmap != NULL)
     {
       if (gdk_device_get_history (event->device, event->window,
       motion_time, event->time,
       &events, &n_events))
{
    for (i=0; i<n_events; i++)
      {
        double x = 0, y = 0, pressure = 0.5;

        gdk_device_get_axis (event->device, events[i]->axes, GDK_AXIS_X, &x);
        gdk_device_get_axis (event->device, events[i]->axes, GDK_AXIS_Y, &y);
        gdk_device_get_axis (event->device, events[i]->axes, GDK_AXIS_PRESSURE, &pressure);
        draw_brush (widget,   event->device->source, x, y, pressure);

        print_axes (event->device, events[i]->axes);
      }
    gdk_device_free_history (events, n_events);
}
       else
{
    double pressure = 0.5;

    gdk_event_get_axis ((GdkEvent *)event, GDK_AXIS_PRESSURE, &pressure);

    draw_brush (widget,   event->device->source, event->x, event->y, pressure);
}
       motion_time = event->time;
     }

   if (event->is_hint)
     gdk_device_get_state (event->device, event->window, NULL, NULL);

   print_axes (event->device, event->axes);
   update_cursor (widget, event->x, event->y);

   return TRUE;
}

/* We track the next two events to know when we need to draw a
    cursor */

static gint
proximity_out_event (GtkWidget *widget, GdkEventProximity *event)
{
   cursor_proximity = FALSE;
   update_cursor (widget, cursor_x, cursor_y);
   return TRUE;
}

static gint
leave_notify_event (GtkWidget *widget, GdkEventCrossing *event)
{
   cursor_proximity = FALSE;
   update_cursor (widget, cursor_x, cursor_y);
   return TRUE;
}

void
input_dialog_destroy (GtkWidget *w, gpointer data)
{
   *((GtkWidget **)data) = NULL;
}

void
create_input_dialog (void)
{
   static GtkWidget *inputd = NULL;

   if (!inputd)
     {
       inputd = gtk_input_dialog_new ();

       g_signal_connect (inputd, "destroy",
    G_CALLBACK (input_dialog_destroy), &inputd);
       g_signal_connect_swapped (GTK_INPUT_DIALOG (inputd)->close_button,
            "clicked",
            G_CALLBACK (gtk_widget_hide),
            inputd);
       gtk_widget_hide (GTK_INPUT_DIALOG (inputd)->save_button);

       gtk_widget_show (inputd);
     }
   else
     {
       if (!GTK_WIDGET_MAPPED(inputd))
gtk_widget_show(inputd);
       else
gdk_window_raise(inputd->window);
     }
}

void
quit (void)
{
   gtk_main_quit ();
}

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

   GtkWidget *button;

   gtk_init (&argc, &argv);

   current_device = gdk_device_get_core_pointer ();

   window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
   gtk_widget_set_name (window, "Test Input");

   vbox = gtk_vbox_new (FALSE, 0);
   gtk_container_add (GTK_CONTAINER (window), vbox);
   gtk_widget_show (vbox);

   g_signal_connect (window, "destroy",
       G_CALLBACK (quit), NULL);

   /* Create the drawing area */

   drawing_area = gtk_drawing_area_new ();
   gtk_widget_set_size_request (drawing_area, 200, 200);
   gtk_box_pack_start (GTK_BOX (vbox), drawing_area, TRUE, TRUE, 0);

   gtk_widget_show (drawing_area);

   /* Signals used to handle backing pixmap */

   g_signal_connect (drawing_area, "expose_event",
       G_CALLBACK (expose_event), NULL);
   g_signal_connect (drawing_area, "configure_event",
       G_CALLBACK (configure_event), NULL);

   /* Event signals */

   g_signal_connect (drawing_area, "motion_notify_event",
       G_CALLBACK (motion_notify_event), NULL);
   g_signal_connect (drawing_area, "button_press_event",
       G_CALLBACK (button_press_event), NULL);
   g_signal_connect (drawing_area, "key_press_event",
       G_CALLBACK (key_press_event), NULL);

   g_signal_connect (drawing_area, "leave_notify_event",
       G_CALLBACK (leave_notify_event), NULL);
   g_signal_connect (drawing_area, "proximity_out_event",
       G_CALLBACK (proximity_out_event), NULL);

   gtk_widget_set_events (drawing_area, GDK_EXPOSURE_MASK
     | GDK_LEAVE_NOTIFY_MASK
     | GDK_BUTTON_PRESS_MASK
     | GDK_KEY_PRESS_MASK
     | GDK_POINTER_MOTION_MASK
     | GDK_POINTER_MOTION_HINT_MASK
     | GDK_PROXIMITY_OUT_MASK);

   /* The following call enables tracking and processing of extension
      events for the drawing area */
   gtk_widget_set_extension_events (drawing_area, GDK_EXTENSION_EVENTS_ALL);

   GTK_WIDGET_SET_FLAGS (drawing_area, GTK_CAN_FOCUS);
   gtk_widget_grab_focus (drawing_area);

   /* .. And create some buttons */
   button = gtk_button_new_with_label ("Input Dialog");
   gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);

   g_signal_connect (button, "clicked",
       G_CALLBACK (create_input_dialog), NULL);
   gtk_widget_show (button);

   button = gtk_button_new_with_label ("Quit");
   gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);

   g_signal_connect_swapped (button, "clicked",
        G_CALLBACK (gtk_widget_destroy),
        window);
   gtk_widget_show (button);

   gtk_widget_show (window);

   gtk_main ();

   return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章