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.

Oui, ceci est correct : un objet Window peut contenir au plus un élément graphique. Comment, alors, se servir utilement d'une fenêtre ? En y plaçant un conteneur multi-enfants. Les conteneurs d'éléments graphiques les plus utiles sont les objets Gtk:Grid, Gtk:VBox, Gtk::HBox et Gtk::Table.

  • 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::VBox et Gtk::HBox disposent respectivement leurs éléments graphiques enfants verticalement et horizontalement. Utilisez pack_start() et pack_end() pour insérer des éléments graphiques enfants.
  • Gtk::Table place ses éléments graphiques sur une grille. Utilisez attach() pour insérer les éléments graphiques.

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::HBox 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/main.h>

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

  HelloWorld helloworld;
  //Shows the window and returns when it is closed.
  Gtk::Main::run(helloworld);

  return 0;
}

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

Les fonctions membres pack_start() et pack_end() placent les éléments graphiques dans ces conteneurs. La fonction membre pack_start() débute l'insertion en haut de l'objet VBox et va en descendant ; elle empaquette de gauche vers la droite dans un objet HBox. La fonction membre pack_end() fait l'inverse. Elle empaquette de bas en haut dans un VBox, de droite à gauche dans un HBox. L'utilisation de ces fonctions membres vous permet de justifier à droite ou à gauche les éléments graphiques. Nous nous servirons de pack_start() dans la plupart de nos exemples.

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

Chaque ligne est constituée d'une boîte horizontale (HBox) avec plusieurs boutons. Chacun des boutons de la ligne est empaqueté dans l'objet HBox avec les mêmes paramètres à l'aide de la fonction membre pack_start().

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::HBox
{
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::VBox m_box1;
  Gtk::HBox m_boxQuit;
  Gtk::Button m_buttonQuit;

  Gtk::Label m_Label1, m_Label2;

  Gtk::HSeparator 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_buttonQuit("Quit")
{
  set_title("Gtk::Box example");

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

  switch(which)
  {
    case 1:
    {
      m_Label1.set_text("Gtk::HBox(false, 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::HBox(true, 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::HBox(false, 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::HBox(false, 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/main.h>
#include <iostream>
#include <cstdlib>

using std::atoi;

int main(int argc, char *argv[])
{
  Gtk::Main main_instance(argc, argv);

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

  ExampleWindow window( atoi(argv[1]) );
  Gtk::Main::run(window); //Shows the window and returns when it is closed.

  return 0;
}

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::HBox(homogeneous, 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"))
{
  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

Les boîtes à boutons sont un moyen commode de disposer rapidement un groupe de boutons. Il en existe deux types : un horizontal (Gtk::HButtonBox) et un vertical (Gtk::VButtonBox). Ils sont tout à fait semblables ; ils ne différent que par leur nom et leur orientation.

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::VBox m_VBox_Main, m_VBox;
  Gtk::HBox 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::HButtonBox() );
  else
    bbox = Gtk::manage( new Gtk::VButtonBox() );

  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_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/main.h>

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

  ExampleWindow window;
  //Shows the window and returns when it is closed.
  Gtk::Main::run(window);

  return 0;
}

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/main.h>

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

  ExampleWindow window;
  // Shows the window and returns when it is closed.
  Gtk::Main::run(window);

  return 0;
}

VIII.II.VI. Tableau

Les tableaux permettent de placer les éléments graphiques sur une grille, de manière identique à Gtk::Grid.

VIII.II.VI.I. Constructeur

Il est nécessaire de définir les dimensions de la grille dans le constructeur :

Gtk::Table(int rows = 1, int columns = 1, bool homogeneous = false);

Le premier paramètre est le nombre de lignes du tableau, le second le nombre de colonnes. Si homogeneous est égal à true, toutes les cellules du tableau auront la même taille (la taille du plus grand élément graphique du tableau).

Les lignes et les colonnes sont numérotées à partir de zéro. Si vous définissez rows = 2 et columns = 2, la disposition ressemble à ceci :

 0          1          2
0+----------+----------+
 |          |          |
1+----------+----------+
 |          |          |
2+----------+----------+

Notez que l'origine du système de coordonnées est l'angle supérieur gauche.

VIII.II.VI.II. Ajout d'éléments graphiques

Pour placer un élément graphique dans une cellule, utilisez la fonction membre suivante :

void Gtk::Table::attach(Gtk::Widget& child,
                        guint left_attach, guint right_attach,
                        guint top_attach, guint bottom_attach,
                        guint xoptions = Gtk::FILL | Gtk::EXPAND,
                        guint yoptions = Gtk::FILL | Gtk::EXPAND,
                        guint xpadding = 0, guint ypadding = 0);

Le premier paramètre est l'élément graphique que vous souhaitez placer dans la table.

Les paramètres left_attach et right_attach indiquent où placer l'élément graphique et combien de cellules utiliser. Par exemple, si vous voulez placer un bouton dans la cellule inférieure droite d'un tableau 2 x 2 et souhaitez qu'il occupe uniquement cette cellule, alors left_attach doit être égal à 1, right_attach à 2, top_attach à 1 et bottom_attach à 2. Si, d'autre part, vous souhaitez que l'élément graphique occupe la totalité de la rangée haute du tableau 2 x 2, vous définirez left_attach = 0, right_attach = 2, top_attach = 0 et bottom_attach = 1.

Les paramètres xoptions et yoptions s'utilisent pour définir les options d'empaquetage. Pour définir des options multiples, il convient de les lier entre elles avec l'opérateur binaire OR. Ces options sont :

Gtk::FILL

Si la cellule du tableau est plus grande que l'élément graphique et si Gtk::FILL est indiqué, l'élément graphique sera étiré pour utiliser toute la place disponible.

Gtk::SHRINK

Si l'élément graphique tableau dispose de moins de place que nécessaire (parce que l'utilisateur a redimensionné la fenêtre), les éléments graphiques dans les cellules vont être cachés par le bas de la fenêtre. Si Gtk::SHRINK est indiqué, les éléments graphiques vont rétrécir avec le tableau.

Gtk::EXPAND

Cette option va provoquer l'étalement du tableau jusqu'à occuper tout l'espace libre de la fenêtre.

Les paramètres de remplissage fonctionnent exactement comme pour la fonction membre pack_start().

VIII.II.VI.III. Autres fonctions membres

set_row_spacing() et set_col_spacing() définissent l'espacement entre lignes ou entre colonnes pour la ligne ou la colonne indiquée. Notez que, pour les colonnes, l'espacement se situe à droite de la colonne et que, pour les lignes, l'espacement se situe sous la ligne.

Vous pouvez également fixer un espacement d'ensemble pour toutes les lignes et toutes les colonnes avec set_row_spacings() et set_col_spacings(). Notez qu'avec ces fonctions membres, la dernière ligne et la dernière colonne n'ont pas l'espacement indiqué.

Référence

VIII.II.VI.IV. Exemple

Dans l'exemple ci-après, nous créons une fenêtre avec trois boutons dans un tableau 2 x 2. Les deux premiers boutons sont placés dans la ligne supérieure. Le troisième bouton est placé dans la ligne inférieure et occupe les deux colonnes.

Figure VIII.11 Tableau

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_button_numbered(Glib::ustring data);

  //Child widgets:
  Gtk::Table m_Table;
  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_Table(2, 2, true),
  m_Button_1("button 1"),
  m_Button_2("button 2"),
  m_Button_Quit("Quit")
{
  set_title("Gtk::Table");
  set_border_width(20);

  add(m_Table);

  m_Table.attach(m_Button_1, 0, 1, 0, 1);
  m_Table.attach(m_Button_2, 1, 2, 0, 1);
  m_Table.attach(m_Button_Quit, 0, 2, 1, 2);

  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(Glib::ustring data)
{
  std::cout << "Hello again - " << data << " was pressed" << std::endl;
}

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

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

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

  ExampleWindow window;
  //Shows the window and returns when it is closed.
  Gtk::Main::run(window);

  return 0;
}

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.12 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::VBox m_VBox;
  Gtk::Notebook m_Notebook;
  Gtk::Label m_Label1, m_Label2;

  Gtk::HButtonBox 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_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/main.h>

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

  ExampleWindow window;
  //Shows the window and returns when it is closed.
  Gtk::Main::run(window);

  return 0;
}

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.13 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::HBox 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::Table m_table;
  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_table(3, 2),
  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_table.attach(m_button, 0, 2, 0, 1, Gtk::FILL, Gtk::EXPAND);
  m_table.attach(m_label1, 0, 1, 1, 2, Gtk::FILL, Gtk::EXPAND);
  m_table.attach(m_label2, 0, 1, 2, 3, Gtk::FILL, Gtk::EXPAND);
  m_table.attach(m_check, 1, 2, 1, 2);
  m_table.attach(m_entry, 1, 2, 2, 3);
  add(m_table);

  m_label1.set_alignment(0.0, 0.5);
  m_label2.set_alignment(0.0, 0.5);

  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/main.h>

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

  ExampleWindow window;
  // Shows the window and returns when it is closed.
  Gtk::Main::run(window);

  return 0;
}

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

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

ExampleAssistant::ExampleAssistant()
: m_box(false, 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;
}