Confused by doubleclickevent and mousepressevent in Qt - c++

The situation is:
In side a QGraphicsView, I used its doubleClickEvent function to create a QGraphicsItem.
I rewrite the QGraphicsItem's mousePressEvent function to simply print a qDebug information.
However, what I found is that: even if I just double click to create that QGraphicsItem, the newly created QGraphicsItem's mousePressEvent is also called.
How would this happen? What can I do to prevent this?

A QGraphicsView is a widget, so it has a doubleClickEvent. In contrast, a QGraphicsItem is not a widget, but has a mousePressEvent.
When you override events, if you don't want them to be propagated to other objects, you need to accept the event to tell the system that you've handled it.
void MyGraphicsView::mouseDoubleClickEvent(QMouseEvent * event)
{
// Create a graphics item..
...
event->accept(); // tell the system that the event has been handled
}
Without accepting the event, the double click is passed on to the QGraphicsScene and then QGraphicsItem.
If you want to be able to double-click to create an item when clicking on an empty area of a scene, but have an item react if you double-click on an item, you can override the scene's mouseDoubleClickEvent instead of the view and check if there's an item at the cursor position before deciding upon whether or not to create an item, or pass the event on.
void QGraphicsScene::mouseDoubleClickEvent( QGraphicsSceneMouseEvent * mouseEvent )
{
if(items(mouseEvent->scenePos()).isEmpty()) // if no items are at this position
{
// create new graphics item
...
mouseEvent->accept(); // don't propagate the event
}
// no items, so allow the event to propagate by doing nothing
}

Related

Custom Qt QGraphicsItem tooltip

I'm looking for some ways to implement a simple custom tooltip for QGraphicsItem.
I know that I can use setToolTip to set text for tooltip. Now what I want is to change the text dynamically when the mouse hovers at different parts of a QGraphicsItem object.
What I'm thinking to do is when I get an event QEvent::ToolTip, I change the tooltip text in that event handler. However, I cannot find an event function that recieve QEvent::ToolTip for QGraphicsItem.
Or is there some ways to handle an event that mouse hovers for 2 seconds.
How can I make it?
You could implement the hoverMoveEvent in your derived QGraphicsItem class, and set the tooltip based on the position within the graphics item
void MyItem::hoverMoveEvent(QGraphicsSceneHoverEvent* event)
{
QPointF p = event->pos();
// use p.x() and p.y() to set the tooltip accrdingly, for example:
if (p.y() < height()/2)
setTooltip("Upper Half");
else
setTooltip("Bottom Half");
}
Notice that you have to enable hover events for your item.

Knowing inside a leaveEvent which QWidget is entered

I'm implementing a QDialog which shows up when another QWidget (a QFrame) is hovered and destroyed when the mouse cursor leaves the QFrame. I do the construction and destruction via the enterEvent and leaveEvent (inside the QFrame):
void Frame::enterEvent(QEvent *) {
m_dialog = new Dialog(this);
}
void Frame::leaveEvent(QEvent *) {
m_dialog->deleteLater();
}
The dialog moves itself right below the frame when it's constructed. So far so good, but the mouse cursor also has to be able to go to the dialog. I thought about using underMouse() like so:
void Frame::leaveEvent(QEvent *) {
if (! m_dialog->underMouse()) {
m_dialog->deleteLater();
}
}
but the problem is that inside Frame::leaveEvent, m_dialog->underMouse() is always false, even if the mouse is moved right onto it.
How can I know, inside Frame::leaveEvent, if the mouse is now on the dialog or somewhere else?
I could implement it in the following way:
The dialog's leaveEvent triggers a zero timer calling a function in the "target" widget (the widget where the cursor can go without the dialog being destroyed). This function checks if this widget is currently under the mouse cursor. If so, nothing happens. If not, the dialog is destroyed by that widget.
Same for the leaveEvent of the "target" widget, which calls a function implemented in the same way inside the dialog by a zero timer.

QGraphicsItem::SetCursor Won't Deselect

All,
I have a QGraphicsEllipseItem with setFlags(Qt::ItemIsSelectable | Qt::ItemIsMovable). This allows me drag and move ellipses quickly in a QGraphicsView.
I decided to be fancy, and setCursor(Qt::OpenHandCursor) to let the user know they can move it by clicking. However, now it won't let go when I let go of the left mouse button? What am I doing wrong?
Example code: Custom QGraphicItem and Repaint Issues
Note: I removed the update() calls, and added prepareGeometryChange() calls.
Now modify the MakeNewPoint function:
QGraphicsEllipseItem * InteractivePolygon::MakeNewPoint(QPointF & new_point)
{
QGraphicsEllipseItem * result = 0;
result = new QGraphicsEllipseItem();
result->setPos(new_point);
result->setRect(-4, -4, 8, 8);
result->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable)
result->setCursor(Qt::OpenHandCursor); //Setting this removes my ability to let go of an item. NOTE: result is parented by this.
return result;
}
later:
QGraphicsEllipseItem * new_item = MakeNewPoint(bla);
new_item->setParent(this);
//add it to my QList<QGraphicsEllipseItem *> m_points;
I would like to note that my QGraphicsEllipseItem is parented by a custom QGraphicsItem. I don't change the parents/Custom Item cursor, only the ellipse's. I do not experience this problem with non parented ellipses...
Interesting result: So my class custom QGraphicsItem class (the parent of the ellipses) is a QObject so I can filter incoming mouse events from the scene. I did a setCursor(Qt::ArrowCursor) in my custom class's constructor... and here's where it gets interesting:
The eventFilter now catches (event->type() == QEvent::GraphicsSceneMouseMove) even if a mouse button isn't pressed down. If I don't have the setCursor call, that event only fires while a mouse button is pressed... thoughts?
Okay got it, here's what's happening, it's intuitive once you realize it:
When you set that a QGraphicsItem to a unique cursor, the QGraphicsView has to setMouseTracking(true) otherwise the QGraphicsScene will never know when to change the cursor (ie when it's over the graphics item with the unique cursor.) The mouse move events were affecting my ellipse.
Normally, the QGraphicsScene only gets mouse move events when a button is held down.

QGraphicsItem doesn't receive mouse hover events

I have a class derived from QGraphicsView, which contains QGraphicsItem-derived elements. I want these elements to change color whenever the mouse cursor hovers over them, so I implemented hoverEnterEvent (and hoverLeaveEvent):
void MyGraphicsItem::hoverEnterEvent(QGraphicsSceneHoverEvent* event)
{
update (boundingRect());
}
However, this event handler code is never executed. I've explicitly enabled mouse tracking:
MyGraphicsView::MyGraphicsView(MainView *parent) :
QGraphicsView(parent)
{
setMouseTracking(true);
viewport()->setMouseTracking(true);
...
}
Still, no luck. What am I doing wrong?
Fixed it. I need to use setAcceptHoverEvents(true) in the constructor of my QGraphicsItem-derived class.
In my case, hover events wouldn't work if I overrode mouseMoveEvent in my implementation of the QGraphicsView class. I fixed this by adding a call to
QGraphicsView::mouseMoveEvent(event);
which propagated the event to the parent, which in turn sent it out to all the scene items.

Qt -- pass events to multiple objects?

I basically have 3 layers (Window > Scene > View) that each need to handle a mouseMove event without blocking the others. It seems only the youngest child is getting the event though. I was hoping I could process the event and then call event->ignore() to pass the event back up the stack, but it doesn't seem to be working.
Some relevant code if you need it:
void EditorWindow::createScene() {
m_scene = new EditorScene(this);
m_view = new EditorView(m_scene);
// ...
}
void EditorScene::mouseMoveEvent(QGraphicsSceneMouseEvent* mouseEvent) {
printf("B\n");
// ...
}
void EditorView::mouseMoveEvent(QMouseEvent* event) {
printf("C\n");
event->ignore();
}
Only "C" is being printed. Note that EditorScene and EditorView receive different types of mouse events so it's not completely trivial to pass them around.
The EditorWindow also needs the mouse coordinates; currently I'm sending a signal from one of the children which is caught by the window... but it shouldn't really be necessary to relay it that way, should it?
Found this nice article. Calling ignore() tells Qt to find another receiver. Sounds like it should work, but perhaps it means an unrelated receiver. The proper way to propagate it is actually to call BaseClass::Event like so:
void EditorView::mouseMoveEvent(QMouseEvent* event) {
QGraphicsView::mouseMoveEvent(event); // propogate to parent widget
printf("C\n");
}
Now it's printing BCBCBC... which is great, but I can't seem to nudge it up one more level...
Another edit: It was being propogated up properly, I just didn't have setMouseTracking enabled.
QGraphicsView::mouseMoveEvent(event);
Doesn't propagate up to the parent -- it actually propagates down to the scene.
Here is what's happens -- QGraphicsView receives QMouseEvent, translates it into QGraphicsSceneMouseEvent and passes it to the scene. Scene then passes it to appropriate item or, in your case, prints "B". Event handler then returns back to EditorView and prints "C".
Then, if you explicitly ignore event (mouse move is accepted by default), Qt event handler will pass the event to parent of EditorView. So try ignoring after you print "C".
Another thing about mouse move is this:
If mouse tracking is switched off, mouse move events only occur if a mouse button is pressed while the mouse is being moved. If mouse tracking is switched on, mouse move events occur even if no mouse button is pressed.
So make sure you have tracking enabled on parent of EditorView (or that you press buttons :)).
EDIT:
BTW, EditorScene is not a parent of EditorView. Well, it is in your code, but only in QObject meaning of parentship (memory management only).
QGraphicsScene and View don't have normal family relationship -- scene can have multiple views and those views are children of unrelated parents.
For window event propagation purposes you must have QWidget based parent. In fact, I'm pretty sure you reparent EditorView to EditorWindow, or one of its children (when you add it into layout).
INSTAEDIT:
For coordinates you want View itself to emit a signal. Both for decoupling reasons and because you probably want to show local coordinates of the view, and not of the parent window and not screen coordinates (right?). If you actually want scene coordinates, View is right choice too, because it knows transformation matrix.
Coordinates go like this:
Screen -> EditorWindow local -> EditorView local -> Scene transformed -> whatever item local transformed.
QGraphicsView::mousePressEvent( e ) in my mousePressEvent did the trick!