qt get mouse clicked position relative to image in a graphics view

7.8k views Asked by At

I want to allow user to draw lines on image using the mouse, I'm using graphics view and mouse event but the position I get is not the right position relative to the image here is the mouse event function

void InitializationDialog::mousePressEvent(QMouseEvent *event) {
    if(drawing) {
        points.append(event->pos());
        if(!points.isEmpty()) {
            if(points.size()==1) {
                QString fileName = list.at(choosed);
                QImage image(fileName);
                QGraphicsScene* scene = new QGraphicsScene();
                QGraphicsPixmapItem* item = new QGraphicsPixmapItem(QPixmap::fromImage(image));
                scene->addItem(item);
                QColor color(255,0,0);
                QBrush brush(color);
                QPen pen(brush, 4);
                QGraphicsLineItem* line = new QGraphicsLineItem(points.at(0).x(),points.at(0).y(),points.at(0).x()+1,points.at(0).y()+1);
                line->setPen(pen);
                scene->addItem(line);

                ui->graphicsView->setScene(scene);
                return;
            }
        }
    }
}

That should draw a point (I'm replacing it with one-pixel long line to look like a point)

Now I get the red point far away the mouse click like shown in that image enter image description here

How can I make it exactly on the mouse cursor?

Edit:

I made a custom class for graphics scene to get the click relative to it, I tried overriding mouse pressed for graphics view but the scene is not the same size as graphics view and the point still away the mouse click

overriding mouse pressed in my custom scene didn't help much because I always get 0,0 position for clicking the scene no matter where I click

new files code:

header file

#ifndef INITGRAPH_H
#define INITGRAPH_H
#include <QtGui>
#include <QGraphicsView>
#include <QGraphicsScene>
#include <QPoint>
#include <QGraphicsSceneMouseEvent>
class InitGraph : public QGraphicsView {
    Q_OBJECT
public:
    InitGraph(QWidget *parent = 0);
    virtual ~InitGraph() {};
};

class CustomScene : public QGraphicsScene {
    Q_OBJECT
protected:
    void mousePressEvent(QGraphicsSceneMouseEvent *event);
signals:
    void pressed(QPoint p);
};
#endif // INITGRAPH_H

the source file

#include "initgraph.h"
InitGraph::InitGraph(QWidget *parent):QGraphicsView(parent)
{
}
void CustomScene::mousePressEvent(QGraphicsSceneMouseEvent *event){
    qDebug(QString::number(event->pos().rx()).toLatin1());
    QPoint p = event->pos().toPoint();
    emit pressed(p);
}
3

There are 3 answers

1
eyllanesc On BEST ANSWER

If you want to add a QGraphicsLineItem you must use the system coordinates of the scene for this you must use the function the scenePos() method of QGraphicsSceneMouseEvent and the method mapFromScene() of the items.

for this we must override the methods mousePressEvent, mouseMoveEvent and mouseReleaseEvent of QGraphicsScene, all of the above I implemented it in the following class:

class CustomScene : public QGraphicsScene
{
    Q_OBJECT
    QGraphicsLineItem *item;
protected:
    void mousePressEvent(QGraphicsSceneMouseEvent *event){

        item = new QGraphicsLineItem;
        addItem(item);
        const QPointF p = event->scenePos();

        item->setPos(p);
    }

    void mouseMoveEvent(QGraphicsSceneMouseEvent *event){
        const QPointF p  =item->mapFromScene(event->scenePos());
        QLineF l = item->line();
        l.setP2(p);
        item->setLine(l);
    }

    void mouseReleaseEvent(QGraphicsSceneMouseEvent *event){
        const QPointF p  =item->mapFromScene(event->scenePos());
        QLineF l = item->line();
        l.setP2(p);
        item->setLine(l);
    }

};

The complete code is on the following link.

1
Vova Shevchyk On

You are receiving position of click on InitializationDialog but not on your graphicsView, so you need to convert this position by minusing x and y of your graphicsView

QGraphicsLineItem* line = new QGraphicsLineItem(
                          points.at(0).x()-graphicsView->rect().x(), 
                          points.at(0).y()-graphicsView->rect().y(), 
                          points.at(0).x()+1, points.at(0).y()+1);
3
Антон Сергунов On

Since you are overloading mouse events of dialog you have the mouse position in dialog coordinates. It's better to use drawing widget events to let Qt make all the conversation for you and didn't filter dialog areas.

To make it really reusable you can implement the QGraphicsItem Drawable subclass and process mouse events there adding or editing children.

EDIT here is a working example for you qt-drawable-item-example

Briefly:

void DrawableItem::mousePressEvent(QGraphicsSceneMouseEvent *event) {
    m_activePath = new QGraphicsPathItem(this);
    m_activePath->setPen(QPen(Qt::GlobalColor(rand()%17+2), 2.0));
    m_activePath->setPath(QPainterPath(event->pos()));
}

void DrawableItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event) {
    if(!m_activePath) {
        QGraphicsItem::mouseMoveEvent(event);
        return;
    }

    auto path = m_activePath->path();
    path.lineTo(event->pos());
    m_activePath->setPath(path);
}

void DrawableItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) {
    if(!m_activePath) {
        QGraphicsItem::mouseReleaseEvent(event);
        return;
    }
    m_activePath = nullptr;
}