Partage
  • Partager sur Facebook
  • Partager sur Twitter

[Qt] Réctangle de sélection sur une image

Sujet résolu
Anonyme
    1 juin 2012 à 16:41:21

    Salut à tous !

    Je suis un peu paumé... Voilà mon idée : j'aimerai que l'utilisateur puisse sélectionner une partie d'une image en restant le clic gauche appuyé et en bougeant la souris (le truc classique quoi :lol: ). Or, avec le code suivant, plusieurs problèmes apparaissent :
    • Lors de la descente des scrolls, la sélection descend également et sa taille sur l'axe des ordonnées est modifié, sauf si celle touche le bord supérieur
    • Si l'on a descendu le scroll et que l'on veut créer une nouvelle sélection, celle-ci est décalé par rapport au curseur de la souris
    • Si l'on dépasse la taille de l'image, la sélection s’agrandit légèrement sur les bords opposés à ceux de l'image dépassés


    Mon code :
    Scene::Scene(QWidget *parent, int x, int y) : QGraphicsScene(parent), tailleX(x), tailleY(y) { // x et y correspondent à la dimension de l'image
      selection = 0;
      selectionne = false;
    }
    
    Scene::~Scene() {
      delete selection;
    }
    
    QRect Scene::sel() {
      return selection->contentsRect();
    }
    
    void Scene::mouseMoveEvent(QGraphicsSceneMouseEvent *event) {
      if (selection != 0) {
        int x, y, x1, y1, x2, y2;
        x = event->scenePos().x();
        y = event->scenePos().y();
        if (posClicX >= x) {
          x1 = x;
          x2 = posClicX - x;
        } else {
          x1 = posClicX;
          x2 = x - posClicX;
        }
        if (posClicY >= y) {
          y1 = y;
          y2 = posClicY - y;
        } else {
          y1 = posClicY;
          y2 = y - posClicY;
        }
    
        if (x1 < 0) {
          x1 = 0;
        } else if (x1 > tailleX) {
          x1 = tailleX;
        }
        if (x2 < 0) {
          x2 = 0;
        } else if (x2 > tailleX) {
          x2 = tailleX;
        }
        if (y1 < 0) {
          y1 = 0;
        } else if (y1 > tailleY) {
          y1 = tailleY;
        }
        if (y2 < 0) {
          y2 = 0;
        } else if (y2 > tailleY) {
          y2 = tailleY;
        }
    
        selection->setGeometry(x1, y1, x2, y2);
      }
    }
    
    void Scene::mousePressEvent(QGraphicsSceneMouseEvent *event) {
      if (event->button() == Qt::LeftButton) {
        if (selection == 0) {
          selection = new QRubberBand(QRubberBand::Rectangle, views().first()->viewport());
          posClicX = event->buttonDownScreenPos(Qt::LeftButton).x();
          posClicY = event->buttonDownScreenPos(Qt::LeftButton).y();
          selectionne = true;
        } else if (!selectionne) {
          selection->resize(0, 0);
          posClicX = event->buttonDownScenePos(Qt::LeftButton).x();
          posClicY = event->buttonDownScenePos(Qt::LeftButton).y();
          selectionne = true;
        }
        selection->move(event->buttonDownScreenPos(Qt::LeftButton));
        selection->show();
      }
    }
    
    void Scene::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) {
      if (event->button() == Qt::LeftButton) {
        selectionne = false;
      }
    }
    
    // Bouge la sélection
    void Scene::bougerSelection(int x, int y) {
      int x1 = selection->geometry().x();
      int y1 = selection->geometry().y();
      selection->setGeometry(x1 + x, y1 + y, selection->geometry().width(), selection->geometry().height());
    }
    


    A l'aiiiiiiide !
    • Partager sur Facebook
    • Partager sur Twitter
      2 juin 2012 à 21:07:29

      hum... alors je pense qu'en procédant ainsi (mettre le RubberBand dans la class Scene) tu vas te heurter à des problèmes de transformation de coordonnées (c'est d'ailleurs ce que tu décris). En fait, je pense qu'il faut continuer en Model/View de la même façon que Qt marche, je m'explique: En gros, tu vas en effet avoir une "sélection" dans ta scène, mais ce ne sera rien d'autre qu'un stupide rectangle (ou autre forme) dont les coordonnées sont par rapport à la scène. (jette aussi un p'tit oeil à QGraphicsScene::setSelectionArea(), qui pourrait t'intéresser). Ensuite, c'est dans le widget qui peint la scène (point de vue = viewport) qu'il faut ajouter le RubberBand, qui devra alors traduire les coordonnées de la sélection vers les coordonnées du widget parent, et inversement quand celle-ci est modifiée, pour cela, il existe QGraphicsView::mapFromScene() et QGraphicsView::mapToScene(). Voilà, je pense que tu as de quoi faire un code un peu plus propre la déjà. De plus (même si je ne pense pas que ce soit ton intention) cette façon de voir les chose te permet d'avoir plusieurs vue de la même scène, en tout cas, c'est dans ce but que Qt utilise ce design pattern.
      • Partager sur Facebook
      • Partager sur Twitter
      Anonyme
        2 juin 2012 à 22:09:25

        J'ai essayé de modifier mon code mais j'avoue être complétement largué. Voici donc ce que j'ai fais.
        Je créer un QRect qui s'occupe de faire la "sélection" dans ma scène :
        Scene::Scene(QObject *parent) : QGraphicsScene(parent) {
          selEnCours = false;
        }
        
        QRect Scene::selection() {
          return sel;
        }
        
        void Scene::mouseMoveEvent(QGraphicsSceneMouseEvent *event) {
          if (selEnCours) {
            int x0, y0, x1, y1;
            sel.getCoords(&x0, &y0, 0, 0);
            x1 = event->scenePos().x();
            y1 = event->scenePos().y();
            sel.setCoords(x0, y0, x1, y1);
            emit chgntSel();
          }
        }
        
        void Scene::mousePressEvent(QGraphicsSceneMouseEvent *event) {
          if (event->button() == Qt::LeftButton) {
            int x, y;
            x = event->buttonDownScenePos(Qt::LeftButton).x();
            y = event->buttonDownScenePos(Qt::LeftButton).y();
            sel.setRect(x, y, 0, 0);
            selEnCours = true;
          }
        }
        
        void Scene::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) {
          selEnCours = false;
        }
        


        J'ai rajouté dans la classe Scene un signal permettant de savoir quand la sélection change (chgntSel). Celui-ci est connecté avec une fonction de la fenêtre principale :
        FenSelectionnerPartieEcran::FenSelectionnerPartieEcran(QWidget *parent) : QDialog(parent), ui(new Ui::FenSelectionnerPartieEcran) {
          ui->setupUi(this);
        
          sc = new Scene(this);
          cadre = new QRubberBand(QRubberBand::Rectangle, this);
        
          connect(sc, SIGNAL(chgntSel()), this, SLOT(changementSelection()));
        }
        
        FenSelectionnerPartieEcran::~FenSelectionnerPartieEcran() {
          delete ui;
          delete sc;
          delete cadre;
        }
        
        // Change le cadre de sélection
        void FenSelectionnerPartieEcran::changementSelection() {
          QRect sel = sc->selection();
          QPolygon coords = ui->graphicsView->mapFromScene(QRectF(sel));
          int x0, y0, x1, y1;
          coords.point(0, &x0, &y0);
          coords.point(2, &x1, &y1);
          cadre->setGeometry(x0, y0, x1, y1);
          cadre->show();
        }
        


        Or, maintenant, non seulement la sélection de s'affiche pas, mais si l'on essaye de sélectionner, le programme plante... Une idée , :)

        Merci beaucoup de ton aide !
        • Partager sur Facebook
        • Partager sur Twitter
          2 juin 2012 à 22:45:24

          Oui, une idée^^
          Alors, (oui, ça va être un poil plus long que prévu) tu vas devoir dériver la classe QGraphicsView (j'appellerais cette classe fille "Vue", et c'est dans cette nouvelle classe que tu vas devoir mettre les méthodes mouse*Event.
          Ensuite, le RubberBand devra être en attribut de cette nouvelle classe Vue et avoir pour parent cette même classe, et c'est cette classe Vue qui "demandera" à la scene d'effectuer les changement de coords.

          ...en gros ça donne des header avec ce profile:


          class Vue : public QGraphicsView
          {
          protected:
             QRubberBand * sel;// Ta représentation de la sélection (je n'allais quand même pas l'appeler "poivre")
             QPointF o_sel;// Point cliqué (origine de la selection)
             
          public:
             void mousePressEvent(QMouseEvent * e);// enregistre les coordonnée du clique dans o_sel
             void mouseMoveEvent(QMouseEvent * e);// met à jour l'affichage, mais pas la scene (à moins qu'il y ai besoin d'un comportement particulier, mais ça m'étonnerais)
             void moiseReleaseEvent(QMouseEvent * e); // Là seulement on enregistre la selection dans la scène (il faut envoyer les coordoné transphormer avec mapToScene()
             
          public slots:
             void rafraichirSel(const QRectF & s); // Permet de raffraichir la selection si une modification est apporté sur la scène (transphormer les coordonner avec mapFromScene)
          }
          
          class Scene : public QGraphicsScene
          {
          protected:
             QRectF sel; //La "véritable sélection"
             
          public:
             setSelection(const QRectF & n_sel); // Éffectue la modification de la selection et emet le signal selectionChangee
          signals:
             void selectionChangee(const QRectF & s);
          }
          

          rafraichirSel est connecté à selectionChangee.

          Ensuite il y a une option dans l'éditeur graphique d'interface qui permet justement de dire que tu utilises une classe dérivée de QGraphicsView, en faisant clique droit sur le widget mais après je sais plus là comme ça.
          • Partager sur Facebook
          • Partager sur Twitter
          Anonyme
            3 juin 2012 à 0:34:24

            J'ai commencé à implémenter tout ça. Merci encore !

            Mais voilà que je suis confronté à un problème : je dois enregistrer la sélection dans la scène dans mouseReleaseEvent. Or, je n'ai pas accès à la scène... Car si je fais :
            Scene sc(scene());
            

            cela va créer une nouvelle scène, mais ne va pas me renvoyer celle actuelle...
            • Partager sur Facebook
            • Partager sur Twitter
              3 juin 2012 à 1:17:17

              C'est sûr que si tu fais ainsi...

              Alors, t'as le choix de faire le cast direct, mais c'est dangereux si par mégarde, tu passes une QGraphicsScene à ta vue...
              moi, je ferrais ceci pour se protéger:

              class View : public QGraphicsView
              {
              private:// Pas sûr que ce soit vraiment jolie, mais ça permet de rendre innaccessible ces méthode de QGraphicsView à l'exterieur
                 using scene;
                 using setScene;
              public:
                 inline Scene * scene()//et on les réimplémente avec les bonne scene
                 {
                    return static_cast<Scene*>(QGraphicsView::scene());
                 }
                 
                 inline void setScene(Scene * s)
                 {
                    QGraphicsView::setScene(s);
                 }
              };
              
              • Partager sur Facebook
              • Partager sur Twitter
              Anonyme
                3 juin 2012 à 18:28:53

                Y'a du progrès ! :p
                Maintenant la sélection s'affiche mais pas au bon endroit (j'ai du mélanger les coordonnées) et elle n'est pas de la bonne taille. Lors de la descente du scroll, j'ai toujours le même problème. Voici le code actuel :

                scene.h
                class Scene : public QGraphicsScene {
                  Q_OBJECT
                
                  public:
                    explicit Scene(QObject *parent = 0);
                
                    void changerSel(QRectF nvSel);
                
                  signals:
                    void changementSel(QRectF nvSel);
                
                  private:
                    QRectF sel;
                };
                

                scene.cpp
                Scene::Scene(QObject *parent) : QGraphicsScene(parent) {
                
                }
                
                void Scene::changerSel(QRectF nvSel) {
                  sel = nvSel;
                  emit changementSel(sel);
                }
                

                vue.h
                class Vue : public QGraphicsView {
                  Q_OBJECT
                
                  public:
                    explicit Vue(QWidget *parent = 0);
                    ~Vue();
                
                    inline Scene *scene() {
                      return static_cast<Scene*>(QGraphicsView::scene());
                    }
                    inline void setScene(Scene *s) {
                      QGraphicsView::setScene(s);
                    }
                
                  protected:
                    void mouseMoveEvent(QMouseEvent *event);
                    void mousePressEvent(QMouseEvent *event);
                    void mouseReleaseEvent(QMouseEvent *event);
                
                  private slots:
                    void changerSel(QRectF nvSel);
                
                  private:
                    QRubberBand *sel;
                    QPointF origineSel;
                    bool selEnCours;
                };
                

                vue.cpp
                Vue::Vue(QWidget *parent) : QGraphicsView(parent) {
                  sel = new QRubberBand(QRubberBand::Rectangle, this);
                  selEnCours = false;
                
                  connect(scene(), SIGNAL(changementSel(QRectF)), this, SLOT(changerSel(QRectF)));
                }
                
                Vue::~Vue() {
                  delete sel;
                }
                
                void Vue::mouseMoveEvent(QMouseEvent *event) {
                  if (selEnCours) {
                    sel->setGeometry(origineSel.x(), origineSel.y(), event->x(), event->y());
                    sel->show();
                  }
                }
                
                void Vue::mousePressEvent(QMouseEvent *event) {
                  if (event->button() == Qt::LeftButton) {
                    origineSel = event->posF();
                    selEnCours = true;
                  }
                }
                
                void Vue::mouseReleaseEvent(QMouseEvent *event) {
                  QPolygonF coo = mapToScene(sel->rect());
                  Scene *sc = scene();
                  sc->changerSel(coo.boundingRect());
                  selEnCours = false;
                }
                
                void Vue::changerSel(QRectF nvSel) {
                  QPolygonF coo = mapFromScene(nvSel);
                  sel->setGeometry(coo.boundingRect().toRect());
                  sel->show();
                }
                


                Je rajoute qu'il est impossible de connecter le signal changementSel(QRectF) avec le slot changerSel(QRectF), j'ai un message d'erreur :
                .QObject::connect: Cannot connect (null)::changementSel(QRectF) to Vue::changerSel(QRectF)

                Je suppose que cela vient de la ré implémentation de QGraphicsView::scene(). Je n'ai pas pu ajouter les using. Les erreurs de compilations ci-dessous apparaissaient :
                expected nested-name-specifier before 'scene' (ligne 13)
                using-declaration for non-member at class scope (ligne 13)
                expected nested-name-specifier before 'setScene' (ligne 14)
                using-declaration for non-member at class scope (ligne 14)

                Les lignes 13 et 14 étant :
                private:
                    using scene; // ligne 13
                    using setScene; // ligne 14
                


                Enfin, dernier problème : il arrive que quand l'utilisateur ferme la fenêtre, le programme cesse de répondre...

                Merci beaucoup en tout cas :)
                • Partager sur Facebook
                • Partager sur Twitter
                  3 juin 2012 à 23:08:57

                  alors, d'abord, pour les erreurs de compilation:
                  excuse moi, j'ai oublié un p'tit truc pour le using:
                  c'est
                  using QGgraphicsScene::setScene;
                  au lieu de:
                  using setScene;

                  et ce n'est pas là peine pour scene() car la surcharge suffit.

                  Ensuite, pour le connect, c'est aussi normale: au moment où tu le fait, la sene n'existe pas encore (donc, impossible de la connecter). il faut le mettre dans le setScene() après l'appel à QGraphicsScene::setScene()
                  ...Mais du coup, il ne faut pas oublier de déconnecter la précédente Scene, mais SEULEMENT si il y en avait une précédente^^ (if(scene()) disconnect(scene(), 0, this, 0);)

                  Ensuite, dans le constructeur de Scene, ce n'est pas la peine d'émettre le signal (en effet, ton objet venant tout juste d'être créé, il ne peut y avoir quoique ce soit de connecté).

                  pour les probs de coordonnées, j'aurais besoin d'un screen pour voir (même si au point où j'en suis, je peux carrément faire le prog pour voir où ça coince)
                  • Partager sur Facebook
                  • Partager sur Twitter
                  Anonyme
                    4 juin 2012 à 0:11:57

                    Merci encore pour l'aide, je t'ai fais une mention spéciale dans le "A propos" du soft' ! Merci beaucoup !

                    Mais malheureusement, ce n'est toujours pas bon. Commençons par la bonne nouvelle : la connexion se fais bien.
                    Les mauvaises maintenant :
                    Lors du relâchement du clic de la souris, la sélection part de tout en haut de la scène pour arriver jusqu'au curseur, alors qu'elle devrait rester telle qu'elle.
                    Voici un screen pour les erreurs de coordonnées :
                    Image utilisateur

                    La croix c'est le curseur :)
                    • Partager sur Facebook
                    • Partager sur Twitter
                      4 juin 2012 à 9:14:58

                      ok, j'ai essayé en local. Alors plusieurs choses:
                      d'abord, il ne faut pas utiliser QWidget::rect() mais QWidget::geometry()
                      ( rect() renvoie toujours QRect(QPoint(0,0), QWidget::size()) du coup, forcément, ta selection est remise à l'origine).

                      ensuite, p'tite erreur de ma part, dans la vue, pour stocker le point d'origine, ce n'est pas un QPointF mais un QPoint (il faut donc adapter certaines fonctions par exemple: origineSel = event->pos() au lieu de event->posF()), en effet, le widget va travailler avec des entiers alors que la scene elle par contre travaille avec des réels (on le remarque d'ailleurs en utilisant les map(From/To)Scene())

                      Après, quand tu fais:
                      sel->setGeometry(origineSel.x(), origineSel.y(), event->x(), event->y());
                      tu dis que event->x() représente la largeur et event->y() la hauteur, ce qui évidemment est faux (Qt ne fait pas ses rectangles en prenant deux points au pif. Si on construit un QRect avec deux points, il faut impérativement que ce soient les supérieur-gauche et l'inférieur-droit et dans le bon ordre... c'est donc plus simple de prendre une origine et une taille (bien que celle-ci doit impérativement être positive...) ) j'admets que c'est un peu casse-tête au début^^

                      enfin, dernière remarque:
                      QPolygonF coo = mapFromScene(nvSel); sel->setGeometry(coo.boundingRect().toRect());
                      parait simple au premier abord, mais en réalité, ça alourdi vachement le code, car déterminer la boundingbox nécessité plusieurs calcules, bien que très certainement optimisé pour un rectangle, il faut minimum 8 tests, + les appels de fonctions + les opérations...
                      De plus, ça oblige à stocker trois points non nécessaires. Certes, vue qu'on ne fait qu'une fois seule fois, ça ne peut se voir ici, mais si un jour t'es amené à le faire sur des milliers de rectangles, on verra p't'être une différence... bref, même si ça ne sert pas à grand chose, on est toujours fier de gagner 2-3µs dans un prog :D

                      du coup, ce qu'il faut faire, c'est d'effectuer uniquement le calcule du point d'origine (la taille ne sera qu'un cast vers une taille entière). Pareil dans l'autre sens.

                      Voici donc tes fonctions corrigées :
                      void Vue::mouseMoveEvent(QMouseEvent *event)
                      {
                         if (selEnCours)
                         {
                            //Fameux calcule de la bouding box qui ne necessite que 2 test dans ce cas (mais plus avec les polygones)
                            qreal x = origineSel.x(), y = origineSel.y() ,w = event->x()-origineSel.x(),h = event->y()-origineSel.y();//Valeurs par défaut
                            if(x>event->x())
                            {
                               x = event->x();
                               w*=-1;//Alors il faut multiplier pat -1 pour rendre la taille positive
                            }
                            if(y>event->y())
                            {
                               y = event->y();
                               h*=-1;
                            }
                            sel->setGeometry(x,y , w,h);
                            sel->show();
                         }
                      }
                      
                      void Vue::mousePressEvent(QMouseEvent *event)
                      {
                         if (event->button() == Qt::LeftButton && scene())
                         {
                             origineSel = event->pos();
                             selEnCours = true;
                        }
                      }
                      
                      void Vue::mouseReleaseEvent(QMouseEvent *)
                      {
                         if(selEnCours)
                         {
                            scene()->changerSel(QRectF(mapToScene(sel->geometry().topLeft()), sel->geometry().size()));
                            selEnCours = false;
                         }
                      }
                      
                      void Vue::changerSel(QRectF nvSel)
                      {
                         sel->setGeometry(QRect(mapFromScene(nvSel.topLeft()), nvSel.size().toSize()));
                         sel->show();
                      }
                      


                      ps: je dois aussi te remercier car je ne connaissait pas QRubberBand :p ça pourrait m'être utile
                      • Partager sur Facebook
                      • Partager sur Twitter
                      Anonyme
                        4 juin 2012 à 12:47:26

                        Ah bah comme ça nos deux connaissances en Qt augmentent :p

                        Bon, y'a du mieux, mais c'est pas encore ça :D
                        Deux problèmes :

                        Toujours celui des scrolls (je t'ai même fais des screens, rien que pour toi :p) :
                        Imaginons que je fasse cette sélection...
                        Image utilisateur

                        Si je descend le scroll...

                        Image utilisateur

                        Et deuxième problème : la sélection peut dépasser du QGraphicsView (en bas à droite, ça dépasse dans l'intersection des scrolls) :
                        Image utilisateur

                        Merci encore !

                        P.S. Petite question : après je vais avoir besoin de récupérer la sélection (seulement les coordonnées), or si j'ai bien compris, l'attribut sel dans la classe Scene me donne les coordonnées de la sélection dans la scène ?
                        • Partager sur Facebook
                        • Partager sur Twitter
                          4 juin 2012 à 13:38:21

                          pour la scrollbar, regarde ceci (je te laisse deviner la suite...) http://doc-snapshot.qt-project.org/4.8 [...] ollContentsBy
                          en gros, la doc te dis que cette fonction est appelée à chaque fois que le contenu est scrollé.

                          Pour les coordonnées de la selection, (Scene::sel) elle sont en effet exprimées par rapport à la scene, et tant mieux, comme ça, si t'a besoin de récupérer l'image à l'interieur de la sélection, tu peux le faire facilement avec render():
                          QImage Scene::ImgSel()
                          {
                             QImage img(sel.size().toSize());
                             QPainter p(img);
                             render(&p, QRectF(), sel);
                             return img;
                          }
                          

                          ...à adapter selon la résolution que l'on souhaite...
                          • Partager sur Facebook
                          • Partager sur Twitter
                          Anonyme
                            4 juin 2012 à 22:38:55

                            Allez, un p'tit dernier problème pour la route lol
                            Tout marche quasiment bien, sauf quand il s'agit pour moi de récupérer le QRect de la sélection de Scene. Car pour éviter les dépassements sur les côtés, j'ai rajouté quelques petites choses :

                            void Scene::changerSel(const QRectF &nvSel) {
                              QSizeF taille(nvSel.size());
                              if (nvSel.x() + taille.width() > reso.width()) {
                                taille.setWidth(reso.width() - nvSel.x());
                              }
                              if (nvSel.y() + taille.height() > reso.height()) {
                                taille.setHeight(reso.height() - nvSel.y());
                              }
                            
                              QPointF origine(nvSel.topLeft());
                              if (origine.x() < 0) {
                                taille.setWidth(taille.width() + origine.x());
                                origine.setX(0);
                              }
                              if (origine.y() < 0) {
                                taille.setHeight(taille.height() + origine.y());
                                origine.setY(0);
                              }
                            
                              qDebug() << "Scene" << sel;
                            
                              sel = QRectF(origine, taille);
                              emit changementSel(sel);
                            }
                            


                            void Vue::changerSel(const QRectF &nvSel) {
                              QSize taille(nvSel.size().toSize());
                              if (nvSel.x() + taille.width() > reso.width()) {
                                taille.setWidth(reso.width() - nvSel.x());
                              }
                              if (nvSel.y() + taille.height() > reso.height()) {
                                taille.setHeight(reso.height() - nvSel.y());
                              }
                            
                              QPoint origine(mapFromScene(nvSel.topLeft()));
                              if (origine.x() < 0) {
                                taille.setWidth(taille.width() + origine.x());
                                origine.setX(0);
                              }
                              if (origine.y() < 0) {
                                taille.setHeight(taille.height() + origine.y());
                                origine.setY(0);
                              }
                            
                              qDebug() << "Vue" << sel->rect();
                            
                              sel->setGeometry(QRect(origine, taille));
                              sel->show();
                            }
                            


                            Or, pour tester si c'était bon, j'ai fais un qDebug (j'ai laissé les traces dans le code). Les chiffres sont assez aberrants : aucun similitude entre les coordonnées de la vue et de la scène...

                            Et merci encore !
                            • Partager sur Facebook
                            • Partager sur Twitter
                              4 juin 2012 à 23:23:01

                              en fait, tu ne peux pas tester cela à partir de la scene. si tu souhaite le faire, il faut que tu fixe des maximums dans le mouseMoveEvent(). Ensuite, si la sélection est modifié directement sur la scene sans passer par Vue, les seules choses que tu puisses faire, c'est de ne tout simplement pas afficher, ou alors corriger dans le slot Vue::changerSel(). mais attention, car tu risque de rentrer dans une récurrence indirect : imaginons que tu ais deux Vues sur la même Scene, mais qu'elles regardes à des endroit totalement différent, alors aucune sélection ne peut avoir lieux sans être modifiée par l'autre Vue, or cette sélection modifié ne conviendra pas non plus à la première vue qui va alors la remodifier etc. etc.

                              Pour la différence de coordonnée, c'est aussi normale : la Vue centre la Scene (en se basant sur les items dessus), mais quand il n'y a pas d'item, c'est complétement aléatoire. essai d'ajouter une ligne entre (-10, 10) et (10,10) et tu verras que celle-ci sera parfaitement centrée.
                              • Partager sur Facebook
                              • Partager sur Twitter
                              Anonyme
                                4 juin 2012 à 23:40:39

                                Je ne te suis pas sur ce point là... Comment la sélection pourrait être modifié sans passer par la vue ?
                                Et le cas où la vue affiche plusieurs scène est impossible pour mon soft :p
                                • Partager sur Facebook
                                • Partager sur Twitter
                                  5 juin 2012 à 13:01:21

                                  Scene::SetSelection()

                                  ...Ben en fait, de ce que j'avais pu comprendre de ton premier code, c'est que la sélection appartient à la scène (c'est à dire que la vue "obéit" à la scène) ainsi, n'importe quoi d'extérieur peut actuellement modifier cette sélection... après, l'autre façon de voir les choses, c'est "la sélection appartient à la vue" et c'est comme ça que t'as envie que ça marche on dirait? dans ce cas, il y a pas mal de trucs à modifier (mais le tout c'est d'être d'accord sur le fonctionnement). Cette Vision à l'inconvénient d'avoir un code plus compliqué à changer si un jour tu change d'avis et que tu veuilles avoir plusieurs point de vue sur la scène... un exemple : tu veux imprimé ta sélection, ben, avec le model que tu souhaite, tu rajouter une fonction, qui doit re-traduire les coordonnée vers la scene etc. tandis qu'avec l'autre, tu attaque directement la scène avec sa fonction de rendu (qui pourra être éventuellement surchargé pour ne rendre que la selection) Imaginons ensuite que la sélection ne convienne pas trop pour l'impression, et ben tu peux avoir une fenêtre qui demande coment la modifer en pixels etc. bon, ensuite, les deux marcheront évidemment, mais c'est une histoire de clarté. J'ai déjà fait l'expérience d'un projet où je n'avait pas vraiment respecté le rôle de chaque objet, puis au final, j'ai dû tout reprendre de zéro, car j'avais des fonctions qui ne devaient pas être là où elles était puis un "bordel" total...
                                  Si à chaque ois tu dois passer par la vue pour modifier la scène, tu te doute bien que c'est lourd... autant direct attaquer la scène.

                                  Pour ton problème de scroll, à ta place, je mettrais le limite dans mouseMoveEvent(). Ensuite, si la selection est modifiée sans passer par la Vue, soir tu affiche dan tous les cas, soit tu test si elles rentre, si oui tu affiche, sinon, tu masque la sélection... Dans tous les cas, t'obtiens le résultat voulu en utilisant qu'une seule Vue, mais avec la possibilité d'ajouter des fonctionnalités après.

                                  ...Maintenant, si tu es sûr de n'avoir qu'une seule vue, tu peux toujours supprimer toutes les trace de "sélection" dans la scène, et directement te servir du QRubberBand, et mettre les fonctions qui te sont nécessaire dans la vue. ce design a aussi ces point fort : plus besoin de signaux/Slot, plus besoin d'avoir une Scène custom, autant directement utiliser la QGraphicsScene. C'est vrai, que si tu es certain, ça allège, mais faut être sûr de ne pas revenir sur cette position.
                                  • Partager sur Facebook
                                  • Partager sur Twitter
                                  Anonyme
                                    5 juin 2012 à 20:05:20

                                    Bah en fait le soft' permet de faire des captures d'écran sur une durée données avec un nombre donné de captures par secondes. Or, l'utilisateur a le choix de ne capturer qu'une partie de l'écran, d'où la sélection d'une partie de l'écran. Que me conseilles-tu ? Car je ne m'attendais vraiment pas à ça, je croyais que ça allait être bien plus simple ! :p
                                    • Partager sur Facebook
                                    • Partager sur Twitter
                                    Anonyme
                                      6 juin 2012 à 20:21:02

                                      Pas d'idée ? :(

                                      J'ai changé mon code dans mouseMoveEvent :
                                      void Vue::mouseMoveEvent(QMouseEvent *event) {
                                        if (selEnCours) {
                                          qreal x = origineSel.x(), y = origineSel.y(), w = event->x() - origineSel.x(), h = event->y() - origineSel.y();
                                          if(x > event->x()) {
                                            x = event->x();
                                            w *= -1;
                                          }
                                          if(y > event->y()) {
                                            y = event->y();
                                            h *= -1;
                                          }
                                      
                                          if (x + w > reso.width()) {
                                            w = reso.width() - x;
                                          }
                                          if (x < 0) {
                                            w += x;
                                            x = 0;
                                          }
                                          if (y + h > reso.height()) {
                                            h = reso.height() - y;
                                          }
                                          if (y < 0) {
                                            h += y;
                                            y = 0;
                                          }
                                          sel->setGeometry(x, y, w, h);
                                          sel->show();
                                        }
                                      }
                                      


                                      Or ce code ne fonctionne pas puisqu'il prend en compte les coordonnées à l'écran et non sur la scène. Et je pige pas vraiment mapToScene et mapFromScene...
                                      • Partager sur Facebook
                                      • Partager sur Twitter
                                        6 juin 2012 à 22:25:50

                                        si, idée, mais pas présent pour présenter idée ^^

                                        Alors, déjà, si c'est juste pour une affaire de screenshot, je vois pas trop pourquoi tu t'embêtes avec une QGraphicsScene, soit ton image est déjà prête au moment où tu fais la sélection, dans ce cas, suffit de la mettre dans un QLabel, tu mets le mouse*event() dans le QLabel (enfin classe dérivée de QLabel, puis t'utilses directement les coordonnées de QRubberband) sinon, il doit sûrement falloir composer avec QDesktopWidget pour pouvoir sélectionner directement sur l'écran.

                                        [Edit]
                                        QGraphicsScene sert à dessiner des "formes", en gros, c'est une surface de dessin vectoriel, qui peut notamment être utilisées pour des jeux, représenter une image SVG, faire quelques trus animés etc. etc. mais sûrement pas pour pour une simple image (enfin ça peut mais c'est lourd)...
                                        Du coup, ça marche ainsi:
                                        T'as une scène (abstraite) qui possède tous les items (les "formes"), avec son propre repère, et son propre système de coordonnées. Après, il y a la QGraphicsView qui permet d'afficher cette Scène ou plutôt, une partie de cette scène. T'as donc un "paysage" et un ou plusieurs "point de vue". Tu te doutes bien que les coordonnées par rapport à une vue et les coordonnée par rapport à la scène sont différentes. C'est pour ça qu'il existe les fonctions map(To/From)Scene(), qui permettent d'effectuer ce qu'on appellerait en math le changement de base. (en interne, ça marche avec des matrices de passage) ainsi, si tu as les coordonnées de ton rectangle exprimée dans la vu, mapToScene va permettre des les "traduire" par rapport à la Scène. Par ailleurs, les Widgets marchent avec des coordonnées entières (avec pour unité le pixel), or, la scène étant "virtuelle", elle par contre travaille avec des coordonnées réelles qui permettent une meilleur précision.

                                        Voilà donc pour l'explication, comme tu vois, ça ne semble pas adapté à ce que tu veux faire... Maintenant, si tu veux des conseils précis et adaptés, il faudrait que tu détailles exactement ce que tu souhaites. ;)
                                        (même si d'après le screenshot, il semblerais que tu ais déjà ton image au moment de la sélection, donc il faudrait s'orienter vers le QLabel)
                                        [/EDIT]
                                        • Partager sur Facebook
                                        • Partager sur Twitter
                                        Anonyme
                                          6 juin 2012 à 23:50:35

                                          Ouais j'aurai du t'expliquer plus tôt... Mais j'avais déjà pensé au QLabel, un seul problème : je veux que la sélection renvoie un QRect qui représente la partie de l'écran à capturer. Or, si j'utilise un QLabel + QRubberBand, les coordonnées renvoyées vont elles être les mêmes que ceux de l'écran ?
                                          Je m'explique plus clairement :
                                          Si l'utilisateur fais une sélection sur l'image ayant pour coordonnées le coin supérieur gauche à (0, 0) et le inférieur droit à (10, 10). Le QLabel va me renvoyer un truc du genre (10, 15) -> (20, 25) puisque les coordonnées vont être émise dans le repère de l'écran actuelle et non de la capture, non ?
                                          • Partager sur Facebook
                                          • Partager sur Twitter
                                            6 juin 2012 à 23:57:05

                                            ...Pas si le QRubberBand est fils du QLabel, dans ce cas, les coordonnées renvoyées sont par rapport au QLabel, et donc au coin sup-gauche de l'image et encore donc des coordonnées "réelles" du screenshot (= tu peux réutiliser ces coordonnées pour faire une nouvelle prise au même endroit :D )

                                            <== Les coordonnées d'un Widget sont TOUJOURS par rapport au Widget parent
                                            ...et là encore, t'as des fonctions mapTo*() pour traduire ces coordonnées vers un autre Widget ou directement par rapport à l'écran si jamais il y avait besoin (mais là, tu n'en as pas besoin, c'est juste pour l'anecdote ;))
                                            • Partager sur Facebook
                                            • Partager sur Twitter
                                            Anonyme
                                              7 juin 2012 à 13:51:46

                                              J'ai bien peur que j'en ai tout de même besoin... La partie sélectionnée à l'écran n'est pas la même qui a été capturée...

                                              Mais rien ne vaut mieux que quelques screens :
                                              La sélection
                                              Image utilisateur

                                              La capture
                                              Image utilisateur

                                              Le code
                                              // Retourne la sélection définie par l'utilisateur
                                              QRect Label::selection() {
                                                return sel->rect();
                                              }
                                              
                                              void Label::mouseMoveEvent(QMouseEvent *ev) {
                                                if (selEnCours) {
                                                  qreal x = origineSel.x(), y = origineSel.y(), w = ev->x() - origineSel.x(), h = ev->y() - origineSel.y();
                                                  if(x > ev->x()) {
                                                    x = ev->x();
                                                    w *= -1;
                                                  }
                                                  if(y > ev->y()) {
                                                    y = ev->y();
                                                    h *= -1;
                                                  }
                                              
                                                  if (x + w > reso.width()) {
                                                    w = reso.width() - x;
                                                  }
                                                  if (x < 0) {
                                                    w += x;
                                                    x = 0;
                                                  }
                                                  if (y + h > reso.height()) {
                                                    h = reso.height() - y;
                                                  }
                                                  if (y < 0) {
                                                    h += y;
                                                    y = 0;
                                                  }
                                                  sel->setGeometry(x, y, w, h);
                                                  sel->show();
                                                }
                                              }
                                              


                                              Le QRect renvoyé est celui utilisé lors du découpage de la capture. Une idée ?
                                              • Partager sur Facebook
                                              • Partager sur Twitter
                                                7 juin 2012 à 14:17:20

                                                bon, fait moi un zip du projet, ce sera plus simple je pense ;)

                                                EDIT:

                                                Citation : AceSir

                                                return sel->rect();


                                                il est la le problème (je te l'ai déjà corrigé):

                                                Citation : Moi, le plus beau le plus fort x)

                                                d'abord, il ne faut pas utiliser QWidget::rect() mais QWidget::geometry()
                                                ( rect() renvoie toujours QRect(QPoint(0,0), QWidget::size()) du coup, forcément, ta selection est remise à l'origine).

                                                • Partager sur Facebook
                                                • Partager sur Twitter
                                                Anonyme
                                                  7 juin 2012 à 14:50:28

                                                  Ah désolé de te faire répéter. Et tout marche (enfin !). Merci, merci, merci, merci, merci ! Je te ferai un câlin si je le pouvais :lol:
                                                  You're the best ! :p
                                                  • Partager sur Facebook
                                                  • Partager sur Twitter
                                                    4 février 2019 à 11:17:21

                                                    Bonjour,

                                                    y a t il une méthode pour sélectionner une image dans un QtextEdit avec la souris (comme dans le Word) ? 

                                                    • Partager sur Facebook
                                                    • Partager sur Twitter

                                                    [Qt] Réctangle de sélection sur une image

                                                    × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
                                                    × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
                                                    • Editeur
                                                    • Markdown