L'élément graphique zone de dessin
The DrawingArea widget is a blank window that gives you the freedom to create any graphic you desire. Along with that freedom comes the responsibility to handle draw signals on the widget. When a widget is first shown, or when it is covered and then uncovered again it needs to redraw itself. Most widgets have code to do this, but the DrawingArea does not, allowing you to write your own draw signal handler to determine how the contents of the widget will be drawn. This is most often done by overriding the virtual on_draw() member function.
GTK+ uses the Cairo drawing API. With gtkmm, you may use the cairomm C++ API for cairo.
Avec Cairo, vous pouvez tracer des figures très sophistiquées, mais les fonctions membres pour réaliser cela sont tout à fait simples. Cairo fournit des fonctions membres pour tracer des lignes droites, des lignes courbes et des arcs (y compris des cercles). Ces figures de base peuvent être combinées pour créer des formes complexes et des régions qui peuvent être remplies par des couleurs unies, des dégradés, des motifs et autres choses. En plus, Cairo peut effectuer des transformations complexes, des compositions d'images et effectuer des rendus de texte sans crénelage.

Cairo et Pango
Même si Cairo peut afficher du texte, il n'a pas été conçu pour être un remplaçant de Pango. Pango est un meilleur choix si vous devez exécuter des rendus de texte complexes comme des textes enveloppants ou inscrits dans une ellipse. L'écriture avec Cairo doit être réservée à des textes intégrés dans un graphique.
Dans ce paragraphe du tutoriel, nous voyons le modèle du tracé de base Cairo ; nous décrivons chacun des éléments de tracé de base dans le détail (avec des exemples) et ensuite nous présentons une application simple utilisant Cairo pour dessiner un élément graphique d'horloge analogique.

Le modèle de tracé Cairo
Le concept de base du tracé dans Cairo consiste à définir des chemins « invisibles », puis à les tracer ou les remplir pour les rendre visibles.
To do any drawing in gtkmm with Cairo, you must first create a Cairo::Context object. This class holds all of the graphics state parameters that describe how drawing is to be done. This includes information such as line width, color, the surface to draw to, and many other things. This allows the actual drawing functions to take fewer arguments to simplify the interface. In gtkmm, a Cairo::Context is created by calling the Gdk::Window::create_cairo_context() function. Since Cairo contexts are reference-counted objects, this function returns a Cairo::RefPtr<Cairo::Context> object.
L'exemple ci-après montre comment définir un contexte Cairo avec une couleur de premier plan rouge et une épaisseur de trait de 2. Toute fonction de tracé se servant de ce contexte utilisera ces réglages.

Gtk::DrawingArea myArea;
Cairo::RefPtr<Cairo::Context> myContext = myArea.get_window()->create_cairo_context();
myContext->set_source_rgb(1.0, 0.0, 0.0);
myContext->set_line_width(2.0);

Chaque Cairo::Context est associé à une fenêtre Gdk::Window donnée ; ainsi, la première ligne de l'exemple ci-dessus crée un élément graphique Gtk::DrawingArea et la deuxième ligne utilise son objet Gdk::Window associé pour créer un objet Cairo::Context. Les deux dernières lignes modifient les caractéristiques graphiques du contexte.
De nombreuses variables d'état graphique peuvent être définies pour un contexte Cairo. Les attributs les plus courants sont la couleur (avec set_source_rgb() ou set_source_rgba() pour des couleurs transparentes), l'épaisseur du trait (avec set_line_width()), le type de pointillé (avec set_dash()), les bouts de lignes (avec set_line_cap()), les jonctions de lignes (avec set_line_join()) et les styles de polices de caractères (avec set_font_size(), set_font_face() et autres). Il existe beaucoup d'autres réglages : les matrices de transformation, les règles de remplissage, les types d'anti-crénelage, etc. Pour plus d'informations, consultez la documentation de l'API cairomm.
The current state of a Cairo::Context can be saved to an internal stack of saved states and later be restored to the state it was in when you saved it. To do this, use the save() method and the restore() method. This can be useful if you need to temporarily change the line width and color (or any other graphics setting) in order to draw something and then return to the previous settings. In this situation, you could call Cairo::Context::save(), change the graphics settings, draw the lines, and then call Cairo::Context::restore() to restore the original graphics state. Multiple calls to save() and restore() can be nested; each call to restore() restores the state from the matching paired save().
It is good practice to put all modifications to the graphics state between save()/restore() function calls. For example, if you have a function that takes a Cairo::Context reference as an argument, you might implement it as follows:

void doSomething(const Cairo::RefPtr<Cairo::Context>& context, int x)
{
    context->save();
    // change graphics state
    // perform drawing operations
    context->restore();
}

The virtual on_draw() method provides a Cairo context that you shall use for drawing in the Gtk::DrawingArea widget. It is not necessary to save and restore this Cairo context in on_draw().

Tracé de droites
Now that we understand the basics of the Cairo graphics library, we're almost ready to start drawing. We'll start with the simplest of drawing elements: the straight line. But first you need to know a little bit about Cairo's coordinate system. The origin of the Cairo coordinate system is located in the upper-left corner of the window with positive x values to the right and positive y values going down.
Since the Cairo graphics library was written with support for multiple output targets (the X window system, PNG images, OpenGL, etc), there is a distinction between user-space and device-space coordinates. The mapping between these two coordinate systems defaults to one-to-one so that integer values map roughly to pixels on the screen, but this setting can be adjusted if desired. Sometimes it may be useful to scale the coordinates so that the full width and height of a window both range from 0 to 1 (the 'unit square') or some other mapping that works for your application. This can be done with the Cairo::Context::scale() function.

Exemple
Dans cet exemple, nous allons écrire un programme gtkmm petit, mais pleinement fonctionnel, et tracer quelques lignes dans la fenêtre. Les lignes sont dessinées en créant un chemin, puis en le traçant. Un tracé est créé avec les fonctions Cairo::Context::move_to() et Cairo::Context::line_to(). La fonction move_to() est semblable à l'action de lever la pointe de votre crayon au dessus du papier et de la placer quelque part ailleurs — aucune ligne n'est tracée entre le point où vous étiez et celui où vous vous êtes déplacé. Pour tracer une ligne entre deux points, utilisez la fonction line_to().
Après avoir terminé la création de votre chemin, vous n'avez encore dessiné rien de visible. Pour rendre le chemin visible, il faut utiliser la fonction stroke() qui dessine le tracé actuel avec une ligne d'épaisseur et de style précisés dans l'objet Cairo::Context. Après le traçage, le chemin actuel est effacé ; vous pouvez donc en débuter un nouveau.
Beaucoup de fonctions de dessin de Cairo disposent d'une variante _preserve(). Normalement, les fonctions de dessin telles que clip(), fill() ou stroke() effacent le chemin actuel. Si vous utilisez la variante _preserve(), le chemin actuel sera préservé de sorte que vous pouvez à nouveau l'utiliser avec une autre fonction de dessin.

Zone de dessin ‑ Lignes
Source Code
File: myarea.h (For use with gtkmm 3, not gtkmm 2)

#ifndef GTKMM_EXAMPLE_MYAREA_H
#define GTKMM_EXAMPLE_MYAREA_H

#include <gtkmm/drawingarea.h>

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

protected:
  //Override default signal handler:
  virtual bool on_draw(const Cairo::RefPtr<Cairo::Context>& cr);
};

#endif // GTKMM_EXAMPLE_MYAREA_H

File: myarea.cc (For use with gtkmm 3, not gtkmm 2)

#include "myarea.h"
#include <cairomm/context.h>

MyArea::MyArea()
{
}

MyArea::~MyArea()
{
}

bool MyArea::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();

  // coordinates for the center of the window
  int xc, yc;
  xc = width / 2;
  yc = height / 2;

  cr->set_line_width(10.0);

  // draw red lines out from the center of the window
  cr->set_source_rgb(0.8, 0.0, 0.0);
  cr->move_to(0, 0);
  cr->line_to(xc, yc);
  cr->line_to(0, height);
  cr->move_to(xc, yc);
  cr->line_to(width, yc);
  cr->stroke();

  return true;
}

File: main.cc (For use with gtkmm 3, not gtkmm 2)

#include "myarea.h"
#include <gtkmm/application.h>
#include <gtkmm/window.h>

int main(int argc, char** argv)
{
   Glib::RefPtr<Gtk::Application> app = Gtk::Application::create(argc, argv, "org.gtkmm.example");

   Gtk::Window win;
   win.set_title("DrawingArea");

   MyArea area;
   win.add(area);
   area.show();

   return app->run(win);
}

This program contains a single class, MyArea, which is a subclass of Gtk::DrawingArea and contains an on_draw() member function. This function is called whenever the image in the drawing area needs to be redrawn. It is passed a Cairo::RefPtr pointer to a Cairo::Context that we use for the drawing. The actual drawing code sets the color we want to use for drawing by using set_source_rgb() which takes arguments defining the Red, Green, and Blue components of the desired color (valid values are between 0 and 1). After setting the color, we created a new path using the functions move_to() and line_to(), and then stroked this path with stroke().

Tracé en coordonnées relatives
Dans l'exemple ci-dessus nous avons tout tracé en utilisant des coordonnées absolues. Nous pouvons également tracer en coordonnées relatives. Pour une droite, on utilise la fonction Cairo::Context::rel_line_to().

Styles de trait
En plus du tracé élémentaire de droites, vous pouvez personnaliser un certain nombre de choses dans les tracés de lignes. Nous avons déjà vu des exemples de paramétrage de couleur et d'épaisseur de trait, mais il en existe bien d'autres.
Si vous avez tiré une série de traits formant un chemin, vous pouvez vouloir les raccorder d'une certaine façon. Cairo offre trois façons différentes de raccorder les lignes entre elles : l'onglet, le biseau et l'arrondi. Voyez-les ci-dessous :

Différents types de raccord dans Cairo
Le type de raccord est défini avec la fonction Cairo::Context::set_line_join().
Les fins de ligne peuvent avoir différents styles également. Le style par défaut est de débuter et arrêter la ligne exactement aux points de définition. On nomme cela la coupe droite. Les autres options sont l'arrondi (utilise une terminaison en demi-cercle, le centre du demi-cercle étant au point terminal de la ligne) ou le carré (utilise une terminaison carrée diagonale, le centre du carré étant au point terminal de la ligne). Ce paramétrage est défini avec la fonction Cairo::Context::set_line_cap().
There are other things you can customize as well, including creating dashed lines and other things. For more information, see the Cairo API documentation.

Drawing thin lines
If you try to draw one pixel wide lines, you may notice that the line sometimes comes up blurred and wider than it ought to be. This happens because Cairo will try to draw from the selected position, to both sides (half to each), so if you're positioned right on the intersection of the pixels, and want a one pixel wide line, Cairo will try to use half of each adjacent pixel, which isn't possible (a pixel is the smallest unit possible). This happens when the width of the line is an odd number of pixels (not just one pixel).
The trick is to position in the middle of the pixel where you want the line to be drawn, and thus guaranteeing you get the desired results. See Cairo FAQ.

Drawing Area - Thin Lines
Source Code
File: examplewindow.h (For use with gtkmm 3, not gtkmm 2)

#ifndef GTKMM_EXAMPLEWINDOW_H
#define GTKMM_EXAMPLEWINDOW_H

#include <gtkmm/window.h>
#include <gtkmm/grid.h>
#include <gtkmm/checkbutton.h>
#include "myarea.h"

class ExampleWindow : public Gtk::Window
{
public:
  ExampleWindow();
  virtual ~ExampleWindow();

protected:
  //Signal handlers:
  void on_button_toggled();

private:
  Gtk::Grid m_Container;
  MyArea m_Area_Lines;
  Gtk::CheckButton m_Button_FixLines;
};

#endif //GTKMM_EXAMPLEWINDOW_H

File: myarea.h (For use with gtkmm 3, not gtkmm 2)

#ifndef GTKMM_EXAMPLE_MYAREA_H
#define GTKMM_EXAMPLE_MYAREA_H

#include <gtkmm/drawingarea.h>

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

  void fix_lines(bool fix = true);
  void force_redraw();

protected:
  //Override default signal handler:
  virtual bool on_draw(const Cairo::RefPtr<Cairo::Context>& cr);

private:
  double m_fix;
};

#endif // GTKMM_EXAMPLE_MYAREA_H

File: examplewindow.cc (For use with gtkmm 3, not gtkmm 2)

#include "examplewindow.h"

ExampleWindow::ExampleWindow()
: m_Button_FixLines("Fix lines")
{
  set_title("Thin lines example");

  m_Container.set_orientation(Gtk::ORIENTATION_HORIZONTAL);

  m_Container.add(m_Area_Lines);
  m_Container.add(m_Button_FixLines);

  add(m_Container);

  m_Button_FixLines.signal_toggled().connect(
    sigc::mem_fun(*this, &ExampleWindow::on_button_toggled));

  // Synchonize the drawing in m_Area_Lines with the state of the toggle button.
  on_button_toggled();

  show_all_children();
}

ExampleWindow::~ExampleWindow()
{
}

void ExampleWindow::on_button_toggled()
{
  m_Area_Lines.fix_lines(m_Button_FixLines.get_active());
}

File: myarea.cc (For use with gtkmm 3, not gtkmm 2)

#include "myarea.h"

MyArea::MyArea()
: m_fix (0)
{
  set_size_request (200, 100);
}

MyArea::~MyArea()
{
}

bool MyArea::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();

  cr->set_line_width(1.0);

  // draw one line, every two pixels
  // without the 'fix', you won't notice any space between the lines,
  // since each one will occupy two pixels (width)
  for (int i = 0; i < width; i += 2)
  {
    cr->move_to(i + m_fix, 0);
    cr->line_to(i + m_fix, height);
  }

  cr->stroke();

  return true;
}

// Toogle between both values (0 or 0.5)
void MyArea::fix_lines(bool fix)
{
  // to get the width right, we have to draw in the middle of the pixel
  m_fix = fix ? 0.5 : 0.0;

  force_redraw();
}

// force the redraw of the image
void MyArea::force_redraw()
{
  Glib::RefPtr<Gdk::Window> win = get_window();
  if (win)
  {
    Gdk::Rectangle r(0, 0, get_allocation().get_width(), get_allocation().get_height());
    win->invalidate_rect(r, false);
  }
}

File: main.cc (For use with gtkmm 3, not gtkmm 2)

#include "examplewindow.h"
#include <gtkmm/application.h>

int main(int argc, char* argv[])
{
  Glib::RefPtr<Gtk::Application> app = Gtk::Application::create(argc, argv, "org.gtkmm.example");

  ExampleWindow window;

  //Shows the window and returns when it is closed.
  return app->run(window);
}

Tracé de courbes
Outre la possibilité de tracer des droites, Cairo vous permet de tracer facilement des lignes courbes (techniquement une courbe de Bézier d'ordre 3) avec les fonctions Cairo::Context::curve_to() et Cairo::Context::rel_curve_to(). Ces fonctions prennent en paramètres les coordonnées des extrémités ainsi que celles de deux points de « contrôle ». Ce sera plus facile à expliquer sur un exemple, alors lançons-nous.

Exemple
Cette simple application trace une courbe avec Cairo et affiche les points de contrôle pour chaque extrémité de la courbe.

Zone de dessin ‑ Lignes
Source Code
File: myarea.h (For use with gtkmm 3, not gtkmm 2)

#ifndef GTKMM_EXAMPLE_MYAREA_H
#define GTKMM_EXAMPLE_MYAREA_H

#include <gtkmm/drawingarea.h>

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

protected:
  //Override default signal handler:
  virtual bool on_draw(const Cairo::RefPtr<Cairo::Context>& cr);
};

#endif // GTKMM_EXAMPLE_MYAREA_H

File: myarea.cc (For use with gtkmm 3, not gtkmm 2)

#include "myarea.h"
#include <cairomm/context.h>

MyArea::MyArea()
{
}

MyArea::~MyArea()
{
}

bool MyArea::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();

  double x0=0.1, y0=0.5, // start point
         x1=0.4, y1=0.9,  // control point #1
         x2=0.6, y2=0.1,  // control point #2
         x3=0.9, y3=0.5;  // end point

  // scale to unit square (0 to 1 width and height)
  cr->scale(width, height);

  cr->set_line_width(0.05);
  // draw curve
  cr->move_to(x0, y0);
  cr->curve_to(x1, y1, x2, y2, x3, y3);
  cr->stroke();
  // show control points
  cr->set_source_rgba(1, 0.2, 0.2, 0.6);
  cr->move_to(x0, y0);
  cr->line_to (x1, y1);
  cr->move_to(x2, y2);
  cr->line_to (x3, y3);
  cr->stroke();

  return true;
}

File: main.cc (For use with gtkmm 3, not gtkmm 2)

#include "myarea.h"
#include <gtkmm/application.h>
#include <gtkmm/window.h>

int main(int argc, char** argv)
{
   Glib::RefPtr<Gtk::Application> app = Gtk::Application::create(argc, argv, "org.gtkmm.example");

   Gtk::Window win;
   win.set_title("DrawingArea");

   MyArea area;
   win.add(area);
   area.show();

   return app->run(win);
}

The only difference between this example and the straight line example is in the on_draw() function, but there are a few new concepts and functions introduced here, so let's examine them briefly.
We make a call to Cairo::Context::scale(), passing in the width and height of the drawing area. This scales the user-space coordinate system such that the width and height of the widget are both equal to 1.0 'units'. There's no particular reason to scale the coordinate system in this case, but sometimes it can make drawing operations easier.
The call to Cairo::Context::curve_to() should be fairly self-explanatory. The first pair of coordinates define the control point for the beginning of the curve. The second set of coordinates define the control point for the end of the curve, and the last set of coordinates define the destination point. To make the concept of control points a bit easier to visualize, a line has been drawn from each control point to the end-point on the curve that it is associated with. Note that these control point lines are both translucent. This is achieved with a variant of set_source_rgb() called set_source_rgba(). This function takes a fourth argument specifying the alpha value of the color (valid values are between 0 and 1).

Tracé des arcs et des cercles
With Cairo, the same function is used to draw arcs, circles, or ellipses: Cairo::Context::arc(). This function takes five arguments. The first two are the coordinates of the center point of the arc, the third argument is the radius of the arc, and the final two arguments define the start and end angle of the arc. All angles are defined in radians, so drawing a circle is the same as drawing an arc from 0 to 2 * M_PI radians. An angle of 0 is in the direction of the positive X axis (in user-space). An angle of M_PI/2 radians (90 degrees) is in the direction of the positive Y axis (in user-space). Angles increase in the direction from the positive X axis toward the positive Y axis. So with the default transformation matrix, angles increase in a clockwise direction. (Remember that the positive Y axis points downwards.)
To draw an ellipse, you can scale the current transformation matrix by different amounts in the X and Y directions. For example, to draw an ellipse with center at x, y and size width, height:

context->save();
context->translate(x, y);
context->scale(width / 2.0, height / 2.0);
context->arc(0.0, 0.0, 1.0, 0.0, 2 * M_PI);
context->restore();

Exemple
Voici un exemple de programme simple traçant un arc, un cercle et une ellipse sur une zone de dessin.

Zone de dessin ‑ Arcs
Source Code
File: myarea.h (For use with gtkmm 3, not gtkmm 2)

#ifndef GTKMM_EXAMPLE_MYAREA_H
#define GTKMM_EXAMPLE_MYAREA_H

#include <gtkmm/drawingarea.h>

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

protected:
  //Override default signal handler:
  virtual bool on_draw(const Cairo::RefPtr<Cairo::Context>& cr);
};

#endif // GTKMM_EXAMPLE_MYAREA_H

File: myarea.cc (For use with gtkmm 3, not gtkmm 2)

#include "myarea.h"
#include <cairomm/context.h>
#include <cmath>

MyArea::MyArea()
{
}

MyArea::~MyArea()
{
}

bool MyArea::on_draw(const Cairo::RefPtr<Cairo::Context>& cr)
{
  // This is where we draw on the window
  Gtk::Allocation allocation = get_allocation();
  const int width = allocation.get_width();
  const int height = allocation.get_height();
  const int lesser = MIN(width, height);

  // coordinates for the center of the window
  int xc, yc;
  xc = width / 2;
  yc = height / 2;

  cr->set_line_width(lesser * 0.02);  // outline thickness changes
                                      // with window size

  // first draw a simple unclosed arc
  cr->save();
  cr->arc(width / 3.0, height / 4.0, lesser / 4.0, -(M_PI / 5.0), M_PI);
  cr->close_path();   // line back to start point
  cr->set_source_rgb(0.0, 0.8, 0.0);
  cr->fill_preserve();
  cr->restore();  // back to opaque black
  cr->stroke();   // outline it

  // now draw a circle
  cr->save();
  cr->arc(xc, yc, lesser / 4.0, 0.0, 2.0 * M_PI); // full circle
  cr->set_source_rgba(0.0, 0.0, 0.8, 0.6);    // partially translucent
  cr->fill_preserve();
  cr->restore();  // back to opaque black
  cr->stroke();

  // and finally an ellipse
  double ex, ey, ew, eh;
  // center of ellipse
  ex = xc;
  ey = 3.0 * height / 4.0;
  // ellipse dimensions
  ew = 3.0 * width / 4.0;
  eh = height / 3.0;

  cr->save();

  cr->translate(ex, ey);  // make (ex, ey) == (0, 0)
  cr->scale(ew / 2.0, eh / 2.0);  // for width: ew / 2.0 == 1.0
                                  // for height: eh / 2.0 == 1.0

  cr->arc(0.0, 0.0, 1.0, 0.0, 2 * M_PI);  // 'circle' centered at (0, 0)
                                          // with 'radius' of 1.0

  cr->set_source_rgba(0.8, 0.0, 0.0, 0.7);
  cr->fill_preserve();
  cr->restore();  // back to opaque black
  cr->stroke();

  return true;
}

File: main.cc (For use with gtkmm 3, not gtkmm 2)

#include "myarea.h"
#include <gtkmm/application.h>
#include <gtkmm/window.h>

int main(int argc, char** argv)
{
   Glib::RefPtr<Gtk::Application> app = Gtk::Application::create(argc, argv, "org.gtkmm.example");

   Gtk::Window win;
   win.set_title("DrawingArea");

   MyArea area;
   win.add(area);
   area.show();

   return app->run(win);
}

There are a couple of things to note about this example code. Again, the only real difference between this example and the previous ones is the on_draw() function, so we'll limit our focus to that function. In addition, the first part of the function is nearly identical to the previous examples, so we'll skip that portion.
Notez que dans ce cas, nous avons pratiquement tout exprimé en termes de hauteur et de largeur de la fenêtre, y compris la largeur des lignes. En raison de cela, quand vous redimensionnez la fenêtre, tous les objets se mettent à l'échelle. Également, il y a trois parties concernant les tracés dans la fonction et chacune est entourée avec une paire save()/restore() pour revenir à un état bien défini après chaque tracé.
La partie concernant le tracé de l'arc comporte une nouvelle fonction : close_path(). Cette fonction a pour effet de provoquer le tracé d'une droite entre le point de fin et de début du tracé. Il y a toutefois une différence significative entre l'appel de close_path() et le tracé à la main d'une ligne jusqu'au point de départ. Si vous utilisez close_path(), les lignes seront proprement jointes. Si vous utilisez line_to() à la place, les lignes se rejoindront, mais Cairo ne fera rien de spécial pour les joindre.

Tracé en sens inverse des aiguilles d'une montre
La fonction Cairo::Context::arc_negative() est identique à Cairo::Context::arc() mais les angles tournent dans le sens opposé.

Tracé de textes

Tracé de texte avec Pango
Text is drawn via Pango Layouts. The easiest way to create a Pango::Layout is to use Gtk::Widget::create_pango_layout(). Once created, the layout can be manipulated in various ways, including changing the text, font, etc. Finally, the layout can be rendered using the Pango::Layout::show_in_cairo_context() method.

Exemple
Here is an example of a program that draws some text, some of it upside-down. The Printing chapter contains another example of drawing text.

Drawing Area - Text
Source Code
File: myarea.h (For use with gtkmm 3, not gtkmm 2)

#ifndef GTKMM_EXAMPLE_MYAREA_H
#define GTKMM_EXAMPLE_MYAREA_H

#include <gtkmm/drawingarea.h>

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

protected:
  //Override default signal handler:
  virtual bool on_draw(const Cairo::RefPtr<Cairo::Context>& cr);

private:
  void draw_rectangle(const Cairo::RefPtr<Cairo::Context>& cr, int width, int height);
  void draw_text(const Cairo::RefPtr<Cairo::Context>& cr, int rectangle_width, int rectangle_height);

};

#endif // GTKMM_EXAMPLE_MYAREA_H

File: myarea.cc (For use with gtkmm 3, not gtkmm 2)

#include "myarea.h"

MyArea::MyArea()
{
}

MyArea::~MyArea()
{
}

bool MyArea::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();

  const int rectangle_width = width;
  const int rectangle_height = height / 2;

  // Draw a black rectangle
  cr->set_source_rgb(0.0, 0.0, 0.0);
  draw_rectangle(cr, rectangle_width, rectangle_height);

  // and some white text
  cr->set_source_rgb(1.0, 1.0, 1.0);
  draw_text(cr, rectangle_width, rectangle_height);

  // flip the image vertically
  // see http://www.cairographics.org/documentation/cairomm/reference/classCairo_1_1Matrix.html
  // the -1 corresponds to the yy part (the flipping part)
  // the height part is a translation (we could have just called cr->translate(0, height) instead)
  // it's height and not height / 2, since we want this to be on the second part of our drawing
  // (otherwise, it would draw over the previous part)
  Cairo::Matrix matrix(1.0, 0.0, 0.0, -1.0, 0.0, height);

  // apply the matrix
  cr->transform(matrix);

  // white rectangle
  cr->set_source_rgb(1.0, 1.0, 1.0);
  draw_rectangle(cr, rectangle_width, rectangle_height);

  // black text
  cr->set_source_rgb(0.0, 0.0, 0.0);
  draw_text(cr, rectangle_width, rectangle_height);

  return true;
}

void MyArea::draw_rectangle(const Cairo::RefPtr<Cairo::Context>& cr,
                            int width, int height)
{
  cr->rectangle(0, 0, width, height);
  cr->fill();
}

void MyArea::draw_text(const Cairo::RefPtr<Cairo::Context>& cr,
                       int rectangle_width, int rectangle_height)
{
  // http://developer.gnome.org/pangomm/unstable/classPango_1_1FontDescription.html
  Pango::FontDescription font;

  font.set_family("Monospace");
  font.set_weight(Pango::WEIGHT_BOLD);

  // http://developer.gnome.org/pangomm/unstable/classPango_1_1Layout.html
  Glib::RefPtr<Pango::Layout> layout = create_pango_layout("Hi there!");

  layout->set_font_description(font);

  int text_width;
  int text_height;

  //get the text dimensions (it updates the variables -- by reference)
  layout->get_pixel_size(text_width, text_height);

  // Position the text in the middle
  cr->move_to((rectangle_width-text_width)/2, (rectangle_height-text_height)/2);

  layout->show_in_cairo_context(cr);
}

File: main.cc (For use with gtkmm 3, not gtkmm 2)

#include "myarea.h"
#include <gtkmm/application.h>
#include <gtkmm/window.h>

int main(int argc, char* argv[])
{
  Glib::RefPtr<Gtk::Application> app = Gtk::Application::create(argc, argv, "org.gtkmm.example");

  Gtk::Window window;
  window.set_title("Drawing text example");

  MyArea area;
  window.add(area);
  area.show();

  return app->run(window);
}

Affichage d'images
There is a method for drawing from a Gdk::Pixbuf to a Cairo::Context. A Gdk::Pixbuf buffer is a useful wrapper around a collection of pixels, which can be read from files, and manipulated in various ways.
La façon la plus courante pour créer un objet Gdk::Pixbuf est d'utiliser la fonction Gdk::Pixbuf::create_from_file(). Elle peut lire un fichier d'image, comme un fichier .png dans un tampon Gdk::Pixbuf prêt à être rendu.
The Gdk::Pixbuf can be rendered by setting it as the source pattern of the Cairo context with Gdk::Cairo::set_source_pixbuf(). Then draw the image with either Cairo::Context::paint() (to draw the whole image), or Cairo::Context::rectangle() and Cairo::Context::fill() (to fill the specified rectangle). set_source_pixbuf() is not a member of Cairo::Context. It takes a Cairo::Context as its first parameter.
Here is a small bit of code to tie it all together: (Note that usually you wouldn't load the image every time in the draw signal handler! It's just shown here to keep it all together.)

bool MyArea::on_draw(const Cairo::RefPtr<Cairo::Context>& cr)
{
  Glib::RefPtr<Gdk::Pixbuf> image = Gdk::Pixbuf::create_from_file("myimage.png");
  // Draw the image at 110, 90, except for the outermost 10 pixels.
  Gdk::Cairo::set_source_pixbuf(cr, image, 100, 80);
  cr->rectangle(110, 90, image->get_width()-20, image->get_height()-20);
  cr->fill();
  return true;
}

Exemple
Here is an example of a simple program that draws an image.

Drawing Area - Image
Source Code
File: myarea.h (For use with gtkmm 3, not gtkmm 2)

#ifndef GTKMM_EXAMPLE_MYAREA_H
#define GTKMM_EXAMPLE_MYAREA_H

#include <gtkmm/drawingarea.h>
#include <gdkmm/pixbuf.h>

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

protected:
  //Override default signal handler:
  virtual bool on_draw(const Cairo::RefPtr<Cairo::Context>& cr);

  Glib::RefPtr<Gdk::Pixbuf> m_image;
};

#endif // GTKMM_EXAMPLE_MYAREA_H

File: myarea.cc (For use with gtkmm 3, not gtkmm 2)

#include "myarea.h"
#include <cairomm/context.h>
#include <gdkmm/general.h> // set_source_pixbuf()
#include <glibmm/fileutils.h>
#include <iostream>

MyArea::MyArea()
{
  try
  {
    // The fractal image has been created by the XaoS program.
    // http://xaos.sourceforge.net
    m_image = Gdk::Pixbuf::create_from_file("fractal_image.png");
  }
  catch(const Glib::FileError& ex)
  {
    std::cerr << "FileError: " << ex.what() << std::endl;
  }
  catch(const Gdk::PixbufError& ex)
  {
    std::cerr << "PixbufError: " << ex.what() << std::endl;
  }

  // Show at least a quarter of the image.
  if (m_image)
    set_size_request(m_image->get_width()/2, m_image->get_height()/2);
}

MyArea::~MyArea()
{
}

bool MyArea::on_draw(const Cairo::RefPtr<Cairo::Context>& cr)
{
  if (!m_image)
    return false;

  Gtk::Allocation allocation = get_allocation();
  const int width = allocation.get_width();
  const int height = allocation.get_height();

  // Draw the image in the middle of the drawing area, or (if the image is
  // larger than the drawing area) draw the middle part of the image.
  Gdk::Cairo::set_source_pixbuf(cr, m_image,
    (width - m_image->get_width())/2, (height - m_image->get_height())/2);
  cr->paint();

  return true;
}

File: main.cc (For use with gtkmm 3, not gtkmm 2)

#include "myarea.h"
#include <gtkmm/application.h>
#include <gtkmm/window.h>

int main(int argc, char** argv)
{
  Glib::RefPtr<Gtk::Application> app = Gtk::Application::create(argc, argv, "org.gtkmm.example");

  Gtk::Window win;
  win.set_title("DrawingArea");
  win.set_default_size(300, 200);

  MyArea area;
  win.add(area);
  area.show();

  return app->run(win);
}

Exemple d'application : création d'un cadran d'horloge avec Cairo
Maintenant que nous avons vu les fondements du tracé avec Cairo, rassemblons le tout et créons une application simple qui fasse effectivement quelque chose. L'exemple qui suit utilise Cairo pour créer un élément graphique Clock classique. L'horloge dispose d'aiguilles pour les secondes, les minutes et les heures ; elle se met à jour toutes les secondes.
Source Code
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:
  virtual bool on_draw(const Cairo::RefPtr<Cairo::Context>& cr);

  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 );

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

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.
    Glib::RefPtr<Gdk::Window> 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)
{
   Glib::RefPtr<Gtk::Application> 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);
}

As before, almost all of the interesting stuff is done in the draw signal handler on_draw(). Before we dig into the draw signal handler, 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.
Now let's take a look at the code that performs the actual drawing. The first section of on_draw() should be pretty familiar by now. 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.
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.
Après avoir tracé le pourtour, nous parcourons la circonférence du cadran et traçons des marques pour chaque heure, avec une marque plus importante pour midi, trois, six et neuf heures. Nous sommes maintenant prêts pour implémenter la fonctionnalité horloge de notre cadran : cela consiste à obtenir la valeur actuelle des heures, minutes et secondes et à tracer les aiguilles avec les angles adéquats.