From e7b1d8b488c0a61c04a6bdae817020e1b3014356 Mon Sep 17 00:00:00 2001 From: Sébastien Dailly Date: Sun, 9 Feb 2014 12:05:40 +0100 Subject: 3 ways to use gtk with ocaml --- content/Informatique/ocaml_gtk.rst | 206 +++++++++++++++++++++++++++++++++++++ 1 file changed, 206 insertions(+) create mode 100644 content/Informatique/ocaml_gtk.rst (limited to 'content/Informatique/ocaml_gtk.rst') diff --git a/content/Informatique/ocaml_gtk.rst b/content/Informatique/ocaml_gtk.rst new file mode 100644 index 0000000..b124cb5 --- /dev/null +++ b/content/Informatique/ocaml_gtk.rst @@ -0,0 +1,206 @@ +.. -*- rst -*- +.. -*- coding: utf-8 -*- + +======================================= +Trois manière d'utiliser Gtk avec OCaml +======================================= + +:date: 2014-02-09 +:tags: ocaml, gtk +:summary: |summary| +:logo: static/images/ocaml/camel_75.jpg + +.. default-role:: literal + +.. figure:: |filename|/images/ocaml/camel_2.jpg + :figwidth: 150 + :figclass: floatleft + :alt: Pavement + + Image : `Kevin Botto`_ (creativecommons_) + +.. _Kevin Botto: http://www.flickr.com/photos/kevinbotto/3251157974/ +.. _creativecommons: http://creativecommons.org/licenses/by-nd/2.0/deed.fr + +|summary| + + +.. |summary| replace:: + 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). + +.. _lablgtk: http://lablgtk.forge.ocamlcore.org/ + +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. + +.. _ocaml.org: http://ocaml.org/learn/tutorials/introduction_to_gtk.html + +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 : + +.. code-block:: ocaml + + 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` : + +.. code-block:: console + + $ 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. + +.. image:: |filename|/images/glade.jpg + :class: center + :alt: 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 : + +.. code-block:: make + + %.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` : + +.. code-block:: console + + $ ocamlfind ocamlc -package lablgtk2.glade -I . -c hello.ml + +.. _gtkbuilder: https://developer.gnome.org/gtk3/3.4/GtkBuilder.html + +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 : + +.. code-block:: ocaml + + 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. + +.. _developpez.com: http://blog.developpez.com/damien-guichard/p7748/programmation-fonctionnelle/hello_developpez_avec_libglade_xml_1 + +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 : + +.. code-block:: make + + %.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 : + +.. code-block:: ocaml + + 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… -- cgit v1.2.3