IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Bases de données Discussion :

SQLite et QTreeView : charger une arborescence


Sujet :

Bases de données

  1. #1
    Membre du Club
    Inscrit en
    Juin 2008
    Messages
    125
    Détails du profil
    Informations forums :
    Inscription : Juin 2008
    Messages : 125
    Points : 57
    Points
    57
    Par défaut SQLite et QTreeView : charger une arborescence
    Bonjour,

    Je cherche à charger une arborescence stockée dans une table sqlite et afficher son contenu dans un arbre (QTreeView). L'arborescence en question est un ensemble de répertoires pouvant contenir eux mêmes des répertiores (d'où la table récursive).
    La table dans le base contient donc un ID, PARENT_ID et NAME respectivement l'identifiant du répertoire, l'identifiant du parent du répertoire (éventuellement NULL) et le nom du répertoire.

    En cherchant sur le net je suis tombé sur ce lien qui montre comment faire cela en passant par QAbstractProxyModel. Mais voilà, je n'arrive à afficher que la racine et ne vois pas ce qui ne va pas !

    Si quelqu'un a déjà eu à traiter ce genre de cas ou par hasard a pu faire fonctionner le code exemple ci-dessus, merci de m'aiguiller.

    Cordialement.

  2. #2
    Membre averti
    Homme Profil pro
    Analyse système
    Inscrit en
    Novembre 2008
    Messages
    227
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Analyse système
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Novembre 2008
    Messages : 227
    Points : 311
    Points
    311
    Par défaut
    Bonjour,

    J'ai jeté un coup d'œil à ton lien, perso je trouve cela un peu compliqué, pour juste afficher une arbo ( ce qui n'enlève rien au tutoriel).

    J'ai déjà traité ce genre de point, et j'ai utilise une méthode récursive pour afficher mon arborescence.
    Le plus difficile est d'avoir le point d'entrée, mais à partir du moment ou tu as le point d'entrée, ta méthode récursive d'analyse se débrouille toute seule.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
     
    // id : identifiant du père dont on veut trouver les enfants.
    // item : Item auquel on va rattacher les enfants.
    void analyseArborescence(QTreeWidgetItem *item, int id)
    {
        QTreeWidgetItem * s_item;
        int identifiant;
        QSqlQuery query;
     
        if ( id > 0 ) {
            QString requete = QString("SELECT id,parent_id,name FROM TABLE WHERE Parent_id = %1").arg(id);
     
            if ( query.exec(requete)) {
                while ( query.next() ) {
                    s_item = new QTreeWidgetItem(item);
                    s_item->setExpanded(true);
                    identifiant = query.value(0).toInt();
                    if ( identifiant > 0 ) {
                        s_item->setText(0,query.value(2).toString());     // type de
                        analyseMoyen(s_item,identifiant);
                    } else {
                        qDebug() << "/!\\ Identifiant non valide : " << identifiant;
                        qDebug() << "Query result : " << query.lastError().text();
                        qDebug() << query.size();
                        qDebug() << query.lastQuery();
                    }
                }
            }
        } else {
            qDebug() << "/!\\ Identifiant non valide : " << id;
        }
    }
    Personnellement j'ai intégré cette fonction dans une classe.
    Si tu as des questions, n'hésite pas.

  3. #3
    Membre du Club
    Inscrit en
    Juin 2008
    Messages
    125
    Détails du profil
    Informations forums :
    Inscription : Juin 2008
    Messages : 125
    Points : 57
    Points
    57
    Par défaut
    Tout d'abord, merci pour ta réponse. J'avais pu avancer entre temps et effectivement je suis passé par une méthode récursive comme tu l'as indiqué (juste que dans ton code c'est plutôt analyseArborescence qu'il fallait appeler à la ligne 20).

    Aussi, je ne sais pas si je devrais en parler dans ce post mais j'ai effectué quelques recherches afin de vérifier s'il était possible de charger la table sans passer par la récursivité et là je suis tomber sur ce lien qui présente une autre manière de représenter une arborescence et du coup éviter les traitements récursifs. Mais voilà, j'ai pu faire fonctionner cette technique mais malheureusement toujours en passant (côté métier) par la récursivité. Je posterai peut être un message en section SQL afin de vérifier si le soucis ne peut pas être réglé au niveau SQL lui même : trouver une requête SQL qui ressort TOUTE une arborescence avec les éléments déjà rangés ...

    Au passage, mon objectif final est de charger l'arborescence qui ne contient pas que des répertoires mais également d'autres éléments (liés à ma table répertoire) ...

    Je reste à l'écoute pour toute autre proposition.

  4. #4
    Membre du Club
    Inscrit en
    Juin 2008
    Messages
    125
    Détails du profil
    Informations forums :
    Inscription : Juin 2008
    Messages : 125
    Points : 57
    Points
    57
    Par défaut Solution à améliorer
    Salut,

    Je reviens encore avec du nouveau. Je poste le code ci-après qui fonctionne et me permet de charger l'arborescence et ce sans passer par la récursivité (adoption de la représentation intervallaire). Ouvert à vos remarques / propositions ...

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
     
    #include "mainwindow.h"
    #include <QApplication>
     
    #include <QMap>
     
    #include <QTreeView>
     
    #include <QStandardItem>
    #include <QStandardItemModel>
     
    #include <QtSql/QSqlDatabase>
    #include <QtSql/QSqlQuery>
    #include <QtSql/QSqlRecord>
     
    #include <QElapsedTimer>
    #include <QDebug>
     
    typedef enum
    {
        FolderItem,
        PlaylistItem,
        TrainingItem
     
    } ItemType;
     
    class StandardItem : public QStandardItem
    {
    public:
     
        typedef enum
        {
            FolderItem,
            PlaylistItem,
            TrainingItem
     
        } ItemType;
     
        StandardItem(int ai_nodeId, int ai_nodeLevel, int ai_parentId, QString ai_nodeName, ItemType ai_type)
        {
            m_node_id = ai_nodeId;
            m_node_name = ai_nodeName;
            m_node_level = ai_nodeLevel;
            m_parent_id = ai_parentId;
            setText(m_node_name);
            if (ai_type == FolderItem)
                setIcon(QIcon(":/images/folder.png"));
            else if (ai_type == PlaylistItem)
                setIcon(QIcon(":/images/playlist.png"));
            else if (ai_type == TrainingItem)
                setIcon(QIcon(":/images/training.png"));
        }
     
        int m_node_id;
        QString m_node_name;
        int m_node_level;
        int m_parent_id;
    };
     
    typedef QMap<int, StandardItem*> MapStandardItem;
     
    int main(int argc, char *argv[])
    {
        QApplication a(argc, argv);
    //    MainWindow w;
    //    w.show();
     
        QMap<int, MapStandardItem> map;
     
        QStandardItemModel * model = new QStandardItemModel();
     
        QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
     
        db.setDatabaseName("classeur.db");
        if (!db.isOpen())
        {
            db.open();
        }
     
    //    CREATE TABLE "PLAYLISTS" (
    //        "ID" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
    //        "FOLDER_ID" INTEGER,
    //        "NAME" TEXT NOT NULL
    //    );
     
    //    CREATE TABLE "FOLDERS" (
    //        "ID" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
    //        "LEFT_RANGE" INTEGER,
    //        "RIGHT_RANGE" INTEGER,
    //        "_LEVEL_" INTEGER,
    //        "NAME" TEXT NOT NULL
    //    );
     
    //    CREATE TABLE "TRAININGS" (
    //            "ID" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
    //            "FOLDER_ID" INTEGER,
    //            "NAME" TEXT NOT NULL
    //    );
     
    //    INSERT INTO FOLDERS (LEFT_RANGE, RIGHT_RANGE, _LEVEL_,  NAME) VALUES (1,  6, 1, "R1");
    //    INSERT INTO FOLDERS (LEFT_RANGE, RIGHT_RANGE, _LEVEL_,  NAME) VALUES (2,  3, 2, "R2");
    //    INSERT INTO FOLDERS (LEFT_RANGE, RIGHT_RANGE, _LEVEL_,  NAME) VALUES (4,  5, 2, "R3");
     
    //    INSERT INTO PLAYLISTS (FOLDER_ID, NAME) VALUES (2, "P1");
    //    INSERT INTO PLAYLISTS (FOLDER_ID, NAME) VALUES (2, "P2");
    //    INSERT INTO PLAYLISTS (FOLDER_ID, NAME) VALUES (3, "P3");
     
        // Remplissage des tables
    //    for (int f=1; f <= 3; f++)
    //    {
    //        for (int p=1; p <= 25; p++)
    //            QSqlQuery(QString("INSERT INTO PLAYLISTS (FOLDER_ID, NAME) VALUES (%1, \"P%2\");").arg(f).arg(p));
     
    //        for (int t=1; t <= 40; t++)
    //            QSqlQuery(QString("INSERT INTO TRAININGS (FOLDER_ID, NAME) VALUES (%1, \"T%2\");").arg(f).arg(t));
    //    }
     
        QElapsedTimer t;
        t.start();
     
        QSqlQuery query("SELECT parent.NAME as node_name, parent._LEVEL_ as node_niv, node.ID as node_id, "
                        "(SELECT ID FROM FOLDERS WHERE LEFT_RANGE < node.LEFT_RANGE AND RIGHT_RANGE > node.LEFT_RANGE ORDER BY _LEVEL_ DESC LIMIT 1) as parent_id "
                    "FROM FOLDERS AS node, FOLDERS AS parent "
                    "WHERE node.LEFT_RANGE BETWEEN parent.LEFT_RANGE AND parent.RIGHT_RANGE "
                    "GROUP BY node.NAME "
                    "ORDER BY node.LEFT_RANGE ");
     
        while (query.next())
        {
            int node_niv = query.record().value("node_niv").toInt();
            int node_id = query.record().value("node_id").toInt();
            int parent_id = query.record().value("parent_id").toInt();
            QString node_name = query.record().value("node_name").toString();
     
            StandardItem * nodeItem = new StandardItem(node_id, node_niv, parent_id, node_name, StandardItem::FolderItem);
     
            // Recherche des enfants playlists
            QSqlQuery q1(QString("SELECT * FROM PLAYLISTS WHERE FOLDER_ID = %1").arg(node_id));
            while (q1.next())
            {
                StandardItem * playlistItem = new StandardItem(q1.record().value("ID").toInt(),
                                                               node_niv + 1, node_id,
                                                               q1.record().value("NAME").toString(),
                                                               StandardItem::PlaylistItem);
                nodeItem->appendRow(playlistItem);
            }
     
            // Recherche des enfants entrainements
            QSqlQuery q2(QString("SELECT * FROM TRAININGS WHERE FOLDER_ID = %1").arg(node_id));
            while (q2.next())
            {
                StandardItem * trainingItem = new StandardItem(q2.record().value("ID").toInt(),
                                                               node_niv + 1, node_id,
                                                               q2.record().value("NAME").toString(),
                                                               StandardItem::TrainingItem);
                trainingItem->setText(q2.record().value("NAME").toString());
                nodeItem->appendRow(trainingItem);
            }
     
            // Liste des éléments de niveau immédiatement supérieur
            MapStandardItem list1 = map.value(node_niv - 1);
     
            if (!list1.isEmpty())
            {
                // Si le parent existe, on lui associe son enfant (ça devrait toujours être le cas !)
                StandardItem * parent = list1.value(parent_id);
                if (parent)
                {
                    parent->appendRow(nodeItem);
                }
     
                // Ajout de l'élément à la liste des éléments de même niveau
                MapStandardItem list2 = map.value(node_niv);
                list2.insert(nodeItem->m_node_id, nodeItem);
                map.insert(node_niv, list2);
            }
            else
            {
                // Cet élément est le premier dans son niveau : on l'ajoute directement à la racine
                model->appendRow(nodeItem);
                list1.insert(nodeItem->m_node_id, nodeItem);
                map.insert(node_niv, list1);
            }
        }
     
        qWarning() << "Temps de chargement = " << t.elapsed() / 1000 << "s.";
     
        QTreeView * view = new QTreeView;
        view->setModel(model);
        view->show();
     
        return a.exec();
    }
    PS : le script de création de remplissage de mes tables est en commentaire dans le code.

    A+

  5. #5
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 614
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Salut,

    A vrai dire, dés le moment où tu te trouves dans une situation proche du patron de conception "composite" (comprend : un objet qui peut etre soit un noeud, soit une feuille, le noeud pouvant contenir lui meme soit un (voire plusieurs ) autre(s) noeud(s) soit une (voire plusieurs ) feuille(s) ), il devient particulièrement difficile d'éviter la récursivité

    Mais bon, la récursivité est une technique qui mérite d'être évaluée avec la plus grande attention (je n'ai, par exemple, jamais compris qu'on présente la récursivité avec une fonction comme la factorielle ou l'exponentielle, bien que ce soient, mathématiquement parlant des fonctions récursives ), mais il ne faut pas non plus avoir "peur" de l'utiliser

    Le principe de base pour gérer "en confiance" la récursivité, c'est de s'intéresser tout particulièrement à ce que l'on appelle le "cas de base":

    Il s'agit du cas dans lequel il n'est plus possible / nécessaire de faire un nouvel appel récursif à la fonction.

    A partir de là, tout le reste de la logique va faire en sorte de tendre vers le cas de base
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  6. #6
    Membre du Club
    Inscrit en
    Juin 2008
    Messages
    125
    Détails du profil
    Informations forums :
    Inscription : Juin 2008
    Messages : 125
    Points : 57
    Points
    57
    Par défaut
    Citation Envoyé par koala01 Voir le message
    comprend : un objet qui peut etre soit un noeud, soit une feuille
    Juste ce passage englobe le reste de ta définition ...

    Citation Envoyé par koala01 Voir le message
    il devient particulièrement difficile d'éviter la récursivité
    Ben, justement je viens de l'éviter (voir code ci-dessus). Bien entendu, je fais allusion à l'usage de fonction récursive pour le chargement et non pas à trouver la définition non récursive d'un arbre ...

    La récursivité est généralement plus couteuse (en terme de complexité spacio-temporelle) qu'une ou plusieurs itérations et c'est pour cela que j'étais à la recherche d'une alternative.

    Enfin, les remarques aux quelles je m'attendais, c'était plutôt : tiens tu aurais pu faire en une seule requête la recherche de répertoires, playlists et entrainements ou encore, pour ta map tu pouvais peut être t'en passer car ... et enfin, ton algo pouvait être optimisé en ne faisant que ...

    Je reste toujours ouvert à des propositions mais vu que j'ai trouvé la réponse à ce pourquoi j'ai créé ce poste, je le passe à Résolu !

    Merci à tous pour vos interventions.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 1
    Dernier message: 13/03/2015, 10h33
  2. [QtGui] Affichage optimal d'une arborescence (QTreeView) ?
    Par Thyxx dans le forum PyQt
    Réponses: 6
    Dernier message: 11/07/2014, 11h03
  3. Charger une arborescence
    Par hisoft dans le forum Langage SQL
    Réponses: 2
    Dernier message: 20/06/2013, 00h54
  4. [VB6] parcourir une arborescence de repertoire
    Par pecheur dans le forum VB 6 et antérieur
    Réponses: 8
    Dernier message: 30/04/2003, 17h33
  5. créer une arborescence windows sous forme d'arbre java
    Par chupachoc dans le forum Composants
    Réponses: 3
    Dernier message: 01/10/2002, 16h48

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo