Formation avancé sur les Objets de Matlab

PROGRAMMATION SOUSMATLAB
D. Legland
29 mai 2015
Table des mati?res
1. Introduction 4
I. Programmer avec Matlab 5
2. Syntaxe de base 6
2.1. Types de données . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
2.2. Tests . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
2.3. Boucles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
3. Conventions de programmation 8
3.1. Nommage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
3.2. Codage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
3.3. Documentation des fonctions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
3.4. Modules et bibliothèques . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
3.5. Compatibilité . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
II. Programmation orientØe objets 14
4. Structures de donnØes et classes par valeur 16
4.1. Structures de données . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
4.2. Objets « par valeur » . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
5. Programmation orientØe objets 19
5.1. Déclaration de classe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
5.2. Création et utilisation des objets . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
5.3. Méthodes privées . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
5.4. Méthodes statiques . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
5.5. Autres méthodes particulières . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
6. HØritage de classe 25
6.1. Créer et utiliser une hiérarchie de classes . . . . . . . . . . . . . . . . . . . . . . 25
6.2. Afficher les objets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
6.3. Surcharger les opérateurs mathématiques . . . . . . . . . . . . . . . . . . . . . . 26
6.4. Surcharger subsref et subsasgn . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
6.5. Surcharge des opérations de tableau . . . . . . . . . . . . . . . . . . . . . . . . . 29
6.6. Méthodes et classes abstraites . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
Table des matières
7. Bonnes pratiques en programmation orientØe objet 31
7.1. Conventions de nommage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
7.2. Construction des objets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
7.3. Gérer des tableaux d’objets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
7.4. Paquetages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
III. Concepts avancØs 34
8. Interfaces graphiques 35
8.1. Concevoir ses interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
8.2. Les composants . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
8.3. Évènements graphiques (callbacks) . . . . . . . . . . . . . . . . . . . . . . . . . . 36
8.4. Évènements fenêtre . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
8.5. Gestion de la souris . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
9. Exceptions et gestion des erreurs 40
9.1. Principe des exceptions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
9.2. Traiter une exception . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
9.3. Créer ses erreurs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
10.Gestion des Øv?nements 42
10.1. Définir un évènement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
10.2. Diffuser un évènement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
10.3. Définir un listener . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
10.4. Regrouper la gestion des évènements . . . . . . . . . . . . . . . . . . . . . . . . . 44
10.5. Personnaliser un évènement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
page 3 sur 45
1. Introduction
Ce document rassemble différents éléments de pratique de programmation sous Matlab. Au fur et a mesure de la rédaction, il apparait qu’il pourrait servir de tutoriel sur la progammation Matlab, en particulier sur la programmation orientée objets (POO).
La première partie concerne des généralités sur le langages. Le chapitre 3 pose les conventions que j’utilise. La deuxième partie reprend mes notes sur les structures de données et la programmation orientée objets (chapitres 4, 5 et 6). La troisième partie aborde des thèmes plus techniques, qui reposent le plus souvent sur la manipulation et / ou la création de classes : la conception d’interfaces graphiques (chapitre 8), la gestion des exceptions et des erreurs (chapitre 9), et enfin une proposition d’architecture pour manipuler les évènements Matlab de manière similaire à Java (chapitre 10).
Premi?re partie .
Programmer avec Matlab
5
2. Syntaxe de base
Ce chapitre rappelle très brièvement quelques éléments fondamentaux de la programmation sous Matlab : les types de données, et les structures de contrôle (tests, boucles ).
2.1. Types de donnØes
Les données manipulées peuvent être :
— des nombres en virgule flottante,
— des nombres complexes (du type a + ib)
— des nombres entiers (signés ou non)
— des chaînes de caractères
— des tableaux (de valeurs ou de cellules)
2.2. Tests
2.2.1. Tests if-then-else
On découpe le programme en plusieurs blocs, et on execute un bloc ou l’autre en fonction d’une condition.
i f a > b r e s = a ; e l s e r e s = b ; end |
1
2
3
4
5
2.2.2. switch
Permet de gérer plusieurs tests en même temps. Très pratique avec les chaînes de caractères.
switch option case ' v r a i ' d i s p ( 'OK ' ) ; case ' faux ' d i s p ( ' Pas bon ' ) ; d e f a u l t e r r o r ( [ ' Valeur inconnue pour option : end | ' option ] ) ; |
1
2
3
4
5
6
7
8
2.3 Boucles
2.3. Boucles
2.3.1. Boucles for
Outre la boucle « for » classique, on peut utiliser d’autres syntaxes.
2.3.1.1. Boucle for classique
f o r i = 1:5 d i s p ( 2 i ) ; end |
1
2
3
2.3.1.2. ItØration sur des tableaux
Il est possible d’utiliser les tableaux à la manière des collections en Java. Exemple :
tab = 1 : 5 ; f o r i = tab d i s p ( i ) ; end |
1
2
3
4
Note : on itère en fait sur les colonnes (c’est à dire sur la dimension 2). Le reste du tableau est simplifié. On peut aussi itérer sur un tableau de cellules :
inds = c e l l s t r ( num2str ( ( 1 : 5 ) ' , f o r i = inds d i s p ( i {1}) ; end | ' ind%d ' ) ) ' ; |
1
2
3
4
Là aussi on itère sur les colonnes du tableau, d’où la deuxième transposition du tableau « inds ».
2.3.1.3. Boucle for parall?le
Il s’agit de la même syntaxe que la boucle for, mais cette version permet de paralléliser les calculs faits dans la boucle. Ne doit pas être utilisé si les calculs dépendent d’une itération précédente
p a r f o r i = 1:5 r e s ( i ) = maFonction ( i ) ; end |
1
2
3
3. Conventions de programmation
Résumé de quelques conventions de codage préconisées pour le langage Matlab. On peut aussi consulter le document de Richard Johnson , dont ce document est en grande partie inspiré. Le chapitre sur la POO redonne quelques conventions/bonnes pratiques spécifiques.
3.1. Nommage
De manière générale, on évite les caractères spéciaux dans les noms, et on se limite aux caractères alphabétiques. Il est préférable d’employer des noms anglais. Cela permet (1) d’homogénéiser la langue du code et (2) d’éviter les problèmes dus aux accents On évite les numéros sauf pour des fichiers temporaires ou de données.
Le seul caratère spécial toléré (à part les caractères spéciaux imposés par Matlab, tels « @ » ou « + ») est le caractère souligné « _ ». Il est à utiliser pour nommer les constantes statiques.
Attention au caractère « - », qui est interprété par Matlab comme une soustraction
3.1.1. Noms de chiers
Les fichiers Matlab peuvent contenir des scripts, des fonctions ou des classes. Ils ont tous l’extension « .m ». Il n’y a pas de limite sur le nombre de caractères.
De manière générale, il est préférable d’écrire les noms de fichier en minuscule. Pour les noms composés, on utilise la syntaxe qui consiste à coller les noms et à mettre en majuscule la première lettre de chaque mot. Exemple : nomDeLaFonction. Exception : les noms de classe, qui commencent par une majuscule.
3.1.2. Noms des fonctions
La convention Matlab est de stocker les fonction dans des fichiers qui portent leur nom. On applique donc aux fonctions les mêmes conventions que pour les noms de fichier.
Dans la mesure du possible, on utilise des noms de fonction qui commencent par des verbes. Cela permet de différencier les variables (noms) et les fonctions (verbes). Exemple :
d i s t a n c e = computeDistance ( . . . ) |
1
On peut utiliser des préfixes pour des groupes de fonctions qui travaillent sur des types communs. Par exemple, des fonctions qui travaillent sur des données « polygones » peuvent avoir les noms : polygonArea, polygonCentroid Autre exemple : un préfixe « im » pour les fonctions travaillant sur des images (conventions utilisée par la boîte à outils « Image Processing » de Mathworks).
3.2 Codage
3.1.3. Noms des classes
Matlab permet la programmation orientée objet. Afin de différencier les classes et les fonction, j’utilise comme convention de nommer les classes en commençant par une majuscule. Les variables membres et les méthodes suivent la convention générale de nommage (« nomDeMembre », « nomDeMethode » ).
3.2. Codage
3.2.1. Commentaires
Penser à mettre des commentaires dans les codes Matlab est un langage concis, il est donc très facile d’écrire des lignes courtes mais obscures. L’idée est de résumer l’intention. Une proportion de 30 à 50% de commentaire semble correcte.
On préfèrera les commentaires sur une ligne, commençant par le caractère « % », suivi d’une espace, et du commentaire commençant par une majuscule. Exemple :
% Exemple de commentaire |
1
3.2.2. Ponctuation
On termine les instructions par un point-virgule, sans mettre d’espace avant. Cela permet (1) d’avoir une syntaxe similaire à d’autres langages comme C ou Java, et (2) de ne pas afficher les résultats intermédiaires.
On évite les point-virgule à la fin des instructions « end », cela surcharge la lecture. On en met à la fin des mots-clés « return », « continue », qui sont considérées comme des instructions à part entières.
3.2.3. AØrer le code
L’écriture du code doit être relativement aérée. Quelques principes généraux :
— une instruction par ligne si possible
— une espace avant et après le signe d’affectation "="
— on évite les parenthèses autant que possible (notamment pour les tests et les boucles)
— on essaie de faire tenir les lignes dans la limite de 80 caractères. L’éditeur de Matlab permet d’afficher la limite, et de passer à la ligne automatiquement.
— on préconise souvent de mettre des espaces autour des opérateurs mathématiques, à voir selon les cas
3.2.4. Structuration du code
On essaie de faire des « blocs fonctionnels », composés de quelques lignes, qui s’occupent d’une tâche donnée, et que l’on commente.
Les structures de contrôle (boucles, tests ) sont indentées avec quatre caractères espace. On évite les tabulations, qui apparaissent différemment selon les éditeurs. L’idéal est de les
3 Conventions de programmation
remplacer par quatre caractères espace. On évite d’écrire les boucles ou les tests sur une seule ligne.
Exemple :
% I n i t i a l i s a t i o n des données data = z e r o s (1 , N) ; f o r i = 1:N value = g e t I n i t V a l u e ( i ) ; data ( i ) = i n i t D a t a ( value ) ; end |
1
2
3
4
5
6
3.2.5. Ecrire du code court
On évite les encapsulations d’indentation. Un niveau de 3 ou 4 indentations devrait être un maximum. Sinon, essayer de faire des sous-fonctions.
On évite les boucles ou les tests trop longs en terme de nombre de lignes.
3.3. Documentation des fonctions
Les commentaires en début des fichiers de fonctions peuvent être utilisés pour construire automatiquement la documentation. Il est donc préférable de les soigner
La première ligne du fichier contient la déclaration de la fonction, suivie de l’en-tête, puis du code.
3.3.1. Ligne d’entŒte ( h1 )
La première ligne de la documentation contient le nom de la fonction en majuscule, suivi d’une courte description (qui tient sur la ligne). Pour homogénéiser, il est préférable de l’écrire en anglais, en commençant par un verbe à l’infinitif, avec la première lettre en majuscule.
Exemple :
f u n c t i o n r e s = demoFunction ( x , n) %DEMOFUNCTION Compute a sample f u n c t i o n of x and n . . . |
1
2
3
3.3.2. Description de la fonction
Le reste de l’entête doit contenir l’explication de la syntaxe d’utilisation de la fonction, avec la description des paramètres.
Suggestion de syntaxe : écrire le nom des fonctions tel quel, écrire les paramètres en majuscule. L’idée est de favoriser le copier-coller après avoir tapé un ’help’. La description utilise des verbes conjugués.
Exemple :
f u n c t i o n r e s = demoFunction ( x , n) %DEMOFUNCTION Compute a sample f u n c t i o n of x and n % |
1
2
3
page 10 sur 45
3.3 Documentation des fonctions
% RES = demoFunction (X , N) % Computes the N- th power of the number X. % % RES = demoFunction (X , N, METHOD) % S p e c i f i e s the method to u s e . METHOD can be one of : % ' normal ' : use the normal method % ' optimized ' : use the optimized method |
4
5
6
7
8
9
10
Un en-tête de fonction peut aussi contenir un section d’exemples, et une section « See also », qui fait référence à des fonctions voisines. Les sections couramment utilisées dans l’aide de Matlab sont :
— Syntax
— Description
— Examples
— See also
— Algorithm
— References
Il est possible d’inclure des liens hypertexte dans l’aide, et de faire exécuter du code Matlab quand on clique dessus.
d i s p ( '<a h r e f ="matlab : magic (4) ">Generate magic square </a> ' ) |
1
3.3.3. Liens entre fonctions
Les noms de fonctions renseignées après la balise « See also » génèrent automatiquement des liens hypertexte. C’est assez pratique pour naviguer entre des fonctions apparentées. Si un fichier « Contents.m » existe (voir ci-dessous), on peut aussi donner le nom du module, ce qui permet créer une ébauche de navigation.
Il est aussi possible d’inclure explicitement des liens hypertexte vers d’autres fonctions. Exemple :
% See the help f o r the <matlab : doc ( ' publish ' ) | publish > f u n c t i o n . |
1
3.3.4. Cartouche
La fin de l’en-tête devrait contenir le nom de l’auteur, la date de création, et une mention de copyright. Un outil comme « tedit » permet d’ajouter automatiquement un cartouche pré-défini à tout nouveau fichier. Exemple de cartouche :

% - - - - - - % Author : Name and given name % e - mail : % Created : 2011 -04 -01 , using Matlab 7 . 9 . 0 . 5 2 9 ( R2009b) % Copyright 2011 INRA - My Laboratory |
1
2
3
4
5
3 Conventions de programmation
3.4. Modules et biblioth?ques
On peut regrouper des fonctions voisines dans des répertoires, et créer un paquetage ou module. Il suffit que le répertoire en question soit accessible via le path.
Si le répertoire du module est accessible depuis Matlab, un help avec le nom du module permet d’afficher la première ligne des en-tête des fonctions du module.
3.4.1. Fichier Contents.m
On peut aussi créer un fichier « Contents.m », qui est affiché si on appelle l’aide de Matlab sur le module. L’idée d’un tel fichier est de présenter un sommaire des fonctions du répertoire. Chaque fonction apparaît ainsi avec la première ligne de son en-tête.
Le fichier Contents peut être généré automatiquement par Matlab, via un « content report ». Il contient basiquement la liste des fonctions accompagnées de leur description courte. Il peut être retravaillé, pour grouper les fonctions similaires, et pour donner une description générale du module.
Quelques précautions à prendre :
— Le système d’aide de Matlab utilise le fichier « Contents.m », mais ne trouve pas le fichier « contents.m »
— Matlab n’aime pas les liste à puces commençant par des tirets (« - »), il lui arrive de les faire disparaître (confusion avec les résumés de fonction ?). Recommandation : utiliser des astérisque (« * »).
3.4.2. Infos de version
Les deux premières lignes du fichier Contents.m sont analysées par Matlab pour fournir des informations de version (par la commande « ver »).
% Toolbox d e s c r i p t i o n % Version xxx dd -mmm- yyyy . |
1
2
La convention de Matlab est de donner uniquement la description de la bibliothèque. Je préfère ajouter le nom, ça permet de la retrouver a posteriori. Il faut aussi avoir un point à la fin de la deuxième ligne pour que la date soit prise en compte (2009b).
3.4.3. NumØrotation des versions
Grande question L’idée est de numéroter par majeur.mineur.correctif. Une alternative est d’utiliser systématiquement la date de release (format yyyy-mm-dd).
3.5. CompatibilitØ
Quelques points pour faciliter le passage entre différents systèmes d’exploitation :
— La gestion des noms de fichiers devrait se baser sur les fonction « fullfile » et fileparts », qui se chargent de gérer les séparateurs de fichier
page 12 sur 45
3.5 Compatibilité
— L’utilisation des ’ ’ à la fin d’une ligne pourrait causer des problèmes de compatibilité pour les passages de Linux à Windows (à vérifier).
Deuxi?me partie .
Programmation orientØe objets
14
Pour regrouper ses fonctions utilitaires, une pratique courante dans certains langages de programmation est d’utiliser la programmation orientée objets (POO). On crée des objets ayant certains attributs, et on les modifie ou on fait des calculs avec en appelant leurs méthodes, qui sont des fonctions spécifiques à l’objet.
Matlab offre plusieurs mécanismes pour manipuler des objets. Les plus simples sont les structures, mais elles ne permettent pas de créer de méthodes spécifiques. Depuis la version 2006 ( ?), Matlab propose une gestion simplifiée des objets, qui permet de créer ses propres types de données, mais qui reste assez limitée. Enfin, depuis 2008, la gestion des objets dans un sens plus classique a été introduite, qui permet plus de possibilités et de rigueur, au prix d’un léger apprentissage.
Un premier chapitre présente les structures de données, ainsi que les objets par valeurs, qui corerspondent à l’ancienne version des objets. Un deuxième chapitre présente la création et l’utilisation des objets par référence (« handle-based ») . Le chapitre suivant présente l’héritage de classe, qui permet de construire des objets en réutilisant une partie du code. On termine ensuite avec quelques bonnes pratiques de programmation objet.
4. Structures de donnØes et classes par valeur
Il existe plusieurs manière d’aborder la programmation orientée objets sous Matlab. Les structures de données permettent d’encapsuler et d’org des données hétérogènes. Il est ensuite possible de leur associer des fonctions, ce qui donne les objets dit « par valeur ». La gestion des objets par référence sera vue au chapitre suivant.
4.1. Structures de donnØes
Les structures permettent d’agglomérer des données hétérogènes (nombres, tableaux, chaînes, autres structures ), et sont à la base du concept de classe.
4.1.1. DØclaration et utilisation
La déclaration d’une structure est plutôt simple :
= ' Dupont ' ; personne.prenom = ' T h i e r r y ' ; = 23; |
1
2
3
On peut aussi assigner ou récupérer les champs de manière détournée, en plaçant le nom du champ dans une variable :
champ = 'nom ' ; nom = g e t f i e l d ( personne , champ) ; |
1
2
Il s’agit d’une méthode simple pour passer des arguments à une fonction. Il est aussi possible d’utiliser la syntaxe suivante :
nom = personne. ( 'nom ' ) ; |
1
4.1.2. Tableaux de structures
On peut aussi accéder aux éléments d’une structure dans un tableau :
nom3 = personnes (3) .nom ; |
1
Dans le cas d’un tableau de structures, on peut aussi récupérer l’ensemble des valeurs pour un champ donné, en utilisant une des syntaxes suivantes :
noms = { } ; ages = [ ] ; |
1
2
16
4.2 Objets « par valeur »
Un inconvénient est que l’on perd le dimensionnement du tableau de départ : on obtient en sortie un vecteur colonne. Dans les cas où c’est nécessaire, on peut redimensionner avec l’instruction reshape :
ages = reshape ( [ ] , s i z e ( personnes ) ) ; |
1
4.1.3. Fonctions utiles
Matlab fournit quelques fonctions pratiques pour manipuler les structures :
eldnames(S) liste les champs de la structure
is eld(S, NAME) pour tester si le champs NAME existe (NAME est une chaîne de caractères)
4.2. Objets par valeur
Première version des classes sous Matlab. Elles sont appelées ’value object’ dans la doc Matlab. On manipule à tout instant une copie des objets. Les conséquences directes sont (1) une occupation mémoire plus grande et (2) la nécessité de renvoyer un objet modifié dès que l’on veut changer son état.
4.2.1. DØclaration
On commence par créer un répertoire ’@MaClass’, qui contient toutes les fonctions qui s’appliqueront à ce type d’objets.
On définit ensuite un constructeur, dont la tâche principale est d’affecter un nom de classe à l’objet. Ce constructeur a le même nom que le répertoire dans lequel il est placé, sans l’arobase.
f u n c t i o n obj = MaClass ( data ) %MACLASS cree un o b j e t de type MaClass o b j . d a t a = data ; obj = c l a s s ( obj , ' MaClass ' ) ; |
1
2
3
4
Un objet se crée ensuite par un appel à la fonction ’MaClasse’
> obj = MaClass ( ' maDonnee ' ) ; |
1
4.2.2. Manipulation
Pour agir sur l’objet, on ajoute des fonctions dans le répertoire de la classe. Si la fonction est appelée, c’est qu’au moins un des arguments est un objet de la classe considérée. On peut ainsi récupérer les valeurs des champs de l’objets, et renvoyer ou afficher un résultat.
f u n c t i o n showData ( obj ) %SHOWDATA a f f i c h e l e s donnees de l ' o b j e t d i s p ( ' Donnees de l o b j e t : ' ) ; d i s p ( o b j . d a t a ) ; |
1
2
3
4
4 Structures de données et classes « par valeur »
4.2.3. Modi cation
Pour modifier un objet, il faut en créer un nouveau et le renvoyer :
f u n c t i o n obj = m o d i f i e r ( obj , newData ) %MACLASS cree un o b j e t de type MaClass o b j . d a t a = newData ; |
1
2
3
On modifie un objet de la façon suivante :
> obj = m o d i f i e r ( obj , ' Nouvelle Donnee ' ) ; > showData ( obj ) Donnees de l ' o b j e t : Nouvelle Donnee |
1
2
3
4
L’inconvénient de cette approche est que pour des objets volumineux (un polygone, une image, un maillage ), la totalité de l’objet est passée en argument, ce qui conduit à une utilisation mémoire intensive.
5. Programmation orientØe objets
Ce chapitre présente la gestion des objets introduite depuis la version 2008b. Cette version permet notamment de gérer des objets par référence, c’est à dire qui peuvent être modifiés et qui gardent trace de leur modification (contrairement aux objets pre-2008). On commence par présenter la déclaration des objets, puis des différents types de méthodes (les fonctions assoxiées aux objets).
5.1. DØclaration de classe
Les classes peuvent être définies soit entièrement dans un fichier, soit dans un répertoire propre. Dans le cas d’un répertoire, le nom du répertoire commence par un arobase (caractère ’@’), et le reste correspond au nom de la classe.
5.1.1. DØclaration
Pour chaque classe, on créé au minimum un fichier ’NomDeLaClasse.m’. Ce fichier contient la déclaration des champs, des constructeurs, et éventuellement d’autres méthodes. La définition d’une classe se fait dans un bloc « classdef », qui comprend tout le fichier :
c l a s s d e f NomDeLaClasse < NomClasseParente . . . % d é c l a r a t i o n de l a c l a s s e end |
1
2
3
Le signe « < » sert à indiquer la ou les classes parentes. Pour manipuler des objets par référence, il faut que la classe hérite de la classe « handle », ou d’une classe qui hérite ellemême de handle.
5.1.2. DØclaration des variables membre
Dans la suite du fichier, on déclare les variables locales contenues dans chaque objet. On peut spécifier des valeurs par défaut.
. . . % Déclare l e s champs à l a s u i t e p r o p e r t i e s x = 0; y = 0; end % f i n d e c l champs . . . |
1
2
3
4
5
6
7
19
5.1.3. DØclaration des mØthodes
Une méthode se déclare dans le corps de la déclaration de la classe. On commence en général par le ou les constructeurs, qui créent une nouvelle instance de la classe à partir des arguments passés en paramètre.
5.2 Création et utilisation des objets
Exemple de déclaration complète :
c l a s s d e f Point2D < handle . . . % Déclare l e s d i f f e r e n t e s methodes methods % Déclare l e c o n s t r u c t e u r f u n c t i o n t h i s = Point2D ( x , y ) t h i s . x = 0; t h i s . y = 0; end % f i n d e c l . c o n s t r u c t e u r % d e c l a r e une methode de l a c l a s s e % ( a j o u t e r l e s corodonnees de deux p o i n t s ) f u n c t i o n r e s = addCoords ( t h i s ) r e s = t h i s . x + t h i s . y ; end % f i n d e c l . de l a methode ' addCoords ' ; % d e c l a r e une a u t r e methode % ( c a l c u l de l a d i s t a n c e e n t r e deux p o i n t s ) f u n c t i o n d i s t = d i s t a n c e ( t h i s , t h a t ) d i s t = hypot ( t h i s . x - t h a t . x , t h i s . y - t h a t . y ) ; end end % f i n d e c l methodes end % f i n d e c l c l a s s e |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Il est aussi possible d’écrire chaque méthode dans un fichier séparé. Cela montre mieux quelles sont les méthodes implémentées ou surchargées, et évite d’avoir un fichier de classe trop gros.
5.2. CrØation et utilisation des objets
Pour construire un objet (c’est à dire instancier une classe), on passe par un appel au constructeur :
> p1 = Point2D (2 , 3) ; > p2 = Point2D (5 , 7) ; |
1
2
Pour appeler une méthode, plusieurs syntaxes sont possibles :
> res1 = p1.addCoords ; > res2 = p1.addCoords ( ) ; > res3 = addCoords ( p1 ) ; |
1
2
3
La deuxième syntaxe est préférable. La première introduit une confusion entre les noms de champs et de méthode, tandis que la troisième risque d’introduire des conflits de noms. (Au niveau technique, les deux premières ajoutent aussi un appel à la fonction subsref, à voir comment cela se traduit au niveau des performances).
Pour appeler des méthodes sur plusieurs objets :
> d12 = p 1 . d i s t a n c e ( p2 ) d12 = 5 |
1
2
3
Ici, la méthode est appelée sur l’objet « p1 », avec comme deuxième argument l’objet « p2 ».
page 21 sur 45
5.3. MØthodes privØes
Les méthodes privées ne sont accessibles que depuis les méthodes de la classe. Elle permettent de partager du code entre les différentes méthodes, sans que ce code ne soit rendu public. Cela concerne typiquement du code d’initialisation, ou du code de vérification de l’intégrité des données de la classe.
5.3.1. MØthodes privØes dans la dØclaration de classe
Si toutes les méthodes sont déclarées dans un même fichier, les fonctions privées doivent être déclarées dans un bloc séparé. On crée un bloc de déclarations de méthodes, pour lequel on spécifie l’accès privé « private », et on place le code des fonctions privées :
c l a s s d e f ClassName % dans l e b l oc des méthodes , on é c r i t l e patron de l a méthode methods ( Access = p r i v a t e ) f u n c t i o n r e s = maFonction ( t h i s , arg1 , arg2 ) % . . . code de l a f o n c t i o n p r i v é e end end end |
1
2
3
4
5
6
7
8
5.3.2. MØthodes privØes dans un chier sØparØ
Si on a déclaré la fonction dans un répertoire spécifique (« @-répertoire »), on peut écrire le code dans un fichier séparé. Il faut quand même déclarer la fonction le bloc de déclaration des méthodes de la classe, afin de pouvoir la rendre privée.
c l a s s d e f ClassName % dans l e b l oc des méthodes , on é c r i t l e patron de l a méthode methods ( Access = p r i v a t e ) r e s = maFonction ( t h i s , arg1 , arg2 ) end end |
1
2
3
4
5
6
On écrit ensuite le code de la méthode dans un fichier appelé « maFonction.m », qui se trouve dans le répertoire de la classe.
% f i c h i e r " maFonction.m " f u n c t i o n r e s = maFonction ( t h i s , arg1 , arg2 ) % i c i , l e code de l a f o n c t i o n p r i v é e end |
1
2
3
4
5.3.3. MØthodes locales
Quand on définit une classe dans un répertoire, on peut créer un répertoire « private ». Les fonctions présentes dans ce répertoire sont utilisables depuis les méthodes de la classe, mais ne sont pas visible de l’extérieur. Elles se comportent comme des méthodes privées, à la différence que le premier argument ne doit pas être la référence de l’objet.

5.4 Méthodes statiques
5.4. MØthodes statiques
Il est possible de déclarer des méthodes statiques, qui seront partagées par toutes les instances de la classe. Ces méthodes ne peuvent manipuler les données membres de la classe. Leur intérêt est soit de regrouper des fonctions opérant sur des objets d’une classe donnée, soit de définir des constantes ou des méthodes spécifiques à la classe d’objets utilisée.
Les méthodes statiques se déclarent de la même manière que des méthodes classiques, en ajoutant le mot-clé « Static » (avec la majuscule). Pour utiliser une méthode statique, on utilise le nom de la classe, suivi du nom de la méthode
r e s = NomDeClasse.nomDeMethode ( . . . ) ; |
1
5.4.1. Exemple 1 : calcul de ? avec une prØcision arbitraire
On commence par définir une classe « Math » contenant une méthode « pi », qui accepte un argument de tolérance numérique en entrée.
c l a s s d e f Math methods ( S t a t i c ) % c a l c u l e PI avec une p r e c i s i o n donnee f u n c t i o n p = p i ( t o l ) [n , d] = r a t ( pi , t o l ) ; p = n/d ; end end end |
1
2
3
4
5
6
7
8
9
On calcule ensuite selon différentes précisions :
pi1 = ( . 1 ) pi1 = 3.1429 pi2 = ( .001 ) pi2 = 3.1416 |
1
2
3
4
5
6
5.4.2. Exemple 2 : calcul de la distance entre deux points
écriture de la classe :
c l a s s d e f Point2D . . . methods ( S t a t i c ) % C a l c u l e l a d i s t a n c e e n t r e deux p o i n t s f u n c t i o n d = d i s t a n c e P o i n t s (p1 , p2 ) % c a l c u l e l a d i s t a n c e par Pythagore d = hypot ( p1.x - p2.x , p1.y - p2.y ) ; end % f i n de l a methode s t a t i q u e end |
1
2
3
4
5
6
7
8
9
Exemple d’appel :
p1 = Point2D (2 , 3) ; p2 = Point2D (5 , 7) : d = P o i n t 2 D . d i s t a n c e P o i n t s (p1 , p2 ) |
1
2
3
page 23 sur 45
d = | 5 . 0 |
4
5
5.4.3. Limitations
On ne peut pas se passer du nom de la classe pour appeler une classe statique. Cela implique des lignes de code un peu longues, et peut être pénible pour des hiérarchies de classe un peu touffues.
On peut redéfinir une classe statique dans une classe dérivée, à condition que la classe ne soit pas « scellée » (équivalent du mot-clé « final » en java, qui se traduit en Matlab par « Sealed = true »).
On ne peut pas avoir de champs de classe statiques. Cela peut être pénible pour certaines classes (typiquement une classe singleton). On peut s’en sortir en utilisant des variables persistantes dans le corps d’une méthode statique.
5.5. Autres mØthodes particuli?res
Certaines méthodes spécifiques sont appelées dans certains cas particuliers : set, loadobj, saveobj Un « help handle » permet de les lister, afin éventuellement de ré-implémenter celles qui le nécessitent.
6. HØritage de classe
Utiliser l’héritage permet de gagner en modularité, et parfois en clarté. Ce chapitre présente la gestion de l’héritage de classes sous Matlab, et se consacre ensuite à quelques techniques particulières que l’on peut utilsier pour l’héritage.
6.1. CrØer et utiliser une hiØrarchie de classes
6.1.1. DØclaration de l’hØritage
On utilise le signe « < », suivi du nom de la classe parente :
c l a s s d e f | C l a s s e F i l l e < C l a s s e P a r e n t e |
1
Si on déclare un objet de la classe ClasseFille, alors elle bénéficiera de toutes les fonctionalités déjà implémentées par la classe parente.
Matlab supporte l’héritage multiple, il faut utiliser le caractère « & » :
c l a s s d e f | C l a s s e F i l l e < ClasseMere & C l a s s e P e r e |
1
Par contre, il faut prendre quelques précautions pour la gestion des conflits (par exemple, si les deux classes parentes implémentent une même méthode, laquelle faut-il appeler ).
6.1.2. Appeler le constructeur parent
Exemple de constructeur appelant le constructeur de la classe parente :
methods f u n c t i o n t h i s = C l a s s e F i l l e ( v a r a r g i n ) % c a l l s the parent c o n s t r u c t o r t h i s = this@ClasseMere ( v a r a r g i n { : } ) ; end % c o n s t r u c t o r end % methods |
1
2
3
4
5
6
Cet exemple est minimaliste, l’idée est de pouvoir rajouter un traitement spécifique des entrées avant ou après l’appel au constructeur. Pour l’héritage multiple, on appelle les constructeurs parents successivement.
methods f u n c t i o n t h i s = C l a s s e F i l l e ( v a r a r g i n ) % c a l l s the parent c o n s t r u c t o r t h i s = this@ClasseMere ( ) ; % sans argument t h i s = this@ClassePere ( v a r a r g i n {1}) ; % avec argument end % c o n s t r u c t o r end % methods |
1
2
3
4
5
6
7
Pour une classe parente située dans un package, il faut ajouter le nom du package. Ex :
25
t h i s = [email protected] ( v a r a r g i n { : } ) ; |
1
Il faudrait vérifier si le constructeur vide de la classe parente est appelé par défaut ? A priori oui
6.1.3. Appeler la mØthode parente
L’appel à une méthode de la classe parente suit la même logique que pour le constructeur :
c l a s s d e f C l a s s e F i l l e < ClasseMere methods f u n c t i o n uneMethode ( obj ) % . . . p r e t r a i t e m e n t s uneMethode@ClasseMere ( obj ) ; % Appelle l a methode de l a c l a s s e parente % . . . post - t r a i t e m e n t s end end end |
1
2
3
4
5
6
7
8
9
6.2. A cher les objets
Pour gérer la manière dont les objets peuvent être affichés, on peut surcharger les méthodes disp et display :
disp affiche un résumé de l’objet, s’utilise de manière explicite (en tapant « disp obj » ou
« disp(obj) ») display fonction d’affichage, qui fait le plus souvent appel à disp, et qui est appelée quand le nom de l’objet termine une ligne de commande non terminée par un point-virgule. char le comportement par défaut de cette méthode est de convertir un objet en un tableau de caractères. Cela peut être utilisé par les fonctions d’affichage.
Si on tape une ligne de commande contenant uniquement le nom d’un objet, on a ainsi une chaîne d’appel : display->disp->char
6.3. Surcharger les opØrateurs mathØmatiques
Quand une classe a pour vocation de représenter des données calculables (par exemple, un vecteur mathématique, un quaternion ), on peut pouvoir utiliser des opérations arithmétiques classiques de manière naturelle. Cela est possible en surchargeant certaines fonctions.
Les fonctions mathématiques classiques (additions, multiplication ) peuvent être surchargées en suivant le tableau 6.1. Les fonctions se présentent souvent sous deux formes, l’une opérant sur le tableau global (nom de fonction préfixé par un « m »), l’autre sur les éléments du tableau (pour les opérateur préfixés par un point).
Il est aussi possible de surcharger des fonctions plus complexes, comme le logarithme ou l’exponentielle (voir le tableau 6.2). On peut aussi surcharger les fonctions trigonométriques, qui sont relativement nombreuses
La manipulation des tableaux binaires se fait grâce aux fonctions données dans les tableaux
6.3 et 6.4.
6.4 Surcharger subsref et subsasgn
opérateur | fonction matlab | note |
a .\ b | ldivide.m | |
a - b | minus.m | |
a \ b | mldivide.m | |
a ^ b | mpower.m | |
a / b | mrdivide.m | |
a * b | mtimes.m | |
a + b | plus.m | |
a .^ b | power.m | |
a ./ b | rdivide.m | |
a .* b | times.m | |
-a | uminus.m | opérateur unaire |
+a | uplus.m | opérateur unaire |
TABLE 6.1. – Surcharge des opérateurs arithmétiques.
fonction matlab | note |
sqrt | |
nthroot | |
exp | |
log | logarithme naturel |
log2 | logarithme binaire |
log10 | logarithme décimal |
TABLE 6.2. – Surcharge de quelques fonctions mathématiques classiques.
6.4. Surcharger subsref et subsasgn
Il est possible sous Matlab de modifier et ajuster le comportement des opérateurs d’indexation comme les parenthèses et le point. Cela peut améliorer la facilité d’utilisation, mais cela requiert un peu de technicité.
Les quelques méthodes à modifier sont les suivantes :
subsref permet de spécifier le retour des appels du type monObjet(3, 3) ou subsasgn permet de spécifier le retour des appels du type monObjet(3, 3)=2 ou = ’demo’.
end permet de changer la manière dont les indices sont calculés dans un tableau (ex :
monObjet( :, 4 :end))
subsindex permet d’utiliser un objet comme un index. Ex : res = obj1(obj2).
numel renvoie le nombre d’éléments dans un tableau. size pour avoir la taille de l’objet ou du tableau d’objets length renvoie la taille selon la plus grande dimension
page 27 sur 45
opérateur | fonction matlab | note |
a == b | eq.m | |
a >= b | ge.m | |
a > b | gt.m | |
a <= b | le.m | |
a < b | lt.m | |
a ~= b | ne.m |
TABLE 6.3. – Surcharge des opérateurs de comparaison. Ils fournissent un résultat binaire.
opérateur | fonction matlab | note |
a & b | and.m | |
~a | not.m | opérateur unaire |
a | b | or.m | |
xor.m | pas de symbole |
TABLE 6.4. – Surcharge des opérateurs logiques. Ils travaillent sur des arguments binaires.
6.4.1. Modi er la lecture des donnØes
Quelques idées de ce qu’il serait bien d’avoir pour des tableaux d’objets. On considère une classe « Point » avec deux champs x et y, et un tableau de points de 2 lignes et 3 colonnes.
obj1 = P o i n t (10 , 1) ; obj2 = P o i n t (20 , 1) ; obj3 = P o i n t (30 , 1) ; obj4 = P o i n t (10 , 2) ; obj5 = P o i n t (20 , 2) ; obj6 = P o i n t (30 , 2) ; a r r a y = [ obj1 obj2 obj3 ; obj4 obj5 obj6 ] ; |
1
2
3
4
5
6
7
array(1, 1) renvoie obj1 array(2 :3, 1 :2) renvoie un sous-tableau de même classe obj1.x renvoie 10 array(1, 1).x renvoie 10
array.x renvoie un tableau [10 20 30 ;10 20 30]
6.4.2. Modi er les donnØes
à utiliser :
a r r a y (2 , 3) = p2 ; | % a l l o c a t i o n simple |
a r r a y (2 , 3) . x = 4; | % a l l o c a t i o n de donnees ![]() |
a r r a y (23 , 12) = p24 ; | % copie d un p o i n t |
1
2
3
6.5 Surcharge des opérations de tableau
6.4.3. Utiliser subsindex
Cette méthode permet d’utiliser un objet comme un index. Ex :
r e s = obj1 ( obj2 ) ; |
1
La méthode accepte un seul argument : l’objet servant d’index. Elle renvoie un ensemble d’indices compris entre 0 et numel(obj)-1.
6.5. Surcharge des opØrations de tableau
La plupart de ces fonctions concernent principalement les tableaux définis par deux coordonnées. Les fonctions qui se traduisent par des surcharges d’opérateurs sont listées dans le tableau 6.5.
Méthodes de concaténation : vertcat concaténation verticale horzcat concaténation horizontale cat concaténation selon une dimension arbitraire repmat réplique un tableau pour faire une mosaïque kron une autre manière de répéter des tableaux
opérateur | fonction matlab | note |
a’ | ctranspose.m | |
[a b] | horzcat.m | |
a.’ | transpose.m | |
[a ; b] | vertcat.m | |
a :b | colon.m |
TABLE 6.5. – Surcharge des opérateurs manipulant la forme des tableaux des objets
Si cela a un sens pour les objets manipulés, on a aussi des fonctions permettant de changer la taille des objets
permute permute les dimensions du tableau reshape ré-organise les éléments du tableau pour avoir une nouvelle taille de tableau ipdim retourne selon une direction spécifique iplr retourne de droite à gauche ipud retourne de haut en bas rot90 rotation par 90 degrés transpose retournement par rapport à la diagonale ctranspose le même sort renvoie une version triée de l’objet
page 29 sur 45
6.6. MØthodes et classes abstraites
6.6.1. MØthode abstraite
On peut déclarer des méthodes comme abstraites. Les méthodes ne sont pas implémentées par la classe, mais seront implémentées par les sous-classes. L’intérêt est de manipuler les différentes classes concrètes à travers les méthodes déclarées dans les classes parentes, sans se soucier des détails d’implémentation.
c l a s s d e f Group % La c l a s s e Group d é c l a r e deux méthodes ' add ' e t ' times ' % Une c l a s s e h é r i t a n t de Group devra l e s implémenter t o u t e s % l e s deux avant de pouvoir ê t r e i n s t a n c i é e methods ( A b s t r a c t ) r e s u l t = add ( g1 , g2 ) r e s u l t = times ( group , k ) end end |
1
2
3
4
5
6
7
8
9
6.6.2. Classe abstraite et interface
Une classe est dite abstraite si elle comporte des méthodes abstraites. Une classe abstraite ne peut pas être instanciée (on ne peut pas créer d’objet de cette classe).
Matlab ne permet apparemment pas de déclarer comme abstraite une classe qui ne contient pas de méthode abstraite.
Si une classe ne contient que des classes abstraites, on l’appelle une interface.
6.6.3. MØthode scellØe
Il est possible de déclarer une méthode comme scellée (sealed). Une telle méthode ne pourra pas être redéfinie par une classe dérivée.
c l a s s d e f sealedDemo methods ( Sealed ) f u n c t i o n r e s = sealedMethod ( t h i s , value ) r e s = t h i s . v a l u e + value ; end end end |
1
2
3
4
5
6
7
7. Bonnes pratiques en programmation orientØe objet
J’essaie de noter ici ce que je considère comme des bonnes pratiques de programmation objet sous Matlab.
7.1. Conventions de nommage
J’utilise globalement les conventions de nommage java.
classes la première lettre en majuscule. Exemple : « NomDeClasse » mØthodes la première lettre en minuscule. Exemple : « nomDeMethode » membres pas de préfixe, mais j’évite d’utiliser des noms de fonctions matlab pour éviter les conflits. Dans le corps des fonctions, je référence systématiquement les champs par « this.nomDuChamp ».
7.2. Construction des objets
7.2.1. Constructeurs
Il faut toujours prévoir un constructeur vide : dans le cas où on déclare un tableau d’objets, le constructeur vide est appelé automatiquement.
De plus, il est préférable d’avoir systématiquement un constructeur de copie, qui équivaut à cloner les variables membres.
7.2.2. Constructeurs statiques
Les constructeurs statiques offrent une alternative intéressante à l’appel direct des constructeurs. Ils consistent à déclarer dans la classe une ou plusieurs méthodes statiques qui retrournent une instance de la classe.
On leur trouve plusieurs avantages :
— il est possible de donner un nom plus explicite pour la méthode (par exemple, une classe Point peut proposer les méthodes createCartesian ou createPolar, selon l’interprétation que l’on veut faire des arguments)
— on peut renvoyer une instance d’une classe dérivée à la place de la classe appelante. Cela permet de renvoyer un objet bien adapté aux arguments d’entrée, et facilite les évolutions ultérieures des objets.
31
7 Bonnes pratiques en programmation orientée objet
En particulier sous Matlab, il est même possible de déclarer des classes totalement abstraites (des interfaces), mais qui proposent des fabriques statiques. Les fabriques statiques renvoient (en général) une instance d’une implémentation particulière de l’interface.
7.3. GØrer des tableaux d’objets
7.3.1. CrØations d’un tableaux d’objets
Lorsque l’on itère sur plusieurs objets, il peut être préférable de pré-allouer la mémoire afin d’éviter les redimensionnements de tableau.
dim = num2cell ( s i z e ( input ) ) ; % recupere l a memoire r e s u l t ( dim { : } ) = NomDeLaClasse ( ) ; % a l l o u e un tableau , a p p e l l e l e c o n s t r u c t e u r vide f o r i =1: l e n g t h ( input ( : ) ) % boucle pour c r é e r chaque o b j e t % t r a i t e m e n t sur chaque o b j e t r e s u l t ( i ) = NomDeLaClasse ( input ( i ) ) ; end |
1
2
3
4
5
6
Le fait de passer par un tableau de cellules pour calculer la taille permet de gérer les cas de tableaux en dimension quelconque. Remarque : il ne faut pas oublier de prévoir un constructeur vide, qui sera appelé automatiquement quand le tableau sera créé.
7.3.2. Appels de mØthodes sur un tableau d’objets
Il est assez agréable d’avoir des fonctions « vectorisées », qui calculent le résultat sur tous les éléments d’un tableau sans devoir passer par une boucle. De plus, l’idéal est d’avoir en sortie un tableau de même taille que le tableau d’entrée.
Par exemple, on souhaite calculer la norme de plusieurs vecteurs stockés dans un tableau appelé « vectors ». On souhaite en sortie un tableau de double. Le contenu du fichier « getNorm », stocké dans le répertoire « Vector2D » serait le suivant :
f u n c t i o n r e s = getNorm ( t h i s ) %GETNORM Renvoie l a norme d ' un v e c t e u r dim = num2cell ( s i z e ( t h i s ) ) ; % recupere l a memoire du t a b l e a u r e s = z e r o s ( dim { : } ) ; % cree un t a b l e a u vide f o r i =1: l e n g t h ( t h i s ( : ) ) % c a l c u l e l a norme de chaque v e c t e u r r e s ( i ) = hypot ( t h i s ( i ) .x , t h i s ( i ) . y ) ; end |
1
2
3
4
5
6
7
Une alternative (ici plus concise) est d’utiliser la fonction « reshape » :
r e s = reshape ( hypot ( [ t h i s . x ] , [ t h i s . y ] ) , s i z e ( t h i s ) ) ; |
1
Dans les cas où le résultat de la fonction appelée sur un objet unique est un tableau, on ne peut plus utiliser cette stratégie. Une possibilité est de renvoyer soit le tableau dans le cas où l’entrée est un objet unique, soit un tableau de cellules si l’entrée est un tableau d’objets.
7.4. Paquetages
Depuis la version 2008a, Matlab permet aussi l’utilisation de modules « importables », à la Java par exemple. Le nom du répertoire doit commencer par le caractère « + ».
7.4 Paquetages
+mypack +mypack/ pkfonc.m % une f o n c t i o n de paquetage +mypack/@pkClass % une c l a s s e dans un paquetage |
1
2
3
L’utilisation des classes du modules se fait soit via une directive d’importation, soit en utilisant une syntaxe du type « nomDuModule.NomDeClasse ».
Exemple :
r e s = mypack.pkfunc ( data ) |
1
est équivalent à :
import mypack. ; r e s = pkfunc ( data ) |
1
2
7.4.1. Encapsulation des classes
L’utilisation des paquetages permet de faciliter la manipulation des espaces de nom. On peut aussi placer dans des sous-modules les classes utilitaires, qui ne seront ainsi pas visibles par l’utilisateur sans être explicitement importées.
7.4.1.1. Exemple 1
Conidérons un package ’+Truc’, qui contient :
— un objet Truc
— une fonction create
La fonction create peut être appelée de la manière suivante :
T r u c . c r e a t e ; |
1
et renvoie un objet de classe Truc.
7.4.1.2. Exemple 2
Autre possibilité est ce créer une classe Graph, et de placer dans un répertoire utilitaire ’+GraphImpl’ les classes des objets ’VertexHandle’, ’EdgeHandle’ La classe principale Graph importe à façon les classes nécessaires, mais l’utilisateur final n’a pas à se préoccuper du contenu de ’+GraphImpl’, et ne le voit même pas dans le chemin courant.
page 33 sur 45
Troisi?me partie .
Concepts avancØs
34
8. Interfaces graphiques
Matlab permet de développer des interfaces graphiques relativement conviviales, ce qui permet de faciliter la diffusion des outils. Par contre le développement est un peu plus techniques qu’avec certains autres langages.
8.1. Concevoir ses interfaces
On peut développer ses interfaces à la main, c’est-à-dire en positionnant chaque composant individuellement, ou bien s’aider d’outils exterieurs, tels que Guide, ou des gestionnaires de mise en page (layout managers).
8.1.1. Conception manuelle
Une méthode pour concevoir ses interfaces graphiques est de les programmer soi-même à la main. Long et vite fastidieux, mais on sait ce que l’on fait.
8.1.2. Utilitaire Guide
L’outil Guide est un environnement de conception d’interfaces utilisateur graphiques. Il permet de générer très rapidement de petites interfaces graphiques. Par contre le code généré devient très vite difficile à maintenir.
8.1.3. Utilisation de layouts
Possibilité de gérer des layouts « à la java ». Le gros avantage est que cela permet de mixer des composants redimensionnables et des composants de taille fixe, ce qui donne un aspect plus fini aux interfaces.
8.2. Les composants
8.2.1. Composants de fenŒtre
On dipose de plusieurs types d’objets graphiques :
gure c’est le conteneur principal, qui va encapsuler les autres objets graphiques uicontrol composant de base pour la plupart des widgets : boutons, boîtes à cocher, boutons radio, boîtes de texte, labels, sliders, listes
axes objet graphique qui contient les graphes et / ou les images uipanel permet d’encapsuler des widgets, et d’ajouter un peu de décorations
35
8 Interfaces graphiques
uibuttongroup utilitaire qui permet de grouper les boutons radios pour n’en avoir qu’un seul de sélectionné
uitable permet d’afficher un tableau de données.
On peut aussi trouver sur internet la manière d’afficher une arborescence (composant uitree).
Quelques attributs de composants :
TooltipString petite bulle d’aide qui s’affiche lorsque le curseur de la souris passe par dessus le composant
8.2.2. Menus
On peut aussi créer ses menus et barres d’outils. uimenu pour générer la barre de menus d’une fenêtre
uicontextmenu il s’agit d’un menu « flottant », typiquement après un clic droit
8.2.3. Barre d’outils
Les fenêtres ont par défaut une barre d’outils prédéfinie. On peut la supprimer, ou définir la sienne.
uitoolbar permet de créer sa propre barre d’outils uipushtool ajouter un bouton dans une barre d’outils uitoggletool ajoute un bouton à deux états dans une barre d’outils
8.2.4. Bo tes de dialogue prØ-dØ nies
On dispose d’un grand nombre de boîtes de dialogue prêtes à l’emploi :
— message d’erreur, d’avertissement ou d’information
— lire ou sauver un fichier
— fenêtre affichant un menu
8.3. v?nements graphiques (callbacks)
Les callback sont les portions de code qui seront exécutées lors de l’activation de l’objet
(appui sur le bouton, sélection du menu ). Il s’agit le plus souvent d’un pointeur de fonction.
8.3.1. Callback dans un objet
En utilisant la programmation orientée objet, on peut définir le callback sur une méthode de l’objet. C’est assez pratique, car cela permet de stocker les données dans la classe, et d’éviter de jongler entre les UserData des différents objets graphiques.
page 36 sur 45
8.4 Évènements fenêtre
8.3.2. Partager une fonction callback entre plusieurs objets
On peut se trouver dans le cas où plusieurs objets graphiques ont des actions très similaires. Dans ce cas on peut vouloir partager la fonction « callback » entre les menus. Pour différencier les menus, on peut spécifier des ’UserData’ différents. Exemple de déclaration de deux menus :
uimenu ( menuTransform , . . . ' Label ' , ' Rotate Y L e f t ' , . . . ' UserData ' , [2 1] , . . . ' C a l l b a c k ' , @this.onRotateImage ) ; uimenu ( menuTransform , . . . ' Label ' , ' Rotate Y Right ' , . . . ' UserData ' , [2 - 1 ] , . . . ' C a l l b a c k ' , @this.onRotateImage ) ; |
1
2
3
4
5
6
7
8
Dans le corps de la classe, on définti la fonction de callBack « onRotateImage » :
f u n c t i o n onRotateImage ( t h i s , hObject , eventdata ) data = get ( hObject , ' UserData ' ) ; t h i s . r o t a t e I m a g e ( data (1) , data (2) ) ; end |
1
2
3
4
8.4. v?nements fenŒtre
Quelques pointeurs de fonctions que l’on peut renseigner pour les objets de type fenêtre :
CreateFcn appelé quand le fenêtre s’ouvre. Vide par défaut. Si on l’initialise après que la fenêtre est créée, cela n’a pas d’effet.
DeleteFcn appelé quand la fenêtre se ferme. Vide par défaut.
CloseRequestFcn appelé quand on demande à fermer la fenêtre. Par défaut, il s’agit d’une fonction « closereq ». Possibilité de surcharger, par exemple pour demander confirmation de la fermeture, vérifier que le document est sauvé
ResizeFcn appelé quand on redimensionne la fenêtre à la souris. Vide par défaut.
On trouve aussi des fonctions pour la gestion de la souris et du clavier.
8.5. Gestion de la souris
8.5.1. Clic souris
On intercepte les clics de la souris en renseignant l’attribut « ButtonDownFcn » de l’objet graphique considéré. On applique pour cela le principe des pointeurs de fonction (function handle). On utilise en général un objet de type axis ou image.
ButtonDownFcn (objet graphique) appelée quand le bouton est enfoncé
Attention : quand on utilise ButtonDownFcn sur un objet de type axes, l’utilisation de fonctions telles que plot peut re-initialiser le callback. Il vaut donc mieux spécifier cette propriété sur un objet autre que axes (button, image ), qui lui ne sera pas affecté.
8 Interfaces graphiques
Pour savoir si le clic s’est fait avec le bouton droit, ou si les touches CTRL ou SHIFT ont été utilisées, il faut passer par la propriété « SelectionType » de la fenêtre courante. Les valeurs de cette propriété sont :
Normal un clic gauche
Extend clic gauche avec la touche shift enfoncée
Alternate un clic gauche avec la touche control, ou un clic droit
Open les deux boutons en même temps Exemple de l’aide de Matlab :
fh_cb = @newfig ; % Create f u n c t i o n handle f o r newfig f u n c t i o n f i g u r e ( ' ButtonDownFcn ' , fh_cb ) ; f u n c t i o n newfig ( src , evnt ) i f strcmp ( get ( src , ' S e l e c t i o n T y p e ' ) , ' a l t ' ) f i g u r e ( ' ButtonDownFcn ' , fh_cb ) e l s e d i s p ( ' Use c o n t r o l - c l i c k to c r e a t e a new f i g u r e ' ) end end |
1
2
3
4
5
6
7
8
9
10
8.5.2. DØplacement souris
Pour la gestion du mouvement, on passe par l’attribut « WindowButtonMotionFcn ». Par contre cet attribut n’est disponible que pour des objets de type « figure ». Du coup il faut stocker quelque part la référence de l’objet courant.
La fin de trainée de la souris est interceptée par la fonction « WindowButtonUpFcn », attribut de l’objet figure.
Attention : les fonction de type « drawnow » semblent causer un appel supplémentaire aux fonctions de type « WindowButtonXXXFcn ». Il faut donc en tenir compte lors de l’écriture du corps des fonctions.
WindowButtonDownFcn (figure) appelée quand le bouton est enfoncé WindowButtonUpFcn (figure) appelée quand le bouton est relaché
WindowButtonMotionFcn (figure) appelé quand la souris est déplacée
8.5.3. Position du curseur
La position du curseur est obtenue en récupérant la valeur de l’attribut « CurrentPoint » de l’objet axes considéré. Il s’agit d’un tableau de 2 lignes et 3 colonnes, qui correspond aux coordonnées des deux points 3D du début et de la fin du fenêtrage de la droite d’interception. C’est pratique pour les graphes 3D. Si le graphe est 2D, on n’utilise que les valeurs « point(1,1 :2) ».
8.5.4. Molette de la souris
Il est possible de gérer les évènements associés à la molette de défilement de la souris. Il faut utiliser l’attribut « WindowScrollWheelFcn ». Là aussi, ce n’est disponible que pour les
page 38 sur 45
8.5 Gestion de la souris
objets de type « figure ». Pour récupérer la direction du scroll (vers le haut ou vers le bas) on utilise la propriété VerticalScrollCount de l’argument eventdata, qui vaut -1 ou +1. Exemple :
f u n c t i o n mouseWheelScrolled ( hObject , eventData ) i f e v e n t D a t a . V e r t i c a l S c r o l l C o u n t == 1 d i s p ( ' Wheel up ' ) ; ![]() e l s e d i s p ( ' Wheel down ' ) ; end end |
1
2
3
4
5
6
7
Et on ajoute le pointeur de fonction à la fenêtre courante par :
s e t ( gcf , | ' WindowScrollWheelFcn ' , @mouseWheelScrolled ) ; |
1
9. Exceptions et gestion des erreurs
Les mécanismes à base d’exceptions permettent une certaine souplesse dans le traitement des erreurs pouvant survenir lors de l’exécution d’un programme.
9.1. Principe des exceptions
Quand une erreur survient dans un programme, le comportement par défaut est d’arrêter le fonctionnement du programme, et d’afficher l’erreur. Exemple :
>> s u r f ??? E r r o r using ==> s u r f a t 50 Not enough input arguments. |
1
2
3
Il est possible d’intercepter les erreurs, ce qui permet de L’idée est d’encapsuler les traitements générants des erreurs dans des blocs « try ».
Quand une erreur survient lors de l’éxecution d’un programme ou d’une fonction, une exception est levée. Le traitement de la fonction s’interrompt, et l’erreur est prise en charge par un bloc « catch ». Si aucun bloc catch n’est trouvé, un message d’erreur est affiché avec la liste des fonctions appelées pour produire l’erreur. Exemple
t r y % b l oc d ' i n s t r u c t i o n s pouvant poser des problèmes f i d = fopen ( filename , ' r ' ) ; d_in = f r e a d ( f i d ) ; catch ME % b l oc d ' i n t e r c e p t i o n de l ' e x c e p t i o n f p r i n t f ( ' Unable to a c c e s s f i l e %s \n ' , filename ) ; r e t u r n ; end % on reprend l e programme p r i n c i p a l |
1
2
3
4
5
6
7
8
9
10
L’objet ME est une instance d’un objet MException, qui contient quelques informations pour faciliter le traitement et le debuggage.
9.2. Traiter une exception
Les informations sur le problème sont encapsulées dans un objet de type MException. Cet objet contient les champs (publics) suivants :
identi er un identifiant d’erreur, de la forme « matlab :lib :function » message le message expliquant le type d’erreur
stack la liste des fonctions appelées avant que l’erreur ne se produise
40
9.3 Créer ses erreurs
cause une liste d’exceptions, ce qui permet éventuellement d’encapsuler les traitements d’erreur
9.3. CrØer ses erreurs
C’est en fait assez simple, il suffit d’utiliser la fonction « error ».
Matlab préconise d’utiliser un système d’identification d’erreurs. Cela permet en théorie de mieux aiguiller le traitement des erreurs. Syntaxe du type : « library :function :ErrorType ».
10. Gestion des Øv?nements
La gestion des évènements permet une grande souplesse dans la programmation. L’idée est qu’à un certain moment de son déroulement, le programme puisse envoyer un signal qui sera intercepté par des objets qui « écoutent » les évènements. Les écouteurs vont traiter l’évènement, puis le programme principal reprendra la main.
On a ainsi trois catégories d’entités :
— les objets qui envoient des évènements. Typiquement, ce sont des instances de classes assez grosses, qui stockent une liste d’instances d’écouteurs, et qui diffusent les évènements quand il y a lieu
— les évènement eux-mêmes. En Java, ce sont des objets minimalistes, avec une référence à l’objet appelant et éventuellement quelques données d’état.
— les écouteurs (listeners), qui traitent les évènements. Sous Java, ils implémentent des méthodes qui prennent en entrée un évènement, et qui ne renvoient rien.
Sous Matlab, la classe « handle » (classe de base pour les objets) propose nativement plusieurs méthodes pour gérer les évènements. Les points à retenir sont :
— on définit les évènements avec le mot-clé « events »
— on ajoute les écouteurs avec la méthode « addlistener »
— on diffuse un évènement par la méthode « notify »
— les évènements sont soit une instance de « event.EventData », soit des sous-classes de la classe « event.EventData ».
Ce chapitre présente la définition des évènement et des écouteurs, puis le mécanisme de diffusion des évènements. Enfin, une proposition de classe de type « Ecouteur » est présentée.
10.1. DØ nir un Øv?nement
On définit un évènement comme pour les propriétés de classe, par la directive « events ». On doit juste donner l’identification de l’évènement.
events Overflow end | % d e f i n i t i o n de l ' evenement |
1
2
3
C’est tout !
42
10.2 Diffuser un évènement
10.2. Di user un Øv?nement
On lance un appel aux fonctions écouteur de la façon suivante :
n o t i f y ( obj , | ' Overflow ' ) ; |
1
Ou en spécifiant le type d’évènement :
n o t i f y ( obj , | ' Overflow ' , S p e c i a l E v e n t D a t a C l a s s ( value ) ) ; |
1
Un exemple un peu plus fourni : on crée un nouvel objet avec un champ nommé « prop1 », et on envoie un évènement si on modifie ce champ avec une valeur numérique supérieure à 10.
c l a s s d e f DemoGestionEvent < handle % Ne marche que pour des c l a s s e s d e r i v e e s de handle p r o p e r t i e s Prop1 = 0; % une p r o p r i e t e de c l a s s e end events Overflow % i d e n t i f i c a t i o n de l ' evenement end methods f u n c t i o n s e t . P r o p 1 ( obj , value ) % surcharge de l a m o d i f i c a t i o n de l a orgvalue = o b j . P r o p 1 ; o b j . P r o p 1 = value ; i f ( o b j . P r o p 1 > 10) % On envoie un evenement p e r s o n n a l i s é n o t i f y ( obj , ' Overflow ' , S p e c i a l E v e n t D a t a C l a s s ( orgvalue ) ) ; end end end % f i n des methodes end % f i n du c l a s s d e f | p r o p r i e t e |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
10.3. DØ nir un listener
On a deux possibilités pour définir ses écouteurs : par pointeur de fonction et par objet spécialisé.
10.3.1. Par pointeur de fonction
L’idée est de fournir à la classe principale un pointeur vers une fonction de traitement. La fonction doit suivre le format suivant : le premier argument correspond à l’objet qui envoie l’évènement, le deuxième argument correspond à l’objet évènement proprement dit. Un exemple est donné dans le listing suivant :
f u n c t i o n overflowHandler ( eventSrc , eventData ) d i s p ( ' The value of prop1 i s v e r f l o w i n g ' ) ; d i s p ( [ ' I t s value was : ' num2str ( eventData.OrgValue ) ] ) ; d i s p ( [ ' I t s c u r r e n t value i s : ' num2str ( eve ntSr c.Pr op1 ) ] ) end |
1
2
3
4
5
Pour associer l’évènement à la classe, on utilise la méthde « addlistener », à laquelle on précise le nom de l’évènement et le pointeur de fonction qui gère l’évènement.
10 Gestion des évènements
a d d l i s t e n e r ( obj , | ' EventName ' , C a l l b a c k F u n c t i o n ) ; |
1
Dans l’exemple courant, cela correspond au code suivant :
dge = DemoGestionEvent ; % o b j e t à é co ut er a d d l i s t e n e r ( dge , ' Overflow ' , @overflowHandler ) ; |
1
2
10.3.2. Par classe spØcialisØe
Une alternative est de créer sa classe écouteur sur mesure. Il faut définir une classe qui implémente une méthode d’interception d’évènements de la manière suivante :
methods f u n c t i o n listenMyEvent ( t h i s , source , event ) % t h i s - i n s t a n c e de l a c l a s s e écouteur % source - o b j e t qui génère l ' évènement % event - l e s données a s s o c i é e s à l ' évènement . . . end end |
1
2
3
4
5
6
7
8
On peut ensuite ajouter l’écouteur (c’est à dire le pointeur de fonction de l’objet) à l’objet qui envoie les évènements.
h l i s t e n e r = a d d l i s t e n e r ( eventSourceObj , | ' Overflow ' , @obj.listenMyEvent ) |
1
10.4. Regrouper la gestion des Øv?nements
Le fait d’utiliser une classe pour intercepter les évènements a aussi l’avantage que l’on peut définir des écouteurs pour plusieurs évènements, et les regrouper.
Exemple pour la définition d’une classe abstraite pour gérer les évènements d’une procédure d’optimization :
c l a s s d e f O p t i m i z a t i o n L i s t e n e r methods ( A b s t r a c t ) o p t i m i z a t i o n S t a r t e d ( t h i s , source , event ) o p t i m i z a t i o n I t e r a t e d ( t h i s , source , event ) optimizationTerminated ( t h i s , source , event ) end |
1
2
3
4
5
6
On crée ensuite une ou plusieurs classes dérivées qui implémentent ces 3 méthodes.
Pour un objet « optimizable », on ajoute 3 évènements, et une méthode addOptimizationListener :
c l a s s d e f O p t i m i z a b l e C l a s s < handle . . . events O p t i m i z a t i o n S t a r t e d O p t i m i z a t i o n I t e r a t e d OptimizationTerminated end . . . methods f u n c t i o n a d d O p t i m i z a t i o n L i s t e n e r ( t h i s , | l i s t e n e r ) |
1
2
3
4
5
6
7
8
9
10
page 44 sur 45
10.5 Personnaliser un évènement
a d d l i s t e n e r ( t h i s , | ' O p t i m i z a t i o n S t a r t e d ' , @ l i s t e n e r . o p t i m i z a t i o n S t a r t e d ) ; |
a d d l i s t e n e r ( t h i s , | ' O p t i m i z a t i o n I t e r a t e d ' , @ l i s t e n e r . o p t i m i z a t i o n I t e r a t e d ) ; |
a d d l i s t e n e r ( t h i s , end end | ' OptimizationTerminated ' , @ l i s t e n e r . o p t i m i z a t i o n T e r m i n a t e d ) ; |
11
12
13
14
15
Exemple d’utilisation dans un script :
o p t i m i z e r = NelderMeadSimplexOptimizer ( ) ; l i s t e n e r = OptimizedValueDisplay ( f i g u r e (1) ) ; o p t i m i z e r . a d d O p t i m i z a t i o n L i s t e n e r ( l i s t e n e r ) ; o p t i m i z e r . s t a r t ( ) ; |
1
2
3
4
10.5. Personnaliser un Øv?nement
Par défaut, un évènement est une instance de classe « event.EventData ». Si on veut un traitement plus fin, il est possible de créer une classe d’évènement personnalisée :
c l a s s d e f S p e c i a l E v e n t D a t a C l a s s < event.EventData p r o p e r t i e s OrgValue = 0; end methods f u n c t i o n eventData = S p e c i a l E v e n t D a t a C l a s s ( value ) eventData.OrgValue = value ; end end end |
1
2
3
4
5
6
7
8
9
10
Comme on le voit ici, une classe d’évènement doit hériter de « event.EventData ».
.
.