示例程序:使用开罗创建时钟
既然我们已经将使用开罗画图的基础知识介绍完了,那么现在让我们尝试将所有的内容放在一起,创建一个可以执行某些操作的简单应用程序。以下示例使用开罗创建一个自定义的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; }
和之前一样,所有有趣的东西都在绘图函数on_draw()中完成。在深入研究绘图函数之前,请注意在Clock部件的构造函数中将处理函数on_timeout()连接到了用计时器上,该计时器的超时时间为1000毫秒(1秒)。这意味着on_timeout()将每秒被调用一次。这个函数的唯一任务就是使窗口无效,以便gtkmm强制重绘它。
现在让我们研究实际执行绘图的函数。到现在on_draw()函数的第一部分你应该很熟悉了。此示例再次将坐标系缩放为单位正方形,以便更轻松的按窗口大小的百分比绘制时钟。此外坐标系是按比例缩放的,所以(0,0)坐标位于窗口的正中央。
Cairo::Context::paint()方法用于设置窗口的背景色。这个函数不接受任何参数,并使用处于活跃状态的源颜色填充当前表面(或表面裁剪的一部分)。设置背景色以后,我们画一个圆作为时钟的轮廓,用白色填充它然后用黑色进行描边。请注意,这两个操作都用_preserve变体以保留当前路径,然后对相同的路径进行剪辑以确保我们下一行不会超过时钟的轮廓。
绘制轮廓后,我们围绕时钟为每个小时绘制可读线并为12、3、6、9绘制相对更大的刻度线。接着我们就可以实现时钟的计时功能了 -- 获取当前时间的时、分、秒然后以正确的角度将指针绘制出来。