Projet

Général

Profil

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 :
  1. Parcourir une arborescence de dossier et fichiers
  2. 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.

menudynamique.txt Magnifier - Le code en entier (2,03 ko) Jean-Louis Frucot, 16/01/2011 20:03

Redmine Appliance - Powered by TurnKey Linux