RadioCoche

Beaucoup d'informations sont données pour le bouton PushZone qui ne sont pas toutes reprises ici. Une lecture de PushZone est une bonne idée.

RadioXXXX sont des classes de boutons radio comme on avait sur les vieux postes. Un bouton par gamme FM1, FM2, MW, SW, en appuyant sur un bouton, cela fait remonter les autres. Les commandes de volets roulants sont de ce type, ainsi que le choix d'un programme de lavage... Pour cette bibliothèque, lorsqu'on appuie dessus, le bouton s'active (ou reste actif), et un événement onSelect est généré. Lorsque l'on relâche l'appui, ou si l'appui sort de la zone d'action, il ne se passe rien. Si on appuie sur un bouton radio, cela va désactiver les autres boutons radio du même groupe.

On peut avoir plusieurs groupes indépendants. Avec cette bibliothèque, les limitations sont:
- le nombre maximum de boutons dans tous les groupes est le même et c'est une puissance de 2
- le nombre maximum de séries est une puissance de 2
- le produit des deux est 128
Par défaut, on peut avoir 4 groupes de 32 boutons. Ces nombres peuvent se changer au début du fichier PecheuxGraph_ILI9341_8bits.h . Dans les premières lignes on trouve:

#define RADIO_NB_BITS_GROUPE 2 // Nombre de bits pour la définition du groupe radio 0..6
                               // 0: 1 seul groupe de 128 boutons maxi
                               // 1: 2 groupes maxi de 64 boutons maxi chacun
                               // 2: 4 groupes maxi de 32 boutons maxi
                               // 3: 8 groupes maxi de 16 boutons maxi
                               // 4: 16 groupes maxi de 8 boutons maxi
                               // 5: 32 groupes maxi de 4 boutons maxi
                               // 6: 64 paires de boutons maxi

Il suffit de changer le chiffre 2 en rouge pour ajuster les nombres à nos besoins.

On peut attribuer à chaque bouton une valeur comprise entre 0 inclus et le nombre maximum de boutons exclu (par défaut ente 0 et 31, tout excès sera tronqué). On n'est pas obligé, mais cela peut être pratique pour savoir quel bouton est sélectionné.

Un groupe est un ensemble de boutons se désactivant les uns les autres, mais ne touchant pas aux autres groupes. On peut attribuer à chaque groupe un nom GROUPE_0, GROUPE_1, GROUPE_2 ou GROUPE_3. Cela correspond aux nombres de 0 à 3, mais c'est plus lisible.

Trois fonctions vont permettre de gérer efficacement les boutons radio:
- unselectRadio() permet de désélectionner tous les boutons radios d'un groupe
- getRadioValeur() nous donne la valeur du contrôle actif du groupe
- getRadioPointeur() retourne l'adresse du contrôle actif, ce qui nous permet d'accéder à toutes ses données, par exemple sa position.

XXXXCoche sont des classes de boutons carrés avec une coche quand ils sont actifs. Le carré visible mesure 11 pixels sur 11 pixels, mais la zone sur laquelle on peut pointer est définie par les paramètres passés au constructeur. Elle peut être plus grande ou plus petite. Le carré se positionne au milieu, ce n'est qu'un dessin.

RadioCoche est donc une classe de boutons radio carrées avec une coche quand ils sont actifs.

 

Exemples d'utilisation

RadioCoche bouton(0,0,LARGEUR,HAUTEUR); // Crée bouton sur toute la surface de l'écran
RadioCoche bouton(150,100,170,120); // Crée bouton au centre en mode PAYSAGE 320x240
RadioCoche bouton = new RadioCoche(100,100,120,120); // Crée une instance dynamique bouton
new RadioCoche(100,100,120,120); // Crée une instance dynamique bouton sans pointeur dessus

circle(bouton.demiX1+bouton.demiX2, bouton.demiY1+bouton.demiY2, 10); // Cercle centré sur le bouton

bouton.onSelectFunction = &action; // Appel la fonction void action(void) lors de l'appui
bouton.onUnselectFunction = &action; // Appel la fonction void action(void) lors du relâchement

if (bouton.isSelect())... // Si on est en train d'appuyer sur le bouton

groupe = getRadioValeur();// groupe prend la valeur du bouton sélectionné du groupe GROUPE_0 
groupe = getRadioValeur(GROUPE_1); // groupe prend la valeur du bouton sélectionné du groupe GROUPE_1
unselectRadio(); // Désélectionne les boutons radio du groupe GROUPE_0 

 

Référence

class RadioCoche
 public:
  RadioCoche(int x1, int y1, int x2, int y2, char valeur = 0, char groupe = 0);
  byte demiX1, demiY1, demiX2, demiY2;
  word buttonColor;
  void (*onSelectFunction)(void);
  void (*onUnselectFunction)(void);
  RadioCoche *controleSuivant;
  void select(void);
  void unselect(void);
  boolean isSelected(void);
  virtual void onSelect(void);
  virtual void onUnselect(void)
  virtual void drawButton(void);

Attributs et méthodes

RadioCoche(): Constructeur, s'insère dans la liste des contrôles gérées. x1, y1, x2, y2: zone d'action

demiX1, demiY1, demiX2, demiY2: Occupation de l'espace dans l'écran, coordonnés absolues DIVISEES par 2

buttonColor: Couleur du fond des boutons de la classe

*onSelectFunction: : Pointeur sur la fonction à appeler lorsque le bouton est sélectionné

*onUnselectFunction: : Pointeur sur la fonction à appeler lorsque le bouton est désélectionné

*controleSuivant: Pointeur sur le contrôle suivant dans la liste des contrôles

select(): : Active ce bouton et désactive tous les boutons du groupe.

unselect(): : Désactive ce bouton. Si il était actif, plus aucun bouton de ce groupe ne l'est.

isSelected(): : Informe de l'état du bouton.

onSelect(): : Fonction appelée lors de la sélection. On peut surcharger cette fonction, par défaut elle est vide

onUnselect(): : Fonction appelée lors de la désélection. On peut surcharger cette fonction, par défaut elle est vide

drawButton(): : Fonction appelée lors de l'activation, la désactivation, à l'initialisation de la librairie ou sur appel de la fonction drawControles(). On peut surcharger cette fonction, par défaut elle est vide pour les boutons XXXXZone, et fait ce qu'il faut pour les autres. On peut surcharger cette méthode, mais si on veut garder le dessin par défaut, il faut appeler la méthode drawButton() de la classe mère.

 

Voir aussi

- unselectRadio() Désélectionne tous les boutons radios d'un groupe
- getRadioValeur() Donne la valeur du contrôle actif du groupe
- getRadioPointeur() retourne l'adresse du contrôle actif
- scanEvent(); Moteur de la gestion des événements
- PushZone; Bouton poussoir sans dessin
- CheckZone; Bouton bistable (va vient) sans dessin
- RadioZone; Bouton radio (un seul bouton actif parmi plusieurs) sans dessin
- RadioCircle; Bouton radio (un seul bouton actif parmi plusieurs) rond

 

Notes

Pour définir un bouton, il faut donner sa zone d'action. Pour des raisons de stockage mémoire et de précision, n'est enregistré que les nombres pairs des valeurs passées. Les nombres impairs sont arrondis au nombre pair inférieur. Ainsi la zone (100,100,201,201) est équivalente à (100,100,200,200).

Si on a plusieurs boutons dont les zones d'actions ont une partie commune, comme les boutons sont analysés dans l'ordre inverse de leur déclaration, la zone commune devient propriété du bouton défini en dernier. Cela évite les conflits. Cela permet aussi d'avoir un bouton dont la zone n'est pas rectangulaire, il peut manquer un coin.

 

Exemples

Premier exemple: 4 séries de 10 boutons RadioCoche

PecheuxGraph_ILI9341_8bits.zip\PecheuxGraph_ILI9341_8bits\examples\Documentation\Exemple-950-RadioCoche\Exemple-950-RadioCoche.ino (dans votre fichier téléchargé):

// Ceci montre un exemple d'utilisation de boutons radio
// - avec quatre séries
// - avec des boutons dynamiques trop de boutons pour écrire sans boucle!

#include <PecheuxGraph_ILI9341_8bits.h>

word valeur; // Ce qui est écrit avec les boutons
void onTimeAffiche(void) // Cette fonction met à jour l'affichage
{
  valeur = 0; // Lecture des boutons radio et calcul de la valeur à afficher
  for (byte chiffre = 0; chiffre < 4; chiffre++) // 4 chiffres à lire, on commence par les milliers
    valeur = (valeur * 10 + (getRadioValeur(chiffre))); // A chaque fois on décale d'un chiffre
  fillRect(0, 0, 83, 25, BLACK); // Efface ce qui est nécessaire
  setTextCursor(0, 0); // Écrire toujours au même endroit
  text(String(valeur)); // Écrit la valeur
}

void setup()
{
  RadioCoche *radio; // Pointeur sur un bouton, nécessaire pour présélectionner les zéros
  Clock *horloge = new Clock(1000); // Pour un rafraîchissement toutes les secondes
  horloge->onTimeFunction = &onTimeAffiche; // Pour appeler la fonction d'affichage toutes les secondes

  // Initialisations
  setGraphMode(PAYSAGE); // Initialisation générale de l'afficheur
  setTextCursor(0, 100);
  text("Cliquez\nsur\nles\nboutons"); // Dire un minimum ce qu'il faut faire

  // Création de boutons dynamiques, doit être fait ici après setGraphMode() car on utilise LARGEUR
  for (word colonne = 0; colonne < 150; colonne += 40) for (word ligne = 0; ligne < 200; ligne += 20) // Pour avoir une matrice de boutons
    { // Création d'un bouton
      // valeur sera 0, 1, 2, ... soit le dernier paramètre ligne/20
      radio = new RadioCoche(LARGEUR - 160 + colonne, ligne, LARGEUR - 140 + colonne, ligne + 20, /*parametres =*/ligne / 20, /*groupe =*/colonne / 40); // Zones de 20x20
      if (ligne == 0) radio->select(); // Présélection des 0
      // Nom du bouton
      setTextCursor(LARGEUR - 140 + colonne, ligne + 2);
      text(String(ligne / 20)); // Affichage des chiffres 0, 1, 2, 3, ...
    }

  // Pour avoir un bouton cyan/rouge, pour montrer comment on fait, pour la démo
  RadioCoche::buttonColor = CYAN; // Sera vrai pour tous les boutons radio, cela diminue le nombre de données


  drawControles(); // Il faut tout dessiner car les contrôles sont définis après setGraphMode()
  setTextSize(4); // Pour bien voir le résultat
}

void loop()
{
  scanEvent(); // Gestion des boutons et des horloges, le plus souvent seul dans le loop
}

Résultat:

 

Deuxième exemple: Petit QCM

PecheuxGraph_ILI9341_8bits.zip\PecheuxGraph_ILI9341_8bits\examples\Documentation\Exemple-951-RadioCoche-QCM\Exemple-951-RadioCoche-QCM.ino (dans votre fichier téléchargé):

// Attention: ce programme ne fonctionne qu'en mode paysage (inversé ou non)

// Ceci montre un exemple d'utilisation de boutons radio avec un gros mélange
// de genre

#include <PecheuxGraph_ILI9341_8bits.h>


//#####################################®#####################################
//##                               RadioPerso                              ##
//#####################################®#####################################
// On peut dériver une classe, normalement pour changer un comportement
// Définition de la classe RadioPerso pour le bouton 8
class RadioPerso: public RadioZone
{
  public:
    RadioPerso(int x1, int y1, int x2, int y2): RadioZone(x1, y1, x2, y2, 0, 0) {}
    void drawButton(void)
    { // Dessine un disque comme les RadioCircle
      word x = demiX1 + demiX2; // se placer au milieu de la zone
      word y = demiY1 + demiY2;
      fillCircle(x, y, 4, isSelected()? RED : CYAN); // Y tracer un disque de rayon 4
      circle(x, y, 5,  isSelected()? CYAN : RED); // Et une bordure opposée
    }
};


//#####################################®#####################################
//##                         Définition des boutons                        ##
//#####################################®#####################################
// bouton1 est un RadioCoche qui en plus devient jaune si il est actif
// bouton2 est un RadioCoche
// bouton3 est un RadioCoche crée avec new dans le setup
// bouton4 est un RadioCircle crée avec new avant le setup
// bouton5 est un RadioCircle
// bouton6 est un RadioCircle qui en plus devient jaune si il est actif
// bouton7 est un RadioZone mais fait croire qu'il est un RadioCircle
// bouton8 est un RadioPerso mais fait aussi croire qu'il est un RadioCircle
// bouton9 est un RadioCoche mais fait aussi croire qu'il est un RadioCircle

RadioCoche bouton1(10, 20, 30, 40);
RadioCoche bouton2(10, 44, 30, 64);
// RadioCoche bouton3(10, 68, 30, 88); // créé dans le setup
RadioCircle *bouton4 = new RadioCircle(10, 92, 30, 112);
RadioCircle bouton5(10, 116, 30, 136);
RadioCircle bouton6(10, 140, 30, 160);
RadioZone bouton7(10, 164, 30, 184);
RadioPerso bouton8(10, 188, 30, 208);
RadioCoche bouton9(10, 212, 30, 232);


//#####################################®#####################################
//##                                 action                                ##
//#####################################®#####################################
// C'est cette fonction qui est appelée chaque fois que l'on touche à un
// bouton. C'est elle qui gère tout le programme une fois l'affichage terminé
boolean run = true; // bloque scanEvent() quand c'est fini
void action() // Se produit quand on clique sur un bouton radio
{
  // getRadioValeur() retourne ici pour l'exemple 0 quel que soit le bouton. Mais
  // je peux savoir quel bouton est appuyé en analysant l'ordonnée "y" passée
  int y = getTouchY();
  y = (y + 5) / 24; // N° du bouton 1..9
  if (y == 9) // Gagné
  {
    run = false; // bloque le scanEvent()
    clrscr(); // Affiche en gros le mot "Gagné"
    setTextSize(5);
    setTextCursor(100, 100);
    text(F("Gagné"));
  }
  else // on a appuyé sur une phrase fausse
  {
    fillRect(35, y * 24 - 3, 319, y * 24 + 18, BLACK); // Efface l'affirmation
    setTextColor(RGBcolor(31,50,25)); // On écrira la correction en rouge
    setLimites(35, 0, 319, 239); // PushZone du texte; évite d'avoir à utiliser un setTextCursor() après un retour chariot
    switch (y) // C'est le n° du bouton
    {
      case 1:
        setTextCursor(35, 22); text(F("Faux: 1 et 2 sont des RadioCoche. Inactif, l'un est gris,\nl'autre noir. Actif 1 devient jaune")); break;
      case 2:
        setTextCursor(35, 46); text(F("Faux: 5 et 6 sont des RadioCircle et sont nettement de\ncouleur différentes")); break;
      case 3:
        setTextCursor(35, 70); text(F("Faux: 7 est un RadioZone avec une fonction de dessin\nvia onFunctionSelect()")); break;
      case 4:
        setTextCursor(35, 94); text(F("Faux: 1 et 9 sont tous deux des RadioCoche, mais 9 a une\nfonction draw externe qui le redessine")); break;
      case 5:
        setTextCursor(35, 122); text(F("Faux: ici la moitié sont carrés, l'autre moitié ronds")); break;
      case 6:
        setTextCursor(35, 142); text(F("Faux: ici il n'y a qu'un groupe, mais 2 est RadioCoche, 4\nest RadioCircle, 7 est RadioZone, et 8 vient d'une sous-classe")); break;
      case 7:
        setTextCursor(35, 172); text(F("Faux: 3 et 4 sont dynamiques, les autres sont statiques")); break;
      case 8:
        setTextCursor(35, 194); text(F("Faux: ici, n'utilisant pas valeur, tous les boutons on un\nattribut valeur nul")); break;
    }
    setLimites(); // On peut maintenant dessiner sur tout l'écran (les boutons radios se redessinent quand ils se sélectionnent ou se désélectionnent
  }
}


//#####################################®#####################################
//##                                 cercle                                ##
//#####################################®#####################################
// Les boutons qui se prennent pour un RadioCircle utilisent cette fonction pour se dessiner
void cercle7() // les boutons 7 8 et 9 se dessinent
{
  if (bouton7.isSelected())
  {
    fillRect(15, 169, 25, 179, BLACK); // Efface l'ancien dessin qui ne nous plaît pas
    fillCircle(20, 174, 4, RED); // Dessin qui efface éventuellement la coche
    circle(20, 174, 5, CYAN); // Et une bordure opposée
    action();
  }
  else
  {
    fillRect(15, 169, 25, 179, BLACK); // Efface l'ancien dessin qui ne nous plaît pas
    fillCircle(20, 174, 4, CYAN); // Dessin qui efface éventuellement la coche
    circle(20, 174, 5, RED); // Et une bordure opposée
  }
}
void cercle8() // les boutons 7 8 et 9 se dessinent
{
  if (bouton8.isSelected())
  {
    fillRect(15, 193, 25, 203, BLACK); // Efface l'ancien dessin qui ne nous plaît pas
    fillCircle(20, 198, 4, RED); // Dessin qui efface éventuellement la coche
    circle(20, 198, 5, CYAN); // Et une bordure opposée
    action();
  }
  else
  {
    fillRect(15, 193, 25, 203, BLACK); // Efface l'ancien dessin qui ne nous plaît pas
    fillCircle(20, 198, 4, CYAN); // Dessin qui efface éventuellement la coche
    circle(20, 198, 5, RED); // Et une bordure opposée
  }
}
void cercle9() // les boutons 7 8 et 9 se dessinent
{
  if (bouton9.isSelected())
  {
    fillRect(15, 216, 25, 227, BLACK); // Efface l'ancien dessin qui ne nous plaît pas
    fillCircle(20, 222, 4, RED); // Dessin qui efface éventuellement la coche
    circle(20, 222, 5, CYAN); // Et une bordure opposée
    action();
  }
  else
  {
    fillRect(15, 216, 25, 227, BLACK); // Efface l'ancien dessin qui ne nous plaît pas
    fillCircle(20, 222, 4, CYAN); // Dessin qui efface éventuellement la coche
    circle(20, 222, 5, RED); // Et une bordure opposée
  }
}


//#####################################®#####################################
//##                                jaunisse                               ##
//#####################################®#####################################
// Le bouton 1 (RadioCoche) et le bouton 6 (RadioCircle) appelleront jaunisse() une fois dessinés.
// La couleur des autres ne plaisait pas, ils vont être jaune si sélectionnés, noirs sinon.
// On peut donc changer le dessin d'un seul bouton, de plusieurs, de tous.
// Fond noir par défaut, fond jaune si sélectionné
void jaunisse1()
{
  if (bouton1.isSelected())
  {
    fill(20, 30, YELLOW);
    action();
  }
  else fill(20, 30, DARK_GREY);
}
void jaunisse6()
{
  if (bouton6.isSelected())
  {
    fill(20, 150, YELLOW);
    action();
  }
  else fill(20, 150, DARK_GREY);
}


//#####################################®#####################################
//##                                 Setup                                 ##
//#####################################®#####################################
void setup()
{
  // Instanciation du bouton 3
  RadioCoche *bouton3 = new RadioCoche(10, 68, 30, 89);

  // Pour avoir un bouton sélectionné rouge, pour montrer comment on fait, pour la démo
  RadioCoche::buttonColor = BLUE;

  // Fonctions onSelect() associée
  bouton1.onSelectFunction = &jaunisse1; // Ont la jaunisse
  bouton1.onUnselectFunction = &jaunisse1;
  bouton6.onSelectFunction = &jaunisse6; // Aussi
  bouton6.onUnselectFunction = &jaunisse6;
  bouton7.onSelectFunction = &cercle7; // Se prend pour un cercle
  bouton7.onUnselectFunction = &cercle7;
  bouton8.onSelectFunction = &cercle8; // Aussi
  bouton8.onUnselectFunction = &cercle8;
  bouton9.onSelectFunction = &cercle9; // Aussi
  bouton9.onUnselectFunction = &cercle9;
  // Pour les autres:
  bouton2.onSelectFunction = &action; // La même action pour tous
  bouton3->onSelectFunction = &action; // La même action pour tous
  bouton4->onSelectFunction = &action; // La même action pour tous
  bouton5.onSelectFunction = &action; // La même action pour tous


  // Initialisations
  Serial.begin(115200);
  setGraphMode(PAYSAGE); // PAYSAGE ou PAYSAGE_INVERSE seulement
  setTextCursor(35, 0); text(F("Cochez la bonne réponse")); // Dire un minimum ce qu'il faut faire

  setTextKeep(CUT); // Bien pratique pour débugger!
  // N° des boutons ou des questions
  setTextCursor(0, 25); text("1");
  setTextCursor(0, 49); text("2");
  setTextCursor(0, 73); text("3");
  setTextCursor(0, 97); text("4");
  setTextCursor(0, 121); text("5");
  setTextCursor(0, 145); text("6");
  setTextCursor(0, 169); text("7");
  setTextCursor(0, 193); text("8");
  setTextCursor(0, 217); text("9");

  // Questions
  setTextSize(1); setLimites(35, 0, 319, 239); // Pour avoir de bon retours à la ligne
  setTextCursor(35, 26); text(F("Tous les boutons RadioCoche ont les mêmes couleurs"));
  setTextCursor(35, 50); text(F("Tous les boutons RadioCircle ont les mêmes couleurs"));
  setTextCursor(35, 70); text(F("On ne peut pas utiliser un bouton RadioZone car il ne se\nvoit pas"));
  setTextCursor(35, 98); text(F("Tous les boutons RadioCoche ont la même forme"));
  setTextCursor(35, 118); text(F("Tous les boutons Radio d'un même groupe doivent avoir\n le même dessin"));
  setTextCursor(35, 142); text(F("On ne peut pas avoir dans un même groupe un bouton\nZoneRadio, un bouton RadioCoche et un bouton RadioCircle"));
  setTextCursor(35, 166); text(F("On en peut pas dans un même groupe avoir un bouton\nstatique et un bouton dynamique (déclaré par new()"));
  setTextCursor(35, 194); text(F("Deux boutons Radio doivent avoir des valeurs différentes\n(dernier paramètre)"));
  setTextCursor(35, 218); text(F("Toutes les affirmations au dessus sont fausses"));

  setLimites();
  //   drawControles(); // Inutilsé si on déclare les boutons avant setGraphMode()
  bouton1.unselect(); // active sa jaunisse;
  bouton6.unselect(); // active sa jaunisse;
  bouton7.unselect(); // active son cercle;
  bouton8.unselect(); // active son cercle;
  bouton9.unselect(); // active son cercle;
}

void loop()
{
  if (run) scanEvent(); // Agit si on appuie sur l'écran, ne fera plus rien quand run deviendra faux
}

Résultat:

 

RadioZone   <<     >>   RadioCircle