Précédent   Forum du club des développeurs et IT Pro > C et C++ > Bibliothèques > Qt
Qt Forum d'entraide technique sur la bibliothèque Qt. Avant de poster -> F.A.Q Qt
Partagez cette discussion sur d'autres réseaux sociaux : Viadeo Twitter Google Facebook Digg Delicious MySpace Yahoo
Réponse
 
Outils de la discussion
Publicité
'
Vieux 29/11/2012, 00h37   #1
kéraunos
Membre régulier
 
Avatar de kéraunos
 
Homme
Inscription : janvier 2005
Messages : 189
Détails du profil
Informations personnelles :
Sexe : Homme
Localisation : France

Informations forums :
Inscription : janvier 2005
Messages : 189
Points : 74
Points : 74
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();
}
kéraunos est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 29/11/2012, 01h52   #2
koala01
Modérateur
 
Avatar de koala01
 
Philippe Dunski
Inscription : octobre 2004
Messages : 8 613
Détails du profil
Informations personnelles :
Nom : Philippe Dunski
Âge : 41

Informations forums :
Inscription : octobre 2004
Messages : 8 613
Points : 13 289
Points : 13 289
Envoyer un message via MSN à koala01 Envoyer un message via Skype™ à koala01
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
je ne répondrai à aucune question technique par E-mail, message visiteur ou message privé
Vous avez obtenu votre réponse pensez au bouton en bas de page
koala01 est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 29/11/2012, 02h35   #3
koala01
Modérateur
 
Avatar de koala01
 
Philippe Dunski
Inscription : octobre 2004
Messages : 8 613
Détails du profil
Informations personnelles :
Nom : Philippe Dunski
Âge : 41

Informations forums :
Inscription : octobre 2004
Messages : 8 613
Points : 13 289
Points : 13 289
Envoyer un message via MSN à koala01 Envoyer un message via Skype™ à koala01
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
je ne répondrai à aucune question technique par E-mail, message visiteur ou message privé
Vous avez obtenu votre réponse pensez au bouton en bas de page
koala01 est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 29/11/2012, 12h17   #4
kéraunos
Membre régulier
 
Avatar de kéraunos
 
Homme
Inscription : janvier 2005
Messages : 189
Détails du profil
Informations personnelles :
Sexe : Homme
Localisation : France

Informations forums :
Inscription : janvier 2005
Messages : 189
Points : 74
Points : 74
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.
kéraunos est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 29/11/2012, 17h56   #5
kéraunos
Membre régulier
 
Avatar de kéraunos
 
Homme
Inscription : janvier 2005
Messages : 189
Détails du profil
Informations personnelles :
Sexe : Homme
Localisation : France

Informations forums :
Inscription : janvier 2005
Messages : 189
Points : 74
Points : 74
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();
}
kéraunos est déconnecté   Envoyer un message privé Réponse avec citation 00
Réponse Cette discussion est résolue.
Outils de la discussion

Navigation rapide


Fuseau horaire GMT +2. Il est actuellement 11h23.


 
 
 
 
Partenaires

Hébergement Web