PDA

View Full Version : QGraphicsItem scenePos() broken after aborting previous item drag



philw
24th February 2009, 19:12
We're trying to abort an item drag when the user hits the ESC key before releasing the mouse button. We do that within the item's keyPressEvent() method by momentarily disabling the item. (See code, below).

That basically works, but it causes a problem. During the subsequent move-drag operation of a different QGraphicsItem, in the processing of mouseMoveEvents, the item is "warped" to near the origin of the scene, and QGraphicsItem::scenePos() is, in fact, returning those sorts of coordinates: it's returning the difference of the drag mouse position from the mouse press position which started the item drag.

This is with Qt version Qt 4.3.3, both Solaris and Windows XP. Here's a excerpt (and minor simplification) of our code: (1) keyPressEvent, and (2) mouseMoveEvent:



// virtual from QGraphicsItem
void SimObjGfxItem::keyPressEvent (QKeyEvent* evt)
{
const QGraphicsItem* mGrabItem = scene()->mouseGrabberItem();
const bool mouseGrabActive = (this == mGrabItem);
const bool isEscKey (evt->key() == Qt::Key_Escape);

if (mouseGrabActive && isEscKey)
{
// Schedule restoration of Pre-Move Positions
_restorePreMovePosOnNextFocusOut = true;

// DROP THE MOUSE GRAB (by momentarily becomming disabled).
// This works, but it causes scenePos() to return strange results
// during mouseMoveEvents on the subsequent drag-move of a different
// QGraphicsItem. (See below).
//
setEnabled (false);
qApp->processEvents(); // (same behavior with or without this).
setEnabled (true);
setSelected (true);

return;
//------->>
}

// call base class method
QGraphicsItem::keyPressEvent (evt);
}

//----------------

// virtual from QGraphicsItem
void SimObjGfxItem::mouseMoveEvent (QGraphicsSceneMouseEvent* evt)
{
// Call base class method
QGraphicsItem::mouseMoveEvent (evt);

... ... ...

// NOTE: On the QGraphicsItem drag (only) immediately after aborting a
// drag of another item with the ESC key -- see keyPressEvent, above --
// QGraphicsItem::scenePos() returns instead coordinates relative to the
// starting position of the QGraphicsItem drag -- i.e. on the first
// several moves, near (0,0).

const QPointF objScenePos (scenePos());
const double sceneX (objScenePos.x());
const double sceneY (objScenePos.y());

... ... ...
std::cout << ...
<< " scene (" << sceneX << "," << sceneY << ")"
<< ... << std::endl;
... ... ...
}


Thank you in advance,
Phil Weinstein, CADSWES,
University of Colorado at Boulder, USA

philw
25th February 2009, 00:34
I believe that I see what's going on. I would call this a Qt bug.

The implementation of:
QGraphicsItem::mouseRelaseEvent (QGraphicsSceneMouseEvent*).

... normally clears this QGraphicsScene private data member:

QMap<QGraphicsItem*, QPointF>
QGraphicsScenePrivate::movingItemsInitialPositions ;

But forcing the QGraphicsItem to lose the mouse grab prevents a subsequent mouse release event from being sent to the QGraphicsItem, so that QMap is not cleared.

The movingItemsInitialPositions map must be empty for the QGraphicsItem:: mouseMoveEvent() implementation to populate its local 'initialPositions' copy of that QMap. And this is, I believe, the source of this problem.

I believe the correct fix would be for the QGraphicsScene's movingItemsInitialPositions QMap to be cleared when the scene's mouseGrabberItem asynchronously loses the grab (e.g. when the item is disabled).

Also, it would be useful if QGraphicsItem had a public 'dropMouseGrab()' method which did the same cleanup as does QMouseReleaseEvent().

In the mean time, instead of dropping the mouse grab (when the user hits the ESC key during an item drag-move -- see code snippet in prior post), I think we'll be able to fake out subsequent moves by intervening with the use of an override of QGraphicsItem::itemChange(), for the ItemPositionChange case. It will also be important to deal with the other selected QGraphicItems which were involved in the drag-move.

This ESC key behavior might be appropriate as an enhancement to QGraphicsItem drag-moves -- i.e. dropping the grab and restoring the initial position of all selected items involved in the drag-move.