librarie pour creer des menus (indépendante de l'ecran utilisé)

Bonjour à tous.

Pour mon projet de termostat, j'aurais besoin de réaliser des menu sur mon ecran oled spi 128*64.
J'ai donc cherché un lib pour gérer les menus, et la seule que j'ai trouvé qui est independante de l'ecran est "menubackend".
J'en ai trouvé d'autre mais qui elles, exigent un LCD standart.

J'ai testé rapidement menubackend, cela a l'air largement sufisant.
Dans ma tête, je voyais cela assez simple: Il me sufisait de creer une fonction "displayMenuOnOled()" par exemple. Cette fonction sera appelé dans les fonction callback de menuback, dès qu'un changement d'item de menu intervient.
Ensuite je pensait qu'il suffisait de récupérer l'ensemble des items du niveau courant et de les afficher sur l'ecran oled. Mais a ma grande surprise, il n'y a aucun moyen simple de récupéré la liste des items du niveau courant.
J'ai cherché des examples, et dans tous ceux que j'ai vu, tous le monde a besoin de faire une grosse strucure de IF pour gérér chaque cas.
Par exemple:
Si menu selectionné == abc, alors display "ABC" sur l'ecran

Je trouve que cela fait double emploi avec toutes la génération du menu.

Y a t'il une méthode plus simple ou une librarie plus simple?
J'espère avoir été assez clair

merci beaucoup

J'avoue que je visualise pas bien ta problématique. Qu'est-ce qui te génerait par exemple avec une variable "Menu" qui correspondrait au menu à afficher et un select Case ?

Bonjour,

J'ai déjà bricoler des gui (certes très basiques) avec un écran TFT tactile.

Moi je procède en deux parties.

  • affichage du menu (une fonction de dessin par menu + surlignage de la sélection courante)
  • gestion du menu avec une machine à états fini
    Chaque menu, sous menu, ... étant un état de la machine (= un bête switch sur une variable avec un enum pour rendre les "case" un peu plus lisible).

Sinon tu peut faire un système à base de classes (comme le fait menubackend), avec une classe représentant un item de menu, une classe représentant un menu (qui hérite elle même de la classe item).
Avec pour chaque classes une fonction de dessin et une liste de sous-item/menu.

Oui je m'en doutais, ce n'etait pas tres clair :slight_smile:

Voici un example. J'ai pris le code de demo

/**
 * HelloMenu
 * by BREVIG http://alexanderbrevig.com
 * 
 * This is the structure of the modelled menu
 * 
 * Settings
 *   Pin
 *   Debug
 * Options
 *   Delay (D)
 *     100 ms
 *     200 ms
 *     300 ms
 *     400 ms
 */

#include <MenuBackend.h>

//this controls the menu backend and the event generation
MenuBackend menu = MenuBackend(menuUseEvent,menuChangeEvent);
  //beneath is list of menu items needed to build the menu
  MenuItem settings   = MenuItem(menu, "Settings", 1);
    MenuItem pin      = MenuItem(menu, "Pin", 2);
    MenuItem debug    = MenuItem(menu, "Debug", 2);
  MenuItem options    = MenuItem(menu, "Options", 1);
    MenuItem setDelay = MenuItem(menu, "Delay",'D', 2);
      MenuItem d100   = MenuItem(menu, "100 ms", 3);
      MenuItem d200   = MenuItem(menu, "200 ms", 3);
      MenuItem d300   = MenuItem(menu, "300 ms", 3);
      MenuItem d400   = MenuItem(menu, "400 ms", 3);

//this function builds the menu and connects the correct items together
void menuSetup()
{
  Serial.println("Setting up menu...");
  //add the file menu to the menu root
  menu.getRoot().add(settings); 
    //setup the settings menu item
    settings.addAfter(options); 
    options.addAfter(settings); 
    
    settings.addLeft(settings); //loop back if left on settings
    settings.addRight(pin);     //chain settings to  pin on right
      debug.addLeft(settings);  //also go to settings left for debug
      //we want looping both up and down
      pin.addBefore(debug);
      pin.addAfter(debug);
      debug.addBefore(pin);
      debug.addAfter(pin);
    
    options.addLeft(options);   //loop back if left on settings
    options.addRight(setDelay); //chain options to delay on right
      setDelay.addRight(d100);
        d100.addBefore(d100);   //loop to d400 
        d100.addAfter(d200);
        d200.addAfter(d300);
        d300.addAfter(d400);
        d400.addAfter(d100);    //loop back to d100
        //we want left to always be bak to delay
        d100.addLeft(setDelay);
        d200.addLeft(setDelay);
        d300.addLeft(setDelay);
        d400.addLeft(setDelay);
}

/*
  This is an important function
  Here all use events are handled
  
  This is where you define a behaviour for a menu item
*/
void menuUseEvent(MenuUseEvent used)
{
  Serial.print("Menu use ");
  Serial.println(used.item.getName());
  
  if (used.item.isEqual(setDelay)) //comparison agains a known item
  {
    Serial.println("menuUseEvent found Delay (D)");
  }
}

/*
  This is an important function
  Here we get a notification whenever the user changes the menu
  That is, when the menu is navigated
*/
void menuChangeEvent(MenuChangeEvent changed)
{
  Serial.print("Menu change ");
  Serial.print(changed.from.getName());
  Serial.print(" ");
  Serial.println(changed.to.getName());
}

void setup()
{
  Serial.begin(9600);
  
  menuSetup();
  Serial.println("Starting navigation:\r\nUp: w   Down: s   Left: a   Right: d   Use: e");
  
  //fire Delay : menu.use('D');
  //bail back to Options by moving up one logical level after a D : menu.moveRelativeLevels(-1);
}


}

Ensuite je me suis dis, ca va etre simple il me suffit de faire ceci:

void function displayOnOledScreen(MenuItem item){
   
   //pseudo code
   for(tous les items du meme niveau as currentItem){
      si le current == item
         print ">>"  //pour montrer que c'est celui la selectionné
      
      print le currentItem sur mon ecran oled
   }

}

Et j'appelais cette fonction directement dans la fonction menuChangeEvent

Sauf que je me suis appercu qu'il n'est pas simple de recupéré les autres items d'un meme niveau a partir d'un item. Par conséquent ca complique tout et ca oblige a se faire une grosse structure de if ou case alors qu'on a déja toute la structure du menu detaillé dans l'objet "menu". Je trouves que ca duplique un peu le code.

À moins bien sur que je n'ai rien compris... Ce qui est possible aussi :slight_smile:

Dans ce cas pourquoi ne pas stocker ton avancé dans une chaine, et à chaque avancé tu ajoutes le menu selectionné, et à chaque retour tu remonte d'un index. A moins que j'ai toujours rien pigé xD

En fait je ne vois pas comment "avancer" ou "remonter"

Comment avoir tous les items d'un meme niveau a partir de menu backend?

Imaginons tu attribues une valeur à chaque menu (1,2,3 ...). Tu as ta fonction displayMenu(NuméroDuMenu). Ca jusque la je pense pas de problème. Ensuite tu as un tableau SuiviDesMenu[10], et un suivi de la "profondeur" de ton avancé nommé i. Quand tu "avances" dans tes menus, tu stocks dans SuiviDesMenu le numéro du menu actuel, tu passes au menu selectionné et tu incrémente i. Ainsi si tu veux revenir en arrière, du décrémente i, et tu appelle le numéro de menu stocker dans SuiviDesMenu*.*

atlas2003:
En fait je ne vois pas comment "avancer" ou "remonter"

Comment avoir tous les items d'un meme niveau a partir de menu backend?

Regarde le principe des arbres.
Le parcours devient hyper facile, si chaque nœud possède un pointeur vers ses fils (pour avancer dans le menu) et un pointeur vers son père (pour remonter dans le menu) la navigation dans le menu ce fait d'elle même au moyen des pointeurs.

Salut

Oui je comprend le principes des arbres (meme si mes années à l'iut ou j'ai appris ca remonte a un peu plus de 10 ans :slight_smile: )
Mais ca a l'air circulaire. Du coup, comment trouver le premier menu de la liste quand c'est circulaire? A moins que je me fasse un menu ou la valeur selectionné est toujours au centre de l'ecran
et j'affiche deux menus au dessus et deux menus en dessous. Je viens d'y penser a l'instant. Ca pourrait marcher :slight_smile:

Peut-être que tu peux t'inspirer de ça:
http://www.hw2sw.com/2011/09/22/circular-menu-in-arduino/

:wink:

salut perso j'utilise aussi une methode avec un switch case dans le loop et une variable qui change en fonction du menu ou on est!

exemple :

int selecteur_menu;

switch (selecteur_menu){

case 1 : fonction_aff_principale(); break;
  case 11 : fonction_sous_aff_principale1(); break;
  case 12 : fonction_sous_aff_principale2(); break;
case 2 : fonction_aff_menu2(); break;
  case 21 : fonction_sous_aff_menu21(); break;
  case 22 : fonction_sous_aff_menu22(); break;
etc...
les fonction etant comme dit skywood des fonction de dessin par example
avantage un simple retour arriere donne 
if (appuis){selecteur_menu=selecteur_menu/10;// et hop on retourne sur le menu precedant

voila si sa peut t'aider !