|
|
| Строка 1: |
Строка 1: |
| - | In this part of the GTK+ programming tutorial, we will create a custom GTK+ widget, where we will use the Cairo graphics library.
| |
| | | | |
| - | == CPU widget ==
| |
| - |
| |
| - | In the next example we will create a CPU widget.
| |
| - |
| |
| - | <source lang="cpp">
| |
| - | /* 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 */
| |
| - | </source>
| |
| - |
| |
| - | <source lang="cpp">
| |
| - | /* 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, >k_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);
| |
| - | }
| |
| - | }
| |
| - |
| |
| - | </source>
| |
| - |
| |
| - | <source lang="cpp">
| |
| - | /* 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;
| |
| - | }
| |
| - | </source>
| |
| - |
| |
| - | The CPU widget is a <b>GtkWidget</b>, 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 <b>GtkVScale</b> widget controls the number of 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 <b>gtk_cpu_paint()</b> function.
| |
| - |
| |
| - | <source lang="cpp">
| |
| - | 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);
| |
| - | </source>
| |
| - |
| |
| - | 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.
| |
| - |
| |
| - | <source lang="cpp">
| |
| - | gint pos = GTK_CPU(widget)->sel;
| |
| - | gint rect = pos / 5;
| |
| - | </source>
| |
| - |
| |
| - | 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.
| |
| - |
| |
| - | <source lang="cpp">
| |
| - | 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);
| |
| - | }
| |
| - | </source>
| |
| - |
| |
| - | Depending on the rect number, we draw 40 rectangles in two dark green color or in bright green color. Remember, that we are drawing these rectangles from top to bottom.
| |
| - |
| |
| - | <source lang="cpp">
| |
| - | GtkRange *range = (GtkRange *) widget;
| |
| - | GtkWidget *cpu = (GtkWidget *) data;
| |
| - | GTK_CPU(cpu)->sel = gtk_range_get_value(range);
| |
| - | </source>
| |
| - |
| |
| - | In the <b>set_value()</b> call, we get the reference to the CPU widget and set the sel value to current value, selected on the scale widget.
| |
| - |
| |
| - | <source lang="cpp">
| |
| - | 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);
| |
| - | </source>
| |
| - |
| |
| - | This code invalides the complete window of the CPU widget and thus makes it redraw itself.
| |
| - |
| |
| - | [[image: gtk_faq_cpuwidget.png | center]]
| |
| - |
| |
| - | [[Категория:GTK+]]
| |