mirror of
https://github.com/ToshioCP/Gtk4-tutorial.git
synced 2025-01-11 20:03:35 +01:00
Add section 19.
This commit is contained in:
parent
9847c5fcd7
commit
7f1228cb26
11 changed files with 399 additions and 4 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -6,6 +6,8 @@ src/img.rb
|
|||
src/imgsize.rb
|
||||
src/toi.rb
|
||||
src/misc/a.out
|
||||
src/misc/cairo2.c
|
||||
src/misc/cairo2.pdf
|
||||
src/tfv/a.out
|
||||
src/tfe/a.out
|
||||
src/tfe/hello.txt
|
||||
|
|
8
Rakefile
8
Rakefile
|
@ -201,9 +201,9 @@ end
|
|||
end
|
||||
end
|
||||
|
||||
task html: htmlfilenames+["html/index.html"]
|
||||
task html: ["html/index.html"]
|
||||
|
||||
file "html/index.html" do
|
||||
file "html/index.html" => htmlfilenames do
|
||||
0.upto(srcfiles.size-1) do |i|
|
||||
h = File.open(srcfiles[i].path) { |file| file.readline }
|
||||
h = h.gsub(/^#* */,"").chomp
|
||||
|
@ -249,9 +249,9 @@ task pdf: "latex" do
|
|||
sh "mv latex/main.pdf latex/gtk4_tutorial.pdf"
|
||||
end
|
||||
|
||||
task latex: texfilenames+["latex/main.tex"]
|
||||
task latex: ["latex/main.tex"]
|
||||
|
||||
file "latex/main.tex" do
|
||||
file "latex/main.tex" => texfilenames do
|
||||
0.upto(srcfiles.size-1) do |i|
|
||||
main += " \\input{#{srcfiles[i].to_tex}}\n"
|
||||
end
|
||||
|
|
|
@ -31,3 +31,4 @@ You can read it without download.
|
|||
1. [Menu and action](gfm/sec16.md)
|
||||
1. [Stateful action](gfm/sec17.md)
|
||||
1. [Ui file for menu and action entries](gfm/sec18.md)
|
||||
1. [GtkDrawingArea and Cairo](gfm/sec19.md)
|
||||
|
|
197
gfm/sec19.md
Normal file
197
gfm/sec19.md
Normal file
|
@ -0,0 +1,197 @@
|
|||
Up: [Readme.md](../Readme.md), Prev: [Section 18](sec18.md)
|
||||
|
||||
# GtkDrawingArea and Cairo
|
||||
|
||||
If you want to draw dynamically, like an image window of gimp graphics editor, GtkDrawingArea widget is the most suitable widget.
|
||||
You can draw or redraw an image in this widget freely.
|
||||
It is called custom drawing.
|
||||
|
||||
GtkDrawingArea provides a cairo context so users can draw images by cairo functions.
|
||||
In this section, I will explain:
|
||||
|
||||
1. Cairo, but briefly.
|
||||
2. GtkDrawingArea with very simple example.
|
||||
|
||||
## Cairo
|
||||
|
||||
Cairo is a two dimensional graphics library.
|
||||
First, you need to know surface, source, mask, destination, cairo context and transformation.
|
||||
|
||||
- surface represents an image.
|
||||
It is like a canvas.
|
||||
We can draw shapes and images with different colors on surfaces.
|
||||
- source pattern, or simply source, is a kind of paint, which will be transfered to destination surface by cairo functions.
|
||||
- mask is image mask used in the transference.
|
||||
- destination is a target surface.
|
||||
- cairo context manages the transference from source to destination through mask with its functions.
|
||||
For example, `cairo_stroke` is a function to draw a path to the destination by the transference.
|
||||
- transformation is applied before the transfer completes.
|
||||
The transformation is called affine, which is a mathematicsterminology, and represented by matrix multiplication and vector addition.
|
||||
Scaling, rotation, reflection, shearing and translation are examples of affine transformation.
|
||||
In this section, we don't use it.
|
||||
That means we only use identity transformation.
|
||||
Therefore, the coordinate in source and mask is the same as the coordinate in destination.
|
||||
|
||||
![Stroke a rectangle](../image/cairo.png)
|
||||
|
||||
The instruction is as follows:
|
||||
|
||||
1. Create a surface.
|
||||
This will be a destnation.
|
||||
2. Create a cairo context with the surface and set it to the destination.
|
||||
3. Create a source pattern within the context.
|
||||
4. Create paths, which are lines, rectangles, arcs, texts or more complicated shapes, to generate a mask.
|
||||
5. Use drawing operator such as `cairo_stroke` to transfer the paint in the source to the destination.
|
||||
6. Save the destination surface to a file if necessary.
|
||||
|
||||
Here's a simple example code that draws a small square and save it as a png file.
|
||||
|
||||
1 #include <cairo.h>
|
||||
2
|
||||
3 int
|
||||
4 main (int argc, char **argv)
|
||||
5 {
|
||||
6 cairo_surface_t *surface;
|
||||
7 cairo_t *cr;
|
||||
8 int width = 100;
|
||||
9 int height = 100;
|
||||
10
|
||||
11 /* Generate surface and cairo */
|
||||
12 surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, height);
|
||||
13 cr = cairo_create (surface);
|
||||
14
|
||||
15 /* Drawing starts here. */
|
||||
16 /* Paint the background white */
|
||||
17 cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
|
||||
18 cairo_paint (cr);
|
||||
19 /* Draw a black rectangle */
|
||||
20 cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
|
||||
21 cairo_set_line_width (cr, 2.0);
|
||||
22 cairo_rectangle (cr, width/2.0 - 20.0, height/2.0 - 20.0, 40.0, 40.0);
|
||||
23 cairo_stroke (cr);
|
||||
24
|
||||
25 /* Write the surface to a png file and clean up cairo and surface. */
|
||||
26 cairo_surface_write_to_png (surface, "rectangle.png");
|
||||
27 cairo_destroy (cr);
|
||||
28 cairo_surface_destroy (surface);
|
||||
29
|
||||
30 return 0;
|
||||
31 }
|
||||
|
||||
- 1: Include the header file of cairo.
|
||||
- 12: `cairo_image_surface_create` creates an image surface.
|
||||
`CAIRO_FORMAT_RGB24` is a constant which means that each pixel has red, green and blue data.
|
||||
Each data has 8 bit quantity.
|
||||
Modern displays have this type of color depth.
|
||||
Width and hieight are pixels and given as integers.
|
||||
- 13: Create cairo context.
|
||||
The surface given as an argument will be set to the destination of the context.
|
||||
- 17: `cairo_set_source_rgb` creates a source pattern, which is a solid white paint.
|
||||
The second to fourth argument is red, green and blue color depth respectively.
|
||||
Their type is float and the values are between zero and one.
|
||||
(0,0,0) is black and (1,1,1) is white.
|
||||
- 18: `cairo_paint` copies everywhere in the source to destination.
|
||||
The destination is filled with white pixels by this command.
|
||||
- 20: Set the source color to black.
|
||||
- 21: `cairo_set_line_width` set the width of lines.
|
||||
In this case, the line width is set to two pixels.
|
||||
(It is because the transformation is identity.
|
||||
If we set it different one, for example scaling with the factor three, the actual width in destnation is six (2x3=6) pixels.)
|
||||
- 22: Draw a rectangle (square).
|
||||
The top-left coordinate is (width/2.0-20.0, height/2.0-20.0) and the width and height have the same length 40.0.
|
||||
- 23: `cairo_stroke` transfer the source to destnation through the rectangle in mask.
|
||||
- 26: Output the image to a png file `rectangle.png`.
|
||||
- 27: Destroy the context. At the same time the source is destroyed.
|
||||
- 28: Destroy the destnation surface.
|
||||
|
||||
To compile this, type the following.
|
||||
|
||||
$ gcc `pkg-config --cflags cairo` cairo.c `pkg-config --libs cairo`
|
||||
|
||||
![rectangle.png](../src/misc/rectangle.png)
|
||||
|
||||
There are lots of documentations in [Cairo's website](https://www.cairographics.org/).
|
||||
If you aren't familiar with cairo, it is strongly recommended to read the [tutorial](https://www.cairographics.org/tutorial/) in the website.
|
||||
|
||||
## GtkDrawingArea
|
||||
|
||||
The following is a very simple example.
|
||||
|
||||
1 #include <gtk/gtk.h>
|
||||
2
|
||||
3 static void
|
||||
4 draw_function (GtkDrawingArea *area, cairo_t *cr, int width, int height, gpointer user_data) {
|
||||
5 cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); /* whilte */
|
||||
6 cairo_paint (cr);
|
||||
7 cairo_set_line_width (cr, 2.0);
|
||||
8 cairo_set_source_rgb (cr, 0.0, 0.0, 0.0); /* black */
|
||||
9 cairo_rectangle (cr, width/2.0 - 20.0, height/2.0 - 20.0, 40.0, 40.0);
|
||||
10 cairo_stroke (cr);
|
||||
11 }
|
||||
12
|
||||
13 static void
|
||||
14 on_activate (GApplication *app, gpointer user_data) {
|
||||
15 GtkWidget *win = gtk_application_window_new (GTK_APPLICATION (app));
|
||||
16 GtkWidget *area = gtk_drawing_area_new ();
|
||||
17
|
||||
18 gtk_window_set_title (GTK_WINDOW (win), "da1");
|
||||
19 /* Set initial size of width and height */
|
||||
20 gtk_drawing_area_set_content_width (GTK_DRAWING_AREA (area), 100);
|
||||
21 gtk_drawing_area_set_content_height (GTK_DRAWING_AREA (area), 100);
|
||||
22 gtk_drawing_area_set_draw_func (GTK_DRAWING_AREA (area), draw_function, NULL, NULL);
|
||||
23 gtk_window_set_child (GTK_WINDOW (win), area);
|
||||
24
|
||||
25 gtk_widget_show (win);
|
||||
26 }
|
||||
27
|
||||
28 int
|
||||
29 main (int argc, char **argv) {
|
||||
30 GtkApplication *app;
|
||||
31 int stat;
|
||||
32
|
||||
33 app = gtk_application_new ("com.github.ToshioCP.da1", G_APPLICATION_FLAGS_NONE);
|
||||
34 g_signal_connect (app, "activate", G_CALLBACK (on_activate), NULL);
|
||||
35 stat =g_application_run (G_APPLICATION (app), argc, argv);
|
||||
36 g_object_unref (app);
|
||||
37 return stat;
|
||||
38 }
|
||||
39
|
||||
|
||||
The function `main` is almost same as before.
|
||||
The two functions `on_activate` and `draw_function` is important in this example.
|
||||
|
||||
- 16: Generate a GtkDrawingArea object.
|
||||
- 20,21: Set the width and height of the contents of the GtkDrawingArea widget.
|
||||
These width and height is the size of the destination surface of the cairo context provided by the widget.
|
||||
- 22: Set a drawng function to the widget.
|
||||
GtkDrawingArea widget uses the function to draw the contents of itself whenever its necessary.
|
||||
For example, when a user drag a mouse pointer and resize a top level window, GtkDrawingArea also changes the size.
|
||||
Then, the whole window needs to be redrawn.
|
||||
|
||||
The drawing function has five parameters.
|
||||
|
||||
void drawing_function (GtkDrawingArea *drawing_area, cairo_t *cr, int width, int height,
|
||||
gpointer user_data);
|
||||
|
||||
The first parameter is the GtkDrawingArea widget which calls the drawing function.
|
||||
However, you can't change any properties, for example `content-width` or `content-height`, in this function.
|
||||
The second parameter is a cairo context given by the widget.
|
||||
The destnation surface of the context is connected to the contents of the widget.
|
||||
What you draw to this surface will appear in the widget on the screen.
|
||||
The third and fourth paranmeters are the size of the destination surface.
|
||||
|
||||
- 3-11: The drawing function.
|
||||
- 4-5: Set the source to be white and paint the destination white.
|
||||
- 7: Set the line width to be 2.
|
||||
- 8: Set the source to be black.
|
||||
- 9: Add a rectangle to the mask.
|
||||
- 10: Draw the rectangle with black color to the destination.
|
||||
|
||||
Compile and run it, then a window with a black rectangle (square) appears.
|
||||
Try resizing the window.
|
||||
The square always appears at the center of the window because the drawing function is invoked every moment the window is resized.
|
||||
|
||||
![Square in the window](../image/da1.png)
|
||||
|
||||
|
||||
Up: [Readme.md](../Readme.md), Prev: [Section 18](sec18.md)
|
BIN
image/cairo.png
Normal file
BIN
image/cairo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 16 KiB |
BIN
image/cairo.xcf
Normal file
BIN
image/cairo.xcf
Normal file
Binary file not shown.
BIN
image/da1.png
Normal file
BIN
image/da1.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 9.3 KiB |
31
src/misc/cairo.c
Normal file
31
src/misc/cairo.c
Normal file
|
@ -0,0 +1,31 @@
|
|||
#include <cairo.h>
|
||||
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
cairo_surface_t *surface;
|
||||
cairo_t *cr;
|
||||
int width = 100;
|
||||
int height = 100;
|
||||
|
||||
/* Generate surface and cairo */
|
||||
surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, height);
|
||||
cr = cairo_create (surface);
|
||||
|
||||
/* Drawing starts here. */
|
||||
/* Paint the background white */
|
||||
cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
|
||||
cairo_paint (cr);
|
||||
/* Draw a black rectangle */
|
||||
cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
|
||||
cairo_set_line_width (cr, 2.0);
|
||||
cairo_rectangle (cr, width/2.0 - 20.0, height/2.0 - 20.0, 40.0, 40.0);
|
||||
cairo_stroke (cr);
|
||||
|
||||
/* Write the surface to a png file and clean up cairo and surface. */
|
||||
cairo_surface_write_to_png (surface, "rectangle.png");
|
||||
cairo_destroy (cr);
|
||||
cairo_surface_destroy (surface);
|
||||
|
||||
return 0;
|
||||
}
|
39
src/misc/da1.c
Normal file
39
src/misc/da1.c
Normal file
|
@ -0,0 +1,39 @@
|
|||
#include <gtk/gtk.h>
|
||||
|
||||
static void
|
||||
draw_function (GtkDrawingArea *area, cairo_t *cr, int width, int height, gpointer user_data) {
|
||||
cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); /* whilte */
|
||||
cairo_paint (cr);
|
||||
cairo_set_line_width (cr, 2.0);
|
||||
cairo_set_source_rgb (cr, 0.0, 0.0, 0.0); /* black */
|
||||
cairo_rectangle (cr, width/2.0 - 20.0, height/2.0 - 20.0, 40.0, 40.0);
|
||||
cairo_stroke (cr);
|
||||
}
|
||||
|
||||
static void
|
||||
on_activate (GApplication *app, gpointer user_data) {
|
||||
GtkWidget *win = gtk_application_window_new (GTK_APPLICATION (app));
|
||||
GtkWidget *area = gtk_drawing_area_new ();
|
||||
|
||||
gtk_window_set_title (GTK_WINDOW (win), "da1");
|
||||
/* Set initial size of width and height */
|
||||
gtk_drawing_area_set_content_width (GTK_DRAWING_AREA (area), 100);
|
||||
gtk_drawing_area_set_content_height (GTK_DRAWING_AREA (area), 100);
|
||||
gtk_drawing_area_set_draw_func (GTK_DRAWING_AREA (area), draw_function, NULL, NULL);
|
||||
gtk_window_set_child (GTK_WINDOW (win), area);
|
||||
|
||||
gtk_widget_show (win);
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char **argv) {
|
||||
GtkApplication *app;
|
||||
int stat;
|
||||
|
||||
app = gtk_application_new ("com.github.ToshioCP.da1", G_APPLICATION_FLAGS_NONE);
|
||||
g_signal_connect (app, "activate", G_CALLBACK (on_activate), NULL);
|
||||
stat =g_application_run (G_APPLICATION (app), argc, argv);
|
||||
g_object_unref (app);
|
||||
return stat;
|
||||
}
|
||||
|
BIN
src/misc/rectangle.png
Normal file
BIN
src/misc/rectangle.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 341 B |
125
src/sec19.src.md
Normal file
125
src/sec19.src.md
Normal file
|
@ -0,0 +1,125 @@
|
|||
# GtkDrawingArea and Cairo
|
||||
|
||||
If you want to draw dynamically, like an image window of gimp graphics editor, GtkDrawingArea widget is the most suitable widget.
|
||||
You can draw or redraw an image in this widget freely.
|
||||
It is called custom drawing.
|
||||
|
||||
GtkDrawingArea provides a cairo context so users can draw images by cairo functions.
|
||||
In this section, I will explain:
|
||||
|
||||
1. Cairo, but briefly.
|
||||
2. GtkDrawingArea with very simple example.
|
||||
|
||||
## Cairo
|
||||
|
||||
Cairo is a two dimensional graphics library.
|
||||
First, you need to know surface, source, mask, destination, cairo context and transformation.
|
||||
|
||||
- surface represents an image.
|
||||
It is like a canvas.
|
||||
We can draw shapes and images with different colors on surfaces.
|
||||
- source pattern, or simply source, is a kind of paint, which will be transfered to destination surface by cairo functions.
|
||||
- mask is image mask used in the transference.
|
||||
- destination is a target surface.
|
||||
- cairo context manages the transference from source to destination through mask with its functions.
|
||||
For example, `cairo_stroke` is a function to draw a path to the destination by the transference.
|
||||
- transformation is applied before the transfer completes.
|
||||
The transformation is called affine, which is a mathematicsterminology, and represented by matrix multiplication and vector addition.
|
||||
Scaling, rotation, reflection, shearing and translation are examples of affine transformation.
|
||||
In this section, we don't use it.
|
||||
That means we only use identity transformation.
|
||||
Therefore, the coordinate in source and mask is the same as the coordinate in destination.
|
||||
|
||||
![Stroke a rectangle](../image/cairo.png)
|
||||
|
||||
The instruction is as follows:
|
||||
|
||||
1. Create a surface.
|
||||
This will be a destnation.
|
||||
2. Create a cairo context with the surface and set it to the destination.
|
||||
3. Create a source pattern within the context.
|
||||
4. Create paths, which are lines, rectangles, arcs, texts or more complicated shapes, to generate a mask.
|
||||
5. Use drawing operator such as `cairo_stroke` to transfer the paint in the source to the destination.
|
||||
6. Save the destination surface to a file if necessary.
|
||||
|
||||
Here's a simple example code that draws a small square and save it as a png file.
|
||||
|
||||
@@@ misc/cairo.c
|
||||
|
||||
- 1: Include the header file of cairo.
|
||||
- 12: `cairo_image_surface_create` creates an image surface.
|
||||
`CAIRO_FORMAT_RGB24` is a constant which means that each pixel has red, green and blue data.
|
||||
Each data has 8 bit quantity.
|
||||
Modern displays have this type of color depth.
|
||||
Width and hieight are pixels and given as integers.
|
||||
- 13: Create cairo context.
|
||||
The surface given as an argument will be set to the destination of the context.
|
||||
- 17: `cairo_set_source_rgb` creates a source pattern, which is a solid white paint.
|
||||
The second to fourth argument is red, green and blue color depth respectively.
|
||||
Their type is float and the values are between zero and one.
|
||||
(0,0,0) is black and (1,1,1) is white.
|
||||
- 18: `cairo_paint` copies everywhere in the source to destination.
|
||||
The destination is filled with white pixels by this command.
|
||||
- 20: Set the source color to black.
|
||||
- 21: `cairo_set_line_width` set the width of lines.
|
||||
In this case, the line width is set to two pixels.
|
||||
(It is because the transformation is identity.
|
||||
If we set it different one, for example scaling with the factor three, the actual width in destnation is six (2x3=6) pixels.)
|
||||
- 22: Draw a rectangle (square).
|
||||
The top-left coordinate is (width/2.0-20.0, height/2.0-20.0) and the width and height have the same length 40.0.
|
||||
- 23: `cairo_stroke` transfer the source to destnation through the rectangle in mask.
|
||||
- 26: Output the image to a png file `rectangle.png`.
|
||||
- 27: Destroy the context. At the same time the source is destroyed.
|
||||
- 28: Destroy the destnation surface.
|
||||
|
||||
To compile this, type the following.
|
||||
|
||||
$ gcc `pkg-config --cflags cairo` cairo.c `pkg-config --libs cairo`
|
||||
|
||||
![rectangle.png](misc/rectangle.png)
|
||||
|
||||
There are lots of documentations in [Cairo's website](https://www.cairographics.org/).
|
||||
If you aren't familiar with cairo, it is strongly recommended to read the [tutorial](https://www.cairographics.org/tutorial/) in the website.
|
||||
|
||||
## GtkDrawingArea
|
||||
|
||||
The following is a very simple example.
|
||||
|
||||
@@@ misc/da1.c
|
||||
|
||||
The function `main` is almost same as before.
|
||||
The two functions `on_activate` and `draw_function` is important in this example.
|
||||
|
||||
- 16: Generate a GtkDrawingArea object.
|
||||
- 20,21: Set the width and height of the contents of the GtkDrawingArea widget.
|
||||
These width and height is the size of the destination surface of the cairo context provided by the widget.
|
||||
- 22: Set a drawng function to the widget.
|
||||
GtkDrawingArea widget uses the function to draw the contents of itself whenever its necessary.
|
||||
For example, when a user drag a mouse pointer and resize a top level window, GtkDrawingArea also changes the size.
|
||||
Then, the whole window needs to be redrawn.
|
||||
|
||||
The drawing function has five parameters.
|
||||
|
||||
void drawing_function (GtkDrawingArea *drawing_area, cairo_t *cr, int width, int height,
|
||||
gpointer user_data);
|
||||
|
||||
The first parameter is the GtkDrawingArea widget which calls the drawing function.
|
||||
However, you can't change any properties, for example `content-width` or `content-height`, in this function.
|
||||
The second parameter is a cairo context given by the widget.
|
||||
The destnation surface of the context is connected to the contents of the widget.
|
||||
What you draw to this surface will appear in the widget on the screen.
|
||||
The third and fourth paranmeters are the size of the destination surface.
|
||||
|
||||
- 3-11: The drawing function.
|
||||
- 4-5: Set the source to be white and paint the destination white.
|
||||
- 7: Set the line width to be 2.
|
||||
- 8: Set the source to be black.
|
||||
- 9: Add a rectangle to the mask.
|
||||
- 10: Draw the rectangle with black color to the destination.
|
||||
|
||||
Compile and run it, then a window with a black rectangle (square) appears.
|
||||
Try resizing the window.
|
||||
The square always appears at the center of the window because the drawing function is invoked every moment the window is resized.
|
||||
|
||||
![Square in the window](../image/da1.png)
|
||||
|
Loading…
Reference in a new issue