Up: index.html, Prev: Section 27, Next: Section 29
GtkExpression is a fundamental type. It is not a descendant of GObject. GtkExpression provides a way to describe references to values. GtkExpression needs to be evaluated to obtain a value.
It is similar to arithmetic calculation.
1 + 2 = 3
1+2
is an expression. It shows the way how to calculate. 3
is the value comes from the expression. Evaluation is to calculate the expression and get the value.
GtkExpression is a way to get a value. Evaluation is like a calculation. A value is got by evaluating the expression.
First, I want to show you the C file of the example for GtkExpression. Its name is exp.c
and located under src/expression directory. You don’t need to understand the details now, just look at it. It will be explained in the next subsection.
#include <gtk/gtk.h>
GtkWidget *win1;
int width, height;
GtkExpressionWatch *watch_width;
GtkExpressionWatch *watch_height;
/* Notify is called when "default-width" or "default-height" property is changed. */
static void
notify (gpointer user_data) {
GValue value = G_VALUE_INIT;
char *title;
if (watch_width && gtk_expression_watch_evaluate (watch_width, &value))
width = g_value_get_int (&value);
g_value_unset (&value);
if (watch_height && gtk_expression_watch_evaluate (watch_height, &value))
height = g_value_get_int (&value);
g_value_unset (&value);
title = g_strdup_printf ("%d x %d", width, height);
gtk_window_set_title (GTK_WINDOW (win1), title);
g_free (title);
}
/* This function is used by closure tag in exp.ui. */
char *
set_title (GtkWidget *win, int width, int height) {
return g_strdup_printf ("%d x %d", width, height);
}
/* ----- activate, open, startup handlers ----- */
static void
app_activate (GApplication *application) {
GtkApplication *app = GTK_APPLICATION (application);
GtkWidget *box;
GtkWidget *label1, *label2, *label3;
GtkWidget *entry;
GtkEntryBuffer *buffer;
GtkBuilder *build;
GtkExpression *expression, *expression1, *expression2;
GValue value = G_VALUE_INIT;
char *s;
/* Creates GtkApplicationWindow instance. */
/* The codes below are complecated. It does the same as "win1 = gtk_application_window_new (app);". */
/* The codes are written just to show how to use GtkExpression. */
expression = gtk_cclosure_expression_new (GTK_TYPE_APPLICATION_WINDOW, NULL, 0, NULL,
G_CALLBACK (gtk_application_window_new), NULL, NULL);
if (gtk_expression_evaluate (expression, app, &value)) {
win1 = GTK_WIDGET (g_value_get_object (&value)); /* GtkApplicationWindow */
g_object_ref (win1);
g_print ("Got GtkApplicationWindow instance.\n");
}else
g_print ("The cclosure expression wasn't evaluated correctly.\n");
gtk_expression_unref (expression);
g_value_unset (&value); /* At the same time, the reference count of win1 is decreased by one. */
/* Builds a window with components */
box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 10);
label1 = gtk_label_new (NULL);
label2 = gtk_label_new (NULL);
label3 = gtk_label_new (NULL);
buffer = gtk_entry_buffer_new (NULL, 0);
entry = gtk_entry_new_with_buffer (buffer);
gtk_box_append (GTK_BOX (box), label1);
gtk_box_append (GTK_BOX (box), label2);
gtk_box_append (GTK_BOX (box), label3);
gtk_box_append (GTK_BOX (box), entry);
gtk_window_set_child (GTK_WINDOW (win1), box);
/* Constant expression */
expression = gtk_constant_expression_new (G_TYPE_INT,100);
if (gtk_expression_evaluate (expression, NULL, &value)) {
s = g_strdup_printf ("%d", g_value_get_int (&value));
gtk_label_set_text (GTK_LABEL (label1), s);
g_free (s);
} else
g_print ("The constant expression wasn't evaluated correctly.\n");
gtk_expression_unref (expression);
g_value_unset (&value);
/* Property expression and binding*/
expression1 = gtk_property_expression_new (GTK_TYPE_ENTRY, NULL, "buffer");
expression2 = gtk_property_expression_new (GTK_TYPE_ENTRY_BUFFER, expression1, "text");
gtk_expression_bind (expression2, label2, "label", entry);
/* Constant expression instead of "this" instance */
expression1 = gtk_constant_expression_new (GTK_TYPE_APPLICATION, app);
expression2 = gtk_property_expression_new (GTK_TYPE_APPLICATION, expression1, "application-id");
if (gtk_expression_evaluate (expression2, NULL, &value))
gtk_label_set_text (GTK_LABEL (label3), g_value_get_string (&value));
else
g_print ("The property expression wasn't evaluated correctly.\n");
gtk_expression_unref (expression1); /* expression 2 is also freed. */
g_value_unset (&value);
width = 800;
height = 600;
gtk_window_set_default_size (GTK_WINDOW (win1), width, height);
notify(NULL);
/* GtkExpressionWatch */
expression1 = gtk_property_expression_new (GTK_TYPE_WINDOW, NULL, "default-width");
watch_width = gtk_expression_watch (expression1, win1, notify, NULL, NULL);
expression2 = gtk_property_expression_new (GTK_TYPE_WINDOW, NULL, "default-height");
watch_height = gtk_expression_watch (expression2, win1, notify, NULL, NULL);
gtk_widget_show (win1);
/* Builds a window with exp.ui resource */
build = gtk_builder_new_from_resource ("/com/github/ToshioCP/exp/exp.ui");
GtkWidget *win2 = GTK_WIDGET (gtk_builder_get_object (build, "win2"));
gtk_window_set_application (GTK_WINDOW (win2), app);
g_object_unref (build);
gtk_widget_show (win2);
}
static void
app_startup (GApplication *application) {
}
#define APPLICATION_ID "com.github.ToshioCP.exp"
int
main (int argc, char **argv) {
GtkApplication *app;
int stat;
app = gtk_application_new (APPLICATION_ID, G_APPLICATION_FLAGS_NONE);
g_signal_connect (app, "startup", G_CALLBACK (app_startup), NULL);
g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL);
/* g_signal_connect (app, "open", G_CALLBACK (app_open), NULL);*/
stat =g_application_run (G_APPLICATION (app), argc, argv);
g_object_unref (app);
return stat;
}
exp.c
consists of five functions.
notify
set_title
app_activate
. This is a handler of “activate” signal on GtkApplication instance.app_startup
. This is a handler of “startup”signal. But nothing is done in this function.main
.The function app_activate
is an actual main body in exp.c
.
Constant expression provides constant value or instance when it is evaluated.
100);
expression = gtk_constant_expression_new (G_TYPE_INT,if (gtk_expression_evaluate (expression, NULL, &value)) {
"%d", g_value_get_int (&value));
s = g_strdup_printf (
gtk_label_set_text (GTK_LABEL (label1), s);
g_free (s);else
} "The constant expression wasn't evaluated correctly.\n");
g_print (
gtk_expression_unref (expression); g_value_unset (&value);
gtk_constant_expression_new
function. The parameter of the function is a type (GType) and a value (or instance).gtk_expression_evaluate
evaluates the expression. It has three parameters, the expression to evaluate, this
instance and GValue for being set with the value. this
instance isn’t necessary for constant expressions. Therefore the second argument is NULL. gtk_expression_evaluate
returns TRUE if it successfully evaluates the expression. Otherwise it returns FALSE.value
is set with the value of the expression. The type of the value is int. g_strdup_printf
converts the value to a string s
.label1
is set with s
. The string s
needs to be freed.Constant expression is usually used to give a constant value or instance to another expression.
Property expression looks up a property in a GObject object. For example, a property expression that refers “label” property in GtkLabel object is created like this.
"label"); expression = gtk_property_expression_new (GTK_TYPE_LABEL, another_expression,
another_expression
is expected to give a GtkLabel instance when it is evaluated. For example,
"Hello");
label = gtk_label_new (
another_expression = gtk_constant_expression_new (GTK_TYPE_LABEL, label);"label"); expression = gtk_property_expression_new (GTK_TYPE_LABEL, another_expression,
If expression
is evaluated, the second parameter another_expression
is evaluated in advance. The value of another_expression
is label
(GtkLabel instance). Then, expression
looks up “label” property of label
and the evaluation result is “Hello”.
In the example above, the second argument of gtk_property_expression_new
is another expression. But the second argument can be NULL. If it is NULL, this
instance is used instead. this
is given by gtk_expression_evaluate
function at the evaluation.
Now look at exp.c
. The lines from 83 to 85 is extracted here.
"buffer");
expression1 = gtk_property_expression_new (GTK_TYPE_ENTRY, NULL, "text");
expression2 = gtk_property_expression_new (GTK_TYPE_ENTRY_BUFFER, expression1, "label", entry); gtk_expression_bind (expression2, label2,
expression1
looks up “buffer” property of this
object, which is GTK_TYPE_ENTRY
type.expression2
looks up “text” property of GtkEntryBuffer object given by epression1
.gtk_expression_bind
binds a property to a value given by the expression. In this program, it binds a “label” property in label2
to the value evaluated with expresion2
with entry
as this
object. The evaluation process is as follows.
expression2
is evaluated. But it includes expression1
so expression1
is evaluated in advance.expression1
is NULL, this
object is used. this
is given by gtk_expression_bind
. It is entry
(GtkEntry instance). expression1
looks up “buffer” property in entry
. It is a GtkEntryBuffer instance buffer
. (See line 64 in exp.c
.)expression2
looks up “text” property in buffer
. It is a text held in entry
.label2
.gtk_expression_bind
creates a GtkExpressionWatch. (But it isn’t assigned to a variable in the program above. If you want to keep the GtkExpressionWatch instance, assign it to a variable.)
GtkExpressionWatch *watch;"label", entry); watch = gtk_expression_bind (expression2, label2,
expression2
changes, it evaluates expression2
and set “label” property in label2
. So, the change of the text in entry
makes the “label” property reflect it immediately.Closure expression calls closure when it is evaluated. A closure is a generic representation of a callback (a pointer to a function). For information about closure, see GObject API Reference, The GObject messaging system. A closure expression is created with gtk_cclosure_expression_new
function.
GtkExpression *
gtk_cclosure_expression_new (GType value_type,
GClosureMarshal marshal,
guint n_params,
GtkExpression **params,
GCallback callback_func,
gpointer user_data, GClosureNotify user_destroy);
value_type
is the type of the value when it is evaluated.marshal
is a marshaller. You can assign NULL. If it is NULL, then g_cclosure_marshal_generic ()
is used as a marshaller. It is a generic marshaller function implemented via libffi.n_params
is the number of parameters.params
points expressions for each parameter of the call back function.callback_func
is a callback function.user_data
is user data. You can add it for the closure. It is like user_data
in g_signal_connect
. If it is not necessary, assign NULL.user_destroy
is a destroy notify for user_data
. It is called to destroy user_data
when it is no longer needed. If NULL is assigned to user_data
, assign NULL to user_destroy
, too.The following is extracted from exp.c
. It is from line 47 to line 56.
0, NULL,
expression = gtk_cclosure_expression_new (GTK_TYPE_APPLICATION_WINDOW, NULL,
G_CALLBACK (gtk_application_window_new), NULL, NULL);if (gtk_expression_evaluate (expression, app, &value)) {
/* GtkApplicationWindow */
win1 = GTK_WIDGET (g_value_get_object (&value));
g_object_ref (win1);"Got GtkApplicationWindow object.\n");
g_print (else
}"The cclosure expression wasn't evaluated correctly.\n");
g_print (
gtk_expression_unref (expression);/* At the same time, the reference count of win1 is decreased by one. */ g_value_unset (&value);
The callback function is gtk_application_window_new
. This function has one parameter which is an instance of GtkApplication. And it returns newly created GtkApplicationWindow instance. So, the first argument is GTK_TYPE_APPLICATION_WINDOW
which is the type of the return value. The second argument is NULL so general marshaller g_cclosure_marshal_generic ()
will be used. I think assigning NULL works in most cases when you program in C language.
The arguments given to the call back function are this
object and parameters which are the fourth argument of gtk_cclosure_expression_new
. So, the number of arguments is n_params + 1
. Because gtk_application_window_new
has one parameter, so n_params
is zero and **params
is NULL. No user data is necessary, so user_data
and user_destroy
are NULL.
gtk_expression_evaluate
evaluates the expression. this
instance will be the first argument for gtk_application_window_new
, so it is app
.
If the evaluation succeeds, the GValue value
holds a newly created GtkApplicationWindow instance. It is assigned to win1
. The GValue will be unset when it is no longer used. And when it is unset, the GtkApplicationWindow instance will be released and its reference count will be decreased by one. It is necessary to increase the reference count by one in advance to keep the instance. gtk_expression_unref
frees expression
and value
is unset.
As a result, we got a GtkApplicationWindow instance win1
. We can do the same by:
win1 = gtk_application_window_new (app);
The example is more complicated and not practical than this one line code. The aim of the example is just to show how closure expression works.
Closure expression is flexible than other type of expression because you can specify your own callback function.
GtkExpressionWatch watches an expression and if the value of the expression changes it calls its notify handler.
The example uses GtkExpressionWatch in the line 103 to 106.
"default-width");
expression1 = gtk_property_expression_new (GTK_TYPE_WINDOW, NULL,
watch_width = gtk_expression_watch (expression1, win1, notify, NULL, NULL);"default-height");
expression2 = gtk_property_expression_new (GTK_TYPE_WINDOW, NULL, watch_height = gtk_expression_watch (expression2, win1, notify, NULL, NULL);
The expressions above refer to “default-width” and “default-height” properties of GtkWindow. The variable watch_width
watches expression1
. The second argument win1
is this
instance for expression1
. So, watch_width
watches the value of “default-width” property of win1
. If the value changes, it calls notify
handler. The fourth and fifth arguments are NULL because no user data is necessary.
The variable watch_height
connects notify
handler to expression2
. So, notiry
is also called when “default-height” changes.
The handler norify
is as follows.
static void
notify (gpointer user_data) {
GValue value = G_VALUE_INIT;
char *title;
if (watch_width && gtk_expression_watch_evaluate (watch_width, &value))
width = g_value_get_int (&value);
g_value_unset (&value);
if (watch_height && gtk_expression_watch_evaluate (watch_height, &value))
height = g_value_get_int (&value);
g_value_unset (&value);
title = g_strdup_printf ("%d x %d", width, height);
gtk_window_set_title (GTK_WINDOW (win1), title);
g_free (title);
}
expression1
and expression2
with expression_watch_evaluate
function.title
. It contains the width and height, for example, “800 x 600”.win1
with the string title
.The title of the window reflects the size of the window.
exp.c
builds a GtkWindow instance win2
with exp.ui
. The ui file exp.ui
includes tags to create GtkExpressions. The tags are:
The window win2
behaves like win1
. Because similar expressions are built with the ui file.
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<object class="GtkWindow" id="win2">
<binding name="title">
<closure type="gchararray" function="set_title">
<lookup name="default-width" type="GtkWindow"></lookup>
<lookup name="default-height" type="GtkWindow"></lookup>
</closure>
</binding>
<property name="default-width">600</property>
<property name="default-height">400</property>
<child>
<object class="GtkBox">
<property name="orientation">GTK_ORIENTATION_VERTICAL</property>
<child>
<object class="GtkLabel">
<binding name="label">
<constant type="gint">100</constant>
</binding>
</object>
</child>
<child>
<object class="GtkLabel">
<binding name="label">
<lookup name="text">
<lookup name="buffer">
entry
</lookup>
</lookup>
</binding>
</object>
</child>
<child>
<object class="GtkLabel">
<binding name="label">
<lookup name="application-id">
<lookup name="application">win2</lookup>
</lookup>
</binding>
</object>
</child>
<child>
<object class="GtkEntry" id="entry">
<property name="buffer">
<object class="GtkEntryBuffer"></object>
</property>
</object>
</child>
</object>
</child>
</object>
</interface>
A constant tag corresponds to a constant expression.
G_TYPE_INT
type. Similarly, the types which is registered to the type system has type and name. For example, “gchararray” is a name of G_TYPE_STRING
type. You need to use the name of types for the type
attribute. See GObject tutorial.gtk_expression_bind
function. name
attribute specifies the “label” property of the GtkLabel object just before the binding tag. The expression returns a int type GValue. On the other hand “label” property holds a string type GValue. When a GValue is copied to another GValue, the type is automatically converted if possible. In this case, an int 100
is converted to a string "100"
.These binding and constant tag works. But they are not good. A property tag is more straightforward.
<object class="GtkLabel">
<property name="label">100</property>
</object>
This example just shows the way how to use constant tag. Constant tag is mainly used to give a constant argument to a closure.
A lookup tag corresponds to a property expression. Line 23 to 31 is copied here.
<object class="GtkLabel">
<binding name="label">
<lookup name="text">
<lookup name="buffer">
entry</lookup>
</lookup>
</binding>
</object>
buffer
property of the entry
instance. The entry
instance is defined in the line 43. It is a GtkEntry entry
. A lookup tag takes an instance in some ways to look up for a property.
this
instance when it is evaluated.As a result, the label of the GtkLabel instance are bound to the text in the field of GtkEntry. If a user input a text in the field in the GtkEntry, GtkLabel displays the same text.
Another lookup tag is in the lines from 34 to 40.
<object class="GtkLabel">
<binding name="label">
<lookup name="application-id">
<lookup name="application">win2</lookup>
</lookup>
</binding>
</object>
win2
instance.As a result, the “label” property in the GtkLabel instance is bound to the “application-id” property. The nested tag makes a chain like:
"label" <= "application-id" <= "application" <= `win2`
By the way, the application of win2
is set after the objects in ui file are built. Look at exp.c
. gtk_window_set_application
is called after gtk_build_new_from_resource
.
"/com/github/ToshioCP/exp/exp.ui");
build = gtk_builder_new_from_resource ("win2"));
GtkWidget *win2 = GTK_WIDGET (gtk_builder_get_object (build, gtk_window_set_application (GTK_WINDOW (win2), app);
Therefore, before the call for gtk_window_set_application
, the “application” property of win2
is not set. So, the evaluation of <lookup name="application">win2</lookup>
fails. And the evaluation of <lookup name="application-id">
also fails. A function gtk_expression_bind ()
, which corresponds to binding
tag, doesn’t update the target property if the expression fails. So, the “label” property isn’t updated at the first evaluation.
Note that an evaluation can fail. The care is especially necessary when you write a callback for a closure tag which has contents of expressions like lookup tags. The expressions are given to the callback as an argument. If an expression fails the argument will be NULL. You need to check if the argument exactly points the instance that is expected by the callback.
The lines from 3 to 9 include a closure tag.
<object class="GtkWindow" id="win2">
<binding name="title">
<closure type="gchararray" function="set_title">
<lookup name="default-width" type="GtkWindow"></lookup>
<lookup name="default-height" type="GtkWindow"></lookup>
</closure>
</binding>
gtk_expression_bind
function. name
attribute specifies the “title” property of win2
. Binding tag gives win2
as the this
instance to the expressions, which are the contents of the binding tag. So, closure tag and lookup tags use win2
as the this
object when they are evaluated.set_title
and it returns “gchararray” type, which is “an array of characters” i.e. a string. The contents of the closure tag are assigned to parameters of the function. So, set_title
has three parameters, win2
(this
instance), default width and default height.win2
(this
instance).set_title
function in exp.c
is as follows.
char *
set_title (GtkWidget *win, int width, int height) {
return g_strdup_printf ("%d x %d", width, height);
}
It just creates a string, for example, “800 x 600”, and returns it.
You’ve probably been noticed that ui file is easier and clearer than the corresponding C program. One of the most useful case of GtkExpression is building GtkListItem instance with GtkBuilderListItemFatory. Such case has already been described in the prior two sections.
It will be used in the next section to build GtkListItem in GtkColumnView, which is the most useful view object for GListModel.
All the sources are in src/expression directory. Change your current directory to the directory and run meson and ninja. Then, execute the application.
$ meson _build
$ ninja -C _build
$ build/exp
Then, two windows appear.
If you put some text in the field of the entry, then the same text appears in the second GtkLabel. Because the “label” property of the second GtkLabel instance is bound to the text in the GtkEntryBuffer.
If you resize the window, then the size appears in the title bar because the “title” property is bound to “default-width” and “default-height” properties.
Up: index.html, Prev: Section 27, Next: Section 29