Trois manière d'utiliser Gtk avec OCaml

Publié le dans « Informatique ». Mots-clefs : ocaml, gtk

Créer des interfaces Gtk pour OCaml peut se réveler compliquer. On trouve beaucoup d’explications un peu confuses sur le net, et n’est pas facile de trouver comment faire. Je vous propose ici trois manières différentes en utilisants les différents outils disponibles.

Il est possible d’utiliser la bibliothèque Gtk à l’aide de la librairie lablgtk, qui fourni un binding pour l’ensemble de la bibliothèque. L’avantage est de pouvoir fonctionner sur toutes les plateformes où gtk est disponible, et de bénéficier d’une interface évoluée et agréable pour écrire son application.

L’inconvénient réside dans la bibliothèque gtk: api très compliquée, documentation confuse, bref on se retrouve rapidement face à des problèmes là où l’on pensait pouvoir avancer sans difficulté. De plus, on est aujourd’hui passé à gtk3 alors que lablgtk utilise toujours l’api de gtk2. Cela ne pose pas de problème dans la compilation (la compatibilité est assurée), mais peut poser problème lors de l’utilisation d’outils tels que glade (voir plus loin).

Tout construire à la main

C’est la première solution, qui demande de connaître Gtk: tous les objets sont construits à la main, et le code décrit la manière de faire. L’avantage est que le code ne dépend d’aucune ressource externes contrairement aux deux suivantes. De plus on contrôle complètement la création de l’interface, on peut donc choisir de paramétrer l’interface au lieu d’avoir une interface unique. Un tutoriel complet est disponible sur le site d’OCaml.org, je ne vais pas le reprendre ici et vous encourage à le suivre.

L’exemple est donné dans la console interactive. Si l’on souhaite le compiler dans un module, il faut initialiser Gtk avant de lancer l’affichage:

GtkMain.Main.init ();

Pour compiler un module, il est nécessaire de faire référence au package lablgtk2. Voici un exemple pour compiler un fichier hello.ml :

$ ocamlfind ocamlc -package lablgtk2 -I . -c hello.ml

Maintenant il ne reste plus qu’à se plonger dans la documentation de gtk pour construire son interface!

Utiliser Glade

Glade est une interface graphique permettant de construire son application en plaçant les contrôles de manière visuelle. Elle génère un fichier XML qui décrit l’interface et sera chargé par l’application pour construire l’interface. Cela permet de gagner du temps et d’éviter d’écrire le code nécessaire pour construire son interface, on se concentre sur les actions à exécuter lorsque l’utilisateur interagit.

Utilisation de glade.

Les XMLs générés par glade sont destinés à être utilisés avec gtk3. Or, lablgtk utilise encore gtk2, il est donc nécessaire d’utiliser une conversion pour pouvoir les charger par la suite. Voici une petite règle make qui se charge de faire le travail:

%.glade2: %.glade
        cat $< | sed 's/interface/glade-interface/' | sed 's/object/widget/' | sed 's/GtkBox/GtkVBox/' > $@

Maintenant qu’on dispose d’un fichier au format glade2, on peut le charger dans OCaml.

Attention, lors de la compilation, il est nécessaire d’utiliser libglade pour construire l’application, celle-ci est disponible dans la librairie lablgtk2.glade. Voici donc un exemple de commande pour compiler un fichier hello.ml :

$ ocamlfind ocamlc -package lablgtk2.glade -I . -c hello.ml

Charger le fichier xml

Il s’agit de la solution la plus dynamique: on référence le fichier xml dans le code, et l’interface se construit toute seule. Cette solution est présentée sur le site de developpez.com. L’exemple donné est toujours valide, il ne faut pas oublier d’initialiser Gtk avec la commande suivante avant de lancer la construction de l’interface:

GtkMain.Main.init ();

L’inconvénient de cette méthode (du moins pour un développeur OCaml) est que l’on est obligé de convertir de manière dynamique tous les objets présents dans le XML. Il n’est pas possible de savoir au moment de la compilation si le code que l’on va exécuter est bien valide.

En effet, les objets chargés depuis le XML nous sont retournés sous la forme widget gtk, qu’il faut convertir en Bouton, Menu via les méthodes appropriées (GtkWindow.Window.cast par exemple). On n’est donc pas à l’abri d’avoir une erreur lors du fonctionnement du programme, alors que celui-ci a pu compiler sans problème. Je pense que lorsqu’on cherche à développer en OCaml, ce genre de problème peut être rédhibitoire.

De plus, rien ne garantie que le fichier XML ne va pas évoluer de manière incompatible du code; les deux étant distincts.

Utiliser lablgladecc2

Heureusement, la librairie lablgtk2 nous fourni un petit utilitaire nommé lablgladecc2 qui va convertir un fichier xml glade2 en un fichier OCaml. On dispose donc d’un chargement dynamique du fichier xml, mais en gardant un code cohérent, qui détectera les erreurs dès la compilation. Il s’agit en quelque sorte d’un moyen de combiner les deux solutions précédentes.

On va ajouter une règle au makefile pour générer notre fichier OCaml:

%.ml: %.glade2
        lablgladecc2 -embed $< > $@

Le fichier généré se compose d’une classe reprenant les différents composants de notre interface, il ne nous reste plus qu’à réaliser les connexions, ainsi, à partir d’un fichier glade nommé gui composé d’une fenêtre window1, d’un bouton button et d’une entrée de menu, on peut créer le code suivant:

let gladecc () =
  let window = new Gui.window1 () in

  window#button#connect#clicked (fun () -> prerr_endline "Ouch!");
  window#window1#connect#destroy GMain.quit;
  window#imagemenuitem5#connect#activate GMain.quit;

  window#toplevel#show ()

let () =
    GtkMain.Main.init ();
    gladecc ();
    GMain.Main.main ()

l’objet toplevel est créé par lablgladecc2 et correspond à la fenêtre principale de notre objet.

Dans cette chaîne de compilation, le fichier xml est écrit dans le programme OCaml (il s’agit de la signification de l’option -embed), ainsi, le fichier XML n’a pas besoin de figurer parmi les ressources de l’application.

Conclusion

Trois manière de faire qui répondent à trois besoin différents, entre le tout codé et le tout dynamique, il est possible de créer des interfaces graphiques en utilisant les capacités du langage caml sur l’inférence de type et le contrôle de l’ensemble de l’application.

Pour ma part, je préfère la dernière solution, qui permet de conserver la simplicité de glade combiné avec la force du langage OCaml. J’ai écrit cet article suite à pas mal d’errance sur le net pour trouver les informations nécessaires, j’espère que la documentation va évoluer par la suite et permettre de faire ce genre de choses plus facilement…

À lire aussi :

Commentaires :

Aucun commentaire pour l'instant.