How to Draw a Point (On Mouseclick) on a Qgraphicsscene

How to draw a point (on mouseclick) on a QGraphicsScene?

UPDATE: There is a new class called QGraphicsSceneMouseEvent that makes this a little easier.
I just finished an example using it here:

https://stackoverflow.com/a/26903599/999943

It differs with the answer below in that it subclasses QGraphicsScene, not QGraphicsView, and it uses mouseEvent->scenePos() so there isn't a need to manually map coordinates.


You are on the right track, but you still have a little more to go.

You need to subclass QGraphicsView to be able to do something with mouse presses or with mouse releases using QMouseEvent.

    #include <QGraphicsView>
#include <QGraphicsScene>
#include <QGraphicsEllipseItem>
#include <QMouseEvent>

class MyQGraphicsView : public QGraphicsView
{
Q_OBJECT
public:
explicit MyQGraphicsView(QWidget *parent = 0);

signals:

public slots:
void mousePressEvent(QMouseEvent * e);
// void mouseReleaseEvent(QMouseEvent * e);
// void mouseDoubleClickEvent(QMouseEvent * e);
// void mouseMoveEvent(QMouseEvent * e);
private:
QGraphicsScene * scene;
};

QGraphicsView doesn't natively have dimension-less points. You will probably want to use QGraphicsEllipse item or simply, scene->addEllipseItem() with a very small radius.

    #include "myqgraphicsview.h"
#include <QPointF>

MyQGraphicsView::MyQGraphicsView(QWidget *parent) :
QGraphicsView(parent)
{
scene = new QGraphicsScene();
this->setSceneRect(50, 50, 350, 350);
this->setScene(scene);
}

void MyQGraphicsView::mousePressEvent(QMouseEvent * e)
{
double rad = 1;
QPointF pt = mapToScene(e->pos());
scene->addEllipse(pt.x()-rad, pt.y()-rad, rad*2.0, rad*2.0,
QPen(), QBrush(Qt::SolidPattern));
}

Note the usage of mapToScene() to make the pos() of the event map correctly to where the mouse is clicked on the scene.

You need to add an instance of your subclassed QGraphicsView to the centralWidget's layout of your ui if you are going to use a form.

    QGridLayout * gridLayout = new QGridLayout(ui->centralWidget);
gridLayout->addWidget( new MyQGraphicsView() );

or if your ui has a layout already it will look like this:

    ui->centralWidget->layout()->addWidget( new MyGraphicsView() );

If you don't use a QMainWindow and a form, you can add it to a QWidget if you set a layout for it and then add your QGraphicsView to that layout in a similar manner. If you don't want a margin around your QGraphicsView, just call show on it and don't put it inside a different layout.

    #include <QtGui/QApplication>
#include "myqgraphicsview.h"

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

MyQGraphicsView view;
view.show();

return a.exec();
}

And that's it. Now you are dangerous with QGraphicsView's and their interaction with the mouse.

Be sure to read and study about Qt's Graphics View Framework and the related examples to be effective when using QGraphicsView and QGraphicsScene. They are very powerful tools for 2D graphics and can have a bit of a learning curve but they are worth it.

How to select a position and add an item in a QGraphicsView by mouse click?

There are few ways to do it:

  • Reimplement mousePressEvent(QMouseEvent*) (so, you need to implement subclass of QGraphicsView),

  • Call installEventFilter(QObject *) for QGraphicsView and implement bool eventFilter(QObject *, QEvent *) to catch all events (and process only QEvent::MouseButtonPress inside this function). In this case you do not need to implement subclass of QGraphicsView.

See also: Click event for QGraphicsView Qt and How to draw a point (on mouseclick) on a QGraphicsScene

QGraphicsView/Scene - items are drawn 2x away from mouse click

QGraphicsView and QGraphicsScene handle different coordinate systems, in the case of boundingRect() and paint() methods they must do in local coordinates with respect to the item and you should not use the pos() method since that refers to coordinates with respect to the scene.

void NodeItem::paint(QPainter *painter, const QStyleOptionGraphicsItem * /*option*/, QWidget * /*widget*/){
painter->drawEllipse(boundingRect());
}

QRectF NodeItem::boundingRect() const{
return QRectF(QPointF(-15, -15), QSizeF(30,30));
}

How to draw on QGraphicScene in real time?

If anyone will have trouble with something similar, here's my implementation:

DrawArea::DrawArea(QObject *parent) : QGraphicsScene(parent), mRadius(2), mDrawPath{}
{
drawing = false; // drawing is boolean type attribute
}

void DrawArea::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
if(event->button() & Qt::LeftButton)
{
mDrawPath.moveTo(event->scenePos());
event->accept();
drawing = true;
}
else event->ignore();
}

void DrawArea::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
if(event->button() & Qt::LeftButton)
{
drawing = false;
}
}

void DrawArea::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
if(drawing == false){}
else
{
mDrawPath.lineTo(event->scenePos());

this->clear();
addPath(mDrawPath);
}
}

It's worth to mention that this->clear() method in mouseMoveEvent method is a must-have. Without that performance will be great on beginning but during process of drawing it'll drop fast because you will constantly adding new paths which is performance killer.

PyQt5 - How to draw a dot on mouse click position?

You should only draw within the paintEvent method, and this paint does not save memory so if you want to graph several points you must store them in some container, for example using QPolygon.

paintEvent() is called every time you call update() or repaint(), for example it is called every time it is resized, the window is moved, etc.

import sys
from PyQt5 import QtWidgets, QtGui, QtCore, uic

class GUI(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
uic.loadUi('gui.ui', self)
self.setFixedSize(self.size())
self.show()
self.points = QtGui.QPolygon()

def mousePressEvent(self, e):
self.points << e.pos()
self.update()

def paintEvent(self, ev):
qp = QtGui.QPainter(self)
qp.setRenderHint(QtGui.QPainter.Antialiasing)
pen = QtGui.QPen(QtCore.Qt.red, 5)
brush = QtGui.QBrush(QtCore.Qt.red)
qp.setPen(pen)
qp.setBrush(brush)
for i in range(self.points.count()):
qp.drawEllipse(self.points.point(i), 5, 5)
# or
# qp.drawPoints(self.points)

if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
window = GUI()
sys.exit(app.exec_())

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

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.

Arc in QGraphicsScene

A Subclass of QGraphicsItem, that takes 3 points, and intersects the three with an arc of a circle. The second point is always in the middle. (Selectablity and other properties haven't been fully implemented or tested).

Note: Qt Creator includes more advanced examples of subclassed QGraphicsItems such as Colliding Mice, and 40,000 chips examples.

http://qt-project.org/doc/qt-5/examples-graphicsview.html

Also to enable QObject signals and slots and properties from a QGraphicsItem, you should use QGraphicsObject.

Note: added onto github here.

arcgraphicsitem.h

#ifndef ARCGRAPHICSITEM_H
#define ARCGRAPHICSITEM_H

#include <QGraphicsItem>
#include <QLineF>
#include <QPointF>
#include <QPainter>
#include <QStyleOptionGraphicsItem>
#include <QWidget>

class ArcGraphicsItem : public QGraphicsItem
{

public:
ArcGraphicsItem();
ArcGraphicsItem(int i, QPointF point0, QPointF point1, QPointF point2);
~ArcGraphicsItem();

QRectF boundingRect() const;
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);

int type() const { return Type;}
int id() {return m_id;}

QPainterPath shape() const;
protected:

private:
void init();

enum { Type = UserType + 6 };
int m_id;

QPointF startP, midP, endP, p1, p2, p3, center;
QLineF lineBC;
QLineF lineAC;
QLineF lineBA;
QLineF lineOA;
QLineF lineOB;
QLineF lineOC;
QLineF bisectorBC;
QLineF bisectorBA;
qreal startAngle;
qreal spanAngle;

QRectF circle;
QRectF boundingRectTemp;
qreal rad;
};

#endif // ARCGRAPHICSITEM_H

arcgraphicsitem.cpp

#include "arcgraphicsitem.h"
#include "qmath.h"
#include <QPen>
#include <QDebug>
#include <QPainterPath>

ArcGraphicsItem::ArcGraphicsItem(int i,
QPointF point1,
QPointF point2,
QPointF point3)
: m_id(i), p1(point1), p2(point2), p3(point3)

{
init();
}

ArcGraphicsItem::ArcGraphicsItem()
{
p1 = QPointF(0,0);
p2 = QPointF(0,1);
p3 = QPointF(1,1);
m_id = -1;
init();
}

ArcGraphicsItem::~ArcGraphicsItem()
{

}

void ArcGraphicsItem::init()
{
lineBC = QLineF(p2, p3);
lineAC = QLineF(p1, p3);
lineBA = QLineF(p2, p1);

rad = qAbs(lineBC.length()/(2*qSin(qDegreesToRadians(lineAC.angleTo(lineBA)))));

bisectorBC = QLineF(lineBC.pointAt(0.5), lineBC.p2());
bisectorBC.setAngle(lineBC.normalVector().angle());

bisectorBA = QLineF(lineBA.pointAt(0.5), lineBA.p2());
bisectorBA.setAngle(lineBA.normalVector().angle());
bisectorBA.intersect(bisectorBC, ¢er);

circle = QRectF(center.x() - rad, center.y() - rad, rad*2, rad*2);

lineOA = QLineF(center, p1);
lineOB = QLineF(center, p2);
lineOC = QLineF(center, p3);

startAngle = lineOA.angle();
spanAngle = lineOA.angleTo(lineOC);
// Make sure that the span angle covers all three points with the second point in the middle
if(qAbs(spanAngle) < qAbs(lineOA.angleTo(lineOB)) || qAbs(spanAngle) < qAbs(lineOB.angleTo(lineOC)))
{
// swap the end point and invert the spanAngle
startAngle = lineOC.angle();
spanAngle = 360 - spanAngle;
}

int w = 10;
boundingRectTemp = circle.adjusted(-w, -w, w, w);
}

QRectF ArcGraphicsItem::boundingRect() const
{
// outer most edges
return boundingRectTemp;
}

void ArcGraphicsItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
QPen paintpen;
painter->setRenderHint(QPainter::Antialiasing);
paintpen.setWidth(1);
// Draw arc

if(isSelected())
{
painter->setBrush(Qt::SolidPattern);
paintpen.setColor(Qt::black);
painter->setPen(paintpen);
}
else
{
paintpen.setStyle(Qt::DashLine);
paintpen.setColor(Qt::black);
painter->setPen(paintpen);
}

QPainterPath path;
path.arcMoveTo(circle,startAngle);
path.arcTo(circle, startAngle, spanAngle);

// Draw points

if (isSelected())
{
// sets brush for end points
painter->setBrush(Qt::SolidPattern);
paintpen.setColor(Qt::red);
painter->setPen(paintpen);

qreal ptRad = 10;
painter->drawEllipse(p1, ptRad, ptRad);
painter->drawEllipse(p2, ptRad, ptRad);
painter->drawEllipse(p3, ptRad, ptRad);
}

painter->setBrush(Qt::NoBrush);
painter->drawPath(path);
}

QPainterPath ArcGraphicsItem::shape() const
{
QPainterPath path;
path.arcMoveTo(circle,startAngle);
path.arcTo(circle, startAngle, spanAngle);
return path;
}

Hope that helps

Correct way to draw a QGraphicsItem on QGraphicsScene

You are combining the boundingRect coordinates that are relative to the item with the coordinates relative to the scene. On the other hand, don't complicate creating a custom item, instead use a custom QGraphicsRectItem. Finally it is recommended that you establish a sceneRect.

component.h

#ifndef COMPONENT_H
#define COMPONENT_H

#include <QGraphicsRectItem>

class Component : public QGraphicsRectItem
{
public:
Component(unsigned int id, QString cname, QString ctype, QGraphicsItem *parent=nullptr);
private:
unsigned int m_id;
QString m_cname;
QString m_ctype;
};

#endif // COMPONENT_H

component.cpp

#include "component.h"

Component::Component(unsigned int id, QString cname, QString ctype, QGraphicsItem*parent):
QGraphicsRectItem(parent), m_id(id), m_cname(cname), m_ctype(ctype)
{
setRect(-40, -40, 80, 80);
setFlag(ItemIsMovable);
}
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);

ui->graphView->setContextMenuPolicy(Qt::CustomContextMenu);
scene = new QGraphicsScene();
ui->graphView->setScene(scene);
ui->graphView->setSceneRect(QRect(0, 0, 400, 400));
}

MainWindow::~MainWindow()
{
delete ui;
}

void MainWindow::on_graphView_customContextMenuRequested(const QPoint &pos)
{

QPointF pp = ui->graphView->mapToScene(pos);
Component* component = new Component(s, n, t);
scene->addItem(component);
component->setPos(pp);
}

Map mouse coordinates to QGraphicsScene coordinates

Use QGraphicsView::mapToScene to convert the mouse position to the coordinates system of your scene.



Related Topics



Leave a reply



Submit