Publicité
+ Répondre à la discussion
Affichage des résultats 1 à 5 sur 5
  1. #1
    Membre habitué Avatar de kéraunos
    Homme Profil pro
    Inscrit en
    janvier 2005
    Messages
    236
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : janvier 2005
    Messages : 236
    Points : 114
    Points
    114

    Par défaut Conflit entre itemIsMovable et mouseReleaseEvent

    Bonjour.

    Pour la question, prière de compiler le code (très simplifié) ci-dessous.
    Pourquoi mon objet rect (classe RectItem) revient-il dans sa position initiale à partir du deuxième déplacement à la souris ? Essayez de le bouger plusieurs fois et vous comprendrez. Ce comportement étrange survient lorsque j'implémente mouseReleaseEvent. Avez-vous une solution qui me permette d'implémenter cette méthode tout en conservant le flag ItemIsMovable ? Merci.

    Le code :
    Code :
    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
    #include <QApplication>
    #include <QGraphicsScene>
    #include <QGraphicsView>
    #include <QGraphicsRectItem>
    #include <QGraphicsSceneMouseEvent>
    #include <QDebug>
     
     
    // QGraphicsView subclass
    class GraphicsView : public QGraphicsView {
    public:
        GraphicsView(QGraphicsScene *scene) : QGraphicsView(scene) {}
    };
     
    // QGraphicsRectItem subclass
    class RectItem : public QGraphicsRectItem {
    public:
        RectItem(int x, int y, int width, int height) : QGraphicsRectItem(x, y, width, height) {}
        void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);
    };
     
    // mouse events on RectItem items: make drag&drop bug! (see flag ItemIsMovable)
    void RectItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) {
        qDebug() << "released mouse";
        event->accept();
    }
     
     
    int main(int argc, char *argv[]) {
     
        QApplication app(argc, argv);
     
        // test drag&drop with flag ItemIsMovable
        QGraphicsScene scene(0, 0, 400, 300);
     
        RectItem *rect = new RectItem(100, 100, 40, 40);
        rect->setBrush(QColor(Qt::blue));
        rect->setPen(Qt::NoPen);
        rect->setFlag(rect->ItemIsMovable, true);
        scene.addItem(rect);
     
        GraphicsView view(&scene);
        view.setBackgroundBrush(QColor(255, 255, 200));
        view.show();
     
        return app.exec();
    }

  2. #2
    Modérateur
    Avatar de koala01
    Profil pro Philippe Dunski
    Inscrit en
    octobre 2004
    Messages
    9 740
    Détails du profil
    Informations personnelles :
    Nom : Philippe Dunski
    Âge : 42

    Informations forums :
    Inscription : octobre 2004
    Messages : 9 740
    Points : 17 222
    Points
    17 222

    Par défaut

    Salut,

    Sans avoir compilé, je dirais que c'est parce que tu accepte l'événement, mais que tu ne redéfinis pas la nouvelle position de ton objet

    La méthode accept d'un événement indique juste que l'on s'attendait à ce que l'événement survienne à ce moment là, et qu'on le juge donc valide, mais cela ne fait rien d'autre

    Dans le cas présent, tu dis "juste" que, oui, l'utilisateur avait raison de relâcher le bouton de la souris, mais il manque le principal : le fait de déplacer effectivement ton objet à la position indiquée par la souris

    Ton code devrait donc ressembler à quelque chose comme
    Code :
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    void RectItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) {
        qDebug() << "released mouse";
        setPos(event->scenePos());
        /* ou peut etre bien quelque chose comme 
        QPointf pos = event->scenePos();
        QRectf rect(pos.x(), pos.y(),rect().width(),rect().height());
        setRect(rect);
        car je ne sais plus de tete si la première version gardera le rectangle
        non modifiée :P */
        event->accept();
    }
    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

  3. #3
    Modérateur
    Avatar de koala01
    Profil pro Philippe Dunski
    Inscrit en
    octobre 2004
    Messages
    9 740
    Détails du profil
    Informations personnelles :
    Nom : Philippe Dunski
    Âge : 42

    Informations forums :
    Inscription : octobre 2004
    Messages : 9 740
    Points : 17 222
    Points
    17 222

    Par défaut

    En fait, je te dois une petite explication...

    Le flag "ItemIsMovable" indique uniquement que, si ton objet fait partie d'une sélection et que tu déplace la souris dans un mouvement de drag (bouton gauche enfoncé), ton objet suivra les mouvements de la souris pour t'indiquer la position qu'il pourrait prendre.

    Mais ce flag n'implique absolument pas le fait que, au moment où tu vas relâcher la souris, la position de ton objet soit modifiée pour prendre la position indiquée par ta souris, et c'est normal...:

    Imagine un peu que tu aies trois rectangles à des endroits différents qui soient sélectionnés.

    Ces trois rectangles ont, bien évidemment les mêmes dimensions, sinon, ce ne serait pas marrant, hein

    Si le fait de relâcher la souris modifiait la position des trois rectangles, ils se retrouveraient tous les trois superposés.

    Cela ne te conviendrait certainement pas parce que tu te serais sans doute attendu à ce que celui sur lequel était ta souris prenne la position indiquée par la souris et que les deux autres prenne une position relative identique à celle qu'ils avaient avant d'être déplacés (le premier en haut à gauche du deuxième et le troisième en bas à droit du deuxième, le tout à des distances de X, Y entre le premier et le deuxième et de X' et Y' entre le deuxième et le troisième, si c'étaient leurs positions d'origines )

    Et puis, il y a tous les problèmes liés à l'imprécision de la souris...

    Mettons que tu aies divisé ta scène en dix zones horizontales, représentées par 9 lignes et que ton rectangle doive, pour une raison qui ne tient qu'à toi, être positionné exactement à équidistance des deux lignes entre lesquelles il se trouve.

    Ton rectangle réagira au déplacement de la souris qu'elle se trouve près du coin supérieur gauche, au centre ou près du coin inférieur droit de ton rectangle (en fait : quelle que soit la position de ta souris sur la surface de ton rectangle )

    Quand tu vas relacher le bouton de la souris, tu peux le faire sur toute la distance qui sépare les deux lignes.

    Si tu te contentes de prendre la position de la souris et de considérer que cette position est celle du coin supérieur gauche de ton rectangle, il y a 9 chances sur 10 que ton rectangle ne sera pas correctement positionné, et les chances seront encore moindre si tu considère qu'il faut poser le rectangle exactement là où il apparait (en effectuant donc le calcul de la position réelle du coin supérieur gauche par rapport à la position relative de la souris au moment où l'on a cliqué )

    Dans les deux cas, il t'appartiendra de récupérer la position de la souris et d'en déduire, d'une manière ou d'une autre, la position effective que doit (doivent) prendre l'élément (les éléments) sélectionnés

    Comme tu le vois, il est tout à fait impossible de définir le moindre comportement de déplacement par défaut de manière sereine.

    C'est la raison pour laquelle, quand l'événement se produit, le comportement par défaut est uniquement... de signaler que l'événement est accepté, et qu'il est donc temps de remettre la vue à jour

    Le fait d'invoquer la fonction accept() de ton événement va uniquement placer un flag à true pour indiquer de, effectivement, ton objet s'attendait bel et bien à ce que tu finisses par lâcher le bouton de la souris, mais rien d'autre... Et surtout pas aller modifier tes données, ni les propriétés des objets de ta vue, simplement parce que ce n'est pas le rôle d'un événement
    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

  4. #4
    Membre habitué Avatar de kéraunos
    Homme Profil pro
    Inscrit en
    janvier 2005
    Messages
    236
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : janvier 2005
    Messages : 236
    Points : 114
    Points
    114

    Par défaut

    Merci pour tes réponses.

    En ajoutant la ligne setPos(event->scenePos()); dans la méthode mouseReleaseEvent, le bug se produit toujours. Je te conseille vraiment de compiler le code pour t'en rendre compte.

    De plus, sans implémenter aucune méthode de gestion d'événement (ni press, release, ni doubleclick, rien), le flag ItemIsMovable se suffit à lui-même : l'objet rect est bien déplacé et ne retourne pas dans sa position initiale quand on essaie de le déplacer à nouveau.

    En outre, l'implémentation de la seule méthode mousePressEvent ne produit pas du tout ce mauvais comportement : je peux gérer l'événement comme bon me semble (même laisser la méthode vide !), le rectangle se déplacera effectivement et correctement.

  5. #5
    Membre habitué Avatar de kéraunos
    Homme Profil pro
    Inscrit en
    janvier 2005
    Messages
    236
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : janvier 2005
    Messages : 236
    Points : 114
    Points
    114

    Par défaut

    Je n'ai pas résolu ce bug mystérieux du conflit en le flag ItemIsMovable et la méthode mouseReleaseEvent.

    En tout cas j'ai trouvé le moyen d'opérer une sorte de drag&drop (pas au sens Qt avec les types MIME, mais juste le déplacement du rectangle via la souris), sans passer par ce flag maudit :

    Code :
    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
    #include <QApplication>
    #include <QGraphicsScene>
    #include <QGraphicsView>
    #include <QGraphicsRectItem>
    #include <QGraphicsSceneMouseEvent>
    #include <QPoint>
    #include <QDebug>
     
     
    // QGraphicsView subclass
    class GraphicsView : public QGraphicsView {
    public:
        GraphicsView(QGraphicsScene *scene) : QGraphicsView(scene) {}
    };
     
    // QGraphicsRectItem subclass
    class RectItem : public QGraphicsRectItem {
    public:
        RectItem(int x, int y, int width, int height) : QGraphicsRectItem(x, y, width, height) {
            setCursor(Qt::OpenHandCursor);
        }
        void mousePressEvent(QGraphicsSceneMouseEvent *event);
        void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
        void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);
     
    protected:
        QPoint mousePressPt;
    };
     
    // mouse events on RectItem items
    void RectItem::mousePressEvent(QGraphicsSceneMouseEvent *event) {
        qDebug() << "pressed mouse";
     
        setCursor(Qt::ClosedHandCursor);
     
        mousePressPt.setX(pos().x() - event->scenePos().x());
        mousePressPt.setY(pos().y() - event->scenePos().y());
    }
    void RectItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event) {
        setX(mousePressPt.x() + event->scenePos().x());
        setY(mousePressPt.y() + event->scenePos().y());
     
        event->accept();
    }
    void RectItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) {
        qDebug() << "released mouse";
     
        setCursor(Qt::OpenHandCursor);
     
        setX(mousePressPt.x() + event->scenePos().x());
        setY(mousePressPt.y() + event->scenePos().y());
     
        event->accept();
    }
     
     
    int main(int argc, char *argv[]) {
     
        QApplication app(argc, argv);
     
        // test drag&drop with flag ItemIsMovable
        QGraphicsScene scene(0, 0, 400, 300);
     
        RectItem *rect = new RectItem(100, 100, 40, 40);
        rect->setBrush(QColor(Qt::blue));
        rect->setPen(Qt::NoPen);
        scene.addItem(rect);
     
        GraphicsView view(&scene);
        view.setBackgroundBrush(QColor(255, 255, 200));
        view.show();
     
        return app.exec();
    }

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

Liens sociaux

Règles de messages

  • Vous ne pouvez pas créer de nouvelles discussions
  • Vous ne pouvez pas envoyer des réponses
  • Vous ne pouvez pas envoyer des pièces jointes
  • Vous ne pouvez pas modifier vos messages
  •