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

Discussion :

Update nécessaire mais refusé car "dirty"

  1. #1
    Membre confirmé
    Profil pro
    Inscrit en
    Mars 2004
    Messages
    76
    Détails du profil
    Informations personnelles :
    Âge : 43
    Localisation : France

    Informations forums :
    Inscription : Mars 2004
    Messages : 76
    Par défaut Update nécessaire mais refusé car "dirty"
    Bonjour à vous,

    Je viens vers vous car je n'arrive pas à trouver de solution avec notre expert Qt (bon, il est un peu surchargé, il faut admettre).

    Pour ma part, je travaille sur un projet basé sur Qt, mais je n'utilise que les classes filles, je n'ai pas accès au code de Qt. Donc si vous voulez me renvoyer vers des sites où je peux étudier le code de Qt en passant, n'hésitez pas.
    Et pour ne rien arranger, nous utilisons une version modifiée de Qt 4.5.

    Mon soucis concerne l'affichage d'images. Vu que c'est un projet d'envergure, je ne pourrai évidemment pas vous donner tout le code, mais je vais expliquer la logique principale.

    Logique du code :

    Dans un objet comprenant pas mal de choses et héritant de QGraphicsItem, il y a un QObject fils qui contient le chemin d'une image à charger et qui est en charge de son affichage.

    Pour charger les images, nous utilisons un QThread qui charge l'image et envoye un signal une fois le chargement terminé. A la réception du signal, mon QObject met à jour ses paramètres et déclenche un update().


    Problème:

    Parfois, cet update est ... refusé !!

    L'appel à l'update de QGraphicsItem est bien fait, celui-ci appelant 2 fonctions :
    - QGraphicsItemPrivate::discardUpdateRequest, qui teste pas mal de paramètres et annule l'update si un des paramètres n'est pas correcte ;
    - itemUpdated qui lance l'affichage, si discardUpdateRequest n'a pas mis son véto.

    Dans mon cas, le paramètre dirty a été mis à vrai, et empêche l'affichage.
    Une des raisons pour lesquelles il peut devenir vrai, c'est si le Rect() est null, ce qui est le cas avant le chargement de mon image.

    Mais malgré la mise à jour de mon objet, je n'arrive pas à déclencher l'affichage !!

    Et si un nouvel affichage est demandé plus tard, alors l'affichage se déroulera correctement.


    Solutions testées qui ne fonctionnent pas :

    - update() x2
    - prepareGeometryChange dans le QGraphicsItem parent (après la demande de chargement -envoi de la requête au QThread-, pas "avant" la modification -réception du signal de fin de chargement-)


    Solutions testées qui fonctionnent mais ne conviennent pas :

    - déclenchement d'un nouvel update() après un timer donné.


    Questions :

    - Que connaissez-vous comme méthode pour préciser à Qt que l'objet a changé et qu'il doit reconsidérer ses paramètres afin de vérifier s'il doit l'afficher ou non ?

    - Quelles sont les fonctions de Qt qui ré-initialisent les paramètres d'affichage ? Car ils doivent bien être re-initialisé à un moment ou un autre, non ?


    Merci d'avance pour toute l'aide que vous pourrez m'apporter.

    Respectueusement,

    Cyborg

  2. #2
    Membre expérimenté

    Homme Profil pro
    Inscrit en
    Février 2010
    Messages
    243
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations forums :
    Inscription : Février 2010
    Messages : 243
    Par défaut
    Normalement, si ton item est visible au moment de l'update tu dois alors passer dans le paint event de l'item lorsque Qt traitera les affichages.

    Pour que l'item soit visible, il ne doit pas être caché derrière un autre, il doit avoir un "boundingRect" correct, et doit se trouver dans la zone de la scene visualisée par un viewport;

    Est-ce que le bounding rectangle de ton item est mis à jour avant l'appel à update ?

  3. #3
    Membre confirmé
    Profil pro
    Inscrit en
    Mars 2004
    Messages
    76
    Détails du profil
    Informations personnelles :
    Âge : 43
    Localisation : France

    Informations forums :
    Inscription : Mars 2004
    Messages : 76
    Par défaut
    Bonjour ness522,

    En effet, avant de déclencher l'update, j'ai vérifié 2 choses :
    - mon QObject et son parent QGraphicsItem sont visibles ;
    - boundingRect() ne renvoie pas un Rect null pour ces 2 objets.

    Ce que je ne comprends pas, c'est par exemple si mon Rect devient null, la valeur de dirty passe à 1, mais itemUpdated est quand même appelé.
    Alors que si dirty est passé à 1 précédemment, il ne reteste pas le Rect, il zappe directement l'update via discardUpdaterequest, jusqu'à ce que dirty repasse à 0 je ne sais comment...

  4. #4
    Membre confirmé
    Profil pro
    Inscrit en
    Mars 2004
    Messages
    76
    Détails du profil
    Informations personnelles :
    Âge : 43
    Localisation : France

    Informations forums :
    Inscription : Mars 2004
    Messages : 76
    Par défaut
    Pour m'assurer de la visibilité de mes objets, j'ai utilisé un setVisible(true). Il n'y a pas d'objet devant car rien n'est modifié après la mise à jour de mon image.

    C'est d'ailleurs le problème, semble-t-il, puisque si le chargement de l'image est fini avant que l'ensemble des objets soient affichés, tout se passe bien : les paramètres de mon QObject sont mis à jour, et mon image est dessinée quand vient son tour, je suppose.
    Mais si mon signal de fin de chargement de l'image est reçu après ... patatra, l'update() est rejeté.

    Il me faut alors attendre une nouvelle demande de mise à jour non rejetée pour que mon image apparaissent enfin.

    Dois-je en conclure que personne ne sait comment Qt fait pour se dire qu'il doit ré-évaluer ses objets, après que ceux-ci aient acquis un des paramètres problématiques testés dans discardUpdateRequest ?

  5. #5
    Membre expérimenté

    Homme Profil pro
    Inscrit en
    Février 2010
    Messages
    243
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations forums :
    Inscription : Février 2010
    Messages : 243
    Par défaut
    Là comme ça difficile à dire, je n'ai jamais beaucoup été à l'intérieur de Qt donc je ne sais pas trop en effet.

    Mais d'une part on a pas de code à tester, tu utilises Qt 4.5 il y a surement eu des corrections de bugs depuis, et en plus tu utilises une version customisée de Qt.

    Si tu arrives à nous faire un projet minimaliste qui compile et reproduit le problème on pourrait alors tester aussi...

  6. #6
    Membre confirmé
    Profil pro
    Inscrit en
    Mars 2004
    Messages
    76
    Détails du profil
    Informations personnelles :
    Âge : 43
    Localisation : France

    Informations forums :
    Inscription : Mars 2004
    Messages : 76
    Par défaut
    Citation Envoyé par ness522 Voir le message
    Là comme ça difficile à dire, je n'ai jamais beaucoup été à l'intérieur de Qt donc je ne sais pas trop en effet.
    Si je peux pas compter sur toi...

    Citation Envoyé par ness522 Voir le message
    Si tu arrives à nous faire un projet minimaliste qui compile et reproduit le problème on pourrait alors tester aussi...
    Ma question est bien sur le comportement de Qt, justement parce que je serai bien incapable de refaire ce qui est fait actuellement... Et qu'en plus, même si j'arrive à refaire un code minimaliste, rien ne garantie que je reproduise mon problème ... qui arrive environ 1 fois sur 20.

    Et si tu as d'autres idées pour des méthodes permettant de "forcer" la mise à jour, je suis preneur aussi.

  7. #7
    Membre expérimenté

    Homme Profil pro
    Inscrit en
    Février 2010
    Messages
    243
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations forums :
    Inscription : Février 2010
    Messages : 243
    Par défaut
    Vérifies toujours si les updates ne sont pas désactivées :
    http://doc-snapshot.qt-project.org/4...esEnabled-prop

    Par contre il n'y a pas de repaint forcé pour un graphics item :
    Citation Envoyé par doc
    The paint() function is called by QGraphicsView to paint the item's contents. The item has no background or default fill of its own; whatever is behind the item will shine through all areas that are not explicitly painted in this function. You can call update() to schedule a repaint, optionally passing the rectangle that needs a repaint. Depending on whether or not the item is visible in a view, the item may or may not be repainted; there is no equivalent to QWidget::repaint() in QGraphicsItem.

  8. #8
    Membre expérimenté

    Homme Profil pro
    Inscrit en
    Février 2010
    Messages
    243
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations forums :
    Inscription : Février 2010
    Messages : 243
    Par défaut
    Sinon qq lignes de codes nécessaires :
    - la fonction paint de ton QGraphicsItem
    - la fonction boundingRect() de ton QGraphicsItem
    - le slot appelé lq le thread a fini son boulot, là où l'on met à jour le bounding rect et le buffer image puis appele le update.

  9. #9
    Membre confirmé
    Profil pro
    Inscrit en
    Mars 2004
    Messages
    76
    Détails du profil
    Informations personnelles :
    Âge : 43
    Localisation : France

    Informations forums :
    Inscription : Mars 2004
    Messages : 76
    Par défaut
    Allons-y !! Je vais essayer d'extraire la substantifique moëlle de mon code.

    Voilà le code de paint de mon QGraphicsItem :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    void Bouton::paint(QPainter * painter,
                       const QStyleOptionGraphicsItem * option,
                       QWidget * widget)
    {
      ParentDuBouton::paint(painter, option, widget);
      if (m_qObject)
      {
        m_qObject->draw(painter, m_qObject->m_width, m_qObject->m_height);
      }
    }
    Je te passe malheureusement le code de draw de mon QObject, celle-ci étant assez complexe.
    Y'a pas mal de conditions, mais globalement, je récupère un QPixmap qui va bien, et je l'affiche avec une transformation donnée.

    Voilà le slot de mon QObject qui reçoit le fin de chargement :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    void CheminImage::tileDone(int tx, int ty, QPixmap* p_pixmap)
    {
      if (p_pixmap)
      {
        m_pixmapPath = m_loading_path_pixmap; // Chemin de l'image chargée
        m_pixmap = p_pixmap;                  // Référence de l'image chargée, le contrôle du pointeur est fait avant 
        update();
      }
    }
    Et le BoundingRect() de mon QObject :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
     
    QRectF CheminImage::boundingRect() const
    {
      QRectF l_rect = ParentDeCheminImage::boundingRect();
      if (m_pixmap)
      {
        l_rect = QRectF(m_posX,
                        m_posY,
                        m_pixmap->width(),
                        m_pixmap->height());
      }
      return (l_rect);
    }
    Et enfin celui de la classe parente de la classe parente (x2, oui oui) de mon QGraphicsItem, vu qu'il n'y a pas de BoundingRect() directement implémenté dans ma classe bouton :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
     
    QRectF ParentDuParentDuBouton::boundingRect() const
    {
      QRectF l_rect(0, 0, m_width, m_height);
      QHash<int, ParentDeCheminImage*>::const_iterator it;
      for (it = m_subObjects.constBegin(); it != m_subObjects.constEnd(); ++it)
      {
        if ((*it))
        {
          l_rect = l_rect | (*it)->boundingRect();
        }
      }
      return l_rect;
    }
    L'appel au BoundingRect() donnera toujours la bonne valeur si mon QPixmap a été mis à jour.

    ...
    ...
    ...

    Et maintenant, je viens de voir ton précédent message et la référence au setUpdatesEnabled. J'essaierai ça.


    Edit: Sauf que c'est sur un QWidget, pas sur un QGraphicsItem... Comme tu le disais...
    Dommage, ça correspondait exactement à ce que je cherchais.

  10. #10
    Membre confirmé
    Profil pro
    Inscrit en
    Mars 2004
    Messages
    76
    Détails du profil
    Informations personnelles :
    Âge : 43
    Localisation : France

    Informations forums :
    Inscription : Mars 2004
    Messages : 76
    Par défaut
    Dans le même esprit, j'ai essayé d'ajouter un hide() avec le chargement de mettre à jour mon lien, et le show() juste après...

    ... mais ça a pas marché, évidemment.

  11. #11
    Membre expérimenté

    Homme Profil pro
    Inscrit en
    Février 2010
    Messages
    243
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations forums :
    Inscription : Février 2010
    Messages : 243
    Par défaut
    Première réponse à chaud, un truc qui me chiffone :

    Tu a dit, il me semble, charger une image dans un thread et puis envoyer un signal lorsque celui-ci a fini afin d'afficher l'image chargée.

    Je vois cette signature pour le slot en question :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    void CheminImage::tileDone(int tx, int ty, QPixmap* p_pixmap)
    Est-ce que le thread chargerait directement les données dans le QPixmap ?
    Auquel cas, aie aie aie ! :p
    La classe QPixmap ne peut être utilisée en dehors du thread principal (celui qui lance le main) encore appelé thread GUI. Les résultats dans le cas contraire sont indéterminés (ça peut fonctionner parfois, et foirer ensuite...)

    Normalement le thread doit travailler sur une QImage, et ensuite tu dois soit transformer QImage en QPixmap, ou dessiner directement l'image avec drawImage(...)

  12. #12
    Membre confirmé
    Profil pro
    Inscrit en
    Mars 2004
    Messages
    76
    Détails du profil
    Informations personnelles :
    Âge : 43
    Localisation : France

    Informations forums :
    Inscription : Mars 2004
    Messages : 76
    Par défaut
    1ère réponse, à chaud, je te dirai que ce n'est probablement pas lié à mon soucis mais ... connaissant les boulettes que j'ai déjà pu voir dans le code, je préfère vérifier, tout est possible !!

    Il y a plusieurs soucis d'agencement du code au niveau du chargement des images, et j'essaie de convaincre mes chefs de me laisser travailler dessus.

    A noter en passant que j'ai un second slot pour le chargement des images, et basé sur une QImage... qui transforme simplement la QImage en QPixmap (dans le thread principal) avant de faire un traitement identique. Il faudra que je vérifie lequel il prend (j'aurais parié sur QPixmap, mais je préfère vérifier).

    Si on revient à notre problème initial, cela signifie donc qu'à la fin de mon chargement, je demande l'affichage d'une image, dont je peux obtenir la taille, mais qui n'est pas finie d'être construite, ce qui explique que l'affichage ne se fasse pas. C'est bien ça ?
    Car à la mise à jour suivante de l'affichage, qui arrive quelques secondes après, l'image apparaît enfin ... sans qu'un nouveau chargement ne soit nécessaire.

    J'vais y regarder, et essayer de faire quelques tests.

    Note en passant: J'avais vu que les QImage étaient chargées nativement dans un thread différent. Mais je n'arrive plus à retrouver la référence. Par contre, sur le coup, je n'avais pas trouvé de moyen de savoir quand le chargement était terminé.
    Par contre, les QPixmap ont un chargement séquentiel, donc en créant un QPixmap à partir d'une QImage, j'me disais que Qt devait gérer ça correctement pour attendre le fin du chargement de la QImage.
    Mais comme je ne retrouve pas l'explication à ce sujet, c'est pas réellement constructif.


    Edit: Zut, dans mon cas, c'est bien une QImage qui est utilisée.

    Dans ce cas-là, car dans d'autres circonstances, c'est bien un QPixmap qui est créé par le QThread de gestion d'images.
    J'vais voir avec notre expert Qt si ça vaut le coup de changer cet aspect du thread de chargement d'images, ou si des protections ont déjà été mises de chaque bord.

    ... mais du coup, mon problème initial est toujours là ...

  13. #13
    Membre expérimenté

    Homme Profil pro
    Inscrit en
    Février 2010
    Messages
    243
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations forums :
    Inscription : Février 2010
    Messages : 243
    Par défaut
    J'essaie de comprendre...

    Voilà le slot de mon QObject qui reçoit le fin de chargement :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    void CheminImage::tileDone(int tx, int ty, QPixmap* p_pixmap)
    {
      if (p_pixmap)
      {
        m_pixmapPath = m_loading_path_pixmap; // Chemin de l'image chargée
        m_pixmap = p_pixmap;                  // Référence de l'image chargée, le contrôle du pointeur est fait avant 
        update();
      }
    }
    Ce slot serait dans un QObject (forcément) mais donc le update(), il fait quoi ? QObject n'a pas de fonction update() et c'est le QGraphicsItem qu'il faut faire update non ?

    Et puis avec les bounding rect des parent du parent je suis complètement paumé :p
    Un petit schéma visuel avec des flèches, qui hérite qui, qui recoit le pixmap, fait l'update etc ?

  14. #14
    Membre expérimenté

    Homme Profil pro
    Inscrit en
    Février 2010
    Messages
    243
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations forums :
    Inscription : Février 2010
    Messages : 243
    Par défaut
    QImage, c'est grosso modo un tableau à deux dimensions avec de l'enrobage autours (gestion de formats, fonction de chargement, ...) Mais ça reste un tableau de pixels en mémoire que l'on peut parcourir, lire et écrire dedans.

    QPixmap, bien qu'ayant une API similaire est totalement différent. Son implémentation dépend de la plateforme, les données peuvent être stockée en mémoire graphique, dans le serveur graphique ou que sais-je. On ne peut donc plus accéder aux données n'importe comment, ni les modifier facilement.
    De plus les contraintes des systèmes graphiques imposent que tout ce qui touche à QPixmap soit effectué dans le thread principal. Même un code aussi simple que la création de l'object : QPixmap img();

    Tu peux sans problèmes charger ton QImage dans un thread, mais à un moment donné, dans le thread principal tu dois transformer ton image en pixmap (et empêcher ton thread d'écrire dedans à ce moment là, c'est plus propre). Même si c'est pour garder le pixmap en cache et l'afficher plus tard.

  15. #15
    Membre confirmé
    Profil pro
    Inscrit en
    Mars 2004
    Messages
    76
    Détails du profil
    Informations personnelles :
    Âge : 43
    Localisation : France

    Informations forums :
    Inscription : Mars 2004
    Messages : 76
    Par défaut
    On a un expert Qt qui est là pour 2 jours : chaque fois qu'il émet une hypothèse sur des défauts possibles quelque part dans l'appli, le thread d'images possède ce problème... On est déjà à trois.

    Conclusion: je pense que je continuerai à travailler sur le sujet une fois qu'on aura fait le nettoyage de cette classe...

    En tout cas, merci de l'info pour les QPixmap !!

    Il y a une documentation, ou même un défaut dans la base Qt, qui traite du sujet, pour argumenter auprès de mes chefs cette correction ?
    Parce que moi, je n'ai rien trouvé.

  16. #16
    Membre expérimenté

    Homme Profil pro
    Inscrit en
    Février 2010
    Messages
    243
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations forums :
    Inscription : Février 2010
    Messages : 243
    Par défaut
    doc de qpixmap :

    Note that the pixel data in a pixmap is internal and is managed by the underlying window system. Because QPixmap is a QPaintDevice subclass, QPainter can be used to draw directly onto pixmaps. Pixels can only be accessed through QPainter functions or by converting the QPixmap to a QImage. However, the fill() function is available for initializing the entire pixmap with a given color.
    dans QImage :
    Note: All functions in this class are reentrant.
    mais aucune mention de la sorte dans QPixmap... donc non je ne trouve pas que c'est le point le mieux renseigné de la doc loin de là.

    Fait une recherche google, tu tomberas sur beaucoup de fil de discussions sur ce sujet.

  17. #17
    Membre confirmé
    Profil pro
    Inscrit en
    Mars 2004
    Messages
    76
    Détails du profil
    Informations personnelles :
    Âge : 43
    Localisation : France

    Informations forums :
    Inscription : Mars 2004
    Messages : 76
    Par défaut
    Concernant mon problème, on a trouvé le problème.

    Un objet Qt considéré comme dirty ne sera plus mis à jour (update). Et pour que ce paramètre soit ré-évalué, il faut ... un update !
    L'astuce est donc que quelqu'un d'autre déclenche l'update, et ça fonctionne.

    Donc plutôt qu'un :
    J'ai fait un :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    mongraphicsitem->scene()->update()
    Et ça marche !!

    Merci pour toute l'attention que vous aurez consacré à ce poste.

    Cyborg

  18. #18
    Membre expérimenté

    Homme Profil pro
    Inscrit en
    Février 2010
    Messages
    243
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations forums :
    Inscription : Février 2010
    Messages : 243
    Par défaut
    Ok, bizarre, mais tant mieux si ça résoud ton problème

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

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