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

QGraphics Confusion (Differences between QT4 / QT5)



  • I saw an old example of using QGraphics. The example is from 2007 so it doesn't seem to work with the actual Qt5 Version. I couln't find a documentation of the old interface.

    The old example is like this:

    int main( int argc, char **argv )
    {
      QApplication app( argc, argv );
      
      QGraphicsScene scene( 0, 0, 200, 200 );
      
      QGraphicsRectItem *rectItem = new QGraphicsRectItem( QRect( 10, 10, 50, 100 ), 0, &scene );
    
      QGraphicsView view;
      view.setScene( &scene );
      view.show();
      
      return app::excec();
    }
    

    Now it looks like the constructor of QGraphicsRectItem does not has this constructor anymore. It doesn't seem to be possible to add the scene in it . Instead i used scene.addItem like this:

    int main(int argv, char** argc)
    {
        QApplication app{argv, argc};
        
        QGraphicsScene scene{QRect{0, 0, 200, 200}};
        
        auto rectItem = new QGraphicsRectItem{QRect{10, 10, 50, 100}};
        
        scene.addItem(rectItem);
        
        QGraphicsView view;
        view.setScene(&scene);
        view.show();
        
        return QApplication::exec();
    }
    

    Now why do i ask all this if I found a workarround? There is annother bigger example here:

    https://github.com/Apress/foundations-of-qt-dev/tree/master/Chapter07/graphicsview/interaction

    It creates a Ellipse and a Square on the Screen. These can be resized or moved by special designed handles. See the 3 files in the link.

    I rewrote it to QT5 but i have two issues:

    I can only move the single handles. Not all Handles and the Circle or Ellipse are moved / resized.

    My code looks like this:

    int main(int argv, char** argc)
    {
        QApplication app{argv, argc};
        
        QGraphicsScene scene{QRect{0, 0, 200, 200}};
        
        auto rectItem = new QGraphicsRectItem{QRect{10, 10, 50, 100}};
        auto elItem = new QGraphicsEllipseItem{QRect{80, 40, 100, 80}};
        
        auto trh = new HandleItem(rectItem, Qt::red, HandleItem::Role::Top);
        auto rrh = new HandleItem(rectItem, Qt::red, HandleItem::Role::Right);
        auto crh = new HandleItem(rectItem, Qt::red, HandleItem::Role::Center,
                                  QList<HandleItem*>{trh, rrh});
        
        auto teh = new HandleItem(elItem, Qt::green, HandleItem::Role::Top);
        auto reh = new HandleItem(elItem, Qt::green, HandleItem::Role::Right);
        auto ceh = new HandleItem(elItem, Qt::green, HandleItem::Role::Center,
                                  QList<HandleItem*>{teh, reh});
        
        scene.addItem(rectItem);
        scene.addItem(elItem);
        
        scene.addItem(trh);
        scene.addItem(rrh);
        scene.addItem(crh);
        
        scene.addItem(teh);
        scene.addItem(reh);
        scene.addItem(ceh);
        
        QGraphicsView view;
        view.setScene(&scene);
        view.show();
        
        return QApplication::exec();
    }
    
    #ifndef HANDLEITEM_H
    #define HANDLEITEM_H
    
    #include <QGraphicsItem>
    
    class HandleItem : public QGraphicsItem
    {
    public:
    
        enum class Role
        {
            Center,
            Right,
            Top
        };
    
        HandleItem(QGraphicsItem *item,
                   QColor color, Role role = Role::Center,
                   QList<HandleItem*> handles = QList<HandleItem*>{});
    
        QRectF boundingRect() const override;
        void paint(QPainter *painter,
                   const QStyleOptionGraphicsItem *option, QWidget *widget) override;
    
    protected:
        void mousePressEvent(QGraphicsSceneMouseEvent *event) override;
        void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override;
        QVariant itemChange(
            GraphicsItemChange change, const QVariant &value) override;
    
    private:
        QGraphicsItem *mItem;
    
        Role mRole;
        QColor mColor;
    
        QList<HandleItem*> mHandles;
    
        bool mPressed{false};
    };
    
    #endif // HANDLEITEM_H
    
    #include "handleitem.h"
    
    #include <QPainter>
    #include <QTransform>
    
    HandleItem::HandleItem(
        QGraphicsItem *item,
        QColor color, HandleItem::Role role,
        QList<HandleItem *> handles)
        : QGraphicsItem{},
        mItem{item},
        mRole{role},
        mColor{color},
        mHandles{handles}
    {
        setZValue(100);
        setFlag(ItemIsMovable);
    }
    
    QRectF HandleItem::boundingRect() const
    {
        auto point = mItem->boundingRect().center();
    
        switch(mRole) {
        case Role::Center:
            return QRectF(point-QPointF{5, 5}, QSize{10, 10});
        case Role::Right:
            point.setX(mItem->boundingRect().right());
            return QRectF(point-QPointF{3, 5}, QSize{6, 10});
        case Role::Top:
            point.setY(mItem->boundingRect().top());
            return QRectF(point-QPointF{5, 3}, QSize{10, 6});
        }
        return QRectF{};
    }
    
    void HandleItem::paint(
        QPainter *painter, const QStyleOptionGraphicsItem * /*option*/,
        QWidget * /*widget*/)
    {
        painter->setPen(mColor);
        painter->setBrush(mColor);
    
        auto rect = boundingRect();
        QVector<QPointF> points;
    
        switch(mRole) {
        case Role::Center:
            painter->drawEllipse(rect);
            break;
        case Role::Right:
            points.push_back(rect.center()+QPointF{3, 0});
            points.push_back(rect.center()+QPointF{-3, -5});
            points.push_back(rect.center()+QPointF{-3, 5});
            painter->drawConvexPolygon(QPolygonF{points});
            break;
        case Role::Top:
            points.push_back(rect.center()+QPointF{0, -3});
            points.push_back(rect.center()+QPointF{-5, 3});
            points.push_back(rect.center()+QPointF{5, 3});
            painter->drawConvexPolygon(QPolygonF{points});
            break;
        }
    }
    
    void HandleItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
    {
        mPressed = true;
        QGraphicsItem::mousePressEvent(event);
    }
    
    void HandleItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
    {
        mPressed = false;
        QGraphicsItem::mousePressEvent(event);
    }
    
    QVariant HandleItem::itemChange(
        QGraphicsItem::GraphicsItemChange change, const QVariant &value)
    {
        if(change == ItemPositionChange && mPressed) {
            auto movement = value.toPoint() - pos();
            auto center = mItem->boundingRect().center();
    
            switch(mRole)
            {
            case Role::Center:
            {
                mItem->moveBy(movement.x(), movement.y());
    
                QTransform transform;
                transform.translate(movement.x(), movement.y());
    
                for(auto& handle : mHandles) {
                    handle->setTransform(transform);
                }
            }
                break;
            case Role::Right:
            {
                if(-2*movement.x() + mItem->sceneBoundingRect().width() <= 5){
                    return QGraphicsItem::itemChange(change, pos());
                }
    
                movement.setY(0);
    
                QTransform transform;
                transform.translate(center.x(), center.y());
                transform.scale(
                    1.0 + 2.0*movement.x() / mItem->sceneBoundingRect().width(),
                    1);
                transform.translate(-center.x(), -center.y());
                mItem->setTransform(transform);
            }
                break;
            case Role::Top:
            {
                if(-2*movement.y() + mItem->sceneBoundingRect().height() <= 5){
                    return QGraphicsItem::itemChange(change, pos());
                }
    
                movement.setX(0);
    
                QTransform transform;
                transform.translate(center.x(), center.y());
                transform.scale(
                    1,
                    1.0-2.0*movement.y() / mItem->sceneBoundingRect().height());
                transform.translate(-center.x(), -center.y());
                mItem->setTransform(transform);
    
            }
                break;
            }
            return QGraphicsItem::itemChange( change, pos()+movement );
        }
        return QGraphicsItem::itemChange(change, value);
    }
    

    From my understanding i add the ellipse, the rectangle and the handles into the scene. Also I add the pointers for the ellipse and the rectangle to the handles.
    In the handles the ellipse / rectangle is accesed and modified via the handles. Then why the modification is not drawn in the scene as expected?

    I would be gratefull if you can give me some hint.


Log in to reply