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.
A constant expression (GtkConstantExpression) provides constant value or instance when it is evaluated.
= G_VALUE_INIT;
GValue value = gtk_constant_expression_new (G_TYPE_INT,100);
expression (expression, NULL, &value); gtk_expression_evaluate
G_VALUE_INIT
, first. Be careful that value
is
a structure, not a pointer to a structure.gtk_constant_expression_new
function. The parameter of the
function is a type (GType) and a value (or instance). This expression
holds a constant value. G_TYPE_INT
is a type that is
registered to the type system. It is integer type. Some types are shown
in the following table.gtk_expression_evaluate
evaluates the expression. It
has three parameters, the expression to evaluate, this
instance and a pointer to a 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.GType | C type | type name | notes |
---|---|---|---|
G_TYPE_CHAR | char | gchar | |
G_TYPE_BOOLEAN | int | gboolean | |
G_TYPE_INT | int | gint | |
G_TYPE_FLOAT | float | gfloat | |
G_TYPE_DOUBLE | double | gdouble | |
G_TYPE_POINTER | void * | gpointer | general pointer |
G_TYPE_STRING | char * | gchararray | null-terminated Cstring |
G_TYPE_OBJECT | GObject | ||
GTK_TYPE_WINDOW | GtkWindow |
A sample program exp_constant_simple.c
is in
src/expression
directory.
#include <gtk/gtk.h>
int
main (int argc, char **argv) {
GtkExpression *expression;
GValue value = G_VALUE_INIT;
/* Create an expression */
expression = gtk_constant_expression_new (G_TYPE_INT,100);
/* Evaluate the expression */
if (gtk_expression_evaluate (expression, NULL, &value))
g_print ("The value is %d.\n", g_value_get_int (&value));
else
g_print ("The constant expression wasn't evaluated correctly.\n");
gtk_expression_unref (expression);
g_value_unset (&value);
return 0;
}
expression
points the expression.Constant expression is usually used to give a constant value or instance to another expression.
A property expression (GtkPropertyExpression) looks up a property in a GObject instance. For example, a property expression that refers “label” property in a GtkLabel object is created like this.
= gtk_property_expression_new (GTK_TYPE_LABEL, another_expression, "label"); expression
The second parameter another_expression
is one of:
this
object.For example,
= gtk_label_new ("Hello");
label = gtk_constant_expression_new (GTK_TYPE_LABEL, label);
another_expression = gtk_property_expression_new (GTK_TYPE_LABEL, another_expression, "label"); expression
If expression
is evaluated, the second parameter
another_expression
is evaluated in advance. The value of
another_expression
is the label
(GtkLabel
instance). Then, expression
looks up “label” property of
the label and the evaluation results in “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.
There’s a simple program exp_property_simple.c
in
src/expression
directory.
#include <gtk/gtk.h>
int
main (int argc, char **argv) {
GtkWidget *label;
GtkExpression *expression;
GValue value = G_VALUE_INIT;
gtk_init ();
label = gtk_label_new ("Hello world.");
/* Create an expression */
expression = gtk_property_expression_new (GTK_TYPE_LABEL, NULL, "label");
/* Evaluate the expression */
if (gtk_expression_evaluate (expression, label, &value))
g_print ("The value is %s.\n", g_value_get_string (&value));
else
g_print ("The property expression wasn't evaluated correctly.\n");
gtk_expression_unref (expression);
g_value_unset (&value);
return 0;
}
gtk_init
initializes GTK GUI toolkit. It isn’t
usually necessary because the GtkApplication default startup handler
does the initialization. A GtkLabel instance is created with the text
“Hello world.”.gtk_expression_evaluate
evaluates
the expression with a ‘this’ instance label
. The result is
stored in the GValue value
. The function
g_value_get_string
gets a string from the GValue. But the
string is owned by the GValue so you must not free the string.If the second argument of gtk_property_expression_new
isn’t NULL, it is another expression. The expression is owned by a newly
created property expression. So, when the expressions are useless, you
just release the last expression. Then it releases another expression it
has.
A 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. There are simple
closure example files closure.c
and
closure_each.c
in the src/expression
directory.
There are two types of closure expressions, GtkCClosureExpression and GtkClosureExpression. They corresponds to GCClosure and GClosure respectively. When you program in C language, GtkCClosureExpression and GCClosure are appropriate.
A closure expression is created with
gtk_cclosure_expression_new
function.
int
(GObject *object, int x, const char *s) callback
The following is exp_closure_simple.c
in
src/expression
.
#include <gtk/gtk.h>
static int
calc (GtkLabel *label) { /* this object */
const char * s;
int i, j;
s = gtk_label_get_text (label); /* s is owned by the label. */
sscanf (s, "%d+%d", &i, &j);
return i+j;
}
int
main (int argc, char **argv) {
GtkExpression *expression;
GValue value = G_VALUE_INIT;
GtkLabel *label;
gtk_init ();
label = GTK_LABEL (gtk_label_new ("123+456"));
g_object_ref_sink (label);
expression = gtk_cclosure_expression_new (G_TYPE_INT, NULL, 0, NULL,
G_CALLBACK (calc), NULL, NULL);
if (gtk_expression_evaluate (expression, label, &value)) /* 'this' object is the label. */
g_print ("%d\n", g_value_get_int (&value));
else
g_print ("The closure expression wasn't evaluated correctly.\n");
gtk_expression_unref (expression);
g_value_unset (&value);
g_object_unref (label);
return 0;
}
gtk_cclosure_expression_new
is
G_TYPE_POINTER
. There is a sample program
exp_closure_with_error_report
in
src/expression
directory.G_TYPE_INT
and no parameters or ‘this’ object.Closure expression is flexible than other type of expression because you can specify your own callback function.
GtkExpressionWatch is a structure, not an object. It represents a
watched GtkExpression. Two functions create GtkExpressionWatch
structure. They are gtk_expression_bind
and
gtk_expression_watch
.
This function binds the target object’s property to the expression. If the value of the expression changes, the property reflects the value immediately.
*
GtkExpressionWatch(
gtk_expression_bind * self,
GtkExpression* target,
GObjectconst char* property,
* this_
GObject)
This function takes the ownership of the expression. So, if you want
to own the expression, call gtk_expression_ref ()
to
increase the reference count of the expression. And you should unref it
when it is useless. If you don’t own the expression, you don’t care
about releasing the expression.
An example exp_bind.c
and exp_bind.ui
is in
src/expression
directory.
It includes a label and a scale. If you move the slider to the right, the scale value increases and the number on the label also increases. In the same way, if you move it to the left, the number on the label decreases. The label is bound to the scale value via an adjustment.
<?xml version='1.0' encoding='UTF-8'?>
<interface>
<object class='GtkApplicationWindow' id='win'>
<property name='default-width'>600</property>
<child>
<object class='GtkBox'>
<property name='orientation'>GTK_ORIENTATION_VERTICAL</property>
<child>
<object class='GtkLabel' id='label'>
<property name="label">10</property>
</object>
</child>
<child>
<object class='GtkScale'>
<property name='adjustment'>
<object class='GtkAdjustment' id='adjustment'>
<property name='upper'>20.0</property>
<property name='lower'>0.0</property>
<property name='value'>10.0</property>
<property name='step-increment'>1.0</property>
<property name='page-increment'>5.0</property>
<property name='page-size'>0.0</property>
</object>
</property>
<property name='digits'>0</property>
<property name='draw-value'>true</property>
<property name='has-origin'>true</property>
<property name='round-digits'>0</property>
</object>
</child>
</object>
</child>
</object>
</interface>
The ui file describes the following parent-child relationship.
GtkApplicationWindow (win) -- GtkBox -+- GtkLabel (label)
+- GtkScale
Four GtkScale properties are defined.
#include <gtk/gtk.h>
GtkExpressionWatch *watch;
static int
f2i (GObject *object, double d) {
return (int) d;
}
static int
close_request_cb (GtkWindow *win) {
gtk_expression_watch_unwatch (watch);
return false;
}
static void
app_activate (GApplication *application) {
GtkApplication *app = GTK_APPLICATION (application);
gtk_window_present (gtk_application_get_active_window(app));
}
static void
app_startup (GApplication *application) {
GtkApplication *app = GTK_APPLICATION (application);
GtkBuilder *build;
GtkWidget *win, *label;
GtkAdjustment *adjustment;
GtkExpression *expression, *params[1];
/* Builds a window with exp.ui resource */
build = gtk_builder_new_from_resource ("/com/github/ToshioCP/exp/exp_bind.ui");
win = GTK_WIDGET (gtk_builder_get_object (build, "win"));
label = GTK_WIDGET (gtk_builder_get_object (build, "label"));
// scale = GTK_WIDGET (gtk_builder_get_object (build, "scale"));
adjustment = GTK_ADJUSTMENT (gtk_builder_get_object (build, "adjustment"));
gtk_window_set_application (GTK_WINDOW (win), app);
g_signal_connect (win, "close-request", G_CALLBACK (close_request_cb), NULL);
g_object_unref (build);
/* GtkExpressionWatch */
params[0] = gtk_property_expression_new (GTK_TYPE_ADJUSTMENT, NULL, "value");
expression = gtk_cclosure_expression_new (G_TYPE_INT, NULL, 1, params, G_CALLBACK (f2i), NULL, NULL);
watch = gtk_expression_bind (expression, label, "label", adjustment); /* watch takes the ownership of the expression. */
}
#define APPLICATION_ID "com.github.ToshioCP.exp_watch"
int
main (int argc, char **argv) {
GtkApplication *app;
int stat;
app = gtk_application_new (APPLICATION_ID, G_APPLICATION_DEFAULT_FLAGS);
g_signal_connect (app, "startup", G_CALLBACK (app_startup), NULL);
g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL);
stat =g_application_run (G_APPLICATION (app), argc, argv);
g_object_unref (app);
return stat;
}
The point of the program is:
gtk_expression_bind
binds the label property of the
GtkLabel instance to the integer returned by the closure expression. It
creates a GtkExpressionWatch structure. The binding works during the
watch lives. When the window is destroyed, the scale and adjustment are
also destroyed. And the watch recognizes the value of the expression
changes and tries to change the property of the label. Obviously, it is
not a correct behavior. The watch should be unwatched before the window
is destroyed.close_request_cb
. This signal is emitted when the close
button is clicked. The handler is called just before the window closes.
It is the right moment to make the GtkExpressionWatch unwatched.gtk_expression_watch_unwatch (watch)
makes the watch stop
watching the expression. It also releases the expression.If you want to bind a property to an expression,
gtk_expression_bind
is the best choice. You can do it with
gtk_expression_watch
function, but it is less suitable.
*
GtkExpressionWatch(
gtk_expression_watch * self,
GtkExpression* this_,
GObject,
GtkExpressionNotify notify,
gpointer user_data
GDestroyNotify user_destroy)
The function doesn’t take the ownership of the expression. It differs
from gtk_expression_bind
. So, you need to release the
expression when it is useless. It creates a GtkExpressionWatch
structure. The third parameter notify
is a callback to
invoke when the expression changes. You can set user_data
to give it to the callback. The last parameter is a function to destroy
the user_data
when the watch is unwatched. Put NULL if you
don’t need them.
Notify callback has the following format.
void
(
notify
gpointer user_data)
This function is used to do something when the value of the
expression changes. But if you want to bind a property to the value, use
gtk_expression_bind
instead.
There’s a sample program exp_watch.c
in
src/expression
directory. It outputs the width of the
window to the standard output.
When you resize the window, the width is displayed in the terminal.
#include <gtk/gtk.h>
GtkExpression *expression;
GtkExpressionWatch *watch;
static void
notify (gpointer user_data) {
GValue value = G_VALUE_INIT;
if (gtk_expression_watch_evaluate (watch, &value))
g_print ("width = %d\n", g_value_get_int (&value));
else
g_print ("evaluation failed.\n");
}
static int
close_request_cb (GtkWindow *win) {
gtk_expression_watch_unwatch (watch);
gtk_expression_unref (expression);
return false;
}
static void
app_activate (GApplication *application) {
GtkApplication *app = GTK_APPLICATION (application);
gtk_window_present (gtk_application_get_active_window(app));
}
static void
app_startup (GApplication *application) {
GtkApplication *app = GTK_APPLICATION (application);
GtkWidget *win;
win = GTK_WIDGET (gtk_application_window_new (app));
g_signal_connect (win, "close-request", G_CALLBACK (close_request_cb), NULL);
expression = gtk_property_expression_new (GTK_TYPE_WINDOW, NULL, "default-width");
watch = gtk_expression_watch (expression, win, notify, NULL, NULL);
}
#define APPLICATION_ID "com.github.ToshioCP.exp_watch"
int
main (int argc, char **argv) {
GtkApplication *app;
int stat;
app = gtk_application_new (APPLICATION_ID, G_APPLICATION_DEFAULT_FLAGS);
g_signal_connect (app, "startup", G_CALLBACK (app_startup), NULL);
g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL);
stat =g_application_run (G_APPLICATION (app), argc, argv);
g_object_unref (app);
return stat;
}
notify
is called every time the value of the expression
changes. The ‘this’ object is win
, so the expression
returns the default width of the window.notify
. It uses
gtk_expression_watch_evaluate
to get the value of the
expression. The ‘this’ object is given in advance (when the watch is
created). It outputs the window width to the standard output.gtk_expression_watch
doesn’t take the
ownership of the expression, you own it. So, the release is
necessary.GtkBuilder supports GtkExpressions. There are four tags.
constant type="gchararray">Hello world</constant>
<lookup name="label" type="GtkLabel">label</lookup>
<closure type="gint" function="callback_function"></closure>
<bind name="label">
<lookup name="default-width">win</lookup>
<bind> </
These tags are usually used for GtkBuilderListItemFactory.
interface>
<template class="GtkListItem">
<property name="child">
<object class="GtkLabel">
<binding name="label">
<lookup name="string" type="GtkStringObject">
<lookup name="item">GtkListItem</lookup>
<lookup>
</binding>
</object>
</property>
</template>
</interface> </
GtkBuilderListItemFactory uses GtkBuilder to extends the GtkListItem with the XML data.
In the xml file above, “GtkListItem” is an instance of the GtkListItem template. It is the ‘this’ object given to the expressions. (The information is in the GTK Development Blog).
GtkBuilder calls gtk_expression_bind
function when it
finds a binding tag. The function sets the ‘this’ object like this:
GTK 4 document doesn’t describe information about “this” object when expressions are defined in a ui file. The information above is found from the GTK 4 source files and it is possible to include mistakes. If you have accurate information, please let me know.
A sample program exp.c
and a ui file exp.ui
is in src/expression
directory. The ui file includes
lookup, closure and bind tags. No constant tag is included. However,
constant tags are not used so often.
If you resize the window, the size is shown at the title of the window. If you type characters in the entry, the same characters appear on the label.
The ui file is as follows.
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<object class="GtkApplicationWindow" id="win">
<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">
<lookup name="text">
buffer
</lookup>
</binding>
</object>
</child>
<child>
<object class="GtkEntry">
<property name="buffer">
<object class="GtkEntryBuffer" id="buffer"></object>
</property>
</object>
</child>
</object>
</child>
</object>
</interface>
set_title
is defined in
the C source file. It returns a string because the type attribute of the
tag is “gchararray”. Two parameters are given to the function. They are
width and height of the window. Lookup tags don’t have contents, so
‘this’ object is used to look up the properties. The ‘this’ object is
win
, which is the target of the binding (win
includes the binding tag).buffer
, which is the buffer of GtkEntry
defined in line 25. If a user types characters in the entry, the same
characters appear on the label.The C source file is as follows.
#include <gtk/gtk.h>
char *
set_title (GtkWidget *win, int width, int height) {
return g_strdup_printf ("%d x %d", width, height);
}
static void
app_activate (GApplication *application) {
GtkApplication *app = GTK_APPLICATION (application);
gtk_window_present (gtk_application_get_active_window(app));
}
static void
app_startup (GApplication *application) {
GtkApplication *app = GTK_APPLICATION (application);
GtkBuilder *build;
GtkWidget *win;
build = gtk_builder_new_from_resource ("/com/github/ToshioCP/exp/exp.ui");
win = GTK_WIDGET (gtk_builder_get_object (build, "win"));
gtk_window_set_application (GTK_WINDOW (win), app);
g_object_unref (build);
}
#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_DEFAULT_FLAGS);
g_signal_connect (app, "startup", G_CALLBACK (app_startup), NULL);
g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL);
stat =g_application_run (G_APPLICATION (app), argc, argv);
g_object_unref (app);
return stat;
}
The C source file is very simple because almost everything is done in the ui file.
If you bind different type properties, type conversion is automatically done. Suppose a label property (string) is bound to default-width property (int).
object class="GtkLabel">
<binding name="label">
<lookup name="default-width">
<
winlookup>
</binding>
</object> </
The expression created by the lookup tag 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. If the current width is 100, an int 100
is
converted to a string "100"
.
If you use g_object_get
and g_object_set
to
copy properties, the value is automatically converted.
The source files are in src/expression
directory. You
can build all the files at once.
$ cd src/expression
$ meson setup _build
$ ninja -C _build
For example, if you want to run “exp”, which is the executable file
from “exp.c”, type _build/exp
. You can run other programs
as well.
The file meson.build
is as follows.
project('exp', 'c')
gtkdep = dependency('gtk4')
gnome=import('gnome')
resources = gnome.compile_resources('resources','exp.gresource.xml')
sourcefiles=files('exp.c')
executable('exp', sourcefiles, resources, dependencies: gtkdep, export_dynamic: true, install: false)
executable('exp_constant', 'exp_constant.c', dependencies: gtkdep, export_dynamic: true, install: false)
executable('exp_constant_simple', 'exp_constant_simple.c', dependencies: gtkdep, export_dynamic: true, install: false)
executable('exp_property_simple', 'exp_property_simple.c', dependencies: gtkdep, export_dynamic: true, install: false)
executable('closure', 'closure.c', dependencies: gtkdep, export_dynamic: true, install: false)
executable('closure_each', 'closure_each.c', dependencies: gtkdep, export_dynamic: true, install: false)
executable('exp_closure_simple', 'exp_closure_simple.c', dependencies: gtkdep, export_dynamic: true, install: false)
executable('exp_closure_with_error_report', 'exp_closure_with_error_report.c', dependencies: gtkdep, export_dynamic: true, install: false)
executable('exp_bind', 'exp_bind.c', resources, dependencies: gtkdep, export_dynamic: true, install: false)
executable('exp_watch', 'exp_watch.c', dependencies: gtkdep, export_dynamic: true, install: false)
executable('exp_test', 'exp_test.c', resources, dependencies: gtkdep, export_dynamic: true, install: false)