Sophie

Sophie

distrib > Mandriva > 2010.0 > i586 > media > contrib-release > by-pkgid > 345aa895e80053137c21f8693106c3a0 > files > 143

gtkmm2.4-documentation-2.17.4-1mdv2010.0.noarch.rpm

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Example Application: Creating a Clock with Cairo</title>
<link rel="stylesheet" href="style.css" type="text/css">
<meta name="generator" content="DocBook XSL Stylesheets V1.75.1">
<link rel="home" href="index.html" title="Programming with gtkmm">
<link rel="up" href="chapter-drawingarea.html" title="Chapter 15. The Drawing Area Widget">
<link rel="prev" href="sec-draw-images.html" title="Drawing Images">
<link rel="next" href="chapter-draganddrop.html" title="Chapter 16. Drag and Drop">
</head>
<body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF">
<div class="navheader">
<table width="100%" summary="Navigation header">
<tr><th colspan="3" align="center">Example Application: Creating a Clock with Cairo</th></tr>
<tr>
<td width="20%" align="left">
<a accesskey="p" href="sec-draw-images.html"><img src="icons/prev.png" alt="Prev"></a> </td>
<th width="60%" align="center">Chapter 15. The Drawing Area Widget</th>
<td width="20%" align="right"> <a accesskey="n" href="chapter-draganddrop.html"><img src="icons/next.png" alt="Next"></a>
</td>
</tr>
</table>
<hr>
</div>
<div class="sect1" title="Example Application: Creating a Clock with Cairo">
<div class="titlepage"><div><div><h2 class="title" style="clear: both">
<a name="sec-drawing-clock-example"></a>Example Application: Creating a Clock with Cairo</h2></div></div></div>
<p>
          Now that we've covered the basics of drawing with Cairo, let's try to
          put it all together and create a simple application that actually
          does something.  The following example uses Cairo to create a custom
          <code class="classname">Clock</code> widget.  The clock has a second hand, a
          minute hand, and an hour hand, and updates itself every second.
      </p>
<div class="screenshot"><div><img src="figures/cairo_clock.png"></div></div>
<p><a class="ulink" href="http://git.gnome.org/cgit/gtkmm-documentation/tree/examples/book/drawingarea/clock" target="_top">Source Code</a></p>
<p>File: <code class="filename">clock.h</code>
</p>
<pre class="programlisting">
#ifndef GTKMM_EXAMPLE_CLOCK_H
#define GTKMM_EXAMPLE_CLOCK_H

#include &lt;gtkmm/drawingarea.h&gt;

class Clock : public Gtk::DrawingArea
{
public:
  Clock();
  virtual ~Clock();

protected:
  //Override default signal handler:
  virtual bool on_expose_event(GdkEventExpose* event);

  bool on_timeout();

  double m_radius;
  double m_line_width;

};

#endif // GTKMM_EXAMPLE_CLOCK_H
</pre>
<p>File: <code class="filename">clock.cc</code>
</p>
<pre class="programlisting">
#include &lt;ctime&gt;
#include &lt;cmath&gt;
#include &lt;cairomm/context.h&gt;
#include "clock.h"

Clock::Clock()
: m_radius(0.42), m_line_width(0.05)
{
  Glib::signal_timeout().connect( sigc::mem_fun(*this, &amp;Clock::on_timeout), 1000 );

  #ifndef GLIBMM_DEFAULT_SIGNAL_HANDLERS_ENABLED
  //Connect the signal handler if it isn't already a virtual method override:
  signal_expose_event().connect(sigc::mem_fun(*this, &amp;Clock::on_expose_event), false);
  #endif //GLIBMM_DEFAULT_SIGNAL_HANDLERS_ENABLED
}

Clock::~Clock()
{
}

bool Clock::on_expose_event(GdkEventExpose* event)
{
  // This is where we draw on the window
  Glib::RefPtr&lt;Gdk::Window&gt; window = get_window();
  if(window)
  {
    Gtk::Allocation allocation = get_allocation();
    const int width = allocation.get_width();
    const int height = allocation.get_height();

    Cairo::RefPtr&lt;Cairo::Context&gt; cr = window-&gt;create_cairo_context();

    if(event)
    {
        // clip to the area indicated by the expose event so that we only
        // redraw the portion of the window that needs to be redrawn
        cr-&gt;rectangle(event-&gt;area.x, event-&gt;area.y,
                event-&gt;area.width, event-&gt;area.height);
        cr-&gt;clip();
    }

    // scale to unit square and translate (0, 0) to be (0.5, 0.5), i.e.
    // the center of the window
    cr-&gt;scale(width, height);
    cr-&gt;translate(0.5, 0.5);
    cr-&gt;set_line_width(m_line_width);

    cr-&gt;save();
    cr-&gt;set_source_rgba(0.337, 0.612, 0.117, 0.9);   // green
    cr-&gt;paint();
    cr-&gt;restore();
    cr-&gt;arc(0, 0, m_radius, 0, 2 * M_PI);
    cr-&gt;save();
    cr-&gt;set_source_rgba(1.0, 1.0, 1.0, 0.8);
    cr-&gt;fill_preserve();
    cr-&gt;restore();
    cr-&gt;stroke_preserve();
    cr-&gt;clip();

    //clock ticks
    for (int i = 0; i &lt; 12; i++)
    {
        double inset = 0.05;

        cr-&gt;save();
        cr-&gt;set_line_cap(Cairo::LINE_CAP_ROUND);

        if(i % 3 != 0)
        {
            inset *= 0.8;
            cr-&gt;set_line_width(0.03);
        }

        cr-&gt;move_to(
                (m_radius - inset) * cos (i * M_PI / 6),
                (m_radius - inset) * sin (i * M_PI / 6));
        cr-&gt;line_to (
                m_radius * cos (i * M_PI / 6),
                m_radius * sin (i * M_PI / 6));
        cr-&gt;stroke();
        cr-&gt;restore(); /* stack-pen-size */
    }

    // store the current time
    time_t rawtime;
    time(&amp;rawtime);
    struct tm * timeinfo = localtime (&amp;rawtime);

    // compute the angles of the indicators of our clock
    double minutes = timeinfo-&gt;tm_min * M_PI / 30;
    double hours = timeinfo-&gt;tm_hour * M_PI / 6;
    double seconds= timeinfo-&gt;tm_sec * M_PI / 30;

    cr-&gt;save();
    cr-&gt;set_line_cap(Cairo::LINE_CAP_ROUND);

    // draw the seconds hand
    cr-&gt;save();
    cr-&gt;set_line_width(m_line_width / 3);
    cr-&gt;set_source_rgba(0.7, 0.7, 0.7, 0.8); // gray
    cr-&gt;move_to(0, 0);
    cr-&gt;line_to(sin(seconds) * (m_radius * 0.9), 
            -cos(seconds) * (m_radius * 0.9));
    cr-&gt;stroke();
    cr-&gt;restore();

    // draw the minutes hand
    cr-&gt;set_source_rgba(0.117, 0.337, 0.612, 0.9);   // blue
    cr-&gt;move_to(0, 0);
    cr-&gt;line_to(sin(minutes + seconds / 60) * (m_radius * 0.8),
            -cos(minutes + seconds / 60) * (m_radius * 0.8));
    cr-&gt;stroke();

    // draw the hours hand
    cr-&gt;set_source_rgba(0.337, 0.612, 0.117, 0.9);   // green
    cr-&gt;move_to(0, 0);
    cr-&gt;line_to(sin(hours + minutes / 12.0) * (m_radius * 0.5),
            -cos(hours + minutes / 12.0) * (m_radius * 0.5));
    cr-&gt;stroke();
    cr-&gt;restore();

    // draw a little dot in the middle
    cr-&gt;arc(0, 0, m_line_width / 3.0, 0, 2 * M_PI);
    cr-&gt;fill();

  }

  return true;
}


bool Clock::on_timeout()
{
    // force our program to redraw the entire clock.
    Glib::RefPtr&lt;Gdk::Window&gt; win = get_window();
    if (win)
    {
        Gdk::Rectangle r(0, 0, get_allocation().get_width(),
                get_allocation().get_height());
        win-&gt;invalidate_rect(r, false);
    }
    return true;
}
</pre>
<p>File: <code class="filename">main.cc</code>
</p>
<pre class="programlisting">
#include "clock.h"
#include &lt;gtkmm/main.h&gt;
#include &lt;gtkmm/window.h&gt;

int main(int argc, char** argv)
{
   Gtk::Main kit(argc, argv);

   Gtk::Window win;
   win.set_title("Cairomm Clock");

   Clock c;
   win.add(c);
   c.show();

   Gtk::Main::run(win);

   return 0;
}
</pre>
<p>
          As before, almost all of the interesting stuff is done in the expose
          event handler <code class="methodname">on_expose_event()</code>.  Before we dig
          into the expose event handler, notice that the constructor for the
          <code class="classname">Clock</code> widget connects a handler function
          <code class="methodname">onSecondElapsed()</code> to a timer with a timeout
          period of 1000 milliseconds (1 second).  This means that
          <code class="methodname">onSecondElapsed()</code> will get called once per
          second.  The sole responsibility of this function is to invalidate
          the window so that <span class="application">gtkmm</span> will be forced to redraw it.
      </p>
<p>
          Now let's take a look at the code that performs the actual drawing.
          The first section of <code class="methodname">on_expose_event()</code> should be
          pretty familiar by now as it's mostly 'boilerplate' code for getting
          the <code class="classname">Gdk::Window</code>, creating a
          <code class="classname">Cairo::Context</code>, and clipping to the area that
          we want to re-draw.  This example again scales the coordinate system
          to be a unit square so that it's easier to draw the clock as a
          percentage of window size so that it will automatically scale when
          the window size is adjusted.  Furthermore, the coordinate system is
          scaled over and down so that the (0, 0) coordinate is in the very
          center of the window.
      </p>
<p>
          The function <code class="methodname">Cairo::Context::paint()</code> 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
          <code class="methodname">_preserve</code> variant to preserve the current path,
          and then this same path is clipped to make sure than our next lines
          don't go outside the outline of the clock.
      </p>
<p>
          After drawing the outline, we go around the clock and draw ticks for
          every hour, with a larger tick at 12, 3, 6, and 9. Now we're finally
          ready to implement the time-keeping functionality of the clock, which
          simply involves getting the current values for hours, minutes and
          seconds, and drawing the hands at the correct angles.
      </p>
</div>
<div class="navfooter">
<hr>
<table width="100%" summary="Navigation footer">
<tr>
<td width="40%" align="left">
<a accesskey="p" href="sec-draw-images.html"><img src="icons/prev.png" alt="Prev"></a> </td>
<td width="20%" align="center"><a accesskey="u" href="chapter-drawingarea.html"><img src="icons/up.png" alt="Up"></a></td>
<td width="40%" align="right"> <a accesskey="n" href="chapter-draganddrop.html"><img src="icons/next.png" alt="Next"></a>
</td>
</tr>
<tr>
<td width="40%" align="left" valign="top">Drawing Images </td>
<td width="20%" align="center"><a accesskey="h" href="index.html"><img src="icons/home.png" alt="Home"></a></td>
<td width="40%" align="right" valign="top"> Chapter 16. Drag and Drop</td>
</tr>
</table>
</div>
</body>
</html>