Ma petite horloge transparente [1]
Dans un premier temps on écrit un programme gtk minimal:
#include <gtk/gtk.h>
GtkWidget *window;
int main(int argc, char **argv) {
gtk_init (&argc, &argv);
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
g_signal_connect (window, "destroy",
G_CALLBACK (gtk_main_quit), NULL);
gtk_widget_show_all (window);
gtk_main ();
return 0;
}
La ligne de commande pour compiler est:
gcc `pkg-config --cflags --libs gtk+-2.0 cairo` -o clock clock.c
Ce code est très simple, il initialise la librairie GTK, crée une fenêtre puis lance
la boucle d'évènements. Maintenant passons aux choses sérieuses et ajoutons de quoi dessiner
notre horloge.
// nécessaire pour accéder à l'heure système
#include <time.h>
gboolean expose(GtkWidget *widget, GdkEventExpose *event);
...
g_signal_connect (window, "expose_event",
G_CALLBACK (expose), NULL);
g_signal_connect (window, "destroy",
G_CALLBACK (gtk_main_quit), NULL);
...
gboolean expose(GtkWidget *widget, GdkEventExpose *event) {
cairo_t *cr;
double x, y, w, h;
double radius;
double angle;
x = widget->allocation.x;
y = widget->allocation.y;
w = widget->allocation.width;
h = widget->allocation.height;
cr = gdk_cairo_create (widget->window);
cairo_rectangle (cr,
x, y, w, h);
cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR);
cairo_fill(cr);
cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
x = widget->allocation.x + widget->allocation.width / 2;
y = widget->allocation.y + widget->allocation.height / 2;
radius = MIN (widget->allocation.width / 2,
widget->allocation.height / 2) - 5;
cairo_arc (cr, x, y, radius, 0, 2 * M_PI);
cairo_set_source_rgb (cr, 1, 1, 1);
cairo_fill_preserve (cr);
// le contour préservé est réutilisé pour
// cercler ce rond de noir
cairo_set_source_rgb (cr, 0, 0, 0);
cairo_stroke (cr);
struct tm* now_hms;
time_t now = time(NULL); // heure GMT
now_hms = localtime(&now); // conversion vers heure locale
angle = now_hms->tm_hour/12.0*M_PI-M_PI/2;
cairo_save (cr);
cairo_set_line_width (cr, 1.5 *
cairo_get_line_width (cr));
cairo_move_to(cr, x, y);
cairo_line_to(cr,
x + radius * 0.9 * cos(angle-M_PI/100),
y + radius * 0.9 * sin(angle-M_PI/100));
cairo_stroke (cr);
cairo_restore (cr);
return TRUE;
}
Bon c'est bien beau j'ai l'heure courante mais ca ne bouge pas ton truc ?
...
gboolean expose(GtkWidget *widget, GdkEventExpose *event);
gboolean tick(gpointer data);
...
g_timeout_add(1000, tick, NULL);
gtk_main ();
...
gboolean tick(gpointer data) {
gtk_widget_queue_draw(window);
return TRUE;
}
...
La transparence
Pour obtenir une fenêtre transparente vous aurez besoin d'un serveur X
comprenant l'extension Composite et d'un gestionaire de composition, comme
xcompmgr
.
Aprés cela il suffit dire à GTK que notre fenêtre doit supporter
les informations de transparence.
...
gtk_widget_set_colormap(window, gdk_screen_get_rgba_colormap
(gtk_widget_get_screen(window)));
gtk_window_set_decorated(GTK_WINDOW(window), FALSE);
g_signal_connect (window, "expose_event",
G_CALLBACK (expose), NULL);
...
Annexe
Le code pour afficher les autres aiguilles et les repères des heures.
...
cairo_stroke (cr);
int i;
for (i = 0; i < 12; i++)
{
int inset;
inset = 0.1 * radius;
cairo_move_to (cr,
x + (radius - inset) * cos (i * M_PI / 6),
y + (radius - inset) * sin (i * M_PI / 6));
cairo_line_to (cr,
x + radius * cos (i * M_PI / 6),
y + radius * sin (i * M_PI / 6));
cairo_stroke (cr);
}
for (i = 0; i < 12; i++)
{
int inset;
cairo_save (cr); /* save pen size to stack */
if (i % 3 == 0)
inset = 0.2 * radius;
else
{
inset = 0.1 * radius;
cairo_set_line_width (cr, 0.5 *
cairo_get_line_width (cr));
}
cairo_move_to (cr,
x + (radius - inset) * cos (i * M_PI / 6),
y + (radius - inset) * sin (i * M_PI / 6));
cairo_line_to (cr,
x + radius * cos (i * M_PI / 6),
y + radius * sin (i * M_PI / 6));
cairo_stroke (cr);
cairo_restore (cr); /* recover pen size from stack */
}
...
angle = now_hms->tm_min/60.0*M_PI-M_PI/2;
cairo_move_to(cr, x, y);
cairo_line_to(cr,
x + radius * 0.95 * cos(angle),
y + radius * 0.95 * sin(angle);
cairo_stroke (cr);
angle = now_hms->tm_sec/60.0*M_PI-M_PI/2;
cairo_save (cr); /* save pen size to stack */
cairo_set_line_width (cr, 0.7 *
cairo_get_line_width (cr));
cairo_move_to(cr, x, y);
cairo_line_to(cr,
x + radius * 0.95 * cos(angle),
y + radius * 0.95 * sin(angle);
cairo_stroke (cr);
cairo_restore (cr); /* recover pen size from stack */
...
Références
-
Article, dans le gnome journal, dont est tiré ce tutorial
- Référence de la librairie cairo sur le
type cairo_operator_t
- un texte traitant des opérateurs de Porter-Duff
- Code source complet
Remerciements
- Davyd Madeley pour son article dans Gnome Journal
Benjamin Dauvergne <benjamin.dauvergne@gmail.com>
Cet article et son code sont dans le domaine public