Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

QGraphicsItem visible but mouse events disabled: foggy for me!



  • Using QtCreator and Qt Designer, I defined a QmainWindow subclass named PG_QT_MainWindow which incorporates a QtabWidget. The Architecture tab contains a QGraphicsView intended to display several QGraphicsPG objects (subclass of QGraphicsItem). The QGraphicsPG tree-like structure is mirroring that of its Phygenic member (model object). I can't use a standard QTreeWidgetItem since the Phygenic model incorporates some connections between children in addition to parent-child connections.
    This is the window obtained after loading 3 models. The 2nd model (large red frame= QRectF cadre) is shown as selected and in its expanded state:
    0_1520260525065_Tout_développé.png
    After a double click on this QgraphicsItem, it is now shown in its collapsed state:
    0_1520260508869_Tout_réduit.png
    This is the intended behavior and everything works ALMOST as expected BUT the subsequent QGraphicsPG item (Dzanibekov) does not react to mouse events: no ToolTip, nor click, nor double click anymore. Despite the fact that position and visibility is OK.
    After expanding again the 2nd QGraphicsPG item, all items accept again the mouse events.
    This my code:

    #ifndef PG_QT_MAINWINDOW_H
    #define PG_QT_MAINWINDOW_H
    
    #include <QMainWindow>
    #include "xploit_pg.h"
    #include <QGraphicsScene>
    #include "qgraphicspg.h"
    
    namespace Ui { class PG_Qt_MainWindow; }
    class PG_Qt_MainWindow : public QMainWindow
    {
        Q_OBJECT
    public:
        explicit PG_Qt_MainWindow(QWidget *parent = 0);
        ~PG_Qt_MainWindow();
        void redessiner_architecture() ;
    private slots:
        void on_actionQuitter_triggered();
        void on_actionOuvrir_triggered();
        void on_graphicsView_customContextMenuRequested(const QPoint &pos);
    private:
        bool chargerModelePG(const std::string nom_pgML ) ;
        Ui::PG_Qt_MainWindow *ui;
        QGraphicsScene scene ; 
        std::vector<Phygenic*> ptr_modele ; 
        std::vector<QGraphicsPG*> items ; 
    };
    #endif // PG_QT_MAINWINDOW_H
    

    With implementation (extract):

    #include "pg_qt_mainwindow.h"
    #include "ui_pg_qt_mainwindow.h"
    #include <QFileDialog>
    #include <QFile>
    #include <QTextStream>
    #include <QMessageBox>
    #include <QGraphicsTextItem>
    
    PG_Qt_MainWindow::PG_Qt_MainWindow(QWidget *parent) :
        QMainWindow(parent),
        ui(new Ui::PG_Qt_MainWindow)
    {
        ui->setupUi(this);
        ui->graphicsView->setScene(&scene);
        QObject::connect(&scene, SIGNAL(selectionChanged()), &scene, SLOT(update()));
        ui->graphicsView->show() ;
    }
    void PG_Qt_MainWindow::on_actionOuvrir_triggered()
    {
    	// (... omitted details = reading Phygenic file and push_back to ptr_modele vector)
            Indices2D position= { 0 , 0 } ;
            if ( items.size() > 0 ) {
                position.iV= items.at(items.size()-1)->Etat_develop && items.at(items.size()-1)->ptr_source->Nph >0 
    		       ? items.at(items.size()-1)->cadre.iV+1 : items.at(items.size()-1)->boite.iV+1 ;
            }
            bool develop= true ; int item_racine= items.size() ;
            QGraphicsPG* item= new QGraphicsPG(ptr_modele.back(), position, this, item_racine, 0, develop  ) ;
            items.push_back(item);
            scene.addItem(item);
    } 
    void PG_Qt_MainWindow::redessiner_architecture() 
    {
        Indices2D position= { 0 , 0 } ;
        for ( std::vector<QGraphicsPG*>::iterator it= items.begin() ; it !=items.end() ; ++it ) {
            (*it)->calculer(position) ;
            position.iV= (*it)->cadre.iV +1 ;
        } 
        scene.update() ;
    }
    

    QgraphicsPG header (extract):

    #ifndef QGRAPHICSPG_H
    #define QGRAPHICSPG_H
    
    #include <QGraphicsItem>
    #include <QMessageBox>
    #include "xploit_pg.h"
    class PG_Qt_MainWindow ;
    
    struct Indices2D { int iH ; int iV ; } ;
    (...)
    struct FlecheTransfert {
        QRect boule_o ; 
        QVector<QPointF> chemin ;
    } ;
    struct FlecheTransfert {
        QRect boule_o ;
        QVector<QPointF> chemin ;
    } ;
    char* c_char(QString qs) ;
    QString c_QString(std::string s) ;
    
    const int DIM_NOM_BOITE= 30 , DIM_TOTAL_BOITE= 70 ;
    const double LONG_ENTREES= 20. , LONG_BOITE= 60. , LONG_SORTIES= 10. , LONG_CELL= 100. ;
    const double HAUT_CELL= 46. , HAUT_BOITE= 30. , L_MARGE_CADRE= -4. ;
    const double RAYON_BOULE=  3. ;
    const QPen noir(Qt::black) ;
    const QBrush jaune(Qt::yellow) ;
    const QBrush brush_boule(Qt::black) ;
    const QPen rouge(Qt::red) ;
    const QPen bleu(Qt::blue) ;
    const QPen sans_trait(Qt::transparent) ;
    const QBrush nonRempli(Qt::transparent) ;
    const QBrush couleur_selection(Qt::black , Qt::Dense7Pattern) ;
    
    class QGraphicsPG : public QGraphicsItem
    {
    public:
        QGraphicsPG(Phygenic* ptr_pg, Indices2D position, PG_Qt_MainWindow *fenetre, int item_racine, 
    		QGraphicsItem *parent = 0, bool develop= true, bool avecCalcul= true );
        ~QGraphicsPG();
        QRectF boundingRect() const ;
        void paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
                   QWidget *widget) ;
        void calculer(const Indices2D &position) ;
    
        PG_Qt_MainWindow *fenetre ; 
        Phygenic* ptr_source    ; 
        bool Etat_develop       ; 
        vector<QGraphicsPG*> ss_qgpg ; /*!< Phygenic children vector, filled if type is "assemblage" */
        Indices2D boite ; 
        Indices2D cadre ; 
        QRectF rectBoite ; 
        QRectF rectTexte ; 
        QString texte ; 
        QPolygonF flecheEntreeBoite ; 
        QPolygonF flecheSortieBoite ; 
        QRectF rectCadre ; 
        vector<FlecheTransfert> flechesInternes ; 
        vector<QVector<QPointF> > flechesResultats ; 
        QRectF rectBounds ; 
    protected:
        void mouseDoubleClickEvent(QGraphicsSceneMouseEvent * event) ;
    };
    #endif // QGRAPHICSPG_H
    

    QGraphicsPG implementation (extract):

    #include <QtGui>
    #include <QGraphicsSceneMouseEvent>
    #include <QToolTip>
    #include "qgraphicspg.h"
    #include "pg_qt_mainwindow.h"
    const char* c_chr(QString qs) 
    {
        std::string s= qs.toStdString() ;
        return s.c_str() ;
    } 
    QString c_QString(std::string s) { 
        return QString(s.c_str() ) ; 
    }
    QGraphicsPG::QGraphicsPG(Phygenic *ptr_pg, Indices2D position,  PG_Qt_MainWindow *fenetre1, int item_racine , 
                             QGraphicsItem *parent, bool develop, bool avecCalcul )
        : QGraphicsItem(parent) , rectBoite() , rectTexte(), texte() , flecheEntreeBoite() , 
          flecheSortieBoite() , rectCadre(), rectBounds()
    {
        setToolTip( c_QString(" "+ptr_pg->nom) ) ;
        setFlag(QGraphicsItem::ItemIsSelectable, true) ;
        fenetre= fenetre1 ;
        ptr_source= ptr_pg ;
        Etat_develop= develop ;
        boite= position ;
        cadre= position ;
        Indices2D ss_pos ;
        for  ( int iph= 0 ; iph< ptr_source->Nph ; iph++ ) {
           ss_pos.iH= position.iH ; ss_pos.iV= position.iV + iph ;
           ss_qgpg.push_back ( new QGraphicsPG(ptr_source->ph[iph], ss_pos, fenetre, item_racine, this, Etat_develop, false ) ) ;
        } // next iph
        if (avecCalcul) calculer(boite) ;
    } 
    QRectF QGraphicsPG::boundingRect() const 
    { 
        return rectBounds; 
    }
    void QGraphicsPG::calculer(const Indices2D &position) 
    {
        flechesInternes.clear() ;
        // (... omis= tuyautage pour flèches d'entrée des sous-modèles ... )
        boite= position ;
        cadre= boite ;
        if ( Etat_develop && ptr_source->Nph> 0 ) 
        {
            // (... omitted= calc of cadre and children's positions with recursive calls of calculer() if needed)
        }
        // géométrie boite
        double x_boite= LONG_ENTREES +LONG_CELL * boite.iH , y_boite= (HAUT_CELL-HAUT_BOITE)/2 +HAUT_CELL * boite.iV ;
        rectBoite.setRect(x_boite, y_boite, LONG_BOITE, HAUT_BOITE) ;
        rectBounds.setRect(x_boite, y_boite, LONG_BOITE, HAUT_BOITE) ;
        // géométrie texte
       texte= c_QString(" "+ptr_source->nom) ;
        texte.append("\n") ;
        texte.append(c_QString(" "+ptr_source->espece) ) ;
        rectTexte.setRect(x_boite, y_boite+2 , 2.*LONG_BOITE, HAUT_BOITE) ;
        rectBounds|=rectTexte ;
        // géométrie cadre
        if ( ptr_source->Nph> 0 ) { // assemblage => cadre à tracer
            if ( Etat_develop )  cadre= ss_qgpg.at(ptr_source->Nph-1)->boite  ; else cadre= boite ;
            double L_cadre= LONG_CELL +2.*L_MARGE_CADRE +(Etat_develop ? LONG_CELL * (cadre.iH -boite.iH) : 0 ) ;
            double H_cadre= HAUT_CELL +2.*L_MARGE_CADRE +(Etat_develop ? HAUT_CELL * (cadre.iV -boite.iV) : 0 ) ;
            rectCadre.setRect(x_boite -LONG_ENTREES -L_MARGE_CADRE, y_boite -(HAUT_CELL-HAUT_BOITE)/2 -L_MARGE_CADRE, L_cadre, H_cadre ) ;
            rectBounds|=rectCadre ;
        } //end if ( ptr_source->Nph> 0 )
        // géométrie petite flèche d'entrée boite
        double x_entree= x_boite -LONG_ENTREES/2 ;
        flecheEntreeBoite.erase(flecheEntreeBoite.begin(),flecheEntreeBoite.end());
        flecheEntreeBoite<<QPointF(x_entree, y_boite +HAUT_BOITE/2. )
                   <<QPointF(x_boite, y_boite +HAUT_BOITE/2. )
                   <<QPointF(x_boite-3., y_boite +HAUT_BOITE/2. -3.)
                   <<QPointF(x_boite-3., y_boite +HAUT_BOITE/2. +3.)
                   <<QPointF(x_boite, y_boite +HAUT_BOITE/2. )  ;
        rectBounds|= flecheEntreeBoite.boundingRect() ;
        // géométrie petite flèche de sortie boite
        double x_sortie= x_boite +LONG_BOITE +LONG_SORTIES  ;
        flecheSortieBoite.erase(flecheSortieBoite.begin(),flecheSortieBoite.end());
        flecheSortieBoite<<QPointF(x_boite +LONG_BOITE, y_boite +HAUT_BOITE/2. )
                   <<QPointF(x_sortie, y_boite +HAUT_BOITE/2. )
                   <<QPointF(x_sortie-3., y_boite +HAUT_BOITE/2. -3.)
                   <<QPointF(x_sortie-3., y_boite +HAUT_BOITE/2. +3.)
                   <<QPointF(x_sortie, y_boite +HAUT_BOITE/2. )  ;
        rectBounds|= flecheSortieBoite.boundingRect() ;
        // géométrie flèches des liaisons internes
        if (Etat_develop && ptr_source->Nph >0) {
            QVector<QPointF> cheminFl ;
            double x_orig, y_orig, x_dest, y_dest ;
            for ( size_t it= 0 ; it<TuyauxEntree.size() ; it++ ) { 
                x_orig= TuyauxEntree.at(it).sortie
                  ? LONG_ENTREES +LONG_CELL * ( ss_qgpg.at( TuyauxEntree.at(it).iph_orig )->boite.iH ) +LONG_BOITE +LONG_SORTIES +RAYON_BOULE
                  : LONG_ENTREES +LONG_CELL * ( ss_qgpg.at( TuyauxEntree.at(it).iph_orig )->boite.iH ) -LONG_ENTREES/2    ;
                y_orig=  HAUT_CELL/2 +HAUT_CELL * ( ss_qgpg.at( TuyauxEntree.at(it).iph_orig )->boite.iV ) ;
                x_dest=  LONG_CELL * ( ss_qgpg.at( TuyauxEntree.at(it).iph_dest )->boite.iH )  +LONG_ENTREES/2 ;
                y_dest=  HAUT_CELL/2 +HAUT_CELL * ( ss_qgpg.at( TuyauxEntree.at(it).iph_dest )->boite.iV ) ;
                cheminFl.clear() ;
                cheminFl.push_back(QPointF(x_orig   , y_orig ));
                // (... omis= le détail du chemin)
                cheminFl.push_back(QPointF(x_dest   , y_dest ));
                QRect boule(x_orig -RAYON_BOULE, y_orig-RAYON_BOULE, 2.*RAYON_BOULE, 2.*RAYON_BOULE) ;
                FlecheTransfert fl ; fl.boule_o= boule ; fl.chemin= cheminFl ;
                flechesInternes.push_back(fl);
            } 
            // géométrie flèches de distribution à l'entrée
            x_orig= LONG_ENTREES +LONG_CELL * ss_qgpg.at( 0 )->boite.iH -LONG_ENTREES/2    ;
            y_orig=  HAUT_CELL/2 +HAUT_CELL * ss_qgpg.at( 0 )->boite.iV ;
            QRect boule(x_orig -RAYON_BOULE, y_orig-RAYON_BOULE, 2.*RAYON_BOULE, 2.*RAYON_BOULE) ;
    
            for  ( int iph= 1 ; iph< ptr_source->Nph ; iph++ ) {
                x_dest=  LONG_ENTREES +LONG_CELL * ss_qgpg.at( iph )->boite.iH -LONG_ENTREES/2  ;
                y_dest=  HAUT_CELL/2 +HAUT_CELL * ss_qgpg.at( iph )->boite.iV ;
                cheminFl.clear() ;
                cheminFl.push_back(QPointF(x_orig   , y_orig ));
                // (... omis= le détail du chemin)
                cheminFl.push_back(QPointF(x_dest   , y_dest ));
                FlecheTransfert fl ; fl.boule_o= boule ; fl.chemin= cheminFl ;
                flechesInternes.push_back(fl);
            } // next iph
            // géométrie des flèches collectant les résultats
            x_dest=  LONG_ENTREES +LONG_CELL * ss_qgpg.at( ptr_source->Nph-1 )->boite.iH +LONG_BOITE +LONG_SORTIES   ;
            y_dest=  HAUT_CELL/2 +HAUT_CELL * ss_qgpg.at( ptr_source->Nph-1 )->boite.iV ;
            for  ( int iph= 0 ; iph< ptr_source->Nph-1 ; iph++ ) {
                x_orig= LONG_ENTREES +LONG_CELL * ss_qgpg.at( iph )->boite.iH +LONG_BOITE +LONG_SORTIES   ;
                y_orig= HAUT_CELL/2 +HAUT_CELL * ss_qgpg.at( iph )->boite.iV ; ;
                cheminFl.clear() ;
                cheminFl.push_back(QPointF(x_orig   , y_orig ));
                // (... omis= le détail du chemin)
                cheminFl.push_back(QPointF(x_dest   , y_dest-RAYON_BOULE ));
                flechesResultats.push_back(cheminFl);
            } // next iph
        } 
    }
    void QGraphicsPG::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
               QWidget *widget) 
    {
        Q_UNUSED(option) ;
        Q_UNUSED(widget) ;
        QGraphicsItem *parent= this->parentItem() ;
        if ( parent != 0 && ((QGraphicsPG*)(parent))->Etat_develop == false ) return ;
        if ( isSelected() ) 
        {
            painter->setBrush(couleur_selection) ;
            painter->setPen(sans_trait) ;
            painter->drawRect(rectBounds);
        }
        if ( ptr_source->Nph== 0 || (!Etat_develop && ptr_source->Nph> 0 )  ) 
        {
            // boite
            painter->setBrush(jaune);
            painter->setPen(noir);
            painter->drawRect(rectBoite);
            // texte
            QTextOption texteOption= QTextOption() ; texteOption.setWrapMode(QTextOption::NoWrap) ;
            painter->drawText(rectTexte , texte, texteOption );
            // flèche d'entrée boite
            painter->setBrush(nonRempli);
            QPainterPath pathEntree ; pathEntree.addPolygon(flecheEntreeBoite) ;
            painter->drawPath(pathEntree );
            // flèche de sortie boite
            painter->setPen(bleu);
            QPainterPath pathSortie ; pathSortie.addPolygon(flecheSortieBoite) ;
            painter->drawPath(pathSortie );
        }
        if ( ptr_source->Nph> 0 ) 
        { 
            // cadre
            painter->setBrush(nonRempli);
            painter->setPen(rouge);
            painter->drawRect(rectCadre) ;
            if ( Etat_develop && ptr_source->Nph> 0 ) 
            {
                // flèches internes
                painter->setPen(noir);
                for ( size_t ifl= 0 ; ifl<flechesInternes.size() ; ifl++ ) { // parcours des flèches
                    painter->setBrush(brush_boule);
                    QRect boule= flechesInternes.at(ifl).boule_o ;
                    painter->drawEllipse(boule) ;
                    painter->setBrush(nonRempli);
                    QPainterPath pathTransfert; pathTransfert.addPolygon(flechesInternes.at(ifl).chemin) ;
                    painter->drawPath(pathTransfert);
                } 
                // flèches collectant les résultats
                painter->setPen(bleu);
                painter->setBrush(nonRempli);
                for ( size_t ifl= 0 ; ifl<flechesResultats.size() ; ifl++ ) { // parcours des flèches
                    QPainterPath pathResultat; pathResultat.addPolygon(flechesResultats.at(ifl)) ;
                    painter->drawPath(pathResultat);
                } 
            }
        }
    }
    void QGraphicsPG::mouseDoubleClickEvent(QGraphicsSceneMouseEvent * event) 
    {
        Q_UNUSED(event) ;
        qDebug()<<"Evènement double clic détecté sur "<<c_QString(ptr_source->nom) ;
       if ( ptr_source->Nph >0  ) {
           Etat_develop= !Etat_develop ;
           for  ( int iph= 0 ; iph< ptr_source->Nph ; iph++ ) ss_qgpg.at(iph)->setVisible(Etat_develop)  ;
           fenetre->redessiner_architecture() ;
       }
    }
    

    Could anyone help me cure that problem? Why can't the mouse events operate on the next QGraphicsPG item in the items vector after collapsing the previous item despite the fact that everything is visible and correctly positioned in the scene?


  • Lifetime Qt Champion

    Hi,

    Do you have any other mouse related method implemented ?



  • Thanks for replying!
    Mouse related methods are:

    • void QGraphicsPG::mouseDoubleClickEvent(QGraphicsSceneMouseEvent * event) (re-implemented);
    • throught the scene.selectedChanged() method (QGraphicsScene base class, not reimplemented), which is connected using: QObject::connect(&scene, SIGNAL(selectionChanged()), &scene, SLOT(update())) ;
    • throught the QGraphicsPG::QToolTip() which is set in the constructor.
    • through the 3 PG_Qt_MainWindow private slots: hereafter the code for the 2 missing in my previous post:
    void PG_Qt_MainWindow::on_actionQuitter_triggered()
    {
        ui->statusBar->showMessage("Quitter application PG_Qt... Bye!") ; 
        QCoreApplication::quit();
    }
    void PG_Qt_MainWindow::on_graphicsView_customContextMenuRequested(const QPoint &pos)
    {
        Q_UNUSED(pos) ;
        QMessageBox msgBox;
        msgBox.setIcon(QMessageBox::Information) ;
        msgBox.setText("Slot on_graphicsView_customContextMenuRequested appelé!");
        msgBox.setInformativeText("Ouais.....");
        msgBox.setStandardButtons(QMessageBox::Ok);
        msgBox.setDefaultButton(QMessageBox::Ok);
        msgBox.exec();
    }
    

    Nothing more I am aware of...


  • Lifetime Qt Champion

    No mouse tracking ?



  • @SGaist
    Mouse tracking is set using QtDesigner in PG_Qt_MainWindow and in graphicsView (as shown below) and never reset in the code:

    <widget class="QMainWindow" name="PG_Qt_MainWindow">
      (... omitted)
      </property>
      <property name="mouseTracking">
       <bool>true</bool>
      </property>
    (... omitted)
             <widget class="QGraphicsView" name="graphicsView">
              <property name="mouseTracking">
               <bool>true</bool>
              </property>
    


  • @SGaist
    I just found that connection:

    QObject::connect(&scene, SIGNAL(selectionChanged()), &scene, SLOT(update()));
    

    in the PG_QT_MainWindow constructor is useless: sounds like scene already connects selectedChanged() with update(). My code keeps the same GUI behavior; with still the same problem...


  • Lifetime Qt Champion

    Why are you tracking the mouse in the graphics view ?



  • @SGaist
    Well... I erroneously thought mouseTrack must be set in order to respond to any mouse event, including clicks and ToolTips. I just tried and reset mouseTrack in both PG_QT_MainWindow and graphicsView ; now GUI behavior is still the same. I need to read more carefully the documentation concerning mouse tracking...
    Concerning my initial question, i checked Enabled() status of QGraphicsPG objects because I suspected some of them were somehow disabled, but this appears not to be the case.
    I throughly checked their boundingRect() and detected no collision in any circumstances.
    now I suspect the pathTransfert and pathResultat (arrows):

    painter->drawPath(pathTransfert);
    (...)
    painter->drawPath(pathResultat);
    

    to persist and obscure/mask the next QGraphicsPG object with their enlarged boundingRect() even after collapsing their parent QGraphicsPG object since they are neither removed from the scene nor re-drawn ; but since they are not visible, how could they obscure anything?
    A drastic solution would be to clear() the scene and re-construct all GraphicsItem's after double-click, although I think there should be some lighter way to cure the problem...



  • @SGaist
    I corrected several bugs in my code but initial problem persists. Now engaged a substantial revamping trying to apply good (better) C++/Qt coding practise, hoping this will finally solve the problem.
    This topic will be closed. Thanks for your time & support anyway!



  • @SGaist
    Just for information: I have been desperatly hunting for this bug for more that 10 days and... Only this line was missing in my code:

    prepareGeometryChange();
    

    in method: void QGraphicsPG::calculer(const Indices2D &position) { ... }
    Now everything works perfectly OK (even if my code is not so nice).
    I finally nailed it after reading Qt documentation again and again.


Log in to reply