Menu dynamique et récursivité¶
Objectif :¶
Créer un menu de manière dynamique dont l'organisation repose sur une arborescence de dossiers et fichiers.
Ceci permet d'avoir un menu "exercices" par exemple qui s'adapte seul au contenu des dossiers contenant les fichiers des exercices.
Analyse :¶
La tâche se décompose en deux parties :- Parcourir une arborescence de dossier et fichiers
- Créer les entrées de menu et sous-menus correspondantes
Parcourir l'arborescence :¶
Ce problème a été présenté (bien que dans un autre contexte) par Philippe Cadaugade Parcourir_des_sous-dossiers. Ici on ne cherche pas un fichier particulier, on les veut tous ! Mais la solution que j'ai adoptée repose sur la récursivité dont le parcours des arbres est un des domaine privilégié.
La difficulté liée à la récursivité est la condition de sortie. Quand arrête-t-on les appels ?
//On se positionne dans le répertoire des exercices QDir dir; dir.cd(repertoire); dir.setFilter(QDir::AllEntries | QDir::AllDirs | QDir::NoSymLinks | QDir::NoDotAndDotDot); dir.setSorting(QDir::DirsLast);
Ici seuls les exercices ayant pour extension .ini m'intéressent : on installe un filtre sur extension setNameFilters(filters)
//On crée un filtre pour ne conserver que les fichiers .ini QStringList filters; filters << "*.ini"; dir.setNameFilters(filters);
On rentre dans le vif du sujet : le parcours de l'arborescence (tant qu'il y a des dossiers, on continue l'exploration)
QFileInfoList list = dir.entryInfoList(); for (int i = 0; i < list.size(); ++i) { QFileInfo fileInfo = list.at(i); if (fileInfo.isDir()) //Condition de sortie de la boucle de récursivité { //créer un sousmenu QMenu * menucree=new QMenu(fileInfo.fileName(),this); menu=menuCourant->addMenu(menucree); MainWindow::creeMenuExo(fileInfo.filePath(),menucree); //appel récursif } else { //créer un item de menu // Les lignes qui suivent sont adaptées à la façon de stocker les paramètres de calculette // On peut sinon utiliser : // QString nomItem = fileinfo.fileName(); // qui affichera le nom du fichier débarrassé de son path QSettings parametres(fileInfo.filePath(),QSettings::IniFormat); //On récupère le nom de la calculette QString nomItem = parametres.value("nom").toString(); //qui est caché dans le .ini QAction *act=menuCourant->addAction(nomItem); //et on l'affecte à l'item de menu // Reste à définir les actions elles-mêmes et à les connecter !!! ... }
On a donc parcouru toute l'arborescence, si on a rencontré un dossier , on a créé un sous-menu, si on a rencontré un fichier.ini, on a créé un item de menu.
Créer les Actions liées aux items de menu¶
C'est assez facile à faire lorsqu'on utilise le designer, mais ici on ne connait pas à l'avance l'arborescence, il va donc falloir faire autrement !
Notre problème va être d'associer une entrée de menu à l'ouverture d'un fichier. Il faut qu'un clic sur l'exercice sélectionné charge le fichier correspondant : on est typiquement dans le mécanisme Signal/slot.
Comme souvent en Qt, il existe un "truc" très pratique qui s'appelle un SignalMapper et qui établira le lien entre l'entrée de menu sélectionnée et le fichier à charger.
Création du SignalMapper
//On crée un signalMapper qui établira le lien entre l'entrée de menu sélectionnée et le fichier à charger QSignalMapper *signalMapper = new QSignalMapper(this); connect(signalMapper, SIGNAL(mapped(QString)),this, SLOT(slotLanceExo(QString)));
On associe les Qaction *act définies plus haut dans la boucle récursive au signalmapper :
// Reste à définir les actions elles-mêmes et à les connecter !!! signalMapper->setMapping(act,fileInfo.filePath()); connect(act, SIGNAL(triggered()), signalMapper, SLOT(map()));
Et maintenant, à chaque item sélectionné, le slot "slotLanceExo(QString)" et appelé.
Bilan :¶
- On a utilisé la récursivité pour parcourir une arborescence dans laquelle on plonge jusqu'à ce qu'il n'y ait plus de sous-dossiers (condition d'arrêt)
- On a utilisé un signalmapper pour connecter les items créés avec le slot qui lance les exercices.