Conteneurs multi-éléments

Les éléments graphiques conteneurs multi-éléments héritent de la classe Gtk::Container ; comme avec Gtk::Bin, vous utilisez les fonctions membres add() et remove() pour ajouter ou supprimer des éléments graphiques du conteneur. Mais, contrairement à Gtk::Bin::remove(), la fonction membre remove() de la classe Gtk::Container exige un paramètre désignant l'élément graphique à enlever.

VIII.II.I. Empaquetage

Vous avez probablement remarqué que les fenêtres gtkmm semblent « élastiques » — elles peuvent être étirées de diverses manières. Cette propriété résulte du système « d'empaquetage des éléments graphiques ».

Beaucoup de boîtes à outils GUI exigent que les éléments graphiques soient placés avec précision dans une fenêtre, avec un positionnement absolu, souvent en utilisant un utilitaire visuel. Plusieurs problèmes en découlent :

  • Les éléments graphiques ne se réarrangent pas par eux-mêmes quand la fenêtre est redimensionnée. Certains éléments graphiques sont masqués quand la fenêtre est rapetissée et des espaces libres inutiles apparaissent quand la fenêtre est agrandie.
  • Il n'est pas possible de prévoir l'espace nécessaire à un texte après traduction en d'autres langues ou après affichage avec une police différente. Sur Unix, il est également impossible d'anticiper les effets de chaque thème ou de chaque gestionnaire de fenêtres.
  • Modifier la disposition d'une fenêtre « à la volée », pour faire apparaître quelques éléments graphiques supplémentaires, par exemple, est compliqué. Cela demande un nouveau calcul ennuyeux de la position de chaque élément graphique.

gtkmm utilise un système d'empaquetage pour résoudre ces problèmes. Plutôt que de définir la position et la taille de chaque élément graphique dans la fenêtre, vous pouvez les disposer en rangées, en colonnes ou en tableaux. gtkmm dimensionne votre fenêtre automatiquement, selon la taille des éléments graphiques qu'elle contient. Les tailles des éléments graphiques sont, à leur tour, déterminées par la quantité de texte contenue ou bien par les tailles minimales ou maximales fixées de même que par les règles voulues pour le partage des espaces libres entre les jeux d'éléments graphiques. Vous pouvez parfaire la disposition en définissant les dimensions des remplissages et des valeurs de centrage pour chacun des éléments graphiques. gtkmm utilise ces informations pour redimensionner et repositionner chaque élément intelligemment et souplement quand l'utilisateur manipule la fenêtre.

gtkmm dispose les éléments graphiques de manière hiérarchique en utilisant des conteneurs. Un élément graphique conteneur contient d'autres éléments graphiques. La plupart des éléments graphiques gtkmm sont des conteneurs. Fenêtres, onglets de bloc-notes et boutons sont tous des éléments graphiques conteneurs. Il existe deux sortes de conteneurs : les conteneurs mono-enfant, qui dérivent tous de la classe Gtk::Bin et les conteneurs multi-enfants qui héritent de la classe Gtk::Container. La plupart des éléments graphiques de gtkmm dérivent de la classe Gtk::Bin, y compris Gtk::Window.

Yes, that's correct: a Window can contain at most one widget. How, then, can we use a window for anything useful? By placing a multiple-child container in the window. The most useful container widgets are Gtk::Grid and Gtk::Box.

  • Gtk::Grid disposent respectivement leurs éléments graphiques enfants en lignes et en colonnes. Utilisez attach(), attach_next_to() et add() pour insérer des éléments graphiques enfants.
  • Gtk::Box arranges its child widgets vertically or horizontally. Use pack_start() and pack_end() to insert child widgets.

Il existe plusieurs autres conteneurs. Nous en parlerons également.

Si vous n'avez jamais utilisé auparavant un utilitaire d'empaquetage, son utilisation nécessitera une prise en main. Toutefois, vous constaterez probablement que, contrairement à d'autres boîtes à outils, il n'y a pas besoin de faire appel à un éditeur de type visuel.

VIII.II.II. Un Hello World amélioré

Regardons un helloworld légèrement perfectionné en se servant de nos nouvelles connaissances.

Figure VIII.6 Hello World 2

Code source

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

#ifndef GTKMM_EXAMPLE_HELLOWORLD_H
#define GTKMM_EXAMPLE_HELLOWORLD_H

#include <gtkmm/box.h>
#include <gtkmm/button.h>
#include <gtkmm/window.h>

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

protected:

  // Signal handlers:
  // Our new improved on_button_clicked(). (see below)
  void on_button_clicked(Glib::ustring data);

  // Child widgets:
  Gtk::Box m_box1;
  Gtk::Button m_button1, m_button2;
};

#endif // GTKMM_EXAMPLE_HELLOWORLD_H

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

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

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

  HelloWorld helloworld;

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

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

#include "helloworld.h"
#include <iostream>

HelloWorld::HelloWorld()
: m_button1("Button 1"),
  m_button2("Button 2")
{
  // This just sets the title of our new window.
  set_title("Hello Buttons!");

  // sets the border width of the window.
  set_border_width(10);

  // put the box into the main window.
  add(m_box1);

  // Now when the button is clicked, we call the "on_button_clicked" function
  // with a pointer to "button 1" as it's argument
  m_button1.signal_clicked().connect(sigc::bind<Glib::ustring>(
              sigc::mem_fun(*this, &HelloWorld::on_button_clicked), "button 1"));

  // instead of gtk_container_add, we pack this button into the invisible
  // box, which has been packed into the window.
  // note that the pack_start default arguments are Gtk::EXPAND | Gtk::FILL, 0
  m_box1.pack_start(m_button1);

  // always remember this step, this tells GTK that our preparation
  // for this button is complete, and it can be displayed now.
  m_button1.show();

  // call the same signal handler with a different argument,
  // passing a pointer to "button 2" instead.
  m_button2.signal_clicked().connect(sigc::bind<-1, Glib::ustring>(
              sigc::mem_fun(*this, &HelloWorld::on_button_clicked), "button 2"));

  m_box1.pack_start(m_button2);

  // Show the widgets.
  // They will not really be shown until this Window is shown.
  m_button2.show();
  m_box1.show();
}

HelloWorld::~HelloWorld()
{
}

// Our new improved signal handler.  The data passed to this method is
// printed to stdout.
void HelloWorld::on_button_clicked(Glib::ustring data)
{
  std::cout << "Hello World - " << data << " was pressed" << std::endl;
}

Après avoir construit et lancé ce programme, essayez de redimensionner la fenêtre pour voir son comportement. Également, essayez de jouer avec les options de la fonction membre pack_start() après avoir lu le paragraphe relatif aux boîtes.

VIII.II.III. Boîtes

Beaucoup d'empaquetages utilisent des boîtes comme dans l'exemple précédent. Ce sont des conteneurs invisibles dans lesquels nous pouvons placer les éléments graphiques. Si nous empaquetons dans une boîte horizontale, les objets seront insérés horizontalement de la gauche vers la droite ou inversement selon que l'on utilise pack_start() ou pack_end(). Dans une boîte verticale, les éléments graphiques sont empaquetés du haut vers le bas ou vice versa. Vous pouvez utiliser toute combinaison de boîtes dans ou à côté d'autres boîtes pour créer les effets voulus.

VIII.II.III.I. Ajout d'éléments graphiques

VIII.II.III.I.I. Options d'empaquetage par enfant

The pack_start() and pack_end() methods place widgets inside these containers. The pack_start() method will start at the top and work its way down in a Box with vertical orientation, or pack left to right in a Box with horizontal orientation. pack_end() will do the opposite, packing from bottom to top or from right to left. Using these methods allows us to right justify or left justify our widgets. We will use pack_start() in most of our examples.

Il y a plusieurs options régissant la manière dont les éléments graphiques doivent être empaquetés ; cela peut sembler confus au début. Si vous avez des difficultés, nous vous suggérons l'idée de manipuler le concepteur GUI glade pour voir ce qui est possible. Vous pourrez même décider d'utiliser l'API Gtk::Builder pour charger les interfaces utilisateur au lancement du programme.

À la base, il y cinq styles différents comme illustré dans cette figure :

Figure VIII.7 Empaquetage dans boîte n°1

Each line contains one horizontal Box with several buttons. Each of the buttons on a line is packed into the Box with the same arguments to the pack_start() method.

Voici la déclaration de la fonction membre pack_start() :

void pack_start(Gtk::Widget& child,
                PackOptions options = PACK_EXPAND_WIDGET,
                guint padding = 0);

Le premier paramètre désigne l'élément graphique à empaquer. Dans notre exemple, ce sont tous des objets Button.

Le paramètre options peut prendre une des trois valeurs suivantes :

  • PACK_SHRINK : l'espace est condensé à la taille des éléments graphiques enfants. L'élément graphique prend la place qui lui est juste nécessaire et n'est jamais étiré.
  • PACK_EXPAND_PADDING : l'espace excédentaire est constitué de remplissage. Les éléments graphiques sont régulièrement espacés, mais leurs tailles ne varient pas — des espaces vides sont ménagés entre éléments graphiques.
  • PACK_EXPAND_WIDGET : l'espace excédentaire est occupé par une augmentation de la taille des éléments graphiques enfants, sans modifier la répartition de l'espacement entre éléments graphiques.

Le paramètre padding définit la largeur de la bordure libre autour de l'élément graphique empaqueté.

Référence

VIII.II.III.I.II. Options d'empaquetage par conteneur

Voici le constructeur des éléments graphiques de la boîte :

Gtk::Box(bool homogeneous = false, int spacing = 0);
Si le paramètre homogeneous est défini à true, tous les éléments graphiques de la boîte auront la même taille. spacing représente le nombre de pixels (minimum) à laisser entre chaque élément graphique.

Quelle est la différence entre espacement (déterminé quand la boîte est crée) et remplissage (défini quand les éléments sont empaquetés) ? L'espacement est inséré entre les objets alors que le remplissage est ajouté sur les côtés du élément graphique. L'illustration ci-après clarifie cela :

Figure VIII.8 Empaquetage dans boîte n°2

VIII.II.III.II. Exemple

Voici le code source de l'exemple qui a servi à générer la capture d'écran ci-dessus. Quand vous lancez cet exemple, indiquez un nombre entre 1 et 3 en option de ligne de commande pour voir les diverses options d'empaquetage utilisées.

Code source

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

#ifndef GTKMM_EXAMPLE_PACKBOX_H
#define GTKMM_EXAMPLE_PACKBOX_H

#include <gtkmm.h>

class PackBox : public Gtk::Box
{
public:
  PackBox(bool homogeneous, int spacing, Gtk::PackOptions, int padding = 0);
  virtual ~PackBox();

protected:
  Gtk::Button m_button1, m_button2, m_button3;
  Gtk::Button* m_pbutton4;

  char padstr[80];
};

#endif //GTKMM_EXAMPLE_PACKBOX_H

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

#ifndef GTKMM_EXAMPLEWINDOW_H
#define GTKMM_EXAMPLEWINDOW_H

#include <gtkmm.h>
#include "packbox.h"

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

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

  //Child widgets:
  Gtk::Button m_button;
  Gtk::Box m_box1;
  Gtk::Box m_boxQuit;
  Gtk::Button m_buttonQuit;

  Gtk::Label m_Label1, m_Label2;

  Gtk::Separator m_seperator1, m_seperator2, m_seperator3, m_seperator4, m_seperator5;
};

#endif //GTKMM_EXAMPLEWINDOW_H

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

#include <iostream>
#include "examplewindow.h"

ExampleWindow::ExampleWindow(int which)
: m_box1(Gtk::ORIENTATION_VERTICAL),
  m_buttonQuit("Quit")
{
  set_title("Gtk::Box example");

  PackBox *pPackBox1, *pPackBox2, *pPackBox3, *pPackBox4, *pPackBox5;

  switch(which)
  {
    case 1:
    {
      m_Label1.set_text("Gtk::Box(Gtk::ORIENTATION_HORIZONTAL, 0);");

      // Align the label to the left side.  We'll discuss this function and
      // others in the section on Widget Attributes.
      m_Label1.set_alignment(Gtk::ALIGN_START, Gtk::ALIGN_START);

      // Pack the label into the vertical box (vbox box1).  Remember that
      // widgets added to a vbox will be packed one on top of the other in
      // order.
      m_box1.pack_start(m_Label1, Gtk::PACK_SHRINK);

      // Create a PackBox - homogeneous = false, spacing = 0,
      // options = Gtk::PACK_SHRINK, padding = 0
      pPackBox1 = Gtk::manage(new PackBox(false, 0, Gtk::PACK_SHRINK));
      m_box1.pack_start(*pPackBox1, Gtk::PACK_SHRINK);

      // Create a PackBox - homogeneous = false, spacing = 0,
      // options = Gtk::PACK_EXPAND_PADDING, padding = 0
      pPackBox2 = Gtk::manage(new PackBox(false, 0, Gtk::PACK_EXPAND_PADDING));
      m_box1.pack_start(*pPackBox2, Gtk::PACK_SHRINK);

      // Create a PackBox - homogeneous = false, spacing = 0,
      // options = Gtk::PACK_EXPAND_WIDGET, padding = 0
      pPackBox3 = Gtk::manage(new PackBox(false, 0, Gtk::PACK_EXPAND_WIDGET));
      m_box1.pack_start(*pPackBox3, Gtk::PACK_SHRINK);

      // pack the separator into the vbox.  Remember each of these
      // widgets are being packed into a vbox, so they'll be stacked
      // vertically.
      m_box1.pack_start(m_seperator1, Gtk::PACK_SHRINK, 5);

      // create another new label, and show it.
      m_Label2.set_text("Gtk::Box(Gtk::ORIENTATION_HORIZONTAL, 0);");
      m_Label2.set_alignment(Gtk::ALIGN_START, Gtk::ALIGN_START);
      m_box1.pack_start(m_Label2, Gtk::PACK_SHRINK);

      // Args are: homogeneous, spacing, options, padding
      pPackBox4 = Gtk::manage(new PackBox(true, 0, Gtk::PACK_EXPAND_PADDING));
      m_box1.pack_start(*pPackBox4, Gtk::PACK_SHRINK);

      // Args are: homogeneous, spacing, options, padding
      pPackBox5 = Gtk::manage(new PackBox(true, 0, Gtk::PACK_EXPAND_WIDGET));
      m_box1.pack_start(*pPackBox5, Gtk::PACK_SHRINK);

      m_box1.pack_start(m_seperator2, Gtk::PACK_SHRINK, 5);

      break;
    }

    case 2:
    {

      m_Label1.set_text("Gtk::Box(Gtk::ORIENTATION_HORIZONTAL, 10);");
      m_Label1.set_alignment(Gtk::ALIGN_START, Gtk::ALIGN_START);
      m_box1.pack_start(m_Label1, Gtk::PACK_SHRINK);

      pPackBox1 = Gtk::manage(new PackBox(false, 10, Gtk::PACK_EXPAND_PADDING));
      m_box1.pack_start(*pPackBox1, Gtk::PACK_SHRINK);

      pPackBox2 = Gtk::manage(new PackBox(false, 10, Gtk::PACK_EXPAND_WIDGET));
      m_box1.pack_start(*pPackBox2, Gtk::PACK_SHRINK);

      m_box1.pack_start(m_seperator1, Gtk::PACK_SHRINK, 5);


      m_Label2.set_text("Gtk::Box(Gtk::ORIENTATION_HORIZONTAL, 0);");
      m_Label2.set_alignment(Gtk::ALIGN_START, Gtk::ALIGN_START);
      m_box1.pack_start(m_Label2, Gtk::PACK_SHRINK);

      pPackBox3 = Gtk::manage(new PackBox(false, 0, Gtk::PACK_SHRINK, 10));
      m_box1.pack_start(*pPackBox3, Gtk::PACK_SHRINK);

      pPackBox4 = Gtk::manage(new PackBox(false, 0, Gtk::PACK_EXPAND_WIDGET, 10));
      m_box1.pack_start(*pPackBox4, Gtk::PACK_SHRINK);

      m_box1.pack_start(m_seperator2, Gtk::PACK_SHRINK, 5);

      break;
    }

    case 3:
    {
      // This demonstrates the ability to use Gtk::Box::pack_end() to
      // right justify widgets.  First, we create a new box as before.
      pPackBox1 = Gtk::manage(new PackBox(false, 0, Gtk::PACK_SHRINK));

      // create the label that will be put at the end.
      m_Label1.set_text("end");

      // pack it using pack_end(), so it is put on the right side
      // of the PackBox.
      pPackBox1->pack_end(m_Label1, Gtk::PACK_SHRINK);

      m_box1.pack_start(*pPackBox1, Gtk::PACK_SHRINK);

      // this explicitly sets the separator to 400 pixels wide by 5 pixels
      // high.  This is so the hbox we created will also be 400 pixels wide,
      // and the "end" label will be separated from the other labels in the
      // hbox.  Otherwise, all the widgets in the hbox would be packed as
      // close together as possible.
      m_seperator1.set_size_request(400, 5);

      // pack the separator into ourselves
      m_box1.pack_start(m_seperator1, Gtk::PACK_SHRINK, 5);

      break;
    }

    default:
    {
      std::cerr << "Unexpected command-line option." << std::endl;
      break;
    }
  }

  // Connect the signal to hide the window:
  m_buttonQuit.signal_clicked().connect( sigc::mem_fun(*this,
              &ExampleWindow::on_button_quit_clicked) );

  // pack the button into the quitbox.
  // The last 2 arguments to Box::pack_start are: options, padding.
  m_boxQuit.pack_start(m_buttonQuit, Gtk::PACK_EXPAND_PADDING);
  m_box1.pack_start(m_boxQuit, Gtk::PACK_SHRINK);

  // pack the vbox (box1) which now contains all our widgets, into the
  // main window.
  add(m_box1);

  show_all_children();
}

ExampleWindow::~ExampleWindow()
{
}

void ExampleWindow::on_button_quit_clicked()
{
  hide();
}

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

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

using std::atoi;

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

  if(argc != 2)
  {
    std::cerr << "usage: packbox num, where num is 1, 2, or 3." << std::endl;
    return 1;
  }

  ExampleWindow window( atoi(argv[1]) );
  return app->run(window); //Shows the window and returns when it is closed.
}

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

#include "packbox.h"
#include <cstdio> //For sprintf().

PackBox::PackBox(bool homogeneous, int spacing, Gtk::PackOptions options,
        int padding)
: Gtk::Box(Gtk::ORIENTATION_HORIZONTAL, spacing),
  m_button1("box.pack_start("),
  m_button2("button,"),
  m_button3((options == Gtk::PACK_SHRINK) ? "Gtk::PACK_SHRINK" :
            ((options == Gtk::PACK_EXPAND_PADDING) ?
             "Gtk::PACK_EXPAND_PADDING" : "Gtk::PACK_EXPAND_WIDGET"))
{
  set_homogeneous(homogeneous);

  pack_start(m_button1, options, padding);
  pack_start(m_button2, options, padding);
  pack_start(m_button3, options, padding);

  sprintf(padstr, "%d);", padding);

  m_pbutton4 = new Gtk::Button(padstr);
  pack_start(*m_pbutton4, options, padding);
}

PackBox::~PackBox()
{
  delete m_pbutton4;
}

VIII.II.IV. Boîtes à boutons

Button boxes are a convenient way to quickly arrange a group of buttons. Their orientation can be either horizontal or vertical.

Les ButtonBox donnent une apparence homogène aux applications en utilisant des réglages normalisés pour l'espacement entre boutons et pour l'empaquetage.

Les boutons sont insérés dans l'objet ButtonBox à l'aide de la fonction membre add().

Les boîtes à boutons prennent en charge divers styles de dispositions. Le style peut être obtenu et modifié avec les fonctions membres get_layout() et set_layout().

Référence

VIII.II.IV.I. Exemple

Figure VIII.9 Boîte à bouton

Code source

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

#ifndef GTKMM_EXAMPLE_BUTTONBOX_H
#define GTKMM_EXAMPLE_BUTTONBOX_H

#include <gtkmm.h>

class ExampleButtonBox : public Gtk::Frame
{
public:
  ExampleButtonBox(bool horizontal,
       const Glib::ustring& title,
       gint spacing,
       Gtk::ButtonBoxStyle layout);

protected:
  Gtk::Button m_Button_OK, m_Button_Cancel, m_Button_Help;
};

#endif //GTKMM_EXAMPLE_BUTTONBOX_H

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

#ifndef GTKMM_EXAMPLEWINDOW_H
#define GTKMM_EXAMPLEWINDOW_H

#include <gtkmm.h>

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

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

  //Child widgets:
  Gtk::Box m_VBox_Main, m_VBox;
  Gtk::Box m_HBox;
  Gtk::Frame m_Frame_Horizontal, m_Frame_Vertical;
};

#endif //GTKMM_EXAMPLEWINDOW_H

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

#include "examplebuttonbox.h"

ExampleButtonBox::ExampleButtonBox(bool horizontal,
       const Glib::ustring& title,
       gint spacing,
       Gtk::ButtonBoxStyle layout)
: Gtk::Frame(title),
  m_Button_OK("OK"),
  m_Button_Cancel("Cancel"),
  m_Button_Help("Help")
{
  Gtk::ButtonBox* bbox = 0;

  if(horizontal)
    bbox = Gtk::manage( new Gtk::ButtonBox(Gtk::ORIENTATION_HORIZONTAL) );
  else
    bbox = Gtk::manage( new Gtk::ButtonBox(Gtk::ORIENTATION_VERTICAL) );

  bbox->set_border_width(5);

  add(*bbox);

  /* Set the appearance of the Button Box */
  bbox->set_layout(layout);
  bbox->set_spacing(spacing);

  bbox->add(m_Button_OK);
  bbox->add(m_Button_Cancel);
  bbox->add(m_Button_Help);
}

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

#include "examplewindow.h"
#include "examplebuttonbox.h"

ExampleWindow::ExampleWindow()
: m_VBox_Main(Gtk::ORIENTATION_VERTICAL),
  m_VBox(Gtk::ORIENTATION_VERTICAL),
  m_Frame_Horizontal("Horizontal Button Boxes"),
  m_Frame_Vertical("Vertical Button Boxes")
{
  set_title("Gtk::ButtonBox");
  add(m_VBox_Main);

  m_VBox_Main.pack_start(m_Frame_Horizontal, Gtk::PACK_EXPAND_WIDGET, 10);

  //The horizontal ButtonBoxes:
  m_VBox.set_border_width(10);
  m_Frame_Horizontal.add(m_VBox);

  m_VBox.pack_start(*Gtk::manage(
              new ExampleButtonBox(true, "Spread (spacing 40)", 40,
                  Gtk::BUTTONBOX_SPREAD)),
          Gtk::PACK_EXPAND_WIDGET, 0);

  m_VBox.pack_start(*Gtk::manage(
              new ExampleButtonBox(true, "Edge (spacing 30)", 30,
                  Gtk::BUTTONBOX_EDGE)),
          Gtk::PACK_EXPAND_WIDGET, 5);

  m_VBox.pack_start(*Gtk::manage(
              new ExampleButtonBox(true, "Start (spacing 20)", 20,
                  Gtk::BUTTONBOX_START)),
          Gtk::PACK_EXPAND_WIDGET, 5);

  m_VBox.pack_start(*Gtk::manage(
              new ExampleButtonBox(true, "end (spacing 10)", 10,
                  Gtk::BUTTONBOX_END)),
          Gtk::PACK_EXPAND_WIDGET, 5);


  //The vertical ButtonBoxes:
  m_VBox_Main.pack_start(m_Frame_Vertical, Gtk::PACK_EXPAND_WIDGET, 10);

  m_HBox.set_border_width(10);
  m_Frame_Vertical.add(m_HBox);

  m_HBox.pack_start(*Gtk::manage(
              new ExampleButtonBox(false, "Spread (spacing 5)", 5,
                  Gtk::BUTTONBOX_SPREAD)),
          Gtk::PACK_EXPAND_WIDGET, 0);

  m_HBox.pack_start(*Gtk::manage(
              new ExampleButtonBox(false, "Edge (spacing 30)", 30,
                  Gtk::BUTTONBOX_EDGE)),
          Gtk::PACK_EXPAND_WIDGET, 5);

  m_HBox.pack_start(*Gtk::manage(
              new ExampleButtonBox(false, "Start (spacing 20)", 20,
                  Gtk::BUTTONBOX_START)),
          Gtk::PACK_EXPAND_WIDGET, 5);

  m_HBox.pack_start(*Gtk::manage(new ExampleButtonBox(false, "End (spacing 10)",
                  10, Gtk::BUTTONBOX_END)),
          Gtk::PACK_EXPAND_WIDGET, 5);

  show_all_children();
}

ExampleWindow::~ExampleWindow()
{
}

void ExampleWindow::on_button_clicked()
{
  hide();
}

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

VIII.II.V. Grille

A Grid dynamically lays out child widgets in rows and columns. The dimensions of the grid do not need to be specified in the constructor.

Child widgets can span multiple rows or columns, using attach(), or added next to an existing widget inside the grid with attach_next_to(). Individual rows and columns of the grid can be set to have uniform height or width with set_row_homogeneous() and set_column_homogeneous().

You can set the margin and expand properties of the child Widgets to control their spacing and their behaviour when the Grid is resized.

Référence

VIII.II.V.I. Exemple

This example creates a window with three buttons in a grid. The first two buttons are in the upper row, from left to right. A third button is attached underneath the first button, in a new lower row, spanning two columns.

Figure VIII.10 Grille

Code source

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

#ifndef GTKMM_EXAMPLEWINDOW_H
#define GTKMM_EXAMPLEWINDOW_H

#include <gtkmm.h>

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

private:
  // Signal handlers:
  void on_button_quit();
  void on_button_numbered(const Glib::ustring& data);

  // Child widgets:
  Gtk::Grid m_grid;
  Gtk::Button m_button_1, m_button_2, m_button_quit;
};

#endif /* GTKMM_EXAMPLEWINDOW_H */

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

#include <iostream>
#include "examplewindow.h"

ExampleWindow::ExampleWindow()
: m_button_1("button 1"),
  m_button_2("button 2"),
  m_button_quit("Quit")
{
  set_title("Gtk::Grid");
  set_border_width(12);

  add(m_grid);

  m_grid.add(m_button_1);
  m_grid.add(m_button_2);
  m_grid.attach_next_to(m_button_quit, m_button_1, Gtk::POS_BOTTOM, 2, 1);

  m_button_1.signal_clicked().connect(
    sigc::bind<Glib::ustring>( sigc::mem_fun(*this,
      &ExampleWindow::on_button_numbered), "button 1") );
  m_button_2.signal_clicked().connect(
    sigc::bind<Glib::ustring>( sigc::mem_fun(*this,
      &ExampleWindow::on_button_numbered), "button 2") );

  m_button_quit.signal_clicked().connect(sigc::mem_fun(*this,
    &ExampleWindow::on_button_quit) );

  show_all_children();
}

ExampleWindow::~ExampleWindow()
{
}

void ExampleWindow::on_button_quit()
{
  hide();
}

void
ExampleWindow::on_button_numbered(const Glib::ustring& data)
{
  std::cout << data << " was pressed" << std::endl;
}

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

VIII.II.VI. Tableau

Gtk::Table allows us to place widgets in a grid, similar to Gtk::Grid.

Gtk::Table is deprecated from gtkmm version 3.4 and should not be used in newly-written code. Use Gtk::Grid instead.

VIII.II.VII. Bloc-note

L'objet Notebook est constitué d'un jeu de pages empilées, chacune contenant des éléments graphiques. Les onglets étiquetés permettent à l'utilisateur de choisir les pages. Notebook permet de placer plusieurs jeux d'éléments graphiques dans un espace réduit, mais en ne montrant qu'une seule page à la fois. Cet objet est souvent utilisé, par exemple, dans les boîtes de dialogue de préférences.

Servez-vous des fonctions membres append_page(), prepend_page() et insert_page() pour ajouter des pages à onglets dans l'objet Notebook en précisant l'élément graphique enfant et le nom de l'onglet.

Pour connaître la page actuellement affichée, utilisez la fonction membre get_current_page(). Elle renvoie un numéro de page, puis, en appelant get_nth_page() avec ce numéro de page, vous obtiendrez un pointeur sur l'élément graphique enfant de la page.

Pour changer de page sélectionnée au cours du programme, utilisez la fonction membre set_current_page().

Référence

VIII.II.VII.I. Exemple

Figure VIII.11 Bloc-note

Code source

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

#ifndef GTKMM_EXAMPLEWINDOW_H
#define GTKMM_EXAMPLEWINDOW_H

#include <gtkmm.h>

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

protected:
  //Signal handlers:
  void on_button_quit();
  void on_notebook_switch_page(Gtk::Widget* page, guint page_num);

  //Child widgets:
  Gtk::Box m_VBox;
  Gtk::Notebook m_Notebook;
  Gtk::Label m_Label1, m_Label2;

  Gtk::ButtonBox m_ButtonBox;
  Gtk::Button m_Button_Quit;
};

#endif //GTKMM_EXAMPLEWINDOW_H

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

#include <iostream>
#include "examplewindow.h"

ExampleWindow::ExampleWindow()
: m_VBox(Gtk::ORIENTATION_VERTICAL),
  m_Label1("Contents of tab 1"),
  m_Label2("Contents of tab 2"),
  m_Button_Quit("Quit")
{
  set_title("Gtk::Notebook example");
  set_border_width(10);
  set_default_size(400, 200);


  add(m_VBox);

  //Add the Notebook, with the button underneath:
  m_Notebook.set_border_width(10);
  m_VBox.pack_start(m_Notebook);
  m_VBox.pack_start(m_ButtonBox, Gtk::PACK_SHRINK);

  m_ButtonBox.pack_start(m_Button_Quit, Gtk::PACK_SHRINK);
  m_Button_Quit.signal_clicked().connect(sigc::mem_fun(*this,
              &ExampleWindow::on_button_quit) );

  //Add the Notebook pages:
  m_Notebook.append_page(m_Label1, "First");
  m_Notebook.append_page(m_Label2, "Second");

  m_Notebook.signal_switch_page().connect(sigc::mem_fun(*this,
              &ExampleWindow::on_notebook_switch_page) );

  show_all_children();
}

ExampleWindow::~ExampleWindow()
{
}

void ExampleWindow::on_button_quit()
{
  hide();
}

void ExampleWindow::on_notebook_switch_page(Gtk::Widget* /* page */, guint page_num)
{
  std::cout << "Switched to tab with index " << page_num << std::endl;

  //You can also use m_Notebook.get_current_page() to get this index.
}

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

VIII.II.VIII. Assistant

Un objet Assistant divise une opération complexe en étapes successives. À chaque étape correspond une page, avec un en-tête, un élément graphique enfant et une zone d'action. La zone d'action de l'assistant possède des boutons de navigation qui se mettent à jour automatiquement selon le type de page fixé avec set_page_type().

Utilisez les fonctions membres append_page(), prepend_page et insert_page() pour ajouter des pages à un objet Assistant, tout en indiquant l'élément graphique enfant pour chacune d'entre elles.

Pour connaître la page en cours d'utilisation, servez-vous de la fonction membre get_current_page(), passez le résultat à get_nth_page() qui vous renverra un pointeur sur l'élément graphique affiché. Pour changer en cours de programme la page active, utilisez la fonction membre set_current_page().

Pour définir le titre de la page, servez-vous de la fonction membre set_page_title(). Les images de l'en-tête et du côté de la page sont insérées avec les fonctions membres set_page_header_image() et set_page_side_image().

Pour ajouter des éléments graphiques dans la zone d'action, utilisez la fonction membre add_action_widget(). Ils seront empaquetés à côté des boutons par défaut. Servez-vous de la fonction membre remove_action_widget() pour enlever des éléments graphiques.

Référence

VIII.II.VIII.I. Exemple

Figure VIII.12 Assistant

Code source

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

#ifndef GTKMM_EXAMPLEASSISTANT_H
#define GTKMM_EXAMPLEASSISTANT_H

#include <gtkmm.h>

class ExampleAssistant : public Gtk::Assistant
{
public:
  ExampleAssistant();
  virtual ~ExampleAssistant();

  void get_result(bool& check_state, Glib::ustring& entry_text);

private:
  // Signal handlers:
  void on_assistant_apply();
  void on_assistant_cancel();
  void on_assistant_close();
  void on_assistant_prepare(Gtk::Widget* widget);
  void on_entry_changed();

  // Member functions:
  void print_status();

  // Child widgets:
  Gtk::Box m_box;
  Gtk::Label m_label1, m_label2;
  Gtk::CheckButton m_check;
  Gtk::Entry m_entry;
};

#endif /* GTKMM_EXAMPLEASSISTANT_H */

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

#ifndef GTKMM_EXAMPLEWINDOW_H
#define GTKMM_EXAMPLEWINDOW_H

#include "exampleassistant.h"
#include <gtkmm.h>

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

private:
  // Signal handlers:
  void on_button_clicked();
  void on_assistant_apply();

  // Child widgets:
  Gtk::Grid m_grid;
  Gtk::Button m_button;
  Gtk::Label m_label1, m_label2;
  Gtk::CheckButton m_check;
  Gtk::Entry m_entry;
  ExampleAssistant m_assistant;
};

#endif /* GTKMM_EXAMPLEWINDOW_H */

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

#include "examplewindow.h"
#include "exampleassistant.h"

ExampleWindow::ExampleWindow()
: m_button("Show the assistant"),
  m_label1("State of assistant checkbutton:"),
  m_label2("Contents of assistant entry:")
{
  set_title("Gtk::Assistant example");
  set_border_width(12);

  m_grid.set_row_homogeneous(true);

  m_grid.attach(m_button, 0, 0, 2, 1);
  m_button.set_hexpand(true);
  m_button.set_valign(Gtk::ALIGN_CENTER);

  m_grid.attach(m_label1, 0, 1, 1, 1);
  m_label1.set_alignment(0.0, 0.5);

  m_grid.attach(m_label2, 0, 2, 1, 1);
  m_label2.set_alignment(0.0, 0.5);

  m_grid.attach(m_check, 1, 1, 1, 1);
  m_check.set_halign(Gtk::ALIGN_START);

  m_grid.attach(m_entry, 1, 2, 1, 1);
  m_entry.set_hexpand(true);

  add(m_grid);

  m_button.signal_clicked().connect(sigc::mem_fun(*this,
    &ExampleWindow::on_button_clicked));
  m_assistant.signal_apply().connect(sigc::mem_fun(*this,
    &ExampleWindow::on_assistant_apply));

  m_check.set_sensitive(false);
  m_entry.set_sensitive(false);

  show_all_children();
}

ExampleWindow::~ExampleWindow()
{
}

void ExampleWindow::on_assistant_apply()
{
  bool check_state;
  Glib::ustring entry_text;

  m_assistant.get_result(check_state, entry_text);
  m_check.set_active(check_state);
  m_entry.set_text(entry_text);
}

void ExampleWindow::on_button_clicked()
{
  m_assistant.show();
}

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

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

#include <iostream>
#include "exampleassistant.h"

ExampleAssistant::ExampleAssistant()
: m_box(Gtk::ORIENTATION_HORIZONTAL, 12),
  m_label1("Type text to allow the assistant to continue:"),
  m_label2("Confirmation page"),
  m_check("Optional extra information")
{
  set_title("Gtk::Assistant example");
  set_border_width(12);
  set_default_size(400, 300);

  m_box.pack_start(m_label1);
  m_box.pack_start(m_entry);

  append_page(m_box);
  append_page(m_check);
  append_page(m_label2);

  set_page_title(*get_nth_page(0), "Page 1");
  set_page_title(*get_nth_page(1), "Page 2");
  set_page_title(*get_nth_page(2), "Confirmation");

  set_page_complete(m_check, true);
  set_page_complete(m_label2, true);

  set_page_type(m_box, Gtk::ASSISTANT_PAGE_INTRO);
  set_page_type(m_label2, Gtk::ASSISTANT_PAGE_CONFIRM);

  signal_apply().connect(sigc::mem_fun(*this,
    &ExampleAssistant::on_assistant_apply));
  signal_cancel().connect(sigc::mem_fun(*this,
    &ExampleAssistant::on_assistant_cancel));
  signal_close().connect(sigc::mem_fun(*this,
    &ExampleAssistant::on_assistant_close));
  signal_prepare().connect(sigc::mem_fun(*this,
    &ExampleAssistant::on_assistant_prepare));

  m_entry.signal_changed().connect(sigc::mem_fun(*this,
    &ExampleAssistant::on_entry_changed));

  show_all_children();
}

ExampleAssistant::~ExampleAssistant()
{
}

void ExampleAssistant::get_result(bool& check_state, Glib::ustring& entry_text)
{
  check_state = m_check.get_active();
  entry_text = m_entry.get_text();
}

void ExampleAssistant::on_assistant_apply()
{
  std::cout << "Apply was clicked";
  print_status();
}

void ExampleAssistant::on_assistant_cancel()
{
  std::cout << "Cancel was clicked";
  print_status();
  hide();
}

void ExampleAssistant::on_assistant_close()
{
  std::cout << "Assistant was closed";
  print_status();
  hide();
}

void ExampleAssistant::on_assistant_prepare(Gtk::Widget* /* widget */)
{
  set_title(Glib::ustring::compose("Gtk::Assistant example (Page %1 of %2)",
    get_current_page() + 1, get_n_pages()));
}

void ExampleAssistant::on_entry_changed()
{
  // The page is only complete if the entry contains text.
  if(m_entry.get_text_length())
    set_page_complete(m_box, true);
  else
    set_page_complete(m_box, false);
}

void ExampleAssistant::print_status()
{
  std::cout << ", entry contents: \"" << m_entry.get_text()
    << "\", checkbutton status: " << m_check.get_active() << std::endl;
}