2022-12-21 14:31:33 +01:00
Up: [README.md ](../README.md ), Prev: [Section 3 ](sec3.md ), Next: [Section 5 ](sec5.md )
2021-01-11 03:15:04 +01:00
2021-01-13 05:29:35 +01:00
# Widgets (1)
2020-12-21 13:12:05 +01:00
2022-05-01 16:13:26 +02:00
## GtkLabel, GtkButton and GtkBox
2020-12-21 13:12:05 +01:00
2021-01-13 05:29:35 +01:00
### GtkLabel
2020-12-21 13:12:05 +01:00
2022-03-04 09:09:56 +01:00
In the previous section we made a window and displayed it on the screen.
Now we go on to the next topic, where we add widgets to this window.
2021-01-13 05:29:35 +01:00
The simplest widget is GtkLabel.
2022-03-04 09:09:56 +01:00
It is a widget with text in it.
2020-12-21 13:12:05 +01:00
2021-01-25 10:35:49 +01:00
~~~C
1 #include < gtk / gtk . h >
2
3 static void
2022-12-21 14:31:33 +01:00
4 app_activate (GApplication *app) {
2021-01-25 10:35:49 +01:00
5 GtkWidget *win;
6 GtkWidget *lab;
7
8 win = gtk_application_window_new (GTK_APPLICATION (app));
9 gtk_window_set_title (GTK_WINDOW (win), "lb1");
10 gtk_window_set_default_size (GTK_WINDOW (win), 400, 300);
11
12 lab = gtk_label_new ("Hello.");
13 gtk_window_set_child (GTK_WINDOW (win), lab);
14
2023-01-03 07:30:06 +01:00
15 gtk_window_present (GTK_WINDOW (win));
2021-01-25 10:35:49 +01:00
16 }
17
18 int
19 main (int argc, char **argv) {
20 GtkApplication *app;
21 int stat;
22
2022-12-21 14:31:33 +01:00
23 app = gtk_application_new ("com.github.ToshioCP.lb1", G_APPLICATION_DEFAULT_FLAGS);
2021-06-14 10:10:23 +02:00
24 g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL);
2021-01-25 10:35:49 +01:00
25 stat =g_application_run (G_APPLICATION (app), argc, argv);
26 g_object_unref (app);
27 return stat;
28 }
29
~~~
2020-12-22 03:30:06 +01:00
2021-01-13 05:29:35 +01:00
Save this program to a file `lb1.c` .
Then compile and run it.
2020-12-22 03:30:06 +01:00
2021-01-13 05:29:35 +01:00
$ comp lb1
$ ./a.out
2020-12-22 03:30:06 +01:00
2021-01-13 05:29:35 +01:00
A window with a message "Hello." appears.
2020-12-22 03:30:06 +01:00
2021-01-13 12:59:15 +01:00
![Screenshot of the label ](../image/screenshot_lb1.png )
2020-12-22 03:30:06 +01:00
2021-01-13 05:29:35 +01:00
There's only a little change between `pr4.c` and `lb1.c` .
2021-06-14 10:10:23 +02:00
A program `diff` is good to know the difference between two files.
2020-12-22 03:30:06 +01:00
2021-02-08 14:24:54 +01:00
~~~
$ cd misc; diff pr4.c lb1.c
2022-12-21 14:31:33 +01:00
4c4
< app_activate ( GApplication * app , gpointer user_data ) {
---
> app_activate (GApplication *app) {
2021-02-08 14:24:54 +01:00
5a6
> GtkWidget *lab;
8c9
< gtk_window_set_title ( GTK_WINDOW ( win ) , " pr4 " ) ;
---
> gtk_window_set_title (GTK_WINDOW (win), "lb1");
9a11,14
>
> lab = gtk_label_new ("Hello.");
> gtk_window_set_child (GTK_WINDOW (win), lab);
>
18c23
2022-12-21 14:31:33 +01:00
< app = gtk_application_new ( " com . github . ToshioCP . pr4 " , G_APPLICATION_DEFAULT_FLAGS ) ;
2021-02-08 14:24:54 +01:00
---
2022-12-21 14:31:33 +01:00
> app = gtk_application_new ("com.github.ToshioCP.lb1", G_APPLICATION_DEFAULT_FLAGS);
2021-02-08 14:24:54 +01:00
~~~
2020-12-22 03:30:06 +01:00
2021-01-13 05:29:35 +01:00
This tells us:
2020-12-22 03:30:06 +01:00
2022-12-21 14:31:33 +01:00
- A signal handler `app_activate` doesn't have `user_data` parameter.
If the fourth argument of `g_signal_connect` is NULL, you can leave out `user_data` .
2022-03-04 09:09:56 +01:00
- The definition of a new variable `lab` is added.
2021-01-13 05:29:35 +01:00
- The title of the window is changed.
2021-06-14 10:10:23 +02:00
- A label is created and connected to the window as a child.
2020-12-22 03:30:06 +01:00
2021-01-13 05:29:35 +01:00
The function `gtk_window_set_child (GTK_WINDOW (win), lab)` makes the label `lab` a child widget of the window `win` .
Be careful.
A child widget is different from a child object.
2021-06-14 10:10:23 +02:00
Objects have parent-child relationships and widgets also have parent-child relationships.
2021-01-13 05:29:35 +01:00
But these two relationships are totally different.
Don't be confused.
In the program `lb1.c` , `lab` is a child widget of `win` .
2021-06-14 10:10:23 +02:00
Child widgets are always located in their parent widget on the screen.
2022-03-04 09:09:56 +01:00
See how the window has appeared on the screen.
The application window includes the label.
2020-12-22 03:30:06 +01:00
2021-01-27 01:12:58 +01:00
The window `win` doesn't have any parents.
2021-01-13 05:29:35 +01:00
We call such a window top-level window.
2022-03-04 09:09:56 +01:00
An application can have more than one top-level window.
2020-12-22 03:30:06 +01:00
2021-01-13 05:29:35 +01:00
### GtkButton
2020-12-22 03:30:06 +01:00
2022-12-21 14:31:33 +01:00
The next widget is GtkButton.
2022-03-04 09:09:56 +01:00
It displays a button on the screen with a label or icon on it.
2021-01-13 05:29:35 +01:00
In this subsection, we will make a button with a label.
2022-03-04 09:09:56 +01:00
When the button is clicked, it emits a "clicked" signal.
2022-12-21 14:31:33 +01:00
The following program shows how to catch the signal and do something.
2020-12-21 13:12:05 +01:00
2021-01-25 10:35:49 +01:00
~~~C
1 #include < gtk / gtk . h >
2
3 static void
2022-12-21 14:31:33 +01:00
4 click_cb (GtkButton *btn) {
2021-01-25 10:35:49 +01:00
5 g_print ("Clicked.\n");
6 }
7
8 static void
2022-12-21 14:31:33 +01:00
9 app_activate (GApplication *app) {
2021-01-25 10:35:49 +01:00
10 GtkWidget *win;
11 GtkWidget *btn;
12
13 win = gtk_application_window_new (GTK_APPLICATION (app));
14 gtk_window_set_title (GTK_WINDOW (win), "lb2");
15 gtk_window_set_default_size (GTK_WINDOW (win), 400, 300);
16
17 btn = gtk_button_new_with_label ("Click me");
18 gtk_window_set_child (GTK_WINDOW (win), btn);
19 g_signal_connect (btn, "clicked", G_CALLBACK (click_cb), NULL);
20
2023-01-03 07:30:06 +01:00
21 gtk_window_present (GTK_WINDOW (win));
2021-01-25 10:35:49 +01:00
22 }
23
24 int
25 main (int argc, char **argv) {
26 GtkApplication *app;
27 int stat;
28
2022-12-21 14:31:33 +01:00
29 app = gtk_application_new ("com.github.ToshioCP.lb2", G_APPLICATION_DEFAULT_FLAGS);
2021-06-14 10:10:23 +02:00
30 g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL);
2021-01-25 10:35:49 +01:00
31 stat =g_application_run (G_APPLICATION (app), argc, argv);
32 g_object_unref (app);
33 return stat;
34 }
35
~~~
2020-12-22 03:30:06 +01:00
2021-01-13 05:29:35 +01:00
Look at the line 17 to 19.
2021-06-14 10:10:23 +02:00
First, it creates a GtkButton instance `btn` with a label "Click me".
2021-02-06 09:26:57 +01:00
Then, adds the button to the window `win` as a child.
2022-12-21 14:31:33 +01:00
Finally, connects a "clicked" signal of the button to the handler `click_cb` .
2021-01-13 05:29:35 +01:00
So, if `btn` is clicked, the function `click_cb` is invoked.
2021-06-14 10:10:23 +02:00
The suffix "cb" means "call back".
2021-01-13 05:29:35 +01:00
2022-03-04 09:09:56 +01:00
Name the program `lb2.c` and save it.
2020-12-22 03:30:06 +01:00
Now compile and run it.
2021-01-13 05:29:35 +01:00
2021-01-13 12:59:15 +01:00
![Screenshot of the label ](../image/screenshot_lb2.png )
2022-03-04 09:09:56 +01:00
2021-01-13 05:29:35 +01:00
A window with the button appears.
2021-06-14 10:10:23 +02:00
Click the button (it is a large button, you can click everywhere in the window), then a string "Clicked." appears on the terminal.
2021-01-13 05:29:35 +01:00
It shows the handler was invoked by clicking the button.
2022-03-04 09:09:56 +01:00
It's good that we make sure that the clicked signal was caught and the handler was invoked by using `g_print` .
2022-12-21 14:31:33 +01:00
However, using `g_print` is out of harmony with GTK, which is a GUI library.
2021-01-13 05:29:35 +01:00
So, we will change the handler.
The following code is `lb3.c` .
2021-01-25 10:35:49 +01:00
~~~C
1 static void
2022-12-21 14:31:33 +01:00
2 click_cb (GtkButton *btn, GtkWindow *win) {
3 gtk_window_destroy (win);
4 }
5
6 static void
7 app_activate (GApplication *app) {
8 GtkWidget *win;
9 GtkWidget *btn;
10
11 win = gtk_application_window_new (GTK_APPLICATION (app));
12 gtk_window_set_title (GTK_WINDOW (win), "lb3");
13 gtk_window_set_default_size (GTK_WINDOW (win), 400, 300);
14
15 btn = gtk_button_new_with_label ("Close");
16 gtk_window_set_child (GTK_WINDOW (win), btn);
17 g_signal_connect (btn, "clicked", G_CALLBACK (click_cb), win);
18
2023-01-03 07:30:06 +01:00
19 gtk_window_present (GTK_WINDOW (win));
2022-12-21 14:31:33 +01:00
20 }
2021-01-25 10:35:49 +01:00
~~~
2021-01-13 05:29:35 +01:00
And the difference between `lb2.c` and `lb3.c` is as follows.
2021-02-08 14:24:54 +01:00
~~~
$ cd misc; diff lb2.c lb3.c
2022-12-21 14:31:33 +01:00
4,5c4,5
< click_cb ( GtkButton * btn ) {
2021-02-08 14:24:54 +01:00
< g_print (" Clicked . \n");
---
2022-12-21 14:31:33 +01:00
> click_cb (GtkButton *btn, GtkWindow *win) {
2021-02-08 14:24:54 +01:00
> gtk_window_destroy (win);
2022-12-21 14:31:33 +01:00
14c14
2021-02-08 14:24:54 +01:00
< gtk_window_set_title ( GTK_WINDOW ( win ) , " lb2 " ) ;
---
> gtk_window_set_title (GTK_WINDOW (win), "lb3");
2022-12-21 14:31:33 +01:00
17c17
2021-02-08 14:24:54 +01:00
< btn = gtk_button_new_with_label ( " Click me " ) ;
---
2022-12-21 14:31:33 +01:00
> btn = gtk_button_new_with_label ("Close");
19c19
2021-02-08 14:24:54 +01:00
< g_signal_connect ( btn , " clicked " , G_CALLBACK ( click_cb ) , NULL ) ;
---
> g_signal_connect (btn, "clicked", G_CALLBACK (click_cb), win);
2022-12-21 14:31:33 +01:00
29c29
< app = gtk_application_new ( " com . github . ToshioCP . lb2 " , G_APPLICATION_DEFAULT_FLAGS ) ;
2021-02-08 14:24:54 +01:00
---
2022-12-21 14:31:33 +01:00
> app = gtk_application_new ("com.github.ToshioCP.lb3", G_APPLICATION_DEFAULT_FLAGS);
35d34
2021-02-08 14:24:54 +01:00
<
~~~
2021-01-13 05:29:35 +01:00
2022-03-04 09:09:56 +01:00
The changes are:
2021-01-13 05:29:35 +01:00
2022-12-21 14:31:33 +01:00
- The function `g_print` in `lb2.c` was deleted and two lines are inserted.
- `click_cb` has the second parameter, which comes from the fourth argument of the `g_signal_connect` at the line 19.
One thing to be careful is the types are different between the second parameter of `click_cb` and the fourth argument of `g_signal_connect` .
The former is `GtkWindow *` and the latter is `GtkWidget *` .
The compiler doesn't complain because `g_signal_connect` uses gpointer (general type of pointer).
In this program the instance pointed by `win` is a GtkApplicationWindow object.
It is a descendant of GtkWindow and GtkWidget class, so both `GtkWindow *` and `GtkWidget *` are correct types for the instance.
- `gtk_destroy (win)` destroys the top-level window. Then the application quits.
- The label of `btn` is changed from "Click me" to "Close".
2022-03-04 09:09:56 +01:00
- The fourth argument of `g_signal_connect` is changed from `NULL` to `win` .
2021-01-13 05:29:35 +01:00
2022-12-21 14:31:33 +01:00
The most important change is the fourth argument of the `g_signal_connect` .
This argument is described as "data to pass to handler" in the definition of [`g_signal_connect` ](https://docs.gtk.org/gobject/func.signal_connect.html ).
2021-01-13 05:29:35 +01:00
### GtkBox
GtkWindow and GtkApplicationWindow can have only one child.
2021-06-14 10:10:23 +02:00
If you want to add two or more widgets in a window, you need a container widget.
2021-01-13 05:29:35 +01:00
GtkBox is one of the containers.
It arranges two or more child widgets into a single row or column.
The following procedure shows the way to add two buttons in a window.
2021-06-14 10:10:23 +02:00
- Create a GtkApplicationWindow instance.
- Create a GtkBox instance and add it to the GtkApplicationWindow as a child.
- Create a GtkButton instance and append it to the GtkBox.
- Create another GtkButton instance and append it to the GtkBox.
2021-01-13 05:29:35 +01:00
2021-06-14 10:10:23 +02:00
After this, the Widgets are connected as the following diagram.
2021-01-13 05:29:35 +01:00
2021-01-13 12:59:15 +01:00
![Parent-child relationship ](../image/box.png )
2021-01-13 05:29:35 +01:00
2022-12-21 14:31:33 +01:00
The program `lb4.c` is as follows.
2021-01-13 05:29:35 +01:00
2021-01-25 10:35:49 +01:00
~~~C
1 #include < gtk / gtk . h >
2
3 static void
2022-12-21 14:31:33 +01:00
4 click1_cb (GtkButton *btn) {
5 const char *s;
2021-01-25 10:35:49 +01:00
6
7 s = gtk_button_get_label (btn);
8 if (g_strcmp0 (s, "Hello.") == 0)
9 gtk_button_set_label (btn, "Good-bye.");
10 else
11 gtk_button_set_label (btn, "Hello.");
12 }
13
14 static void
2022-12-21 14:31:33 +01:00
15 click2_cb (GtkButton *btn, GtkWindow *win) {
16 gtk_window_destroy (win);
17 }
18
19 static void
20 app_activate (GApplication *app) {
21 GtkWidget *win;
22 GtkWidget *box;
23 GtkWidget *btn1;
24 GtkWidget *btn2;
25
26 win = gtk_application_window_new (GTK_APPLICATION (app));
27 gtk_window_set_title (GTK_WINDOW (win), "lb4");
28 gtk_window_set_default_size (GTK_WINDOW (win), 400, 300);
29
30 box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 5);
31 gtk_box_set_homogeneous (GTK_BOX (box), TRUE);
32 gtk_window_set_child (GTK_WINDOW (win), box);
33
34 btn1 = gtk_button_new_with_label ("Hello.");
35 g_signal_connect (btn1, "clicked", G_CALLBACK (click1_cb), NULL);
36
37 btn2 = gtk_button_new_with_label ("Close");
38 g_signal_connect (btn2, "clicked", G_CALLBACK (click2_cb), win);
39
40 gtk_box_append (GTK_BOX (box), btn1);
41 gtk_box_append (GTK_BOX (box), btn2);
42
2023-01-03 07:30:06 +01:00
43 gtk_window_present (GTK_WINDOW (win));
2022-12-21 14:31:33 +01:00
44 }
45
46 int
47 main (int argc, char **argv) {
48 GtkApplication *app;
49 int stat;
50
51 app = gtk_application_new ("com.github.ToshioCP.lb4", G_APPLICATION_DEFAULT_FLAGS);
52 g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL);
53 stat =g_application_run (G_APPLICATION (app), argc, argv);
54 g_object_unref (app);
55 return stat;
56 }
2021-01-25 10:35:49 +01:00
~~~
2021-01-13 05:29:35 +01:00
2021-06-14 10:10:23 +02:00
Look at the function `app_activate` .
2021-01-13 05:29:35 +01:00
2021-06-14 10:10:23 +02:00
After the creation of a GtkApplicationWindow instance, a GtkBox instance is created.
2021-01-13 05:29:35 +01:00
2022-12-21 14:31:33 +01:00
~~~C
box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);
gtk_box_set_homogeneous (GTK_BOX (box), TRUE);
~~~
2021-01-13 05:29:35 +01:00
2021-06-14 10:10:23 +02:00
The first argument arranges the children of the box vertically.
2022-12-21 14:31:33 +01:00
The orientation constants are defined like this:
- GTK\_ORIENTATION\_VERTICAL: the children widgets are arranged vertically
- GTK\_ORIENTATION\_HORIZONTAL: the children widgets are arranged horizontally
2021-06-14 10:10:23 +02:00
The second argument is the size between the children.
2022-12-21 14:31:33 +01:00
The unit of the length is pixel.
2021-06-14 10:10:23 +02:00
The next function fills the box with the children, giving them the same space.
2021-01-13 05:29:35 +01:00
2021-06-14 10:10:23 +02:00
After that, two buttons `btn1` and `btn2` are created and the signal handlers are set.
2021-01-13 05:29:35 +01:00
Then, these two buttons are appended to the box.
2022-12-21 14:31:33 +01:00
~~~C
1 static void
2 click1_cb (GtkButton *btn) {
3 const char *s;
4
5 s = gtk_button_get_label (btn);
6 if (g_strcmp0 (s, "Hello.") == 0)
7 gtk_button_set_label (btn, "Good-bye.");
8 else
9 gtk_button_set_label (btn, "Hello.");
10 }
~~~
2023-03-23 09:15:17 +01:00
The function `gtk_button_get_label` returns a text from the label.
2022-12-21 14:31:33 +01:00
The string is owned by the button and you can't modify or free it.
The `const` qualifier is necessary for the string `s` .
If you change the string, your compiler will give you a waring.
You always need to be careful with the const qualifier when you see the GTK 4 API reference.
2021-01-13 12:59:15 +01:00
![Screenshot of the box ](../image/screenshot_lb4.png )
2021-01-13 05:29:35 +01:00
2023-03-23 09:15:17 +01:00
The handler corresponding to `btn1` toggles its label.
The handler corresponding to `btn2` destroys the top-level window and the application quits.
2020-12-21 13:12:05 +01:00
2022-12-21 14:31:33 +01:00
Up: [README.md ](../README.md ), Prev: [Section 3 ](sec3.md ), Next: [Section 5 ](sec5.md )