PDA

View Full Version : Incorrect mouse behavior in GraphicsScene



framalex
11th June 2010, 22:04
I'm trying to draw my own objects(inherited from QGraphicsItem) on my own scene (inherited from QGraphicsScene) on mouse click. When I handle mousePressEvent it puts new object in the wrong place (namely it doubles its coordinates while setting and speed while moving).

I handle MousePressEvent like this:


void PolygonScene::mousePressEvent(QGraphicsSceneMouseE vent *mouseEvent)
{
if (mouseEvent->button() != Qt::LeftButton)
return;

PPoint *p = new PPoint(QColor(127, 0, 0), mouseEvent->scenePos().x(), mouseEvent->scenePos().y());
addItem(p);
QGraphicsScene::mousePressEvent(mouseEvent);
}


And I create GraphicsScene and GraphicsView this way:


graphScene = new PolygonScene();
QPixmap pix = QPixmap::fromImage(image);
graphScene->clear();
graphScene->addPixmap(pix);
ui.graphicsView->setGeometry(QRect(5, 5, pix.width(), pix.height()));
ui.graphicsView->setViewportUpdateMode(QGraphicsView::SmartViewport Update);
ui.graphicsView->setScene(graphScene);
ui.graphicsView->show();


Where should i look for the source of troube?
Thank you

wysota
11th June 2010, 22:07
How does boundingRect() for your PPoint class look like? Please also post the constructor as it will probably be relevant too.

framalex
11th June 2010, 22:12
Here they are



QRectF PPoint::boundingRect() const
{
return QRectF(x()-size, y()-size, x()+size, y()+size);
}

QPainterPath PPoint::shape() const
{
QPainterPath path;
path.addEllipse(boundingRect());
return path;
}




PPoint::PPoint(const QColor &color, int x, int y)
: QGraphicsItem()
{
this->setX(x);
this->setY(y);
this->color = color;
size = 15;

setFlags(ItemIsSelectable | ItemIsMovable);
setAcceptHoverEvents(true);
}

PPoint::PPoint() : QGraphicsItem()
{
color = QColor(0,0,255);
size = 15;
}

wysota
11th June 2010, 22:26
BoundingRect() of your class is invalid. It should return range of coordinates of the item regardless of the position of the item. In your situation it should probably be QRect(-size, -size, 2*size, 2*size) as I assume you meant the item to have the "anchor" in the center of the rectangle (and not in the bottom right corner as the current code would do (the last two parameters to QRect are the width and height of the rectangle and not the coordinates of its bottom right vertex) if we neglected the incorrect use of x() and y()) and "size" being the radius of the ellipse. Furthermore I'm assuming you didn't set the size of your scene which means it expands to envelop all items it's holding. If the scene is smaller than the view, it will by default be centered in the view which can also cause a virtual effect of translated coordinates.

I'd change your PPoint class constructor and call to:

void PolygonScene::mousePressEvent(QGraphicsSceneMouseE vent *mouseEvent)
{
if (mouseEvent->button() != Qt::LeftButton)
return;

PPoint *p = new PPoint(QColor(127, 0, 0));
addItem(p);
p->setPos(mouseEvent->pos());
}

QRectF PPoint::boundingRect() const
{
return QRectF(-size, -size, 2*size, 2*size);
}

PPoint::PPoint(const QColor &color)
: QGraphicsItem()
{
this->color = color;
size = 15;
setFlags(ItemIsSelectable | ItemIsMovable);
setAcceptHoverEvents(true);
}

Of course paint() should only paint within the coordinates returned by boundingRect() (i.e. [-size, size] in both directions).

framalex
11th June 2010, 22:47
I made all changes you suggested

Set scene size:


QPixmap pix = QPixmap::fromImage(image);
graphScene->clear();
graphScene->setSceneRect(0,0,pix.width(), pix.height());
graphScene->addPixmap(pix);
ui.graphicsView->setGeometry(QRect(5, 5, pix.width(), pix.height()));
ui.graphicsView->setViewportUpdateMode(QGraphicsView::SmartViewport Update);
ui.graphicsView->setScene(graphScene);
ui.graphicsView->show();


Constructor, boundingRect and mousePressEvent looks like you posted exactly.

Here is my paint fuction:


void PPoint::paint(QPainter *painter, const QStyleOptionGraphicsItem *item, QWidget *widget)
{
Q_UNUSED(widget);
QBrush b = painter->brush();
painter->setBrush(QBrush(color));
painter->drawEllipse(0, 0, size, size);
painter->setBrush(b);
}


But now every time i click the mouse on GraphicsView it draws PPoint in the left upper corner. Strange...
This behavior remains even when i write



painter->drawEllipse(x(), y(), size, size);


as it was before

wysota
11th June 2010, 22:54
If you changed boundingRect() to the one I posted then your paint() is incorrect. You are painting an ellipse that occupies only the right lower quarter of your item. The proper call would be:

painter->drawEllipse(boundingRect());

I also suggest to replace the pixmap item you added to the scene with:

graphScene->setBackgroundBrush(pix);

Also please use layouts to position your graphics view instead of manual calls to setGeometry().

framalex
11th June 2010, 23:00
Well, the problem still remains :(

Here is the whole realization of PPoint class



#include "ppoint.h"

PPoint::PPoint(const QColor &color, int x, int y)
: QGraphicsItem()
{
this->setX(x);
this->setY(y);
this->color = color;
size = 15;

setFlags(ItemIsSelectable | ItemIsMovable);
setAcceptHoverEvents(true);
}

PPoint::PPoint(const QColor &color) : QGraphicsItem()
{
this->color = color;
size = 15;
setFlags(ItemIsSelectable | ItemIsMovable);
setAcceptHoverEvents(true);
}

PPoint::~PPoint()
{

}


void PPoint::paint(QPainter *painter, const QStyleOptionGraphicsItem *item, QWidget *widget)
{
Q_UNUSED(widget);
QBrush b = painter->brush();
painter->setBrush(QBrush(color));
painter->drawEllipse(boundingRect());
painter->setBrush(b);
}

void PPoint::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
QGraphicsItem::mousePressEvent(event);
update();
}

void PPoint::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
QGraphicsItem::mouseMoveEvent(event);
if (event->modifiers() & Qt::ShiftModifier) {
update();
return;
}

}

void PPoint::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
QGraphicsItem::mouseReleaseEvent(event);
update();
}

QRectF PPoint::boundingRect() const
{
return QRectF(-size, -size, 2*size, 2*size);
}

QPainterPath PPoint::shape() const
{
QPainterPath path;
path.addEllipse(boundingRect());
return path;
}


and PolygonScene



PolygonScene::PolygonScene(QObject *parent)
: QGraphicsScene(parent)
{
}

PolygonScene::~PolygonScene()
{

}

void PolygonScene::mousePressEvent(QGraphicsSceneMouseE vent *mouseEvent)
{
if (mouseEvent->button() != Qt::LeftButton)
return;

PPoint *p = new PPoint(QColor(127, 0, 0));
addItem(p);
p->setPos(mouseEvent->pos());
// QGraphicsScene::mousePressEvent(mouseEvent);
}

void PolygonScene::mouseMoveEvent(QGraphicsSceneMouseEv ent *mouseEvent)
{

QGraphicsScene::mouseMoveEvent(mouseEvent);
}

void PolygonScene::mouseReleaseEvent(QGraphicsSceneMous eEvent *mouseEvent)
{
QGraphicsScene::mouseReleaseEvent(mouseEvent);
}

wysota
11th June 2010, 23:19
Here is a working example:

#include <QtGui>

class PPoint : public QGraphicsItem {
public:
PPoint(const QColor &c) : QGraphicsItem(), m_size(15), m_color(c){}
QRectF boundingRect() const { return QRectF(-m_size, -m_size, 2*m_size, 2*m_size); }
QPainterPath shape() const { QPainterPath p; p.addEllipse(boundingRect()); return p; }

void paint ( QPainter * painter, const QStyleOptionGraphicsItem * option, QWidget * widget = 0 ) {
painter->setBrush(m_color);
painter->drawEllipse(boundingRect());
if(option->state & QStyle::State_Selected) {
painter->setBrush(Qt::NoBrush);
painter->setPen(Qt::yellow);
painter->drawRect(boundingRect());
}
}
private:
int m_size;
QColor m_color;
};

class MyScene : public QGraphicsScene {
public:
MyScene(const QPixmap &px, QObject *parent = 0) : QGraphicsScene(parent) {
setSceneRect(px.rect());
setBackgroundBrush(px);
}
protected:
void mousePressEvent(QGraphicsSceneMouseEvent * mouseEvent ) {
if(itemAt(mouseEvent->scenePos())){
QGraphicsScene::mousePressEvent(mouseEvent);
return;
}
PPoint *item = new PPoint(Qt::darkRed);
addItem(item);
item->setPos(mouseEvent->scenePos());
item->setFlags(QGraphicsItem::ItemIsMovable|QGraphicsIte m::ItemIsSelectable);
}
};

int main(int argc, char **argv){
QApplication app(argc, argv);
QGraphicsView view;
QPixmap px("/usr/share/wallpapers/default_blue.jpg");
view.setScene(new MyScene(px, &view));
view.show();
return app.exec();
}

framalex
11th June 2010, 23:28
Thank you!


My last code had only one error - I used
mouseEvent->pos()
instead of
mouseEvent->scenePos()
Now it works

wysota
11th June 2010, 23:43
Seems that QGraphicsSceneMouseEvent::pos() returns a null point when called from within the scene. I think it's a bit illogical, it should be consistent with QGraphicsItem::pos().