SUPINFO International University

SUPINFO Institute of Information Technology
Laboratoire Microsoft




Tous les Articles du Laboratoire Microsoft

Programmation sur Tablet PC
Accueil > Articles > Matériels
Auteurs 
Julien BAKMEZDJIAN



 Tous les articles de cet auteur

3,4/5

Assez Bien


39193
59/206

Introduction

 

            Nous continuons ici à présenter la programmation d’applications utilisant la technologie Tablet PC. Cet article nous servira en effet de base pour le développement ultérieur d’un outil d’indexation et de recherche de fichiers encre numérique.       

            Comme nous l’avons vu dans l’article précédent, l'environnement Tablet PC offre de nouvelles possibilités de programmation. Celles-ci sont disponibles via l'API Tablet PC contenue dans le SDK Tablet PC téléchargeable à cette adresse :

http://download.microsoft.com/download/Tabletpc/Install/1.1/WXP/EN-US/setup.exe

Après avoir installé cet SDK, nous verrons comment utiliser l’API Tablet PC en faisant un tour d'horizon de ses différentes classes. Nous essaierons aussi souvent que possible d'illustrer le comportement de ces classes par des exemples agrémentés du code nécessaire (en C#).

 

SDK Tablet PC : de l'installation à la découverte de l'API

 

            Après avoir installé le SDK, il faut configurer Visual Studio .Net. Pour cela, lorsque vous créez un projet, il faut ajouter une référence vers Microsoft.Ink.dll. Allez dans le menu 'Projet' et cliquez sur 'Ajouter une référence…'. Dans l'onglet '.NET' sélectionnez 'Microsoft Tablet PC API' et validez la boîte de dialogue.

Dans l'explorateur de solutions, vous devriez voir apparaître 'Microsoft.Ink' dans la liste des références.

            Deux nouveaux contrôles sont disponibles grâce à l'API Tablet PC : InkEdit et InkPicture. Pour les faire apparaître dans l'onglet 'Windows Forms' de la Boîte à outils, cliquez-droit dessus, puis choisissez 'Personnaliser la boîte à outils…', sélectionnez l'onglet 'Composants .NET Framework'. Enfin, cochez les contrôles InkEdit et InkPicture.

            Le développeur peut maintenant utiliser toutes les classes relatives à la technologie encre numérique. Ces classes, détaillées par la suite, se regroupent en trois catégories : les classes d'interface, les classes de gestion de l'encre et les classes liées au recognizer (le terme recognizer désigne le moteur de la reconnaissance d'écriture manuscrite).

 

            Remarque : Comme dit dans le précédent article, il est possible d'installer le SDK Tablet PC sur un ordinateur traditionnel (i.e. sans Windows Edition Tablet PC). Cependant, nombre de fonctionnalités ne seront pas disponibles, notamment la reconnaissance d'écriture, et il conviendra de placer du code de déroutement des erreurs lors de l'utilisation de ces fonctionnalités.

 

Deux nouveaux contrôles : InkEdit et InkPicture

 

InkEdit

 

            Ce contrôle dérive du contrôle RichTextBox. La grande différence est qu'il permet de capturer de l'encre numérique, et de la convertir au moyen d'un recognizer. Cette conversion peut être automatique (après un cours délai) ou lancée manuellement. Le texte saisi avec le stylet est alors entièrement converti en caractères. Cela est très pratique lorsque l'utilisateur souhaite remplir directement (sans le Panneau de saisie) des champs (d'un formulaire…) à l'aide de son stylet.

            Il permet également la saisie de texte au clavier, ce qui est très commode lors d'une saisie classique au clavier.

 

InkPicture

 

            Ce contrôle permet de capturer de l'encre numérique (notes ou schémas). Il n'y a pas de reconnaissance d'écriture. On peut également y afficher un bitmap, sur lequel on pourra écrire. Il est donc très utile pour l'affichage de l'encre numérique 'brute', lorsqu'il n'y a aucun traitement à effectuer.

            Il est à noter que son utilisation sur une machine non Tablet PC est possible seulement si le SDK Tablet PC y est installé.

 

            Voici enfin une première application Tablet PC, qui présente ces deux contrôles. Le code est téléchargeable ici.

            Cette application a pour but d’illustrer les différentes utilisations que l’on peut faire des deux contrôles précédents. Il y a un InkEdit avec reconnaissance manuelle ou automatique, et un InkPicture sur lequel on peut afficher le bitmap de son choix et où l’on peut dessiner :

 

 

 

            La construction des objets InkEdit et InkPicture se fait de manière traditionnelle :

-          déclaration dans la classe :

      private Microsoft.Ink.InkEdit inkEdit1;

      private Microsoft.Ink.InkPicture inkPicture1;

           

-          construction :

 

      this.inkEdit1 = new Microsoft.Ink.InkEdit();

      this.inkPicture1 = new Microsoft.Ink.InkPicture();

           

            L’initialisation (taille, position, couleur, label…) se fait de manière totalement identique à celle des contrôles classiques. Le changement de couleur de l’encre du InkPicture se fait par :

 

      inkPicture1.DefaultDrawingAttributes.Color= System.Drawing.Color.Red;

 

            Le passage de la reconnaissance automatique à manuelle se fait au moyen de la propriété RecoTimeout de l’objet InkEdit. Cette propriété fixe le délai de la reconnaissance automatique (en millisecondes) ; lorsqu’elle est mise à zéro, la reconnaissance est manuelle :

 

      private void radioBtnAuto_Click(object sender, System.EventArgs e)

      {

            this.inkEdit1.RecoTimeout = 1000;

            this.btnReco.Enabled = false;

      }

      private void btnReco_Click(object sender, System.EventArgs e)

      {

            this.inkEdit1.Recognize();

      }

 

            Le chargement et l’affichage d’un bitmap dans le InkPicture se fait au moyen d’un objet Bitmap :

 

      private void btnParcourir_Click(object sender, System.EventArgs e)

      {

            OpenFileDialog bmpDlg = new OpenFileDialog();

            bmpDlg.Title = "Changer d'image de fond";

            bmpDlg.Filter = "Fichiers Bitmap (*.bmp)|*.bmp";

            try

            {

                  if(bmpDlg.ShowDialog() == DialogResult.OK)

                  {

                  // charge et affiche le bitmap de fond du Inkpicture

                  // avec gestion des exceptions

                        bmpFond = new Bitmap(bmpDlg.FileName);

                        this.inkPicture1.Size = bmpFond.Size;

                        inkPicture1.BackgroundImage = bmpFond;

                        inkPicture1.Ink.DeleteStrokes();

                  }

            }

            catch(System.ArgumentException err)

            {

                  MessageBox.Show ("Le fichier image spécifié est introuvable", "Fichier introuvable",

                  MessageBoxButtons.OK, MessageBoxIcon.Error);

            }    

      }

 

            Notez la ligne :

 

            inkPicture1.Ink.DeleteStrokes();

 

qui permet d’effacer à l’ouverture d’un nouveau bitmap l’encre contenu dans l’InkPicture.

 

            Vous pouvez donc constater que l’utilisation de ces contrôles est assez simple et ressemble énormément à l’utilisation des contrôles classiques. Nous allons maintenant voir les classes de la ‘Managed API’, ensemble de classes qui permettent une gestion beaucoup plus souple de l’encre numérique.

 

Les classes d’entrée

 

            Ces classes gèrent la saisie de l’encre et la gestion des moyens de pointage (souris, stylet…).

 

Classe Tablet (et collection Tablets)

            Cette classe représente un dispositif de pointage qui reçoit les événements et messages liés à ce dispositif. Il permet également d’accéder aux différentes propriétés de ce dispositif : par exemple, les possibilités hardware (intégré ou non à l’écran, l’élément de pointage doit-il être en contact avec l’écran pour pouvoir renvoyer sa position…).

            La collection Tablets liste tous les dispositifs de pointage présents.

 

Classes Cursor et CursorButton (et collection Cursors)

            La classe Cursor représente une extrémité active du stylet (un stylet peut avoir une extrémité encre et une autre gomme). Chaque objet Cursor possède un ensemble de propriétés telles que son Id et ses attributs de dessin. Sa propriété Tablet retourne le dispositif auquel il est rattaché. Il peut posséder par ailleurs un ensemble de CursorButton que l’on accède par la propriété Buttons. Ces CursorButton, associés chacun à un bouton du stylet, permettent entre autre de connaître l’état du bouton (invalidé, enfoncé ou relâché).

            La collection Cursors liste tous les extrémités de stylet disponibles (un Tablet PC peut accepter plusieurs stylets avec plusieurs extrémités).

 

 

Classes InkOverlay et InkCollector

            Ces deux classes permettent de collecter de l’encre numérique et de la gérer. Il est possible à la construction de les attacher à une fenêtre (généralement un Panel, mais pourquoi pas à un bouton ou à un label ?) en précisant le handle de cette fenêtre. Cela permettra par la suite de pouvoir dessiner dans cette fenêtre.

            InkCollector est un sous ensemble de InkOverlay : tout ce que peut faire un InkCollector, un InkOverlay le fait également. A l’inverse du InkOverlay, le InkCollector n’a pas d’objet Selection ; il ne peut donc pas manipuler les Stroke sélectionnés.

            Enfin, chacun d’eux possède un objet Ink qui permet de sauvegarder, charger les fichiers encre numérique, d’exécuter la reconnaissance via sa propriété Strokes

            Toutes les instances de InkCollector pourront donc toujours être remplacés par des InkOverlay.

 

Pour exemple, voilà comment on crée un InkOverlay :

 

            private Microsoft.Ink.InkOverlay inkoverlay1;

            inkoverlay1 = new Microsoft.Ink.InkOverlay(panel1.Handle);

            inkoverlay1.Enabled = true;

 

 

Les classes de 'management'

 

Classe DrawingAttributes

 

            Cette classe permet d’obtenir et de changer les options de dessin à partir des classes InkCollector et InkOverlay (propriété DefaultDrawingAttributes), ou Cursor (propriété DrawingAttributes). On peut par exemple jouer sur la couleur, la transparence, la pression et le type d’interaction avec la couleur du fond.

 

            Nous pouvons maintenant présenter un exemple de programme utilisant les fonctionnalités offertes par les classes InkOverlay, Cursor(s) et DrawingAttributes. Le code complet est disponible ici. Dans cette application, chaque curseur écrit avec des attributs de dessin (couleur et taille) différents.

            Un InkOverlay est créé et attaché à un Panel. On déclare un événement :

 

this.inkoverlay1.CursorInRange += new                      InkCollectorCursorInRangeEventHandler(CursorInRange_Event);

 

Cet événement correspond à l’entrée d’un Cursor dans la zone du InkOverlay. Deux cas peuvent amener à cet événement :

-          le pointeur de la souris entre dans la zone.

-          le pointeur est déjà présent dans la zone mais c’est un nouveau curseur qui s’approche.

Cet événement est indispensable pour la gestion des Cursor. En effet, un nouvel objet Cursor est instancié à chaque apparition d’un nouveau curseur remplissant ainsi la collection Cursors (cette collection est vide au départ et génère une exception en cas de tentative d’accès à un Cursor). Dans notre exemple (une souris et un stylet double extrémité), la collection Cursors contiendra au final trois objets Cursor, chacun faisant l’objet de traitements différents :

 

            private void CursorInRange_Event(object sender, Microsoft.Ink.InkCollectorCursorInRangeEventArgs e)

            {

                  Microsoft.Ink.DrawingAttributes myDrawingAttributes;

                  myDrawingAttributes = new Microsoft.Ink.DrawingAttributes();

 

                  // Traitements différents en fonction de l'ID du curseur entrant (e.Cursor)

                  switch (e.Cursor.Id)

                  {

                        // Cas de la souris

                        case 1:

                             myDrawingAttributes.Color = Color.Red;

                             myDrawingAttributes.Width = 150;

                             break;

                        // Cas de l'extrémité "Ecriture" du stylet

                        case 2:

                             myDrawingAttributes.Color = Color.Green;

                             myDrawingAttributes.Width = 10;

                             break;

                        // Cas de l'extrémité "Gomme" du stylet

                        case 3:

                             myDrawingAttributes.Color = Color.Black;

                             myDrawingAttributes.Width = 50;

                              break;

                  }

                  // Attachement des attributs nouvellement définis au curseur

                  e.Cursor.DrawingAttributes = myDrawingAttributes;

            }

 

On peut noter ici que si le Cursor actif ne s’est pas vu attaché de DrawingAttributes, c’est le DefaultDrawingAttributes du InkOverlay qui sera utilisé.

 

Remarque : il conviendrait de ne pas implémenter en dur la liste des Cursor présents sur le Tablet PC mais d’utiliser la collection Tablets.

 

Classes Stroke (et Strokes)

 

            La classe Stroke représente l’objet créé lorsque l’on dessine un trait sans lever le stylet. Elle permet de manipuler ce trait (déplacement, rotation, effacement…) et d’accéder à des propriétés telles que le nombre de points, l’ID, le statut (s’il est affiché ou s’il a été effacé). L’objet Strokes est une collection de Stroke. Il permet d’appliquer des traitements communs à tous les Stroke qu’il référence.

 

Classe Ink

 

            Cette classe représente l’objet englobant une collection Strokes. Elle permet entre autre le chargement et la sauvegarde d’un fichier encre numérique (via un buffer). Le code ci-après montre comment effectuer ces deux actions :

-          Sauvegarde d’un fichier encre numérique :

 

        SaveFileDialog dlgSave = new SaveFileDialog();

        dlgSave.Filter = "Fichiers encre numérique (*.ISF)|*.ISF" ;

        if (dlgSave.ShowDialog(this) == DialogResult.OK)

        {

            // Sauvegarde le fichier encre numérique

            Stream s = dlgSave.OpenFile();

            byte [] buf =

                myInkOverlay.Ink.Save();

            s.Write(buf, 0, buf.Length);

        }

 

-          Chargement d’un fichier encre numérique :

 

        OpenFileDialog dlgOpen = new OpenFileDialog();

        dlgOpen.Filter = "Fichiers encre numérique (*.ISF)|*.ISF";

        if (dlgOpen.ShowDialog(this) == DialogResult.OK)

        {

            // Charge le fichier encre numérique

            Stream s = dlgOpen.OpenFile();

            byte [] buf = new byte[s.Length];

            s.Read(buf, 0, buf.Length);

            Ink newInk = new Ink();

            newInk.Load(buf);

         }

 

Elle permet de copier et de coller l’ensemble des Stroke vers le Presse-papier.

 

 

Les classes de reconnaissance

 

RecognitionAlternate (et collection RecognitionAlternates)

            Cette classe représente le résultat d’une reconnaissance appliquée à une collection Strokes. Elle contient le texte reconnu accessible via la méthode ToString(), le niveau de confiance dans le mot reconnu (seulement pour le Recognizer en anglais), le nombre de lignes du résultat…

            RecognitionAlternates est une collection de RecognitionAlternate.

           

RecognitionResult

            Cette classe représente tous les résultats possibles renvoyés par une reconnaissance. Il contient la collection RecognitionAlternates renvoyée, ainsi que le résultat bénéficiant de la plus grande confiance (propriété TopAlternate).

 

Recognizer (et collection Recognizers)

            Un Recognizer représente l’objet effectuant la reconnaissance. On peut par exemple accéder via ses propriétés aux langues reconnues. Tous les champs d’un Recognizer sont en lecture seule.

            Une collection Recognizers liste tous les Recognizer disponibles et indique le Recognizer par défaut.

 

WordList

            Un objet de type WordList est une liste de mots que l’on peut créer et qui sert à améliorer la reconnaissance. Elle sert surtout à constituer un dictionnaire personnel. La méthode Add(string word) permet de remplir cette liste.

 

Voici un exemple mettant en œuvre l’édition des Stroke ainsi que la reconnaissance. Le code intégral est disponible ici.

 

 

            Concernant les Stroke, lorsque l’utilisateur change la ComboBox, le mode d’édition des Stroke est changé :

 

      private void comboBox1_SelectionChangeCommitted(object sender, System.EventArgs e)

      {

            switch(comboBox1.SelectedItem.ToString())

            {

                  case "Ecrire" :

                        inkO1.EditingMode = InkOverlayEditingMode.Ink;

                        break;

                  case "Effacer" :

                        inkO1.EditingMode = InkOverlayEditingMode.Delete;

                        break;

                  case "Sélectionner" :

                        inkO1.EditingMode = InkOverlayEditingMode.Select;

                        break;

            }

      }

 

Le changement de couleur se fait au moyen de la propriété DefaultDrawingAttributes du InkOverlay (les DrawingAttributes des différents Cursor n’ayant pas été définis comme dans l’exemple précédent) :

 

      private void button1_Click(object sender, System.EventArgs e)

      {

            ColorDialog dlgColor = new ColorDialog();

            dlgColor.AllowFullOpen = true;

            dlgColor.Color = inkO1.DefaultDrawingAttributes.Color;

            if (dlgColor.ShowDialog(this) == DialogResult.OK)

            {

                  // Changement de la couleur de l'encre

                  inkO1.DefaultDrawingAttributes.Color = dlgColor.Color;

            }

      }

 

 

            Concernant la reconnaissance, nous avons choisi la méthode lourde mais plus paramétrable et plus parlante. En effet, il existe la méthode ToString() de l’objet InkOverlay.Strokes qui permet d’obtenir directement le résultat de la reconnaissance.

Ici, nous instancions un objet Recognizer que nous initialisons au Recognizer par défaut. Puis, nous créons un RecognizerContext à partir de ce dernier :

 

// Récupération du recognizer par défaut (francais) et création d'un contexte

 

Recognizer myRecognizer = myRecognizers.GetDefaultRecognizer();

RecognizerContext myRecoContext = myRecognizer.CreateRecognizerContext();

 

Ensuite, nous créons une WordList que nous remplissons. Nous la rattachons au RecognizerContext puis forçons la prise en compte de cette liste lors des prochaines reconnaissances :

 

            Microsoft.Ink.WordList myWordList = new WordList();

            myWordList.Add("Frodon");

            myWordList.Add("Gandalf");

           

           

            // Attachement de la liste de mots au contexte

            myRecoContext.WordList = myWordList;

            // Permet l'utilisation de la liste de mots

            myRecoContext.Factoid = Factoid.WordList;

 

Il est possible de durcir cette prise en compte en imposant que la reconnaissance se fasse uniquement à partir de cette liste :

 

            // Force l'utilisation de la liste de mots

            myRecoContext.RecognitionFlags = RecognitionModes.Coerce;

 

Il faut ensuite récupérer le Strokes sur lequel va se faire la reconnaissance. Cette action doit impérativement être réalisée après le rattachement de la WordList et le paramétrage du Factoid :

 

            myRecoContext.Strokes = inkO1.Selection;

 

Il ne reste plus qu’à créer un RecognitionStatus et un RecognitionResult pour stocker respectivement les erreurs éventuelles de la reconnaissance et le résultat. On lance la reconnaissance par la méthode Recognize(out RecognitionStatus recognitionStatus) :

 

            RecognitionStatus myRecoStatus;

            RecognitionResult myResult;

            // Récupération du résultat de la reconnaissance

            // Stockage du code d'une ereur éventuelle dans myRecoStatus

            myResult = myRecoContext.Recognize(out myRecoStatus);

 

Nous remplissons finalement la ListBox par les différentes propositions de la reconnaissance :

 

// Ajout de tous les résultats à la listBox

foreach(RecognitionAlternate theAlternate in                                 myResult.GetAlternatesFromSelection())

{                 listBox1.Items.Add(String.Format("{0}",theAlternate.ToString()));

}

 

Finalement, l’application présentée permet de tester dans les grandes lignes la gestion des Stroke ainsi que la reconnaissance de caractères.

 

Conclusion

 

            Lors de la conception d’une application, le développeur peut choisir entre InkControl et Managed API selon la destination du produit. En effet, les InkControl sont pratiques lorsque l’application n’a pas pour objet la manipulation de l’encre numérique. Ils permettent de développer une application destinée à la fois aux Tablet PC et aux ordinateurs de bureau. En revanche, la Managed API offre une interface de gestion de l’encre numérique plus fine.




En Savoir Plus 
Evaluez cet article 


Pour afficher ou poster un commentaire, cliquez sur ce lien : Forum-Microsoft



Retrouvez ci-dessous les autres sections du Laboratoire Microsoft

Définitions

Accès direct aux définitions :
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z

Effectuez une recherche dans les définitions :