Search code examples
c++qtqgraphicsview

Strange effect on QGraphicsView upon Reimplementation of Mouse Event


I started a small Project using this source-code:

#include <QApplication>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QGraphicsRectItem>
#include <QGraphicsLineItem>

class CustomRectItem : public QGraphicsRectItem
{
public:
    CustomRectItem (const QRectF& rect) : QGraphicsRectItem(rect) {
    setFlag(QGraphicsItem::ItemIsMovable);
    setFlag(QGraphicsItem::ItemSendsScenePositionChanges);
    setAcceptHoverEvents(true);
}

void addLine(QGraphicsLineItem *line1, QGraphicsLineItem *line2) {
    if (this->data(0).toString() == "_p1") {
        this->leftLine = line1;
        this->topLine = line2;
    }
    if (this->data(0).toString() == "_p2") {
        this->topLine = line1;
        this->rightLine = line2;
    }
    if (this->data(0).toString() == "_p3") {
        this->rightLine = line1;
        this->bottomLine = line2;
    }
    if (this->data(0).toString() == "_p4") {
        this->bottomLine = line1;
        this->leftLine = line2;
    }
}

QPointF center(void) {
    return QPointF((rect().x() + rect().width() / 2),
                   (rect().y() + rect().height() / 2));
}

QVariant itemChange(GraphicsItemChange change, const QVariant &value)
{
    if (change == ItemPositionChange && scene()) {
        // value is the new position.
        QPointF newPos = value.toPointF();

        moveLineToCenter(newPos, this->data(0).toString());
    }
    return QGraphicsItem::itemChange(change, value);
}

void moveLineToCenter(QPointF newPos, QString pointString) {
    // Converts the polygon upper left position
    // to the upper left "handle"-rect center position
    qreal xOffset = rect().x() + rect().width()/2;
    qreal yOffset = rect().y() + rect().height()/2;

    QPointF newCenterPos = QPointF(newPos.x() + xOffset, newPos.y() + yOffset);

    QPointF p1;
    QPointF p2;

 // upper-left point
    if (pointString == "_p1") {
        p1 = leftLine->line().p1();
        p2 = newCenterPos;
        leftLine->setLine(QLineF(p1, p2));
        p1 = newCenterPos;
        p2 = topLine->line().p2();
        topLine->setLine(QLineF(p1, p2));
  // upper-right point
    } else if (pointString == "_p2") {
        p1 = topLine->line().p1();
        p2 = newCenterPos;
        topLine->setLine(QLineF(p1, p2));
        p1 = newCenterPos;
        p2 = rightLine->line().p2();
        rightLine->setLine(QLineF(p1, p2));
  // lower-right point
    } else if (pointString == "_p3") {
        p1 = rightLine->line().p1();
        p2 = newCenterPos;
        rightLine->setLine(QLineF(p1, p2));
        p1 = newCenterPos;
        p2 = bottomLine->line().p2();
        bottomLine->setLine(QLineF(p1, p2));
  // lower-left point
    } else if (pointString == "_p4") {
        p1 = bottomLine->line().p1();
        p2 = newCenterPos;
        bottomLine->setLine(QLineF(p1, p2));
        p1 = newCenterPos;
        p2 = leftLine->line().p2();
        leftLine->setLine(QLineF(p1, p2));
    } else {
        return;
    }
}

/////////////////////////////////////////////////////
/* -- comment-in this block to observe problem --  //
/////////////////////////////////////////////////////

protected:
virtual void hoverEnterEvent(QGraphicsSceneHoverEvent *event) {
    Q_UNUSED(event);
    this->setCursor(Qt::SizeAllCursor);
}
virtual void hoverLeaveEvent(QGraphicsSceneHoverEvent *event) {
    Q_UNUSED(event);
    this->unsetCursor();
}
virtual void mousePressEvent(QGraphicsSceneMouseEvent *event) {
    Q_UNUSED(event);
    this->setCursor(Qt::BlankCursor);
}
virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) {
    Q_UNUSED(event);
    this->unsetCursor();
    this->data(0).toString();
    if (this->isUnderMouse()) {
           this->setCursor(Qt::SizeAllCursor);
    }
}

/////////////////////////////////////////////////////
//       -- end of problem-causing block --        //
///////////////////////////////////////////////////// */

private:
QGraphicsLineItem *topLine;
QGraphicsLineItem *rightLine;
QGraphicsLineItem *bottomLine;
QGraphicsLineItem *leftLine;
};

int main(int argc, char *argv[]) {
    QApplication a(argc, argv);

    QGraphicsScene scene;

    CustomRectItem *custRect1 = new CustomRectItem(QRectF(30, 30, 10, 10));
    scene.addItem(custRect1);
    custRect1->setData(0, "_p1");

    CustomRectItem *custRect2 = new CustomRectItem(QRectF(70, 30, 10, 10));
    scene.addItem(custRect2);
    custRect2->setData(0, "_p2");

    CustomRectItem *custRect3 = new CustomRectItem(QRectF(70, 70, 10, 10));
    scene.addItem(custRect3);
    custRect3->setData(0, "_p3");

    CustomRectItem *custRect4 = new CustomRectItem(QRectF(30, 70, 10, 10));
    scene.addItem(custRect4);
    custRect4->setData(0, "_p4");

    QGraphicsLineItem *topLine = scene.addLine(QLineF(custRect1->center(), custRect2->center()));
    QGraphicsLineItem *rightLine = scene.addLine(QLineF(custRect2->center(), custRect3->center()));
    QGraphicsLineItem *bottomLine = scene.addLine(QLineF(custRect3->center(), custRect4->center()));
    QGraphicsLineItem *leftLine = scene.addLine(QLineF(custRect4->center(), custRect1->center()));

    custRect1->addLine(leftLine, topLine);
    custRect2->addLine(topLine, rightLine);

    custRect3->addLine(rightLine, bottomLine);
    custRect4->addLine(bottomLine, leftLine);

    QGraphicsView view(&scene);
    view.show();

    return a.exec();
}

compiling the code above you should end up with a small window containing just the QGraphivsView, a Polygon and four Handles attached to the Polygons vertices. Now move a Handle using your mouse. Move it again. Everything should behave as you would expect it from any graphical editor whatsoever. Next, comment-in the Code-Block that reimplements some Mouse Events to basically just change the cursor when hovering over and dragging the handles. Recompile and start again to move a Handle. Moving the very same handle again and... see what happenes! WTF? it jumps back to its initial Position. Does anybody know how to explain this behaviour and how to work around it?

Thanks!


Solution

  • Make sure to call the base class event after your implementation to ensure that the normal functionality remains.

    virtual void hoverEnterEvent(QGraphicsSceneHoverEvent *event) {
        this->setCursor(Qt::SizeAllCursor);
        //
        QGraphicsRectItem::hoverEnterEvent( event );
    }
    virtual void hoverLeaveEvent(QGraphicsSceneHoverEvent *event) {
        this->unsetCursor();
        //
        QGraphicsRectItem::hoverLeaveEvent( event );
    
    }
    virtual void mousePressEvent(QGraphicsSceneMouseEvent *event) {
        this->setCursor(Qt::BlankCursor);
        //
        QGraphicsRectItem::mousePressEvent( event );
    }
    virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) {
        this->unsetCursor();
        this->data(0).toString();
        if (this->isUnderMouse()) {
               this->setCursor(Qt::SizeAllCursor);
        }
        //
        QGraphicsRectItem::mouseReleaseEvent( event );
    }