原文: http://zetcode.com/gui/gtk2/customwidget/

在 GTK+ 编程教程的这一部分中,我们创建一个自定义的 GTK+ 小部件。 我们使用 Cario 图形库。

CPU 小部件

在下一个示例中,我们创建一个自定义 CPU 小部件。

mycpu.h

  1. #ifndef __MY_CPU_H__
  2. #define __MY_CPU_H__
  3. #include <gtk/gtk.h>
  4. #include <cairo.h>
  5. G_BEGIN_DECLS
  6. /* Standart GObject macros */
  7. #define MY_TYPE_CPU (my_cpu_get_type())
  8. #define MY_CPU(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), MY_TYPE_CPU, MyCpu))
  9. #define MY_CPU_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), MY_TYPE_CPU, MyCpuClass))
  10. #define MY_IS_CPU(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), MY_TYPE_CPU))
  11. #define MY_IS_CPU_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), MY_TYPE_CPU))
  12. #define MY_CPU_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), MY_TYPE_CPU, MyCpuClass))
  13. /* Type definition */
  14. typedef struct _MyCpu MyCpu;
  15. typedef struct _MyCpuClass MyCpuClass;
  16. typedef struct _MyCpuPrivate MyCpuPrivate;
  17. struct _MyCpu {
  18. GtkWidget parent;
  19. /*< Private >*/
  20. MyCpuPrivate *priv;
  21. };
  22. struct _MyCpuClass {
  23. GtkWidgetClass parent_class;
  24. };
  25. /* Public API */
  26. GType my_cpu_get_type(void) G_GNUC_CONST;
  27. GtkWidget *my_cpu_new(void);
  28. gdouble my_cpu_get_percent(MyCpu *cpu);
  29. void my_cpu_set_percent(MyCpu *cpu, gdouble sel);
  30. G_END_DECLS
  31. #endif /* __MY_CPU_H__ */

mycpu.h文件中,我们定义了自定义窗口小部件的类型,宏和函数。

mycpu.c

  1. /* mycpu.c */
  2. #include "mycpu.h"
  3. /* Properties enum */
  4. enum {
  5. P_0, /* Padding */
  6. P_PERCENT
  7. };
  8. /* Private data structure */
  9. struct _MyCpuPrivate {
  10. gdouble percent;
  11. GdkWindow *window;
  12. };
  13. const gint WIDTH = 80;
  14. const gint HEIGHT = 100;
  15. /* Internal API */
  16. static void my_cpu_set_property(GObject *object, guint prop_id,
  17. const GValue *value, GParamSpec *pspec);
  18. static void my_cpu_get_property(GObject *object, guint prop_id,
  19. GValue *value, GParamSpec *pspec);
  20. static void my_cpu_size_request(GtkWidget *widget,
  21. GtkRequisition *requisition);
  22. static void my_cpu_size_allocate(GtkWidget *widget,
  23. GtkAllocation *allocation);
  24. static void my_cpu_realize(GtkWidget *widget);
  25. static gboolean my_cpu_expose(GtkWidget *widget,
  26. GdkEventExpose *event);
  27. /* Define type */
  28. G_DEFINE_TYPE(MyCpu, my_cpu, GTK_TYPE_WIDGET)
  29. /* Initialization */
  30. static void my_cpu_class_init(MyCpuClass *klass) {
  31. GObjectClass *g_class;
  32. GtkWidgetClass *w_class;
  33. GParamSpec *pspec;
  34. g_class = G_OBJECT_CLASS(klass);
  35. w_class = GTK_WIDGET_CLASS(klass);
  36. /* Override widget class methods */
  37. g_class->set_property = my_cpu_set_property;
  38. g_class->get_property = my_cpu_get_property;
  39. w_class->realize = my_cpu_realize;
  40. w_class->size_request = my_cpu_size_request;
  41. w_class->size_allocate = my_cpu_size_allocate;
  42. w_class->expose_event = my_cpu_expose;
  43. /* Install property */
  44. pspec = g_param_spec_double("percent", "Percent",
  45. "What CPU load should be displayed", 0, 1, 0,
  46. G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
  47. g_object_class_install_property(g_class, P_PERCENT, pspec);
  48. /* Add private data */
  49. g_type_class_add_private(g_class, sizeof(MyCpuPrivate));
  50. }
  51. static void my_cpu_init(MyCpu *cpu) {
  52. MyCpuPrivate *priv;
  53. priv = G_TYPE_INSTANCE_GET_PRIVATE(cpu, MY_TYPE_CPU, MyCpuPrivate);
  54. gtk_widget_set_has_window(GTK_WIDGET(cpu), TRUE);
  55. /* Set default values */
  56. priv->percent = 0;
  57. /* Create cache for faster access */
  58. cpu->priv = priv;
  59. }
  60. /* Overriden virtual methods */
  61. static void my_cpu_set_property(GObject *object, guint prop_id,
  62. const GValue *value, GParamSpec *pspec) {
  63. MyCpu *cpu = MY_CPU(object);
  64. switch(prop_id) {
  65. case P_PERCENT:
  66. my_cpu_set_percent(cpu, g_value_get_double(value));
  67. break;
  68. default:
  69. G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
  70. break;
  71. }
  72. }
  73. static void my_cpu_get_property(GObject *object, guint prop_id,
  74. GValue *value, GParamSpec *pspec) {
  75. MyCpu *cpu = MY_CPU(object);
  76. switch(prop_id) {
  77. case P_PERCENT:
  78. g_value_set_double(value, cpu->priv->percent);
  79. break;
  80. default:
  81. G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
  82. break;
  83. }
  84. }
  85. static void my_cpu_realize(GtkWidget *widget) {
  86. MyCpuPrivate *priv = MY_CPU(widget)->priv;
  87. GtkAllocation alloc;
  88. GdkWindowAttr attrs;
  89. guint attrs_mask;
  90. gtk_widget_set_realized(widget, TRUE);
  91. gtk_widget_get_allocation(widget, &alloc);
  92. attrs.x = alloc.x;
  93. attrs.y = alloc.y;
  94. attrs.width = alloc.width;
  95. attrs.height = alloc.height;
  96. attrs.window_type = GDK_WINDOW_CHILD;
  97. attrs.wclass = GDK_INPUT_OUTPUT;
  98. attrs.event_mask = gtk_widget_get_events(widget) | GDK_EXPOSURE_MASK;
  99. attrs_mask = GDK_WA_X | GDK_WA_Y;
  100. priv->window = gdk_window_new(gtk_widget_get_parent_window(widget),
  101. &attrs, attrs_mask);
  102. gdk_window_set_user_data(priv->window, widget);
  103. gtk_widget_set_window(widget, priv->window);
  104. widget->style = gtk_style_attach(gtk_widget_get_style( widget ),
  105. priv->window);
  106. gtk_style_set_background(widget->style, priv->window, GTK_STATE_NORMAL);
  107. }
  108. static void my_cpu_size_request(GtkWidget *widget,
  109. GtkRequisition *requisition) {
  110. requisition->width = WIDTH;
  111. requisition->height = HEIGHT;
  112. }
  113. static void my_cpu_size_allocate(GtkWidget *widget,
  114. GtkAllocation *allocation) {
  115. MyCpuPrivate *priv;
  116. priv = MY_CPU(widget)->priv;
  117. gtk_widget_set_allocation(widget, allocation);
  118. if (gtk_widget_get_realized(widget)) {
  119. gdk_window_move_resize(priv->window, allocation->x, allocation->y,
  120. WIDTH, HEIGHT);
  121. }
  122. }
  123. static gboolean my_cpu_expose(GtkWidget *widget,
  124. GdkEventExpose *event) {
  125. MyCpuPrivate *priv = MY_CPU(widget)->priv;
  126. cairo_t *cr;
  127. gint limit;
  128. gint i;
  129. cr = gdk_cairo_create(event->window);
  130. cairo_translate(cr, 0, 7);
  131. cairo_set_source_rgb(cr, 0, 0, 0);
  132. cairo_paint(cr);
  133. limit = 20 - priv->percent / 5;
  134. for (i = 1; i <= 20; i++) {
  135. if (i > limit) {
  136. cairo_set_source_rgb(cr, 0.6, 1.0, 0);
  137. } else {
  138. cairo_set_source_rgb(cr, 0.2, 0.4, 0);
  139. }
  140. cairo_rectangle(cr, 8, i * 4, 30, 3);
  141. cairo_rectangle(cr, 42, i * 4, 30, 3);
  142. cairo_fill(cr);
  143. }
  144. cairo_destroy(cr);
  145. return TRUE;
  146. }
  147. /* Public API */
  148. GtkWidget *my_cpu_new(void) {
  149. return(g_object_new(MY_TYPE_CPU, NULL));
  150. }
  151. gdouble my_cpu_get_percent(MyCpu *cpu) {
  152. g_return_val_if_fail(MY_IS_CPU(cpu), 0);
  153. return(cpu->priv->percent);
  154. }
  155. void my_cpu_set_percent(MyCpu *cpu, gdouble sel) {
  156. g_return_if_fail(MY_IS_CPU(cpu));
  157. cpu->priv->percent = sel;
  158. gtk_widget_queue_draw(GTK_WIDGET(cpu));
  159. }

mycpu.c是 CPU 小部件的实现。 CPU 小部件是GtkWidget,我们可以使用 Cairo API 在其上进行绘制。 我们绘制一个黑色背景和 40 个小矩形。 矩形以两种颜色绘制:深绿色和亮绿色。 GtkVScale小部件控制在小部件上绘制的鲜绿色矩形的数量。

main.c

  1. #include "mycpu.h"
  2. void cb_changed(GtkRange *range, GtkWidget *cpu) {
  3. my_cpu_set_percent(MY_CPU(cpu), gtk_range_get_value(range));
  4. }
  5. int main(int argc, char *argv[]) {
  6. GtkWidget *window;
  7. GtkWidget *hbox;
  8. GtkWidget *vbox;
  9. GtkWidget *cpu;
  10. GtkWidget *scale;
  11. gtk_init(&argc, &argv);
  12. window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  13. gtk_container_set_border_width(GTK_CONTAINER(window), 15);
  14. gtk_window_set_title(GTK_WINDOW(window), "CPU widget");
  15. vbox = gtk_vbox_new(FALSE, 0);
  16. hbox = gtk_hbox_new(FALSE, 25);
  17. cpu = my_cpu_new();
  18. gtk_box_pack_start(GTK_BOX(hbox), cpu, FALSE, FALSE, 0);
  19. scale = gtk_vscale_new_with_range(0, 100, 1);
  20. gtk_scale_set_draw_value(GTK_SCALE(scale), FALSE);
  21. gtk_range_set_inverted(GTK_RANGE(scale), TRUE);
  22. gtk_box_pack_start(GTK_BOX(hbox), scale, FALSE, FALSE, 0);
  23. gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
  24. gtk_container_add(GTK_CONTAINER(window), vbox);
  25. g_signal_connect(scale, "value-changed", G_CALLBACK(cb_changed), cpu);
  26. g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
  27. gtk_widget_show_all(window);
  28. gtk_main();
  29. return 0;
  30. }

这是主文件。 我们创建自定义 CPU 小部件,并使其与GtkVScale小部件一起使用。

  1. gcc -Wall $(pkg-config --cflags gtk+-2.0) -o mycpu.o -c mycpu.c
  2. gcc -Wall $(pkg-config --cflags gtk+-2.0) -o main.o -c main.c
  3. gcc -o test_cpu main.o mycpu.o $(pkg-config --libs gtk+-2.0)

使用这些命令,我们构建了示例。

自定义 GTK  小部件 - 图1

图:CPU小部件

在本章中,我们创建了一个自定义的 GTK+ 小部件。