<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>OCaml de pied en cap &#187; LablGTK2</title>
	<atom:link href="https://blog.developpez.com/ocamlblog/pcategory/informatique/lablgtk2/feed" rel="self" type="application/rss+xml" />
	<link>https://blog.developpez.com/ocamlblog</link>
	<description></description>
	<lastBuildDate>Fri, 22 Mar 2013 03:12:26 +0000</lastBuildDate>
	<language>fr-FR</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>https://wordpress.org/?v=4.1.42</generator>
	<item>
		<title>Les fonctions callback dans LablGTK</title>
		<link>https://blog.developpez.com/ocamlblog/p9294/informatique/les_fonctions_callback_dans_lablgtk</link>
		<comments>https://blog.developpez.com/ocamlblog/p9294/informatique/les_fonctions_callback_dans_lablgtk#comments</comments>
		<pubDate>Fri, 17 Sep 2010 07:01:47 +0000</pubDate>
		<dc:creator><![CDATA[Cacophrene]]></dc:creator>
				<category><![CDATA[Informatique]]></category>
		<category><![CDATA[LablGTK2]]></category>
		<category><![CDATA[OCaml]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[Bonjour ! Après un long moment d&#8217;absence et de sérieux doutes sur la pérennité de ce blog, je reviens vous parler de LablGTK. Cette fois-ci, je ne vous présenterai pas un autre module de la bibliothèque. Pour célébrer la renaissance de ce blog, à la suite de mon admission en thèse (dans un domaine très éloigné du sujet de ce blog), je vous propose de parler du mécanisme de callback dans LablGTK. Nous allons aborder [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>Bonjour !</p>
<p><center></p>
<table>
<tr>
<td><a href="http://wwwfun.kurims.kyoto-u.ac.jp/soft/lsl/lablgtk.html" target="_blank"><img src="http://wwwfun.kurims.kyoto-u.ac.jp/soft/lsl/gtk-logo-half.gif"/></a></td>
<td><a href="http://caml.inria.fr/" target="_blank"><img src="http://caml.inria.fr//pub/logos/caml-inria-fr.128x58.gif"/></a></td>
</tr>
</table>
<p></center></p>
<p>Après un long moment d&rsquo;absence et de sérieux doutes sur la pérennité de ce blog, je reviens vous parler de LablGTK. Cette fois-ci, je ne vous présenterai pas un autre module de la bibliothèque. Pour célébrer la renaissance de ce blog, à la suite de mon admission en thèse (dans un domaine très éloigné du sujet de ce blog), je vous propose de parler du mécanisme de callback dans LablGTK.</p>
<p><span id="more-18"></span></p>
<p>Nous allons aborder dans ce billet une manière <em>automatisée</em> d&rsquo;associer des fonctions callback à des widgets. Mais, pour partir sur de bonnes bases, nous allons d&rsquo;abord rappeler ce qu&rsquo;est une fonction callback.</p>
<h3>Le principe d&rsquo;une fonction callback</h3>
<p>Une fonction callback (je ne me risque pas à proposer de traduction) est une fonction qui est communiquée dans un argument passé à une autre fonction. Dans le cas de GTK, un widget interagit avec l&rsquo;utilisateur en émettant des événements auxquels des fonctions peuvent être associées. Ces dernières, on l&rsquo;aura compris, ont pour but de répondre à l&rsquo;événement émis, et donc <em>in fine</em> à l&rsquo;utilisateur. Par exemple, le code suivant est typique :</p>
<pre><strong>let</strong> main_window =
  <strong>let</strong> window = GWindow.window
    ~title:"LablGTK demo"
    ~position:`CENTER
    ~width:640 ~height:480 () <strong>in</strong>
  window#connect#destroy ~callback:GMain.quit;
  window </pre>
<p>Ce code crée la fenêtre principale d&rsquo;une application. On y indique également qu&rsquo;il faut <em>fermer l&rsquo;application</em> (<code class="codecolorer text default"><span class="text">GMain.quit</span></code>) lorsque la fenêtre est détruite (ce qui se traduit par la levée de l&rsquo;événement <code class="codecolorer text default"><span class="text">destroy</span></code>). Dans ce cas, la fonction <code class="codecolorer text default"><span class="text">GMain.quit</span></code> est utilisée comme callback.</p>
<h3>Séparation de l&rsquo;interface et du code</h3>
<p>Pour construire une application, on recommande généralement de dissocier les éléments de l&rsquo;interface et les actions associées. Le code est ainsi mieux structuré et plus facile à maintenir. Par exemple, on peut considérer un module <code class="codecolorer text default"><span class="text">GUI</span></code> qui contient l&rsquo;ensemble des widgets de l&rsquo;interface, et divers modules qui définissent les actions associées (copie, collage, zoom, enregistrement, etc.). Malgré des qualités indéniables, cette approche présente au moins deux inconvénients de taille :</p>
<ul>
<li>Chaque fois que l&rsquo;interface est modifiée (par exemple en ajoutant ou en supprimant un widget), <em>le module qui définit l&rsquo;action correspondante doit être mis à jour</em> pour ajouter ou supprimer un enregistrement de fonction callback et tenir ainsi compte des modifications réalisées en amont.</li>
<li><em>Le module qui définit les actions n&rsquo;est plus autonome</em> : il dépend du module d&rsquo;interface de l&rsquo;application, de sorte que sa réutilisation dans un autre contexte devient plus difficile.</li>
</ul>
<p>Nous allons présenter ici une solution qui résout ces deux problèmes et permet d&rsquo;automatiser l&rsquo;enregistrement des fonctions callback.</p>
<h3>Automatisation du mécanisme de callback</h3>
<h4>Quelques nouveaux types</h4>
<p>Pour automatiser le mécanisme d&rsquo;enregistrement des fonctions callback, nous allons commencer par définir des types qui correspondent à une action particulière, comme l&rsquo;activation d&rsquo;un élément ou un clic gauche de souris. Ce sont :</p>
<pre><strong>class type</strong> clickable = 
  <strong>object</strong>
    <strong>method</strong> clicked : callback:(unit -> unit) -> GtkSignal.id
  <strong>end</strong>

<strong>class type</strong> activatable = 
  <strong>object</strong>
    <strong>method</strong> activate : callback:(unit -> unit) -> GtkSignal.id
  <strong>end</strong>
</pre>
<p>Notez que l&rsquo;on pourrait écrire de très nombreux autres types de ce genre, mais ceux-ci sont les plus fréquents car ils couvrent à la fois les barres d&rsquo;outils et les menus, c&rsquo;est-à-dire les éléments les plus susceptibles d&rsquo;être modifiés dans une interface (on peut aussi voir dans ces définitions, quoique d&rsquo;assez loin, une influence des typeclass de Haskell). On va ensuite définir un type somme pour réunir le tout :</p>
<pre><strong>type</strong> item =
  | C <strong>of</strong> clickable
  | A <strong>of</strong> activatable</pre>
<h4>Stockage des couples identifiant/données</h4>
<p>Nous allons ensuite définir une table de hachage destinée à stocker des identifiants (type <code class="codecolorer text default"><span class="text">string</span></code>) et des données associées. Ces données sont constituées d&rsquo;une liste d&rsquo;objets (<code class="codecolorer text default"><span class="text">item list</span></code>) et d&rsquo;une fonction de callback (de type <code class="codecolorer text default"><span class="text">unit -&gt; unit</span></code>). Comme nous voulons modifier ces valeurs directement, nous allons les stocker sous forme de références :</p>
<pre><strong>val</strong> table : (string, action list ref * (unit -> unit) ref) Hashtbl.t</pre>
<p>Nous allons maintenant définir une fonction <code class="codecolorer text default"><span class="text">register_any</span></code> qui, étant donné une fonction <code class="codecolorer text default"><span class="text">f</span></code>, un identifiant <code class="codecolorer text default"><span class="text">id</span></code> et un widget <code class="codecolorer text default"><span class="text">wid</span></code>, ajoute <code class="codecolorer text default"><span class="text">f wid</span></code> (de type <code class="codecolorer text default"><span class="text">item</span></code>) à la liste des objets associés à l&rsquo;identifiant <code class="codecolorer text default"><span class="text">id</span></code> :</p>
<pre><strong>let</strong> register_any f id wid =
  <strong>let</strong> item = f wid <strong>in</strong>
  <strong>try</strong>
    <strong>let</strong> items = fst (Hashtbl.find table id) <strong>in</strong>
    items := item :: !items
  <strong>with</strong> Not_found -> Hashtbl.add table id (ref [item], ref ignore)</pre>
<p><strong>Remarque</strong> : vous avez peut-être remarqué que, si l&rsquo;identifiant n&rsquo;existe pas dans la table de hachage, les données ajoutées comportent une fonction callback qui ne fait rien (<code class="codecolorer text default"><span class="text">Pervasives.ignore</span></code>). Elle pourra bien sûr être modifiée par la suite !</p>
<h4>Enregistrement des widgets</h4>
<p>Nous pouvons ensuite définir des fonctions spécialisées pour les deux types <code class="codecolorer text default"><span class="text">clickable</span></code> et <code class="codecolorer text default"><span class="text">activatable</span></code> définis précédemment :</p>
<pre><strong>let</strong> register_clickable ~id obj = 
  register_any (<strong>fun</strong> obj -> C (obj#connect :> clickable)) id obj

<strong>let</strong> register_activatable ~id obj = 
  register_any (<strong>fun</strong> obj -> A (obj#connect :> activatable)) id obj</pre>
<p>Ces fonctions ont la signature suivante :</p>
<pre><strong>val</strong> register_clickable : id:string -> <connect : #clickable; ..> -> unit</pre>
<pre><strong>val</strong> register_activatable : id:string -> <connect : #activatable; ..> -> unit</pre>
<p>En d&rsquo;autres termes, elles reçoivent en entrée un identifiant de type <code class="codecolorer text default"><span class="text">string</span></code> et un widget dont la méthode <code class="codecolorer text default"><span class="text">connect</span></code> est un sur-ensemble de la classe <code class="codecolorer text default"><span class="text">clickable</span></code> (ou <code class="codecolorer text default"><span class="text">activatable</span></code>, respectivement).</p>
<h4>Enregistrement des fonctions de callback</h4>
<p>Il faut ensuite écrire une fonction capable d&rsquo;enregistrer les fonctions callback. Pour simplifier son utilisation, on souhaite qu&rsquo;elle renvoie en sortie la fonction reçue en entrée :</p>
<pre><strong>let</strong> register_callback ~id f =
  <strong>try</strong> snd (Hashtbl.find table id) := f; f <strong>with</strong> Not_found -> f</pre>
<p>Cette fonction est de type :</p>
<pre><strong>val</strong> register_callback : id:string -> (unit -> unit) -> unit -> unit</pre>
<h4>Association des fonctions aux widgets</h4>
<p>Il ne reste plus qu&rsquo;à définir la fonction qui associe les callback au lancement de l&rsquo;application :</p>
<pre><strong>let</strong> connect_all () =
  Hashtbl.iter (<strong>fun</strong> _ ({contents = t}, {contents = f}) ->
    List.iter (<strong>function</strong>
      | C connect -> ignore (connect#clicked ~callback:f)
      | A connect -> ignore (connect#activate ~callback:f)
    ) t
  ) table</pre>
<p><strong>Rappel</strong> : en OCaml, les références peuvent être vues comme des valeurs de type record avec un champ unique appelé <code class="codecolorer text default"><span class="text">contents</span></code>. On peut donc filtrer les références en écrivant <code class="codecolorer text default"><span class="text">{contents = x}</span></code>, où <code class="codecolorer text default"><span class="text">x</span></code> désigne le contenu de la référence.</p>
<p>Cette fonction présente une signature très simple :</p>
<pre><strong>val</strong> connect_all : unit -> unit</pre>
<h3>Utilisation du module dans le programme</h3>
<p>L&rsquo;utilisation de ce module dans un programme se déroule en trois temps :</p>
<ul>
<li>Tout d&rsquo;abord, les éléments de l&rsquo;interface sont créés et enregistrés avec les fonctions <code class="codecolorer text default"><span class="text">register_clickable</span></code> ou <code class="codecolorer text default"><span class="text">register_activatable</span></code>. À cet effet, un identifiant spécifique est nécessaire&#8230; mais celui-ci peut être recyclé avantageusement et servir pour les plugins, voire pour la traduction de l&rsquo;application en plusieurs langues.</li>
<li>Ensuite, les fonctions callback sont enregistrées avec <code class="codecolorer text default"><span class="text">register_callback</span></code> et le même identifiant que précédemment. Les modules qui les contiennent restent indépendants du module d&rsquo;interface. Accessoirement, les équipes de développeurs qui se parlent peu auront moins de surprises lors de la mise en commun de leur code ;-).</li>
<li>Enfin, au lancement de l&rsquo;application, on connecte tous les widgets à leurs fonctions respectives à l&rsquo;aide d&rsquo;un unique appel à <code class="codecolorer text default"><span class="text">connect_all</span></code>. On n&rsquo;oublie personne !</li>
</ul>
<p>On peut bien entendu imaginer diverses généralisations plus ou moins complexes de ce procédé, notamment au cas des fonctions callback multiples. On aurait alors une table de hachage de type :</p>
<pre><strong>val</strong> table : (string, action list ref * (unit -> unit) list ref) Hashtbl.t</pre>
<p>et la fonction <code class="codecolorer text default"><span class="text">connect_all</span></code> deviendrait :</p>
<pre>let connect_all () =
  Hashtbl.iter (<strong>fun</strong> _ ({contents = obj_list}, {contents = fun_list}) ->
    List.iter (<strong>fun</strong> item ->
      <strong>let</strong> f = <strong>match</strong> item <strong>with</strong>
        | C connect -> (<strong>fun</strong> f -> ignore (connect#clicked ~callback:f))
        | A connect -> (<strong>fun</strong> f -> ignore (connect#activate ~callback:f))
      <strong>in</strong> List.iter f fun_list
    ) obj_list
  ) table</pre>
<p><strong>Remarque</strong> : il commence à y avoir beaucoup d&rsquo;itérateurs imbriqués. Il faut bien voir qu&rsquo;il s&rsquo;agit d&rsquo;appliquer les fonctions associées à l&rsquo;identifiant <code class="codecolorer text default"><span class="text">id</span></code> à <em>chacun</em> des éléments enregistrés.</p>
<h3>En conclusion&#8230;</h3>
<p>D&rsquo;abord, un lien vers le <a href="http://ocaml.pastebin.com/P6fnJVpW"><strong>code complet</strong></a> et l&rsquo;<a href="http://ocaml.pastebin.com/WBC4Zgc3"><strong>interface complète</strong></a>.</p>
<p>Il est certain que l&rsquo;intérêt d&rsquo;un tel module se ressent surtout dans de moyennes ou grosses applications, dont l&rsquo;interface est complexe et présente un certain niveau de redondance. Je ne pense pas qu&rsquo;il soit raisonnable de l&rsquo;utiliser pour écrire un pong.</p>
<p>Voilà, c&rsquo;est tout pour ajourd&rsquo;hui !</p>
<p>Cordialement,<br />
Cacophrène</p>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Aller simple pour le pays des fantômes</title>
		<link>https://blog.developpez.com/ocamlblog/p8646/informatique/aller_simple_pour_le_pays_des_fantomes</link>
		<comments>https://blog.developpez.com/ocamlblog/p8646/informatique/aller_simple_pour_le_pays_des_fantomes#comments</comments>
		<pubDate>Fri, 19 Feb 2010 20:04:04 +0000</pubDate>
		<dc:creator><![CDATA[Cacophrene]]></dc:creator>
				<category><![CDATA[Biologie]]></category>
		<category><![CDATA[Camlimages]]></category>
		<category><![CDATA[Informatique]]></category>
		<category><![CDATA[LablGTK2]]></category>
		<category><![CDATA[OCaml]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[Après un long moment d&#8217;absence, je reviens pour vous parler d&#8217;une notion dont l&#8217;intérêt m&#8217;a été révélé par un article sur le blog de bluestorm. Il s&#8217;agit des types fantômes, dont je souhaite vous montrer ici tout le bien. Naturellement, je vais partir d&#8217;un cas concret autour de ce que je développe actuellement. Le contexte Une application pour microscopistes Je suis actuellement en train d&#8217;écrire les grandes lignes d&#8217;un programme destiné à des microscopistes amateurs, [&#8230;]]]></description>
				<content:encoded><![CDATA[<p><center></p>
<table>
<tr>
<td><a href="http://wwwfun.kurims.kyoto-u.ac.jp/soft/lsl/lablgtk.html" target="_blank"><img src="http://wwwfun.kurims.kyoto-u.ac.jp/soft/lsl/gtk-logo-half.gif"/></a></td>
<td><a href="http://pauillac.inria.fr/camlimages/" target="_blank"><img src="http://pauillac.inria.fr/camlimages/fumicaml2-small.jpg"/></a></td>
<td><a href="http://caml.inria.fr/" target="_blank"><img src="http://caml.inria.fr//pub/logos/caml-inria-fr.128x58.gif"/></a></td>
</tr>
</table>
<p></center></p>
<p>Après un long moment d&rsquo;absence, je reviens pour vous parler d&rsquo;une notion dont l&rsquo;intérêt m&rsquo;a été révélé par un article sur le blog de <a href="http://blog.huoc.org/7-en-bref-aussi.html" target="_blank">bluestorm</a>. Il s&rsquo;agit des <strong>types fantômes</strong>, dont je souhaite vous montrer ici tout le bien. Naturellement, je vais partir d&rsquo;un cas concret autour de ce que je développe actuellement.</p>
<p><span id="more-7"></span></p>
<h3>Le contexte</h3>
<h4>Une application pour microscopistes</h4>
<p>Je suis actuellement en train d&rsquo;écrire les grandes lignes d&rsquo;un programme destiné à des microscopistes amateurs, dont il existe déjà une version en Visual Basic (à réécrire et faire évoluer). Ce programme doit permettre de mettre en forme des images pour un forum en leur ajoutant un cartouche contenant diverses informations utiles (échelle, type de matériel, coloration éventuelle, etc.). Je vous donne une image ci-dessous pour vous faire une idée. Une caractéristique importante de ce logiciel réside dans sa capacité à dessiner des échelles à partir d&rsquo;une relation entre un nombre X de pixels et une taille Y en micromètres, déterminée sur une image étalon.</p>
<p><center><img src="http://blog.developpez.com/media/Helvella_leucomelaena-1_small.jpg" width="800" height="600" alt="" /><br />
<strong>Spores d&rsquo;helvelle après coloration.<br />
Objectif Zeiss Planapo 100/1.3</strong><br />
</center></p>
<h4>Image d&rsquo;étalon</h4>
<p>Une image étalon est une photo dont les éléments ont une taille connue, prise dans les mêmes conditions qu&rsquo;une image classique. Le plus souvent, il s&rsquo;agit d&rsquo;une « règle » dont les graduations sont espacées de 10 µm (la bagatelle de 100 graduations dans un millimètre&#8230;). Le programme charge l&rsquo;image d&rsquo;étalon, l&rsquo;analyse à la manière d&rsquo;un outil de reconnaissance de caractères (en beaucoup plus simple !), et en déduit la taille d&rsquo;une graduation, exprimée en pixels. De son côté, l&rsquo;utilisateur indique la taille réelle correspondante, en micromètres cette fois-ci <em>(cf. l&rsquo;image ci-dessous)</em>.</p>
<p><center><img src="http://blog.developpez.com/media/IMG_0029_small.JPG" width="800" height="533" alt="Micromètre" /><br />
<strong>Image d&rsquo;étalon (1 graduation correspond à 10 µm).<br />
Objectif Zeiss Plan Neofluar 63/1.25</strong><br />
</center></p>
<h3>Manipulation d&rsquo;images</h3>
<p>Pour automatiser la reconnaissance des graduations, on souhaite implémenter l&rsquo;algorithme suivant (plutôt une recette de cuisine) :</p>
<ul>
<li>Charger l&rsquo;image en couleur (24 bits).</li>
<li>La transformer en niveaux de gris.</li>
<li>Augmenter la luminosité pour éclaircir le fond.</li>
<li>Passer en noir et blanc.</li>
<li>Effacer les poussières pour uniformiser le fond.</li>
<li>Reboucher les trous dans les traits de graduations.</li>
<li>Mesurer les traits de graduation et les espaces qu&rsquo;ils délimitent.</li>
<li>En déduire la taille d&rsquo;une graduation (un espace + un trait).</li>
</ul>
<p>Pour y parvenir, nous allons utiliser <a href="http://wwwfun.kurims.kyoto-u.ac.jp/soft/lsl/lablgtk.html">LablGTK</a> et <a href="http://pauillac.inria.fr/camlimages/">CamlImages</a>, deux bibliothèques qui dialoguent plutôt bien. Nous allons nous fixer comme objectif d&rsquo;écrire un type <code class="codecolorer text default"><span class="text">picture</span></code> polymorphe. Nous nous en servirons pour distinguer les images en couleur (24 bits), en niveaux de gris et en noir et blanc. Les fonctions telles que <code class="codecolorer text default"><span class="text">get_width</span></code> ou <code class="codecolorer text default"><span class="text">get_pixbuf</span></code> s&rsquo;appliqueront à tout type d&rsquo;image. </p>
<h3>Choix d&rsquo;une interface</h3>
<p>Pour faire court, nous voudrions quelque chose comme ceci :</p>
<pre><strong>type</strong> 'a picture

<strong>type</strong> fullcolor
<strong>type</strong> grayscale
<strong>type</strong> monochrom

<strong>val</strong> from_file : string -> fullcolor picture
<em>(** Charge une image en couleurs. *)</em>

<em>(** Fonctions usuelles, valables pour tout type d'image. *)</em>
<strong>val</strong> copy : 'a picture -> 'a picture
<strong>val</strong> get_width : 'a picture -> int
<strong>val</strong> get_height : 'a picture -> int
<strong>val</strong> get_pixbuf : 'a picture -> GdkPixbuf.pixbuf

<strong>val</strong> grayscale : 
  ?filter:(Color.rgb -> int) -> 
  fullcolor picture -> grayscale picture
<em>(** Conversion en niveaux de gris d'une image couleur (24 bits). *)</em>

<strong>val</strong> black_and_white :
  ?threshold:int ->
  grayscale picture -> monochrom picture
<em>(** Conversion en noir et blanc d'une image en niveaux de gris. *)</em>

<strong>val</strong> noise_reduction :
  ?threshold:int ->
  monochrom picture -> monochrom picture
<em>(** Effacement des poussières sur une image en noir et blanc. *)</em>

<strong>val</strong> fill_gaps :
  ?threshold:int ->
  monochrom picture -> monochrom picture
<em>(** Rebouchage des trous sur une image en noir et blanc. *)</em></pre>
<p>Pour que cet algorithme ne reste pas nébuleux, voici <a href="http://www.box.net/shared/88aynv0ory" target="_blank">un PDF d&rsquo;illustration</a>.</p>
<h3>Du côté de l&rsquo;implémentation</h3>
<p>Après l&rsquo;interface, voyons l&rsquo;implémentation. <em>A priori</em>, cela peut sembler difficile. Pourtant, grâce aux types fantômes, nous n&rsquo;avons presque rien à faire ! Définissons d&rsquo;abord le type <code class="codecolorer text default"><span class="text">picture</span></code> :</p>
<pre><strong>type</strong> 'a picture = {pict: OImages.rgb24_class}</pre>
<p>Ne vous étonnez pas de trouver ici un record alors qu&rsquo;il paraît inutile. Le vrai type <code class="codecolorer text default"><span class="text">picture</span></code>, que j&rsquo;utilise dans mon application, contient d&rsquo;autres champs (mais <strong>aucun</strong> n&rsquo;est de type <code class="codecolorer text default"><span class="text">'a</span></code>, c&rsquo;est très important). Il a été simplifié ici par souci de pédagogie.</p>
<p>Définissons maintenant trois types fantômes :</p>
<pre><strong>type</strong> fullcolor
<strong>type</strong> grayscale
<strong>type</strong> monochrom</pre>
<p><font color="#000080">Un <strong>type fantôme</strong> est un <strong>type utilisé comme paramètre d&rsquo;un autre type</strong> (à la manière du type <code class="codecolorer text default"><span class="text">int</span></code> dans <code class="codecolorer text default"><span class="text">int list</span></code>), mais <strong>qui ne sert pas dans la définition de ce type</strong> (comme <code class="codecolorer text default"><span class="text">'a</span></code> dans <code class="codecolorer text default"><span class="text">'a picture</span></code> ci-dessus). Le type fantôme vous permet d&rsquo;ajouter une information (une <em>contrainte</em>) qui sera propagée correctement par l&rsquo;algorithme d&rsquo;inférence de types, sans impact sur l&rsquo;implémentation. Dans mon cas, il s&rsquo;agit surtout de s&rsquo;assurer que l&rsquo;on passe bien une image en niveaux de gris à la fonction <code class="codecolorer text default"><span class="text">black_and_white</span></code>, par exemple.</font></p>
<p>Nous pouvons maintenant écrire la fonction de création :</p>
<pre><strong>let</strong> from_file str = {pict = OImages.rgb24 (OImages.load path [])}</pre>
<p>puis les fonctions utilitaires :</p>
<pre><strong>let</strong> get_width t = t.pict#width
<strong>let</strong> get_height t = t.pict#height
<strong>let</strong> get_pixbuf t = Imagegdk.to_pixbuf t.pict#coerce
<strong>let</strong> copy t = {t with pict = Oo.copy t.pict}</pre>
<p>Nous pouvons maintenant en venir aux fonctions qui agissent directement sur l&rsquo;image et, comme vous allez le constater, on ne se doute de rien en lisant cette partie du code  :</p>
<pre><strong>let</strong> grayscale ?(filter = Color.brightness) pic =
  <strong>let</strong> res = copy pic <strong>in</strong>
  <strong>let</strong> img = res.pict <strong>in</strong>
  <strong>for</strong> x = 0 <strong>to</strong> img#width - 1 <strong>do</strong>
    <strong>for</strong> y = 0 <strong>to</strong> img#height - 1 <strong>do</strong>
      <strong>let</strong> res = filter (img#unsafe_get x y) <strong>in</strong>
      img#unsafe_set x y {Color.r = res; g = res; b = res}
    <strong>done</strong>
  <strong>done</strong>;
  res

<strong>let</strong> black = {Color.r = 0; g = 0; b = 0}
<strong>let</strong> white = {Color.r = 255; g = 255; b = 255}

<strong>let</strong> black_and_white ?(threshold = 0x90) pic =
  <strong>let</strong> res = copy pic <strong>in</strong>
  <strong>let</strong> img = res.pict <strong>in</strong>
  <strong>for</strong> x = 0 <strong>to</strong> img#width - 1 <strong>do</strong>
    <strong>for</strong> y = 0 <strong>to</strong> img#height - 1 <strong>do</strong>
      <strong>let</strong> rgb = img#unsafe_get x y <strong>in</strong>
      img#unsafe_set x y (<strong>if</strong> rgb.Color.r > threshold <strong>then</strong> white <strong>else</strong> black)
    <strong>done</strong>
  <strong>done</strong>;
  res</pre>
<p>et ainsi de suite (le détail d&rsquo;implémentation des fonctions <code class="codecolorer text default"><span class="text">&nbsp;</span></code> et <code class="codecolorer text default"><span class="text">fill_gaps</span></code> sort du cadre de ce billet; je peux développer sur demande, mais il ne me semble pas pertinent de le faire ici). Avec un appel à <code class="codecolorer text default"><span class="text">ocamlc -i</span></code>, toutes ces fonctions accepteraient des images de type <code class="codecolorer text default"><span class="text">'a picture</span></code>. La contrainte que nous avons posée dans l&rsquo;interface est à la fois valide et transparente du côté de l&rsquo;implémentation.</p>
<p>Il est intéressant de noter que les types fantômes apportent une information supplémentaire sans impact sur l&rsquo;implémentation. Cela renforce la sûreté du code sans nuire aux performances (imaginez la lourdeur qui consisterait à s&rsquo;assurer d&rsquo;abord que tous les pixels sont bien des teintes de gris !).</p>
<h3>En guise de conclusion&#8230;</h3>
<p>Je parlais d&rsquo;un <em>aller simple</em> vers le monde des fantômes. Vous savez maintenant pourquoi : quand on y a goûté, on ne peut plus s&rsquo;en passer. <strong>Les types fantômes, c&rsquo;est bien, mangez-en !</strong></p>
<p>À bientôt,<br />
Cacophrène</p>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Exemple d&#8217;utilisation du widget GtkSourceView 2.0</title>
		<link>https://blog.developpez.com/ocamlblog/p8057/informatique/title_145</link>
		<comments>https://blog.developpez.com/ocamlblog/p8057/informatique/title_145#comments</comments>
		<pubDate>Sun, 13 Sep 2009 18:35:16 +0000</pubDate>
		<dc:creator><![CDATA[Cacophrene]]></dc:creator>
				<category><![CDATA[Informatique]]></category>
		<category><![CDATA[LablGTK2]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[Bonjour ! Je vous ai parlé, dans le précédent billet, de l&#8217;intégration du widget GtkSourceView 2.0 dans la bibliothèque LablGTK. Voyons ici ce qui change par rapport à l&#8217;ancien widget. Voici ci-dessous un petit code qui permet de voir quelques-unes des nouveautés de GtkSourceView 2.0, entre autres les thèmes (GtkStyleScheme) et la matérialisation des espaces. Un thème est choisi aléatoirement au lancement de l&#8217;application (évidemment, plus vous en avez et plus il y aura de [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>Bonjour !</p>
<p><center><a href="http://wwwfun.kurims.kyoto-u.ac.jp/soft/lsl/lablgtk.html" target="_blank"><img src="http://wwwfun.kurims.kyoto-u.ac.jp/soft/lsl/gtk-logo-half.gif"/></a></center></p>
<p>Je vous ai parlé, dans le précédent billet, de l&rsquo;intégration du widget <strong>GtkSourceView 2.0</strong> dans la bibliothèque LablGTK. Voyons ici ce qui change par rapport à l&rsquo;ancien widget.</p>
<p><span id="more-13"></span></p>
<p>Voici ci-dessous un petit code qui permet de voir quelques-unes des nouveautés de <strong>GtkSourceView 2.0</strong>, entre autres les thèmes (<strong>GtkStyleScheme</strong>) et la matérialisation des espaces. Un thème est choisi aléatoirement au lancement de l&rsquo;application (évidemment, plus vous en avez et plus il y aura de visuels possibles&#8230;). Pour tester, placez le code suivant dans un fichier <strong>test.ml</strong>, puis compilez avec :</p>
<p><center>ocamlopt -w s -I +lablgtk2 lablgtk.cmxa lablgtksourceview2.cmxa test.ml -o test</center></p>
<p>Si vous voulez utiliser un nom différent, n&rsquo;oubliez pas de modifier la fonction <strong>read</strong> dans le code source !</p>
<pre><strong>let</strong> window =
  GMain.init ();
  <strong>let</strong> wnd = GWindow.window 
    ~title:"GtkSourceView 2.0 Demo" 
    ~position:`CENTER 
    ~resizable:<strong>false</strong>  
    ~width:800 ~height:600 () in 
  wnd#connect#destroy GMain.quit; 
  wnd 
 
<strong>let</strong> ocaml = 
  <strong>let</strong> manager = GSourceView2.source_language_manager ~default:<strong>true</strong> <strong>in</strong> 
  <strong>match</strong> manager#language "objective-caml" <strong>with</strong> 
  | None -> failwith "La coloration syntaxique pour OCaml n'est pas disponible !" 
  | some -> some
 
<strong>let</strong> manager = GSourceView2.source_style_scheme_manager ~default:<strong>true</strong> 
<strong>let</strong> schemes = Array.of_list manager#style_scheme_ids 
 
<strong>let</strong> get_random_style_scheme () = 
  Random.self_init (); 
  <strong>match</strong> manager#style_scheme schemes.(Random.int (Array.length schemes)) <strong>with</strong> 
  | None -> <strong>assert false</strong> 
  | some -> some 
 
<strong>let</strong> scroll = GBin.scrolled_window 
  ~hpolicy:`ALWAYS 
  ~vpolicy:`ALWAYS 
  ~packing:window#add () 
 
<strong>let</strong> source = 
  <strong>let</strong> src = GSourceView2.source_view 
    ~draw_spaces:[`NEWLINE; `SPACE] <em>(* Matérialisation des espaces. *)</em> 
    ~auto_indent:<strong>true</strong> 
    ~indent_on_tab:<strong>true</strong> <em>(* Quelques étiquettes ont été renommées *)</em> 
    ~indent_width:2 
    ~insert_spaces_instead_of_tabs:<strong>true</strong> 
    ~right_margin_position:80 
    ~show_line_numbers:<strong>true</strong> 
    ~show_right_margin:<strong>true</strong> 
    ~packing:scroll#add () <strong>in</strong> 
  src#misc#modify_font_by_name "Monospace 10"; 
  <strong>let</strong> buf = src#source_buffer <strong>in</strong> 
  buf#set_language ocaml; 
  buf#set_highlight_syntax <strong>true</strong>; 
  buf#set_style_scheme (get_random_style_scheme ()); 
  src 
 
<strong>let</strong> read () = 
  <strong>let</strong> ich = open_in "test.ml" <strong>in</strong> 
  <strong>let</strong> len = in_channel_length ich <strong>in</strong> 
  <strong>let</strong> str = String.create len <strong>in</strong> 
  really_input ich str 0 len; 
  close_in ich; 
  Glib.Convert.locale_to_utf8 str 
 
<strong>let</strong> _ = 
  source#source_buffer#set_text (read ()); 
  window#show (); 
  GMain.main ()</pre>
<p>Ce code nous donne quelque chose comme ça :</p>
<p><center><br />
<img src="http://blog.developpez.com/media/GtkSourceView2.png" width="810" height="629" alt="" /><br />
</center></p>
<p>Le nouveau widget bénéficie d&rsquo;une interface plus agréable à utiliser, avec des étiquettes plus parlantes qu&rsquo;auparavant. Il ne lui manque plus que la factorisation du code (<em>code folding</em>) et l&rsquo;auto-complétion, qui sont sans doute les deux fonctionnalités les plus attendues pour les prochaines versions.</p>
<p>À bientôt,<br />
Cacophrène</p>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>LablGTK &#8211; Le module GPack</title>
		<link>https://blog.developpez.com/ocamlblog/p8552/informatique/lablgtk_le_module_gpack</link>
		<comments>https://blog.developpez.com/ocamlblog/p8552/informatique/lablgtk_le_module_gpack#comments</comments>
		<pubDate>Sat, 23 Jan 2010 12:57:10 +0000</pubDate>
		<dc:creator><![CDATA[Cacophrene]]></dc:creator>
				<category><![CDATA[Informatique]]></category>
		<category><![CDATA[LablGTK2]]></category>
		<category><![CDATA[OCaml]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[Bonjour ! Je vous propose de poursuivre notre exploration de la bibliothèque LablGTK. Dans ce billet, nous allons parler du module GPack. Celui-ci contient tous les widgets nécessaires à la réalisation d&#8217;interfaces complexes. La plupart d&#8217;entre eux sont transparents pour l&#8217;utilisateur, car ils n&#8217;ont pas de représentation graphique. Pourtant, comme nous allons le voir ici, ce sont de précieux alliés ! Introduction Nous avons vu dans un précédent billet que l&#8217;élément principal d&#8217;une interface est [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>Bonjour !</p>
<p><center><a href="http://wwwfun.kurims.kyoto-u.ac.jp/soft/lsl/lablgtk.html" target="_blank"><img src="http://wwwfun.kurims.kyoto-u.ac.jp/soft/lsl/gtk-logo-half.gif"/></a></center></p>
<p>Je vous propose de poursuivre notre exploration de la bibliothèque LablGTK. Dans ce billet, nous allons parler du module <code class="codecolorer text default"><span class="text">GPack</span></code>. Celui-ci contient tous les widgets nécessaires à la réalisation d&rsquo;interfaces complexes. La plupart d&rsquo;entre eux sont transparents pour l&rsquo;utilisateur, car ils n&rsquo;ont pas de représentation graphique. Pourtant, comme nous allons le voir ici, ce sont de précieux alliés !</p>
<p><span id="more-17"></span></p>
<h3>Introduction</h3>
<p>Nous avons vu dans <a href="http://blog.developpez.com/ocamlblog/p8477/programmation-fonctionnelle/lablgtk2/title-157/#more8477" target="_blank">un précédent billet</a> que l&rsquo;élément principal d&rsquo;une interface est généralement une fenêtre (GtkWindow). Or ce widget ne peut contenir qu&rsquo;<em>un seul</em> widget enfant. Cela semble, a priori, une limitation majeure. Pourtant, GTK permet de réaliser des interfaces arbitrairement complexes. Il est donc possible de lever cette limitation. Pour cela, GTK définit des conteneurs, c&rsquo;est-à-dire des widgets dont la fonction principale est de contenir d&rsquo;autres widgets. Ces conteneurs peuvent être imbriqués à souhait et constituent donc une solution à la fois élégante et souple.</p>
<h3>Les boîtes horizontales (<a href="http://library.gnome.org/devel/gtk/unstable/GtkHBox.html" target="_blank">GtkHBox</a>)</h3>
<p><center><img src="http://blog.developpez.com/media/ocamlblog-lablgtk-hbox_01.png" width="156" height="126" alt="" /></center></p>
<p>Ces boîtes permettent d&rsquo;insérer des widgets <strong>horizontalement</strong>. Les enfants sont insérés avec la méthode <code class="codecolorer text default"><span class="text">#add</span></code>, ou avec <code class="codecolorer text default"><span class="text">#pack</span></code> si l&rsquo;on souhaite paramétrer finement le style d&rsquo;insertion (entre autres, la capacité du widget enfant à être élargi pour remplir l&rsquo;espace disponible). Voyez par exemple :</p>
<pre><strong>let</strong> window =
  GMain.init ();
  <strong>let</strong> wnd = GWindow.window ~width:150 ~height:100 () <strong>in</strong>
  wnd#connect#destroy GMain.quit;
  wnd

<strong>let</strong> packing = 
  <strong>let</strong> hbox = GPack.hbox
    ~spacing:5 <em>(* Les enfants sont espacés de 5 pixels. *)</em>
    ~border_width:5 <em>(* La boîte possède une bordure de 5 pixels. *)</em>
    ~packing:window#add () <strong>in</strong>
  hbox#pack ~expand:<strong>false</strong>

<strong>let</strong> _ = 
  List.iter (<strong>fun</strong> text -> ignore (GMisc.label ~packing ~text ())) ["foo"; "bar"];
  window#show ();
  GMain.main ()</pre>
<p><strong>Pour info</strong> : cet exemple, comme les suivants, est complet et peut-être testé dans l&rsquo;interpréteur après avoir chargé la bibliothèque LablGTK.  </p>
<h3>Les boîtes verticales (<a href="http://library.gnome.org/devel/gtk/unstable/GtkVBox.html" target="_blank">GtkVBox</a>)</h3>
<p><center><img src="http://blog.developpez.com/media/ocamlblog-lablgtk-vbox.png" width="156" height="126" alt="" /></center></p>
<p>Les boîtes verticales fonctionnent de la même manière que les boîtes horizontales, à un détail près : les widgets enfants sont insérés <strong>verticalement</strong>.</p>
<pre><strong>let</strong> window =
  GMain.init ();
  <strong>let</strong> wnd = GWindow.window ~width:150 ~height:100 () <strong>in</strong>
  wnd#connect#destroy GMain.quit;
  wnd

<strong>let</strong> packing = 
  <strong>let</strong> vbox = GPack.vbox
    ~spacing:5 <em>(* Les enfants sont espacés de 5 pixels. *)</em>
    ~border_width:5 <em>(* La boîte possède une bordure de 5 pixels. *)</em>
    ~packing:window#add () <strong>in</strong>
  vbox#pack ~expand:<strong>false</strong>

<strong>let</strong> _ = 
  List.iter (<strong>fun</strong> text -> ignore (GMisc.label ~packing ~text ())) ["foo"; "bar"];
  window#show ();
  GMain.main ()</pre>
<h3>Les tableaux (<a href="http://library.gnome.org/devel/gtk/unstable/GtkTable.html" target="_blank">GtkTable</a>)</h3>
<p><center><img src="http://blog.developpez.com/media/ocamlblog-lablgtk-table.png" width="332" height="227" alt="" /></center></p>
<p>Les tableaux (GtkTable) sont un moyen pratique d&rsquo;organiser des widgets sous forme de tableaux (on s&rsquo;en serait douté&#8230;). Ils peuvent être utilisés, par exemple, pour créer des palettes. Les widgets sont insérés à l&rsquo;aide de la méthode <code class="codecolorer text default"><span class="text">#attach</span></code> en indiquant l&rsquo;emplacement des coins gauche (<code class="codecolorer text default"><span class="text">left</span></code>) et haut (<code class="codecolorer text default"><span class="text">top</span></code>) du widget.</p>
<pre><strong>let</strong> window =
  GMain.init ();
  <strong>let</strong> wnd = GWindow.window () <strong>in</strong>
  wnd#connect#destroy GMain.quit;
  wnd

<strong>let</strong> attach = 
  <strong>let</strong> table = GPack.table 
    ~rows:4 <em>(* Nombre de rangées. *)</em>
    ~columns:4 <em>(* Nombre de colonnes. *)</em>
    ~row_spacings:2 <em>(* Espacement des éléments d'une même rangée. *)</em>
    ~col_spacings:2 <em>(* Espacement des éléments d'une même colonne. *)</em>
    ~packing:window#add () <strong>in</strong>
  <strong>fun</strong> i -> table#attach ~left:(i <strong>mod</strong> 4) ~top:(i / 4)

<strong>let</strong> _ = 
  Random.self_init ();
  <strong>for</strong> i = 0 <strong>to</strong> 24 <strong>do</strong>
    <strong>let</strong> r = Random.int 256 <strong>and</strong> g = Random.int 256 <strong>and</strong> b = Random.int 256 <strong>in</strong>
    <strong>let</strong> text = Printf.sprintf "#%02x%02x%02x" r g b <strong>in</strong>
    <strong>let</strong> entry = GEdit.entry ~text ~width:80 ~packing:(attach i) () <strong>in</strong>
    entry#misc#modify_base [`NORMAL, `NAME text]
  <strong>done</strong>;
  window#show ();
  GMain.main ()</pre>
<h3>Les boîtes pour boutons (<a href="http://library.gnome.org/devel/gtk/unstable/GtkButtonBox.html" target="_blank">GtkButtonBox</a>)</h3>
<p><center><img src="http://blog.developpez.com/media/ocamlblog-lablgtk-button_box.png" width="306" height="176" alt="" /></center></p>
<p>GTK fournit une boîte spécialement conçue pour recevoir des boutons : il s&rsquo;agit de GtkButtonBox, ou <code class="codecolorer text default"><span class="text">GPack.button_box</span></code> dans LablGTK. Elle est conçue pour vous permettre d&rsquo;organiser des boutons à la manière des boîtes de dialogue :</p>
<pre><strong>let</strong> window =
  GMain.init ();
  <strong>let</strong> wnd = GWindow.window
    ~title:"Button box demo"
    ~position:`CENTER 
    ~resizable:<strong>false</strong>
    ~width:300 ~height:150 () in
  wnd#connect#destroy GMain.quit;
  wnd

<strong>let</strong> vbox = GPack.vbox 
  ~spacing:2 
  ~border_width:2
  ~packing:window#add ()

<strong>let</strong> view = GText.view ~packing:vbox#add ()

<strong>let</strong> bbox = GPack.button_box `HORIZONTAL
  ~layout:`EDGE <em>(* C'est ici que l'on choisit la disposition des boutons. *)</em>
  ~border_width:2
  ~packing:(vbox#pack ~expand:<strong>false</strong>) ()

<strong>let</strong> help = GButton.button ~stock:`HELP ~packing:bbox#add ()
<strong>let</strong> quit = GButton.button ~stock:`QUIT ~packing:bbox#add ()

<strong>let</strong> _ =
  window#show ();
  GMain.main ()</pre>
<h3>Autres éléments</h3>
<p>Le module GPack définit d&rsquo;autres widgets, notamment les <strong>pages à onglets</strong> (<a href="http://library.gnome.org/devel/gtk/unstable/GtkNotebook.html" target="_blank">GtkNotebook</a>) et les <strong>panneaux mobiles</strong> (<a href="http://library.gnome.org/devel/gtk/unstable/GtkPaned.html" target="_blank">GtkPaned</a>). En raison de leur intérêt tout particulier dans la réalisation d&rsquo;interfaces, nous y consacrerons un billet entier.</p>
<h3>Atelier : Le triangle de Pascal</h3>
<p>Pour terminer, je vous propose de réaliser une petite application qui affiche à l&rsquo;écran les coefficients binomiaux en les présentant sous la forme du célèbre <a href="http://fr.wikipedia.org/wiki/Triangle_de_Pascal" target="_blank">triangle de Pascal</a>. Voici à quoi devra ressembler notre application :</p>
<p><center><img src="http://blog.developpez.com/media/ocamlblog-lablgtk-atelier-triangle-Pascal.png" width="406" height="326" alt="" /></center></p>
<p>Pour réaliser notre application, nous avons besoin d&rsquo;une fenêtre :</p>
<pre><strong>let</strong> window =
  <strong>let</strong> wnd = GWindow.window
    ~title:"GPack demo"
    ~position:`CENTER
    ~resizable:<strong>false</strong>
    ~width:400 ~height:300 () in
  wnd#connect#destroy GMain.quit;
  wnd</pre>
<p>Celle-ci doit recevoir deux éléments distincts : une fenêtre assortie de barres de défilement, pour recevoir le triangle, et un bouton de fermeture. Nous allons donc commencer par insérer une boîte verticale dans <code class="codecolorer text default"><span class="text">window</span></code> :</p>
<pre><strong>let</strong> vbox = GPack.vbox 
  ~border_width:2 
  ~spacing:2 
  ~packing:window#add ()</pre>
<p>Nous pouvons alors ajouter les barres de défilement pour permettre à l&rsquo;utilisateur d&rsquo;explorer les lignes du triangle :</p>
<pre><strong>let</strong> scroll = GBin.scrolled_window
  ~hpolicy:`ALWAYS
  ~vpolicy:`ALWAYS
  ~packing:window#add ()</pre>
<p>Ces barres de défilement ajoutées avec <code class="codecolorer text default"><span class="text">GBin.scrolled_window</span></code> ne résolvent pas le problème de <code class="codecolorer text default"><span class="text">GWindow.window</span></code>, car l&rsquo;une comme l&rsquo;autre ne peuvent contenir qu&rsquo;un seul widget enfant. Pour lever cette limitation, nous devons donc insérer une boîte verticale à l&rsquo;intérieur des barres de défilement. Ici, comme il doit être possible de faire défiler le contenu de la boîte, on utilise la méthode <code class="codecolorer text default"><span class="text">#add_with_viewport</span></code> au lieu du classique <code class="codecolorer text default"><span class="text">#add</span></code>. Ce qui nous donne :</p>
<pre><strong>let</strong> vbox, packing = 
  <strong>let</strong> vbox = GPack.vbox
    ~spacing:2
    ~packing:scroll#add_with_viewport () <strong>in</strong>
  vbox, vbox#pack ~expand:<strong>false</strong></pre>
<p>Écrivons maintenant une fonction spécialisée (par application partielle) de <code class="codecolorer text default"><span class="text">GMisc.label</span></code> pour nous simplifier la tâche :</p>
<pre><strong>let</strong> label = GMisc.label ~height:50 ~width:50 ~xalign:0.5</pre>
<p>Pour information, les paramètres <code class="codecolorer text default"><span class="text">height</span></code> et <code class="codecolorer text default"><span class="text">width</span></code> fixent la taille des étiquettes produites (pas étonnant quand même&#8230;), et <code class="codecolorer text default"><span class="text">xalign</span></code> détermine l&rsquo;<strong>alignement</strong> du texte (aligné à gauche avec 0.0, centré avec 0.5 et aligné à droite avec 1.0).</p>
<p>Venons-en maintenant au coeur du problème. Nous devons écrire une fonction qui affiche les lignes successives du triangle de Pascal en calculant les coefficients. Ici, les boucles sont pratiques, je préfère donc donner une version en style impératif. Les principales étapes sont les suivantes :</p>
<ul>
<li>Pour tout entier i entre 1 et n, créer un nouveau conteneur horizontal (<code class="codecolorer text default"><span class="text">GPack.hbox</span></code>) et la fonction d&rsquo;empaquetage associée (<code class="codecolorer text default"><span class="text">packing</span></code>).</li>
<li>Pour tout entier j entre 1 et j, calculer le coefficient binomial, égal à 1 lorsque j = 1 ou j = i, et coeff(i &#8211; 1, j &#8211; 1) + coeff(i &#8211; 1, j) sinon. Le stocker dans une table de hachage pour réutilisation ultérieure (mémoïsation).</li>
<li>Créer une étiquette (<code class="codecolorer text default"><span class="text">GMisc.label</span></code>) contenant le coefficient, et l&rsquo;insérer dans le conteneur horizontal précédemment créé.</li>
</ul>
<p>Nous pouvons satisfaire à toutes ces exigences avec le code suivant :</p>
<pre><strong>let</strong> create n =
  <strong>let</strong> mem = Hashtbl.create n <strong>in</strong>
  <strong>let</strong> get = Hashtbl.find mem <strong>in</strong>
  <strong>for</strong> i = 1 <strong>to</strong> n <strong>do</strong>
    <strong>let</strong> packing = (GPack.hbox ~spacing:2 ~packing ())#pack ~expand:<strong>false</strong> <strong>in</strong>
    <strong>for</strong> j = 1 <strong>to</strong> i <strong>do</strong>
      <strong>let</strong> coef =
        <strong>if</strong> j = 1 || j = i <strong>then</strong> 1 
        <strong>else</strong> get (i - 1, j) + get (i - 1, j - 1) <strong>in</strong>
      label ~text:(string_of_int coef) ~packing ();
      Hashtbl.add mem (i, j) coef
    <strong>done</strong>
  <strong>done</strong></pre>
<p>Nous pouvons maintenant terminer notre application en lui ajoutant un bouton de fermeture. C&rsquo;est le moment de se rappeler que le conteneur <code class="codecolorer text default"><span class="text">GPack.button_box</span></code> est destiné tout spécialement à recevoir des boutons :</p>
<pre><strong>let</strong> quit = 
  <strong>let</strong> bbox = GPack.button_box `HORIZONTAL 
    ~layout:`END 
    ~packing:(vbox#pack ~expand:<strong>false</strong>) () in
  <strong>let</strong> quit = GButton.button ~stock:`QUIT ~packing:bbox#add () <strong>in</strong>
  quit#connect#clicked window#destroy;
  quit</pre>
<p>Pour finir, quelques lignes qui vérifient que le nombre de lignes à afficher est bien passé en argument, et qu&rsquo;il s&rsquo;agit d&rsquo;un entier valide compris entre 1 et 25 (limites arbitraires) :</p>
<pre><strong>let</strong> _ =
  <strong>try</strong>
    <strong>if</strong> Array.length Sys.argv = 2 <strong>then</strong> (
      <strong>let</strong> n = int_of_string Sys.argv.(1) <strong>in</strong>
      <strong>if</strong> n > 0 &#038;&#038; n < 26 <strong>then</strong> create n <strong>else</strong> raise Exit;
      window#show ();
      GMain.main ()
    ) <strong>else</strong> raise Exit 
  <strong>with</strong> _ -> prerr_endline "Usage: triangle.ml TAILLE"</pre>
<p>C&rsquo;est tout ! Il ne nous reste plus qu&rsquo;à lancer l&rsquo;application :<br />
<center><code class="codecolorer text default"><span class="text">ocaml -w s -I +lablgtk2 lablgtk.cma code.ml</span></code></center></p>
<p>Voici aussi un lien vers le <a href="http://ocaml.pastebin.com/f7517af71" target="_blank">code complet de cet atelier</a>.</p>
<p>À bientôt,<br />
Cacophrène</p>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>LablGTK &#8211; Le module GWindow</title>
		<link>https://blog.developpez.com/ocamlblog/p8477/informatique/title_157</link>
		<comments>https://blog.developpez.com/ocamlblog/p8477/informatique/title_157#comments</comments>
		<pubDate>Mon, 28 Dec 2009 07:32:10 +0000</pubDate>
		<dc:creator><![CDATA[Cacophrene]]></dc:creator>
				<category><![CDATA[Informatique]]></category>
		<category><![CDATA[LablGTK2]]></category>
		<category><![CDATA[OCaml]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[Bonjour ! Dans le précédent billet consacré à LablGTK, nous avons parlé du module GButton. Nous allons maintenant nous pencher sur un autre module incontournable dès que l&#8217;on veut créer une interface graphique : le module GWindow. Le module GWindow définit des fenêtres (GtkWindow), des boîtes de dialogue qui affichent des messages (GtkMessageDialog) ou un contenu plus complexe (GtkDialog). On y trouve aussi des fenêtres spécialisées dans la manipulation des fichiers (GtkFileChooserDialog), des couleurs (GtkColorDialog), [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>Bonjour !</p>
<p><center><a href="http://wwwfun.kurims.kyoto-u.ac.jp/soft/lsl/lablgtk.html" target="_blank"><img src="http://wwwfun.kurims.kyoto-u.ac.jp/soft/lsl/gtk-logo-half.gif"/></a></center></p>
<p>Dans le précédent billet consacré à LablGTK, nous avons parlé du module GButton. Nous allons maintenant nous pencher sur un autre module incontournable dès que l&rsquo;on veut créer une interface graphique : le module <a href="http://wwwfun.kurims.kyoto-u.ac.jp/soft/olabl/lablgtk/html/GWindow.html" target="_blank">GWindow</a>.</p>
<p><span id="more-16"></span></p>
<p>Le module <code class="codecolorer text default"><span class="text">GWindow</span></code> définit des fenêtres (<code class="codecolorer text default"><span class="text">GtkWindow</span></code>), des boîtes de dialogue qui affichent des messages (<code class="codecolorer text default"><span class="text">GtkMessageDialog</span></code>) ou un contenu plus complexe (<code class="codecolorer text default"><span class="text">GtkDialog</span></code>). On y trouve aussi des fenêtres spécialisées dans la manipulation des fichiers (<code class="codecolorer text default"><span class="text">GtkFileChooserDialog</span></code>), des couleurs (<code class="codecolorer text default"><span class="text">GtkColorDialog</span></code>), des fontes (<code class="codecolorer text default"><span class="text">GtkFontDialog</span></code>). Enfin, le module <code class="codecolorer text default"><span class="text">GWindow</span></code> fournit une boîte de dialogue &lsquo;À propos&#8230;&rsquo; (<code class="codecolorer text default"><span class="text">GtkAboutDialog</span></code>) qui simplifie le développement des applications.</p>
<h3><a href="http://library.gnome.org/devel/gtk/2.11/GtkWindow.html" target="_blank">GtkWindow</a></h3>
<p>Les fenêtres de type <code class="codecolorer text default"><span class="text">GtkWindow</span></code> sont appelées <em>toplevel</em>. Elles servent de conteneur principal pour une interface et n&rsquo;ont donc pas de widget parent. Voici quelques méthodes  intéressantes pour modifier la taille de la fenêtre :</p>
<table>
<tr>
<th>Méthode</th>
<th>Description</th>
</tr>
<tr>
<td><code class="codecolorer text default"><span class="text">#fullscreen</span></code></td>
<td>Passer en mode &lsquo;Plein écran&rsquo;</td>
</tr>
<tr>
<td><code class="codecolorer text default"><span class="text">#unfullscreen</span></code></td>
<td>Sortir du mode &lsquo;Plein écran&rsquo;</td>
</tr>
<tr>
<td><code class="codecolorer text default"><span class="text">#maximize</span></code></td>
<td>Occuper toute la place disponible</td>
</tr>
<tr>
<td><code class="codecolorer text default"><span class="text">#unmaximize</span></code></td>
<td>Réduire à la taille normale</td>
</tr>
<tr>
<td><code class="codecolorer text default"><span class="text">#stick</span></code></td>
<td>Rendre la fenêtre visible sur tous les bureaux</td>
</tr>
<tr>
<td><code class="codecolorer text default"><span class="text">#unstick</span></code></td>
<td>Rendre la fenêtre visible sur le bureau actif uniquement</td>
</tr>
</table>
<h5>Fenêtre principale et fermeture de l&rsquo;application</h5>
<p>En général, on utilise <code class="codecolorer text default"><span class="text">#event#connect#delete</span></code> pour afficher un message de confirmation de la fermeture à l&rsquo;attention de l&rsquo;utilisateur, et <code class="codecolorer text default"><span class="text">#connect#destroy</span></code> pour appeler la fonction <code class="codecolorer text default"><span class="text">GMain.quit</span></code> et quitter l&rsquo;application.</p>
<h5>Widgets enfants</h5>
<p>Dernier point, et non des moindres : les fenêtres sont des widgets qui ne peuvent contenir qu&rsquo;un seul widget enfant. Par conséquent, toute interface tant soit peu complexe devra faire appel à des boîtes (module <a href="http://wwwfun.kurims.kyoto-u.ac.jp/soft/olabl/lablgtk/html/GPack.html" target="_blank"><code class="codecolorer text default"><span class="text">GPack</span></code></a>) dont nous parlerons dans le prochain billet.</p>
<h3><a href="http://library.gnome.org/devel/gtk/unstable/GtkMessageDialog.html" target="_blank">GtkMessageDialog</a></h3>
<p><center><img src="http://blog.developpez.com/media/GtkMessageDialog.png" width="390" height="190" alt="" /></center></p>
<p>Les fenêtres de type <code class="codecolorer text default"><span class="text">GtkMessageDialog</span></code> servent à afficher un court message à l&rsquo;attention de l&rsquo;utilisateur. On s&rsquo;en sert notamment pour demander confirmation avant de fermer l&rsquo;application lorsque des modifications ont été apportées depuis le dernier enregistrement. Ces boîtes de dialogue permettent d&rsquo;écrire des textes très lisibles grâce aux <strong>balises Pango</strong> (gras, italique, choix de la taille de la police, etc.).</p>
<h3><a href="http://library.gnome.org/devel/gtk/unstable/GtkDialog.html" target="_blank">GtkDialog</a></h3>
<p><center><img src="http://blog.developpez.com/media/GtkDialog.png" width="606" height="486" alt="" /><br />
Boîte de dialogue de type GtkDialog<br />
</center></p>
<p>Les boîtes de dialogue de type <code class="codecolorer text default"><span class="text">GtkDialog</span></code> sont souvent utilisées pour créer la page &lsquo;Préférences&rsquo; dans une application. Ce sont des fenêtres composées dès l&rsquo;origine d&rsquo;une boîte verticale (méthode <code class="codecolorer text default"><span class="text">#vbox</span></code>) dans laquelle il est possible de placer n&rsquo;importe quelle combinaison de widgets. On ajoute des boutons (fermeture, annulation, validation, aide, etc.) avec les méthodes :</p>
<table>
<tr>
<th>Méthode</th>
<th>Description</th>
</tr>
<tr>
<td><code class="codecolorer text default"><span class="text">#add_button</span></code></td>
<td>Ajoute un bouton en indiquant le texte qu&rsquo;il contient</td>
</tr>
<tr>
<td><code class="codecolorer text default"><span class="text">#add_button_stock</span></code></td>
<td>Ajoute un bouton avec une icône associée (<code class="codecolorer text default"><span class="text">GtkStock.id</span></code>)</td>
</tr>
</table>
<p>Les boutons sont insérés dans un widget de type GtkButtonBox accessible avec la méthode #action_area.</p>
<h3><a href="http://library.gnome.org/devel/gtk/unstable/GtkAboutDialog.html" target="_blank">GtkAboutDialog</a></h3>
<p><center><img src="http://blog.developpez.com/media/GtkAboutDialog.png" width="297" height="186" alt="" /></center></p>
<p>La boîte de dialogue <code class="codecolorer text default"><span class="text">GtkAboutDialog</span></code> permet de créer simplement une boîte de dialogue de type &laquo;&nbsp;À propos&#8230;&nbsp;&raquo; pour votre application. Elle permet de décrire brièvement le logiciel. Voici quelques-unes des méthodes disponibles :</p>
<table>
<tr>
<th>Méthode</th>
<th>Description</th>
</tr>
<tr>
<td><code class="codecolorer text default"><span class="text">#set_authors</span></code></td>
<td>Nom des développeurs du logiciel.</td>
</tr>
<tr>
<td><code class="codecolorer text default"><span class="text">#set_documenters</span></code></td>
<td>Auteurs de la documentation.</td>
</tr>
<tr>
<td><code class="codecolorer text default"><span class="text">#set_license</span></code></td>
<td>Licence du logiciel.</td>
</tr>
<tr>
<td><code class="codecolorer text default"><span class="text">#set_logo</span></code></td>
<td>Logo du logiciel.</td>
</tr>
<tr>
<td><code class="codecolorer text default"><span class="text">#set_website</span></code></td>
<td>Site web du logiciel.</td>
</tr>
<tr>
<td><code class="codecolorer text default"><span class="text">#set_version</span></code></td>
<td>Version du logiciel.</td>
</tr>
<tr>
<td><code class="codecolorer text default"><span class="text">#set_copyright</span></code></td>
<td>Copyright.</td>
</tr>
</table>
<p>Comme vous pouvez le voir, cette boîte de dialogue a été conçue pour uniformiser les applications qui utilisent GTK, mais aussi et surtout pour accélérer le développement d&rsquo;un nouveau logiciel.</p>
<h3>Autres boîtes de dialogue</h3>
<p>Il existe d&rsquo;autres boîtes de dialogue plus spécialisées comme <code class="codecolorer text default"><span class="text">GtkColorSelectionDialog</span></code> (sélection de couleur), <code class="codecolorer text default"><span class="text">GtkFontSelectionDialog</span></code> (sélection de fonte) ou <code class="codecolorer text default"><span class="text">GtkFileChooserDialog</span></code> (sélection de fichier). Plutôt qu&rsquo;un long discours, je vous propose de les découvrir à travers un exemple de code. Il s&rsquo;agit d&rsquo;un éditeur de texte simpliste que vous pouvez tester avec la commande <strong>ocaml -w s -I +lablgtk2 lablgtk.cma test.ml</strong>. De plus, vous verrez que ces quelques boîtes de dialogue ressemblent beaucoup à ce que nous avons vu précédemment avec le module <strong>GButton</strong>.</p>
<p><center><img src="http://blog.developpez.com/media/GWindow Demo.png" width="631" height="326" alt="" /></center></p>
<pre><strong>module</strong> Aux =
  <strong>struct</strong>
    <strong>let</strong> load (text : GText.view) file =
      <strong>let</strong> ich = open_in file <strong>in</strong>
      <strong>let</strong> len = in_channel_length ich <strong>in</strong>
      <strong>let</strong> buf = Buffer.create len <strong>in</strong>
      Buffer.add_channel buf ich len;
      close_in ich;
      text#buffer#set_text (Buffer.contents buf)

    <strong>let</strong> save (text : GText.view) file =
      <strong>let</strong> och = open_out file <strong>in</strong>
      output_string och (text#buffer#get_text ());
      close_out och
  end

<strong>let</strong> _ = GMain.init ()
<em>
(* <a href="http://library.gnome.org/devel/gtk/2.11/GtkWindow.html" target="_blank">GtkWindow</a> - Fenêtre principale. *)</em>
<strong>let</strong> window =
  <strong>let</strong> wnd = GWindow.window 
    ~height:300 
    ~resizable:<strong>false</strong>
    ~position:`CENTER
    ~title:"GWindow Demo" () <strong>in</strong>
  wnd#connect#destroy GMain.quit;
  wnd

<em>(* <a href="http://library.gnome.org/devel/gtk/unstable/GtkMessageDialog.html" target="_blank">GtkMessageDialog</a> - Court message à l'attention de l'utilisateur. *)</em>
<strong>let</strong> confirm _ =
  <strong>let</strong> dlg = GWindow.message_dialog
    ~message:"&lt;b&gt;&lt;big&gt;Voulez-vous vraiment quitter ?&lt;/big&gt;\n\n\
      Attention :\nToutes les modifications seront perdues.&lt;/b&gt;\n"
    ~parent:window
    ~destroy_with_parent:<strong>true</strong>
    ~use_markup:<strong>true</strong>
    ~message_type:`QUESTION
    ~position:`CENTER_ON_PARENT
    ~buttons:GWindow.Buttons.yes_no () <strong>in</strong>
  <strong>let</strong> res = dlg#run () = `NO <strong>in</strong>
  dlg#destroy ();
  res

<strong>let</strong> vbox = GPack.vbox 
  ~spacing:5
  ~border_width:5
  ~packing:window#add ()

<strong>let</strong> text =
  <strong>let</strong> scroll = GBin.scrolled_window
    ~hpolicy:`ALWAYS
    ~vpolicy:`ALWAYS
    ~shadow_type:`ETCHED_IN
    ~packing:vbox#add () <strong>in</strong>
  <strong>let</strong> txt = GText.view ~packing:scroll#add () <strong>in</strong>
  txt#misc#modify_font_by_name "Monospace 10";
  txt

<strong>let</strong> bbox = GPack.button_box `HORIZONTAL
  ~spacing:5
  ~layout:`SPREAD
  ~packing:(vbox#pack ~expand:<strong>false</strong>) ()
<em>
(* <a href="http://library.gnome.org/devel/gtk/unstable/GtkFileChooserDialog.html" target="_blank">GtkFileChooserDialog</a> - Boîte de dialogue d'ouverture et d'enregistrement. *)</em>
<strong>let</strong> action_button stock event action =
  <strong>let</strong> dlg = GWindow.file_chooser_dialog
    ~action:`OPEN
    ~parent:window
    ~position:`CENTER_ON_PARENT
    ~destroy_with_parent:<strong>true</strong> () <strong>in</strong>
  dlg#add_button_stock `CANCEL `CANCEL;
  dlg#add_select_button_stock stock event;
  <strong>let</strong> btn = GButton.button ~stock ~packing:bbox#add () <strong>in</strong>
  GMisc.image ~stock ~packing:btn#set_image ();
  btn#connect#clicked (<strong>fun</strong> () ->
    <strong>if</strong> dlg#run () = `OPEN <strong>then</strong> Gaux.may action dlg#filename;
    dlg#misc#hide ());
  btn

<strong>let</strong> open_button = action_button `OPEN `OPEN (Aux.load text)
<strong>let</strong> save_button = action_button `SAVE `SAVE (Aux.save text)

<em>(* <a href="http://library.gnome.org/devel/gtk/unstable/GtkColorSelectionDialog.html" target="_blank">GtkColorSelectionDialog</a> - Sélection de couleur. *)</em>
<strong>let</strong> color_picker =
  <strong>let</strong> dlg = GWindow.color_selection_dialog
    ~parent:window
    ~destroy_with_parent:<strong>true</strong>
    ~position:`CENTER_ON_PARENT () <strong>in</strong>
  dlg#ok_button#connect#clicked (<strong>fun</strong> () -> 
    text#misc#modify_base [`NORMAL, `COLOR dlg#colorsel#color]);
  <strong>let</strong> btn = GButton.button ~label:"Arrière-plan" ~packing:bbox#add () <strong>in</strong>
  GMisc.image ~stock:`COLOR_PICKER ~packing:btn#set_image ();
  btn#connect#clicked (<strong>fun</strong> () -> ignore (dlg#run ()); dlg#misc#hide ());
  btn

<em>(* <a href="http://library.gnome.org/devel/gtk/stable/GtkFontSelectionDialog.html" target="_blank">GtkFontSelectionDialog</a> - Sélection de fonte. *)</em>
<strong>let</strong> font_button =
  <strong>let</strong> dlg = GWindow.font_selection_dialog
    ~parent:window
    ~destroy_with_parent:<strong>true</strong>
    ~position:`CENTER_ON_PARENT () <strong>in</strong>
  dlg#ok_button#connect#clicked (<strong>fun</strong> () -> 
    text#misc#modify_font_by_name dlg#selection#font_name);
  <strong>let</strong> btn = GButton.button ~stock:`SELECT_FONT ~packing:bbox#add () <strong>in</strong>
  GMisc.image ~stock:`SELECT_FONT ~packing:btn#set_image ();
  btn#connect#clicked (<strong>fun</strong> () -> ignore (dlg#run ()); dlg#misc#hide ());
  btn

<em>(* <a href="http://library.gnome.org/devel/gtk/unstable/GtkAboutDialog.html" target="_blank">GtkAboutDialog</a> - Boîte de dialogue "À propos..." *)</em>
<strong>let</strong> about_button =
  <strong>let</strong> dlg = GWindow.about_dialog
    ~authors:["Cacophrene (&lt;cacophrene AT gmail DOT com&gt;)"]
    ~copyright:"Copyright © 2009-2010 Cacophrene"
    ~license:"GNU General Public License v3"
    ~version:"1.0"
    ~website:"http://blog.developpez.com/ocamlblog/"
    ~website_label:"OCamlBlog"
    ~position:`CENTER_ON_PARENT
    ~parent:window
    ~destroy_with_parent:<strong>true</strong> () <strong>in</strong>
  <strong>let</strong> btn = GButton.button ~stock:`ABOUT ~packing:bbox#add () <strong>in</strong>
  GMisc.image ~stock:`ABOUT ~packing:btn#set_image ();
  btn#connect#clicked (<strong>fun</strong> () -> ignore (dlg#run ()); dlg#misc#hide ());
  btn

<strong>let</strong> _ =
  window#event#connect#delete confirm;
  window#show ();
  GMain.main ()</pre>
<p>Bonne année 2010 et à bientôt pour de nouvelles aventures caméliennes,<br />
Cacophrène</p>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>LablGTK &#8211; Le module GButton</title>
		<link>https://blog.developpez.com/ocamlblog/p8433/informatique/lablgtk_revue_de_detail</link>
		<comments>https://blog.developpez.com/ocamlblog/p8433/informatique/lablgtk_revue_de_detail#comments</comments>
		<pubDate>Mon, 07 Dec 2009 09:05:17 +0000</pubDate>
		<dc:creator><![CDATA[Cacophrene]]></dc:creator>
				<category><![CDATA[Informatique]]></category>
		<category><![CDATA[LablGTK2]]></category>
		<category><![CDATA[OCaml]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[Bonjour ! Ce billet est le premier d&#8217;une longue série consacrée à LablGTK et, plus particulièrement, à la présentation détaillée des modules qui la constituent. Beaucoup de questions sur le forum caml de developpez.com plaident en faveur de cette idée. Il est vrai que la bibliothèque comporte plusieurs de 20 modules d&#8217;utilisation courante&#8230; ce qui n&#8217;est pas toujours évident au premier abord. Dans ce billet, nous allons commencer par quelque chose de simple : le [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>Bonjour !</p>
<p><center><a href="http://wwwfun.kurims.kyoto-u.ac.jp/soft/lsl/lablgtk.html" target="_blank"><img src="http://wwwfun.kurims.kyoto-u.ac.jp/soft/lsl/gtk-logo-half.gif"/></a></center></p>
<p>Ce billet est le premier d&rsquo;une longue série consacrée à LablGTK et, plus particulièrement, à la présentation détaillée des modules qui la constituent. Beaucoup de questions sur le forum caml de developpez.com plaident en faveur de cette idée. Il est vrai que la bibliothèque comporte plusieurs de 20 modules d&rsquo;utilisation courante&#8230; ce qui n&rsquo;est pas toujours évident au premier abord. Dans ce billet, nous allons commencer par quelque chose de simple : le module <strong>GButton</strong>.</p>
<p><span id="more-15"></span></p>
<p>Le module GButton définit des widgets cliquables communément appelés boutons. Ce sont des boutons simples (<strong>GtkButton</strong>), des bouton à bascule (<strong>GtkToggleButton</strong>), des cases à cocher (<strong>GtkCheckButton</strong>) ou des choix multiples (<strong>GtkRadioButton</strong>). On y trouve également des versions spécialisées dans la sélection d&rsquo;une fonte (<strong>GtkFontButton</strong>) ou d&rsquo;une couleur (<strong>GtkColorButton</strong>), ou encore la définition de liens cliquables (<strong>GtkLinkButton</strong>). Enfin, c&rsquo;est avec ce module que l&rsquo;on peut définir des barres d&rsquo;outils (<strong>GtkToolbar</strong>).</p>
<p><center><img src="http://blog.developpez.com/media/GButton_01.png" width="331" height="360" alt="" /></center></p>
<p><strong>Les boutons simples (<a href="http://library.gnome.org/devel/gtk/unstable/GtkButton.html" target="_blank">GtkButton</a>)</strong><br />
Les boutons simples sont principalement utilisés pour répondre à un clic de l&rsquo;utilisateur (<strong>#connect#clicked</strong>). Ils possèdent un seul état et se retrouvent surtout dans les boîtes de dialogue sous la forme de boutons <em>OK</em>, <em>Appliquer</em> ou <em>Annuler</em>. Un bouton simple peut contenir une icône prédéfinie (module <strong>GtkStock</strong>) ou issue de n&rsquo;importe quel widget (<strong>#set_image</strong>).</p>
<p><strong>Les boutons à bascule (<a href="http://library.gnome.org/devel/gtk/unstable/GtkToggleButton.html" target="_blank">GtkToggleButton</a>)</strong><br />
Les boutons à bascule sont des boutons à deux états (état enfoncé et état relâché). Leur état peut être déterminé avec <strong>#active</strong> (<strong>true</strong> quand le bouton est enfoncé). Pour les applications partielles, on utilisera quelque chose comme ceci :</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">let foo toggle () = if toggle#active then (*...*) else (*...*) <br />
&nbsp;<br />
let toggle = <br />
&nbsp; let toggle = GButton.toggle_button () in <br />
&nbsp; toggle#connect#toggled (foo toggle); <br />
&nbsp; toggle</div></div>
<p><strong>Les cases à cocher (<a href="http://library.gnome.org/devel/gtk/unstable/GtkCheckButton.html" target="_blank">GtkCheckButton</a>)</strong><br />
Les cases à cocher permettent de sélectionner des options qui ne sont pas exclusives. En d&rsquo;autres termes, plusieurs choix peuvent être sélectionnés simultanément. La fonction <strong>GButton.check_button</strong> qui permet de les créer renvoie un widget de type <strong>toggle_button</strong>. En d&rsquo;autres termes, les cases à cocher sont des boutons à bascule dont l&rsquo;apparence a été modifiée. Pour le programmeur, la manipulation est exactement la même que pour les boutons à bascule.</p>
<p><strong>Les choix multiples (<a href="http://library.gnome.org/devel/gtk/unstable/GtkRadioButton.html" target="_blank">GtkRadioButton</a>)</strong><br />
Les choix multiples obligent l&rsquo;utilisateur à faire un choix parmi un ensemble de cas exclusifs. En d&rsquo;autres termes, lorsqu&rsquo;un nouveau choix est effectué, le choix précédent est automatiquement désélectionné. Pour assurer ce comportement, il ne faut pas oublier de définir le paramètre <strong>group</strong> pour relier les différents choix. Voici un exemple :</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">let days = [&quot;Monday&quot;; &quot;Tuesday&quot;; &quot;Wednesday&quot;; &quot;Thursday&quot;; &quot;Friday&quot;; &quot;Saturday&quot;; &nbsp;<br />
&nbsp; &quot;Sunday&quot;] <br />
&nbsp;<br />
let build packing = <br />
&nbsp; let rec loop group = function <br />
&nbsp; &nbsp; &nbsp; | [] -&gt; [] <br />
&nbsp; &nbsp; &nbsp; | label :: t -&gt; let btn = GButton.radio_button ~label ?group ~packing () in <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; btn :: loop (Some (Gaux.default btn#group ~opt:group)) t <br />
&nbsp; in loop None days</div></div>
<p><strong>Sélection de fonte (<a href="http://library.gnome.org/devel/gtk/unstable/GtkFontButton.html" target="_blank">GtkFontButton</a>)</strong><br />
Ce bouton permet de choisir une police de caractères. L&rsquo;événement associé est <strong>#connect#font_set</strong>, qui est déclenché chaque fois qu&rsquo;une fonte est sélectionnée par l&rsquo;utilisateur. La fonte peut être récupérée grâce à <strong>#font_name</strong>. Il s&rsquo;agit d&rsquo;une chaîne de caractères que l&rsquo;on peut convertir dans un type plus adapté (<strong>Pango.font_description</strong>) avec la fonction <strong>Pango.Font.from_string</strong>. On a alors accès à de nombreuses infos telles que la taille, la famille, l&rsquo;épaisseur&#8230;</p>
<p><center><img src="http://blog.developpez.com/media/font-selection.png" width="458" height="362" alt="" /></center></p>
<p><strong>Sélection de couleur (<a href="http://library.gnome.org/devel/gtk/unstable/GtkColorButton.html" target="_blank">GtkColorButton</a>)</strong><br />
Ce bouton permet de choisir une couleur. L&rsquo;événement associé est <strong>#connect#color_set</strong>. La couleur est obtenue avec <strong>#color</strong> et renvoyée au format <strong>Gdk.color</strong>. On récupère les composantes rouge, verte et bleue d&rsquo;une couleur au format Gdk.color avec les fonctions <strong>Gdk.Color.red</strong>, <strong>Gdk.Color.green</strong> et <strong>Gdk.Color.blue</strong>. Attention : les composantes sont sur 16 bits. Ne cherchez pas à reprogrammer ce bouton vous-mêmes, car ce serait long et inutile. Le bouton de sélection de couleur est vraiment bien conçu !</p>
<p><center><img src="http://blog.developpez.com/media/color-picker.png" width="588" height="307" alt="" /></center></p>
<p><strong>Lien cliquable (<a href="http://library.gnome.org/devel/gtk/unstable/GtkLinkButton.html" target="_blank">GtkLinkButton</a>)</strong><br />
Ce bouton permet de définir un lien (par exemple vers une page web) auquel on peut accéder par simple clic. On le retrouve notamment dans la boîte de dialogue &laquo;&nbsp;À propos&#8230;&nbsp;&raquo; (<a href="http://library.gnome.org/devel/gtk/unstable/GtkAboutDialog.html" target="_blank">GtkAboutDialog</a>). Son utilisation est très simple :</p>
<p><code class="codecolorer text default"><span class="text">let link = GButton.link_button &quot;http://www.google.fr/&quot; ~label:&quot;Google France&quot; ()</span></code></p>
<p><strong>Barres d&rsquo;outils (<a href="http://library.gnome.org/devel/gtk/unstable/GtkToolbar.html" target="_blank">GtkToolbar</a>)</strong><br />
Les barres d&rsquo;outils sont utilisées pour grouper plusieurs boutons sur une petite zone de l&rsquo;interface. Elles ont une orientation (verticale ou horizontale) et un style (icône seule (<strong>`ICONS</strong>), texte seul (<strong>`TEXT</strong>), icône et texte en dessous (<strong>`BOTH</strong>) ou icône et texte à côté (<strong>`BOTH_HORIZ</strong>)).</p>
<p>Les éléments d&rsquo;une barre d&rsquo;outils sont des widgets appelés <em>toolitems</em>. On retrouve la plupart des catégories de boutons :</p>
<ul>
<li>Des séparateurs : <a href="http://library.gnome.org/devel/gtk/unstable/GtkSeparatorToolItem.html" target="_blank">GtkSeparatorToolItem</a></li>
<li>Des boutons simples : <a href="http://library.gnome.org/devel/gtk/unstable/GtkToolButton.html" target="_blank">GtkToolButton</a></li>
<li>Des boutons à bascule : <a href="http://library.gnome.org/devel/gtk/unstable/GtkToggleToolButton.html" target="_blank">GtkToggleToolButton</a></li>
<li>Des choix multiples : <a href="http://library.gnome.org/devel/gtk/unstable/GtkRadioToolButton.html" target="_blank">GtkRadioToolButton</a></li>
<li>Des boutons avec menu déroulant : <a href="http://library.gnome.org/devel/gtk/unstable/GtkMenuToolButton.html" target="_blank">GtkMenuToolButton</a></li>
</ul>
<p><center><img src="http://blog.developpez.com/media/GtkToolbar.png" width="672" height="522" alt="" /></center><br />
Voici le code qui a permis d&rsquo;obtenir l&rsquo;image ci-dessus :</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;height:300px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">let window = <br />
&nbsp; GMain.init (); <br />
&nbsp; let wnd = GWindow.window <br />
&nbsp; &nbsp; &nbsp; ~title:&quot;GtkToolbar demo&quot; <br />
&nbsp; &nbsp; &nbsp; ~position:`CENTER <br />
&nbsp; &nbsp; &nbsp; ~resizable:false <br />
&nbsp; &nbsp; &nbsp; ~width:800 ~height:600 () in <br />
&nbsp; wnd#connect#destroy GMain.quit; <br />
&nbsp; wnd <br />
&nbsp;<br />
let box = GPack.vbox ~packing:window#add () <br />
&nbsp;<br />
let toolbar = GButton.toolbar &nbsp;<br />
&nbsp; ~orientation:`HORIZONTAL &nbsp;<br />
&nbsp; ~style:`ICONS <br />
&nbsp; ~packing:(box#pack ~expand:false) () <br />
&nbsp;<br />
let days = &nbsp;<br />
&nbsp; let menu = GMenu.menu () in <br />
&nbsp; List.iter (fun label -&gt; ignore (GMenu.menu_item ~label ~packing:menu#add ())) <br />
&nbsp; &nbsp; &nbsp; [&quot;Lundi&quot;; &quot;Mardi&quot;; &quot;Mercredi&quot;; &quot;Jeudi&quot;; &quot;Vendredi&quot;; &quot;Samedi&quot;; &quot;Dimanche&quot;]; <br />
&nbsp; menu <br />
&nbsp;<br />
let data = [`B `NEW; `B `OPEN; `B `SAVE; `S; `B `CUT; `B `COPY; `B `PASTE; `S; <br />
&nbsp;`T &quot;Bascule&quot;; `S; `M days ] <br />
&nbsp;<br />
let _ = <br />
&nbsp; let packing = toolbar#insert in <br />
&nbsp; List.iter (function <br />
&nbsp; &nbsp; &nbsp; | `S -&gt; ignore (GButton.separator_tool_item ~packing ()) <br />
&nbsp; &nbsp; &nbsp; | `B stock -&gt; ignore (GButton.tool_button ~stock ~packing ()) <br />
&nbsp; &nbsp; &nbsp; | `T label -&gt; ignore (GButton.toggle_tool_button ~label ~packing ()) <br />
&nbsp; &nbsp; &nbsp; | `M menu -&gt; ignore (GButton.menu_tool_button ~label:&quot;Foo&quot; ~menu ~packing ()) <br />
&nbsp; &nbsp; &nbsp; | _ -&gt; () <br />
&nbsp; ) data <br />
&nbsp;<br />
let _ = <br />
&nbsp; window#show (); <br />
&nbsp; GMain.main ()</div></div>
<p>Mais ce n&rsquo;est pas tout ! En fait, on peut insérer à peu près n&rsquo;importe quel widget dans une barre d&rsquo;outils à condition de l&rsquo;empaqueter au préalable dans l&rsquo;unité de base des barres d&rsquo;outils : le <a href="http://library.gnome.org/devel/gtk/unstable/GtkToolItem.html" target="_blank">GtkToolItem</a>.</p>
<p><center><img src="http://blog.developpez.com/media/GtkToolbar-2.png" width="672" height="105" alt="" /></center><br />
Voici les quelques lignes qui ont permis d&rsquo;insérer une fenêtre avec barres de défilement (<a href="http://library.gnome.org/devel/gtk/unstable/GtkScrolledWindow.html" target="_blank">GtkScrolledWindow</a>) dans une barre d&rsquo;outils :</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">let toolbar = GButton.toolbar &nbsp;<br />
&nbsp; ~orientation:`HORIZONTAL &nbsp;<br />
&nbsp; ~style:`BOTH () <br />
&nbsp;<br />
let item = GButton.tool_item ~packing:toolbar#insert () <br />
&nbsp;<br />
let scroll = GBin.scrolled_window &nbsp;<br />
&nbsp; ~hpolicy:`NEVER &nbsp;<br />
&nbsp; ~vpolicy:`ALWAYS &nbsp;<br />
&nbsp; ~width:160 ~height:20 <br />
&nbsp; ~packing:item#add ()</div></div>
<p>C&rsquo;est tout pour aujourd&rsquo;hui. Dans un prochain billet, nous aborderons un module de grande importance : <strong>GWindow</strong>.</p>
<p>À bientôt,<br />
Cacophrène</p>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>GtkSourceStyleScheme</title>
		<link>https://blog.developpez.com/ocamlblog/p8058/informatique/gtksourcestylescheme</link>
		<comments>https://blog.developpez.com/ocamlblog/p8058/informatique/gtksourcestylescheme#comments</comments>
		<pubDate>Mon, 14 Sep 2009 07:49:50 +0000</pubDate>
		<dc:creator><![CDATA[Cacophrene]]></dc:creator>
				<category><![CDATA[Informatique]]></category>
		<category><![CDATA[LablGTK2]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[Bonjour ! Pour mon troisième et dernier volet consacré au widget GtkSourceView 2, je vous propose de parler un peu des thèmes de coloration syntaxique et du widget GtkSourceStyleScheme. Pour accéder aux thèmes installés sur votre ordinateur (ils se trouvent dans le répertoire /usr/share/gtksourceview-2.0/styles et ce sont des fichiers XML), vous devez d&#8217;abord créer un widget de type GtkSourceStyleSchemeManager. Vous l&#8217;aurez compris, c&#8217;est le gestionnaire de thèmes ! Pour en créer un avec LablGTK, rien [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>Bonjour !</p>
<p><center><a href="http://wwwfun.kurims.kyoto-u.ac.jp/soft/lsl/lablgtk.html" target="_blank"><img src="http://wwwfun.kurims.kyoto-u.ac.jp/soft/lsl/gtk-logo-half.gif"/></a></center></p>
<p>Pour mon troisième et dernier volet consacré au widget <strong>GtkSourceView 2</strong>, je vous propose de parler un peu des thèmes de coloration syntaxique et du widget <strong>GtkSourceStyleScheme</strong>.</p>
<p><span id="more-14"></span></p>
<p>Pour accéder aux thèmes installés sur votre ordinateur (ils se trouvent dans le répertoire <strong>/usr/share/gtksourceview-2.0/styles</strong> et ce sont des fichiers XML), vous devez d&rsquo;abord créer un widget de type <strong>GtkSourceStyleSchemeManager</strong>. Vous l&rsquo;aurez compris, c&rsquo;est le gestionnaire de thèmes ! Pour en créer un avec LablGTK, rien de plus facile :</p>
<p><code class="codecolorer text default"><span class="text">let manager = GSourceView2.source_style_scheme_manager ~default:true</span></code></p>
<p>Ce gestionnaire vous donne accès à une liste d&rsquo;identifiants (<em>ids</em>) qui correspondent à tous les thèmes installés. Chaque identifiant vous donne accès à un widget <strong>GtkSourceStyleScheme</strong> qui contient les données du thème proprement dit. Pour récupérer les thèmes, vous pouvez procéder ainsi :</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">let get_sorted_schemes () = <br />
&nbsp; let manager = GSourceView2.source_style_scheme_manager ~default:true in <br />
&nbsp; List.map (fun id -&gt; <br />
&nbsp; &nbsp; &nbsp; match manager#style_scheme id with &nbsp;<br />
&nbsp; &nbsp; &nbsp; | Some scheme -&gt; scheme &nbsp;<br />
&nbsp; &nbsp; &nbsp; | _ -&gt; assert false (* cas logiquement impossible dans ce contexte. *) <br />
&nbsp; ) (List.sort String.compare manager#style_scheme_ids)</div></div>
<p>Enfin, chaque <strong>GtkSourceStyleScheme</strong> contient des informations que vous pouvez extraire et afficher dans vos applications : il s&rsquo;agit du <em>nom</em> et de la <em>description</em> du thème. Voyez par exemple :</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">let display_style_scheme_info scheme = <br />
&nbsp; Printf.printf &quot;Nom : %s\nDescription : %s\n&quot; &nbsp;<br />
&nbsp; &nbsp; scheme#name scheme#description; <br />
&nbsp; flush stdout</div></div>
<p>En combinant ces quelques idées, on peut facilement extraire la liste des thèmes disponibles pour l&rsquo;insérer dans une application et laisser à l&rsquo;utilisateur le soin de choisir ce qui lui plaît le plus (à la manière de <strong>gedit</strong>) :</p>
<p><center><img src="http://blog.developpez.com/media/GtkSourceView2-GtkStyleScheme.png" width="610" height="329" alt="GtkSourceView 2 - Les thèmes" /></center></p>
<p>Pour finir, voici quelques liens qui pourront vous aider à aller plus loin :</p>
<ul>
<li><a href="http://github.com/mig/gedit-themes/tree" target="_blank">Quelques thèmes</a></li>
<li><a href="http://blogs.gnome.org/pbor/2007/08/01/gedit-style-schemes/" target="_blank">Comment créer son propre thème ?</a></li>
</ul>
<p>À bientôt,<br />
Cacophrène</p>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Utilisez GtkSourceView 2.0 dans vos programmes OCaml !</title>
		<link>https://blog.developpez.com/ocamlblog/p8056/informatique/utilisez_gtksourceview_2_0_dans_vos_prog</link>
		<comments>https://blog.developpez.com/ocamlblog/p8056/informatique/utilisez_gtksourceview_2_0_dans_vos_prog#comments</comments>
		<pubDate>Sun, 13 Sep 2009 13:54:47 +0000</pubDate>
		<dc:creator><![CDATA[Cacophrene]]></dc:creator>
				<category><![CDATA[Informatique]]></category>
		<category><![CDATA[LablGTK2]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[Bonjour ! Cela fait déjà quelques mois que le widget GtkSourceView 2.0 a remplacé l&#8217;ancien widget GtkSourceView 1.0. La bibliothèque LablGTK, qui permet d&#8217;utiliser GTK+ avec OCaml, vient tout juste d&#8217;intégrer ce nouveau widget. Pour utiliser dès maintenant le nouveau widget, il faut installer LablGTK à partir du dépôt SVN : 1. Récupérer une copie locale du dépôt SVN de LablGTK2 : svn co svn://svn.gna.org/svn/lablgtk/trunk lablgtk 2. Préparer l&#8217;installation : aclocal &#38;&#38; autoconf 3. Configurer [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>Bonjour !</p>
<p><center><a href="http://wwwfun.kurims.kyoto-u.ac.jp/soft/lsl/lablgtk.html" target="_blank"><img src="http://wwwfun.kurims.kyoto-u.ac.jp/soft/lsl/gtk-logo-half.gif"/></a></center></p>
<p>Cela fait déjà quelques mois que le widget <strong>GtkSourceView 2.0</strong> a remplacé l&rsquo;ancien widget <strong>GtkSourceView 1.0</strong>. La bibliothèque LablGTK, qui permet d&rsquo;utiliser GTK+ avec OCaml, vient tout juste d&rsquo;intégrer ce nouveau widget.</p>
<p><span id="more-12"></span></p>
<p>Pour utiliser dès maintenant le nouveau widget, il faut installer LablGTK à partir du dépôt SVN :</p>
<p>1. Récupérer une copie locale du dépôt SVN de LablGTK2 : <code class="codecolorer text default"><span class="text">svn co svn://svn.gna.org/svn/lablgtk/trunk lablgtk</span></code><br />
2. Préparer l&rsquo;installation : <code class="codecolorer text default"><span class="text">aclocal &amp;&amp; autoconf</span></code><br />
3. Configurer et installer LablGTK : <code class="codecolorer text default"><span class="text">./configure &amp;&amp; make world &amp;&amp; make install</span></code></p>
<p>Le widget GtkSourceView 2 est accessible avec les bibliothèques <strong>lablgtksourceview2.cma</strong> et <strong>lablgtksourceview2.cmxa</strong>. Bien entendu, elles ne peuvent pas être utilisées en même temps que l&rsquo;ancien widget !</p>
<p>Dernière info : comme la <strong>documentation</strong> n&rsquo;a pas encore été mise à jour sur le site officiel de la bibliothèque, vous pouvez la générer de votre côté avec <code class="codecolorer text default"><span class="text">ocamldoc</span></code>.</p>
<p>À bientôt,<br />
Cacophrène</p>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
	</channel>
</rss>
