Vous êtes à peu près ici : Accueil  »   tutoriel PyGTK  »   PyGTK : sommaire

16.2. Les listes déroulantes (ComboBox) et listes déroulantes avec saisie (ComboBoxEntry)

16.2.1. Le widget ComboBox

La boîte déroulante ComboBox remplace le OptionMenu par un widget puissant qui utilise un TreeModel (généralement un ListStore) pour obtenir la liste d'éléments à afficher. Le ComboBox applique l'interface du CellLayout qui fournit un certain nombre de méthodes pour gérer l'affichage des éléments de liste. Un ou plusieurs CellRenderers peuvent être placés dans un ComboBox pour personnaliser l'affichage des éléments de liste.

16.2.1.1. Utiliser la boîte déroulante de base

La manière simple de créer et remplir un ComboBox est d'utiliser la fonction suivante :

  boitederoul = gtk.combo_box_new_text()

Cette fonction crée un ComboBox et son ListStore associé et le place avec un CellRendererText. On utilise les méthodes suivantes pour remplir ou supprimer le contenu du ComboBox et de son ListStore :

  boitederoul.append_text(text)
  boitederoul.prepend_text(text)
  boitederoul.insert_text(position, text)
  boitederoul.remove_text(position)

... où text est une chaîne à ajouter au ComboBox et position est l'index où le text doit être inséré ou retiré. Dans la plupart des cas, on n'aura besoin que de cette fonction et de ces méthodes.

Le programme d'exemple comboboxbasic.py montre l'usage des fonction et méthodes ci-dessus. La Figure 16.5, « Basic ComboBox » illustre le programme en cours :

Figure 16.5. Basic ComboBox

Basic ComboBox
Exemple de ComboBox simple.

Malheureusement, les développeurs de GTK+ n'ont pas fourni de méthode commode pour récupérer l'élément actif. Ceci serait pourtant une méthode utile. Il faut créer la sienne, semblable à :

  def get_active_text(boitederoul):
      modele = boitederoul.get_model()
      est_actif = boitederoul.get_active()
      if est_actif < 0:
          return None
      return modele[est_actif][0]

L'index de l'élément actif est récupéré par la méthode :

  est_actif = boitederoul.get_active()

L'élément actif peut être défini par la méthode :

  boitederoul.set_active(index)

... où index est un entier supérieur à -2. Si index vaut -1, il n'y a aucun élément actif et l'affichage du ComboBox est vierge. Si index est inférieur à -1, l'appel sera ignoré. Si index est supérieur à -1, l'élément de liste qui possède cette valeur d'index sera affiché.

On peut se connecter au signal "changed" d'un ComboBox pour être prévenu lorsque l'élément actif change. Le gestionnaire de "changed" a la forme :

  def fct_rappel_change(combobox, ...):

... où ... représente zéro ou plusieurs arguments transmis à la méthode GObject.connect().

16.2.1.2. Usage avancé de ComboBox

Créer un ComboBox par la fonction gtk.combo_box_new_text() correspond à peu près au code suivant :

  liststore = gtk.ListStore(str)
  boitederoul = gtk.ComboBox(liststore)
  case = gtk.CellRendererText()
  boitederoul.pack_start(case, True)
  boitederoul.add_attribute(case, 'text', 0)  

Pour utiliser les possibilités des divers objets des TreeModel et des CellRenderer, il faut créer le ComboBox par le constructeur :

  combobox = gtk.ComboBox(model=None)

...où model est un TreeModel. Lorsque l'on crée un ComboBox sans l'associer à un TreeModel, on peut ajouter celui-ci ensuite par la méthode :

  boitederoul.set_model(model)

Le TreeModel associé peut être récupéré par la méthode :

  modele = boitederoul.get_model()

Quelques unes des possibilités réalisables avec un ComboBox :

  • Partager le même TreeModel avec un autre ComboBox et TreeView.
  • Afficher des images et du texte dans les éléments de liste du ComboBox.
  • Utiliser un TreeStore ou un ListStore préexistant comme modèle des éléments de liste du ComboBox.
  • Utiliser un TreeModelSort pour obtenir une liste classée dans le ComboBox.
  • Utiliser un TreeModelFilter pour utiliser un sous-arbre d'un TreeStore comme the source pour les éléments de liste du ComboBox.
  • Utiliser un TreeModelFilter pour utiliser un sous-ensemble des lignes d'un TreeStore ou d'un ListStore comme éléments de liste du ComboBox.
  • Utiliser une fonction de données de cellule pour modifier ou synthétiser l'affichage des articles de liste.

L'utilisation des objets TreeModel et CellRenderer objects est exposée en détail dans Chapitre 14, Le widget TreeView.

Si on a ComboBox avec beaucoup d'éléments de liste, ceux-ci peuvent être affichés dans une grille. Autrement, si on ne peut afficher la liste entière, celle-ci aura des flèches de défilement. La méthode suivante est utilisée pour fixer le nombre de colonnes à afficher ::

  boitederoul.set_wrap_width(width)

... où width est le nombre de colonnes de la grille affichant les éléments de liste. Par exemple, le programme comboboxwrap.py affiche une liste de 50 éléments sur 5 colonnes. La Figure 16.6, « ComboBox avec colonnes » illustre le programme en action :

Figure 16.6. ComboBox avec colonnes

Exemple de ComboBox à colonnes.

Avec un grand nombre d'éléments, c'est à dire au-delà de 50, l'utilisation de la méthode set_wrap_width() fournira une faible performance à cause du calcul de l'affichage de la grille. Pour avoir un aperçu de l'effet, modifier la ligne 18 du programme comboboxwrap.py pour afficher 150 éléments.

        for n in range(150):

Lancer le programme pour obtenir une estimation du temps d'initialisation. Puis le modifier en commentant la ligne 17 :

        #boitederoul.set_wrap_width(5)

Relancer le programme et chronométrer à nouveau. Il devrait s'exécuter beaucoup plus vite, à peu prés 20 fois plus rapidement.

Outre la méthode get_active() décrite ci-dessus, il est possible d'obtenir un TreeIter désignant la ligne active en utilisant la méthode :

  iter = boitederoul.get_active_iter()

On peut aussi établir l'élément de liste actif en utilisant un TreeIter avec la méthode :

  boitederoul.set_active_iter(iter)

Les méthodes set_row_span_column() et set_column_span_column() sont supposées permettre la spécification du nombre de colonnes d'un TreeModel qui contient le nombre de lignes ou de colonnes sur lesquelles les éléments de liste doivent s'étendre dans la grille. Malheureusement, ces méthodes sont cassées dans GTK+ 2.4.

Puisque le ComboBox implémente l'interface CellLayout qui possède les mêmes capacités que le TreeViewColumn (voir Section 14.5, « TreeViewColumn » pour plus d'information). En bref, l'interface fournit :

  boitederoul.pack_start(cell, expand=True)
  boitederoul.pack_end(cell, expand=True)
  boitederoul.clear()

Les deux premières méthodes place un CellRenderer dans le ComboBox, la méthode clear() supprime tous les attributs de tous les CellRenderer.

Les méthodes suivantes :

  comboboxentry.add_attribute(cell, attribute, column)

  comboboxentry.set_attributes(cell, ...)

... fixent les attributs pour les CellRenderer indiqués par cell. La méthode add_attribute() prend une chaîne, nom d'attibut attribute (par exemple 'texte') et un entier numéro de colonne de la column du TreeModel à utiliser pour placer l'attribut. Les arguments supplémentaires de la méthode set_attributes() sont des paires attribute=column (par exemple texte=1).

16.2.2. Le widget ComboBoxEntry

Le widget ComboBoxEntry remplace le widget Combo. C'est une sous-classe du widget ComboBox qui contient un widget fils Entry dont le contenu est fixé en choisissant un élément dans une liste déroulante, en entrant directement le texte au clavier ou par un coller depuis un presse-papier Clipboard ou une sélection.

16.2.2.1. Utilisation fondamentale du ComboBoxEntry

Comme le ComboBox, le ComboBoxEntry peut être créé par la fonction :

  boitderoulsaisie = gtk.combo_box_entry_new_text()

Le ComboBoxEntry devrait être rempli en utilisant les méthodes auxiliaires du ComboBox décrites dans la section Section 16.2.1.1, « Utiliser la boîte déroulante de base ».

Comme un widget ComboBoxEntry est un widget Bin, son widget fils Entry est disponible en utilisant l'attibut "child" ou la méthode get_child() :

  saisie = boitderoulsaisie.child
  saisie = boitderoulsaisie.get_child()

On peut récupérer le texte de la zone Entry par la méthode get_text().

Comme pour le ComboBox, on peut surveiller les changements de l'élément actif de la liste en se connectant au signal "changed". Malheureusement, cela ne permet pas de suivre les modifications de texte dans le Entry fils lorsque ce sont des saisies directes. Quand und saisie directe est réalisée dans le widget fils Entry, le signal "changed" est bien émis mais l'index retourné par la méthode get_active() vaudra -1. Pour surveiller tous les changements dans le texte de la zone de saisie Entry, il faudra utiliser le signal "changed" du widget Entry. Par exemple :

  def fct_rappel_change(saisie):
      print saisie.get_text()

  boitderoulsaisie.child.connect('changed', fct_rappel_change)

... affichera le texte après chaque modification dans le widget fils Entry. Pour exemple, le programme comboboxentrybasic.py montre l'utilisation de l'API. La figure Figure 16.7, « Liste déroulante avec zone de saisie » illustre le programme en action.

Figure 16.7. Liste déroulante avec zone de saisie

Exemple de ComboBox avec saisie.

Il faut noter que lorsque le texte du Entry est modifié par un choix dans la liste déroulante, le gestionnaire du signal "changed" est appelé deux fois :une fois quand le texte est supprimé et une autre fois quand le texte prend la valeur du texte de l'élément de liste choisi.

16.2.2.2. Utilisation avancée du ComboBoxEntry

Le constructeur du ComboBoxEntry est :

  boitderoulsaisie = gtk.ComboBoxEntry(model=None, column=-1)

... où model est un TreeModel et column est le numéro de la colonne du model à utiliser pour créer la liste d'éléments. Si la colonne n'est pas précisée, la valeur par défaut est -1, ce qui signifie que la colonne du texte n'est pas indiquée.

Créer un ComboBoxEntry avec la fonction auxiliaire gtk.combo_box_entry_new_text() est équivalent à :

  liststore = gtk.ListStore(str)
  boitderoulsaisie = gtk.ComboBoxEntry(liststore, 0)

Le ComboBoxEntry ajoute quelques méthodes utilisées pour fixer et récupérer le numéro de colonne du TreeModel à utiliser pour fixer les chaînes des éléments de liste :

  boitderoulsaisie.set_text_column(text_column)
  text_column = boitderoulsaisie.get_text_column()

La colonne du texte peut aussi être récupérée et fixée en utilisant la propriété "text-column". Se reporter à la section Section 16.2.1.2, « Usage avancé de ComboBox » pour plus de renseignements sur l'utilisation avancée du ComboBoxEntry.

Note

Votre application doit indiquer la colonne de texte pour que le ComboBoxEntry établisse le contenu de la zone Entry depuis la liste déroulante. la colonne de texte ne peut être indiqué qu'une fois, soit en utilisant le constructeur, soit en utilisant la méthode set_text_column().

Lorqu'on crée un ComboBoxEntry, il est placé avec un nouveau CellRendererText qui n'est pas accessible. L' attribut 'text' du CellRendererText doit être fixé, c'est un effet secondaire de l'utilisation de la méthode set_text_column() pour déterminer la colonne de texte. On peut placer des CellRenderer supplémentaires dans un ComboBoxEntry pour l'affichage dans la liste déroulante. Se reporter à la section Section 16.2.1.2, « Usage avancé de ComboBox » pour plus d'information.