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

16.7. Le gestionnaire d'Interface Utilisateur UIManager

16.7.1. Aperçu

Le gestionnaire UIManager fournit des outils pour créer des menus et des barres d'outils à partir d'une description de type XML. Le UIManager utilise les objets du ActionGroup pour gérer les objets du Action fournissant une infrastructure commune aux éléments de menu et de barre d'outils.

L'utilisation du UIManager permet de fusionner et scinder dynamiquement des descriptions et actions de UI multiples. Ceci permet de modifier les menus et les barres d'outils selon le mode utilisé dans l'application (par exemple, passer d'une édition de texte à une édition d'image), ou quand de nouvelles extensions sont ajoutées ou enlevées de l'application.

Un UIManager peut être utilisé pour créer les menus et les barres d'outils d' un interface utilisateur de la façon suivante :

  • Créer une instance de UIManager.
  • Extraire le AccelGroup du UIManager et l'ajouter à la Fenêtre de niveau supérieur.
  • Créer des instances de ActionGroup et les remplir avec les Action voulues. instances.
  • Ajouter les instances de ActionGroup au UIManager dans l'ordre dans lequel on souhaite présenter les instances des Action.
  • Ajouter les descriptions XML de l'interface UI au UIManager. Il faut s'assurer que toutes les Actions définies par les descriptions soient disponibles dans les instances ActionGroup du UIManager.
  • Extraire, par leur nom, les références des widgets de la barre de menu, du menu et de la barre d'outils pour les utiliser dans la construction de l'interface utilisateur.
  • Modifier l'interface de manière dynamique, en ajoutant ou retirant des descriptions d'Interface Utilisateur et en ajoutant, réordonnant ou retirant les instances associées des groupes d'actions ActionGroup.

16.7.2. Créer un UIManager

Une instance de UIManager se crée par le constructeur :

  gestionui = gtk.UIManager()

Un nouveau UIManager est créé avec son groupe d'accélérateurs associé AccelGroup, lequel peut être récupéré avec la méthode  :

  grouperacc = gestionui.get_accel_group()

Le groupe AccelGroup doit être ajouté à la fenêtre de niveau supérieur pour que les accélérateurs (raccourcis clavier) de Action puissent être disponibles pour les utilisateurs. Par exemple :

  fenetre = gtk.Window()
  ...
  gestionui = gtk.UIManager()
  grouperacc = gestionui.get_accel_group()
  fenetre.add_accel_group(accelgroup)

16.7.3. Ajouter et supprimer des groupe d'actions

Comme on l'a vu dans la Section 16.1.2, « ActionGroup », un groupe d'actions ActionGroups peut être rempli avec des Actions en utilisant les méthodes add_actions(), add_toggle_actions() et add_radio_actions(). Un ActionGroup peut être utilisé par un UIManager lorqu'il a été ajouté à sa liste des ActionGroup par la méthode :

  gestionui.insert_action_group(action_group, pos)

... où pos est l'indication de la position où le action_group doit être inséré. Un UIManager peut comporter plusieurs groupes ActionGroup avec des noms de Action en double. L'ordre des objets d'un ActionGroup est important car la recherche d'une Action par son nom s'arrête sur la première Action trouvée. Ceci signifie que les premières actions dans les objets ActionGroup masquent les suivantes.

Les actions inscrites dans la description XML d'une Interface Utilisateur doivent être ajoutées dans le UIManager avant que la description elle-même puisse l'être.

Un ActionGroup peut être supprimé d'un UIManager par la méthode :

  gestionui.remove_action_group(action_group)

Une liste des objets ActionGroup associés au UIManager peut être récupérée par la méthode :

  listegroupesactions = gestionui.get_action_groups()

16.7.4. Descriptions de l'Interface Utilisateur

Les descriptions d'Interface Utilisateur acceptées par le UIManager sont de simples définitions XML qui comportent les éléments suivants :

ui L'élément racine de la decription de l'Interface Utilisateur. Il peut être omis. Il peut contenir les éléments menubar, popup, toolbar et accelerator.
menubar Un élément de premier niveau qui décrit une structure de barre de menu MenuBar qui peut contenir les éléments MenuItem, separator, placeholder et menu. Il possède en option un attribut name. Si cet attribut name n'est pas précisé, il aura pour valeur "menubar".
popup Un élément de premier niveau qui décrit une structure de menu popup Menu qui peut contenir les éléments menuitem, separator, placeholder et menu. Il possède en option un attribut name. Si cet attribut name n'est pas précisé, il aura pour valeur "popup".
toolbar Un élément de premier niveau qui décrit une structure de barre d'outils Toolbar qui peut contenir les éléments toolitem, separator et placeholder. Il possède en option un attribut name. Si cet attribut name n'est pas précisé, il aura pour valeur "toolbar".
placeholder Un élément identifiant une position dans un menubar, toolbar, popup ou menu. Ce paramètre substituable peut contenir les éléments menuitem, separator, placeholder et menu. Les éléments substituables Placeholder sont utilisés pour fusionner plusieurs descriptions d'Interface Utilisateur pour permettre, par exemple, la construction d'un menu à partir de descriptions d'Interface Utilisateur en utilisant des noms de placeholder partagés. Il possède en option un attribut name. Si cet attribut name n'est pas précisé, il aura pour valeur "placeholder".
menu Un élément décrivant une structure de Menu qui peut contenir les éléments menuitem, separator, placeholder et menu. Un élément menu possède obligatoirement un attribut action qui désigne un objet Action, utilisé pour créer le Menu. Il possède en option les attributs name et position. Si l'attribut name n'est pas précisé, il aura pour valeur le nom de l'élément action. L'attribut position peut avoir pour valeur "top" ou "bottom". Si sa valeur n'est pas précisée, ce sera, par défaut, "bottom".
menuitem Un élément décrivant un MenuItem. Un élément menuitem possède un attribut obligatoire action qui désigne un objet Action utilisé pour créer l'élément de menu MenuItem. Il possède également les attributs facultatifs name et position. Si name n'est pas précisé, le nom de action sera utilisé. L'attribut position peut avoir pour valeur "top" ou "bottom". Si sa valeur n'est pas précisée, ce sera, par défaut, "bottom".
toolitem Décrit un élémént de barre d'outils ToolItem. Un élément toolitem possède un attribut obligatoire action qui désigne un objet Action utilisé pour créer la barre d'outils Toolbar. Il possède également les attributs facultatifs name et position. Si name n'est pas précisé, le nom de action sera utilisé. L'attribut position peut avoir pour valeur "top" ou "bottom". Si sa valeur n'est pas précisée, ce sera, par défaut, "bottom".
separator Élément décrivant un séparateur d'éléments de menu SeparatorMenuItem ou SeparatorToolItem selon les cas.
accelerator Élément décrivant un raccourci clavier. Un élément acceleratorpossède un attribut obligatoire action qui désigne un objet Action pour définir la combinaison de touches du clavier et qui est activé par l'accélérateur. Il possède également l'attribut facultatif name . Si name n'est pas précisé, le nom de action sera utilisé.

Par exemple, voici une description d'Interface Utilisateur qui peut être utilisée pour créer une interface semblable à celle de la Figure 16.4, « Exemple avec ActionGroup » :

  <ui>
    <menubar name="BarreMenu">
      <menu action="Fichier">
        <menuitem action="Quitter"/>
      </menu>
      <menu action="Son">
        <menuitem action="Volume"/>
      </menu>
      <menu action="Modulation">
        <menuitem action="MA"/>
        <menuitem action="MF"/>
        <menuitem action="BLU"/>
      </menu>
    </menubar>
    <toolbar name="BarreOutils">
      <toolitem action="Quitter"/>
      <separator/>
      <toolitem action="Volume"/>
      <separator/>
      <placeholder name="ElementsMod">
        <toolitem action="MA"/>
        <toolitem action="MF"/>
        <toolitem action="BLU"/>
      </placeholder>
    </toolbar>
  </ui>
  

Il faut noter que cette description utilise simplement les noms d'attributs du action comme nom de la plupart des éléments plutôt que de préciser leurs attributs name. Aussi, je recommenderais de ne pas préciser l'élément ui lorsque cela n'est pas indispensable.

La hiérarchie de widgets créée par une description d'Interface Utilisateur est tout à fait semblable à une hiérarchie d'éléments XML, excepté que les éléments placeholder sont fusionnés dans leurs parents.

On peut avoir accès à un widget appartenant à la hiérarchie de la description de l'Interface Utilisateur en utilisant son chemin ; celui-ci est composé du nom du widget et de ses éléments parents reliés par des barres obliques ("/"). À partir de la description ci-dessus, voici des exemples de chemins de widgets valides :

  /BarreMenu
  /BarreMenu/Fichier/Quitter
  /BarreMenu/Modulation/BLU
  /BarreOutils/Volume
  /BarreOutils/ElementsMod/MF

Il faut noter que le nom de l'élément de substitution placeholder doit être inclus dans le chemin. Dans la plupart des cas, on ne s'intéresse qu'aux widgets de niveau supérieur (par exemple "/MenuBar" and "/Toolbar") mais on peut avoir besoin d'accèder à un widget de plus bas niveau pour, par exemple, modifier une propriété.

16.7.5. Ajouter et supprimer des descriptions d'Interface Utilisateur

Une fois le UIManager configuré avec un ActionGroup, une description d'Interface Utilisateur peut être ajoutée ou fusionnée avec l'Interface Utilisateur existant par l'une de ces méthodes :

  fusion_id = gestionui.add_ui_from_string(buffer)

  fusion_id = gestionui.add_ui_from_file(filename)

... où le paramètre buffer est une chaîne de caractères contenant une description d'Interface Utilisateur et filename se réfère au fichier contenant la description. Les deux méthodes renvoient un identifiant fusion_id qui est une valeur entière unique. Si la méthode échoue, une exception GError est levée. L'identifiant merge_id peut servir à supprimer la description d'Interface Utilisateur en utilisant la méthode :

  gestionui.remove_ui(fusion_id)

Les mêmes méthodes peuvent être utilisées plusieurs fois pour ajouter des descriptions supplémentaires qui peuvent être fusionnées pour obtenir une description combinée d'Interface Utilisateur en XML. Les Interfaces Utilisateurs combinées seront traitées plus en détail dans la Section 16.7.8, « Combiner des descriptions d'Interface Utilisateur ».

On peut ajouter un seul élément à la description d'Interface Utilisateur existante par la méthode :

  gestionui.add_ui(fusion_id, path, name, action, type, top)

... où l'identifiant fusion_id est un entier unique, path est le chemin où le nouvel élément doit être ajouté, action est le nom de l'Action ou None pour ajouter un separator, type est le type de l'élément à ajouter et top une valeur booléenne. Si top vaut TRUE, l'élément est inséré avant son frère (élément de même niveau), sinon, il est ajouté à la suite.

Le paramètre merge_id sera obtenu par la méthode :

  fusion_id = gestionui.new_merge_id()

Les valeurs entières retournées par la méthode new_merge_id() augmentent arithmétiquement.

Le paramètre path est une chaîne de caractères composée du nom de l'élément et du nom de ses ancétres, séparés par un trait oblique ("/"), qui n'inclut pas le noeud racine optionnel "/ui". Par exemple "/BarreMenu/Modulation" représente le chemin de l'élément de menu nommé "Modulation" dans la description d'Interface Utilisateur suivante :

  <menubar name="BarreMenu">
    <menu action="Modulation">
    </menu>
  </menubar>

La valeur du paramétre type doit être parmi  :

gtk.UI_MANAGER_AUTO Le type de l'élément d'Interface Utilisateur (menuitem, toolitem ou separator) est défini en fonction du contexte.
gtk.UI_MANAGER_MENUBAR Une barre de menu.
gtk.UI_MANAGER_MENU Un menu.
gtk.UI_MANAGER_TOOLBAR Une barre d'outils.
gtk.UI_MANAGER_PLACEHOLDER Une élément de réservation (placeholder).
gtk.UI_MANAGER_POPUP Un menu popup.
gtk.UI_MANAGER_MENUITEM Un élément de menu).
gtk.UI_MANAGER_TOOLITEM Un élément de barre d'outils.
gtk.UI_MANAGER_SEPARATOR Un séparateur.
gtk.UI_MANAGER_ACCELERATOR Un raccourci ou accélerateur.

La méthode add_ui() échoue sans le signaler si l'élément n'est pas ajouté. La méthode add_ui() est de si bas niveau qu'il faut plutôt toujours essayer d'utiliser les méthodes pratiques add_ui_from_string() et add_ui_from_file()

Ajouter un élément ou une description d'Interface Utilisateur entraine l'actualisation de la hiérarchie des widgets dans une fonction en attente. On peut s'assurer que la hiérarchie a bien été actualisée avant d'y accèder en appelant la méthode :

  gestionui.ensure_update()

16.7.6. Accès aux widgets d'un Interface Utilisateur

On peut avoir accès à un widget dans la hiérarchie de l'Interface Utilisateur en utilsant la méthode :

  widget = gestionui.get_widget(path)

... où le paramètre path est une chaîne de caractères qui contient le nom de l'élément recherché et celui de ses ancétres comme on l'a expliqué dans la Section 16.7.4, « Descriptions de l'Interface Utilisateur ».

Par exemple, étant donné la description d'Interface Utilisateur suivante :

  <menubar name="BarreMenu">
    <menu action="Fichier">
      <menuitem action="Quitter"/>
    </menu>
    <menu action="Son">
      <menuitem action="Volume"/>
    </menu>
    <menu action="Modulation">
      <menuitem action="MA"/>
      <menuitem action="MF"/>
      <menuitem action="BLU"/>
    </menu>
  </menubar>
  <toolbar name="BarreOutils">
    <toolitem action="Quitter"/>
    <separator/>
    <toolitem action="Volume"/>
    <separator name="sep1"/>
    <placeholder name="ElementsMod">
      <toolitem action="MA"/>
      <toolitem action="MF"/>
      <toolitem action="BLU"/>
    </placeholder>
  </toolbar>

... que l'on a ajouté au gestionnaire d'UIManager gestionui, on peut accèder à la BarreMenu et à la BarreOutils pour les utiliser dans une application Window en utilisant le code suivant :

  fenetre = gtk.Window()
  vbox = gtk.VBox()
  barremenus = gestionui.get_widget('/BarreMenu')
  barreoutils = gestionui.get_widget('/BarreOutils')
  vbox.pack_start(barremenus, False)
  vbox.pack_start(barreoutils, False)

De la même manière, les widgets de plus bas niveau dans la hiérarchie sont accessibles en utilisant leur chemin. Par exemple, le bouton RadioToolButton appelé "BLU" est accessible ainsi :

  blu = gestionui.get_widget('/BarreOutils/ElementsMod/BLU')

Pour faciliter la tâche, on peut accéder à tous les widgets de haut niveau d'un même type avec la méthode :

  toplevels = gestionui.get_toplevels(type)

... où le paramètre type indique le type des widgets à renvoyer en utilisant une combinaison des drapeaux : gtk.UI_MANAGER_MENUBAR, gtk.UI_MANAGER_TOOLBAR et gtk.UI_MANAGER_POPUP. On peut utiliser la méthode gtk.Widget.get_name() pour déterminer quel est le widget de haut niveau obtenu.

Pour trouver le Action utilisé par le widget délégué associé à un élément d'Interface Utilisateur, on utilise la méthode :

  action = gestionui_get_action(path)

... où le paramètre path est une chaîne de caractères représentant le chemin d'un élément d'Interface Utilisateur dans le gestionnaire gestionui. Si l'élément ne possède pas d'Action associé, la valeur est retournée.

16.7.7. Un exemple d'Interface Utilisateur simple

Le programme uimanager.py illustre l'utilisation du gestionnaire UIManager. La Figure 16.13, « Exemple simple de UIManager » montre le programme en cours d'exécution.

Figure 16.13. Exemple simple de UIManager

Exemple simple de UIManager.

Le programme uimanager.py utilise la description XML de la Section 16.7.6, « Accès aux widgets d'un Interface Utilisateur ». Le texte des deux étiquettes est modifié par l'activation de l'action interrupteur ToggleAction "Volume" et des choix des RadioAction "MA", "MF" et "BLU". Toutes les actions sont inscrites dans un unique ActionGroup qui permet à la sensibilité et à la visibilité de tous les widgets délégués d'être commutées en utilisant les boutons interrupteurs "Sensible" et "Visible". L'utilisation de l'élément placeholder sera décrit en détail dans la Section 16.7.8, « Combiner des descriptions d'Interface Utilisateur ».

16.7.8. Combiner des descriptions d'Interface Utilisateur

La fusion de plusieurs descriptions d'Interface Utilisateur se réalise en fonctio du nom des éléments XML. Comme on l'a auparavant, on peut avoir accès aux éléments individuels de la hiérarchie par leur nom de chemin qui est représenté par leur nom plus le nom de leurs ancêtres. Par exemple, si on utilise la description d'Interface Utilisateur de la Section 16.7.4, « Descriptions de l'Interface Utilisateur », l'élément toolitem "MA" a pour nom de chemin "/BarreOutils/ElementsMod/MA" alors que le nom de chemin de l'élément menuitem est "/BarreMenu/Modulation/MF".

Si une description d'Interface Utilisateur est fusionnée avec la précédente, les éléments sont ajoutés en tant que frères (au même niveau) aux éléments existants. Par exemple, si la description d'Interface Utilisateur :

  <menubar name="BarreMenu">
    <menu action="Fichier">
      <menuitem action="Enregistrer" position="top"/>
      <menuitem action="Nouveau" position="top"/>
    </menu>
    <menu action="Son">
      <menuitem action="Intensite"/>
    </menu>
    <menu action="Modulation">
      <menuitem action="CB"/>
      <menuitem action="OndesCourtes"/>
    </menu>
  </menubar>
  <toolbar name="BarreOutils">
    <toolitem action="Enregistrer" position="top"/>
    <toolitem action="Nouveau" position="top"/>
    <separator/>
    <toolitem action="Intensite"/>
    <separator/>
    <placeholder name="ElementsMod">
      <toolitem action="CB"/>
      <toolitem action="OndesCourtes"/>
    </placeholder>
  </toolbar>

... est ajoutée à notre exemple précédent :

  <menubar name="BarreMenu">
    <menu action="Fichier">
      <menuitem action="Quitter"/>
    </menu>
    <menu action="Son">
      <menuitem action="Volume"/>
    </menu>
    <menu action="Modulation">
      <menuitem action="MA"/>
      <menuitem action="MF"/>
      <menuitem action="BLU"/>
    </menu>
  </menubar>
  <toolbar name="BarreOutils">
    <toolitem action="Quitter"/>
    <separator/>
    <toolitem action="Volume"/>
    <separator name="sep1"/>
    <placeholder name="ElementsMod">
      <toolitem action="MA"/>
      <toolitem action="MF"/>
      <toolitem action="BLU"/>
    </placeholder>
  </toolbar>
  

Le résultat final créé sera :

  <menubar name="BarreMenu">
    <menu name="Fichier" action="Fichier">
      <menuitem name="Nouveau" action="Nouveau"/>
      <menuitem name="Enregistrer" action="Enregistrer"/>
      <menuitem name="Quitter" action="Quitter"/>
    </menu>
    <menu name="Son" action="Son">
      <menuitem name="Volume" action="Volume"/>
      <menuitem name="Intensite" action="Intensité"/>
    </menu>
    <menu name="Modulation" action="Modulation">
      <menuitem name="MA" action="MA"/>
      <menuitem name="MF" action="MF"/>
      <menuitem name="BLU" action="BLU"/>
      <menuitem name="CB" action="CB"/>
      <menuitem name="OndesCourtes" action="OndesCourtes"/>
    </menu>
  </menubar>
  <toolbar name="BarreOutils">
    <toolitem name="Nouveau" action="Nouveau"/>
    <toolitem name="Enregistrer" action="Enregistrer"/>
    <toolitem name="Quitter" action="Quitter"/>
    <separator/>
    <toolitem name="Son" action="Son"/>
    <separator name="sep1"/>
    <placeholder name="ElementsMod">
      <toolitem name="MA" action="MA"/>
      <toolitem name="MF" action="MF"/>
      <toolitem name="BLU" action="BLU"/>
      <toolitem name="CB" action="CB"/>
      <toolitem name="OndesCourtes" action="OndesCourtes"/>
    </placeholder>
    <separator/>
    <toolitem name="Intensite" action="Intensite"/>
    <separator/>
  </toolbar>

Si on examine le résultat XML, on peut constater que les éléments menuitem "Nouveau" et "Enregistrer" ont été incorporés avant l'élément "Quitter". Ceci est dû à la valeur "top" de l'attribut "position" qui signifie que l'élément doit être ajouté en-tête. De la même manière, les éléments toolitem "Nouveau" et "Enregistrer" ont été incorporés au début de la barre d'outils "BarreOutils". Il faut remarque que les éléments "Nouveau" et "Enregistrer" ont été inversés par le processus de fusion.

L'élément toolitem "Intensite" est ajouté aux autres éléments de la barre d'outils "BarreOutils" et apparaît en dernière place dans la description fusionnée même s'il n'est pas le dernier de sa propre description. Dans les deux descriptions, l'élément placeholder "ElementsMod" mêle les éléments toolitem "CB" et "OndesCourtes" avec les éléments "MA", "MF", et "BLU". Si on n'avait pas utilisé un élément placeholder "ElementsMod", les éléments "CB" et "OndesCourtes" se seraient retrouvés après l'élément "Intensite".

On peut obtenir une représentation de l'Interface Utilisateur utilisée par un UIManager par la méthode :

  uidesc = uimanager.get_ui()

Le programme uimerge.py illustre la fusion des description précédentes UIManager. La Figure 16.14, « Exmple de fusion UIMerge » montre les Interfaces Utilisateurs séparés et fusionnés.

Figure 16.14. Exmple de fusion UIMerge

Exemple de fusion UIMerge.

Le programme exemple utilise trois objets ActionGroup :

  • Des objets Action pour les menus "Fichier", "Son" et "Modulation"
  • Des objets Action pour les menus "Quitter", "Son", "MA", "MF", "BLU" et "Modulation"
  • Des objets Action pour les éléments "Intensité", "CB" et "Ondes Courtes"

Les widgets de choix ToggleButton "Sensible" et "Visible" contrôlent la sensibilité et la visibilité du deuxième ActionGroup uniquement.

16.7.9. Signaux de l'Interface Utilisateur

Le UIManager possède une paire de signaux intéressants auxquels se connecter. Le signal "actions-changed" est émis lorsqu'un ActionGroup est ajouté ou retiré du UIManager. La fonction de rappel est :

  def fct_rappel(gestionui, ...)

Le signal "add-widget" est émis lorsqu'un widget délégue de Toolbar ou de MenuBar est créé. La fonction de rappel est :

  def fct_rappel(gestionui, widget, ...)

... où le paramètre widget représente le nouveau widget créé.