PDA

View Full Version : QGraphicsView mouse events



high_flyer
16th July 2008, 10:02
Hi,

I have a custom QGraphicsView, that has a QGraphicsScene (not custom), which has custom QGraphicsItems.
But the Items get no mouse evens.
Here is what I did:
- reimplemented QGraphicsView::mousePressEvent() (and the other events I need) - this gets called. (QGraphicsView::mousePressEvent() gets called in it as well)
- QGraphicsItems have setFlags(QGraphicsItem::ItemIsMovable|QGraphicsIte m::ItemIsSelectable | flags())
- reimplemented QGraphicsItems ::mousePressEvent() - not being called.

So basically events are not propagated from view to items on the scene.
I must be missing something but I can't see what - any suggestions?
Throw anything that comes up your mind, it is probably something silly I just forgot...

Thanks in advance.

aamer4yu
16th July 2008, 10:13
Did you try setting QGraphicsItem::setAcceptsHoverEvents to true on the items ??

high_flyer
16th July 2008, 10:19
no, since I don't need hover events, juts plain click and move mouse events.
But thanks for the suggestion.

high_flyer
16th July 2008, 16:12
in order to debug this I sub classed QGrapicsScene.
The mouse event (in this case click event) gets delivered to the scene.
In the mouse event handler in the scene however, if I ask mouseGrabberItem() - I get -> 0, which means no item is grabbing the mouse - which means no item is getting mouse events.
Any idea idea what could be causing this?
I can't recall nor do I see this in the docs that I need to explicitly enable mouse grabbing on items since its a default behaviour (beyond setting the selectable flag).
(I am using Qt4.3 in this case, and grabMouse() is not yet available. But this should not be needed any how...)

EDIT:
all the items return true on acceptedMouseButtons ().testFlag( Qt::LeftButton)


help?

wysota
16th July 2008, 18:30
Do you call the base class implementation of the view's mouse events? You need to do that in order for events to be delivered to items.

aamer4yu
16th July 2008, 18:39
Can u post some code which mimicks this behaviour.
I tried catching mousePressEvent in of my code, and I do get the events. And I have these flags set - setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable); .

Probably you are missing some thing.
QUERY : Are you calling base class QGraphicsScene::mousePressEvent() from you scene class ?? I guess you are not. I dont see any other reason unless we see ur code ;)

EDIT:
Wysota posted before me, and ya it must be base class of view,,, not scene :D

high_flyer
22nd July 2008, 13:14
Hi,
@wysota:
yes I do.
Here is the class chain mouse press evets:
The QGraphicsView subclass (this gets called):

void QCTimeLineWidget::mousePressEvent(QMouseEvent *event)
{
QGraphicsView::mousePressEvent(event);

if(event->button() == Qt::RightButton)
{
//do some stuff
event->accept();
}
else event->ignore();
}

The Scene subclass (this gets called), and "no mouse grabber" is also being printed:

void SliderScene::mousePressEvent(QGraphicsSceneMouseEv ent * mouseEvent )
{
std::cout<<"scene event"<<std::endl;
if(!mouseGrabberItem()) std::cout<<"no mouse grabber"<<std::endl;
QGraphicsScene::mousePressEvent(mouseEvent);
}

the QGrphicsRectItem subclass - not getting called (but that is no surprise since there is no mouse grabber item in the scene - but I don't know why)


CPLContainer::CPLContainer(QGraphicsItem *parent)
: QGraphicsRectItem(parent)
{
setBoundingRect(QRectF(0,0,20,20));
setFlag(QGraphicsItem::ItemIsMovable);
setFlag(QGraphicsItem::ItemIsSelectable);
}

void CPLContainer::mousePressEvent ( QGraphicsSceneMouseEvent * event )
{
std::cout<<"CPLContainer mouse clicked"<<std::endl;
QGraphicsRectItem::mousePressEvent(event);
}

Thanks again for the help.

wysota
22nd July 2008, 22:51
I don't like the event->ignore() line of yours. Don't you think you should ignore an event if and only if both your handler and the base handler wants to ignore the event? It might be that the default handler accepts the event, but it gets ignored because you ignore it as it was not caused by right click and because of that Something Bad Happens. I don't know if that is the source of your problem (I doubt it), but you should double check that anyway. Despite that I think your item's handler should be getting called... Try extracting the smallest possible chunk of code that reproduces the problem.

caduel
22nd July 2008, 22:58
Why do you call setBoundingRect and not setRect in your CPLContainer constructor? Perhaps your rect is empty and thus gets no mouse events?

high_flyer
23rd July 2008, 08:57
Thanks for you answers guys.
@wysota: the ignore is not a problem since it is only present in the QGraphicsView subclass, and the scene does get the event.
In addition, the original code did not have the ignore in it, I just added it as I started trying all kinds of things.
In order to make a long story short, attached is a project (kdevelop) which illustrates the problem. (only three basically empty classes and a main)
Maybe you can then help me find what it is that I am doing wrong.
I know I am doing something wrong which is basic, but I really just don't see what!

@caduel: setBaoundingRect() ( in my code) sets the rect that is returned by boundingRect(), which is the important thing.
But in the code attached even this is stripped off (bounding rect is hard coded), and still the same behaviour.

I really appreciate the help!
Thanks.

caduel
23rd July 2008, 09:04
i) note besides: the Makefile in your zip does not work for others
ii) using Qt4.4 (Linux) your code seems to work

running your app and clicking the red rectangle, I get:
Hello from Qt 4!
view pressed
QCTimelineScene: mousePressEvent
scene clicked
view pressed
QCTimelineScene: mousePressEvent
scene clicked
view pressed
QCTimelineScene: mousePressEvent

Should it do more?

high_flyer
23rd July 2008, 09:19
i) note besides: the Makefile in your zip does not work for others
of course not - this is why you have the pro file, run qmake on in it first! :-)

yes it should do more - notice that the red rectangle item does not get the mouse click - only the view and the scene.

caduel
23rd July 2008, 09:45
add

setRect(0,0,70,70);
to your constructor.
The boundingRect is not enough...

high_flyer
23rd July 2008, 10:00
thanks!
You are correct!

But then I don't understand the docs.
In the docs it says:

To write your own graphics item, you first create a subclass of QGraphicsItem, and then start by implementing its two pure virtual public functions: boundingRect(), which returns an estimate of the area painted by the item, and paint(), which implements the actual painting.

why is boundingRect() pure virtual if setRect() must be called as well?

Hmm. oh well, no time for that now, I need to work - big thanks!

high_flyer
23rd July 2008, 10:08
hmm.... this is more complicated it seems.
The setRect() was needed because it was a QGraphicsRectItem subclass.
But QGraphicsItem subclasses, or for example QGraphicsPathItem do not have the setRect() method.
And with these items I am back to my problem....

Ideas?

EDIT: I figured it out - following the analogy of the QGraphicsRectItem, then for QGraphicsPathItem setPath() must be called, or setXxxx() depending on the subclass specialisation.
This bit should be added to the docs I think...

wysota
25th July 2008, 14:48
If you reimplement boundingRect() and shape() you shouldn't need to use setXXXX methods. Maybe you forgot about the shape()? The base implementation should simply call boundingRect() (it's reimplemented in subclasses probably causing your problem).

sarbh20ss
24th September 2013, 08:45
hey I am facing the same problem...
I have subclassed QGraphicsView, QGraphicsScene, QGraphicsItem.
I am adding this scene object to view, and item object to scene. I am implementing mouse press events for view subclass and item subclass. Item object's flags are also set.
When I click on item object only view's press event gets called. I want to check if the item is clicked or not through press event of item but that is not getting called.
How do I solve this problem?

Thanks for help in advance.

Cruz
27th May 2014, 22:17
The very same problem here. Reimplented shape(), it never gets called.

wysota
27th May 2014, 23:10
The very same problem here. Reimplented shape(), it never gets called.

Did you remember to make it const? If you are using C++11 you can make sure the prototype is correct by using the "override" specifier.

Cruz
28th May 2014, 15:31
I did make it const, but I doubt this problem has anything to do with shape().

In my case I discovered that I had an error in the boundingRect() definition. My bounding box had an offset to the item, so that the item would not report mouse events when the mouse was moving over it simply because the bounding box was somewhere else. Now that I fixed it, the mouse events do get delivered to the graphics item correctly. I can even remove the shape implementation (which does nothing else than the standard implementation anyways) and it still works.

The one thing that remains to solve is that all three, the graphics item, the graphics scene, and the graphics view, all seem to receive the mouse events simultaneously. I was expecting in this situation that the graphics item consumes the event and it doesn't get propagated all the way up to the view. Is my expectation wrong, or am I not doing something right? Here is the code for the relevant places:




class GraphicsViewWidget : public QGraphicsView
{
Q_OBJECT

GraphicsViewWidget(QWidget *parent = 0);
~GraphicsViewWidget();

protected:
void mousePressEvent(QMouseEvent *event);
void mouseReleaseEvent(QMouseEvent *event);
void mouseMoveEvent(QMouseEvent *event);
};

void GraphicsViewWidget::mousePressEvent(QMouseEvent *event)
{
qDebug() << "view press event";
QGraphicsView::mousePressEvent(event);
}

void GraphicsViewWidget::mouseMoveEvent(QMouseEvent *event)
{
qDebug() << "view mouse move";
QGraphicsView::mouseMoveEvent(event);
}

void GraphicsViewWidget::mouseReleaseEvent(QMouseEvent *event)
{
qDebug() << "view mouse release";
QGraphicsView::mouseReleaseEvent(event);
}



class MyScene : public QGraphicsScene
{
Q_OBJECT

ComState comState;

public:
MyScene (QWidget *parent = 0);
~MyScene (){};

void mousePressEvent(QGraphicsSceneMouseEvent *event);
void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);
void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
};



MyScene::MyScene (QWidget *parent)
: QGraphicsScene(parent)
{
setSceneRect(-100, -100, 200, 200);

addItem(&comState);
comState.setFlags(QGraphicsItem::ItemIsMovable);
}

void MyScene::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
qDebug() << "scene mouse press";
QGraphicsScene::mousePressEvent(event);
}

void MyScene::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
qDebug() << "scene mouse move" << mouseGrabberItem();
QGraphicsScene::mouseMoveEvent(event);
}

void MyScene::mouseReleaseEvent(QGraphicsSceneMouseEven t *event)
{
qDebug() << "scene mouse release";
QGraphicsScene::mouseReleaseEvent(event);
}



class ComState : public QGraphicsItem
{
public:
double vx;
double vy;

public:
ComState(QGraphicsItem *parent = 0);
~ComState(){};

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

void mousePressEvent(QGraphicsSceneMouseEvent *event);
void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);
void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
};



ComState::ComState(QGraphicsItem *parent)
: QGraphicsItem(parent)
{
vx = 0;
vy = 0;
}

QRectF ComState::boundingRect() const
{
return QRectF(-0.1, -0.1, 0.2, 0.2);
}

void ComState::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
qDebug() << "item mouse press";
}

void ComState::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
qDebug() << "item mouse move";
}

void ComState::mouseReleaseEvent(QGraphicsSceneMouseEve nt *event)
{
qDebug() << "item mouse release";
}

wysota
28th May 2014, 15:52
You are calling the base class implementation so the view forwards events to the scene which forwards them to the item.

Cruz
28th May 2014, 17:48
I thought the logic was the other way around. The most specific item is called first, which would be the graphics item, and then the event is propagated up until some object accepts it.

But anyhow, I HAVE to call the base implementation in order to use the functionality provided by the Graphics View Framework. And I also have to remimplement the mouse events in the view and the scene to do custom things. So how can I for example detect the the item is being dragged and not the view, if view and item both react to the mouse move event?

wysota
29th May 2014, 09:03
I thought the logic was the other way around. The most specific item is called first, which would be the graphics item, and then the event is propagated up until some object accepts it.

You click on the view so it is the first one to get the event.


But anyhow, I HAVE to call the base implementation in order to use the functionality provided by the Graphics View Framework. And I also have to remimplement the mouse events in the view and the scene to do custom things. So how can I for example detect the the item is being dragged and not the view, if view and item both react to the mouse move event?

Store additional information somewhere that you have already handled the event and check that information on every level (view, scene, item) of event handling.