Cours:Idees OOP : Différence entre versions

De troyesGEII
Aller à : navigation, rechercher
m (Corrections de l'exercice 2)
m (Exercice 2)
 
Ligne 60 : Ligne 60 :
 
</source>
 
</source>
  
2°) Faire fonctionner la correction de l'exercice 1. Puis réaliser l'affichage du compteur binaire de la correction sur deux digits
+
2°) Faire fonctionner la correction de la question 1. Puis réaliser un affichage de compteur binaire sur deux digits
  
 
3°) Réaliser un affichage de 4 digits destiné à afficher l'heure et les minutes d'un réveil.
 
3°) Réaliser un affichage de 4 digits destiné à afficher l'heure et les minutes d'un réveil.

Version actuelle datée du 12 février 2020 à 14:41

Introduction aux objets avec Processing

Programmation orientée-objet (version sympathique) peut être lue en guise de cours.

Exercice 1

Refaire les exemples développés dans la Playlist Youtube de Daniel Shiffman. On commencera par la classe Bubble développée dans le troisième cours.

Préparation à l'exercice suivant

Processing : Les Objets peut être consulté pour un rappel sur les objets. Nous allons continuer avec les tableaux ou listes.

Les listes (ou tableaux) vous propose des tableaux sur types simples (ou presque).

Pour aller plus loin, les listes d'objets sont abordées avec « Programmation orientée-objet (version sympathique) » en y cherchant la section Liste d'objets.

Array Objects montre une autre syntaxe pour les boucles for.

Exercice 2

1°) Transformer le code ci-dessous en une classe Processing pour utilisation.

Indications : on vous donne le code permettant un affichage d'un seul digit

int[] val={63,6,91,79,102,109,125,7,127,111,119,124,57,94,121,113};

void segment() {
  beginShape();
  vertex(0,10);
  vertex(10,0);
  vertex(50,0);
  vertex(60,10);
  vertex(50,20);
  vertex(10,20);
 endShape(CLOSE); 
}

void affiche7segs(int val,int rouge,int vert, int bleu) {
  pushMatrix(); // on désire laisser le repère inchangé
  noStroke();
  translate(40,0);
  if ((val & 1) == 1) fill(rouge,vert,bleu); else fill(200);
  segment(); // segment a
  translate(-10,70);
  if ((val & 64) == 64) fill(rouge,vert,bleu); else fill(200);
  segment(); // segment g
  translate(-10,70);
  if ((val & 8) == 8) fill(rouge,vert,bleu); else fill(200);
  segment(); // segment d
  translate(15,-53);
  rotate(radians(100));
  if ((val & 16) == 16) fill(rouge,vert,bleu); else fill(200);
  segment(); // segment e
  translate(-70,0);
  if ((val & 32) == 32) fill(rouge,vert,bleu); else fill(200);
  segment(); // segtment f
  translate(-12,-68);
  if ((val & 2) == 2) fill(rouge,vert,bleu); else fill(200);
  segment(); // segment b
  translate(72,0);
  if ((val & 4) == 4) fill(rouge,vert,bleu); else fill(200);
  segment(); // segment c
  popMatrix(); // on récupère le repère initial
}

2°) Faire fonctionner la correction de la question 1. Puis réaliser un affichage de compteur binaire sur deux digits

3°) Réaliser un affichage de 4 digits destiné à afficher l'heure et les minutes d'un réveil.

4°) Modifier la classe pour pouvoir changer la taille de chaque digit (!!! ANNULE !!!)

5°) Réaliser une classe "HHMMClass" de 2x2 digits séparés par deux points.

Indication : voici le début de la classe. Il ne vous reste qu'à positionner les ':' et probablement être capable de positionner cet ensemble de digits si vous voulez un jour afficher l'heure courante et l'heure réveil.

class HHMMClass {
  int posX,posY;
  int hh_mm;
  Digit[] digits = new Digit[4];
  HHMMClass() {
    posX=posY=0;
    hh_mm = 0x2340;
    for(int i=0;i<4;i++)
      digits[i]=new Digit(100*i,50);
  }
  void affiche() {
    for(int i=0;i<4;i++) {
      digits[3-i].put(hh_mm >> (4*i) & 0xF);
      digits[3-i].affiche7segs(255,0,0);
    }
  }
  void incrementHHMM() {
      (hh_mm)++;
      if ((hh_mm & 0x000F) > 0x0009)
        hh_mm += 0x0006;
      if ((hh_mm & 0x00F0) > 0x0050)
        hh_mm += 0x00A0;
      if ((hh_mm & 0x0F00) > 0x0900)
        hh_mm += 0x0600;
      if ((hh_mm & 0xFF00) > 0x2300)
        hh_mm = 0x0000;
  }
  void decrementHHMM() {
      (hh_mm)--;
      if ((hh_mm & 0x000F) == 0x000F)
      hh_mm -= 0x0006;
      if ((hh_mm & 0x00F0) == 0x00F0)
      hh_mm -= 0x00A0;
      if ((hh_mm & 0x0F00) == 0x0F00)
      hh_mm -= 0x0600;
      if ((hh_mm & 0xFFFF) > 0x2359)
      hh_mm = 0x2359;
  }
}

Faire vos propres essais avant de passer à la question suivante. L'utilisation de cette classe nécessite l'utilisation de la classe digit avec laquelle elle possède un lien fort.

6°) Utiliser cette classe pour afficher l'heure courante. On utilisera

Le code d'une horloge à aiguilles est disponible sur Internet.


7°) Modifier la classe HHMMClass pour pouvoir afficher les heures et les minutes à un endroit précis. Cela devrait ainsi vous permettre d'afficher à la fois l'heure courante et l'heure de réveil.


8°) Vous réaliserez ensuite un mécanisme de mise à jour de l'heure réveil. On pourra utiliser pour cela les fonctions souris :

et réaliser deux boutons UP et DOWN avec des rectangles.

Indications : La gestion des boutons serait mieux réalisée avec une classe Bouton. Nous vous en proposons une (assez simplifiée) :

class Bouton
{
  int etat;
  String label;
  int couleur; // en gris
  //int r,v,b; // couleur allumé
  int posX,posY,dX,dY;

  Bouton() {
    couleur = 84;
    posX=0; posY=0; dX=40; dY=40;
  }

  Bouton(int posx,int posy,String txt) {
    couleur = 84;
    posX=posx; posY=posy; dX=40; dY=40;
    label = txt;
  }

  void showOff() {
    fill(couleur);
    rect(posX,posY,dX,dY);
    textSize(24);
    fill(0,255,0);
    text(label,posX,posY+dY+24);
  }

  void showOn() {
    fill(couleur);
    rect(posX,posY,dX,dY);
    fill(0,192,0);
    ellipseMode(CORNER);
    ellipse(posX,posY,dX,dY);
    fill(0,255,0);
    ellipse(posX+5,posY+5,dX-10,dY-10);   
  }
  
  Boolean mouseIn() {
    if ((mouseX>posX) && (mouseX < posX+dX)
      && (mouseY>posY) && (mouseY < posY+dY)) 
         return true;
    else return false;
  }
  
}

9°) Pour finir, il ne vous reste qu'à faire le mécanisme qui fait sonner le réveil quand l'heure courante est égale à l'heure réveil. Vous pourrez vous inspirer de la correction trouvée dans la Wikiversité.

Indications :

  • Le bouton d'armement est un peu particulier puisqu'il mémorise son état. Il faut donc spécialiser la classe bouton de la question précédente.
  • Il existe une classe Sound pour processing, mais vous devez vérifier si elle est installée ou pas.


10°) Utiliser l'héritage pour fabriquer un bouton de type switch.

Indications :

  • Cours Association et héritage appliqué à notre problématique. Il y a la réponse dans l'exercice 6 de ce cours.
  • Et le code ressource :
    • La classe BoutonSuper
class BoutonSuper {
  String label;
  int couleur; // en gris
  int posX,posY,dX,dY;
  BoutonSuper() {
    couleur = 84;
    posX=0; posY=0; dX=40; dY=40;
  }
  BoutonSuper(int posx,int posy,String txt) {
    couleur = 84;
    posX=posx; posY=posy; dX=40; dY=40;
    label = txt;
  }
  void showOff() {
    fill(couleur);
    rect(posX,posY,dX,dY);
    strokeWeight(2);
    stroke(0);
    line(posX,posY+dY,posX+dX,posY+dY);
    line(posX+dX,posY,posX+dX,posY+dY);
    stroke(255);
    line(posX,posY,posX+dX,posY);
    line(posX,posY,posX+dX,posY);
    strokeWeight(1);
    noStroke();
    textSize(24);
    fill(0,255,0);
    text(label,posX,posY+dY+24);
  }
  void showOn() {    
  }
  Boolean mouseIn() {
    if ((mouseX>posX) && (mouseX < posX+dX)
      && (mouseY>posY) && (mouseY < posY+dY)) 
         return true;
    else return false;
  }
}
    • La classe Bouton
class Bouton extends BoutonSuper
{
    Bouton(int posx,int posy,String txt) {
    couleur = 84;
    posX=posx; posY=posy; dX=40; dY=40;
    label = txt;
  }
  void showOn() {
    fill(couleur);
    rect(posX,posY,dX,dY);
    fill(0,192,0);
    ellipseMode(CORNER);
    ellipse(posX,posY,dX,dY);
    fill(0,255,0);
    ellipse(posX+5,posY+5,dX-10,dY-10);
  }
}
    • La classe Switch
class Switch extends BoutonSuper
{ 
  int etat;
  Switch(int posx,int posy,String txt) {
    couleur = 84;
    posX=posx; posY=posy; dX=30; dY=40;
    label = txt;
    etat=0;
  }
  void showOff() {
    fill(couleur);
    rect(posX,posY,dX,dY);
    strokeWeight(2);
    stroke(0);
    line(posX,posY+dY,posX+dX,posY+dY);
    line(posX+dX,posY,posX+dX,posY+dY);
    stroke(255);
    line(posX,posY,posX+dX,posY);
    line(posX,posY,posX+dX,posY);
    strokeWeight(1);
    noStroke();
    textSize(24);
    fill(0,255,0);
    text(label,posX,posY+dY+24);
  }
  void switchEtat() {
    etat ^= 1;
  }
  void showOn() {
    fill(couleur);
    rect(posX,posY,dX,dY);
    fill(0,192,0);
    ellipseMode(CORNER);
    ellipse(posX,posY,dX,dY);
    fill(0,255,0);
    ellipse(posX+5,posY+5,dX-10,dY-10);
  }
  void show() {
    if (etat == 0) showOff(); else showOn();
  }
}
    • et un exemple d'utilisation :
Bouton bouton1 = new Bouton(50,50,"UP");
Switch switch1 = new Switch(150,50,"SW0");
void setup() {
  size(300,300);
  frameRate(10);
}
void draw() {
    bouton1.showOff();
    switch1.show();
}
void mousePressed() {
  if (bouton1.mouseIn()) {
      bouton1.showOn();
    } else if (switch1.mouseIn()) {
      switch1.switchEtat();
    }
}

Poursuivez avec l'exercice 7 du cours ci-dessus qui change seulement l'apparence du bouton.

Corrections de l'exercice 2

Éléments de correction

Exercice3

Caractères matriciel et affichage LCD.

TP programmation Objet

L'objectif de ce TP sur deux séances de trois heures est de réaliser un clavier de Piano capable de d'envoyer les changements d'états de ses touches par une liaison série. Les changements d'états des touches seront réalisés par la souris. Vous devrez donc être capable de gérer les deux événement souris "mousePressed()" et "mouseReleased()". Nous aurons en bout de la liaison série, un Arduino Leonardo qui lui-même sera relié à un amplificateur sonore

Code de départ

Pour vous aider, quelques ressources vous sont fournies.

Un clavier piano fonctionnel sur une octave

Un code de départ vous est fourni pour vous éviter de chercher les tailles et la façon de représenter votre clavier. Ce code n'envoie absolument rien sur la liaison série mais affiche dans le moniteur de Processing la touche appuyée ou relachée.

int spazio = 50;
int value;

void setup(){
size(400,400);
  //minim = new Minim(this); 
}

void draw(){
background(0);
stroke(0);
for (int x = 0; x <= width; x = x + spazio ){
  fill(255); // white
  rect(x - 50,0,x,height);
}
fill(0); // black
rect(35, 0, 30, 250);
rect(85, 0, 30, 250);
rect(185, 0, 30, 250);
rect(235, 0, 30, 250);
rect(285, 0, 30, 250);
rect(385, 0, 30, 250);
}

void mouseReleased() {
    value = 0;
    println(value);
}

void mousePressed(){
  value=0;
 //do 
if (mouseX < 35 && mouseY < 250 || mouseX < 50 && mouseY > 250){
  //song = minim.loadFile("Do1.mp3");
  //song.play();
  value = 1;
  //println(value);
} 
//re
if (mouseX > 65 && mouseX < 85 && mouseY < 250){
  //song = minim.loadFile("Re1.mp3");
  //song.play();
  value = 3;
  //println(value);
} 
if (mouseY > 250 && mouseX > 50 && mouseX < 100 || value == 3){
  //song = minim.loadFile("Re1.mp3");
  //song.play();
  value = 3;
  //println(value);
} 
//mi
if (mouseX > 115 && mouseX < 150 && mouseY < 250){
  //song = minim.loadFile("Mi1.mp3");
  //song.play();
  value = 5;
  //println(value);
} 
if ( mouseY > 250 && mouseX > 100 && mouseX < 150 || value == 5){
  //song = minim.loadFile("Mi1.mp3");
  //song.play();
  value = 5;
  //println(value);
} 
//fa
if (mouseX > 150 && mouseX < 185 && mouseY < 250){
  //song = minim.loadFile("Fa1.mp3");
  //song.play();
  value = 6;
  //println(value);
} 
if (mouseY > 250 && mouseX > 150 && mouseX < 200 || value == 6){
  //song = minim.loadFile("Fa1.mp3");
  //song.play();
  value = 6;
  //println(value);
} 
//sol
if (mouseX > 215 && mouseX < 235 && mouseY < 250){
  //song = minim.loadFile("Sol1.mp3");
  //song.play();
  value = 8;
  //println(value);
} 
if (mouseY > 250 && mouseX > 200 && mouseX < 250 || value == 8){
  //song = minim.loadFile("Sol1.mp3");
  //song.play();
  value = 8;
  //println(value);
} 

//la
if (mouseX > 265 && mouseX < 285 && mouseY < 250){
  //song = minim.loadFile("La1.mp3");
  //song.play();
  value = 10;
  //println(value);
} 
if (mouseY > 250 && mouseX > 250 && mouseX < 300 || value == 10){
  //song = minim.loadFile("La1.mp3");
  //song.play();
  value = 10;
  //println(value);
} else


//si 
if (mouseX > 315 && mouseX < 350 && mouseY < 250){
  //song = minim.loadFile("Si1.mp3");
  //song.play();
  value = 12;
  //println(value);
} 
if (mouseY > 250 && mouseX > 300 && mouseX < 350 || value == 12){
  //song = minim.loadFile("Si1.mp3");
  //song.play();
  value = 12;
  //println(value);
} 

//do2
if (mouseX > 350 && mouseX < 385 && mouseY < 250){
//  song = minim.loadFile("Do2.mp3");
  //song.play();
  value = 13;
  //println(value);
} 
if (mouseY > 250 && mouseX > 350 && mouseX < 400 || value == 13){
  //song = minim.loadFile("Do2.mp3");
  //song.play();
  value = 13;
  //println(value);
} 

// I bemolli
//do#
if ( mouseX > 35 && mouseX < 65 && mouseY < 250){
  //song = minim.loadFile("Do#1.mp3");
  //song.play();
  value = 2;
} 
//re#

if ( mouseX > 85 && mouseX < 115 && mouseY < 250){
  //song = minim.loadFile("Re#1.mp3");
  //song.play();
  value = 4;
} 
// fa#
if ( mouseX > 185 && mouseX < 215 && mouseY < 250){
  //song = minim.loadFile("Fa#1.mp3");
  //song.play();
  value = 7;
} 
// sol#
if ( mouseX > 235 && mouseX < 265 && mouseY < 250){
  //song = minim.loadFile("Sol#1.mp3");
  //song.play();
  value = 9;
} 
//la#
if ( mouseX > 285 && mouseX < 315 && mouseY < 250){
  //song = minim.loadFile("La#1.mp3");
  //song.play();
  value = 11;
}
  println(value);
}

Ce code est un bon point de départ mais il comporte plusieurs défauts :

  1. si le graphisme n'est pas trop mal, son principe est de dessiner d'abord les touches blanches puis, par dessus, les touches noires.
  2. les touches ne sont pas des objets et donc il est difficile de gérer une couleur appuyée et une couleur relachée

La classe Bouton

La question 8 de l'exercice 2 introduit la notion de classe pour un bouton et vous seriez bien inspiré de relire le code correspondant.

Travail à réaliser

  1. Pour éviter de travailler avec de l'héritage, vous allez définir 4 types de touches : 3 blanches et 1 noire. Par défaut ces touches seront considérées comme dessinées en coordonnée (0,0) en haut à gauche. Le constructeur devra contenir la définition du type de touche qui déterminera complètement le graphisme de la touche. Pévoir aussi une couleur pour touche non appuyée (blanc ou noir) et une couleur pour touche appuyée.
  2. Décomposer chacune des touches blanches en deux rectangles dont on déterminera les coordonnées à partir de l'exemple donné. Évidemment, au moment du dessin il ne faudra pas dessiner de bords aux rectangles sauf sur le côté gauche et le côté droit (line et le stroke associé).
  3. Traiter le cas des touches noires
  4. Décider de la taille et de la forme la zone dans laquelle il faudra appuyer pour considérer un appui de touche. La mise en place de cette zone sera faite par le constructeur en fonction du type de touche.
  5. A jouter une possibilité de transfert vers la droite pour chacun des types de touche
  6. Gérer la mise de l'ensemble des touches dans un tableau (Les listes (ou tableaux))
  7. Tester avec des print et/ou des println
  8. Gérer la liaison série (Serial) en lieu et place de l'affichage dans la console processing.

Correction partielle sans liaison série

class ClassPianoKey
{
  int _note;
  int _type; // type d la touche
  int couleurOff, couleurOn; // noir, blanc ou  gris
  int offsetX;
  int clickAreaPosX,clickAreaPosY,clickAreaDX,clickAreaDY;
  
 ClassPianoKey(int deltaX,int type,int note) {
    _note = note;
    _type = type;
    offsetX = deltaX;
    if (type < 4) { // touche blanche
      couleurOff = 255;
      couleurOn = 200;
      clickAreaPosX=offsetX;
      clickAreaPosY = 250; 
      clickAreaDX= 50;
      clickAreaDY = 150;
    } else {
      couleurOff = 0;
      couleurOn = 50;
      clickAreaPosX=offsetX;
      clickAreaPosY = 0; 
      clickAreaDX= 30;
      clickAreaDY = 250;
    }
    
  }

  void showOff() {
    if (_type == 1) {
      fill(couleurOff);
      stroke(couleurOff);
      rect(0+offsetX,0,35,250);
      rect(0+offsetX,250,50,150);
      // black
      stroke(0);
      line(0+offsetX,0,0+offsetX,400);
      line(0+offsetX+50,0+250,0+offsetX+50,400);
    }
    if (_type == 2) {
      fill(couleurOff);
      stroke(couleurOff);
      rect(0+offsetX+15,0,35,250);
      rect(0+offsetX,250,50,150);
      stroke(0);
      line(0+offsetX,0+250,0+offsetX,400);
      line(0+offsetX+50,0,0+offsetX+50,400);
    }
    if (_type == 3) {
      fill(couleurOff);
      stroke(couleurOff);
      rect(0+offsetX+15,0,20,250);
      rect(0+offsetX,250,50,150);
      stroke(0);
      line(0+offsetX,0+250,0+offsetX,400);
      line(0+offsetX+50,0+250,0+offsetX+50,400);
    }
    if (_type == 4) {
      fill(couleurOff);
      stroke(couleurOff);
      rect(0+offsetX,0,30,250);      
    }
    
  }

  void showOn() {
    if (_type == 1) {
      fill(couleurOn);
      stroke(couleurOn);
      rect(0+offsetX,0,35,250);
      rect(0+offsetX,250,50,150);
      // black
      stroke(0);
      line(0+offsetX,0,0+offsetX,400);
      line(0+offsetX+50,0+250,0+offsetX+50,400);
    }
    if (_type == 2) {
      fill(couleurOn);
      stroke(couleurOn);
      rect(0+offsetX+15,0,35,250);
      rect(0+offsetX,250,50,150);
      stroke(0);
      line(0+offsetX,0+250,0+offsetX,400);
      line(0+offsetX+50,0,0+offsetX+50,400);
    }
    if (_type == 3) {
      fill(couleurOn);
      stroke(couleurOn);
      rect(0+offsetX+15,0,20,250);
      rect(0+offsetX,250,50,150);
      stroke(0);
      line(0+offsetX,0+250,0+offsetX,400);
      line(0+offsetX+50,0+250,0+offsetX+50,400);
    }
    if (_type == 4) {
      fill(couleurOn);
      stroke(couleurOn);
      rect(0+offsetX,0,30,250);      
    }
    
  }
  
  Boolean mouseIn() {
    if ((mouseX>clickAreaPosX) && (mouseX < clickAreaPosX+clickAreaDX)
      && (mouseY>clickAreaPosY) && (mouseY < clickAreaPosY+clickAreaDY)) 
         return true;
    else return false;
  }
  
}

Et enfin le programme principal :

ClassPianoKey[] pianokeys = new ClassPianoKey[15];

void setup() {
  size(400,400);
  frameRate(1); 
  pianokeys[0]= new ClassPianoKey(0,1,1);  //do
  pianokeys[1]= new ClassPianoKey(35,4,2);  //do#
  pianokeys[2]= new ClassPianoKey(50,3,3);  //re
  pianokeys[3]= new ClassPianoKey(85,4,4);
  pianokeys[4]= new ClassPianoKey(100,2,5);
  pianokeys[5]= new ClassPianoKey(150,1,6);
  pianokeys[6]= new ClassPianoKey(185,4,7);
  pianokeys[7]= new ClassPianoKey(200,3,8);
  pianokeys[8]= new ClassPianoKey(235,4,9);
  pianokeys[9]= new ClassPianoKey(250,3,10);
  pianokeys[10]= new ClassPianoKey(235,4,11);
  pianokeys[11]= new ClassPianoKey(250,3,12);
  pianokeys[12]= new ClassPianoKey(285,4,13);
  pianokeys[13]= new ClassPianoKey(300,2,14);
  pianokeys[14]= new ClassPianoKey(350,1,15);
  for (int i=0;i<15;i++) {
    pianokeys[i].showOff();
  }
}

void draw() {
  
}

void mousePressed() {
  for (int i=0;i<15;i++) {
    if (pianokeys[i].mouseIn())
      pianokeys[i].showOn();
  }  
}

void mouseReleased() {
  for (int i=0;i<15;i++) {
    if (pianokeys[i].mouseIn())
      pianokeys[i].showOff();
  }  
}

Malheureusement l'ensemble est un peu lent pour être utilisé pour réaliser de la musique !!!

Voir aussi