Εφαρμογή παραδείγματος: Δημιουργία ενός ρολογιού με Cairo
Τώρα, που έχουμε καλύψει τα βασικά της σχεδίασης με Cairo, ας δοκιμάσουμε να τα βάλουμε όλα μαζί και να δημιουργήσουμε μια απλή εφαρμογή που πράγματι κάνει κάτι. Το παρακάτω παράδειγμα χρησιμοποιεί Cairo για τη δημιουργία ενός προσαρμοσμένου γραφικού συστατικού Clock. Το ρολόι έχει ένα δευτερολεπτοδείκτη, έναν λεπτοδείκτη και έναν ωροδείκτη και ενημερώνει τον εαυτόν του κάθε δευτερόλεπτο.

File: clock.h (For use with gtkmm 4)
#ifndef GTKMM_EXAMPLE_CLOCK_H #define GTKMM_EXAMPLE_CLOCK_H #include <gtkmm/drawingarea.h> class Clock : public Gtk::DrawingArea { public: Clock(); virtual ~Clock(); protected: void on_draw(const Cairo::RefPtr<Cairo::Context>& cr, int width, int height); bool on_timeout(); double m_radius; double m_line_width; }; #endif // GTKMM_EXAMPLE_CLOCK_H
File: main.cc (For use with gtkmm 4)
#include "clock.h" #include <gtkmm/application.h> #include <gtkmm/window.h> class ExampleWindow : public Gtk::Window { public: ExampleWindow(); protected: Clock m_clock; }; ExampleWindow::ExampleWindow() { set_title("Cairomm Clock"); set_child(m_clock); } int main(int argc, char** argv) { auto app = Gtk::Application::create("org.gtkmm.example"); return app->make_window_and_run<ExampleWindow>(argc, argv); }
File: clock.cc (For use with gtkmm 4)
#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 ); set_draw_func(sigc::mem_fun(*this, &Clock::on_draw)); } Clock::~Clock() { } void Clock::on_draw(const Cairo::RefPtr<Cairo::Context>& cr, int width, int 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::Context::LineCap::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::Context::LineCap::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(); } bool Clock::on_timeout() { // force our program to redraw the entire clock. queue_draw(); return true; }
As before, almost all of the interesting stuff is done in the draw function on_draw(). Before we dig into the draw function, notice that the constructor for the Clock widget connects a handler function on_timeout() to a timer with a timeout period of 1000 milliseconds (1 second). This means that on_timeout() will get called once per second. The sole responsibility of this function is to invalidate the window so that gtkmm will be forced to redraw it.
Τώρα, ας ρίξουμε μια ματιά στον κώδικα που εκτελεί την ενεργή σχεδίαση. Η πρώτη ενότητα της on_draw() πρέπει να είναι αρκετά οικεία μέχρι τώρα. Αυτό το παράδειγμα κλιμακώνει πάλι το σύστημα συντεταγμένων για να είναι μια τετράγωνη μονάδα, έτσι ώστε να είναι ευκολότερη η σχεδίαση του ρολογιού ως ποσοστού του μεγέθους του παραθύρου, έτσι ώστε να κλιμακώσει αυτόματα όταν το μέγεθος του παραθύρου προσαρμόζεται. Επιπλέον, το σύστημα συντεταγμένων κλιμακώνεται πάνω και κάτω έτσι ώστε η συντεταγμένη (0, 0) να είναι ακριβώς στο κέντρο του παραθύρου.
Η συνάρτηση Cairo::Context::paint() χρησιμοποιείται εδώ για να ορίσει το χρώμα παρασκηνίου του παραθύρου. Αυτή η συνάρτηση δεν παίρνει ορίσματα και γεμίζει την τρέχουσα επιφάνεια (ή το αποκομμένο τμήμα της επιφάνειας) με το τρέχον ενεργό πηγαίο χρώμα. Μετά τον ορισμό του χρώματος παρασκηνίου του παραθύρου, σχεδιάζουμε έναν κύκλο για το περίγραμμα του ρολογιού, το γεμίζουμε με λευκό και έπειτα βάφουμε το περίγραμμα με μαύρο. Σημειώστε ότι και οι δυο αυτές ενέργειες χρησιμοποιούν την παραλλαγή _preserve για να διατηρήσουν το τρέχον μονοπάτι και έπειτα αυτό το ίδιο το μονοπάτι περικόπτεται για να διασφαλίσει ότι οι επόμενες γραμμές μας δεν θα πάνε έξω από το περίγραμμα του ρολογιού.
Μετά τη σχεδίαση του περιγράμματος, πηγαίνουμε γύρω από το ρολόι και σχεδιάζουμε υποδιαιρέσεις για κάθε ώρα, με μια μεγαλύτερη υποδιαίρεση στα 12, 3, 6 και 9. Τώρα, τελικά είμαστε έτοιμοι να υλοποιήσουμε την λειτουργία διατήρησης του χρόνου του ρολογιού, που εμπεριέχει απλά τη λήψη των τρεχουσών τιμών για ώρες, λεπτά και δευτερόλεπτα και σχεδίαση των δεικτών στις σωστές γωνίες.