Search code examples
c++qtqt5qgraphicsscene

QGraphicsScene item is drawn at twice(x2) position


(warning crossposted on: https://forum.qt.io/topic/105158/qgraphicsscene-item-is-drawn-at-twice-x2-position)

In the following code I am creating a custom widget which has a subclass of QGraphicsScene embeded in it. My aim is to click and add a point to the scene. A point is my subclass of QGraphicsItem (GIPoint). I want to move that point around and later connect it to other points and make a spline path.

The problem I am facing is that the point is not drawn at where I click but at a place which is formed by doubling the mouse-event's scenePos() coordinates. So if I click at (100,100) the point is drawn at (200,200). I suspect that I have misunderstood the coordinate system despite reading the documentation.

The question How to add item in a QGraphicsScene? seems relevant but the proposed solution to transform the mouse-event's coordinates via mapToScene(event->pos()); actually doubles up the position (before it will print that it draws on same position but it would then be x2. Now it also prints it as x2).

So I am asking additionally to point me to some simple-to-digest advice on how the widgets placement works. btw. is QRectF GIPoint::boundingRect() const { return QRectF(pos().x(), pos().y(), 5, 5); correct regarding the (x,y) coordinates of the rectangle?

My example code follows:

/* use the following pro:
QT += widgets core gui

CONFIG += debug console
SOURCES = example.cpp
TARGET = example
*/
#include <QGraphicsItem>
#include <QPainter>
#include <QWidget>
#include <QRectF>
#include <QPointF>
#include <QGraphicsScene>
#include <QStyleOptionGraphicsItem>
#include <QGraphicsSceneMouseEvent>
#include <QKeyEvent>
#include <QGraphicsView>
#include <QApplication>
#include <QOpenGLWidget>
#include <QMainWindow>
#include <QGridLayout>
#include <QDebug>

class GIPoint : public QGraphicsItem{
public:
    GIPoint(QGraphicsItem * parent, const QPointF &position);
protected:
    QVariant itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant &value) override;
    QRectF boundingRect() const override;
    void paint(
        QPainter *painter,
        const QStyleOptionGraphicsItem *option,
        QWidget *widget
    );
};
class GraphicsSceneWidget : public QGraphicsScene {

public:
    explicit GraphicsSceneWidget(QObject *parent);
    ~GraphicsSceneWidget();
    virtual void mousePressEvent(QGraphicsSceneMouseEvent *event);
};

class VectorGraphicsWidget : public QWidget {
public:
    VectorGraphicsWidget(QWidget *parent);
    ~VectorGraphicsWidget();
private:
    GraphicsSceneWidget *myGraphicsSceneWidget;
};

// implementation
GIPoint::GIPoint(
    QGraphicsItem *parent,
    const QPointF &position
) : QGraphicsItem(parent) {
    setFlag(QGraphicsItem::ItemIsMovable, true);
    setFlag(QGraphicsItem::ItemIsSelectable, true);
    setPos(position);
    qWarning() << "GIPoint::GIPoint() : init at " << position;
}
QVariant GIPoint::itemChange(
    GraphicsItemChange change,
    const QVariant &value
){
    if (change == QGraphicsItem::ItemPositionChange) {
        qWarning("position changed");
    }
    return value;
}
QRectF GIPoint::boundingRect() const {
    return QRectF(pos().x(), pos().y(), 5, 5);
//  return QRectF(0,0, 5, 5);
}
void GIPoint::paint(
    QPainter *painter,
    const QStyleOptionGraphicsItem *option,
    QWidget *widget
){
    (void )option;
    (void )widget;
    QPointF xx = scenePos();
    QRectF rect = QRectF(xx.x(), xx.y(), 10, 10);
    qWarning() << "painting: scenePos " << scenePos() << ", rect " << rect;
    QBrush brush = QBrush(Qt::black, Qt::SolidPattern);
    //painter->fillRect(rect, brush);
    painter->drawRect(rect);
}

GraphicsSceneWidget::GraphicsSceneWidget(QObject *parent)
    : QGraphicsScene(parent)
{}
GraphicsSceneWidget::~GraphicsSceneWidget(){}
void GraphicsSceneWidget::mousePressEvent(
    QGraphicsSceneMouseEvent *event
){
    GIPoint *gip = new GIPoint(Q_NULLPTR, event->scenePos());
    addItem(gip);
    QGraphicsScene::mousePressEvent(event);
}

VectorGraphicsWidget::VectorGraphicsWidget(QWidget *parent) :
    QWidget(parent)
{
    myGraphicsSceneWidget = new GraphicsSceneWidget(this);
    QGraphicsView *view = new QGraphicsView(myGraphicsSceneWidget);
    myGraphicsSceneWidget->setSceneRect(QRectF(0, 0, 500, 500));
    QGridLayout *centralLayout = new QGridLayout;
    centralLayout->addWidget(view);
    setLayout(centralLayout);
    myGraphicsSceneWidget->addRect(
        QRectF(0, 0, 100, 100),
        QPen(Qt::black),
        QBrush(Qt::green)
    );
    view->show();
}
VectorGraphicsWidget::~VectorGraphicsWidget() {
    delete myGraphicsSceneWidget;
}
int main(int argc, char **argv){
    QApplication app(argc, argv);
    app.setApplicationName("test");
    app.setOrganizationName("myorg");
    app.setOrganizationDomain("myorg.com");

    QMainWindow *w = new QMainWindow();
    w->resize(500, 500);
    w->setCentralWidget(new VectorGraphicsWidget(Q_NULLPTR));
    w->show();

    return app.exec();
}

Solution

  • The problem is that the boundingRect() and the paint() methods is respect the coordinate system of the item, not the scene. So the solution is not to use scenePos() in both methods but 0, 0:

    QRectF GIPoint::boundingRect() const {
        return QRectF(0, 0, 10, 10);
    }
    void GIPoint::paint(
        QPainter *painter,
        const QStyleOptionGraphicsItem *option,
        QWidget *widget
    ){
        (void )option;
        (void )widget;
        QRectF rect = QRectF(0, 0, 10, 10);
        qWarning() << "painting: scenePos " << scenePos() << ", rect " << rect;
        QBrush brush = QBrush(Qt::black, Qt::SolidPattern);
        painter->fillRect(rect, brush);
        painter->drawRect(rect);
    }
    

    Although I would recommend using QGraphicsRectItem as it implements what you have done.