mirror of
https://github.com/ToshioCP/Gtk4-tutorial.git
synced 2025-01-28 19:58:41 +01:00
Merge pull request #21 from PaulSchulz/main
Some more edits. Sec 9 and 22.
This commit is contained in:
commit
978e5b1a34
9 changed files with 190 additions and 174 deletions
|
@ -3,8 +3,8 @@
|
||||||
#### Contents of this Repository
|
#### Contents of this Repository
|
||||||
|
|
||||||
This tutorial illustrates how to write C programs with the Gtk4 library.
|
This tutorial illustrates how to write C programs with the Gtk4 library.
|
||||||
It focuses on beginners so the contents are limited to basic things.
|
It focuses on beginners so the contents are limited to the basics.
|
||||||
The table of contents is shown at the end of this abstract.
|
The table of contents is at the end of this abstract.
|
||||||
|
|
||||||
- Section 3 to 21 describes the basics, with the example of a simple editor `tfe` (Text File Editor).
|
- Section 3 to 21 describes the basics, with the example of a simple editor `tfe` (Text File Editor).
|
||||||
- Section 22 to 24 describes GtkDrawingArea.
|
- Section 22 to 24 describes GtkDrawingArea.
|
||||||
|
@ -56,7 +56,7 @@ There is a documentation \("[How to build Gtk4 Tutorial](gfm/Readme_for_develope
|
||||||
1. [String and memory management](gfm/sec6.md)
|
1. [String and memory management](gfm/sec6.md)
|
||||||
1. [Widgets (3)](gfm/sec7.md)
|
1. [Widgets (3)](gfm/sec7.md)
|
||||||
1. [Defining a Child object](gfm/sec8.md)
|
1. [Defining a Child object](gfm/sec8.md)
|
||||||
1. [Ui file and GtkBuilder](gfm/sec9.md)
|
1. [The User Interface (UI) file and GtkBuilder](gfm/sec9.md)
|
||||||
1. [Build system](gfm/sec10.md)
|
1. [Build system](gfm/sec10.md)
|
||||||
1. [Initialization and destruction of instances](gfm/sec11.md)
|
1. [Initialization and destruction of instances](gfm/sec11.md)
|
||||||
1. [Signals](gfm/sec12.md)
|
1. [Signals](gfm/sec12.md)
|
||||||
|
|
173
gfm/sec22.md
173
gfm/sec22.md
|
@ -2,49 +2,54 @@ Up: [Readme.md](../Readme.md), Prev: [Section 21](sec21.md), Next: [Section 23]
|
||||||
|
|
||||||
# GtkDrawingArea and Cairo
|
# GtkDrawingArea and Cairo
|
||||||
|
|
||||||
If you want to draw dynamically, like an image window of gimp graphics editor, GtkDrawingArea widget is the most suitable widget.
|
If you want to draw dynamically on the screen, like an image window of gimp graphics editor, the GtkDrawingArea widget is the most suitable widget.
|
||||||
You can draw or redraw an image in this widget freely.
|
You can freely draw or redraw an image in this widget.
|
||||||
It is called custom drawing.
|
This is called custom drawing.
|
||||||
|
|
||||||
GtkDrawingArea provides a cairo context so users can draw images by cairo functions.
|
GtkDrawingArea provides a cairo drawing context so users can draw images by using cairo functions.
|
||||||
In this section, I will explain:
|
In this section, I will explain:
|
||||||
|
|
||||||
1. Cairo, but briefly.
|
1. Cairo, but only briefly; and
|
||||||
2. GtkDrawingArea with very simple example.
|
2. GtkDrawingArea, with a very simple example.
|
||||||
|
|
||||||
## Cairo
|
## Cairo
|
||||||
|
|
||||||
Cairo is a two dimensional graphics library.
|
Cairo is a set of two dimensional graphical drawing functions (or graphics library).
|
||||||
First, you need to know surface, source, mask, destination, cairo context and transformation.
|
There is a lot of documentation on [Cairo's website](https://www.cairographics.org/).
|
||||||
|
If you aren't familiar with Cairo, it is worth reading their [tutorial](https://www.cairographics.org/tutorial/).
|
||||||
|
|
||||||
- Surface represents an image.
|
The following is a gentle introduction on the Cairo library and how to use it.
|
||||||
|
Firstly, in order to use Cairo you need to know about surfaces, sources, masks, destinations, cairo context and transformations.
|
||||||
|
|
||||||
|
- A surface represents an image.
|
||||||
It is like a canvas.
|
It is like a canvas.
|
||||||
We can draw shapes and images with different colors on surfaces.
|
We can draw shapes and images with different colors on surfaces.
|
||||||
- Source pattern, or simply source, is a kind of paint, which will be transferred to destination surface by cairo functions.
|
- The source pattern, or simply source, is like paint, which will be transferred to destination surface by cairo functions.
|
||||||
- Mask is image mask used in the transference.
|
- The mask describes the area to be used in the copy;
|
||||||
- Destination is a target surface.
|
- The destination is a target surface;
|
||||||
- Cairo context manages the transference from source to destination through mask with its functions.
|
- The cairo context manages the transfer 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.
|
For example, `cairo_stroke` is a function to draw a path to the destination by the transfer.
|
||||||
- Transformation is applied before the transfer completes.
|
- A transformation can be applied before the transfer completes.
|
||||||
The transformation is called affine, which is a mathematics terminology, and represented by matrix multiplication and vector addition.
|
The transformation which is applied is called affine, which is a mathematical term meaning transofrmations
|
||||||
Scaling, rotation, reflection, shearing and translation are examples of affine transformation.
|
that preserve straight lines.
|
||||||
In this section, we don't use it.
|
Scaling, rotating, reflecting, shearing and translating are all examples of affine transformations.
|
||||||
That means we only use identity transformation.
|
They are mathematically represented by matrix multiplication and vector addition.
|
||||||
Therefore, the coordinate in source and mask is the same as the coordinate in destination.
|
In this section we don't use it, instead we will only use the identity transformation.
|
||||||
|
This means that the coordinates in the source and mask are the same as the coordinates in destination.
|
||||||
|
|
||||||
![Stroke a rectangle](../image/cairo.png)
|
![Stroke a rectangle](../image/cairo.png)
|
||||||
|
|
||||||
The instruction is as follows:
|
The instruction is as follows:
|
||||||
|
|
||||||
1. Create a surface.
|
1. Create a surface.
|
||||||
This will be a destination.
|
This will be the destination.
|
||||||
2. Create a cairo context with the surface and the surface will be the destination of the context.
|
2. Create a cairo context with the surface, the surface will be the destination of the context.
|
||||||
3. Create a source pattern within the context.
|
3. Create a source pattern within the context.
|
||||||
4. Create paths, which are lines, rectangles, arcs, texts or more complicated shapes in the mask.
|
4. Create paths, which are lines, rectangles, arcs, texts or more complicated shapes in the mask.
|
||||||
5. Use drawing operator such as `cairo_stroke` to transfer the paint in the source to the destination.
|
5. Use a 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.
|
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.
|
Here's a simple example program that draws a small square and saves it as a png file.
|
||||||
|
|
||||||
~~~C
|
~~~C
|
||||||
1 #include <cairo.h>
|
1 #include <cairo.h>
|
||||||
|
@ -69,40 +74,43 @@ Here's a simple example code that draws a small square and save it as a png file
|
||||||
20 /* Draw a black rectangle */
|
20 /* Draw a black rectangle */
|
||||||
21 cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
|
21 cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
|
||||||
22 cairo_set_line_width (cr, 2.0);
|
22 cairo_set_line_width (cr, 2.0);
|
||||||
23 cairo_rectangle (cr, width/2.0 - square_size/2, height/2.0 - square_size/2, square_size, square_size);
|
23 cairo_rectangle (cr,
|
||||||
24 cairo_stroke (cr);
|
24 width/2.0 - square_size/2,
|
||||||
25
|
25 height/2.0 - square_size/2,
|
||||||
26 /* Write the surface to a png file and clean up cairo and surface. */
|
26 square_size,
|
||||||
27 cairo_surface_write_to_png (surface, "rectangle.png");
|
27 square_size);
|
||||||
28 cairo_destroy (cr);
|
28 cairo_stroke (cr);
|
||||||
29 cairo_surface_destroy (surface);
|
29
|
||||||
30
|
30 /* Write the surface to a png file and clean up cairo and surface. */
|
||||||
31 return 0;
|
31 cairo_surface_write_to_png (surface, "rectangle.png");
|
||||||
32 }
|
32 cairo_destroy (cr);
|
||||||
|
33 cairo_surface_destroy (surface);
|
||||||
|
34
|
||||||
|
35 return 0;
|
||||||
|
36 }
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
- 1: Includes the header file of Cairo.
|
- 1: Includes the header file of Cairo.
|
||||||
- 6: `cairo_surface_t` is the type of a surface.
|
- 6: `cairo_surface_t` is the type of a surface.
|
||||||
- 7: `cairo_t` is the type of a cairo context.
|
- 7: `cairo_t` is the type of a cairo context.
|
||||||
- 8-10: `width` and `height` is the size of `surface`.
|
- 8-10: `width` and `height` are the size of `surface`.
|
||||||
`square_size` is the size of a square drawn on the surface.
|
`square_size` is the size of a square to be drawn on the surface.
|
||||||
- 13: `cairo_image_surface_create` creates an image surface.
|
- 13: `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.
|
`CAIRO_FORMAT_RGB24` is a constant which means that each pixel has red, green and blue data,
|
||||||
Each data has 8 bits quantity.
|
and each data point is an 8 bits number (for 24 bits in total).
|
||||||
Therefore, 24 bits (3x8=24) is the size of RGB24.
|
|
||||||
Modern displays have this type of color depth.
|
Modern displays have this type of color depth.
|
||||||
Width and height are pixels and given as integers.
|
Width and height are in pixels and given as integers.
|
||||||
- 14: Creates cairo context.
|
- 14: Creates cairo context.
|
||||||
The surface given as an argument will be the destination of the context.
|
The surface given as an argument will be the destination of the context.
|
||||||
- 18: `cairo_set_source_rgb` creates a source pattern, which is a solid white paint.
|
- 18: `cairo_set_source_rgb` creates a source pattern, which in this case is a solid white paint.
|
||||||
The second to fourth argument is red, green and blue color depth respectively.
|
The second to fourth argument are red, green and blue color values respectively, and they are
|
||||||
Their type is float and the values are between zero and one.
|
of type float. The values are between zero (0.0) and one (1.0), with
|
||||||
(0,0,0) is black and (1,1,1) is white.
|
black being given by (0.0,0.0,0.0) and white by (1.0,1.0,1.0).
|
||||||
- 19: `cairo_paint` copies everywhere in the source to destination.
|
- 19: `cairo_paint` copies everywhere in the source to destination.
|
||||||
The destination is filled with white pixels with this command.
|
The destination is filled with white pixels with this command.
|
||||||
- 21: Sets the source color to black.
|
- 21: Sets the source color to black.
|
||||||
- 22: `cairo_set_line_width` set the width of lines.
|
- 22: `cairo_set_line_width` set the width of lines.
|
||||||
In this case, the line width is set to two pixels.
|
In this case, the line width is set to be two pixels and will end up that same size.
|
||||||
(It is because the transformation is identity.
|
(It is because the transformation is identity.
|
||||||
If the transformation isn't identity, for example scaling with the factor three, the actual width in destination will be six (2x3=6) pixels.)
|
If the transformation isn't identity, for example scaling with the factor three, the actual width in destination will be six (2x3=6) pixels.)
|
||||||
- 23: Draws a rectangle (square) on the mask.
|
- 23: Draws a rectangle (square) on the mask.
|
||||||
|
@ -118,8 +126,7 @@ To compile this, type the following.
|
||||||
|
|
||||||
![rectangle.png](../image/rectangle.png)
|
![rectangle.png](../image/rectangle.png)
|
||||||
|
|
||||||
There are lots of documentations in [Cairo's website](https://www.cairographics.org/).
|
See the [Cairo's website](https://www.cairographics.org/) for more details.
|
||||||
If you aren't familiar with Cairo, it is strongly recommended to read the [tutorial](https://www.cairographics.org/tutorial/) in the website.
|
|
||||||
|
|
||||||
## GtkDrawingArea
|
## GtkDrawingArea
|
||||||
|
|
||||||
|
@ -132,46 +139,49 @@ The following is a very simple example.
|
||||||
4 draw_function (GtkDrawingArea *area, cairo_t *cr, int width, int height, gpointer user_data) {
|
4 draw_function (GtkDrawingArea *area, cairo_t *cr, int width, int height, gpointer user_data) {
|
||||||
5 int square_size = 40.0;
|
5 int square_size = 40.0;
|
||||||
6
|
6
|
||||||
7 cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); /* whilte */
|
7 cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); /* white */
|
||||||
8 cairo_paint (cr);
|
8 cairo_paint (cr);
|
||||||
9 cairo_set_line_width (cr, 2.0);
|
9 cairo_set_line_width (cr, 2.0);
|
||||||
10 cairo_set_source_rgb (cr, 0.0, 0.0, 0.0); /* black */
|
10 cairo_set_source_rgb (cr, 0.0, 0.0, 0.0); /* black */
|
||||||
11 cairo_rectangle (cr, width/2.0 - square_size/2, height/2.0 - square_size/2, square_size, square_size);
|
11 cairo_rectangle (cr,
|
||||||
12 cairo_stroke (cr);
|
12 width/2.0 - square_size/2,
|
||||||
13 }
|
13 height/2.0 - square_size/2,
|
||||||
14
|
14 square_size,
|
||||||
15 static void
|
15 square_size);
|
||||||
16 app_activate (GApplication *app, gpointer user_data) {
|
16 cairo_stroke (cr);
|
||||||
17 GtkWidget *win = gtk_application_window_new (GTK_APPLICATION (app));
|
17 }
|
||||||
18 GtkWidget *area = gtk_drawing_area_new ();
|
18
|
||||||
19
|
19 static void
|
||||||
20 gtk_window_set_title (GTK_WINDOW (win), "da1");
|
20 app_activate (GApplication *app, gpointer user_data) {
|
||||||
21 gtk_drawing_area_set_draw_func (GTK_DRAWING_AREA (area), draw_function, NULL, NULL);
|
21 GtkWidget *win = gtk_application_window_new (GTK_APPLICATION (app));
|
||||||
22 gtk_window_set_child (GTK_WINDOW (win), area);
|
22 GtkWidget *area = gtk_drawing_area_new ();
|
||||||
23
|
23
|
||||||
24 gtk_widget_show (win);
|
24 gtk_window_set_title (GTK_WINDOW (win), "da1");
|
||||||
25 }
|
25 gtk_drawing_area_set_draw_func (GTK_DRAWING_AREA (area), draw_function, NULL, NULL);
|
||||||
26
|
26 gtk_window_set_child (GTK_WINDOW (win), area);
|
||||||
27 #define APPLICATION_ID "com.github.ToshioCP.da1"
|
27
|
||||||
28
|
28 gtk_widget_show (win);
|
||||||
29 int
|
29 }
|
||||||
30 main (int argc, char **argv) {
|
30
|
||||||
31 GtkApplication *app;
|
31 #define APPLICATION_ID "com.github.ToshioCP.da1"
|
||||||
32 int stat;
|
32
|
||||||
33
|
33 int
|
||||||
34 app = gtk_application_new (APPLICATION_ID, G_APPLICATION_FLAGS_NONE);
|
34 main (int argc, char **argv) {
|
||||||
35 g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL);
|
35 GtkApplication *app;
|
||||||
36 stat =g_application_run (G_APPLICATION (app), argc, argv);
|
36 int stat;
|
||||||
37 g_object_unref (app);
|
37
|
||||||
38 return stat;
|
38 app = gtk_application_new (APPLICATION_ID, G_APPLICATION_FLAGS_NONE);
|
||||||
39 }
|
39 g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL);
|
||||||
40
|
40 stat =g_application_run (G_APPLICATION (app), argc, argv);
|
||||||
|
41 g_object_unref (app);
|
||||||
|
42 return stat;
|
||||||
|
43 }
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
The function `main` is almost same as before.
|
The function `main` is almost same as before.
|
||||||
The two functions `app_activate` and `draw_function` is important in this example.
|
The two functions `app_activate` and `draw_function` are important in this example.
|
||||||
|
|
||||||
- 18: Creates a GtkDrawingArea instance.
|
- 18: Creates a GtkDrawingArea instance; and
|
||||||
- 21: Sets a drawing function of the widget.
|
- 21: Sets a drawing function of the widget.
|
||||||
GtkDrawingArea widget uses the function to draw the contents of itself whenever its necessary.
|
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.
|
For example, when a user drag a mouse pointer and resize a top-level window, GtkDrawingArea also changes the size.
|
||||||
|
@ -191,7 +201,7 @@ The second parameter is a cairo context given by the widget.
|
||||||
The destination surface of the context is connected to the contents of the widget.
|
The destination 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.
|
What you draw to this surface will appear in the widget on the screen.
|
||||||
The third and fourth parameters are the size of the destination surface.
|
The third and fourth parameters are the size of the destination surface.
|
||||||
Now, look at the program of the example again.
|
Now, look at the program example again.
|
||||||
|
|
||||||
- 3-13: The drawing function.
|
- 3-13: The drawing function.
|
||||||
- 7-8: Sets the source to be white and paint the destination white.
|
- 7-8: Sets the source to be white and paint the destination white.
|
||||||
|
@ -202,9 +212,8 @@ Now, look at the program of the example again.
|
||||||
|
|
||||||
Compile and run it, then a window with a black rectangle (square) appears.
|
Compile and run it, then a window with a black rectangle (square) appears.
|
||||||
Try resizing the window.
|
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.
|
The square always appears at the center of the window because the drawing function is invoked each time the window is resized.
|
||||||
|
|
||||||
![Square in the window](../image/da1.png)
|
![Square in the window](../image/da1.png)
|
||||||
|
|
||||||
|
|
||||||
Up: [Readme.md](../Readme.md), Prev: [Section 21](sec21.md), Next: [Section 23](sec23.md)
|
Up: [Readme.md](../Readme.md), Prev: [Section 21](sec21.md), Next: [Section 23](sec23.md)
|
||||||
|
|
39
gfm/sec9.md
39
gfm/sec9.md
|
@ -1,13 +1,13 @@
|
||||||
Up: [Readme.md](../Readme.md), Prev: [Section 8](sec8.md), Next: [Section 10](sec10.md)
|
Up: [Readme.md](../Readme.md), Prev: [Section 8](sec8.md), Next: [Section 10](sec10.md)
|
||||||
|
|
||||||
# Ui file and GtkBuilder
|
# The User Interface (UI) file and GtkBuilder
|
||||||
|
|
||||||
## New, open and save button
|
## New, Open and Save button
|
||||||
|
|
||||||
We made the simplest editor in the previous section.
|
In the last section we made the almost simplest editor possible.
|
||||||
It reads the files in `app_open` function at start-up and writes them when closing the window.
|
It reads files in the `app_open` function at start-up and writes them out when closing the window.
|
||||||
It works but is not good.
|
It works but is not very good.
|
||||||
It is better to make "New", "Open", "Save" and "Close" buttons.
|
It would be better if we had "New", "Open", "Save" and "Close" buttons.
|
||||||
This section describes how to put those buttons into the window.
|
This section describes how to put those buttons into the window.
|
||||||
Signals and handlers will be explained later.
|
Signals and handlers will be explained later.
|
||||||
|
|
||||||
|
@ -105,7 +105,7 @@ The function `app_open` in the source code `tfe2.c` is as follows.
|
||||||
86 }
|
86 }
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
The point is how to build the window.
|
The aim is to build the widgets of the main application window.
|
||||||
|
|
||||||
- 25-27: Creates a GtkApplicationWindow instance and sets the title and default size.
|
- 25-27: Creates a GtkApplicationWindow instance and sets the title and default size.
|
||||||
- 29-30: Creates a GtkBox instance `boxv`.
|
- 29-30: Creates a GtkBox instance `boxv`.
|
||||||
|
@ -124,18 +124,18 @@ This makes the label expands horizontally as long as possible.
|
||||||
This makes it expand horizontally and vertically as big as possible.
|
This makes it expand horizontally and vertically as big as possible.
|
||||||
It is appended to `boxv` as the second child.
|
It is appended to `boxv` as the second child.
|
||||||
|
|
||||||
The number of lines is 33(=57-25+1) to build the widgets.
|
The number of lines to build the widgets is 33(=57-25+1).
|
||||||
And we needed many variables (`boxv`, `boxh`, `dmy1`, ...).
|
We also needed many additional variables (`boxv`, `boxh`, `dmy1`, ...),
|
||||||
Most of them aren't necessary except building the widgets.
|
most of which weren't necessary, except for building the widgets.
|
||||||
Are there any good solution to reduce these work?
|
Are there any good solution to reduce these work?
|
||||||
|
|
||||||
Gtk provides GtkBuilder.
|
Gtk provides GtkBuilder.
|
||||||
It reads ui data and builds a window.
|
It reads user interface (UI) data and builds a window.
|
||||||
It reduces the cumbersome work.
|
It reduces this cumbersome work.
|
||||||
|
|
||||||
## Ui file
|
## The UI File
|
||||||
|
|
||||||
First, let's look at the ui file `tfe3.ui` that defines a structure of the widgets.
|
First, let's look at the UI file `tfe3.ui` that is used to define the widget structure.
|
||||||
|
|
||||||
~~~xml
|
~~~xml
|
||||||
1 <?xml version="1.0" encoding="UTF-8"?>
|
1 <?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
@ -200,11 +200,11 @@ First, let's look at the ui file `tfe3.ui` that defines a structure of the widge
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
The structure of this file is XML.
|
The structure of this file is XML.
|
||||||
Constructs beginning with `<` and ending with `>` are called tags.
|
Constructs that begin with `<` and end with `>` are called tags.
|
||||||
And there are two types of tags, start tag and end tag.
|
There are two types of tags, the start tag and the end tag.
|
||||||
For example, `<interface>` is a start tag and `</interface>` is an end tag.
|
For example, `<interface>` is a start tag and `</interface>` is an end tag.
|
||||||
Ui file begins and ends with interface tags.
|
The UI file begins and ends with interface tags.
|
||||||
Some tags, for example, object tags can have a class and id attributes in the start tag.
|
Some tags, for example object tags, can have a class and id attributes in their start tag.
|
||||||
|
|
||||||
- 1: The first line is XML declaration.
|
- 1: The first line is XML declaration.
|
||||||
It specifies that the version of XML is 1.0 and the encoding is UTF-8.
|
It specifies that the version of XML is 1.0 and the encoding is UTF-8.
|
||||||
|
@ -431,7 +431,7 @@ It describes resource files.
|
||||||
However, this xml has only one gresource.
|
However, this xml has only one gresource.
|
||||||
- 3: The gresource has a prefix `/com/github/ToshioCP/tfe3`.
|
- 3: The gresource has a prefix `/com/github/ToshioCP/tfe3`.
|
||||||
- 4: The gresource has `tfe3.ui`.
|
- 4: The gresource has `tfe3.ui`.
|
||||||
And it is pointed by `/com/github/ToshioCP/tfe3/tfe3.ui` because it needs prefix.
|
And it is pointed by `/com/github/ToshioCP/tfe3/tfe3.ui` because it needs prefix.
|
||||||
If you want to add more files, then insert them between line 4 and 5.
|
If you want to add more files, then insert them between line 4 and 5.
|
||||||
|
|
||||||
Save this xml text to `tfe3.gresource.xml`.
|
Save this xml text to `tfe3.gresource.xml`.
|
||||||
|
@ -485,5 +485,4 @@ build = gtk_builder_new_from_resource ("/com/github/ToshioCP/tfe3/tfe3.ui");
|
||||||
Then, compile and run it.
|
Then, compile and run it.
|
||||||
The window appears and it is the same as the screenshot at the beginning of this page.
|
The window appears and it is the same as the screenshot at the beginning of this page.
|
||||||
|
|
||||||
|
|
||||||
Up: [Readme.md](../Readme.md), Prev: [Section 8](sec8.md), Next: [Section 10](sec10.md)
|
Up: [Readme.md](../Readme.md), Prev: [Section 8](sec8.md), Next: [Section 10](sec10.md)
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
#### Contents of this Repository
|
#### Contents of this Repository
|
||||||
|
|
||||||
This tutorial illustrates how to write C programs with the Gtk4 library.
|
This tutorial illustrates how to write C programs with the Gtk4 library.
|
||||||
It focuses on beginners so the contents are limited to basic things.
|
It focuses on beginners so the contents are limited to the basics.
|
||||||
The table of contents is shown at the end of this abstract.
|
The table of contents is at the end of this abstract.
|
||||||
|
|
||||||
- Section 3 to 21 describes the basics, with the example of a simple editor `tfe` (Text File Editor).
|
- Section 3 to 21 describes the basics, with the example of a simple editor `tfe` (Text File Editor).
|
||||||
- Section 22 to 24 describes GtkDrawingArea.
|
- Section 22 to 24 describes GtkDrawingArea.
|
||||||
|
|
|
@ -20,7 +20,11 @@ main (int argc, char **argv)
|
||||||
/* Draw a black rectangle */
|
/* Draw a black rectangle */
|
||||||
cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
|
cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
|
||||||
cairo_set_line_width (cr, 2.0);
|
cairo_set_line_width (cr, 2.0);
|
||||||
cairo_rectangle (cr, width/2.0 - square_size/2, height/2.0 - square_size/2, square_size, square_size);
|
cairo_rectangle (cr,
|
||||||
|
width/2.0 - square_size/2,
|
||||||
|
height/2.0 - square_size/2,
|
||||||
|
square_size,
|
||||||
|
square_size);
|
||||||
cairo_stroke (cr);
|
cairo_stroke (cr);
|
||||||
|
|
||||||
/* Write the surface to a png file and clean up cairo and surface. */
|
/* Write the surface to a png file and clean up cairo and surface. */
|
||||||
|
|
|
@ -4,11 +4,15 @@ static void
|
||||||
draw_function (GtkDrawingArea *area, cairo_t *cr, int width, int height, gpointer user_data) {
|
draw_function (GtkDrawingArea *area, cairo_t *cr, int width, int height, gpointer user_data) {
|
||||||
int square_size = 40.0;
|
int square_size = 40.0;
|
||||||
|
|
||||||
cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); /* whilte */
|
cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); /* white */
|
||||||
cairo_paint (cr);
|
cairo_paint (cr);
|
||||||
cairo_set_line_width (cr, 2.0);
|
cairo_set_line_width (cr, 2.0);
|
||||||
cairo_set_source_rgb (cr, 0.0, 0.0, 0.0); /* black */
|
cairo_set_source_rgb (cr, 0.0, 0.0, 0.0); /* black */
|
||||||
cairo_rectangle (cr, width/2.0 - square_size/2, height/2.0 - square_size/2, square_size, square_size);
|
cairo_rectangle (cr,
|
||||||
|
width/2.0 - square_size/2,
|
||||||
|
height/2.0 - square_size/2,
|
||||||
|
square_size,
|
||||||
|
square_size);
|
||||||
cairo_stroke (cr);
|
cairo_stroke (cr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,4 +41,3 @@ main (int argc, char **argv) {
|
||||||
g_object_unref (app);
|
g_object_unref (app);
|
||||||
return stat;
|
return stat;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,48 +1,53 @@
|
||||||
# GtkDrawingArea and Cairo
|
# GtkDrawingArea and Cairo
|
||||||
|
|
||||||
If you want to draw dynamically, like an image window of gimp graphics editor, GtkDrawingArea widget is the most suitable widget.
|
If you want to draw dynamically on the screen, like an image window of gimp graphics editor, the GtkDrawingArea widget is the most suitable widget.
|
||||||
You can draw or redraw an image in this widget freely.
|
You can freely draw or redraw an image in this widget.
|
||||||
It is called custom drawing.
|
This is called custom drawing.
|
||||||
|
|
||||||
GtkDrawingArea provides a cairo context so users can draw images by cairo functions.
|
GtkDrawingArea provides a cairo drawing context so users can draw images by using cairo functions.
|
||||||
In this section, I will explain:
|
In this section, I will explain:
|
||||||
|
|
||||||
1. Cairo, but briefly.
|
1. Cairo, but only briefly; and
|
||||||
2. GtkDrawingArea with very simple example.
|
2. GtkDrawingArea, with a very simple example.
|
||||||
|
|
||||||
## Cairo
|
## Cairo
|
||||||
|
|
||||||
Cairo is a two dimensional graphics library.
|
Cairo is a set of two dimensional graphical drawing functions (or graphics library).
|
||||||
First, you need to know surface, source, mask, destination, cairo context and transformation.
|
There is a lot of documentation on [Cairo's website](https://www.cairographics.org/).
|
||||||
|
If you aren't familiar with Cairo, it is worth reading their [tutorial](https://www.cairographics.org/tutorial/).
|
||||||
|
|
||||||
- Surface represents an image.
|
The following is a gentle introduction to the Cairo library and how to use it.
|
||||||
|
Firstly, in order to use Cairo you need to know about surfaces, sources, masks, destinations, cairo context and transformations.
|
||||||
|
|
||||||
|
- A surface represents an image.
|
||||||
It is like a canvas.
|
It is like a canvas.
|
||||||
We can draw shapes and images with different colors on surfaces.
|
We can draw shapes and images with different colors on surfaces.
|
||||||
- Source pattern, or simply source, is a kind of paint, which will be transferred to destination surface by cairo functions.
|
- The source pattern, or simply source, is like paint, which will be transferred to destination surface by cairo functions.
|
||||||
- Mask is image mask used in the transference.
|
- The mask describes the area to be used in the copy;
|
||||||
- Destination is a target surface.
|
- The destination is a target surface;
|
||||||
- Cairo context manages the transference from source to destination through mask with its functions.
|
- The cairo context manages the transfer 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.
|
For example, `cairo_stroke` is a function to draw a path to the destination by the transfer.
|
||||||
- Transformation is applied before the transfer completes.
|
- A transformation can be applied before the transfer completes.
|
||||||
The transformation is called affine, which is a mathematics terminology, and represented by matrix multiplication and vector addition.
|
The transformation which is applied is called affine, which is a mathematical term meaning transofrmations
|
||||||
Scaling, rotation, reflection, shearing and translation are examples of affine transformation.
|
that preserve straight lines.
|
||||||
In this section, we don't use it.
|
Scaling, rotating, reflecting, shearing and translating are all examples of affine transformations.
|
||||||
That means we only use identity transformation.
|
They are mathematically represented by matrix multiplication and vector addition.
|
||||||
Therefore, the coordinate in source and mask is the same as the coordinate in destination.
|
In this section we don't use it, instead we will only use the identity transformation.
|
||||||
|
This means that the coordinates in the source and mask are the same as the coordinates in destination.
|
||||||
|
|
||||||
![Stroke a rectangle](../image/cairo.png){width=9.0cm height=6.0cm}
|
![Stroke a rectangle](../image/cairo.png){width=9.0cm height=6.0cm}
|
||||||
|
|
||||||
The instruction is as follows:
|
The instruction is as follows:
|
||||||
|
|
||||||
1. Create a surface.
|
1. Create a surface.
|
||||||
This will be a destination.
|
This will be the destination.
|
||||||
2. Create a cairo context with the surface and the surface will be the destination of the context.
|
2. Create a cairo context with the surface, the surface will be the destination of the context.
|
||||||
3. Create a source pattern within the context.
|
3. Create a source pattern within the context.
|
||||||
4. Create paths, which are lines, rectangles, arcs, texts or more complicated shapes in the mask.
|
4. Create paths, which are lines, rectangles, arcs, texts or more complicated shapes in the mask.
|
||||||
5. Use drawing operator such as `cairo_stroke` to transfer the paint in the source to the destination.
|
5. Use a 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.
|
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.
|
Here's a simple example program that draws a small square and saves it as a png file.
|
||||||
|
|
||||||
@@@include
|
@@@include
|
||||||
misc/cairo.c
|
misc/cairo.c
|
||||||
|
@ -51,25 +56,24 @@ misc/cairo.c
|
||||||
- 1: Includes the header file of Cairo.
|
- 1: Includes the header file of Cairo.
|
||||||
- 6: `cairo_surface_t` is the type of a surface.
|
- 6: `cairo_surface_t` is the type of a surface.
|
||||||
- 7: `cairo_t` is the type of a cairo context.
|
- 7: `cairo_t` is the type of a cairo context.
|
||||||
- 8-10: `width` and `height` is the size of `surface`.
|
- 8-10: `width` and `height` are the size of `surface`.
|
||||||
`square_size` is the size of a square drawn on the surface.
|
`square_size` is the size of a square to be drawn on the surface.
|
||||||
- 13: `cairo_image_surface_create` creates an image surface.
|
- 13: `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.
|
`CAIRO_FORMAT_RGB24` is a constant which means that each pixel has red, green and blue data,
|
||||||
Each data has 8 bits quantity.
|
and each data point is an 8 bits number (for 24 bits in total).
|
||||||
Therefore, 24 bits (3x8=24) is the size of RGB24.
|
|
||||||
Modern displays have this type of color depth.
|
Modern displays have this type of color depth.
|
||||||
Width and height are pixels and given as integers.
|
Width and height are in pixels and given as integers.
|
||||||
- 14: Creates cairo context.
|
- 14: Creates cairo context.
|
||||||
The surface given as an argument will be the destination of the context.
|
The surface given as an argument will be the destination of the context.
|
||||||
- 18: `cairo_set_source_rgb` creates a source pattern, which is a solid white paint.
|
- 18: `cairo_set_source_rgb` creates a source pattern, which in this case is a solid white paint.
|
||||||
The second to fourth argument is red, green and blue color depth respectively.
|
The second to fourth argument are red, green and blue color values respectively, and they are
|
||||||
Their type is float and the values are between zero and one.
|
of type float. The values are between zero (0.0) and one (1.0), with
|
||||||
(0,0,0) is black and (1,1,1) is white.
|
black being given by (0.0,0.0,0.0) and white by (1.0,1.0,1.0).
|
||||||
- 19: `cairo_paint` copies everywhere in the source to destination.
|
- 19: `cairo_paint` copies everywhere in the source to destination.
|
||||||
The destination is filled with white pixels with this command.
|
The destination is filled with white pixels with this command.
|
||||||
- 21: Sets the source color to black.
|
- 21: Sets the source color to black.
|
||||||
- 22: `cairo_set_line_width` set the width of lines.
|
- 22: `cairo_set_line_width` set the width of lines.
|
||||||
In this case, the line width is set to two pixels.
|
In this case, the line width is set to be two pixels and will end up that same size.
|
||||||
(It is because the transformation is identity.
|
(It is because the transformation is identity.
|
||||||
If the transformation isn't identity, for example scaling with the factor three, the actual width in destination will be six (2x3=6) pixels.)
|
If the transformation isn't identity, for example scaling with the factor three, the actual width in destination will be six (2x3=6) pixels.)
|
||||||
- 23: Draws a rectangle (square) on the mask.
|
- 23: Draws a rectangle (square) on the mask.
|
||||||
|
@ -85,8 +89,7 @@ To compile this, type the following.
|
||||||
|
|
||||||
![rectangle.png](../image/rectangle.png)
|
![rectangle.png](../image/rectangle.png)
|
||||||
|
|
||||||
There are lots of documentations in [Cairo's website](https://www.cairographics.org/).
|
See the [Cairo's website](https://www.cairographics.org/) for more details.
|
||||||
If you aren't familiar with Cairo, it is strongly recommended to read the [tutorial](https://www.cairographics.org/tutorial/) in the website.
|
|
||||||
|
|
||||||
## GtkDrawingArea
|
## GtkDrawingArea
|
||||||
|
|
||||||
|
@ -97,9 +100,9 @@ misc/da1.c
|
||||||
@@@
|
@@@
|
||||||
|
|
||||||
The function `main` is almost same as before.
|
The function `main` is almost same as before.
|
||||||
The two functions `app_activate` and `draw_function` is important in this example.
|
The two functions `app_activate` and `draw_function` are important in this example.
|
||||||
|
|
||||||
- 18: Creates a GtkDrawingArea instance.
|
- 18: Creates a GtkDrawingArea instance; and
|
||||||
- 21: Sets a drawing function of the widget.
|
- 21: Sets a drawing function of the widget.
|
||||||
GtkDrawingArea widget uses the function to draw the contents of itself whenever its necessary.
|
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.
|
For example, when a user drag a mouse pointer and resize a top-level window, GtkDrawingArea also changes the size.
|
||||||
|
@ -119,7 +122,7 @@ The second parameter is a cairo context given by the widget.
|
||||||
The destination surface of the context is connected to the contents of the widget.
|
The destination 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.
|
What you draw to this surface will appear in the widget on the screen.
|
||||||
The third and fourth parameters are the size of the destination surface.
|
The third and fourth parameters are the size of the destination surface.
|
||||||
Now, look at the program of the example again.
|
Now, look at the program example again.
|
||||||
|
|
||||||
- 3-13: The drawing function.
|
- 3-13: The drawing function.
|
||||||
- 7-8: Sets the source to be white and paint the destination white.
|
- 7-8: Sets the source to be white and paint the destination white.
|
||||||
|
@ -130,7 +133,6 @@ Now, look at the program of the example again.
|
||||||
|
|
||||||
Compile and run it, then a window with a black rectangle (square) appears.
|
Compile and run it, then a window with a black rectangle (square) appears.
|
||||||
Try resizing the window.
|
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.
|
The square always appears at the center of the window because the drawing function is invoked each time the window is resized.
|
||||||
|
|
||||||
![Square in the window](../image/da1.png){width=8cm height=3.4cm}
|
![Square in the window](../image/da1.png){width=8cm height=3.4cm}
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,7 @@ So, readers can skip that part of this sections.
|
||||||
The documentation of turtle is [here](turtle/turtle_doc.src.md).
|
The documentation of turtle is [here](turtle/turtle_doc.src.md).
|
||||||
@@@elif html
|
@@@elif html
|
||||||
The documentation of turtle is [here](../html/turtle_doc.html).
|
The documentation of turtle is [here](../html/turtle_doc.html).
|
||||||
@@@if latex
|
@@@elif latex
|
||||||
The documentation of turtle is in the appendix.
|
The documentation of turtle is in the appendix.
|
||||||
@@@end
|
@@@end
|
||||||
I'll show you a simple example.
|
I'll show you a simple example.
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
# Ui file and GtkBuilder
|
# The User Interface (UI) file and GtkBuilder
|
||||||
|
|
||||||
## New, open and save button
|
## New, Open and Save button
|
||||||
|
|
||||||
We made the simplest editor in the previous section.
|
In the last section we made the almost simplest editor possible.
|
||||||
It reads the files in `app_open` function at start-up and writes them when closing the window.
|
It reads files in the `app_open` function at start-up and writes them out when closing the window.
|
||||||
It works but is not good.
|
It works but is not very good.
|
||||||
It is better to make "New", "Open", "Save" and "Close" buttons.
|
It would be better if we had "New", "Open", "Save" and "Close" buttons.
|
||||||
This section describes how to put those buttons into the window.
|
This section describes how to put those buttons into the window.
|
||||||
Signals and handlers will be explained later.
|
Signals and handlers will be explained later.
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ The function `app_open` in the source code `tfe2.c` is as follows.
|
||||||
tfe/tfe2.c app_open
|
tfe/tfe2.c app_open
|
||||||
@@@
|
@@@
|
||||||
|
|
||||||
The point is how to build the window.
|
The aim is to build the widgets of the main application window.
|
||||||
|
|
||||||
- 25-27: Creates a GtkApplicationWindow instance and sets the title and default size.
|
- 25-27: Creates a GtkApplicationWindow instance and sets the title and default size.
|
||||||
- 29-30: Creates a GtkBox instance `boxv`.
|
- 29-30: Creates a GtkBox instance `boxv`.
|
||||||
|
@ -37,29 +37,29 @@ This makes the label expands horizontally as long as possible.
|
||||||
This makes it expand horizontally and vertically as big as possible.
|
This makes it expand horizontally and vertically as big as possible.
|
||||||
It is appended to `boxv` as the second child.
|
It is appended to `boxv` as the second child.
|
||||||
|
|
||||||
The number of lines is 33(=57-25+1) to build the widgets.
|
The number of lines to build the widgets is 33(=57-25+1).
|
||||||
And we needed many variables (`boxv`, `boxh`, `dmy1`, ...).
|
We also needed many additional variables (`boxv`, `boxh`, `dmy1`, ...),
|
||||||
Most of them aren't necessary except building the widgets.
|
most of which weren't necessary, except for building the widgets.
|
||||||
Are there any good solution to reduce these work?
|
Are there any good solution to reduce these work?
|
||||||
|
|
||||||
Gtk provides GtkBuilder.
|
Gtk provides GtkBuilder.
|
||||||
It reads ui data and builds a window.
|
It reads user interface (UI) data and builds a window.
|
||||||
It reduces the cumbersome work.
|
It reduces this cumbersome work.
|
||||||
|
|
||||||
## Ui file
|
## The UI File
|
||||||
|
|
||||||
First, let's look at the ui file `tfe3.ui` that defines a structure of the widgets.
|
First, let's look at the UI file `tfe3.ui` that is used to define the widget structure.
|
||||||
|
|
||||||
@@@include
|
@@@include
|
||||||
tfe/tfe3.ui
|
tfe/tfe3.ui
|
||||||
@@@
|
@@@
|
||||||
|
|
||||||
The structure of this file is XML.
|
The structure of this file is XML.
|
||||||
Constructs beginning with `<` and ending with `>` are called tags.
|
Constructs that begin with `<` and end with `>` are called tags.
|
||||||
And there are two types of tags, start tag and end tag.
|
There are two types of tags, the start tag and the end tag.
|
||||||
For example, `<interface>` is a start tag and `</interface>` is an end tag.
|
For example, `<interface>` is a start tag and `</interface>` is an end tag.
|
||||||
Ui file begins and ends with interface tags.
|
The UI file begins and ends with interface tags.
|
||||||
Some tags, for example, object tags can have a class and id attributes in the start tag.
|
Some tags, for example object tags, can have a class and id attributes in their start tag.
|
||||||
|
|
||||||
- 1: The first line is XML declaration.
|
- 1: The first line is XML declaration.
|
||||||
It specifies that the version of XML is 1.0 and the encoding is UTF-8.
|
It specifies that the version of XML is 1.0 and the encoding is UTF-8.
|
||||||
|
@ -177,7 +177,7 @@ tfe/tfe3.gresource.xml
|
||||||
However, this xml has only one gresource.
|
However, this xml has only one gresource.
|
||||||
- 3: The gresource has a prefix `/com/github/ToshioCP/tfe3`.
|
- 3: The gresource has a prefix `/com/github/ToshioCP/tfe3`.
|
||||||
- 4: The gresource has `tfe3.ui`.
|
- 4: The gresource has `tfe3.ui`.
|
||||||
And it is pointed by `/com/github/ToshioCP/tfe3/tfe3.ui` because it needs prefix.
|
And it is pointed by `/com/github/ToshioCP/tfe3/tfe3.ui` because it needs prefix.
|
||||||
If you want to add more files, then insert them between line 4 and 5.
|
If you want to add more files, then insert them between line 4 and 5.
|
||||||
|
|
||||||
Save this xml text to `tfe3.gresource.xml`.
|
Save this xml text to `tfe3.gresource.xml`.
|
||||||
|
@ -205,4 +205,3 @@ build = gtk_builder_new_from_resource ("/com/github/ToshioCP/tfe3/tfe3.ui");
|
||||||
|
|
||||||
Then, compile and run it.
|
Then, compile and run it.
|
||||||
The window appears and it is the same as the screenshot at the beginning of this page.
|
The window appears and it is the same as the screenshot at the beginning of this page.
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue