Composed QGraphicsItem not detecting clicks on sub-items - c++

I am trying to make a simple extension of QGraphicsRectItem that allows me to resize the rectangle and move it around with the mouse. I model the handles with elliptical arches on the corners I want to enable for dragging, which I implement as QGraphicsEllipseItems:
class QGraphicsBoxWithHandlesItem : public QObject, public QGraphicsRectItem
{
Q_OBJECT
typedef enum {
None,
BottomLeft,
TopRight
} ActiveAnchor;
private:
QGraphicsEllipseItem m_anchorBottomLeft;
QGraphicsEllipseItem m_anchorTopRight;
float m_anchorRadius;
ActiveAnchor m_activeAnchor;
public:
QGraphicsBoxWithHandlesItem(QRectF r, float handlesRadius = 20.0, QGraphicsItem *parent = nullptr);
void setAnchorRadius(float radius);
float getAnchorRadius();
QPainterPath shape() const;
protected:
void mousePressEvent(QGraphicsSceneMouseEvent * event);
void mouseMoveEvent(QGraphicsSceneMouseEvent * event);
void mouseReleaseEvent(QGraphicsSceneMouseEvent * event);
};
I want to be able to detect clicks both on the rectangle and on the handle items (this is necessary because if the rectangle gets too small the handles are the only easily clickable area), so I thought I'd extend QGraphicsRectItem::shape() to add to the returned QPainterPath the paths of the sub-items (adjusting coordinates to make them relative to the parent item):
QPainterPath QGraphicsBoxWithHandlesItem::shape() const
{
auto curShape = QGraphicsRectItem::shape();
curShape.addPath( mapFromItem(&m_anchorBottomLeft, m_anchorBottomLeft.shape()) );
curShape.addPath( mapFromItem(&m_anchorTopRight, m_anchorTopRight.shape()) );
return curShape;
}
What I get, however, is that now clicks inside the handles areas are completely ignored and only clicks in the central area of the rectangle are handled.
What is the correct way to extend the clickable area of an item when it has a non-trivial shape?
Update: I tried to set the ItemIsSelectable flag on a handle and now, if I click on it, I see that it gets selected. I still don't get any mousePressEvent in the parent, however. What am I doing wrong?
Edit:
This is the constructor implementation:
QGraphicsBoxWithHandlesItem::QGraphicsBoxWithHandlesItem( QRectF r, float handlesRadius, QGraphicsItem * parent) :
QGraphicsRectItem(parent),
m_anchorRadius(handlesRadius),
m_activeAnchor(None)
{
setFlag(QGraphicsItem::ItemIsMovable);
setFlag(QGraphicsItem::ItemIsSelectable);
setRect(r);
m_anchorBottomLeft.setRect(-m_anchorRadius, -m_anchorRadius, m_anchorRadius*2, m_anchorRadius*2);
m_anchorBottomLeft.setPos(rect().bottomLeft());
m_anchorBottomLeft.setSpanAngle(90 * 16); // angle is in 16ths of degree
m_anchorBottomLeft.setParentItem(this);
m_anchorTopRight.setRect(-m_anchorRadius, -m_anchorRadius, m_anchorRadius*2, m_anchorRadius*2);
m_anchorTopRight.setPos(rect().topRight());
m_anchorTopRight.setStartAngle(180 * 16); // angle is in 16ths of degree
m_anchorTopRight.setSpanAngle(90 * 16); // angle is in 16ths of degree
m_anchorTopRight.setParentItem(this);
}

Your QGraphicsEllipseItem are on top of the base item so the mouse events will never come to you.
What you have to do is use a sceneEventFilter, but since the QGraphicsEllipseItem are children of the main item they will never move, so they should not have a parent but you should add them directly to the scene.
The full functionality is implemented in the following code:
*.h
#ifndef QGRAPHICSBOXWITHHANDLESITEM_H
#define QGRAPHICSBOXWITHHANDLESITEM_H
#include <QGraphicsRectItem>
#include <QObject>
class QGraphicsBoxWithHandlesItem : public QObject, public QGraphicsRectItem
{
Q_OBJECT
enum ActiveAnchor{
None,
BottomLeft,
TopRight
};
public:
QGraphicsBoxWithHandlesItem(QRectF r, float handlesRadius = 20.0, QGraphicsItem *parent = nullptr);
protected:
QVariant itemChange(GraphicsItemChange change, const QVariant &value);
bool sceneEventFilter(QGraphicsItem *watched, QEvent *event);
private:
QGraphicsEllipseItem m_anchorBottomLeft;
QGraphicsEllipseItem m_anchorTopRight;
float m_anchorRadius;
ActiveAnchor m_activeAnchor;
};
#endif // QGRAPHICSBOXWITHHANDLESITEM_H
*.cpp
#include "qgraphicsboxwithhandlesitem.h"
#include <QEvent>
#include <QGraphicsScene>
#include <QDebug>
QGraphicsBoxWithHandlesItem::QGraphicsBoxWithHandlesItem( QRectF r, float handlesRadius, QGraphicsItem * parent) :
QGraphicsRectItem(parent),
m_anchorRadius(handlesRadius),
m_activeAnchor(None)
{
setFlag(QGraphicsItem::ItemIsMovable);
setFlag(QGraphicsItem::ItemIsSelectable);
setFlag(QGraphicsItem::ItemSendsGeometryChanges);
setRect(r);
m_anchorBottomLeft.setRect(-m_anchorRadius, -m_anchorRadius, m_anchorRadius*2, m_anchorRadius*2);
m_anchorBottomLeft.setPos(rect().bottomLeft());
m_anchorBottomLeft.setSpanAngle(90 * 16); // angle is in 16ths of degree
//m_anchorBottomLeft.setParentItem(this);
m_anchorBottomLeft.setFlag(QGraphicsItem::ItemIsMovable);
m_anchorBottomLeft.setFlag(QGraphicsItem::ItemIsSelectable);
m_anchorTopRight.setRect(-m_anchorRadius, -m_anchorRadius, m_anchorRadius*2, m_anchorRadius*2);
m_anchorTopRight.setPos(rect().topRight());
m_anchorTopRight.setStartAngle(180 * 16); // angle is in 16ths of degree
m_anchorTopRight.setSpanAngle(90 * 16); // angle is in 16ths of degree
//m_anchorTopRight.setParentItem(this);
m_anchorTopRight.setFlag(QGraphicsItem::ItemIsMovable);
m_anchorTopRight.setFlag(QGraphicsItem::ItemIsSelectable);
}
QVariant QGraphicsBoxWithHandlesItem::itemChange(GraphicsItemChange change, const QVariant & value){
if(change == QGraphicsItem::ItemSceneHasChanged){
if(scene()){
scene()->addItem(&m_anchorBottomLeft);
scene()->addItem(&m_anchorTopRight);
m_anchorBottomLeft.installSceneEventFilter(this);
m_anchorTopRight.installSceneEventFilter(this);
}
}
else if (change == QGraphicsItem::ItemPositionHasChanged) {
m_anchorBottomLeft.setPos(mapToScene(rect().bottomLeft()));
m_anchorTopRight.setPos(mapToScene(rect().topRight()));
}
return QGraphicsRectItem::itemChange(change, value);
}
bool QGraphicsBoxWithHandlesItem::sceneEventFilter(QGraphicsItem *watched, QEvent *event)
{
if(watched == &m_anchorTopRight){
switch (event->type()) {
case QEvent::GraphicsSceneMousePress:{
//mousePressEvent
qDebug()<<"mousePressEvent m_anchorTopRight";
break;
}
case QEvent::GraphicsSceneMouseMove:{
// mouseMoveEvent
QRectF r = rect();
auto p = m_anchorTopRight.mapToScene(m_anchorTopRight.rect().center());
r.setTopRight(mapFromScene(p));
setRect(r);
qDebug()<<"mouseMoveEvent m_anchorTopRight";
break;
}
case QEvent::GraphicsSceneMouseRelease :{
//mouseReleaseEvent
qDebug()<<"mouseReleaseEvent m_anchorTopRight";
break;
}
}
}
if(watched == &m_anchorBottomLeft){
switch (event->type()) {
case QEvent::GraphicsSceneMousePress:{
//mousePressEvent
qDebug()<<"mousePressEvent m_anchorBottomLeft";
break;
}
case QEvent::GraphicsSceneMouseMove:{
// mouseMoveEvent
QRectF r = rect();
auto p = m_anchorBottomLeft.mapToScene(m_anchorBottomLeft.rect().center());
r.setBottomLeft(mapFromScene(p));
setRect(r);
qDebug()<<"mouseMoveEvent m_anchorBottomLeft";
break;
}
case QEvent::GraphicsSceneMouseRelease :{
//mouseReleaseEvent
qDebug()<<"mouseReleaseEvent m_anchorBottomLeft";
break;
}
}
}
return QGraphicsRectItem::sceneEventFilter(watched, event);
}

Related

QGraphicsItemGroup and childs resize

I made an algorithm for resizing a picture(inherited from QGraphicsItem) using vector mathematics (I added points to the corners and using the mouse, the picture is resized while maintaining the aspect ratio QGraphicsItem resize with mouse position and keeping aspect ratio , gif: https://gph.is/g/Zr0WdxJ).
Next, I created a group(inherited from QGraphicsItemGroup) add border dots(inherited from QGraphicsRectItem) and added pictures to the group (via addToGroup).
Is it possible to generalize this algorithm to a group? So that all pictures in the group are resized with border dot position.
this what I want: https://gph.is/g/EJxpeVQ (PureRef app)
and this is what I got: https://gph.is/g/aQnpq5x
here the project if anybody wants to run application: https://github.com/try-hard-factory/familiar/tree/feature/itemgroup-resize
can't resize childs... here the code of some classes:
borderdot.h :
#ifndef BORDERDOT_H
#define BORDERDOT_H
#include <QObject>
#include <QGraphicsRectItem>
class QGraphicsSceneHoverEventPrivate;
class QGraphicsSceneMouseEvent;
class DotSignal : public QObject, public QGraphicsRectItem
{
Q_OBJECT
Q_PROPERTY(QPointF previousPosition READ previousPosition WRITE setPreviousPosition NOTIFY previousPositionChanged)
public:
explicit DotSignal(QGraphicsItem *parentItem = 0, QObject *parent = 0);
explicit DotSignal(QPointF pos, QGraphicsItem *parentItem = 0, QObject *parent = 0);
~DotSignal();
enum Flags {
Movable = 0x01
};
enum { Type = UserType + 1 };
int type() const override
{
return Type;
}
QPointF previousPosition() const;
void setPreviousPosition(const QPointF previousPosition);
void setDotFlags(unsigned int flags);
signals:
void previousPositionChanged();
void signalMouseRelease();
void signalMove(QGraphicsItem *signalOwner, qreal dx, qreal dy);
protected:
void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override;
void mousePressEvent(QGraphicsSceneMouseEvent *event) override;
void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override;
void hoverEnterEvent(QGraphicsSceneHoverEvent *event) override;
void hoverLeaveEvent(QGraphicsSceneHoverEvent *event) override;
public slots:
private:
unsigned int m_flags;
QPointF m_previousPosition;
};
borderdot.cpp:
#include "borderdot.h"
#include <QBrush>
#include <QColor>
#include <QGraphicsSceneHoverEvent>
#include <QGraphicsSceneMouseEvent>
DotSignal::DotSignal(QGraphicsItem *parentItem, QObject *parent) :
QObject(parent)
{
setZValue(999999999);
// setFlags(ItemIsMovable);
setParentItem(parentItem);
setAcceptHoverEvents(true);
setBrush(QBrush(Qt::black));
setRect(-4,-4,8,8);
setDotFlags(0);
}
DotSignal::DotSignal(QPointF pos, QGraphicsItem *parentItem, QObject *parent) :
QObject(parent)
{
setZValue(999999999);
// setFlags(ItemIsMovable);
setParentItem(parentItem);
setAcceptHoverEvents(true);
setBrush(QBrush(Qt::black));
setRect(-4,-4,8,8);
setPos(pos);
setPreviousPosition(pos);
setDotFlags(0);
}
DotSignal::~DotSignal()
{
}
QPointF DotSignal::previousPosition() const
{
return m_previousPosition;
}
void DotSignal::setPreviousPosition(const QPointF previousPosition)
{
if (m_previousPosition == previousPosition)
return;
m_previousPosition = previousPosition;
emit previousPositionChanged();
}
void DotSignal::setDotFlags(unsigned int flags)
{
m_flags = flags;
}
void DotSignal::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
if(m_flags & Movable) {
qDebug()<<"DotSignal::mouseMoveEvent";
auto dx = event->scenePos().x() - m_previousPosition.x();
auto dy = event->scenePos().y() - m_previousPosition.y();
moveBy(dx,dy);
setPreviousPosition(event->scenePos());
emit signalMove(this, dx, dy);
} else {
qDebug()<<"else DotSignal::mouseMoveEvent";
QGraphicsItem::mouseMoveEvent(event);
}
}
void DotSignal::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
if(m_flags & Movable){
setPreviousPosition(event->scenePos());
} else {
QGraphicsItem::mousePressEvent(event);
}
}
void DotSignal::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
emit signalMouseRelease();
QGraphicsItem::mouseReleaseEvent(event);
}
void DotSignal::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
{
qDebug()<<"DotSignal::hoverEnterEvent";
Q_UNUSED(event)
setBrush(QBrush(Qt::red));
}
void DotSignal::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
{
qDebug()<<"DotSignal::hoverLeaveEvent";
Q_UNUSED(event)
setBrush(QBrush(Qt::black));
}
itemgroup.h:
#ifndef ITEMGROUP_H
#define ITEMGROUP_H
#include <QObject>
#include <QGraphicsItemGroup>
class DotSignal;
class QGraphicsSceneMouseEvent;
class ItemGroup : public QObject, public QGraphicsItemGroup
{
Q_OBJECT
Q_INTERFACES(QGraphicsItem)
Q_PROPERTY(QPointF previousPosition READ previousPosition WRITE setPreviousPosition NOTIFY previousPositionChanged)
public:
enum EItemsType {
eBorderDot = QGraphicsItem::UserType + 1,
};
ItemGroup(uint64_t& zc, QGraphicsItemGroup *parent = nullptr);
~ItemGroup();
enum ActionStates {
ResizeState = 0x01,
RotationState = 0x02
};
enum CornerFlags {
Top = 0x01,
Bottom = 0x02,
Left = 0x04,
Right = 0x08,
TopLeft = Top|Left,
TopRight = Top|Right,
BottomLeft = Bottom|Left,
BottomRight = Bottom|Right
};
enum CornerGrabbers {
GrabberTop = 0,
GrabberBottom,
GrabberLeft,
GrabberRight,
GrabberTopLeft,
GrabberTopRight,
GrabberBottomLeft,
GrabberBottomRight
};
public:
void addItem(QGraphicsItem* item);
void printChilds();
QPointF previousPosition() const;
void setPreviousPosition(const QPointF previousPosition);
signals:
void rectChanged(ItemGroup *rect);
void previousPositionChanged();
void clicked(ItemGroup *rect);
void signalMove(QGraphicsItemGroup *item, qreal dx, qreal dy);
protected:
void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override;
void mousePressEvent(QGraphicsSceneMouseEvent *event) override;
void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override;
void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) override;
void hoverEnterEvent(QGraphicsSceneHoverEvent *event) override;
void hoverLeaveEvent(QGraphicsSceneHoverEvent *event) override;
void hoverMoveEvent(QGraphicsSceneHoverEvent *event) override;
QVariant itemChange(GraphicsItemChange change, const QVariant &value) override;
public:
void clearItemGroup();
bool isContain(const QGraphicsItem* item) const;
bool isEmpty() const;
void incZ();
protected:
QRectF boundingRect() const override;
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
private:
QPointF shiftMouseCoords_;
uint64_t& zCounter_;
QRectF m_tmpRect;
private:
unsigned int m_cornerFlags;
unsigned int m_actionFlags;
QPointF m_previousPosition;
bool m_leftMouseButtonPressed;
DotSignal *cornerGrabber[8];
void resizeLeft( const QPointF &pt);
void resizeRight( const QPointF &pt);
void resizeBottom(const QPointF &pt);
void resizeTop(const QPointF &pt);
void rotateItem(const QPointF &pt);
void setPositionGrabbers();
void setVisibilityGrabbers();
void hideGrabbers();
};
#endif // ITEMGROUP_H
itemgroup.cpp:
#include <QPainter>
#include <QDebug>
#include <QCursor>
#include <QGraphicsScene>
#include <QGraphicsSceneMouseEvent>
#include <QGraphicsRectItem>
#include <math.h>
#include "borderdot.h"
static const double Pi = 3.14159265358979323846264338327950288419717;
static double TwoPi = 2.0 * Pi;
ItemGroup::~ItemGroup()
{
for(int i = 0; i < 8; i++){
delete cornerGrabber[i];
}
}
QPointF ItemGroup::previousPosition() const
{
return m_previousPosition;
}
void ItemGroup::setPreviousPosition(const QPointF previousPosition)
{
if (m_previousPosition == previousPosition)
return;
m_previousPosition = previousPosition;
emit previousPositionChanged();
}
ItemGroup::ItemGroup(uint64_t& zc, QGraphicsItemGroup *parent) :
QGraphicsItemGroup(parent),
zCounter_(zc),
m_cornerFlags(0),
m_actionFlags(ResizeState)
{
setAcceptHoverEvents(true);
setFlags(ItemIsSelectable|ItemSendsGeometryChanges);
for(int i = 0; i < 8; i++){
cornerGrabber[i] = new DotSignal(this);
}
setPositionGrabbers();
}
void ItemGroup::addItem(QGraphicsItem* item)
{
addToGroup(item);
auto childs = childItems();
auto tmp = childs.first()->sceneBoundingRect();
for (auto& it : childs) {
if (it->type() == eBorderDot) continue;
tmp = tmp.united(it->sceneBoundingRect());
}
m_tmpRect = tmp;
}
void ItemGroup::printChilds()
{
auto childs = childItems();
for (auto& it : childs) {
LOG_DEBUG(logger, "CHILDREN: ", it);
}
}
QRectF ItemGroup::boundingRect() const
{
return m_tmpRect;
}
void ItemGroup::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
QPointF pt = event->pos();
if(m_actionFlags == ResizeState){
switch (m_cornerFlags) {
case Top:
resizeTop(pt);
break;
case Bottom:
resizeBottom(pt);
break;
case Left:
resizeLeft(pt);
break;
case Right:
resizeRight(pt);
break;
case TopLeft:
resizeTop(pt);
resizeLeft(pt);
break;
case TopRight:
resizeTop(pt);
resizeRight(pt);
break;
case BottomLeft:
resizeBottom(pt);
resizeLeft(pt);
break;
case BottomRight:
resizeBottom(pt);
resizeRight(pt);
break;
default:
if (m_leftMouseButtonPressed) {
setCursor(Qt::ClosedHandCursor);
auto dx = event->scenePos().x() - m_previousPosition.x();
auto dy = event->scenePos().y() - m_previousPosition.y();
moveBy(dx,dy);
setPreviousPosition(event->scenePos());
emit signalMove(this, dx, dy);
}
break;
}
} else {
if (m_leftMouseButtonPressed) {
setCursor(Qt::ClosedHandCursor);
auto dx = event->scenePos().x() - m_previousPosition.x();
auto dy = event->scenePos().y() - m_previousPosition.y();
moveBy(dx,dy);
setPreviousPosition(event->scenePos());
emit signalMove(this, dx, dy);
}
}
QGraphicsItemGroup::mouseMoveEvent(event);
}
void ItemGroup::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
setZValue(++zCounter_);
shiftMouseCoords_ = (this->pos() - mapToScene(event->pos()))/scale();
if (event->button() & Qt::LeftButton) {
m_leftMouseButtonPressed = true;
setPreviousPosition(event->scenePos());
emit clicked(this);
}
QGraphicsItemGroup::mousePressEvent(event);
LOG_DEBUG(logger, "EventPos: (", event->pos().x(),";",event->pos().y(), "), Pos: (", pos().x(),";",pos().y(),")");
}
void ItemGroup::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
if (event->button() & Qt::LeftButton) {
m_leftMouseButtonPressed = false;
}
QGraphicsItemGroup::mouseReleaseEvent(event);
}
void ItemGroup::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
{
qDebug()<<"ItemGroup::hoverEnterEvent";
setPositionGrabbers();
setVisibilityGrabbers();
QGraphicsItem::hoverEnterEvent(event);
}
void ItemGroup::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
{
qDebug()<<"ItemGroup::hoverLeaveEvent";
m_cornerFlags = 0;
hideGrabbers();
setCursor(Qt::CrossCursor);
QGraphicsItem::hoverLeaveEvent( event );
}
void ItemGroup::hoverMoveEvent(QGraphicsSceneHoverEvent *event)
{
QPointF pt = event->pos(); // The current position of the mouse
qreal drx = pt.x() - boundingRect().right(); // Distance between the mouse and the right
qreal dlx = pt.x() - boundingRect().left(); // Distance between the mouse and the left
qreal dby = pt.y() - boundingRect().top(); // Distance between the mouse and the top
qreal dty = pt.y() - boundingRect().bottom(); // Distance between the mouse and the bottom
m_cornerFlags = 0;
if( dby < 10 && dby > -10 ) m_cornerFlags |= Top; // Top side
if( dty < 10 && dty > -10 ) m_cornerFlags |= Bottom; // Bottom side
if( drx < 10 && drx > -10 ) m_cornerFlags |= Right; // Right side
if( dlx < 10 && dlx > -10 ) m_cornerFlags |= Left; // Left side
switch (m_cornerFlags) {
case TopLeft:
case TopRight:
case BottomLeft:
case BottomRight: {
setCursor(Qt::BusyCursor);
break;
}
default:
setCursor(Qt::CrossCursor);
break;
}
}
QVariant ItemGroup::itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant &value)
{
switch (change) {
case QGraphicsItemGroup::ItemSelectedChange:
m_actionFlags = ResizeState;
break;
default:
break;
}
return QGraphicsItemGroup::itemChange(change, value);
}
void ItemGroup::resizeRight(const QPointF &pt)
{
QRectF tmpRect = boundingRect();
if( pt.x() < tmpRect.left() )
return;
qreal widthOffset = ( pt.x() - tmpRect.left() );
if( widthOffset < 10 ) /// limit
return;
if( widthOffset < 10)
tmpRect.setWidth( -widthOffset );
else
tmpRect.setWidth( widthOffset );
prepareGeometryChange();
m_tmpRect = tmpRect;
update();
setPositionGrabbers();
}
void ItemGroup::resizeTop(const QPointF &pt)
{
QRectF tmpRect = boundingRect();
if( pt.y() > tmpRect.bottom() )
return;
qreal heightOffset = ( pt.y() - tmpRect.bottom() );
if( heightOffset > -11 ) /// limit
return;
if( heightOffset < 0)
tmpRect.setHeight( -heightOffset );
else
tmpRect.setHeight( heightOffset );
tmpRect.translate( 0 , boundingRect().height() - tmpRect.height() );
prepareGeometryChange();
m_tmpRect = tmpRect;
update();
setPositionGrabbers();
}
void ItemGroup::setPositionGrabbers()
{
QRectF tmpRect = boundingRect();
cornerGrabber[GrabberTop]->setPos(tmpRect.left() + tmpRect.width()/2, tmpRect.top());
cornerGrabber[GrabberBottom]->setPos(tmpRect.left() + tmpRect.width()/2, tmpRect.bottom());
cornerGrabber[GrabberLeft]->setPos(tmpRect.left(), tmpRect.top() + tmpRect.height()/2);
cornerGrabber[GrabberRight]->setPos(tmpRect.right(), tmpRect.top() + tmpRect.height()/2);
cornerGrabber[GrabberTopLeft]->setPos(tmpRect.topLeft().x(), tmpRect.topLeft().y());
cornerGrabber[GrabberTopRight]->setPos(tmpRect.topRight().x(), tmpRect.topRight().y());
cornerGrabber[GrabberBottomLeft]->setPos(tmpRect.bottomLeft().x(), tmpRect.bottomLeft().y());
cornerGrabber[GrabberBottomRight]->setPos(tmpRect.bottomRight().x(), tmpRect.bottomRight().y());
}
void ItemGroup::setVisibilityGrabbers()
{
cornerGrabber[GrabberTopLeft]->setVisible(true);
cornerGrabber[GrabberTopRight]->setVisible(true);
cornerGrabber[GrabberBottomLeft]->setVisible(true);
cornerGrabber[GrabberBottomRight]->setVisible(true);
cornerGrabber[GrabberTop]->setVisible(true);
cornerGrabber[GrabberBottom]->setVisible(true);
cornerGrabber[GrabberLeft]->setVisible(true);
cornerGrabber[GrabberRight]->setVisible(true);
}
void ItemGroup::hideGrabbers()
{
for(int i = 0; i < 8; i++){
cornerGrabber[i]->setVisible(false);
}
}
I didn't dig through your code, but yes, it can absolutely be done. The product I work in has a "group" concept, and I had to support this exact thing. I can't spell out code for you, but here's the general concept. The same concept works for resizing a related set of selected items that you want to resize all together.
I created a Resizer class that manages the handles and responds to those movements. The Resizer is instantiated when one or more objects is selected. All of the graphics item classes are my own, derived from QGraphicsItem; if you're using the built-in classes, you may need to subclass them and add your own functions.
Whenever the user moves a handle, the Resizer determines the new geometry of the selected items and updates their sizes. For rectangles and ellipses, this involves calls to setRect; for paths, you'll have to write a scaling algorithm that moves the points in the path to their new locations.
For a grouped object, the concept is identical. You look at the proportions of the member objects' areas to the group's encompassing rectangle, recalculate them, and then update them.
What I found to be the key is to save the starting encompassing rectangle, and then create a resize method that accepts the original encompassing rectangle and the new encompassing rectangle (defined by the handle movement), and then scale from the original to the new rectangle. You'll also need the original rectangle of the individual objects.
Off the top of my head, here's the general idea, and this is assuming you want the items to redraw themselves as you move the handles:
When user initiates first handle movement, grab the current geometry of all of the objects being resized, and the encompassing geometry for the overall group.
As the handle moves, call a new "resize" method with the original encompassing rectangle, and the new one defined by the handle positions, and then have each object resize itself by mapping its original area onto the new area, using the group's before/after encompassing rectangles to define the scaling.
When the movement stops, notify each object being resized that the operation is finished, and this is their new size. (This may not be necessary for you, but it was for me because the final object attributes have to be reflected back to a database.)
This takes some effort, but it's absolutely doable. The main things to think about is to abstract the operations and create your own QGraphicsitem-derived classes with methods to support it. If you're using the built-in classes, you can extend them with an interface class that ensures they all have the required methods your resizing code needs.

QGraphicsPathItem between two movable QGraphicsRectItem

I'm trying to follow this example and eyllanesc's answer here
Weird behaviour when dragging QGraphicsItem to draw a line ending with an arrow between two draggable/movable QGraphicsRectItems
Here is my code:
The customized QGraphicsPathItemclass (inherits from QObject also because I use signals):
wireArrow.h
public slots:
void changePosStart(QPointF newpos);
void changePosEnd(QPointF newpos);
private:
component *myStartItem;
component *myEndItem;
QPolygonF arrowHead;
QColor myColor = Qt::black;
WireArrow.cpp
WireArrow::WireArrow(component *startItem, component *endItem,
QGraphicsItem *parent )
: QGraphicsPathItem(parent), myStartItem(startItem), myEndItem(endItem)
{
connect(startItem,SIGNAL(componentPosChanged(QPointF)),this, SLOT(changePosStart(QPointF)) );
);
connect(endItem,SIGNAL(componentPosChanged(QPointF)),this,SLOT(changePosEnd(QPointF)) );;
this->setPos(myStartItem->pos());
setFlag(ItemIsSelectable);
setPen(QPen(myColor, 2, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
// QPainterPath path;
// path.quadTo(100,0,100,100);
QPainterPath rectPath;
rectPath.moveTo(0.0, 0.0);
rectPath.lineTo(myStartItem->pos().x()-myEndItem->pos().x() , myStartItem->pos().y()-myEndItem->pos().y());
this->setPath(rectPath);
}
void WireArrow::changePosStart(QPointF newpos)
{//#to-do: find how to update pos setpos + this->update maybe? }
void WireArrow::changePosEnd(QPointF newpos)
{//#to-do: find how to update the end pos}
The customized qgraphicsitem class (also inherits from QObject to emit signal on position update):
component::component(/*some irrelevant params*/QGraphicsItem*parent ):
QGraphicsRectItem(parent), //...init other params
{
setRect(-40, -40, 80, 80);
setFlag(ItemIsMovable);
setFlag(ItemIsSelectable);
setFlag(QGraphicsItem::ItemSendsScenePositionChanges);
}
QVariant component::itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant &value)
{
if (change == ItemPositionChange && scene()) {
qDebug() << "cmp:: event change on scene";
QPointF newPos = value.toPointF();
emit componentPosChanged(newPos);
}
return QGraphicsItem::itemChange(change, value);
}
main
// create two components out and in
WireArrow * wa = new WireArrow(out,in);
scene->addItem(wa);
I can create the rectangles (components) and move them just fine (thanks to the answer here , my problem is I can draw lines orignation from out but :
I can't draw from one component to another correctly.
I need to make them move automatically when dragging rectangles. In other examples, I saw they were treating the lines as an attribute of the
item, which is something I can't do here since the wire is for two
items and not one so I replaced that with signal/slot connection but
I still can't figure out how to update the position...
An image explaining what I'm trying to do
(squares are really made with qt, but the line is made in paint, the squares are movable ).
So in short: Just trying to make a line between two points(the position of the two squares) and the squares are movable so the line should adapt if one of the the 2 squares is moved.
What is required is very similar to what I implement in this answer so I will limit the code translated from python to c++:
component.h
#ifndef COMPONENT_H
#define COMPONENT_H
#include <QGraphicsRectItem>
class Arrow;
class Component : public QGraphicsRectItem
{
public:
Component(QGraphicsItem *parent = nullptr);
void addArrow(Arrow *arrow);
protected:
QVariant itemChange(GraphicsItemChange change, const QVariant &value);
private:
QVector<Arrow *> mArrows;
};
#endif // COMPONENT_H
component.cpp
#include "arrow.h"
#include "component.h"
Component::Component(QGraphicsItem *parent):
QGraphicsRectItem(parent)
{
setRect(-40, -40, 80, 80);
setFlags(QGraphicsItem::ItemIsMovable |
QGraphicsItem::ItemIsSelectable |
QGraphicsItem::ItemSendsGeometryChanges);
}
void Component::addArrow(Arrow *arrow)
{
mArrows << arrow;
}
QVariant Component::itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant &value)
{
if(change == QGraphicsItem::ItemPositionHasChanged){
for(Arrow * arrow: qAsConst(mArrows)){
arrow->adjust();
}
}
return QGraphicsRectItem::itemChange(change, value);
}
arrow.h
#ifndef ARROW_H
#define ARROW_H
#include <QGraphicsPathItem>
class Component;
class Arrow : public QGraphicsPathItem
{
public:
Arrow(Component *startItem, Component *endItem, QGraphicsItem *parent = nullptr);
void adjust();
private:
Component *mStartItem;
Component *mEndItem;
};
#endif // ARROW_H
arrow.cpp
#include "arrow.h"
#include "component.h"
#include <QPen>
Arrow::Arrow(Component *startItem, Component *endItem, QGraphicsItem *parent):
QGraphicsPathItem(parent), mStartItem(startItem), mEndItem(endItem)
{
mStartItem->addArrow(this);
mEndItem->addArrow(this);
setPen(QPen(QColor("red"), 2, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
adjust();
}
void Arrow::adjust()
{
prepareGeometryChange();
QPainterPath path;
path.moveTo(mStartItem->pos());
path.lineTo(mEndItem->pos());
setPath(path);
}
Component *comp1 = new Component;
Component *comp2 = new Component;
comp1->setPos(50, 50);
comp2->setPos(400, 400);
Arrow *arrow = new Arrow(comp1, comp2);
scene->addItem(comp1);
scene->addItem(comp2);
scene->addItem(arrow);

QGraphicsPolygonItem not updating QPolygonF co-ordinates when dragged

I have a custom version of QGraphicsPolygonItem which is defined here:
#ifndef CUSTOMGPOLYGON_H
#define CUSTOMGPOLYGON_H
#include <QObject>
#include <QGraphicsPolygonItem>
#include <string>
#include <QGraphicsSceneMouseEvent>
#include <QMenu>
#include <QGraphicsTextItem>
class CustomGPolygon : public QObject, public QGraphicsPolygonItem
{
Q_OBJECT
public:
CustomGPolygon(QPolygonF poly, QObject *parent);
~CustomGPolygon();
using QGraphicsPolygonItem::boundingRect;
using QGraphicsPolygonItem::paint;
void mousePressEvent(QGraphicsSceneMouseEvent *event);
void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
QGraphicsTextItem *className;
private slots:
void deletePolygon();
void copyPolygon();
signals:
void duplicatePoly(QPolygonF);
private:
QMenu menu;
};
#endif // CUSTOMGPOLYGON_H
This is the .cpp for my CustomGPolygon:
#include "customgpolygon.h"
#include <iostream>
CustomGPolygon::CustomGPolygon(QPolygonF poly, QObject *parent):QGraphicsPolygonItem(poly)
{
menu.addAction("Copy", this, SLOT(copyPolygon()));
menu.addAction("Delete", this, SLOT(deletePolygon()));
connect(this, SIGNAL(duplicatePoly(QPolygonF)), parent, SLOT(drawPolygon(QPolygonF)));
}
CustomGPolygon::~CustomGPolygon()
{}
void CustomGPolygon::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
if(event->buttons() & Qt::LeftButton)
{
QGraphicsItem::mouseMoveEvent(event);
QPolygonF poly = this->polygon();
QPointF edgePoint(0,0);
for(int i = 0; i<poly.size(); i++){
if(poly.at(i).x() > edgePoint.x() && poly.at(i).y() > edgePoint.y())
{
edgePoint.setX(poly.at(i).x());
edgePoint.setY(poly.at(i).y());
}
}
this->className->setPos(edgePoint);
}
}
void CustomGPolygon::deletePolygon()
{
delete this;
}
void CustomGPolygon::copyPolygon()
{
QPolygonF poly = this->polygon();
emit duplicatePoly(poly);
}
To Draw one of these polygons onto my QGraphicsScene, I use the following function in my mainwindow.cpp:
void MainWindow::drawPolygon(const QPolygonF &poly)
{
CustomGPolygon *objectPt = new CustomGPolygon(poly, this);
objectPt->setPen(pen);
objectPt->setFlag(QGraphicsItem::ItemIsMovable);
scene->addItem(objectPt);
objectPt->className = textItem;
map->drawing = false;
}
When I drag this drawn polygon I need the co-ordinates of the vectors within the boundingRect to update - which at the moment, they are not doing.
I have tried adding these flags to solve the problem:
objectPt->setFlag(QGraphicsItem::ItemSendsGeometryChanges);
objectPt->setFlag(QGraphicsItem::ItemSendsScenePositionChanges);
However the problem remained
The QPolygonF set in the item is not about the coordinates of the scene but about the coordinates of the item, so moving the item will not change the QPolygonF. It is similar to the position of our face: If roads move with respect to the world but not with respect to ourselves. So if you want to get the polygon with respect to the scene you will have to make a conversion using the mapToScene() method. On the other hand if you want to track the position of the item then you should not use mouseMoveEvent() but itemChange().
On the other hand your calculation of the point is incorrect, what you should compare is the distance based on some metric, for example the Euclidean distance, since for example with your logic if the polygon is in position with negative coordinates then the edgePoint will always be ( 0,0).
Considering the above, the solution is:
#include <QtWidgets>
class CustomGPolygon: public QObject, public QGraphicsPolygonItem{
Q_OBJECT
public:
CustomGPolygon(QPolygonF poly, QObject *parent=nullptr):
QObject(parent), QGraphicsPolygonItem(poly), className(nullptr){
setFlag(QGraphicsItem::ItemIsMovable);
setFlag(QGraphicsItem::ItemSendsGeometryChanges);
menu.addAction("Copy", this, &CustomGPolygon::copyPolygon);
menu.addAction("Delete", this, &CustomGPolygon::deletePolygon);
// if(parent)
// connect(this, &CustomGPolygon:: SIGNAL(duplicatePoly(QPolygonF)), parent, SLOT(drawPolygon(QPolygonF)));
}
~CustomGPolygon(){}
QGraphicsTextItem *getClassName() const{return className;}
void setClassName(QGraphicsTextItem *value){className = value;}
protected:
QVariant itemChange(GraphicsItemChange change, const QVariant &value){
if(change == GraphicsItemChange::ItemPositionChange && !polygon().isEmpty()){
QPolygonF p = mapToScene(polygon());
QPointF edgePoint = *std::max_element(p.begin(), p.end(),
[](const QPointF & x, const QPointF & y) -> bool
{
return QVector2D(x).length() > QVector2D(y).length();
});
if(className)
className->setPos(edgePoint);
}
return QGraphicsPolygonItem::itemChange(change, value);
}
private Q_SLOTS:
void deletePolygon(){delete this;}
void copyPolygon(){
QPolygonF poly = mapToScene(polygon());
Q_EMIT duplicatePoly(poly);
}
Q_SIGNALS:
void duplicatePoly(QPolygonF);
private:
QGraphicsTextItem *className;
QMenu menu;
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QGraphicsScene scene;
QGraphicsView view(&scene);
QPolygonF poly;
poly << QPointF(0, 0) << QPointF(100, 0) << QPointF(100, 100);
CustomGPolygon *item = new CustomGPolygon(poly);
QGraphicsTextItem *textItem = new QGraphicsTextItem("Stack Overflow");
scene.addItem(textItem);
scene.addItem(item);
item->setClassName(textItem);
view.show();
view.resize(640, 480);
return a.exec();
}
#include "main.moc"

How to keep the size and position of QGraphicsItem when scaling the view?

I have some QGraphicsItems in the QGraphicsScene which should keep the same size and position when scaling. I've tried QGraphicsItem::ItemIgnoresTransformations but it turns out that the items get wrong positions. Below is a sample code:
I have subclassed QGraphicsView like this:
class Graphics : public QGraphicsView
{
public:
Graphics();
QGraphicsScene *scene;
QGraphicsRectItem *rect;
QGraphicsRectItem *rect2;
protected:
void wheelEvent(QWheelEvent *event);
};
And in its constructor:
Graphics::Graphics()
{
scene = new QGraphicsScene;
rect = new QGraphicsRectItem(100,100,50,50);
rect2 = new QGraphicsRectItem(-100,-100,50,50);
scene->addLine(0,200,200,0);
rect->setFlag(QGraphicsItem::ItemIgnoresTransformations, true);
scene->addItem(rect);
scene->addItem(rect2);
setScene(scene);
scene->addRect(scene->itemsBoundingRect());
}
The wheelEvent virtual function:
void Graphics::wheelEvent(QWheelEvent *event)
{
if(event->delta() < 0)
scale(1.0/2.0, 1.0/2.0);
else
scale(2, 2);
scene->addRect(scene->itemsBoundingRect());
qDebug() << rect->transform();
qDebug() << rect->boundingRect();
qDebug() << rect2->transform();
qDebug() << rect2->boundingRect();
}
orginal view looks like this:
1
take the line as road and rect aside as a symbol. When zoomed out, the rect maintain its size but jumps out of the scene:
2
which should be that topleft of rect to middle of line. I'm also confused with debug info showing that the boundingRect and transform stays the same, which seems that nothing has changed! What causes the problem and is there any way to solve it? Could someone help? Thank you!
Sorry for delay, now I've solved the problem myself.
I found QGraphicsItem::ItemIgnoresTransformations only works when the point you want stick to is at (0,0) in item's coordinate. You need also update boundingRect manually in this way. Nevertheless, the best solution I've found is subclass QGraphicsItem and set matrix in paint() according to world matrix. Below is my code .
QMatrix stableMatrix(const QMatrix &matrix, const QPointF &p)
{
QMatrix newMatrix = matrix;
qreal scaleX, scaleY;
scaleX = newMatrix.m11();
scaleY = newMatrix.m22();
newMatrix.scale(1.0/scaleX, 1.0/scaleY);
qreal offsetX, offsetY;
offsetX = p.x()*(scaleX-1.0);
offsetY = p.y()*(scaleY-1.0);
newMatrix.translate(offsetX, offsetY);
return newMatrix;
}
And the paint function:
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
QWidget *widget)
{
QPointF p(left, top);
painter->setMatrix(stableMatrix(painter->worldMatrix(), p));
painter->drawRect(left, top, width, height);
}
The second argument of stableMatrix is sticked point, in my sample code it's top-left of the item. You can change it to your preference. It works really fine!
Hope this post help :)
The solution to this is even simpler.
QGraphicsItem::ItemIgnoresTransformations
The item ignores inherited transformations (i.e., its position is still anchored to its parent, but the parent or view rotation, zoom or shear transformations are ignored). [...]
And that's the key! Item ignores all transformations, but is still bound to its parent. So you need two items: a parent item that will keep the relative position (without any flags set) and a child item that will do the drawing (with QGraphicsItem::ItemIgnoresTransformations flag set) at parent's (0,0) point.
Here is some working code of a crosshair that have constant size and rotation, while keeping the relative position to its parent:
#include <QGraphicsItem>
#include <QPainter>
class CrossHair : public QGraphicsItem
{
private:
class CrossHairImpl : public QGraphicsItem
{
public:
CrossHairImpl (qreal len, QGraphicsItem *parent = nullptr)
: QGraphicsItem(parent), m_len(len)
{
setFlag(QGraphicsItem::ItemIgnoresTransformations);
}
QRectF boundingRect (void) const override
{
return QRectF(-m_len, -m_len, m_len*2, m_len*2);
}
void paint (QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) override
{
painter->setPen(QPen(Qt::red, 1));
painter->drawLine(0, -m_len, 0, m_len);
painter->drawLine(-m_len, 0, m_len, 0);
}
private:
qreal m_len;
};
public:
CrossHair (qreal x, qreal y, qreal len, QGraphicsItem *parent = nullptr)
: QGraphicsItem(parent), m_impl(len, this) // <-- IMPORTANT!!!
{
setPos(x, y);
}
QRectF boundingRect (void) const override
{
return QRectF();
}
void paint (QPainter *, const QStyleOptionGraphicsItem *, QWidget *) override
{
// empty
}
private:
CrossHairImpl m_impl;
};

Cut copy paste in Graphics View

I have 5 entities that can be added in graphics view on mouse events and button clicks. Each entity has been assigned the unique id. I need to add the operations cut, copy and paste on
these entities. How to proceed with that. I didn't get any example for cut, copy paste operations in graphics View in Qt. How can I do that?
I have different classes for all entities my class line, circle and ellipse are inherited from QGraphicsItem and line and text from QgraphicsLineItem and QgraphicsEllipse Item. Please tell me how can I work with them too.
line.cpp
#include "line.h"
Line::Line(int i, QPointF p1, QPointF p2)
{
// assigns id
id = i;
// set values of start point and end point of line
startP = p1;
endP = p2;
}
int Line::type() const
{
// Enable the use of qgraphicsitem_cast with line item.
return Type;
}
QRectF Line::boundingRect() const
{
qreal extra = 1.0;
// bounding rectangle for line
return QRectF(line().p1(), QSizeF(line().p2().x() - line().p1().x(),
line().p2().y() - line().p1().y()))
.normalized()
.adjusted(-extra, -extra, extra, extra);
}
void Line::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
QWidget *widget)
{
// draws/paints the path of line
QPen paintpen;
painter->setRenderHint(QPainter::Antialiasing);
paintpen.setWidth(1);
if (isSelected())
{
// sets brush for end points
painter->setBrush(Qt::SolidPattern);
paintpen.setColor(Qt::red);
painter->setPen(paintpen);
painter->drawEllipse(startP, 2, 2);
painter->drawEllipse(endP, 2, 2);
// sets pen for line path
paintpen.setStyle(Qt::DashLine);
paintpen.setColor(Qt::black);
painter->setPen(paintpen);
painter->drawLine(startP, endP);
}
else
{
painter->setBrush(Qt::SolidPattern);
paintpen.setColor(Qt::black);
painter->setPen(paintpen);
painter->drawEllipse(startP, 2, 2);
painter->drawEllipse(endP, 2, 2);
painter->drawLine(startP, endP);
}
}
How can I work with this QGraphicsLineItem?
I think you should use custom graphics scene. Create QGraphicsScene subclass. Reimplement keyPressEvent:
if (e->key() == Qt::Key_C && e->modifiers() & Qt::ControlModifier)
{
listCopiedItems = this->selectedItems();
}
 
 
if (e->key() == Qt::Key_V && e->modifiers() & Qt::ControlModifier)
{
for(int i=0; i< listCopiedItems.count(); i++)
{
//create new objects, set position and properties
}
}
You can get all needed properties from old objects such as color, size etc and set to new. For cut do same thing but delete old objects from scene and when all work will be done, delete this objects from memory. Also you can create shortcuts with QShortcut class.
Edit. I want to say that it is very complicate task, so I can't get you code for all cases, for all types. I give just example, but this example works(I tested it). I post here absolutely full code.
Header:
#ifndef GRAPHICSSCENE_H
#define GRAPHICSSCENE_H
#include <QGraphicsScene>
#include <QStack>
#include <QPoint>
#include <QMouseEvent>
class GraphicsScene : public QGraphicsScene
{
Q_OBJECT
public:
explicit GraphicsScene(QObject *parent = 0);
signals:
protected:
void mousePressEvent(QGraphicsSceneMouseEvent *event);
void keyPressEvent(QKeyEvent *event);
public slots:
private:
QList<QGraphicsItem *> lst; QPoint last;
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
QVector<QGraphicsEllipseItem * > vec;
};
#endif // GRAPHICSSCENE_H
Cpp:
#include "graphicsscene.h"
#include <QDebug>
#include <QGraphicsSceneMouseEvent>
#include <QGraphicsItem>
GraphicsScene::GraphicsScene(QObject *parent) :
QGraphicsScene(parent)
{
//add something
addPixmap(QPixmap("G:/2/qt.jpg"));
vec.push_back(addEllipse(0,0,50,50,QPen(Qt::red),QBrush(Qt::blue)));
vec.push_back(addEllipse(0+100,0+100,50,50,QPen(Qt::red),QBrush(Qt::blue)));
vec.push_back(addEllipse(0+150,0+150,50,50,QPen(Qt::red),QBrush(Qt::blue)));
}
void GraphicsScene::mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent)
{
//qDebug() << "in";
if (mouseEvent->button() == Qt::LeftButton)
{
last = mouseEvent->scenePos().toPoint();//remember this point, we need it for copying
QGraphicsItem *item = itemAt(mouseEvent->scenePos(), QTransform());
item->setFlags(QGraphicsItem::ItemIsSelectable);
item->setSelected(!item->isSelected());
}
QGraphicsScene::mousePressEvent(mouseEvent);
}
void GraphicsScene::keyPressEvent(QKeyEvent *e)
{
if (e->key() == Qt::Key_C && e->modifiers() & Qt::ControlModifier)
{
lst = this->selectedItems();
}
if (e->key() == Qt::Key_V && e->modifiers() & Qt::ControlModifier)
{
for(int i=0; i< lst.count(); i++)
{
//if it is ellipse
QGraphicsEllipseItem *ell = qgraphicsitem_cast<QGraphicsEllipseItem *>(lst.at(i));
if(ell)
{//then add ellipse to scene with ell properties and new position
addEllipse(QRect(last,ell->rect().size().toSize()),ell->pen(),ell->brush());
qDebug() << "good";
}
}
}
QGraphicsScene::keyPressEvent(e);
}
It is so complicate because you have no any clone() method, so you can't clone object with all all properties and move it to the new position. If you have your specific items that you should provide something specific too. That's why it is complex and I can't get code for all cases.
EDIT
You can't show scene, you should use this instead:
QGraphicsView vview;
GraphicsScene ss;
vview.setScene(&ss);
vview.show();