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

Qt Discussion :

Bug Qt ? classe QTreeWidgetItem


Sujet :

Qt

  1. #1
    Membre régulier
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    214
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 214
    Points : 115
    Points
    115
    Par défaut Bug Qt ? classe QTreeWidgetItem
    Slt, j'ai toujours du mal à y croire mais je crois avoir trouvé deux bugs dans la bibliothèque de Qt.... Je suis avec la version 4.5 de Qt, la dernière à ce jour. Ce bug concerne la classe QTreeWidgetItem

    Il semblerait que si je crée un objet de ce type, que je fais une boucle pour lui ajouter énormément de fils (assez pour voir une utilisation de la RAM augmenter) et que je fasse un delete de cet objet, Qt ne libére pas la mémoire de tous ses fils... Il appelle bien le delete sur tous ses fils mais la mémoire n'est pas libérée... Je me suis fait une petite classe test qui peut avoir des fils un peu dans le même genre et dans le destructeur, je détruis tous les fils et là, toute la mémoire est bien libérée. Pourquoi pas avec le QTreeWidgetItem ? Le problème vient de moi ou de Qt ?

    Exemple de code, surveillez votre utilisation de la RAM :

    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
     
    #include <QApplication>
    #include <QTreeWidgetItem>
     
    int main(int argc, char *argv[])
    {
        QApplication a(argc, argv);
        QTreeWidgetItem * Root = new QTreeWidgetItem(&w);
     
        for(int i = 0; i < 10000; ++i)
        {
            Root->addChild(new QTreeWidgetItem());
            for(int j = 0; j < 30; ++j)
                Root->child(i)->addChild(new QTreeWidgetItem(Root));
        }
     
        qDebug("Debut sleep ");// Utilisation forte de la memoire
        sleep(5);
        qDebug("Fin sleep, debut delete");
        delete Root;
        qDebug("Fin delete");// devrait avoir une utilisation de la ram
    // d'environ 3Mo... Mais ce n'es pas le cas...
     
        sleep(10000000);
        return a.exec();
    }
    J'ai créé une classe qui dérive de QTreeWidgetItem et j'ai mis un débug dans le destructeur et le destructeur de tous les fils est bien appelé... Pourquoi cette mémoire n'est donc pas libérée ?

    Merci d'avance pour vos réponses !
    Cordialement.

  2. #2
    Membre éprouvé

    Profil pro
    Inscrit en
    Mai 2007
    Messages
    774
    Détails du profil
    Informations personnelles :
    Âge : 37
    Localisation : France, Finistère (Bretagne)

    Informations forums :
    Inscription : Mai 2007
    Messages : 774
    Points : 969
    Points
    969
    Par défaut
    Salut,

    Je pense que tu confonds la parenté entre un widget et ses widgets fils, et la parenté dans un arbre.


    Relis la doc, mais tu verras que la destruction d'un noeud dans l'arbre n'entraine pas la destruction des branches/noeuds qui lui étaient accrochés. Et heureusement !

    Donc il est normal que tes items ne soient effectivement pas détruits.

    G.
    Un problème avec Qt ? Vous trouverez votre réponse ici : http://doc.trolltech.com/4.6/overviews.html
    En français (traduction réalisée par l'équipe Qt de DVP) : http://qt.developpez.com/doc/4.6/vues-d-ensemble/

  3. #3
    Membre régulier
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    214
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 214
    Points : 115
    Points
    115
    Par défaut
    Tout d'abord, merci pour la réponse.

    Je comprends bien la distinction entre ces deux parentés mais justement, les deux se confondent. J'ai justement eut un gros problème en essayant d'insérer un élément au milieu des fils et non à la fin. Il est possible d'insérer un fils à un noeud à l'indice que l'on veut grâce à la méthode void insertChild ( int index, QTreeWidgetItem * child ). Pour ajouter un fils je faisais donc :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    // Pour créer le fils avec comme parent Root
    QTreeWidgetItem * Nouveau = new QTreeWidgetItem(Root);
    // Puis je l'ajoutais dans l'ARBRE
    Root->insertChild(n, Nouveau);
    Et avec ce code, il ne me l'insère pas à l'indice n mais à la fin... Car justement en lui définissant un père, il s'ajoute automatiquement à celui-ci.

    De plus, si il ne supprime pas tous les fils, pourquoi dans ce cas appelle-t-il les destructeurs de tous ses fils et sous-fils ?

    Pour le savoir, j'ai fait ma classe qui héritait de QTreeWidgetItem et j'ai mis un élément de débug qui affiche dans la console "destruction d'un fils". Donc s'il ne supprimait que la racine, je devrait n'avoir qu'une seule ligne et là, j'en ai autant que de fils...

    Il appelle donc bien le destructeur de tous les fils et l'intégralité de la mémoire devrait être libérée.

    De plus, dans mon héritage de la classe, j'ai également ajouté dans le destructeur un parcours de tous les fils pour les deleter. Là, c'est donc fait manuellement, c'est donc obligé qu'il les supprime. Hé ben non, toujours la même chose...

    Pourquoi, je n'arrive vraiment pas à comprendre. Peut-être y-a-t-il quelque chose que je n'ai pas bien compris mais là, j'ai du mal.

    Merci d'avance.
    Cordialement.

  4. #4
    Membre éprouvé

    Profil pro
    Inscrit en
    Mai 2007
    Messages
    774
    Détails du profil
    Informations personnelles :
    Âge : 37
    Localisation : France, Finistère (Bretagne)

    Informations forums :
    Inscription : Mai 2007
    Messages : 774
    Points : 969
    Points
    969
    Par défaut
    Je m'éloigne un peu du sujet, mais as tu un but précis ? Ou tu cherches juste à tester la chose ?

    Car si je me rappelle bien, il me semble que c'est à la destruction de l'arbre (le QTreeWidget) que les items sont détruits, et non à la destruction du noeud père.

    Ensuite, pour la libération mémoire non effective, il y a plusieurs possibilités :
    -comme tu l'a dis, le destructeur des items est appelé, c'est pas pour autant que la destruction est faite. Il peut y avoir un test dans le destructeur qui annule la destruction si celle-ci risque d'entraîner des problèmes.
    -je ne sais pas quel OS tu utilises, mais la mémoire est rarement libérée dès l'appel au delete, il peut se faire un peu plus tard, ou à la fermeture de l'application, voire même après.

    Voici une doc autre que celle de la classe, pour utiliser les qtreewidget.

    G.

    PS : Perso, j'ai pas testé, mais ça m'étonnerait qu'il y ait un bug, car c'est une partie du framework déjà bien éprouvée. A mon avis, c'est juste une mauvaise utilisation des QtreeWidgetItems.
    Un problème avec Qt ? Vous trouverez votre réponse ici : http://doc.trolltech.com/4.6/overviews.html
    En français (traduction réalisée par l'équipe Qt de DVP) : http://qt.developpez.com/doc/4.6/vues-d-ensemble/

  5. #5
    Membre régulier
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    214
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 214
    Points : 115
    Points
    115
    Par défaut
    Mon but est d'éviter des fuites de mémoires lors de multiples ajouts/suppressions des noeuds...

    Mon OS est Ubuntu et j'ai fait une classe de test qui représente une arborescence et la mémoire est libérée à la fin des delete (quand mon code affiche fin delete).

    L'arbre ne sera détruit qu'à la fin de l'application donc ça ne m'intéresse pas.

    Dans mon exemple, je n'utilise pas de QTreeWidget, donc comment serait-ce possible ?

    En tout cas, je trouve ça fou que quand je fais un delete d'un objet, il ne soit pas supprimé...

    Merci d'avance.

  6. #6
    Membre éprouvé

    Profil pro
    Inscrit en
    Mai 2007
    Messages
    774
    Détails du profil
    Informations personnelles :
    Âge : 37
    Localisation : France, Finistère (Bretagne)

    Informations forums :
    Inscription : Mai 2007
    Messages : 774
    Points : 969
    Points
    969
    Par défaut
    Si tu veux créer un arbre sans forcément le représenter, utilise les model (QAbstractItemModel, QStandardItemModel, etc...). Ces classes sont beaucoup plus flexibles et puissantes. Et là, la destruction du modèle entraîne la destruction des items. De même, la destruction d'un item détruit ses items fils.

    Les QTreeWidgets / QTreeWidgetItems servent uniquement à créer et afficher *simplement* un arbre.

    G.
    Un problème avec Qt ? Vous trouverez votre réponse ici : http://doc.trolltech.com/4.6/overviews.html
    En français (traduction réalisée par l'équipe Qt de DVP) : http://qt.developpez.com/doc/4.6/vues-d-ensemble/

  7. #7
    Membre régulier
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    214
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 214
    Points : 115
    Points
    115
    Par défaut
    Mon utilisation réelle est un affichage mais mon exemple ne met en jeu aucun affichage afin d'éviter de surcharger l'exemple. Il est seulement là pour mettre en avant le dysfonctionnement.

  8. #8
    Membre éprouvé

    Profil pro
    Inscrit en
    Mai 2007
    Messages
    774
    Détails du profil
    Informations personnelles :
    Âge : 37
    Localisation : France, Finistère (Bretagne)

    Informations forums :
    Inscription : Mai 2007
    Messages : 774
    Points : 969
    Points
    969
    Par défaut
    Pour les QTreeWidget / QTreeWidgetItem :

    En gros, je pense que pour détruire convenablement tous les items, il faut les détruire dans un ordre précis. A priori, je dirais du plus bas au plus haut niveau.
    Qt a fait le choix de gérer cela automatiquement à la destruction de l'arbre. Ces classes ont été conçu pour simplifier le travail du développeur.

    Si tu veux avoir une réponse claire, et si tu as un peu de temps, tu n'as qu'à déchiffrer le code source.

    G.
    Un problème avec Qt ? Vous trouverez votre réponse ici : http://doc.trolltech.com/4.6/overviews.html
    En français (traduction réalisée par l'équipe Qt de DVP) : http://qt.developpez.com/doc/4.6/vues-d-ensemble/

  9. #9
    Membre régulier
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    214
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 214
    Points : 115
    Points
    115
    Par défaut
    Le code de Qt dans le destructeur :

    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
     
    QTreeModel *model = (view ? qobject_cast<QTreeModel*>(view->model()) : 0);
        bool wasSkipSort = false;
        if (model) {
            wasSkipSort = model->skipPendingSort;
            model->skipPendingSort = true;
        }
        if (par) {
            int i = par->children.indexOf(this);
            if (i >= 0) {
                if (model) model->beginRemoveItems(par, i, 1);
                // users _could_ do changes when connected to rowsAboutToBeRemoved,
                // so we check again to make sure 'i' is valid
                if (!par->children.isEmpty() && par->children.at(i) == this)
                    par->children.takeAt(i);
                if (model) model->endRemoveItems();
            }
        } else if (model) {
            if (this == model->headerItem) {
                model->headerItem = 0;
            } else {
                int i = model->rootItem->children.indexOf(this);
                if (i >= 0) {
                    model->beginRemoveItems(0, i, 1);
                    // users _could_ do changes when connected to rowsAboutToBeRemoved,
                    // so we check again to make sure 'i' is valid
                    if (!model->rootItem->children.isEmpty() && model->rootItem->children.at(i) == this)
                        model->rootItem->children.takeAt(i);
                    model->endRemoveItems();
                }
            }
        }
        // at this point the persistent indexes for the children should also be invalidated
        // since we invalidated the parent
        for (int i = 0; i < children.count(); ++i) {
            QTreeWidgetItem *child = children.at(i);
            // make sure the child does not try to remove itself from our children list
            child->par = 0;
            // make sure the child does not try to remove itself from the top level list
            child->view = 0;
            delete child;
        }
     
        children.clear();
        delete d;
        if (model) {
            model->skipPendingSort = wasSkipSort;
        }
    Tout devrait être détruit dans tous les cas... (Boucle for à la fin qui est exécutée sans conditions). Je n'arrive toujours pas à comprendre.

  10. #10
    Membre éprouvé

    Profil pro
    Inscrit en
    Mai 2007
    Messages
    774
    Détails du profil
    Informations personnelles :
    Âge : 37
    Localisation : France, Finistère (Bretagne)

    Informations forums :
    Inscription : Mai 2007
    Messages : 774
    Points : 969
    Points
    969
    Par défaut
    for(int i = 0; i < 10000; ++i)
    {
    Root->addChild(new QTreeWidgetItem());
    for(int j = 0; j < 30; ++j)
    Root->child(i)->addChild(new QTreeWidgetItem(Root));
    }
    Je reprends ton code.

    Peux tu tester ça ? et me dire si ça marche ? (si toute la mémoire est libérée)

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     for(int i = 0; i < 10000; ++i)
        {
            Root->addChild(new QTreeWidgetItem());
            for(int j = 0; j < 30; ++j)
                Root->child(i)->addChild(new QTreeWidgetItem());
        }
    Merci,

    G.
    Un problème avec Qt ? Vous trouverez votre réponse ici : http://doc.trolltech.com/4.6/overviews.html
    En français (traduction réalisée par l'équipe Qt de DVP) : http://qt.developpez.com/doc/4.6/vues-d-ensemble/

  11. #11
    Membre régulier
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    214
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 214
    Points : 115
    Points
    115
    Par défaut
    Ha enfin, effectivement, le problème venait bien de là !!!!

    Merci beaucoup pour vos réponses, ça m'étonnait vraiment !

    Merci encore !
    Cordialement.

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

Discussions similaires

  1. [Objective-C] bug avec class NSDictionnary, methode bug
    Par manonthemoon dans le forum Objective-C
    Réponses: 1
    Dernier message: 21/09/2012, 10h53
  2. Bug sur class LyPasswordBehavior
    Par zdidier dans le forum W4 Express
    Réponses: 1
    Dernier message: 14/04/2009, 11h51
  3. Bug TabbedTextOut() (classe CDC)
    Par sebas26100 dans le forum Visual C++
    Réponses: 0
    Dernier message: 25/11/2008, 11h14
  4. Utilisation de setTimeout avec des classes : BUG!
    Par seb-oulba dans le forum Général JavaScript
    Réponses: 2
    Dernier message: 01/09/2006, 09h43
  5. [FLASH 8 ] bug compilation avec class
    Par CR_Gio dans le forum Flash
    Réponses: 6
    Dernier message: 31/05/2006, 20h55

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