其实我觊觎 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
这个函数非常好用。
参考资料