Aplicación de ejemplo: crear un reloj con Cairo
Ahora que se ha cubierto lo básico acerca del dibujo con Cairo, junte todo y cree una aplicación simple que haga algo de verdad. El siguiente ejemplo usa Cairo para crear un widget Clock personalizado. El reloj tiene una manecilla de segundos, una de minutos, y una de horas; y se actualiza cada segundo.

File: clock.h (For use with gtkmm 3, not gtkmm 2)
#ifndef GTKMM_EXAMPLE_CLOCK_H #define GTKMM_EXAMPLE_CLOCK_H #include <gtkmm/drawingarea.h> class Clock : public Gtk::DrawingArea { public: Clock(); virtual ~Clock(); protected: //Override default signal handler: bool on_draw(const Cairo::RefPtr<Cairo::Context>& cr) override; bool on_timeout(); double m_radius; double m_line_width; }; #endif // GTKMM_EXAMPLE_CLOCK_H
File: clock.cc (For use with gtkmm 3, not gtkmm 2)
#include <ctime> #include <cmath> #include <cairomm/context.h> #include <glibmm/main.h> #include "clock.h" Clock::Clock() : m_radius(0.42), m_line_width(0.05) { Glib::signal_timeout().connect( sigc::mem_fun(*this, &Clock::on_timeout), 1000 ); } Clock::~Clock() { } bool Clock::on_draw(const Cairo::RefPtr<Cairo::Context>& cr) { Gtk::Allocation allocation = get_allocation(); const int width = allocation.get_width(); const int height = allocation.get_height(); // scale to unit square and translate (0, 0) to be (0.5, 0.5), i.e. // the center of the window cr->scale(width, height); cr->translate(0.5, 0.5); cr->set_line_width(m_line_width); cr->save(); cr->set_source_rgba(0.337, 0.612, 0.117, 0.9); // green cr->paint(); cr->restore(); cr->arc(0, 0, m_radius, 0, 2 * M_PI); cr->save(); cr->set_source_rgba(1.0, 1.0, 1.0, 0.8); cr->fill_preserve(); cr->restore(); cr->stroke_preserve(); cr->clip(); //clock ticks for (int i = 0; i < 12; i++) { double inset = 0.05; cr->save(); cr->set_line_cap(Cairo::LINE_CAP_ROUND); if(i % 3 != 0) { inset *= 0.8; cr->set_line_width(0.03); } cr->move_to( (m_radius - inset) * cos (i * M_PI / 6), (m_radius - inset) * sin (i * M_PI / 6)); cr->line_to ( m_radius * cos (i * M_PI / 6), m_radius * sin (i * M_PI / 6)); cr->stroke(); cr->restore(); /* stack-pen-size */ } // store the current time time_t rawtime; time(&rawtime); struct tm * timeinfo = localtime (&rawtime); // compute the angles of the indicators of our clock double minutes = timeinfo->tm_min * M_PI / 30; double hours = timeinfo->tm_hour * M_PI / 6; double seconds= timeinfo->tm_sec * M_PI / 30; cr->save(); cr->set_line_cap(Cairo::LINE_CAP_ROUND); // draw the seconds hand cr->save(); cr->set_line_width(m_line_width / 3); cr->set_source_rgba(0.7, 0.7, 0.7, 0.8); // gray cr->move_to(0, 0); cr->line_to(sin(seconds) * (m_radius * 0.9), -cos(seconds) * (m_radius * 0.9)); cr->stroke(); cr->restore(); // draw the minutes hand cr->set_source_rgba(0.117, 0.337, 0.612, 0.9); // blue cr->move_to(0, 0); cr->line_to(sin(minutes + seconds / 60) * (m_radius * 0.8), -cos(minutes + seconds / 60) * (m_radius * 0.8)); cr->stroke(); // draw the hours hand cr->set_source_rgba(0.337, 0.612, 0.117, 0.9); // green cr->move_to(0, 0); cr->line_to(sin(hours + minutes / 12.0) * (m_radius * 0.5), -cos(hours + minutes / 12.0) * (m_radius * 0.5)); cr->stroke(); cr->restore(); // draw a little dot in the middle cr->arc(0, 0, m_line_width / 3.0, 0, 2 * M_PI); cr->fill(); return true; } bool Clock::on_timeout() { // force our program to redraw the entire clock. auto win = get_window(); if (win) { Gdk::Rectangle r(0, 0, get_allocation().get_width(), get_allocation().get_height()); win->invalidate_rect(r, false); } return true; }
File: main.cc (For use with gtkmm 3, not gtkmm 2)
#include "clock.h" #include <gtkmm/application.h> #include <gtkmm/window.h> int main(int argc, char** argv) { auto app = Gtk::Application::create(argc, argv, "org.gtkmm.example"); Gtk::Window win; win.set_title("Cairomm Clock"); Clock c; win.add(c); c.show(); return app->run(win); }
Al igual que antes, casi todo lo interesante se hace en el manejador de señal de dibujo on_draw(). Antes de profundizar en el manejador de señal de dibujo, tenga en cuenta que el constructor del widget Clock conecta una función manejadora on_timeout() a un temporizador con un período de espera de 1000 milisegundos (1 segundo). Esto significa que on_timeout() se llamará una vez por segundo. La única responsabilidad de esta función es invalidar la ventana para que gtkmm se vea forzado a redibujarla.
Ahora, eche un vistazo al código que hace el dibujo en sí. La primera sección del on_draw() ya le debería resultar bastante familiar. Este ejemplo, otra vez, escala el sistema de coordenadas para ser un cuadrado unitario, así es más fácil dibujar el reloj como un porcentaje del tamaño de la ventana para que se escale automáticamente cuando el tamaño de la ventana se ajuste. Además, el sistema de coordenadas se escala de tal manera que la coordinada (0, 0) esté justo en el centro de la ventana.
The function Cairo::Context::paint() is used here to set the background color of the window. This function takes no arguments and fills the current surface (or the clipped portion of the surface) with the source color currently active. After setting the background color of the window, we draw a circle for the clock outline, fill it with white, and then stroke the outline in black. Notice that both of these actions use the _preserve variant to preserve the current path, and then this same path is clipped to make sure that our next lines don't go outside the outline of the clock.
Después de haber dibujado el contorno, se recorre el reloj dibujando marcas por cada hora, con una marca más grande en la posición de las 12, 3, 6, y 9. Ahora, está finalmente listo para implementar la función del reloj, mostrar la hora, lo que simplemente implica obtener los valores actuales de la hora, minutos, y segundos, y dibujar las manecillas en los ángulos correctos.