GTK+ events and signals
In this part of the GTK+ programming tutorial, we will talk about the event system in GTK+ library.GTK+ library is an event driven system. All GUI applications are event driven. The applications start a main loop, which continuously checks for newly generated events. If there is no event, the application waits and does nothing. In GTK+ an event is a message from the X server. When the event reaches a widget, it may react to this event by emitting a signal. The GTK+ programmer can connect a specific callback to a signal. The callback is a handler function, that reacts to a signal.
#include <gtk/gtk.h>In our application, we have two signals. The clicked signal and the destroysignal.
void button_clicked(GtkWidget *widget, gpointer data)
{
g_print("clicked\n");
}
int main( int argc, char *argv[])
{
GtkWidget *window;
GtkWidget *fixed;
GtkWidget *button;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(window), "GtkButton");
gtk_window_set_default_size(GTK_WINDOW(window), 230, 150);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
fixed = gtk_fixed_new();
gtk_container_add(GTK_CONTAINER(window), fixed);
button = gtk_button_new_with_label("Click");
gtk_fixed_put(GTK_FIXED(fixed), button, 50, 50);
gtk_widget_set_size_request(button, 80, 35);
g_signal_connect(G_OBJECT(button), "clicked",
G_CALLBACK(button_clicked), NULL);
g_signal_connect(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), NULL);
gtk_widget_show_all(window);
gtk_main();
return 0;
}
g_signal_connect(G_OBJECT(button), "clicked",We use the g_signal_connect() function to connect the clicked signal to the button_clicked() callback.
G_CALLBACK(button_clicked), NULL);
void button_clicked(GtkWidget *widget, gpointer data)The callback will print "clicked" text to the console. The first parameter of the callback function is the object, which emitted the signal. In our case it is the Click button. The second parameter is optional. We may send some data to the callback. In our case, we did not send any data. We provided a NULL parameter in the g_signal_connect() function.
{
g_print("clicked\n");
}
g_signal_connect(G_OBJECT(window), "destroy",If we press on the x button located in the upper right corner of the titlebar, or we press Atl + F4, a destroy signal is emitted. We call the gtk_main_quit() function, which will terminate the application.
G_CALLBACK(gtk_main_quit), NULL);
Moving window
The next example shows, how we react to move events of a window.#include <gtk/gtk.h>In the example, we show the current position of the upper left corner of our window in the titlebar.
void frame_callback(GtkWindow *window,
GdkEvent *event, gpointer data)
{
int x, y;
char buf[10];
x = event->configure.x;
y = event->configure.y;
sprintf(buf, "%d, %d", x, y);
gtk_window_set_title(window, buf);
}
int main(int argc, char *argv[])
{
GtkWidget *window;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 230, 150);
gtk_window_set_title(GTK_WINDOW(window), "Simple");
gtk_widget_add_events(GTK_WIDGET(window), GDK_CONFIGURE);
g_signal_connect_swapped(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), G_OBJECT(window));
g_signal_connect(G_OBJECT(window), "configure-event",
G_CALLBACK(frame_callback), NULL);
gtk_widget_show(window);
gtk_main();
return 0;
}
gtk_widget_add_events(GTK_WIDGET(window), GDK_CONFIGURE);The event mask of the widget determines, what kind of event will a particular widget receive. Some event are preconfigured, other events have to be added to the event mask. The gtk_widget_add_events() adds a GDK_CONFIGURE event type to the mask. The GDK_CONFIGURE event type accounts for all size, position and stack order events.
g_signal_connect(G_OBJECT(window), "configure-event",The configure-event is emitted to size, position and stack order events.
G_CALLBACK(frame_callback), NULL);
void frame_callback(GtkWindow *window,The callback function has three parameters. The object that emitted the signal, GdkEvent and the optional data. We determine the x, y positions and set it to the title.
GdkEvent *event, gpointer data)
{
int x, y;
char buf[10];
x = event->configure.x;
y = event->configure.y;
sprintf(buf, "%d, %d", x, y);
gtk_window_set_title(window, buf);
}
Figure: Move event
The enter signal
The following example will show, how we can react to an enter signal. The enter signal is emitted, when we enter the area of a widget with a mouse pointer.#include <gtk/gtk.h>We will change the background color of the button widget, once we hover a mouse pointer over it.
void enter_button(GtkWidget *widget, gpointer data)
{
GdkColor color;
color.red = 27000;
color.green = 30325;
color.blue = 34181;
gtk_widget_modify_bg(widget, GTK_STATE_PRELIGHT, &color);
}
int main( int argc, char *argv[])
{
GtkWidget *window;
GtkWidget *fixed;
GtkWidget *button;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 230, 150);
gtk_window_set_title(GTK_WINDOW(window), "enter signal");
fixed = gtk_fixed_new();
gtk_container_add(GTK_CONTAINER(window), fixed);
button = gtk_button_new_with_label("Button");
gtk_widget_set_size_request(button, 80, 35);
gtk_fixed_put(GTK_FIXED(fixed), button, 50, 50);
g_signal_connect(G_OBJECT(button), "enter",
G_CALLBACK(enter_button), NULL);
g_signal_connect_swapped(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), NULL);
gtk_widget_show_all(window);
gtk_main();
return 0;
}
g_signal_connect(G_OBJECT(button), "enter",We call the enter_button() user function, when the enter signal occurs.
G_CALLBACK(enter_button), NULL);
GdkColor color;Inside the callback, we change the background of the button by calling the gtk_widget_modify_bg() function.
color.red = 27000;
color.green = 30325;
color.blue = 34181;
gtk_widget_modify_bg(widget, GTK_STATE_PRELIGHT, &color);
Disconnecting a callback
We can disconnect a callback from the signal. Next code example demonstrates such a case.#include <gtk/gtk.h>In the code example, we have a button and a check box. The check box connects or disconnects a callback from the clicked signal of the button.
int handler_id;
void button_clicked(GtkWidget *widget, gpointer data)
{
g_print("clicked\n");
}
void toogle_signal(GtkWidget *widget, gpointer window)
{
if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) {
handler_id = g_signal_connect(G_OBJECT(window), "clicked",
G_CALLBACK(button_clicked), NULL);
} else {
g_signal_handler_disconnect(window, handler_id);
}
}
int main( int argc, char *argv[])
{
GtkWidget *window;
GtkWidget *fixed;
GtkWidget *button;
GtkWidget *check;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 250, 150);
gtk_window_set_title(GTK_WINDOW(window), "Disconnect");
fixed = gtk_fixed_new();
gtk_container_add(GTK_CONTAINER(window), fixed);
button = gtk_button_new_with_label("Click");
gtk_widget_set_size_request(button, 80, 30);
gtk_fixed_put(GTK_FIXED(fixed), button, 30, 50);
check = gtk_check_button_new_with_label("Connect");
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), TRUE);
gtk_fixed_put(GTK_FIXED(fixed), check, 130, 50);
handler_id = g_signal_connect(G_OBJECT(button), "clicked",
G_CALLBACK(button_clicked), NULL);
g_signal_connect(G_OBJECT(check), "clicked",
G_CALLBACK(toogle_signal), (gpointer) button);
g_signal_connect_swapped(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), NULL);
gtk_widget_show_all(window);
gtk_main();
return 0;
}
handler_id = g_signal_connect(G_OBJECT(button), "clicked",The g_signal_connect() returns the handler id, which uniquely identifies the callback.
G_CALLBACK(button_clicked), NULL);
if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) {This code determines the state of the check box. It connects the callback if it is checked, or disconnects otherwise.
handler_id = g_signal_connect(G_OBJECT(window), "clicked",
G_CALLBACK(button_clicked), NULL);
} else {
g_signal_handler_disconnect(window, handler_id);
}
Figure: Disconnect
Drag and Drop example
In the next example, we show an interesting feature. We will show a borderless window and learn, how we can drag and move such a window.#include <gtk/gtk.h>The example demonstrates a drag and drop of a borderless window.
gboolean on_button_press (GtkWidget* widget,
GdkEventButton * event, GdkWindowEdge edge)
{
if (event->type == GDK_BUTTON_PRESS)
{
if (event->button == 1) {
gtk_window_begin_move_drag(GTK_WINDOW(gtk_widget_get_toplevel(widget)),
event->button,
event->x_root,
event->y_root,
event->time);
}
}
return FALSE;
}
int main( int argc, char *argv[])
{
GtkWidget *window;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 230, 150);
gtk_window_set_title(GTK_WINDOW(window), "Drag & drop");
gtk_window_set_decorated(GTK_WINDOW (window), FALSE);
gtk_widget_add_events(window, GDK_BUTTON_PRESS_MASK);
g_signal_connect(G_OBJECT(window), "button-press-event",
G_CALLBACK(on_button_press), NULL);
g_signal_connect_swapped(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), G_OBJECT(window));
gtk_widget_show(window);
gtk_main();
return 0;
}
gtk_window_set_decorated(GTK_WINDOW (window), FALSE);We remove the decoration of the window. This means, that the window will not have borders and titlebar.
g_signal_connect(G_OBJECT(window), "button-press-event",We connect the window to the button-press-event signal.
G_CALLBACK(on_button_press), NULL);
gboolean on_button_press (GtkWidget* widget,Inside the on_button_press(), we do perform the drag and drop operation. We check if the left mouse button was pressed. Then we call the gtk_window_begin_move_drag() function.
GdkEventButton * event, GdkWindowEdge edge)
{
if (event->type == GDK_BUTTON_PRESS)
{
if (event->button == 1) {
gtk_window_begin_move_drag(GTK_WINDOW(gtk_widget_get_toplevel(widget)),
event->button,
event->x_root,
event->y_root,
event->time);
}
}
return FALSE;
}
A timer example
The following example demonstrates a timer example. Timers are used when we have some repeating tasks. It could be a clock, a count down, visual effects or animations.#include <cairo.h>We will display a current local time on the window. We use the Cairo 2D library.
#include <gtk/gtk.h>
#include <time.h>
static char buffer[256];
static gboolean
on_expose_event(GtkWidget *widget,
GdkEventExpose *event,
gpointer data)
{
cairo_t *cr;
cr = gdk_cairo_create(widget->window);
cairo_move_to(cr, 30, 30);
cairo_show_text(cr, buffer);
cairo_destroy(cr);
return FALSE;
}
static gboolean
time_handler(GtkWidget *widget)
{
if (widget->window == NULL) return FALSE;
time_t curtime;
struct tm *loctime;
curtime = time(NULL);
loctime = localtime(&curtime);
strftime(buffer, 256, "%T", loctime);
gtk_widget_queue_draw(widget);
return TRUE;
}
int
main (int argc, char *argv[])
{
GtkWidget *window;
GtkWidget *darea;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
darea = gtk_drawing_area_new();
gtk_container_add(GTK_CONTAINER (window), darea);
g_signal_connect(darea, "expose-event",
G_CALLBACK(on_expose_event), NULL);
g_signal_connect(window, "destroy",
G_CALLBACK(gtk_main_quit), NULL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 170, 100);
gtk_window_set_title(GTK_WINDOW(window), "timer");
g_timeout_add(1000, (GSourceFunc) time_handler, (gpointer) window);
gtk_widget_show_all(window);
time_handler(window);
gtk_main();
return 0;
}
g_signal_connect(darea, "expose-event",We will draw the time inside the on_expose_event() callback. The callback is connected to the expose-event signal. The signal is emitted, when the window is going to be redrawn.
G_CALLBACK(on_expose_event), NULL);
g_timeout_add(1000, (GSourceFunc) time_handler, (gpointer) window);This function registers the timer. The time_handler() function is called repeatedly at regular intervals. In our case in every second. The timer function is called until it returns FALSE.
time_handler(window);This calles the timer function immediately. Otherwise, there would be one sec delay.
cairo_t *cr;This code draws the current time on the window. For more information about the Cairo 2D library, see the Dukeo's Cairo graphics tutorial.
cr = gdk_cairo_create(widget->window);
cairo_move_to(cr, 30, 30);
cairo_show_text(cr, buffer);
cairo_destroy(cr);
if (widget->window == NULL) return FALSE;When the window is destroyed, it may happen, that the timer function is called. This line will prevent working on already destroyed widget.
time_t curtime;These lines determine the current local time.
struct tm *loctime;
curtime = time(NULL);
loctime = localtime(&curtime);
strftime(buffer, 256, "%T", loctime);
gtk_widget_queue_draw(widget);This will invalidate the window area, which will emit the expose-event signal.
This chapter was about events in GTK+.
No comments:
Post a Comment