The Cairo graphics tutorial -------Custom GTK widget

转载 2012年03月30日 08:32:00

In this part of the Cairo graphics tutorial, we will create a custom GTK widget, where we will use the Cairo library.



CPU widget

In the next example we will create a CPU widget.

/* cpu.h */

#ifndef __CPU_H
#define __CPU_H

#include <gtk/gtk.h>
#include <cairo.h>

G_BEGIN_DECLS


#define GTK_CPU(obj) GTK_CHECK_CAST(obj, gtk_cpu_get_type (), GtkCpu)
#define GTK_CPU_CLASS(klass) GTK_CHECK_CLASS_CAST(klass, gtk_cpu_get_type(), GtkCpuClass)
#define GTK_IS_CPU(obj) GTK_CHECK_TYPE(obj, gtk_cpu_get_type())


typedef struct _GtkCpu GtkCpu;
typedef struct _GtkCpuClass GtkCpuClass;


struct _GtkCpu {
  GtkWidget widget;

  gint sel;
};

struct _GtkCpuClass {
  GtkWidgetClass parent_class;
};


GtkType gtk_cpu_get_type(void);
void gtk_cpu_set_sel(GtkCpu *cpu, gint sel);
GtkWidget * gtk_cpu_new();


G_END_DECLS

#endif /* __CPU_H */
/* cpu.c */

#include "cpu.h"


static void gtk_cpu_class_init(GtkCpuClass *klass);
static void gtk_cpu_init(GtkCpu *cpu);
static void gtk_cpu_size_request(GtkWidget *widget,
    GtkRequisition *requisition);
static void gtk_cpu_size_allocate(GtkWidget *widget,
    GtkAllocation *allocation);
static void gtk_cpu_realize(GtkWidget *widget);
static gboolean gtk_cpu_expose(GtkWidget *widget,
    GdkEventExpose *event);
static void gtk_cpu_paint(GtkWidget *widget);
static void gtk_cpu_destroy(GtkObject *object);


GtkType
gtk_cpu_get_type(void)
{
  static GtkType gtk_cpu_type = 0;


  if (!gtk_cpu_type) {
      static const GtkTypeInfo gtk_cpu_info = {
          "GtkCpu",
          sizeof(GtkCpu),
          sizeof(GtkCpuClass),
          (GtkClassInitFunc) gtk_cpu_class_init,
          (GtkObjectInitFunc) gtk_cpu_init,
          NULL,
          NULL,
          (GtkClassInitFunc) NULL
      };
      gtk_cpu_type = gtk_type_unique(GTK_TYPE_WIDGET, &gtk_cpu_info);
  }


  return gtk_cpu_type;
}

void
gtk_cpu_set_state(GtkCpu *cpu, gint num)
{
   cpu->sel = num;
   gtk_cpu_paint(GTK_WIDGET(cpu));
}


GtkWidget * gtk_cpu_new()
{
   return GTK_WIDGET(gtk_type_new(gtk_cpu_get_type()));
}


static void
gtk_cpu_class_init(GtkCpuClass *klass)
{
  GtkWidgetClass *widget_class;
  GtkObjectClass *object_class;


  widget_class = (GtkWidgetClass *) klass;
  object_class = (GtkObjectClass *) klass;

  widget_class->realize = gtk_cpu_realize;
  widget_class->size_request = gtk_cpu_size_request;
  widget_class->size_allocate = gtk_cpu_size_allocate;
  widget_class->expose_event = gtk_cpu_expose;

  object_class->destroy = gtk_cpu_destroy;
}


static void
gtk_cpu_init(GtkCpu *cpu)
{
   cpu->sel = 0;
}


static void
gtk_cpu_size_request(GtkWidget *widget,
    GtkRequisition *requisition)
{
  g_return_if_fail(widget != NULL);
  g_return_if_fail(GTK_IS_CPU(widget));
  g_return_if_fail(requisition != NULL);

  requisition->width = 80;
  requisition->height = 100;
}


static void
gtk_cpu_size_allocate(GtkWidget *widget,
    GtkAllocation *allocation)
{
  g_return_if_fail(widget != NULL);
  g_return_if_fail(GTK_IS_CPU(widget));
  g_return_if_fail(allocation != NULL);

  widget->allocation = *allocation;

  if (GTK_WIDGET_REALIZED(widget)) {
     gdk_window_move_resize(
         widget->window,
         allocation->x, allocation->y,
         allocation->width, allocation->height
     );
   }
}


static void
gtk_cpu_realize(GtkWidget *widget)
{
  GdkWindowAttr attributes;
  guint attributes_mask;

  g_return_if_fail(widget != NULL);
  g_return_if_fail(GTK_IS_CPU(widget));

  GTK_WIDGET_SET_FLAGS(widget, GTK_REALIZED);

  attributes.window_type = GDK_WINDOW_CHILD;
  attributes.x = widget->allocation.x;
  attributes.y = widget->allocation.y;
  attributes.width = 80;
  attributes.height = 100;

  attributes.wclass = GDK_INPUT_OUTPUT;
  attributes.event_mask = gtk_widget_get_events(widget) | GDK_EXPOSURE_MASK;

  attributes_mask = GDK_WA_X | GDK_WA_Y;

  widget->window = gdk_window_new(
     gtk_widget_get_parent_window (widget),
     & attributes, attributes_mask
  );

  gdk_window_set_user_data(widget->window, widget);

  widget->style = gtk_style_attach(widget->style, widget->window);
  gtk_style_set_background(widget->style, widget->window, GTK_STATE_NORMAL);
}


static gboolean
gtk_cpu_expose(GtkWidget *widget,
    GdkEventExpose *event)
{
  g_return_val_if_fail(widget != NULL, FALSE);
  g_return_val_if_fail(GTK_IS_CPU(widget), FALSE);
  g_return_val_if_fail(event != NULL, FALSE);

  gtk_cpu_paint(widget);

  return FALSE;
}


static void
gtk_cpu_paint(GtkWidget *widget)
{
  cairo_t *cr;

  cr = gdk_cairo_create(widget->window);

  cairo_translate(cr, 0, 7);

  cairo_set_source_rgb(cr, 0, 0, 0);
  cairo_paint(cr);

  gint pos = GTK_CPU(widget)->sel;
  gint rect = pos / 5;

  cairo_set_source_rgb(cr, 0.2, 0.4, 0);

  gint i;
  for ( i = 1; i <= 20; i++) {
      if (i > 20 - rect) {
          cairo_set_source_rgb(cr, 0.6, 1.0, 0);
      } else {
          cairo_set_source_rgb(cr, 0.2, 0.4, 0);
      }
      cairo_rectangle(cr, 8, i*4, 30, 3);
      cairo_rectangle(cr, 42, i*4, 30, 3);
      cairo_fill(cr);
  }

  cairo_destroy(cr);
}


static void
gtk_cpu_destroy(GtkObject *object)
{
  GtkCpu *cpu;
  GtkCpuClass *klass;

  g_return_if_fail(object != NULL);
  g_return_if_fail(GTK_IS_CPU(object));

  cpu = GTK_CPU(object);

  klass = gtk_type_class(gtk_widget_get_type());

  if (GTK_OBJECT_CLASS(klass)->destroy) {
     (* GTK_OBJECT_CLASS(klass)->destroy) (object);
  }
}
/* main.c */

#include "cpu.h"


static void set_value(GtkWidget * widget, gpointer data)
{
  GdkRegion *region;

  GtkRange *range = (GtkRange *) widget;
  GtkWidget *cpu = (GtkWidget *) data;
  GTK_CPU(cpu)->sel = gtk_range_get_value(range);

  region = gdk_drawable_get_clip_region(cpu->window);
  gdk_window_invalidate_region(cpu->window, region, TRUE);
  gdk_window_process_updates(cpu->window, TRUE);
}


int main (int argc, char ** argv)
{
  GtkWidget *window;
  GtkWidget *cpu;
  GtkWidget *fixed;
  GtkWidget *scale;

  gtk_init(&argc, &argv);


  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  gtk_window_set_title(GTK_WINDOW(window), "CPU widget");
  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
  gtk_window_set_default_size(GTK_WINDOW(window), 200, 180);


  g_signal_connect(G_OBJECT(window), "destroy", 
       G_CALLBACK(gtk_main_quit), NULL);

  fixed = gtk_fixed_new();
  gtk_container_add(GTK_CONTAINER(window), fixed);

  cpu = gtk_cpu_new();
  gtk_fixed_put(GTK_FIXED(fixed), cpu, 30, 40);


  scale = gtk_vscale_new_with_range(0.0, 100.0, 1.0);
  gtk_range_set_inverted(GTK_RANGE(scale), TRUE);
  gtk_scale_set_value_pos(GTK_SCALE(scale), GTK_POS_TOP);
  gtk_widget_set_size_request(scale, 50, 120);
  gtk_fixed_put(GTK_FIXED(fixed), scale, 130, 20);

  g_signal_connect(G_OBJECT(scale), "value_changed", 
      G_CALLBACK(set_value), (gpointer) cpu);

  gtk_widget_show(cpu);
  gtk_widget_show(fixed);
  gtk_widget_show_all(window);
  gtk_main();

  return 0;
}

The CPU widget is a GtkWidget, on which we draw with Cairo API.We draw a black background and 40 small rectangles. The rectangles are drawn in two colors. Dark green and bright green color. The GtkVScale widget controls the numberof the bright green rectangles drawn on the widget.

The example might look difficult at the first sight. But it is not that difficult after all. Most of the code is boilerplate, it always repeats, when we create a new widget.

The drawing is done within the gtk_cpu_paint() function.

  cairo_t *cr;

  cr = gdk_cairo_create(widget->window);

  cairo_translate(cr, 0, 7);

  cairo_set_source_rgb(cr, 0, 0, 0);
  cairo_paint(cr);

As usual, we create a cairo context. We shift the origin 7 unit down. Next we paint the background of the widget to black color.

 gint pos = GTK_CPU(widget)->sel;
 gint rect = pos / 5;

Here we retrieve the sel number. It is the number that we got from the scale widget. The slider has 100 numbers. The rect parameter makes a convertion from slider values into rectangles,that will be drawn in bright green color.

 gint i;
 for ( i = 1; i <= 20; i++) {
     if (i > 20 - rect) {
         cairo_set_source_rgb(cr, 0.6, 1.0, 0);
     } else {
         cairo_set_source_rgb(cr, 0.2, 0.4, 0);
     }
     cairo_rectangle(cr, 8, i*4, 30, 3);
     cairo_rectangle(cr, 42, i*4, 30, 3);
     cairo_fill(cr);
 }

Depending on the rect number, we draw 40 rectangles in two dark green color or in bright green color. Remember, that weare drawing these rectangles from top to bottom.

 GtkRange *range = (GtkRange *) widget;
 GtkWidget *cpu = (GtkWidget *) data;
 GTK_CPU(cpu)->sel = gtk_range_get_value(range);

In the set_value() call, we get the reference to the CPU widget and set the sel value tocurrent value, selected on the scale widget.

 GdkRegion *region;
 ...
 region = gdk_drawable_get_clip_region(cpu->window);
 gdk_window_invalidate_region(cpu->window, region, TRUE);
 gdk_window_process_updates(cpu->window, TRUE);

This code invalides the complete window of the CPU widget and thus makes it redraw itself.


cpu widget
Figure: CPU widget

In this part of the Cairo tutorial, we have created a custom widget.


The Cairo graphics tutorial -------Transformations

In this part of the Cairo graphics programming tutorial, we will talk about transformations. ...

The Cairo graphics tutorial -------Transparency

In this part of the Cairo C API tutorial, we will talk about transparency. We will provide some basi...

The Cairo graphics tutorial -------Clipping and masking

In this part of the Cairo tutorial, we will talk about clipping and masking. Clipping ...

The Cairo graphics tutorial -------Basic drawing in Cairo

In this part of the Cairo graphics tutorial, we will draw some basic primitives. We will draw simple...

The Cairo graphics tutorial -------Text in Cairo

In this part of the Cairo graphics tutorial, we will work with text. Soulmate In the first...

The Cairo graphics tutorial -------Cairo definitions

Cairo definitions In this part of the Cairo graphics tutorial, we will provide some useful definiti...

The Cairo graphics tutorial -------Shapes and fills in Cairo

In this part of the Cairo tutorial, we will create some basic and more advanced shapes. We will fill...

The Cairo graphics tutorial -------Cairo backends

The Cairo library supports various backends. In this section of the Cairo graphics tutorial, we will...

GTK Cairo LibCurl LibXML 解析实例

  • 2010年08月11日 14:16
  • 75KB
  • 下载

用Cairo在GTK+上绘图

Cairo是跨平台的2D图形库,支持多种后端,支持输出PostScript、PDF和SVG等。 比较有名的例子是,从GTK+2.8开始,GTK+使用Cairo进行图形绘制,另外一个就是Mozilla...
  • uunubt
  • uunubt
  • 2011年01月11日 16:13
  • 1037
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:The Cairo graphics tutorial -------Custom GTK widget
举报原因:
原因补充:

(最多只允许输入30个字)