5
13
2011
6

Cairo 初体验

其实我觊觎 Cairo 很久了。昨天看到这篇文章,就下决心试了下。

简单图形的绘制

这个确实很简单,随便从后文的参考资料处抄点代码过来即可:

static gboolean on_expose_event(GtkWidget *widget,
    GdkEventExpose *event, gpointer data){
  cairo_t *cr;

  cr = gdk_cairo_create(widget->window);

  cairo_set_source_rgb(cr, 0, 0, 0);
  cairo_set_line_width(cr, 1);

  cairo_rectangle(cr, 20, 20, 120, 80);
  cairo_stroke_preserve(cr);
  cairo_set_source_rgb(cr, 1, 1, 1);
  cairo_fill(cr);

  cairo_destroy(cr);

  return FALSE;
}

看函数名基本上知道是在做什么了。我需要一个半透明的矩形,于是查文档,找到cairo_set_source_rgba函数即可。那个 cr,我是折腾到最后才明白它代表了绘制的 context (环境/上下文);至于那个cairo_stroke_preserve,我最终弄明白 Cairo 的绘制方法后才明白的。不管是cairo_rectangle还是我后来用到的cairo_arc,都是“画”了一个路径。之所以要把“画”加上引号,是因为路径本身是看不到的。使用cairo_stroke来描绘路径,使用cairo_fill来填充,而后面带_preserve的版本,是说路径处理完了还留着,下一个操作继续用。我之前犯了个错误,把所有的矩形和圆的路径都画好了才绘制,结果矩形和圆之间有道连线,圆的半径也被画出来了。折腾好久才知道应该在每个路径完成后就绘制。

事件处理

翻了好久的GTK、GDK文档,还是没弄明白该怎么在鼠标拖动时绘制。Google 一下,才看到 Garfileo 的这篇文章,在兴奋之余很有郁闷,Garfileo 以前的 Cairo 相关文章我都收集了,可他为什么后来又换博客了呢。。。。

  gtk_widget_add_events(window, GDK_KEY_PRESS_MASK |
      GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
  g_signal_connect(window, "button-press-event",
      G_CALLBACK(drawing_start), NULL);
  g_signal_connect(window, "button-release-event",
      G_CALLBACK(drawing_finish), NULL);
  g_signal_connect(GTK_OBJECT(window), "motion_notify_event",
      G_CALLBACK(drawing_move), NULL);

就这样就可以了。然后我就开始在每个motion_notify_event发生时画一遍,结果在画的时候图像不停地闪。问了下 gtalk 好友老猫,才知道原来只要在 expose 事件时画就好了,而图像改变后,调用下gtk_widget_queue_draw即可。至于为什么这样图像就不会闪,我至今还不清楚。

右键菜单

我需要在几种不同的选区之间切换。本来用 RadioButton 挺好的,但是我现在只会在 GtkWindow 或者 GtkDialog 里画图。后来就想到了右键菜单。查阅文档后就写出以下代码:

  menu = gtk_menu_new();
  menu_rect = gtk_menu_item_new_with_label("矩形");
  menu_circ = gtk_menu_item_new_with_label("圆形");
  menu_poly = gtk_menu_item_new_with_label("多边形");
  gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_rect);
  gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_circ);
  /* TODO 绘制多边形 */
  /* gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_poly); */
  g_signal_connect(menu_rect, "activate",
      G_CALLBACK(switch_to_rect), NULL);
  g_signal_connect(menu_circ, "activate",
      G_CALLBACK(switch_to_circ), NULL);
  g_signal_connect(menu_poly, "activate",
      G_CALLBACK(switch_to_poly), NULL);

  ...

  }else if(event->button == 3){
    gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, event->button, event->time);
  }

但是这样点右键时只会显示一个小小的白色小边,就像没有菜单项在里面一样。百思不得其解,于是求助于 Google,最后不知道在哪里看到了,原来还需要对menu调用gtk_widget_show_all。想想也是,对窗口调用gtk_widget_show_all只是把窗口里的东西全部显示出来了,但我这个右键弹出菜单不是在窗口里的呀。

一点题外话

因为要绘制多个图形,本来准备自己写个单链表的,刚写完 node 结构体,突然想到,GLib 里不是有各种数据结构吗?于是再查文档,找到 GSList,愉快地用上了。g_slist_foreach这个函数非常好用。

参考资料

Category: 编程 | Tags: cairo C代码 gtk

| Theme: Aeros 2.0 by TheBuckmaker.com