Problem getting QGraphicsItem::mousePressEvent to trigger
-
Windows 11
Qt 6.7.3-03The problem described below started when we upgraded from Qt5.15 to Qt 6.7.3-03.
I have a MyGraphicsItem derived from MyGraphicsObject . I have MyGraphicsScene derived from QGraphicsScene. I want to do something when someone clicks on MyGraphicsObject and then moves the mouse.
When a QMouseEvent is triggered, I make a new QMouseEvent with the contents of the triggering QMouseEvent. I also start a QTimer which is slightly longer than the double-click time. When the timer expires, I call QGraphicsView::mousePressEvent with the QMouseEvent that I created.
When someone clicks on MyGraphicsObject, and waits for the QTimer to expire, MyGraphicsScene::mousePressEvent is called. MyGraphicsScene::mousePressEvent calls QGraphicsScene::mousePressEvent which results in MyGraphicsObject::mousePressEvent being called. Everything works.
But I don't want the user to have to wait the half second or so before dragging the mouse. I figure that if the user is dragging the mouse, they are not doing a double-click and I can go right into mouse move action.
So when a mouseMoveEvent is triggered, I stop the timer, and call QGraphicsView::mousePressEvent which results in MyGraphicsScene::mousePressEvent being called and which results in MyGraphicsObject::mousePressEvent being called, and everything works... almost.
This works 100 percent of the time if the MyGraphicsObject is on one of my two external monitors. It doesn't work if MyGraphicsObject is on my laptop monitor. What happens in that case is MyGraphicsScene::mousePressEvent is called, but MyGraphicsObject::mousePressEvent is NOT called.
I have debugging code in MyGraphicsScene::mousePressEvent which checks that the MyGraphicsObject is in the list of objects and then dumps the value of every accessor in QGraphicsObject. The cases that work and the cases that don't all have the same values. I've checked and there is no mouseGrabberItem. I've checked and the Graphics Object does not have a parent. Everything is the same.
I'm not sure what else to try. I don't know what would make this work differently between my laptop monitor vs the external monitors.
Anyone have any suggestions? Anyone know of a reason why QGraphicsScene might not forward the mouse click to the QGraphicsObject, other than what I've tested for?
some code
The code getting a bunch of values from the graphics object.Qt::MouseButtons acceptedButtons = ngo->acceptedMouseButtons(); const bool isActive = ngo->isActive(); const bool isBlockedByModalPanel = ngo->isBlockedByModalPanel(); const bool isClipped = ngo->isClipped(); const bool isPanel = ngo->isPanel(); const bool isSelected = ngo->isSelected(); const bool isUnderMouse = ngo->isUnderMouse(); const bool isVisible = ngo->isVisible(); const bool isWidget = ngo->isWidget(); const bool isWindow = ngo->isWindow(); const bool acceptDrops = ngo->acceptDrops(); const bool acceptHoverEvents = ngo->acceptHoverEvents(); const bool acceptTouchEvents = ngo->acceptTouchEvents(); const bool signalsBlock = ngo->signalsBlocked(); const bool hasParent = ngo->parent() != nullptr;The code copying the QMouseEvent which triggered the QTimer
m_pMousePressEvent = new QMouseEvent(pEvent->type(), pEvent->pos(), pEvent->globalPos(), pEvent->button(), pEvent->buttons(), pEvent->modifiers()); -
OnPortClickDelay is my code that is called either when the timer expires or someone moves the mouse while the timer is active.
Code path that works
OnPortClickDelay->View::mousePressEvent -> Scene::mousePressEvent->GraphicsObject::mousePressEventCode path that doesn't work (on the laptop monitor)
OnPortClickDelay->View::mousePressEvent -> Scene::mousePressEventAfter OnPortClickDelay is called, the two paths are identical.
-
I think I may have figured out the problem. When my laptop monitor is set to 150 percent scale, things don't work, but when it is set to 100 percent scale, it does work.
So it appears that someone is doing a position calculation wrong, but the calculation that is going wrong is inside of QGraphicsScene.
-
The only thing that happens differently between the cases that work is the following
- When the timer expires, mousePressEvent is triggered by the QTimer. This case always works.
- When someone moves a mouse, the mousePressEvent is triggered by the mouseMoveEvent. After the mousePressEvent is handled, the mouseMoveEvent is passed down to Qt. The wrong behavior occurs befure the mouseMoveEvent is passed down. Its as if QGraphicsScene::mousePressEvent believes that the coordinates in the saved mouse event is not on the QGraphicsObject, even though it is the same coordinates when it works as when it doesn't.
-
Hi,
My first suggestion would be to use a more recent version of Qt to see if you still encounter that issue. The current series is at 6.11 with 6.12 around the corner.
Do you have some scaling applied ? Did you check if you have the same issue with and without it ?
-
I figured out the problem. When the mousePressEvent occurs, I am saving the mouse event by creating a brand new MousePressEvent and copying all the values from the original event via the constructor. When someone moves the mouse, the global position in my private MousePressEvent changes. According to AI, this is a feature.
Can someone verify if AI is correct? It is matching up with what I am seeing, but I can't find anything in Qt documentation. And it seems rather bizarre and unexpected.
"A saved QMouseEvent changes its globalPosition when the mouse moves because QMouseEvent objects are dynamically updated or re-dispatched by the window system, or because saving a pointer to the original event object instead of copying its data (QPointF) causes it to reflect subsequent cursor movements.Why It HappensPointers vs. Value Copies: Saving a pointer (QMouseEvent* event) instead of copying the actual position values (QPointF pos = event->globalPosition()) means your variable references the live event structure or next incoming events processed by the event loop.Live Event Reuse: In certain event-handling implementations or custom overrides, the system updates or repurposes event coordinates during continuous tracking.How to Fix ItCopy the Position Immediately: Extract and store the QPointF or QPoint value rather than saving the event pointer.cpp// Bad: saves the pointer, values change as mouse moves
savedEvent = event;// Good: copies the actual screen coordinates
savedGlobalPos = event->globalPosition();
Use Value Types: Declare your saved variable as a QPointF (or QPoint) instead of QMouseEvent*." -
Since you don't provide code on how you save the QMouseEvent data I would guess you even have luck that your app does not crash when pEvent is really only a pointer to the given QMouseEvent and you use it later in another function called through a QTimer. Noone guarantees that the memory the pointer points to will be deleted.
-
Since you don't provide code on how you save the QMouseEvent data I would guess you even have luck that your app does not crash when pEvent is really only a pointer to the given QMouseEvent and you use it later in another function called through a QTimer. Noone guarantees that the memory the pointer points to will be deleted.
The code is provided in the original post.
What is happening is that Qt seems to update the global position of existing QMouseEvents when a separate mouse event occurs and Google AI is telling me that that is the way it is supposed to work. I can't find any Qt documentation verifying what AI is telling me, but it matches up exactly with what I am seeing.
Code copying the mouse event reposted. As I said, it is creating a new, independent, mouse event with the contents of the old mouse event. I have dozens of trace statements in my code, tracing the value of the global position. Once set, the value doesn't change until a new mouse event occurs. Then the value of the existing m_pMousePressEvent is changed without me either creating a new m_pMousePressEvent or passing the a new global position to the existing m_pMousePressEvent.
The current code uses the newer position() and globalPosition() which did not make a difference.
m_pMousePressEvent = new QMouseEvent(pEvent->type(), pEvent->pos(), pEvent->globalPos(), pEvent->button(), pEvent->buttons(), pEvent->modifiers());basically, I am looking for verification that what AI is telling me and what I am seeing is correct. That that is the way it is supposed to work.
-
In the original post I don't see where you create pEvent. And when this is a pointer to the original event then my comments are valid and you should be happy that your app don't crash due to an invalid pointerw